ethers.js/lib.commonjs/wallet/mnemonic.js

169 lines
6.5 KiB
JavaScript
Raw Permalink Normal View History

2022-09-05 16:57:11 -04:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Mnemonic = void 0;
const index_js_1 = require("../crypto/index.js");
const index_js_2 = require("../utils/index.js");
const lang_en_js_1 = require("../wordlists/lang-en.js");
// Returns a byte with the MSB bits set
function getUpperMask(bits) {
return ((1 << bits) - 1) << (8 - bits) & 0xff;
}
// Returns a byte with the LSB bits set
function getLowerMask(bits) {
return ((1 << bits) - 1) & 0xff;
}
2022-11-30 15:44:23 -05:00
function mnemonicToEntropy(mnemonic, wordlist) {
2022-09-15 22:58:45 -04:00
(0, index_js_2.assertNormalize)("NFKD");
2022-09-05 16:57:11 -04:00
if (wordlist == null) {
2022-11-30 15:44:23 -05:00
wordlist = lang_en_js_1.LangEn.wordlist();
2022-09-05 16:57:11 -04:00
}
const words = wordlist.split(mnemonic);
2022-11-09 02:57:02 -05:00
(0, index_js_2.assertArgument)((words.length % 3) === 0 && words.length >= 12 && words.length <= 24, "invalid mnemonic length", "mnemonic", "[ REDACTED ]");
2022-09-05 16:57:11 -04:00
const entropy = new Uint8Array(Math.ceil(11 * words.length / 8));
let offset = 0;
for (let i = 0; i < words.length; i++) {
let index = wordlist.getWordIndex(words[i].normalize("NFKD"));
2022-11-09 02:57:02 -05:00
(0, index_js_2.assertArgument)(index >= 0, `invalid mnemonic word at index ${i}`, "mnemonic", "[ REDACTED ]");
2022-09-05 16:57:11 -04:00
for (let bit = 0; bit < 11; bit++) {
if (index & (1 << (10 - bit))) {
entropy[offset >> 3] |= (1 << (7 - (offset % 8)));
}
offset++;
}
}
const entropyBits = 32 * words.length / 3;
const checksumBits = words.length / 3;
const checksumMask = getUpperMask(checksumBits);
2022-09-15 22:58:45 -04:00
const checksum = (0, index_js_2.getBytes)((0, index_js_1.sha256)(entropy.slice(0, entropyBits / 8)))[0] & checksumMask;
2022-11-09 02:57:02 -05:00
(0, index_js_2.assertArgument)(checksum === (entropy[entropy.length - 1] & checksumMask), "invalid mnemonic checksum", "mnemonic", "[ REDACTED ]");
2022-09-05 16:57:11 -04:00
return (0, index_js_2.hexlify)(entropy.slice(0, entropyBits / 8));
}
2022-11-30 15:44:23 -05:00
function entropyToMnemonic(entropy, wordlist) {
2022-11-09 02:57:02 -05:00
(0, index_js_2.assertArgument)((entropy.length % 4) === 0 && entropy.length >= 16 && entropy.length <= 32, "invalid entropy size", "entropy", "[ REDACTED ]");
2022-09-05 16:57:11 -04:00
if (wordlist == null) {
2022-11-30 15:44:23 -05:00
wordlist = lang_en_js_1.LangEn.wordlist();
2022-09-05 16:57:11 -04:00
}
const indices = [0];
let remainingBits = 11;
for (let i = 0; i < entropy.length; i++) {
// Consume the whole byte (with still more to go)
if (remainingBits > 8) {
indices[indices.length - 1] <<= 8;
indices[indices.length - 1] |= entropy[i];
remainingBits -= 8;
// This byte will complete an 11-bit index
}
else {
indices[indices.length - 1] <<= remainingBits;
indices[indices.length - 1] |= entropy[i] >> (8 - remainingBits);
// Start the next word
indices.push(entropy[i] & getLowerMask(8 - remainingBits));
remainingBits += 3;
}
}
// Compute the checksum bits
const checksumBits = entropy.length / 4;
const checksum = parseInt((0, index_js_1.sha256)(entropy).substring(2, 4), 16) & getUpperMask(checksumBits);
// Shift the checksum into the word indices
indices[indices.length - 1] <<= checksumBits;
indices[indices.length - 1] |= (checksum >> (8 - checksumBits));
return wordlist.join(indices.map((index) => wordlist.getWord(index)));
}
const _guard = {};
2022-12-02 21:27:06 -05:00
/**
2023-02-12 22:14:26 -05:00
* A **Mnemonic** wraps all properties required to compute [[link-bip-39]]
2022-12-02 21:27:06 -05:00
* seeds and convert between phrases and entropy.
*/
2022-09-05 16:57:11 -04:00
class Mnemonic {
2022-12-02 21:27:06 -05:00
/**
* The mnemonic phrase of 12, 15, 18, 21 or 24 words.
*
* Use the [[wordlist]] ``split`` method to get the individual words.
*/
2022-09-05 16:57:11 -04:00
phrase;
2022-12-02 21:27:06 -05:00
/**
* The password used for this mnemonic. If no password is used this
* is the empty string (i.e. ``""``) as per the specification.
*/
2022-09-05 16:57:11 -04:00
password;
2022-12-02 21:27:06 -05:00
/**
* The wordlist for this mnemonic.
*/
2022-09-05 16:57:11 -04:00
wordlist;
2022-12-02 21:27:06 -05:00
/**
* The underlying entropy which the mnemonic encodes.
*/
2022-09-05 16:57:11 -04:00
entropy;
2022-11-30 15:44:23 -05:00
/**
* @private
*/
2022-09-05 16:57:11 -04:00
constructor(guard, entropy, phrase, password, wordlist) {
if (password == null) {
password = "";
}
if (wordlist == null) {
2022-11-30 15:44:23 -05:00
wordlist = lang_en_js_1.LangEn.wordlist();
2022-09-05 16:57:11 -04:00
}
2022-09-15 22:58:45 -04:00
(0, index_js_2.assertPrivate)(guard, _guard, "Mnemonic");
2022-09-05 16:57:11 -04:00
(0, index_js_2.defineProperties)(this, { phrase, password, wordlist, entropy });
}
2022-12-02 21:27:06 -05:00
/**
* Returns the seed for the mnemonic.
*/
2022-09-05 16:57:11 -04:00
computeSeed() {
const salt = (0, index_js_2.toUtf8Bytes)("mnemonic" + this.password, "NFKD");
return (0, index_js_1.pbkdf2)((0, index_js_2.toUtf8Bytes)(this.phrase, "NFKD"), salt, 2048, 64, "sha512");
}
2022-12-02 21:27:06 -05:00
/**
* Creates a new Mnemonic for the %%phrase%%.
*
* The default %%password%% is the empty string and the default
* wordlist is the [English wordlists](LangEn).
*/
2022-09-05 16:57:11 -04:00
static fromPhrase(phrase, password, wordlist) {
// Normalize the case and space; throws if invalid
const entropy = mnemonicToEntropy(phrase, wordlist);
2022-09-15 22:58:45 -04:00
phrase = entropyToMnemonic((0, index_js_2.getBytes)(entropy), wordlist);
2022-09-05 16:57:11 -04:00
return new Mnemonic(_guard, entropy, phrase, password, wordlist);
}
2022-12-02 21:27:06 -05:00
/**
* Create a new **Mnemonic** from the %%entropy%%.
*
* The default %%password%% is the empty string and the default
* wordlist is the [English wordlists](LangEn).
*/
2022-09-05 16:57:11 -04:00
static fromEntropy(_entropy, password, wordlist) {
2022-09-15 22:58:45 -04:00
const entropy = (0, index_js_2.getBytes)(_entropy, "entropy");
2022-09-05 16:57:11 -04:00
const phrase = entropyToMnemonic(entropy, wordlist);
return new Mnemonic(_guard, (0, index_js_2.hexlify)(entropy), phrase, password, wordlist);
}
2022-12-02 21:27:06 -05:00
/**
* Returns the phrase for %%mnemonic%%.
*/
2022-09-05 16:57:11 -04:00
static entropyToPhrase(_entropy, wordlist) {
2022-09-15 22:58:45 -04:00
const entropy = (0, index_js_2.getBytes)(_entropy, "entropy");
2022-09-05 16:57:11 -04:00
return entropyToMnemonic(entropy, wordlist);
}
2022-12-02 21:27:06 -05:00
/**
* Returns the entropy for %%phrase%%.
*/
2022-09-05 16:57:11 -04:00
static phraseToEntropy(phrase, wordlist) {
return mnemonicToEntropy(phrase, wordlist);
}
2022-12-02 21:27:06 -05:00
/**
2023-02-12 22:14:26 -05:00
* Returns true if %%phrase%% is a valid [[link-bip-39]] phrase.
2022-12-02 21:27:06 -05:00
*
* This checks all the provided words belong to the %%wordlist%%,
* that the length is valid and the checksum is correct.
*/
2022-09-05 16:57:11 -04:00
static isValidMnemonic(phrase, wordlist) {
try {
mnemonicToEntropy(phrase, wordlist);
return true;
}
catch (error) { }
return false;
}
}
exports.Mnemonic = Mnemonic;
//# sourceMappingURL=mnemonic.js.map