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'
const listener = (event: MessageEvent<any>): 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<string, unknown>
const { taskUrl, taskId } = data
return verify(seed, mnemonicPhrase)
}
default: {
- throw new Error(`Unknown wallet action '${action}'`)
+ throw new VaultError(`Unknown wallet action '${action}'`)
}
}
})
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)
} 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 })
}
}
*/
function create (type?: WalletType, id?: UUID, key?: CryptoKey, keySalt?: ArrayBuffer, mnemonicSalt?: string): Promise<Record<string, ArrayBuffer>> {
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)
.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()
})
.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())
}
try {
_timer.pause()
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 (_type !== 'BIP-44' && _type !== 'BLAKE2b' && _type !== 'Exodus') {
- throw new Error('Invalid wallet type')
+ throw new VaultError('Invalid wallet type')
}
if (index == null) {
- throw new Error('Invalid wallet account index(es)')
+ throw new VaultError('Invalid wallet account index(es)')
}
const promises = []
const values = typeof index === 'number' ? [index] : index.values()
.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 })
})
} 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 })
}
}
*/
function load (type?: WalletType, id?: UUID, key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise<Record<string, ArrayBuffer>> {
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())
}
try {
_timer.pause()
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 (index == null) {
- throw new Error('Wallet account index is required to sign')
+ throw new VaultError('Wallet account index is required to sign')
}
if (data == null) {
- throw new Error('Data to sign not found')
+ throw new VaultError('Data to sign not found')
}
return _ckd(index[0])
.then(result => {
.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 })
})
} 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 })
}
}
*/
function unlock (type?: WalletType, id?: UUID, key?: CryptoKey, iv?: ArrayBuffer, encrypted?: ArrayBuffer): Promise<Record<string, boolean>> {
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
.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 })
})
}
try {
_timer.pause()
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 (_type == null) {
- throw new Error('Wallet type not found')
+ throw new VaultError('Wallet type not found')
}
if (_id == null) {
- throw new Error('Wallet ID not found')
+ throw new VaultError('Wallet ID not found')
}
if (key == null || salt == null) {
- throw new TypeError('Wallet password is required')
+ throw new VaultError('Wallet password is required')
}
return WalletAesGcm.encrypt(_type, _id, key, _seed, _mnemonic)
.then(({ iv, encrypted }) => {
.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 })
})
} 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 })
}
}
function verify (seed?: ArrayBuffer, mnemonicPhrase?: string): Promise<Record<string, boolean>> {
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 ?? [])
return Promise.resolve({ isVerified })
} catch (err) {
console.error(err)
- throw new Error('Failed to verify wallet', { cause: err })
+ throw new VaultError('Failed to verify wallet', { cause: err })
}
}
* @param {number} index - 4-byte index of account to derive
* @returns {Promise<ArrayBuffer>} Private key for the account
*/
-function _ckd (index: number): Promise<ArrayBuffer> {
+function _ckd (index?: number): Promise<ArrayBuffer> {
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'): {
function _load (type?: 'BIP-44' | 'BLAKE2b' | 'Exodus', id?: UUID, key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise<Record<string, ArrayBuffer>> {
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
.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 })
}
}