\r
import { Bip39Words } from './bip39-wordlist'\r
import { BIP39_ITERATIONS } from './constants'\r
-import { bin, bytes, dec, hex, utf8 } from './convert'\r
-import { Entropy } from './entropy'\r
-import { Key } from '#types'\r
+import { bytes, hex, utf8 } from './convert'\r
\r
/**\r
* Represents a mnemonic phrase that identifies a wallet as defined by BIP-39.\r
*/\r
export class Bip39Mnemonic {\r
static #isInternal: boolean = false\r
+\r
+ /**\r
+ * SHA-256 hash of entropy that is appended to the entropy and subsequently\r
+ * used to generate the mnemonic phrase.\r
+ *\r
+ * @param {Entropy} entropy - Cryptographically strong pseudorandom data of length N bits\r
+ * @returns {Promise<string>} First N/32 bits of the hash as a hexadecimal string\r
+ */\r
+ static async #checksum (entropy: Uint8Array<ArrayBuffer>): Promise<bigint> {\r
+ const sha256sum = new Uint8Array(await crypto.subtle.digest('SHA-256', entropy))[0]\r
+ const checksumBitLength = BigInt(entropy.byteLength) / 4n\r
+ const checksum = BigInt(sha256sum) >> (8n - checksumBitLength)\r
+ return checksum\r
+ }\r
+\r
+ /**\r
+ * Validates a mnemonic phrase.\r
+ *\r
+ * @param {string} mnemonic - Mnemonic phrase to validate\r
+ * @returns {boolean} True if the mnemonic phrase is valid\r
+ */\r
+ static async validate (mnemonic: string): Promise<boolean> {\r
+ const words = mnemonic.normalize('NFKD').split(' ')\r
+ if (words.length % 3 !== 0) {\r
+ return false\r
+ }\r
+ let bits = 0n, bitLength = 0n\r
+ for (const word of words) {\r
+ const wordIndex = Bip39Words.indexOf(word)\r
+ if (wordIndex === -1) {\r
+ return false\r
+ }\r
+ bits = (bits << 11n) | BigInt(Bip39Words.indexOf(word))\r
+ bitLength += 11n\r
+ }\r
+ if (Number(bitLength) % 33 !== 0) {\r
+ return false\r
+ }\r
+ const checksumLength = bitLength / 33n\r
+ const entropyLength = bitLength - checksumLength\r
+ const entropyBits = bits >> checksumLength\r
+ const checksumBits = bits & ((1n << checksumLength) - 1n)\r
+ if (entropyBits == null\r
+ || entropyBits < 0n\r
+ || entropyBits > (1n << 256n) - 1n\r
+ || entropyLength < 128n\r
+ || entropyLength > 256n\r
+ || Number(entropyLength) % 32 !== 0\r
+ ) {\r
+ return false\r
+ }\r
+ const bytes = new Uint8Array(Number(entropyLength) / 8)\r
+ for (let i = 0; i < bytes.length; i++) {\r
+ const shift = entropyLength - (8n * BigInt(i + 1))\r
+ const byte = (entropyBits >> shift) & 255n\r
+ bytes[i] = Number(byte)\r
+ }\r
+ const expectedChecksum = await this.#checksum(bytes)\r
+ if (expectedChecksum !== checksumBits) {\r
+ return false\r
+ }\r
+ return true\r
+ }\r
+\r
#bip39Seed?: Uint8Array<ArrayBuffer>\r
#blake2bSeed?: Uint8Array<ArrayBuffer>\r
#phrase?: string[]\r
* @param {string} entropy - Hexadecimal string\r
* @returns {string} Mnemonic phrase created using the BIP-39 wordlist\r
*/\r
- static async fromEntropy (entropy: Key): Promise<Bip39Mnemonic> {\r
+ static async fromEntropy (entropy: string | Uint8Array<ArrayBuffer>): Promise<Bip39Mnemonic> {\r
if (typeof entropy === 'string') entropy = hex.toBytes(entropy)\r
if (![16, 20, 24, 28, 32].includes(entropy.byteLength)) {\r
throw new RangeError('Invalid entropy byte length for BIP-39')\r
}\r
const phraseLength = 0.75 * entropy.byteLength\r
- const checksum = await this.checksum(entropy)\r
+ const checksum = await this.#checksum(entropy)\r
\r
let e = 0n\r
for (let i = 0; i < entropy.byteLength; i++) {\r
return this.fromPhrase(sentence)\r
}\r
\r
- /**\r
- * SHA-256 hash of entropy that is appended to the entropy and subsequently\r
- * used to generate the mnemonic phrase.\r
- *\r
- * @param {Entropy} entropy - Cryptographically strong pseudorandom data of length N bits\r
- * @returns {Promise<string>} First N/32 bits of the hash as a hexadecimal string\r
- */\r
- static async checksum (entropy: Uint8Array<ArrayBuffer>): Promise<bigint> {\r
- const sha256sum = new Uint8Array(await crypto.subtle.digest('SHA-256', entropy))[0]\r
- const checksumBitLength = BigInt(entropy.byteLength) / 4n\r
- const checksum = BigInt(sha256sum) >> (8n - checksumBitLength)\r
- return checksum\r
- }\r
-\r
/**\r
* Erases seed bytes and releases variable references to allow garbage\r
* collection.\r
* @returns {Promise<string>} Promise for seed as hexadecimal string\r
*/\r
async toBlake2bSeed (format: 'hex'): Promise<string>\r
- async toBlake2bSeed (format?: 'hex'): Promise<Key> {\r
+ async toBlake2bSeed (format?: 'hex'): Promise<string | Uint8Array<ArrayBuffer>> {\r
if (this.#phrase?.length !== 24) {\r
throw new Error('BIP-39 mnemonic phrase must be 24 words to convert to BLAKE2b seed')\r
}\r
this.#blake2bSeed = new Uint8Array(32)\r
for (let i = 31; i >= 0; i--) {\r
this.#blake2bSeed[i] = Number(bits & 255n)\r
- bits >> 8n\r
+ bits >>= 8n\r
}\r
}\r
return format === 'hex'\r
? bytes.toHex(this.#blake2bSeed)\r
: this.#blake2bSeed\r
}\r
-\r
- /**\r
- * Validates a mnemonic phrase.\r
- *\r
- * @param {string} mnemonic - Mnemonic phrase to validate\r
- * @returns {boolean} True if the mnemonic phrase is valid\r
- */\r
- static async validate (mnemonic: string): Promise<boolean> {\r
- const words = mnemonic.normalize('NFKD').split(' ')\r
- if (words.length % 3 !== 0) {\r
- return false\r
- }\r
-\r
- let bits = 0n, bitLength = 0n\r
- for (const word of words) {\r
- const wordIndex = Bip39Words.indexOf(word)\r
- if (wordIndex === -1) {\r
- return false\r
- }\r
- bits = (bits << 11n) | BigInt(Bip39Words.indexOf(word))\r
- bitLength += 11n\r
- }\r
- if (Number(bitLength) % 33 !== 0) {\r
- return false\r
- }\r
-\r
- const checksumLength = bitLength / 33n\r
- const entropyLength = Number(bitLength - checksumLength)\r
- const entropyBits = bits >> checksumLength\r
- const checksumBits = bits & ((1n << checksumLength) - 1n)\r
-\r
- if (entropyBits == null\r
- || entropyBits < 0n\r
- || entropyBits > (1n << 256n) - 1n\r
- || entropyLength < 128\r
- || entropyLength > 256\r
- || entropyLength % 32 !== 0\r
- ) {\r
- return false\r
- }\r
-\r
- const entropyHex = entropyBits.toString(16).padStart(entropyLength / 4, '0')\r
- const entropy = await Entropy.import(entropyHex)\r
- const expectedChecksum = await this.checksum(entropy.bytes)\r
- if (expectedChecksum !== checksumBits) {\r
- return false\r
- }\r
-\r
- return true\r
- }\r
}\r
\r