\r
import { Bip39Words } from './bip39-wordlist'\r
import { BIP39_ITERATIONS } from './constants'\r
-import { bin, bytes, dec, utf8 } from './convert'\r
+import { bin, bytes, dec, hex, utf8 } from './convert'\r
import { Entropy } from './entropy'\r
import { Key } from '#types'\r
\r
* @param {string} entropy - Hexadecimal string\r
* @returns {string} Mnemonic phrase created using the BIP-39 wordlist\r
*/\r
- static async fromEntropy (entropy: string): Promise<Bip39Mnemonic> {\r
- const e = await Entropy.import(entropy)\r
- const checksum = await this.checksum(e)\r
- let concatenation = `${e.bits}${checksum}`\r
+ static async fromEntropy (entropy: Key): 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
+\r
+ let e = 0n\r
+ for (let i = 0; i < entropy.byteLength; i++) {\r
+ e = e << 8n | BigInt(entropy[i])\r
+ }\r
+\r
+ let concatenation = (e << BigInt(entropy.byteLength) / 4n) | checksum\r
const words: string[] = []\r
- while (concatenation.length > 0) {\r
- const wordBits = concatenation.substring(0, 11)\r
- const wordIndex = parseInt(wordBits, 2)\r
- words.push(Bip39Words[wordIndex])\r
- concatenation = concatenation.substring(11)\r
+ for (let i = 0; i < phraseLength; i++) {\r
+ const wordBits = concatenation & 2047n\r
+ const wordIndex = Number(wordBits)\r
+ words.unshift(Bip39Words[wordIndex])\r
+ concatenation >>= 11n\r
}\r
const sentence = words.join(' ')\r
return this.fromPhrase(sentence)\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: Entropy): Promise<string> {\r
- const hashBuffer = await globalThis.crypto.subtle.digest('SHA-256', entropy.bytes)\r
- const hashBytes = new Uint8Array(hashBuffer)\r
- const hashBits = bytes.toBin(hashBytes)\r
- const checksumLength = entropy.bits.length / 32\r
- const checksum = hashBits.substring(0, checksumLength)\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
\r
const entropy = await Entropy.import(bin.toBytes(entropyBits))\r
- const expectedChecksum = await this.checksum(entropy)\r
+ const expectedChecksum = await this.checksum(entropy.bytes)\r
\r
- if (expectedChecksum !== checksumBits) {\r
+ if (Number(expectedChecksum) !== parseInt(checksumBits, 2)) {\r
return false\r
}\r
\r