break
}
case 'unlock': {
- result = await this.unlock(key, iv, encrypted)
+ result = await this.unlock(type, key, iv, encrypted)
break
}
case 'update': {
/**
* Decrypts the input and sets the seed and, if it is included, the mnemonic.
*/
- static async unlock (key?: CryptoKey, iv?: ArrayBuffer, encrypted?: ArrayBuffer): Promise<NamedData<boolean>> {
+ static async unlock (type?: string, key?: CryptoKey, iv?: ArrayBuffer, encrypted?: ArrayBuffer): Promise<NamedData<boolean>> {
try {
+ if (type == null) {
+ throw new TypeError('Wallet type is required')
+ }
if (key == null) {
throw new TypeError('Wallet password is required')
}
if (encrypted == null) {
throw new TypeError('Wallet encrypted data is required')
}
- const { seed, mnemonic } = await this.#decryptWallet(key, iv, encrypted)
+ const { seed, mnemonic } = await this.#decryptWallet(type, key, iv, encrypted)
if (!(seed instanceof ArrayBuffer)) {
throw new TypeError('Invalid seed')
}
return await crypto.subtle.deriveKey(derivationAlgorithm, derivationKey, derivedKeyType, false, [purpose])
}
- static async #decryptWallet (key: CryptoKey, iv: ArrayBuffer, encrypted: ArrayBuffer): Promise<NamedData<string | ArrayBuffer>> {
- const decrypted = await crypto.subtle.decrypt({ name: 'AES-GCM', iv }, key, encrypted)
- const decoded = JSON.parse(bytes.toUtf8(new Uint8Array(decrypted)))
- const seed = hex.toBuffer(decoded.seed)
- const mnemonic = decoded.mnemonic
+ static async #decryptWallet (type: string, key: CryptoKey, iv: ArrayBuffer, encrypted: ArrayBuffer): Promise<NamedData<string | ArrayBuffer>> {
+ let decrypted, decoded, parsed
+
+ 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 }
}
}
static async #encryptWallet (key: CryptoKey): Promise<NamedData<ArrayBuffer>> {
+ if (this.#type == null) {
+ throw new Error('Invalid wallet type')
+ }
if (this.#seed == null) {
throw new Error('Wallet seed not found')
}
// 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 encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoded)
+ const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv, additionalData }, key, encoded)
return { iv, encrypted }
}