From c71eb30e080d187c18ea5305f2422593fd9e9807 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sat, 8 Aug 2026 09:22:01 -0700 Subject: [PATCH] Typecheck ckd index arg and use VaultError consistently. --- src/lib/vault/vault-worker.ts | 126 ++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 61 deletions(-) diff --git a/src/lib/vault/vault-worker.ts b/src/lib/vault/vault-worker.ts index 53162f1..2f72148 100644 --- a/src/lib/vault/vault-worker.ts +++ b/src/lib/vault/vault-worker.ts @@ -8,6 +8,7 @@ import { TaskData } from '.' import { BIP44_COIN_NANO } from '../constants' import { dec, utf8 } from '../convert' import { Bip39, Bip44, Blake2b, WalletAesGcm } from '../crypto' +import { VaultError } from '../errors' import { WalletType } from '../wallet' import { parseAction, parseData, parseId, parseIv, parseKeySalt, parseType } from './parsers' import { passkey } from './passkey' @@ -23,10 +24,10 @@ let _mnemonic: ArrayBuffer | undefined = undefined const listener = (event: MessageEvent): void => { if (event.data == null) { - throw new TypeError('Worker received no data') + throw new VaultError('Worker received no data') } if (typeof event.data !== 'object') { - throw new Error('Invalid data') + throw new VaultError('Invalid data') } const data = event.data as Record const { taskUrl, taskId } = data @@ -77,7 +78,7 @@ const listener = (event: MessageEvent): void => { return verify(seed, mnemonicPhrase) } default: { - throw new Error(`Unknown wallet action '${action}'`) + throw new VaultError(`Unknown wallet action '${action}'`) } } }) @@ -118,14 +119,14 @@ function config (timeout?: number): Promise { try { _timer?.pause() if (_locked) { - throw new Error('Wallet is locked') + throw new VaultError('Wallet is locked') } if (typeof timeout === 'number') { if (timeout < 10) { - throw new RangeError('Timeout must be at least 10 seconds') + throw new VaultError('Timeout must be at least 10 seconds') } if (timeout > 600) { - throw new RangeError('Timeout must be at most 10 minutes') + throw new VaultError('Timeout must be at most 10 minutes') } _timeout = timeout * 1000 _timer = new VaultTimer(_autolock, _timeout) @@ -134,7 +135,7 @@ function config (timeout?: number): Promise { } catch (err) { console.error(err) _timer?.resume() - throw new Error('Failed to configure Vault', { cause: err }) + throw new VaultError('Failed to configure Vault', { cause: err }) } } @@ -144,7 +145,7 @@ function config (timeout?: number): Promise { */ function create (type?: WalletType, id?: UUID, key?: CryptoKey, keySalt?: ArrayBuffer, mnemonicSalt?: string): Promise> { if (type !== 'BIP-44' && type !== 'BLAKE2b') { - throw new TypeError('Unsupported software wallet algorithm', { cause: type }) + throw new VaultError('Unsupported software wallet algorithm', { cause: type }) } const entropy = crypto.getRandomValues(new Uint8Array(32)) return Bip39.fromEntropy(entropy) @@ -152,7 +153,7 @@ function create (type?: WalletType, id?: UUID, key?: CryptoKey, keySalt?: ArrayB .then(({ iv, salt, encrypted }) => { entropy.fill(0) if (_seed == null || _mnemonic == null) { - throw new Error('Failed to generate seed and mnemonic') + throw new VaultError('Failed to generate seed and mnemonic') } const seed = _seed.slice() const mnemonic = _mnemonic.slice() @@ -160,7 +161,7 @@ function create (type?: WalletType, id?: UUID, key?: CryptoKey, keySalt?: ArrayB }) .catch(err => { console.error(err) - throw new Error('Failed to create wallet', { cause: err }) + throw new VaultError('Failed to create wallet', { cause: err }) }) .finally(() => lock()) } @@ -174,16 +175,16 @@ function derive (index?: number | Uint32Array): Promise { console.error(err) _timer.resume() - throw new Error('Failed to derive account', { cause: err }) + throw new VaultError('Failed to derive account', { cause: err }) }) } catch (err) { console.error(err) _timer.resume() - throw new Error('Failed to derive account', { cause: err }) + throw new VaultError('Failed to derive account', { cause: err }) } } @@ -222,18 +223,18 @@ function derive (index?: number | Uint32Array): Promise> { if (type !== 'BIP-44' && type !== 'BLAKE2b' && type !== 'Exodus') { - throw new TypeError('Unsupported software wallet algorithm', { cause: type }) + throw new VaultError('Unsupported software wallet algorithm', { cause: type }) } return _load(type, id, key, keySalt, secret, mnemonicSalt) .then(record => { if (_seed == null) { - throw new Error('Wallet seed not found') + throw new VaultError('Wallet seed not found') } return record }) .catch(err => { console.error(err) - throw new Error('Failed to load wallet', { cause: err }) + throw new VaultError('Failed to load wallet', { cause: err }) }) .finally(() => lock()) } @@ -256,16 +257,16 @@ function sign (index?: Uint32Array, data?: ArrayBuffer): Promise { @@ -282,12 +283,12 @@ function sign (index?: Uint32Array, data?: ArrayBuffer): Promise { console.error(err) _timer.resume() - throw new Error('Failed to sign message', { cause: err }) + throw new VaultError('Failed to sign message', { cause: err }) }) } catch (err) { console.error(err) _timer.resume() - throw new Error('Failed to sign message', { cause: err }) + throw new VaultError('Failed to sign message', { cause: err }) } } @@ -296,31 +297,31 @@ function sign (index?: Uint32Array, data?: ArrayBuffer): Promise> { if (type == null) { - throw new TypeError('Wallet type is required') + throw new VaultError('Wallet type is required') } if (type !== 'BIP-44' && type !== 'BLAKE2b' && type !== 'Exodus') { - throw new TypeError('Invalid wallet type', { cause: type }) + throw new VaultError('Invalid wallet type', { cause: type }) } if (id == null) { - throw new TypeError('Wallet ID is required') + throw new VaultError('Wallet ID is required') } if (key == null) { - throw new TypeError('Wallet password is required') + throw new VaultError('Wallet password is required') } if (iv == null) { - throw new TypeError('Wallet IV is required') + throw new VaultError('Wallet IV is required') } if (encrypted == null) { - throw new TypeError('Wallet encrypted data is required') + throw new VaultError('Wallet encrypted data is required') } _timer?.pause() return WalletAesGcm.decrypt(type, id, key, iv, encrypted) .then(({ mnemonic, seed }) => { if (!(seed instanceof ArrayBuffer)) { - throw new TypeError('Invalid seed') + throw new VaultError('Invalid seed') } if (mnemonic != null && !(mnemonic instanceof ArrayBuffer)) { - throw new TypeError('Invalid mnemonic') + throw new VaultError('Invalid mnemonic') } _type = type _id = id @@ -333,7 +334,7 @@ function unlock (type?: WalletType, id?: UUID, key?: CryptoKey, iv?: ArrayBuffer .catch(err => { console.error(err) _timer?.resume() - throw new Error('Failed to unlock wallet', { cause: err }) + throw new VaultError('Failed to unlock wallet', { cause: err }) }) } @@ -344,19 +345,19 @@ function update (key?: CryptoKey, salt?: ArrayBuffer): Promise { @@ -366,12 +367,12 @@ function update (key?: CryptoKey, salt?: ArrayBuffer): Promise { console.error(err) _timer.resume() - throw new Error('Failed to update wallet password', { cause: err }) + throw new VaultError('Failed to update wallet password', { cause: err }) }) } catch (err) { console.error(err) _timer.resume() - throw new Error('Failed to update wallet password', { cause: err }) + throw new VaultError('Failed to update wallet password', { cause: err }) } } @@ -382,16 +383,16 @@ function update (key?: CryptoKey, salt?: ArrayBuffer): Promise> { try { if (_locked) { - throw new Error('Wallet is locked') + throw new VaultError('Wallet is locked') } if (_seed == null) { - throw new Error('Wallet seed not found') + throw new VaultError('Wallet seed not found') } if (seed == null && mnemonicPhrase == null) { - throw new Error('Seed or mnemonic phrase is required') + throw new VaultError('Seed or mnemonic phrase is required') } if (seed != null && mnemonicPhrase != null) { - throw new Error('Seed or mnemonic phrase must be verified separately') + throw new VaultError('Seed or mnemonic phrase must be verified separately') } const expected = seed != null ? new Uint8Array(seed) : utf8.toBytes(mnemonicPhrase ?? '') const actual = seed != null ? new Uint8Array(_seed) : new Uint8Array(_mnemonic ?? []) @@ -404,7 +405,7 @@ function verify (seed?: ArrayBuffer, mnemonicPhrase?: string): Promise} Private key for the account */ -function _ckd (index: number): Promise { +function _ckd (index?: number): Promise { if (_seed == null) { - throw new Error('Wallet seed not found') + throw new VaultError('Wallet seed not found') + } + if (typeof index !== 'number' || index < 0) { + throw new VaultError('Invalid CKD index') } switch (_type) { case ('BIP-44'): { @@ -462,45 +466,45 @@ function _ckd (index: number): Promise { function _load (type?: 'BIP-44' | 'BLAKE2b' | 'Exodus', id?: UUID, key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise> { try { if (!_locked) { - throw new Error('Wallet is in use') + throw new VaultError('Wallet is in use') } if (key == null || keySalt == null) { - throw new Error('Wallet password is required') + throw new VaultError('Wallet password is required') } if (type == null) { - throw new TypeError('Wallet type is required') + throw new VaultError('Wallet type is required') } if (id == null) { - throw new TypeError('Wallet ID is required') + throw new VaultError('Wallet ID is required') } if (!utf8.isUuid(id)) { - throw new TypeError('Invalid wallet ID') + throw new VaultError('Invalid wallet ID') } if (type !== 'BIP-44' && type !== 'BLAKE2b' && type !== 'Exodus') { - throw new TypeError('Invalid wallet type') + throw new VaultError('Invalid wallet type') } if (secret == null) { - throw new TypeError('Seed or mnemonic is required') + throw new VaultError('Seed or mnemonic is required') } if (typeof secret !== 'string' && mnemonicSalt !== undefined) { - throw new TypeError('Mnemonic must be a string') + throw new VaultError('Mnemonic must be a string') } if (type === 'BIP-44') { if (secret instanceof ArrayBuffer && (secret.byteLength < 16 || secret.byteLength > 64)) { - throw new RangeError('Seed for BIP-44 wallet must be 16-64 bytes') + throw new VaultError('Seed for BIP-44 wallet must be 16-64 bytes') } } if (type === 'BLAKE2b') { if (secret instanceof ArrayBuffer && secret.byteLength !== 32) { - throw new RangeError('Seed for BLAKE2b wallet must be 32 bytes') + throw new VaultError('Seed for BLAKE2b wallet must be 32 bytes') } } if (type === 'Exodus') { if (typeof secret === 'string' && secret.split(' ').length !== 12) { - throw new RangeError('Mnemonic for Exodus wallet must be 12 words') + throw new VaultError('Mnemonic for Exodus wallet must be 12 words') } if (secret instanceof ArrayBuffer && secret.byteLength !== 64) { - throw new RangeError('Seed for Exodus wallet must be 64 bytes') + throw new VaultError('Seed for Exodus wallet must be 64 bytes') } } _type = type @@ -533,6 +537,6 @@ function _load (type?: 'BIP-44' | 'BLAKE2b' | 'Exodus', id?: UUID, key?: CryptoK .then(({ iv, encrypted }) => ({ iv, salt: keySalt, encrypted })) }) } catch (err) { - throw new Error('Failed to load wallet', { cause: err }) + throw new VaultError('Failed to load wallet', { cause: err }) } } -- 2.52.0