* @param {Uint8Array<ArrayBuffer>} entropy - Cryptographically strong pseudorandom data of length N bits\r
* @returns {Promise<bigint>} First N/32 bits of the hash as a bigint\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
+ static #checksum (entropy: Uint8Array<ArrayBuffer>): Promise<bigint> {\r
+ const checksumBitLength = (entropy.byteLength / 4) | 0\r
+ return crypto.subtle.digest('SHA-256', entropy)\r
+ .then(hash => {\r
+ const sha256sum = new Uint8Array(hash)[0]\r
+ const checksum = sha256sum >> (8 - checksumBitLength)\r
+ return BigInt(checksum)\r
+ })\r
}\r
\r
/**\r
* @param {(ArrayBuffer|Uint8Array<ArrayBuffer>)} entropy - Cryptographically secure random value\r
* @returns {string} Mnemonic phrase created using the BIP-39 wordlist\r
*/\r
- static async fromEntropy (entropy: ArrayBuffer | Uint8Array<ArrayBuffer>): Promise<Bip39> {\r
+ static fromEntropy (entropy: ArrayBuffer | Uint8Array<ArrayBuffer>): Promise<Bip39> {\r
if (entropy instanceof ArrayBuffer) {\r
entropy = new Uint8Array(entropy)\r
}\r
}\r
\r
const phraseLength = 0.75 * entropy.byteLength\r
- const checksum = await this.#checksum(entropy)\r
- const bigEntropy = entropy.reduce((a, b) => a = (a << 8n) | BigInt(b), 0n)\r
+ return this.#checksum(entropy)\r
+ .then(checksum => {\r
+ const bigEntropy = entropy.reduce((a, b) => a = (a << 8n) | BigInt(b), 0n)\r
\r
- let concatenation: bigint = (bigEntropy << BigInt(entropy.byteLength) / 4n) | checksum\r
- const words: string[] = []\r
- for (let i = 0; i < phraseLength; i++) {\r
- const wordBits = concatenation & 2047n\r
- const wordIndex = Number(wordBits)\r
- words.unshift(this.wordlist[wordIndex])\r
- concatenation >>= 11n\r
- }\r
- const sentence = words.join(' ')\r
- return this.fromPhrase(sentence)\r
+ let concatenation: bigint = (bigEntropy << BigInt(entropy.byteLength) / 4n) | checksum\r
+ const words: string[] = []\r
+ for (let i = 0; i < phraseLength; i++) {\r
+ const wordBits = concatenation & 2047n\r
+ const wordIndex = Number(wordBits)\r
+ words.unshift(this.wordlist[wordIndex])\r
+ concatenation >>= 11n\r
+ }\r
+ const sentence = words.join(' ')\r
+ return this.fromPhrase(sentence)\r
+ })\r
}\r
\r
/**\r
* @param {string} phrase - String of 12, 15, 18, 21, or 24 words\r
* @returns {string} Mnemonic phrase validated using the BIP-39 wordlist\r
*/\r
- static async fromPhrase (phrase: string): Promise<Bip39> {\r
+ static fromPhrase (phrase: string): Promise<Bip39> {\r
this.#isInternal = true\r
const self = new this()\r
- const isValid = await this.validate(phrase)\r
- if (isValid) {\r
- self.#phrase = phrase.normalize('NFKD').toLowerCase().split(' ')\r
- return self\r
- } else {\r
- throw new Error('Invalid mnemonic phrase.')\r
- }\r
+ return this.validate(phrase)\r
+ .then(isValid => {\r
+ if (isValid) {\r
+ self.#phrase = phrase.normalize('NFKD').toLowerCase().split(' ')\r
+ return self\r
+ } else {\r
+ throw new Error('Invalid mnemonic phrase.')\r
+ }\r
+ })\r
}\r
\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
- static async validate (mnemonic: unknown): Promise<boolean> {\r
+ static validate (mnemonic: string): Promise<boolean>\r
+ static validate (mnemonic: unknown): Promise<boolean> {\r
if (typeof mnemonic !== 'string') {\r
- return false\r
+ return Promise.resolve(false)\r
}\r
if (!/^(?:[a-z]{3,8} ){11,23}[a-z]{3,8}$/i.test(mnemonic)) {\r
- return false\r
+ return Promise.resolve(false)\r
}\r
const words = mnemonic.normalize('NFKD').split(' ')\r
if (words.length % 3 !== 0) {\r
- return false\r
+ return Promise.resolve(false)\r
}\r
let bits = 0n, bitLength = 0n\r
for (const word of words) {\r
const wordIndex = this.wordlist.indexOf(word)\r
if (wordIndex === -1) {\r
- return false\r
+ return Promise.resolve(false)\r
}\r
bits = (bits << 11n) | BigInt(this.wordlist.indexOf(word))\r
bitLength += 11n\r
}\r
if (Number(bitLength) % 33 !== 0) {\r
- return false\r
+ return Promise.resolve(false)\r
}\r
const checksumLength = bitLength / 33n\r
const entropyLength = bitLength - checksumLength\r
|| entropyLength > 256n\r
|| Number(entropyLength) % 32 !== 0\r
) {\r
- return false\r
+ return Promise.resolve(false)\r
}\r
const bytes = new Uint8Array(Number(entropyLength) / 8)\r
for (let i = 0; i < bytes.length; i++) {\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
+ return this.#checksum(bytes)\r
+ .then(expectedChecksum => expectedChecksum === checksumBits)\r
}\r
\r
#bip39Seed?: Uint8Array<ArrayBuffer>\r
\r
private constructor () {\r
if (!Bip39.#isInternal) {\r
- throw new Error(`Bip39 must be created with async methods 'fromPhrase()' or 'fromEntropy().`)\r
+ throw new Error(`Bip39 must be created with factory methods 'fromPhrase()' or 'fromEntropy().`)\r
}\r
Bip39.#isInternal = false\r
}\r
* @param {string} [passphrase=''] - Used as the PBKDF2 salt. Default: ""\r
* @returns {Promise<Uint8Array<ArrayBuffer>>} Promise for seed as bytes\r
*/\r
- async toBip39Seed (passphrase: string): Promise<Uint8Array<ArrayBuffer>>\r
+ toBip39Seed (passphrase: string): Promise<Uint8Array<ArrayBuffer>>\r
/**\r
* Converts the mnemonic phrase to a BIP-39 seed.\r
*\r
* @param {string} [passphrase=''] - Used as the PBKDF2 salt. Default: ""\r
* @returns {Promise<string>} Promise for seed as hexadecimal string\r
*/\r
- async toBip39Seed (passphrase: string, format: 'hex'): Promise<string>\r
- async toBip39Seed (passphrase: unknown, format?: 'hex'): Promise<string | Uint8Array<ArrayBuffer>> {\r
+ toBip39Seed (passphrase: string, format?: 'hex'): Promise<string>\r
+ toBip39Seed (passphrase: unknown, format?: 'hex'): Promise<string | Uint8Array<ArrayBuffer>> {\r
if (this.phrase == null) {\r
throw new Error('BIP-39 mnemonic phrase not found')\r
}\r
- if (this.#bip39Seed == null) {\r
- const salt = (passphrase == null || typeof passphrase !== 'string')\r
- ? ''\r
- : passphrase\r
+ if (this.#bip39Seed != null) {\r
+ return Promise.resolve(format === 'hex' ? bytes.toHex(this.#bip39Seed) : this.#bip39Seed)\r
+ } else {\r
+ const salt = (typeof passphrase === 'string') ? passphrase : ''\r
const keyData = utf8.toBytes(this.phrase)\r
- const phraseKey = await crypto.subtle.importKey('raw', keyData, 'PBKDF2', false, ['deriveBits', 'deriveKey'])\r
- const algorithm: Pbkdf2Params = {\r
- name: 'PBKDF2',\r
- hash: 'SHA-512',\r
- salt: utf8.toBytes(`mnemonic${salt.normalize('NFKD')}`),\r
- iterations: Bip39.#ITERATIONS\r
- }\r
- const seed = await crypto.subtle.deriveBits(algorithm, phraseKey, 512)\r
- this.#bip39Seed = new Uint8Array(seed)\r
+ return crypto.subtle.importKey('raw', keyData, 'PBKDF2', false, ['deriveBits', 'deriveKey'])\r
+ .then(phraseKey => {\r
+ const algorithm: Pbkdf2Params = {\r
+ name: 'PBKDF2',\r
+ hash: 'SHA-512',\r
+ salt: utf8.toBytes(`mnemonic${salt.normalize('NFKD')}`),\r
+ iterations: Bip39.#ITERATIONS\r
+ }\r
+ return crypto.subtle.deriveBits(algorithm, phraseKey, 512)\r
+ .then(seed => {\r
+ this.#bip39Seed = new Uint8Array(seed)\r
+ return this.toBip39Seed(salt, format)\r
+ })\r
+ })\r
}\r
- return format === 'hex'\r
- ? bytes.toHex(this.#bip39Seed)\r
- : this.#bip39Seed\r
}\r
\r
/**\r
* Converts the mnemonic phrase to a BLAKE2b seed.\r
*\r
- * @returns {Promise<Uint8Array<ArrayBuffer>>} Promise for seed as bytes\r
+ * @returns {Uint8Array<ArrayBuffer>} Seed as bytes\r
*/\r
- async toBlake2bSeed (): Promise<Uint8Array<ArrayBuffer>>\r
+ toBlake2bSeed (): Uint8Array<ArrayBuffer>\r
/**\r
* Converts the mnemonic phrase to a BLAKE2b seed.\r
*\r
* @param {string} format\r
- * @returns {Promise<string>} Promise for seed as hexadecimal string\r
+ * @returns {string} Seed as hexadecimal string\r
*/\r
- async toBlake2bSeed (format: 'hex'): Promise<string>\r
- async toBlake2bSeed (format?: 'hex'): Promise<string | Uint8Array<ArrayBuffer>> {\r
+ toBlake2bSeed (format: 'hex'): string\r
+ toBlake2bSeed (format?: 'hex'): 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