\r
import { BIP39_ITERATIONS } from './constants'\r
import { bytes, utf8 } from './convert'\r
-import { Entropy } from './entropy'\r
\r
/**\r
* Represents a mnemonic phrase that identifies a wallet as defined by BIP-39.\r
* the limit of 128-256 bits defined in BIP-39. Typically, wallets use the\r
* maximum entropy allowed.\r
*\r
- * @param {(string|ArrayBuffer|Uint8Array<ArrayBuffer>)} entropy - Cryptographically secure random value\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: string | ArrayBuffer | Uint8Array<ArrayBuffer>): Promise<Bip39> {\r
- const e = new Entropy(entropy)\r
- const phraseLength = 0.75 * e.byteLength\r
- const checksum = await this.#checksum(e.bytes)\r
+ static async fromEntropy (entropy: ArrayBuffer | Uint8Array<ArrayBuffer>): Promise<Bip39> {\r
+ if (entropy instanceof ArrayBuffer) {\r
+ entropy = new Uint8Array(entropy)\r
+ }\r
+ if (!(entropy instanceof Uint8Array)) {\r
+ throw new TypeError('Invalid entropy')\r
+ }\r
+ if (entropy.byteLength < 16 || entropy.byteLength > 32) {\r
+ throw new RangeError(`Entropy must be 16-32 bytes`)\r
+ }\r
+ if (entropy.byteLength % 4 !== 0) {\r
+ throw new RangeError(`Entropy must be a multiple of 4 bytes`)\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
\r
- let concatenation: bigint = (e.bigint << BigInt(e.byteLength) / 4n) | checksum\r
- e.destroy()\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
import { Blake2b } from './blake2b'
import { default as Constants, BIP44_COIN_NANO } from './constants'
import { default as Convert, bytes, hex, utf8 } from './convert'
-import { Entropy } from './entropy'
import { NanoNaCl } from './nano-nacl'
import { NamedData } from '#types'
static #locked: boolean = true
static #type?: 'BIP-44' | 'BLAKE2b'
static #seed?: ArrayBuffer
- static #mnemonic?: Bip39
+ static #mnemonic?: string
static #parentPort?: any
static {
NODE: this.#parentPort = parentPort
*/
static async create (type?: 'BIP-44' | 'BLAKE2b', key?: CryptoKey, keySalt?: ArrayBuffer, mnemonicSalt?: string): Promise<NamedData<ArrayBuffer>> {
try {
- const entropy = new Entropy()
- const { phrase: mnemonicPhrase } = await Bip39.fromEntropy(entropy.bytes)
+ const entropy = crypto.getRandomValues(new Uint8Array(32))
+ const { phrase: mnemonicPhrase } = await Bip39.fromEntropy(entropy)
const record = await this.import(type, key, keySalt, mnemonicPhrase, mnemonicSalt)
- if (this.#seed == null || this.#mnemonic?.phrase == null) {
+ if (this.#seed == null || this.#mnemonic == null) {
throw new Error('Failed to generate seed and mnemonic')
}
- return { ...record, seed: this.#seed.slice(), mnemonic: utf8.toBuffer(this.#mnemonic.phrase) }
+ return { ...record, seed: this.#seed.slice(), mnemonic: utf8.toBuffer(this.#mnemonic) }
} catch (err) {
throw new Error('Failed to create wallet', { cause: err })
}
if (secret instanceof ArrayBuffer) {
this.#seed = secret
if (type === 'BLAKE2b') {
- this.#mnemonic = await Bip39.fromEntropy(new Uint8Array(secret))
+ this.#mnemonic = (await Bip39.fromEntropy(new Uint8Array(secret))).phrase
}
} else {
- this.#mnemonic = await Bip39.fromPhrase(secret)
+ const mnemonic = await Bip39.fromPhrase(secret)
+ this.#mnemonic = mnemonic.phrase
this.#seed = type === 'BIP-44'
- ? (await this.#mnemonic.toBip39Seed(mnemonicSalt ?? '')).buffer
- : (await this.#mnemonic.toBlake2bSeed()).buffer
+ ? (await mnemonic.toBip39Seed(mnemonicSalt ?? '')).buffer
+ : (await mnemonic.toBlake2bSeed()).buffer
}
const { iv, encrypted } = await this.#encryptWallet(key)
return { iv, salt: keySalt, encrypted }
throw new TypeError('Invalid seed')
}
this.#seed = seed
- if (mnemonic != null) this.#mnemonic = await Bip39.fromPhrase(mnemonic)
+ if (mnemonic != null) this.#mnemonic = (await Bip39.fromPhrase(mnemonic)).phrase
this.#locked = false
return { isUnlocked: !this.#locked }
} catch (err) {
}
}
if (mnemonicPhrase != null) {
- if (mnemonicPhrase === this.#mnemonic?.phrase) {
+ if (mnemonicPhrase === this.#mnemonic) {
isVerified = true
}
}
const data: NamedData<string> = {
seed: bytes.toHex(new Uint8Array(this.#seed))
}
- if (this.#mnemonic?.phrase != null) data.mnemonic = this.#mnemonic.phrase
- const iv = new Entropy().buffer
+ if (this.#mnemonic != null) data.mnemonic = this.#mnemonic
+ const iv = crypto.getRandomValues(new Uint8Array(32)).buffer
const encoded = utf8.toBytes(JSON.stringify(data))
const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoded)
return { iv, encrypted }
}
const iv: ArrayBuffer = action === 'unlock' && messageData.iv instanceof ArrayBuffer
? messageData.iv
- : new Entropy().buffer
+ : crypto.getRandomValues(new Uint8Array(32)).buffer
// Salt for decryption key to unlock
if (action === 'unlock' && !(messageData.keySalt instanceof ArrayBuffer)) {
}
const keySalt: ArrayBuffer = action === 'unlock' && messageData.keySalt instanceof ArrayBuffer
? messageData.keySalt
- : new Entropy().buffer
+ : crypto.getRandomValues(new Uint8Array(32)).buffer
// CryptoKey from password, decryption key if unlocking else encryption key
const key = password instanceof ArrayBuffer
const Bip39 = ${Bip39}
const Bip44 = ${Bip44}
const Blake2b = ${Blake2b}
- const Entropy = ${Entropy}
const NanoNaCl = ${NanoNaCl}
const Safe = ${Safe}
`