UNPKG

@ecash/lib

Version:

Library for eCash transaction building

123 lines 4.7 kB
"use strict"; // Copyright (c) 2025 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. Object.defineProperty(exports, "__esModule", { value: true }); exports.mnemonicToSeed = exports.mnemonicToEntropy = exports.entropyToMnemonic = void 0; const hash_1 = require("./hash"); const str_js_1 = require("./io/str.js"); const pbkdf2_1 = require("./pbkdf2"); const BITS_PER_BYTE = 8; const BITS_PER_WORD = 11; const BITS_PER_CHECKSUM_BIT = 32; // Calculate how many bits there are in the mnemonic function calcNumChecksumBits(numEntropyBytes) { return (numEntropyBytes * BITS_PER_BYTE) / BITS_PER_CHECKSUM_BIT; } // Normalize according to unicode standard function normalize(str) { return (str || '').normalize('NFKD'); } // Turn the password into a salt for seed generation function salt(password) { return 'mnemonic' + (password || ''); } /** Derive the mnemonic from entropy */ function entropyToMnemonic(entropy, wordlist) { if (entropy.length < 16 || entropy.length > 32) { throw new TypeError('Entropy must be between 16 and 32 bytes long'); } if (entropy.length % 4 !== 0) { throw new TypeError('Entropy length must be divisible by 4'); } const checksum = (0, hash_1.sha256)(entropy); const data = new Uint8Array(entropy.length + checksum.length); data.set(entropy, 0); data.set(checksum, entropy.length); let nextBits = 0; let numBits = 0; let numLeftoverBits = entropy.length * BITS_PER_BYTE + calcNumChecksumBits(entropy.length); const words = []; for (const byte of data) { nextBits = (nextBits << BITS_PER_BYTE) | byte; numBits += BITS_PER_BYTE; if (numBits >= BITS_PER_WORD) { const wordIdx = nextBits >> (numBits - BITS_PER_WORD); words.push(wordlist.words[wordIdx]); if (numLeftoverBits <= BITS_PER_WORD) { break; } numBits -= BITS_PER_WORD; numLeftoverBits -= BITS_PER_WORD; nextBits &= 0x7ff >> (BITS_PER_WORD - numBits); } } return words.join(wordlist.separator); } exports.entropyToMnemonic = entropyToMnemonic; /** Recover the entropy from the mnemonic */ function mnemonicToEntropy(phrase, wordlist) { const words = normalize(phrase).split(' '); if (words.length < 12 || words.length > 24) { throw new Error('Number of words in mnemonic phrase must be between 12 and 24'); } if (words.length % 3 !== 0) { throw new Error('Number of words in mnemonic phrase must be divisible by 3'); } const wordIndices = words.map(word => { const idx = wordlist.indexOf(word); if (idx === -1) { throw new Error('Invalid mnemonic phrase word: ' + word); } return idx; }); const numEntropyBytes = (wordIndices.length / 3) * 4; let nextBits = 0; let numBits = 0; let idx = 0; const entropy = new Uint8Array(numEntropyBytes); let checksum = 0; for (const wordIdx of wordIndices) { nextBits = (nextBits << BITS_PER_WORD) | wordIdx; numBits += BITS_PER_WORD; while (numBits >= BITS_PER_BYTE) { const byte = nextBits >> (numBits - BITS_PER_BYTE); if (idx < entropy.length) { entropy[idx] = byte; } else { checksum = (checksum << BITS_PER_BYTE) | byte; } idx++; numBits -= BITS_PER_BYTE; nextBits &= 0xffff >> (16 - numBits); } } if (numBits != 0) { checksum = (checksum << BITS_PER_BYTE) | nextBits; } const entropyHash = (0, hash_1.sha256)(entropy); const numChecksumBits = calcNumChecksumBits(numEntropyBytes); const expectedChecksum = entropyHash[0] >> (BITS_PER_BYTE - numChecksumBits); if (checksum != expectedChecksum) { const expected = expectedChecksum.toString(16); const actual = checksum.toString(16); throw new Error(`Invalid checksum: expected ${expected}, got ${actual}`); } return entropy; } exports.mnemonicToEntropy = mnemonicToEntropy; /** Derive the seed bytes from the mnemonic */ function mnemonicToSeed(phrase, password) { return (0, pbkdf2_1.pbkdf2)({ hashFactory: hash_1.sha512Hasher, password: (0, str_js_1.strToBytes)(normalize(phrase)), salt: (0, str_js_1.strToBytes)(salt(normalize(password))), blockLength: 128, outputLength: 64, dkLen: 64, iterations: 2048, }); } exports.mnemonicToSeed = mnemonicToSeed; //# sourceMappingURL=mnemonic.js.map