You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
142 lines
5.8 KiB
142 lines
5.8 KiB
"use strict"; |
|
Object.defineProperty(exports, "__esModule", { value: true }); |
|
exports.mnemonicToSeedSync = exports.mnemonicToSeed = exports.validateMnemonic = exports.entropyToMnemonic = exports.mnemonicToEntropy = exports.generateMnemonic = void 0; |
|
/*! scure-bip39 - MIT License (c) 2022 Patricio Palladino, Paul Miller (paulmillr.com) */ |
|
const _assert_1 = require("@noble/hashes/_assert"); |
|
const pbkdf2_1 = require("@noble/hashes/pbkdf2"); |
|
const sha256_1 = require("@noble/hashes/sha256"); |
|
const sha512_1 = require("@noble/hashes/sha512"); |
|
const utils_1 = require("@noble/hashes/utils"); |
|
const base_1 = require("@scure/base"); |
|
// Japanese wordlist |
|
const isJapanese = (wordlist) => wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093'; |
|
// Normalization replaces equivalent sequences of characters |
|
// so that any two texts that are equivalent will be reduced |
|
// to the same sequence of code points, called the normal form of the original text. |
|
function nfkd(str) { |
|
if (typeof str !== 'string') |
|
throw new TypeError(`Invalid mnemonic type: ${typeof str}`); |
|
return str.normalize('NFKD'); |
|
} |
|
function normalize(str) { |
|
const norm = nfkd(str); |
|
const words = norm.split(' '); |
|
if (![12, 15, 18, 21, 24].includes(words.length)) |
|
throw new Error('Invalid mnemonic'); |
|
return { nfkd: norm, words }; |
|
} |
|
function assertEntropy(entropy) { |
|
_assert_1.default.bytes(entropy, 16, 20, 24, 28, 32); |
|
} |
|
/** |
|
* Generate x random words. Uses Cryptographically-Secure Random Number Generator. |
|
* @param wordlist imported wordlist for specific language |
|
* @param strength mnemonic strength 128-256 bits |
|
* @example |
|
* generateMnemonic(wordlist, 128) |
|
* // 'legal winner thank year wave sausage worth useful legal winner thank yellow' |
|
*/ |
|
function generateMnemonic(wordlist, strength = 128) { |
|
_assert_1.default.number(strength); |
|
if (strength % 32 !== 0 || strength > 256) |
|
throw new TypeError('Invalid entropy'); |
|
return entropyToMnemonic((0, utils_1.randomBytes)(strength / 8), wordlist); |
|
} |
|
exports.generateMnemonic = generateMnemonic; |
|
const calcChecksum = (entropy) => { |
|
// Checksum is ent.length/4 bits long |
|
const bitsLeft = 8 - entropy.length / 4; |
|
// Zero rightmost "bitsLeft" bits in byte |
|
// For example: bitsLeft=4 val=10111101 -> 10110000 |
|
return new Uint8Array([((0, sha256_1.sha256)(entropy)[0] >> bitsLeft) << bitsLeft]); |
|
}; |
|
function getCoder(wordlist) { |
|
if (!Array.isArray(wordlist) || wordlist.length !== 2048 || typeof wordlist[0] !== 'string') |
|
throw new Error('Worlist: expected array of 2048 strings'); |
|
wordlist.forEach((i) => { |
|
if (typeof i !== 'string') |
|
throw new Error(`Wordlist: non-string element: ${i}`); |
|
}); |
|
return base_1.utils.chain(base_1.utils.checksum(1, calcChecksum), base_1.utils.radix2(11, true), base_1.utils.alphabet(wordlist)); |
|
} |
|
/** |
|
* Reversible: Converts mnemonic string to raw entropy in form of byte array. |
|
* @param mnemonic 12-24 words |
|
* @param wordlist imported wordlist for specific language |
|
* @example |
|
* const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; |
|
* mnemonicToEntropy(mnem, wordlist) |
|
* // Produces |
|
* new Uint8Array([ |
|
* 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, |
|
* 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f |
|
* ]) |
|
*/ |
|
function mnemonicToEntropy(mnemonic, wordlist) { |
|
const { words } = normalize(mnemonic); |
|
const entropy = getCoder(wordlist).decode(words); |
|
assertEntropy(entropy); |
|
return entropy; |
|
} |
|
exports.mnemonicToEntropy = mnemonicToEntropy; |
|
/** |
|
* Reversible: Converts raw entropy in form of byte array to mnemonic string. |
|
* @param entropy byte array |
|
* @param wordlist imported wordlist for specific language |
|
* @returns 12-24 words |
|
* @example |
|
* const ent = new Uint8Array([ |
|
* 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, |
|
* 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f |
|
* ]); |
|
* entropyToMnemonic(ent, wordlist); |
|
* // 'legal winner thank year wave sausage worth useful legal winner thank yellow' |
|
*/ |
|
function entropyToMnemonic(entropy, wordlist) { |
|
assertEntropy(entropy); |
|
const words = getCoder(wordlist).encode(entropy); |
|
return words.join(isJapanese(wordlist) ? '\u3000' : ' '); |
|
} |
|
exports.entropyToMnemonic = entropyToMnemonic; |
|
/** |
|
* Validates mnemonic for being 12-24 words contained in `wordlist`. |
|
*/ |
|
function validateMnemonic(mnemonic, wordlist) { |
|
try { |
|
mnemonicToEntropy(mnemonic, wordlist); |
|
} |
|
catch (e) { |
|
return false; |
|
} |
|
return true; |
|
} |
|
exports.validateMnemonic = validateMnemonic; |
|
const salt = (passphrase) => nfkd(`mnemonic${passphrase}`); |
|
/** |
|
* Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. |
|
* @param mnemonic 12-24 words |
|
* @param passphrase string that will additionally protect the key |
|
* @returns 64 bytes of key data |
|
* @example |
|
* const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; |
|
* await mnemonicToSeed(mnem, 'password'); |
|
* // new Uint8Array([...64 bytes]) |
|
*/ |
|
function mnemonicToSeed(mnemonic, passphrase = '') { |
|
return (0, pbkdf2_1.pbkdf2Async)(sha512_1.sha512, normalize(mnemonic).nfkd, salt(passphrase), { c: 2048, dkLen: 64 }); |
|
} |
|
exports.mnemonicToSeed = mnemonicToSeed; |
|
/** |
|
* Irreversible: Uses KDF to derive 64 bytes of key data from mnemonic + optional password. |
|
* @param mnemonic 12-24 words |
|
* @param passphrase string that will additionally protect the key |
|
* @returns 64 bytes of key data |
|
* @example |
|
* const mnem = 'legal winner thank year wave sausage worth useful legal winner thank yellow'; |
|
* mnemonicToSeedSync(mnem, 'password'); |
|
* // new Uint8Array([...64 bytes]) |
|
*/ |
|
function mnemonicToSeedSync(mnemonic, passphrase = '') { |
|
return (0, pbkdf2_1.pbkdf2)(sha512_1.sha512, normalize(mnemonic).nfkd, salt(passphrase), { c: 2048, dkLen: 64 }); |
|
} |
|
exports.mnemonicToSeedSync = mnemonicToSeedSync;
|
|
|