UNPKG

@hdwallet/core

Version:

A complete Hierarchical Deterministic (HD) Wallet generator for 200+ cryptocurrencies, built with TypeScript.

247 lines 13.7 kB
"use strict"; // SPDX-License-Identifier: MIT Object.defineProperty(exports, "__esModule", { value: true }); exports.CardanoHD = void 0; const cbor2_1 = require("cbor2"); const bip32_1 = require("./bip32"); const cryptocurrencies_1 = require("../cryptocurrencies"); const eccs_1 = require("../eccs"); const crypto_1 = require("../crypto"); const utils_1 = require("../utils"); const addresses_1 = require("../addresses"); const seeds_1 = require("../seeds"); const exceptions_1 = require("../exceptions"); const consts_1 = require("../consts"); class CardanoHD extends bip32_1.BIP32HD { cardanoType; constructor(options = { publicKeyType: consts_1.PUBLIC_KEY_TYPES.COMPRESSED }) { options.ecc = eccs_1.KholawEd25519ECC; super(options); if (!options.cardanoType || !cryptocurrencies_1.Cardano.TYPES.getCardanoTypes().includes(options.cardanoType)) { throw new exceptions_1.BaseError('Invalid Cardano type', { expected: cryptocurrencies_1.Cardano.TYPES.getCardanoTypes(), got: options.cardanoType }); } this.cardanoType = options.cardanoType; } static getName() { return 'Cardano'; } fromSeed(seed, passphrase) { try { this.seed = (0, utils_1.toBuffer)(seed instanceof seeds_1.Seed ? seed.getSeed() : seed); } catch { throw new exceptions_1.SeedError('Invalid seed data'); } const digestSize = 64; const hmacHalfLength = digestSize / 2; const tweakMasterKeyBits = (data) => { const d = new Uint8Array(data); d[0] = (0, utils_1.resetBits)(d[0], 0x07); d[31] = (0, utils_1.resetBits)(d[31], this.cardanoType === cryptocurrencies_1.Cardano.TYPES.BYRON_ICARUS || this.cardanoType === cryptocurrencies_1.Cardano.TYPES.SHELLEY_ICARUS ? 0xE0 : 0x80); d[31] = (0, utils_1.setBits)(d[31], 0x40); return d; }; if (this.cardanoType === cryptocurrencies_1.Cardano.TYPES.BYRON_LEGACY) { if (this.seed.length !== 32) { throw new exceptions_1.BaseError('Invalid seed length', { expected: 32, got: this.seed.length }); } const digestSize = 64; const data = (0, cbor2_1.encode)(this.seed); let iteration = 1; while (true) { const label = (0, utils_1.toBuffer)(`Root Seed Chain ${iteration}`); const i = (0, crypto_1.hmacSha512)(data, label); let il = (0, crypto_1.sha512)(i.slice(0, digestSize / 2)); const ir = i.slice(digestSize / 2); il = (0, utils_1.toBuffer)(tweakMasterKeyBits(il)); if (!(0, utils_1.areBitsSet)(il[31], 0x20)) { this.rootPrivateKey = this.ecc.PRIVATE_KEY.fromBytes(il); this.rootChainCode = ir; break; } iteration++; } } else if ([cryptocurrencies_1.Cardano.TYPES.BYRON_ICARUS, cryptocurrencies_1.Cardano.TYPES.SHELLEY_ICARUS].includes(this.cardanoType)) { if (this.seed.length < 16) { throw new exceptions_1.BaseError('Invalid seed length', { expected: '>= 16', got: this.seed.length }); } const k = tweakMasterKeyBits((0, crypto_1.pbkdf2HmacSha512)(passphrase ?? '', this.seed, 4096, 96)); this.rootPrivateKey = this.ecc.PRIVATE_KEY.fromBytes(k.slice(0, eccs_1.KholawEd25519PrivateKey.getLength())); this.rootChainCode = k.slice(eccs_1.KholawEd25519PrivateKey.getLength()); } else if ([cryptocurrencies_1.Cardano.TYPES.BYRON_LEDGER, cryptocurrencies_1.Cardano.TYPES.SHELLEY_LEDGER].includes(this.cardanoType)) { if (this.seed.length < 16) { throw new exceptions_1.BaseError('Invalid seed length', { expected: '>= 16', got: this.seed.length }); } let hmacData = this.seed; let hmac; while (true) { hmac = (0, crypto_1.hmacSha512)((0, utils_1.getHmac)(this.ecc.NAME), hmacData); if ((hmac[31] & 0x20) === 0) break; hmacData = hmac; } let kl = tweakMasterKeyBits(hmac.slice(0, hmacHalfLength)); const kr = hmac.slice(hmacHalfLength); const chainCode = (0, crypto_1.hmacSha256)((0, utils_1.getHmac)(this.ecc.NAME), (0, utils_1.concatBytes)(Buffer.from([0x01]), this.seed)); this.rootPrivateKey = this.ecc.PRIVATE_KEY.fromBytes((0, utils_1.concatBytes)(kl, kr)); this.rootChainCode = chainCode; } this.rootPublicKey = this.rootPrivateKey.getPublicKey(); this.privateKey = this.rootPrivateKey; this.chainCode = this.rootChainCode; this.parentFingerprint = new Uint8Array(4); this.publicKey = this.privateKey.getPublicKey(); this.strict = true; return this; } fromPrivateKey(privateKey) { if ([cryptocurrencies_1.Cardano.TYPES.BYRON_ICARUS, cryptocurrencies_1.Cardano.TYPES.BYRON_LEGACY, cryptocurrencies_1.Cardano.TYPES.BYRON_LEDGER].includes(this.cardanoType)) { throw new exceptions_1.BaseError(`From private key not supported for ${this.cardanoType}`); } try { this.privateKey = this.ecc.PRIVATE_KEY.fromBytes((0, utils_1.getBytes)(privateKey)); this.publicKey = this.privateKey.getPublicKey(); this.strict = null; return this; } catch { throw new exceptions_1.PrivateKeyError('Invalid private key data'); } } fromPublicKey(publicKey) { if ([cryptocurrencies_1.Cardano.TYPES.BYRON_ICARUS, cryptocurrencies_1.Cardano.TYPES.BYRON_LEGACY, cryptocurrencies_1.Cardano.TYPES.BYRON_LEDGER].includes(this.cardanoType)) { throw new exceptions_1.BaseError(`From public key not supported for ${this.cardanoType}`); } try { this.publicKey = this.ecc.PUBLIC_KEY.fromBytes((0, utils_1.getBytes)(publicKey)); this.strict = null; return this; } catch { throw new exceptions_1.PublicKeyError('Invalid public key data'); } } drive(index) { const digestHalf = 32; // sha512().digest_size / 2 const isLegacy = this.cardanoType === cryptocurrencies_1.Cardano.TYPES.BYRON_LEGACY; const indexBytes = (0, utils_1.integerToBytes)(index, 4, isLegacy ? 'big' : 'little'); if (this.privateKey) { let zHmac, hmac; if (index & 0x80000000) { zHmac = (0, crypto_1.hmacSha512)(this.chainCode, (0, utils_1.concatBytes)(Buffer.from([0x00]), this.privateKey.getRaw(), indexBytes)); hmac = (0, crypto_1.hmacSha512)(this.chainCode, (0, utils_1.concatBytes)(Buffer.from([0x01]), this.privateKey.getRaw(), indexBytes)); } else { const pubRaw = this.publicKey.getRawCompressed().slice(1); zHmac = (0, crypto_1.hmacSha512)(this.chainCode, (0, utils_1.concatBytes)(Buffer.from([0x02]), pubRaw, indexBytes)); hmac = (0, crypto_1.hmacSha512)(this.chainCode, (0, utils_1.concatBytes)(Buffer.from([0x03]), pubRaw, indexBytes)); } const zl = zHmac.slice(0, digestHalf); const zr = zHmac.slice(digestHalf); const kl = this.privateKey.getRaw().slice(0, digestHalf); const kr = this.privateKey.getRaw().slice(digestHalf); const _hmacr = hmac.slice(digestHalf); const left = isLegacy ? (0, utils_1.integerToBytes)(((0, utils_1.bytesToInteger)((0, utils_1.multiplyScalarNoCarry)((0, utils_1.toBuffer)(zl), 8), true) + (0, utils_1.bytesToInteger)(kl, true)) % this.ecc.ORDER, 32, 'little') : (() => { const zlInt = (0, utils_1.bytesToInteger)(zl.slice(0, 28), true); const klInt = (0, utils_1.bytesToInteger)(kl, true); const leftInt = zlInt * BigInt(8) + klInt; if (leftInt % this.ecc.ORDER === BigInt(0)) throw new exceptions_1.BaseError('Invalid child private key'); return (0, utils_1.integerToBytes)(leftInt, eccs_1.KholawEd25519PrivateKey.getLength() / 2, 'little'); })(); const right = isLegacy ? (0, utils_1.addNoCarry)((0, utils_1.toBuffer)(zr), (0, utils_1.toBuffer)(kr)) : (() => { const zrInt = (0, utils_1.bytesToInteger)(zr, true); const krInt = (0, utils_1.bytesToInteger)(kr, true); const sum = (zrInt + krInt) % (BigInt(1) << BigInt(256)); return (0, utils_1.integerToBytes)(sum, eccs_1.KholawEd25519PrivateKey.getLength() / 2, 'little'); })(); const newPrivateKey = this.ecc.PRIVATE_KEY.fromBytes((0, utils_1.concatBytes)(left, right)); this.privateKey = newPrivateKey; this.chainCode = _hmacr; this.publicKey = newPrivateKey.getPublicKey(); } else { if (index & 0x80000000) { throw new exceptions_1.DerivationError('Hardened derivation path is invalid for xpublic key'); } const pubRaw = this.publicKey.getRawCompressed().slice(1); const zHmac = (0, crypto_1.hmacSha512)(this.chainCode, (0, utils_1.concatBytes)(Buffer.from([0x02]), pubRaw, indexBytes)); const hmac = (0, crypto_1.hmacSha512)(this.chainCode, (0, utils_1.concatBytes)(Buffer.from([0x03]), pubRaw, indexBytes)); const zl = zHmac.slice(0, digestHalf); const tweak = isLegacy ? (0, utils_1.bytesToInteger)((0, utils_1.multiplyScalarNoCarry)(zl, 8), true) : (0, utils_1.bytesToInteger)(zl.slice(0, 28), true) * BigInt(8); const newPoint = this.publicKey.getPoint().add(this.ecc.GENERATOR.multiply(tweak)); if (newPoint.getX() === BigInt(0) && newPoint.getY() === BigInt(1)) { throw new exceptions_1.BaseError('Computed public child key is not valid, very unlucky index'); } this.publicKey = this.ecc.PUBLIC_KEY.fromPoint(newPoint); this.chainCode = hmac.slice(digestHalf); } this.parentFingerprint = (0, utils_1.getBytes)(this.getFingerprint()); this.depth += 1; this.index = index; this.fingerprint = (0, utils_1.getBytes)(this.getFingerprint()); return this; } getRootXPrivateKey(version = cryptocurrencies_1.Cardano.NETWORKS.MAINNET.XPRIVATE_KEY_VERSIONS.P2PKH, encoded = true) { return super.getRootXPrivateKey(version, encoded); } getXPrivateKey(version = cryptocurrencies_1.Cardano.NETWORKS.MAINNET.XPRIVATE_KEY_VERSIONS.P2PKH, encoded = true) { return super.getXPrivateKey(version, encoded); } getPathKey() { if (this.cardanoType === cryptocurrencies_1.Cardano.TYPES.BYRON_LEGACY) { return (0, utils_1.bytesToString)((0, crypto_1.pbkdf2HmacSha512)((0, utils_1.concatBytes)(this.rootPublicKey.getRawCompressed().slice(1), this.rootChainCode), 'address-hashing', 500, 32)); } return null; } getAddress(options = { network: 'mainnet' }) { if (this.cardanoType === cryptocurrencies_1.Cardano.TYPES.BYRON_LEGACY) { return addresses_1.CardanoAddress.encodeByronLegacy(this.publicKey, this.getPath(), this.getPathKey(), this.chainCode, options.addressType ?? cryptocurrencies_1.Cardano.ADDRESS_TYPES.PUBLIC_KEY); } else if ([cryptocurrencies_1.Cardano.TYPES.BYRON_ICARUS, cryptocurrencies_1.Cardano.TYPES.BYRON_LEDGER].includes(this.cardanoType)) { return addresses_1.CardanoAddress.encodeByronIcarus(this.publicKey, this.chainCode, options.addressType ?? cryptocurrencies_1.Cardano.ADDRESS_TYPES.PUBLIC_KEY); } else if ([cryptocurrencies_1.Cardano.TYPES.SHELLEY_ICARUS, cryptocurrencies_1.Cardano.TYPES.SHELLEY_LEDGER].includes(this.cardanoType)) { const addressType = options.addressType ?? cryptocurrencies_1.Cardano.ADDRESS_TYPES.PAYMENT; if (addressType === cryptocurrencies_1.Cardano.ADDRESS_TYPES.PAYMENT) { if (!options.stakingPublicKey) { throw new exceptions_1.BaseError('stakingPublicKey is required for Payment address type'); } return addresses_1.CardanoAddress.encodeShelley(this.publicKey, options.stakingPublicKey, options.network ?? 'mainnet'); } else if ([cryptocurrencies_1.Cardano.ADDRESS_TYPES.STAKING, cryptocurrencies_1.Cardano.ADDRESS_TYPES.REWARD].includes(addressType)) { return addresses_1.CardanoAddress.encodeShelleyStaking(this.publicKey, options.network ?? 'mainnet'); } throw new exceptions_1.AddressError(`Invalid ${this.cardanoType} address type`, { expected: [ cryptocurrencies_1.Cardano.ADDRESS_TYPES.PAYMENT, cryptocurrencies_1.Cardano.ADDRESS_TYPES.STAKING, cryptocurrencies_1.Cardano.ADDRESS_TYPES.REWARD ], got: addressType }); } throw new exceptions_1.AddressError(`Invalid Cardano type`, { expected: cryptocurrencies_1.Cardano.TYPES.getCardanoTypes(), got: this.cardanoType }); } } exports.CardanoHD = CardanoHD; //# sourceMappingURL=cardano.js.map