if (encrypted == null) {
throw new TypeError('Wallet encrypted data is required')
}
- const { seed, mnemonic } = await this.#decryptWallet(type, key, iv, encrypted)
- if (!(seed instanceof ArrayBuffer)) {
+ await this.#decryptWallet(type, key, iv, encrypted)
+ if (!(this.#seed instanceof ArrayBuffer)) {
throw new TypeError('Invalid seed')
}
- if (mnemonic != null && typeof mnemonic !== 'string') {
+ if (this.#mnemonic != null && typeof this.#mnemonic !== 'string') {
throw new TypeError('Invalid seed')
}
- this.#seed = seed
- if (mnemonic != null) this.#mnemonic = (await Bip39.fromPhrase(mnemonic)).phrase
this.#locked = false
return { isUnlocked: !this.#locked }
} catch (err) {
return await crypto.subtle.deriveKey(derivationAlgorithm, derivationKey, derivedKeyType, false, [purpose])
}
- static async #decryptWallet (type: string, key: CryptoKey, iv: ArrayBuffer, encrypted: ArrayBuffer): Promise<NamedData<string | ArrayBuffer>> {
- let decrypted, decoded, parsed
-
+ static async #decryptWallet (type: string, key: CryptoKey, iv: ArrayBuffer, encrypted: ArrayBuffer): Promise<void> {
+ const seedLength = type === 'BIP-44' ? 64 : 32
const additionalData = utf8.toBytes(type)
- decrypted = new Uint8Array(await crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData }, key, encrypted))
- decoded = bytes.toUtf8(decrypted)
- bytes.erase(decrypted)
- decrypted = undefined
-
- parsed = JSON.parse(decoded)
- decoded = undefined
-
- const seed = hex.toBuffer(parsed.seed)
- const mnemonic = parsed.mnemonic
- parsed = parsed.seed = parsed.mnemonic = undefined
- return { seed, mnemonic }
+ const decrypted = new Uint8Array(await crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData }, key, encrypted))
+ this.#seed = decrypted.buffer.slice(0, seedLength)
+ this.#mnemonic = bytes.toUtf8(decrypted.slice(seedLength))
+ decrypted.fill(0)
}
/**
if (this.#seed == null) {
throw new Error('Wallet seed not found')
}
- const data: NamedData<string> = {
- seed: bytes.toHex(new Uint8Array(this.#seed))
- }
- if (this.#mnemonic != null) data.mnemonic = this.#mnemonic
-
+ const seed = new Uint8Array(this.#seed)
+ const mnemonic = utf8.toBytes(this.#mnemonic ?? '')
// restrict iv to 96 bits per GCM best practice
const iv = crypto.getRandomValues(new Uint8Array(12)).buffer
const additionalData = utf8.toBytes(this.#type)
- const encoded = utf8.toBytes(JSON.stringify(data))
+ const encoded = new Uint8Array([...seed, ...mnemonic])
const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv, additionalData }, key, encoded)
return { iv, encrypted }
}