UNPKG

@bsv/sdk

Version:

BSV Blockchain Software Development Kit

304 lines 12.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const bip_39_wordlist_en_js_1 = require("./bip-39-wordlist-en.js"); const utils_js_1 = require("../primitives/utils.js"); const Hash = __importStar(require("../primitives/Hash.js")); const Random_js_1 = __importDefault(require("../primitives/Random.js")); /** * @class Mnemonic * * @description * Class representing Mnemonic functionality. * This class provides methods for generating, converting, and validating mnemonic phrases * according to the BIP39 standard. It supports creating mnemonics from random entropy, * converting mnemonics to seeds, and validating mnemonic phrases. */ class Mnemonic { /** * Constructs a Mnemonic object. * @param {string} [mnemonic] - An optional mnemonic phrase. * @param {number[]} [seed] - An optional seed derived from the mnemonic. * @param {object} [wordlist=wordList] - An object containing a list of words and space character used in the mnemonic. */ constructor(mnemonic, seed, wordlist = bip_39_wordlist_en_js_1.wordList) { this.mnemonic = mnemonic ?? ''; // Default to empty string if undefined this.seed = seed ?? []; // Default to empty array if undefined this.Wordlist = wordlist; } /** * Converts the mnemonic and seed into a binary representation. * @returns {number[]} The binary representation of the mnemonic and seed. */ toBinary() { const bw = new utils_js_1.Writer(); if (this.mnemonic !== '') { const buf = (0, utils_js_1.toArray)(this.mnemonic, 'utf8'); bw.writeVarIntNum(buf.length); bw.write(buf); } else { bw.writeVarIntNum(0); } if (this.seed.length > 0) { bw.writeVarIntNum(this.seed.length); bw.write(this.seed); } else { bw.writeVarIntNum(0); } return bw.toArray(); } /** * Loads a mnemonic and seed from a binary representation. * @param {number[]} bin - The binary representation of a mnemonic and seed. * @returns {this} The Mnemonic instance with loaded mnemonic and seed. */ fromBinary(bin) { const br = new utils_js_1.Reader(bin); const mnemoniclen = br.readVarIntNum(); if (mnemoniclen > 0) { this.mnemonic = (0, utils_js_1.encode)(br.read(mnemoniclen), 'utf8'); } const seedlen = br.readVarIntNum(); if (seedlen > 0) { this.seed = br.read(seedlen); } return this; } /** * Generates a random mnemonic from a given bit length. * @param {number} [bits=128] - The bit length for the random mnemonic (must be a multiple of 32 and at least 128). * @returns {this} The Mnemonic instance with the new random mnemonic. * @throws {Error} If the bit length is not a multiple of 32 or is less than 128. */ fromRandom(bits) { if (bits === undefined || bits === null || isNaN(bits) || bits === 0) { bits = 128; } if (bits % 32 !== 0) { throw new Error('bits must be multiple of 32'); } if (bits < 128) { throw new Error('bits must be at least 128'); } const buf = (0, Random_js_1.default)(bits / 8); this.entropy2Mnemonic(buf); this.mnemonic2Seed(); return this; } /** * Static method to generate a Mnemonic instance with a random mnemonic. * @param {number} [bits=128] - The bit length for the random mnemonic. * @returns {Mnemonic} A new Mnemonic instance. */ static fromRandom(bits) { return new this().fromRandom(bits); } /** * Converts given entropy into a mnemonic phrase. * This method is used to generate a mnemonic from a specific entropy source. * @param {number[]} buf - The entropy buffer, must be at least 128 bits. * @returns {this} The Mnemonic instance with the mnemonic set from the given entropy. * @throws {Error} If the entropy is less than 128 bits. */ fromEntropy(buf) { this.entropy2Mnemonic(buf); return this; } /** * Static method to create a Mnemonic instance from a given entropy. * @param {number[]} buf - The entropy buffer. * @returns {Mnemonic} A new Mnemonic instance. */ static fromEntropy(buf) { return new this().fromEntropy(buf); } /** * Sets the mnemonic for the instance from a string. * @param {string} mnemonic - The mnemonic phrase as a string. * @returns {this} The Mnemonic instance with the set mnemonic. */ fromString(mnemonic) { this.mnemonic = mnemonic; return this; } /** * Static method to create a Mnemonic instance from a mnemonic string. * @param {string} str - The mnemonic phrase. * @returns {Mnemonic} A new Mnemonic instance. */ static fromString(str) { return new this().fromString(str); } /** * Converts the instance's mnemonic to a string representation. * @returns {string} The mnemonic phrase as a string. */ toString() { return this.mnemonic; } /** * Converts the mnemonic to a seed. * The mnemonic must pass the validity check before conversion. * @param {string} [passphrase=''] - An optional passphrase for additional security. * @returns {number[]} The generated seed. * @throws {Error} If the mnemonic is invalid. */ toSeed(passphrase) { this.mnemonic2Seed(passphrase); return this.seed; } /** * Converts entropy to a mnemonic phrase. * This method takes a buffer of entropy and converts it into a corresponding * mnemonic phrase based on the Mnemonic wordlist. The entropy should be at least 128 bits. * The method applies a checksum and maps the entropy to words in the wordlist. * @param {number[]} buf - The entropy buffer to convert. Must be at least 128 bits. * @returns {this} The Mnemonic instance with the mnemonic set from the entropy. * @throws {Error} If the entropy is less than 128 bits or if it's not an even multiple of 11 bits. */ entropy2Mnemonic(buf) { if (buf.length < 128 / 8) { throw new Error('Entropy is less than 128 bits. It must be 128 bits or more.'); } const hash = Hash.sha256(buf); let bin = ''; const bits = buf.length * 8; for (let i = 0; i < buf.length; i++) { bin = bin + ('00000000' + buf[i].toString(2)).slice(-8); } let hashbits = hash[0].toString(2); hashbits = ('00000000' + hashbits).slice(-8).slice(0, bits / 32); bin = bin + hashbits; if (bin.length % 11 !== 0) { throw new Error('internal error - entropy not an even multiple of 11 bits - ' + bin.length.toString()); } let mnemonic = ''; for (let i = 0; i < bin.length / 11; i++) { if (mnemonic !== '') { mnemonic = mnemonic + this.Wordlist.space; } const wi = parseInt(bin.slice(i * 11, (i + 1) * 11), 2); mnemonic = mnemonic + this.Wordlist.value[wi]; } this.mnemonic = mnemonic; return this; } /** * Validates the mnemonic phrase. * Checks for correct length, absence of invalid words, and proper checksum. * @returns {boolean} True if the mnemonic is valid, false otherwise. * @throws {Error} If the mnemonic is not an even multiple of 11 bits. */ check() { const mnemonic = this.mnemonic; // confirm no invalid words const words = mnemonic.split(this.Wordlist.space); let bin = ''; for (let i = 0; i < words.length; i++) { const ind = this.Wordlist.value.indexOf(words[i]); if (ind < 0) { return false; } bin = bin + ('00000000000' + ind.toString(2)).slice(-11); } if (bin.length % 11 !== 0) { throw new Error('internal error - entropy not an even multiple of 11 bits - ' + bin.length.toString()); } // confirm checksum const cs = bin.length / 33; const hashBits = bin.slice(-cs); const nonhashBits = bin.slice(0, bin.length - cs); const buf = []; for (let i = 0; i < nonhashBits.length / 8; i++) { buf.push(parseInt(bin.slice(i * 8, (i + 1) * 8), 2)); } const hash = Hash.sha256(buf.slice(0, nonhashBits.length / 8)); let expectedHashBits = hash[0].toString(2); expectedHashBits = ('00000000' + expectedHashBits).slice(-8).slice(0, cs); return expectedHashBits === hashBits; } /** * Converts a mnemonic to a seed. * This method takes the instance's mnemonic phrase, combines it with a passphrase (if provided), * and uses PBKDF2 to generate a seed. It also validates the mnemonic before conversion. * This seed can then be used for generating deterministic keys. * @param {string} [passphrase=''] - An optional passphrase for added security. * @returns {this} The Mnemonic instance with the seed generated from the mnemonic. * @throws {Error} If the mnemonic does not pass validation or if the passphrase is not a string. */ mnemonic2Seed(passphrase = '') { let mnemonic = this.mnemonic; if (!this.check()) { throw new Error('Mnemonic does not pass the check - was the mnemonic typed incorrectly? Are there extra spaces?'); } if (typeof passphrase !== 'string') { throw new Error('passphrase must be a string or undefined'); } mnemonic = mnemonic.normalize('NFKD'); passphrase = passphrase.normalize('NFKD'); const mbuf = (0, utils_js_1.toArray)(mnemonic, 'utf8'); const pbuf = [ ...(0, utils_js_1.toArray)('mnemonic', 'utf8'), ...(0, utils_js_1.toArray)(passphrase, 'utf8') ]; this.seed = Hash.pbkdf2(mbuf, pbuf, 2048, 64, 'sha512'); return this; } /** * Determines the validity of a given passphrase with the mnemonic. * This method is useful for checking if a passphrase matches with the mnemonic. * @param {string} [passphrase=''] - The passphrase to validate. * @returns {boolean} True if the mnemonic and passphrase combination is valid, false otherwise. */ isValid(passphrase = '') { let isValid; try { this.mnemonic2Seed(passphrase); isValid = true; } catch { isValid = false; } return isValid; } /** * Static method to check the validity of a given mnemonic and passphrase combination. * @param {string} mnemonic - The mnemonic phrase. * @param {string} [passphrase=''] - The passphrase to validate. * @returns {boolean} True if the combination is valid, false otherwise. */ static isValid(mnemonic, passphrase = '') { return new Mnemonic(mnemonic).isValid(passphrase); } } exports.default = Mnemonic; //# sourceMappingURL=Mnemonic.js.map