export class VaultWorker {
#encoder: TextEncoder = new TextEncoder()
#locked: boolean
- #timeout: VaultTimer
+ #timeout: number
+ #timer: VaultTimer
#type?: 'BIP-44' | 'BLAKE2b'
#seed?: ArrayBuffer
#mnemonic?: ArrayBuffer
constructor () {
this.#locked = true
- this.#timeout = new VaultTimer(() => { }, 0)
+ this.#timeout = 120_000
+ this.#timer = new VaultTimer(() => { }, 0)
this.#type = undefined
this.#seed = undefined
this.#mnemonic = undefined
NODE: if (this.#parentPort == null) setTimeout(() => listener(event), 0)
const data = this.#parseData(event.data)
const action = this.#parseAction(data)
+ const config = this.#parseConfig(action, data)
const keySalt = this.#parseKeySalt(action, data)
Passkey.create(action, keySalt, data)
.then((key: CryptoKey | undefined): Promise<NamedData | void> => {
BROWSER: close()
NODE: process.exit()
}
+ case 'config': {
+ return this.config(config)
+ }
case 'create': {
return this.create(type, key, keySalt, mnemonicSalt)
}
}
}
+ /**
+ * Configures vault settings. The wallet must be unlocked prior to
+ * configuration.
+ */
+ config (config: { [key: string]: unknown }): Promise<void> {
+ if (typeof config.timeout === 'number') {
+ this.#timeout = config.timeout
+ }
+ return Promise.resolve()
+ }
+
/**
* Generates a new mnemonic and seed and then returns the initialization vector
* vector, salt, and encrypted data representing the wallet in a locked state.
*/
derive (index?: number): Promise<NamedData<number | ArrayBuffer>> {
try {
- this.#timeout.pause()
+ this.#timer.pause()
if (this.#locked) {
throw new Error('Wallet is locked')
}
: Blake2b.ckd(this.#seed, index)
return derive.then(prv => {
const pub = NanoNaCl.convert(new Uint8Array(prv))
- this.#timeout = new VaultTimer(() => this.lock(), 120000)
+ this.#timer = new VaultTimer(() => this.lock(), 120000)
return { index, publicKey: pub.buffer }
})
} catch (err) {
console.error(err)
- this.#timeout.resume()
+ this.#timer.resume()
throw new Error('Failed to derive account', { cause: err })
}
}
this.#mnemonic = undefined
this.#seed = undefined
this.#locked = true
- this.#timeout?.pause()
+ this.#timer?.pause()
BROWSER: postMessage('locked')
NODE: this.#parentPort?.postMessage('locked')
return Promise.resolve()
*/
sign (index?: number, data?: ArrayBuffer): Promise<NamedData<ArrayBuffer>> {
try {
- this.#timeout.pause()
+ this.#timer.pause()
if (this.#locked) {
throw new Error('Wallet is locked')
}
: Blake2b.ckd(this.#seed, index)
return derive.then(prv => {
const sig = NanoNaCl.detached(new Uint8Array(data), new Uint8Array(prv))
- this.#timeout = new VaultTimer(() => this.lock(), 120000)
+ this.#timer = new VaultTimer(() => this.lock(), 120000)
return { signature: sig.buffer }
})
} catch (err) {
console.error(err)
- this.#timeout.resume()
+ this.#timer.resume()
throw new Error('Failed to sign message', { cause: err })
}
}
if (encrypted == null) {
throw new TypeError('Wallet encrypted data is required')
}
- this.#timeout?.pause()
+ this.#timer?.pause()
return WalletAesGcm.decrypt(type, key, iv, encrypted)
.then(({ mnemonic, seed }) => {
if (!(seed instanceof ArrayBuffer)) {
this.#seed = seed
this.#mnemonic = mnemonic
this.#locked = false
- this.#timeout = new VaultTimer(this.lock.bind(this), 120000)
+ this.#timer = new VaultTimer(this.lock.bind(this), 120000)
BROWSER: postMessage('unlocked')
NODE: this.#parentPort?.postMessage('unlocked')
})
.catch(err => {
console.error(err)
- this.#timeout?.resume()
+ this.#timer?.resume()
throw new Error('Failed to unlock wallet', { cause: err })
})
}
*/
update (key?: CryptoKey, salt?: ArrayBuffer): Promise<NamedData<ArrayBuffer>> {
try {
- this.#timeout.pause()
+ this.#timer.pause()
if (this.#locked) {
throw new Error('Wallet is locked')
}
}
return WalletAesGcm.encrypt(this.#type, key, this.#seed, this.#mnemonic)
.then(({ iv, encrypted }) => {
- this.#timeout = new VaultTimer(() => this.lock(), 120000)
+ this.#timer = new VaultTimer(() => this.lock(), 120000)
return { iv, salt, encrypted }
})
} catch (err) {
console.error(err)
- this.#timeout.resume()
+ this.#timer.resume()
throw new Error('Failed to update wallet password', { cause: err })
}
}
throw new TypeError('Wallet action is required')
}
if (data.action !== 'STOP'
+ && data.action !== 'config'
&& data.action !== 'create'
&& data.action !== 'derive'
&& data.action !== 'load'
return data.action
}
+ // Action for configuring vault
+ #parseConfig (action: string, data: { [key: string]: unknown }) {
+ const config: any = {}
+ if (action === 'config') {
+ if (data.timeout != null) {
+ config.timeout = data.timeout
+ }
+ }
+ return config
+ }
+
// Worker message data itself
#parseData (data: unknown) {
if (data == null) {