]> git.codecow.com Git - libnemo.git/commitdiff
Draft vault config.
authorChris Duncan <chris@zoso.dev>
Tue, 9 Sep 2025 21:48:50 +0000 (14:48 -0700)
committerChris Duncan <chris@zoso.dev>
Tue, 9 Sep 2025 21:48:50 +0000 (14:48 -0700)
src/lib/vault/vault-worker.ts

index fb837d7776c6b3189869caa97abf6b5eac7e650e..42f84fa42ab3b09e9a867a86105fe63a6dfc3d75 100644 (file)
@@ -13,7 +13,8 @@ import { VaultTimer } from './vault-timer'
 export class VaultWorker {
        #encoder: TextEncoder = new TextEncoder()
        #locked: boolean
-       #timeout: VaultTimer
+       #timeout: number
+       #timer: VaultTimer
        #type?: 'BIP-44' | 'BLAKE2b'
        #seed?: ArrayBuffer
        #mnemonic?: ArrayBuffer
@@ -21,7 +22,8 @@ export class VaultWorker {
 
        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
@@ -30,6 +32,7 @@ export class VaultWorker {
                        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> => {
@@ -41,6 +44,9 @@ export class VaultWorker {
                                                        BROWSER: close()
                                                        NODE: process.exit()
                                                }
+                                               case 'config': {
+                                                       return this.config(config)
+                                               }
                                                case 'create': {
                                                        return this.create(type, key, keySalt, mnemonicSalt)
                                                }
@@ -107,6 +113,17 @@ export class VaultWorker {
                }
        }
 
+       /**
+       * 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.
@@ -141,7 +158,7 @@ export class VaultWorker {
        */
        derive (index?: number): Promise<NamedData<number | ArrayBuffer>> {
                try {
-                       this.#timeout.pause()
+                       this.#timer.pause()
                        if (this.#locked) {
                                throw new Error('Wallet is locked')
                        }
@@ -159,12 +176,12 @@ export class VaultWorker {
                                : 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 })
                }
        }
@@ -195,7 +212,7 @@ export class VaultWorker {
                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()
@@ -207,7 +224,7 @@ export class VaultWorker {
        */
        sign (index?: number, data?: ArrayBuffer): Promise<NamedData<ArrayBuffer>> {
                try {
-                       this.#timeout.pause()
+                       this.#timer.pause()
                        if (this.#locked) {
                                throw new Error('Wallet is locked')
                        }
@@ -225,12 +242,12 @@ export class VaultWorker {
                                : 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 })
                }
        }
@@ -257,7 +274,7 @@ export class VaultWorker {
                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)) {
@@ -269,13 +286,13 @@ export class VaultWorker {
                                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 })
                        })
        }
@@ -285,7 +302,7 @@ export class VaultWorker {
        */
        update (key?: CryptoKey, salt?: ArrayBuffer): Promise<NamedData<ArrayBuffer>> {
                try {
-                       this.#timeout.pause()
+                       this.#timer.pause()
                        if (this.#locked) {
                                throw new Error('Wallet is locked')
                        }
@@ -300,12 +317,12 @@ export class VaultWorker {
                        }
                        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 })
                }
        }
@@ -512,6 +529,7 @@ export class VaultWorker {
                        throw new TypeError('Wallet action is required')
                }
                if (data.action !== 'STOP'
+                       && data.action !== 'config'
                        && data.action !== 'create'
                        && data.action !== 'derive'
                        && data.action !== 'load'
@@ -525,6 +543,17 @@ export class VaultWorker {
                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) {