@bsv/sdk
Version:
BSV Blockchain Software Development Kit
304 lines • 12.4 kB
JavaScript
"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