]> git.codecow.com Git - libnemo.git/commitdiff
Remove privatization of vault worker properties.
authorChris Duncan <chris@zoso.dev>
Sat, 30 Aug 2025 07:45:09 +0000 (00:45 -0700)
committerChris Duncan <chris@zoso.dev>
Sat, 30 Aug 2025 07:45:09 +0000 (00:45 -0700)
src/lib/vault/vault-worker.ts

index 57840a65f445e8f56e6103d1bee24dd81779e64a..f7ba7242e9d70a9c9ec0db50f79bd3ebc07a280a 100644 (file)
@@ -12,20 +12,20 @@ import { VaultTimer } from './vault-timer'
 * Cross-platform worker for managing wallet secrets.
 */
 export class VaultWorker {
-       static #locked: boolean = true
-       static #timeout: VaultTimer
-       static #type?: 'BIP-44' | 'BLAKE2b'
-       static #seed?: ArrayBuffer
-       static #mnemonic?: ArrayBuffer
-       static #parentPort?: any
+       static locked: boolean = true
+       static timeout: VaultTimer
+       static type?: 'BIP-44' | 'BLAKE2b'
+       static seed?: ArrayBuffer
+       static mnemonic?: ArrayBuffer
+       static parentPort?: any
        static {
-               NODE: this.#parentPort = parentPort
+               NODE: this.parentPort = parentPort
        }
 
        static {
-               NODE: this.#parentPort = parentPort
+               NODE: this.parentPort = parentPort
                const listener = (message: MessageEvent<any>): Promise<void> => {
-                       return this.#extractData(message.data).then(extracted => {
+                       return this._extractData(message.data).then(extracted => {
                                const {
                                        action,
                                        type,
@@ -107,7 +107,7 @@ export class VaultWorker {
                                })
                }
                BROWSER: addEventListener('message', listener)
-               NODE: this.#parentPort?.on('message', listener)
+               NODE: this.parentPort?.on('message', listener)
        }
 
        /**
@@ -119,12 +119,12 @@ export class VaultWorker {
                        const entropy = crypto.getRandomValues(new Uint8Array(32))
                        return Bip39.fromEntropy(entropy)
                                .then(bip39 => {
-                                       return this.#load(type, key, keySalt, bip39.phrase, mnemonicSalt)
+                                       return this._load(type, key, keySalt, bip39.phrase, mnemonicSalt)
                                                .then(record => {
-                                                       if (this.#seed == null || this.#mnemonic == null) {
+                                                       if (this.seed == null || this.mnemonic == null) {
                                                                throw new Error('Failed to generate seed and mnemonic')
                                                        }
-                                                       return { ...record, seed: this.#seed.slice(), mnemonic: this.#mnemonic.slice() }
+                                                       return { ...record, seed: this.seed.slice(), mnemonic: this.mnemonic.slice() }
                                                })
                                })
                } catch (err) {
@@ -142,30 +142,30 @@ export class VaultWorker {
        */
        static derive (index?: number): Promise<NamedData<number | ArrayBuffer>> {
                try {
-                       this.#timeout.pause()
-                       if (this.#locked) {
+                       this.timeout.pause()
+                       if (this.locked) {
                                throw new Error('Wallet is locked')
                        }
-                       if (this.#seed == null) {
+                       if (this.seed == null) {
                                throw new Error('Wallet seed not found')
                        }
-                       if (this.#type !== 'BIP-44' && this.#type !== 'BLAKE2b') {
+                       if (this.type !== 'BIP-44' && this.type !== 'BLAKE2b') {
                                throw new Error('Invalid wallet type')
                        }
                        if (typeof index !== 'number') {
                                throw new Error('Invalid wallet account index')
                        }
-                       const derive = this.#type === 'BIP-44'
-                               ? Bip44.ckd(this.#seed, BIP44_COIN_NANO, index)
-                               : Promise.resolve(this.#deriveBlake2bPrivateKey(this.#seed, index))
+                       const derive = this.type === 'BIP-44'
+                               ? Bip44.ckd(this.seed, BIP44_COIN_NANO, index)
+                               : Promise.resolve(this._deriveBlake2bPrivateKey(this.seed, index))
                        return derive.then(prv => {
                                const pub = NanoNaCl.convert(new Uint8Array(prv))
-                               this.#timeout = new VaultTimer(() => this.lock(), 120000)
+                               this.timeout = new VaultTimer(() => this.lock(), 120000)
                                return { index, publicKey: pub.buffer }
                        })
                } catch (err) {
                        console.error(err)
-                       this.#timeout.resume()
+                       this.timeout.resume()
                        throw new Error('Failed to derive account', { cause: err })
                }
        }
@@ -175,9 +175,9 @@ export class VaultWorker {
        * vector, salt, and encrypted data representing the wallet in a locked state.
        */
        static load (type?: 'BIP-44' | 'BLAKE2b', key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise<NamedData<ArrayBuffer>> {
-               return this.#load(type, key, keySalt, secret, mnemonicSalt)
+               return this._load(type, key, keySalt, secret, mnemonicSalt)
                        .then(record => {
-                               if (this.#seed == null) {
+                               if (this.seed == null) {
                                        throw new Error('Wallet seed not found')
                                }
                                return record
@@ -190,11 +190,11 @@ export class VaultWorker {
        }
 
        static lock (): NamedData<boolean> {
-               this.#mnemonic = undefined
-               this.#seed = undefined
-               this.#locked = true
-               this.#timeout?.pause()
-               return { isLocked: this.#locked }
+               this.mnemonic = undefined
+               this.seed = undefined
+               this.locked = true
+               this.timeout?.pause()
+               return { isLocked: this.locked }
        }
 
        /**
@@ -203,11 +203,11 @@ export class VaultWorker {
        */
        static sign (index?: number, data?: ArrayBuffer): Promise<NamedData<ArrayBuffer>> {
                try {
-                       this.#timeout.pause()
-                       if (this.#locked) {
+                       this.timeout.pause()
+                       if (this.locked) {
                                throw new Error('Wallet is locked')
                        }
-                       if (this.#seed == null) {
+                       if (this.seed == null) {
                                throw new Error('Wallet seed not found')
                        }
                        if (index == null) {
@@ -216,17 +216,17 @@ export class VaultWorker {
                        if (data == null) {
                                throw new Error('Data to sign not found')
                        }
-                       const derive = this.#type === 'BIP-44'
-                               ? Bip44.ckd(this.#seed, BIP44_COIN_NANO, index)
-                               : Promise.resolve(this.#deriveBlake2bPrivateKey(this.#seed, index))
+                       const derive = this.type === 'BIP-44'
+                               ? Bip44.ckd(this.seed, BIP44_COIN_NANO, index)
+                               : Promise.resolve(this._deriveBlake2bPrivateKey(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.timeout = new VaultTimer(() => this.lock(), 120000)
                                return { signature: sig.buffer }
                        })
                } catch (err) {
                        console.error(err)
-                       this.#timeout.resume()
+                       this.timeout.resume()
                        throw new Error('Failed to sign message', { cause: err })
                }
        }
@@ -247,22 +247,22 @@ export class VaultWorker {
                if (encrypted == null) {
                        throw new TypeError('Wallet encrypted data is required')
                }
-               this.#timeout?.pause()
-               return this.#decryptWallet(type, key, iv, encrypted)
+               this.timeout?.pause()
+               return this._decryptWallet(type, key, iv, encrypted)
                        .then(() => {
-                               if (!(this.#seed instanceof ArrayBuffer)) {
+                               if (!(this.seed instanceof ArrayBuffer)) {
                                        throw new TypeError('Invalid seed')
                                }
-                               if (this.#mnemonic != null && !(this.#mnemonic instanceof ArrayBuffer)) {
+                               if (this.mnemonic != null && !(this.mnemonic instanceof ArrayBuffer)) {
                                        throw new TypeError('Invalid mnemonic')
                                }
-                               this.#locked = false
-                               this.#timeout = new VaultTimer(() => this.lock(), 120000)
-                               return { isUnlocked: !this.#locked }
+                               this.locked = false
+                               this.timeout = new VaultTimer(() => this.lock(), 120000)
+                               return { isUnlocked: !this.locked }
                        })
                        .catch(err => {
                                console.error(err)
-                               this.#timeout?.resume()
+                               this.timeout?.resume()
                                throw new Error('Failed to unlock wallet', { cause: err })
                        })
        }
@@ -272,24 +272,24 @@ export class VaultWorker {
        */
        static update (key?: CryptoKey, keySalt?: ArrayBuffer): Promise<NamedData<ArrayBuffer>> {
                try {
-                       this.#timeout.pause()
-                       if (this.#locked) {
+                       this.timeout.pause()
+                       if (this.locked) {
                                throw new Error('Wallet is locked')
                        }
-                       if (this.#seed == null) {
+                       if (this.seed == null) {
                                throw new Error('Wallet seed not found')
                        }
                        if (key == null || keySalt == null) {
                                throw new TypeError('Wallet password is required')
                        }
-                       return this.#encryptWallet(key)
+                       return this._encryptWallet(key)
                                .then(({ iv, encrypted }) => {
-                                       this.#timeout = new VaultTimer(() => this.lock(), 120000)
+                                       this.timeout = new VaultTimer(() => this.lock(), 120000)
                                        return { iv, salt: keySalt, encrypted }
                                })
                } catch (err) {
                        console.error(err)
-                       this.#timeout.resume()
+                       this.timeout.resume()
                        throw new Error('Failed to update wallet password', { cause: err })
                }
        }
@@ -300,10 +300,10 @@ export class VaultWorker {
        */
        static verify (seed?: ArrayBuffer, mnemonicPhrase?: string): NamedData<boolean> {
                try {
-                       if (this.#locked) {
+                       if (this.locked) {
                                throw new Error('Wallet is locked')
                        }
-                       if (this.#seed == null) {
+                       if (this.seed == null) {
                                throw new Error('Wallet seed not found')
                        }
                        if (seed == null && mnemonicPhrase == null) {
@@ -316,7 +316,7 @@ export class VaultWorker {
                        if (seed != null) {
                                let diff = 0
                                const userSeed = new Uint8Array(seed)
-                               const thisSeed = new Uint8Array(this.#seed)
+                               const thisSeed = new Uint8Array(this.seed)
                                for (let i = 0; i < userSeed.byteLength; i++) {
                                        diff |= userSeed[i] ^ thisSeed[i]
                                }
@@ -325,7 +325,7 @@ export class VaultWorker {
                        if (mnemonicPhrase != null) {
                                let diff = 0
                                const userMnemonic = utf8.toBytes(mnemonicPhrase)
-                               const thisMnemonic = new Uint8Array(this.#mnemonic ?? [])
+                               const thisMnemonic = new Uint8Array(this.mnemonic ?? [])
                                for (let i = 0; i < userMnemonic.byteLength; i++) {
                                        diff |= userMnemonic[i] ^ thisMnemonic[i]
                                }
@@ -338,7 +338,7 @@ export class VaultWorker {
                }
        }
 
-       static #createAesKey (purpose: 'encrypt' | 'decrypt', keySalt: ArrayBuffer, password?: ArrayBuffer): Promise<CryptoKey | undefined> {
+       static _createAesKey (purpose: 'encrypt' | 'decrypt', keySalt: ArrayBuffer, password?: ArrayBuffer): Promise<CryptoKey | undefined> {
                return new Promise((resolve, reject): void => {
                        if (password == null) {
                                resolve(undefined)
@@ -369,13 +369,13 @@ export class VaultWorker {
                })
        }
 
-       static #decryptWallet (type: string, key: CryptoKey, iv: ArrayBuffer, encrypted: ArrayBuffer): Promise<void> {
+       static _decryptWallet (type: string, key: CryptoKey, iv: ArrayBuffer, encrypted: ArrayBuffer): Promise<void> {
                const seedLength = type === 'BIP-44' ? 64 : 32
                const additionalData = utf8.toBytes(type)
                return crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData }, key, encrypted)
                        .then(decrypted => {
-                               this.#seed = decrypted.slice(0, seedLength)
-                               this.#mnemonic = decrypted.slice(seedLength)
+                               this.seed = decrypted.slice(0, seedLength)
+                               this.mnemonic = decrypted.slice(seedLength)
                                new Uint8Array(decrypted).fill(0)
                        })
        }
@@ -393,7 +393,7 @@ export class VaultWorker {
        * @param {number} index - 4-byte index of account to derive
        * @returns {ArrayBuffer} Private key for the account
        */
-       static #deriveBlake2bPrivateKey (seed: ArrayBuffer, index: number): ArrayBuffer {
+       static _deriveBlake2bPrivateKey (seed: ArrayBuffer, index: number): ArrayBuffer {
                const b = new ArrayBuffer(4)
                new DataView(b).setUint32(0, index, false)
                const s = new Uint8Array(seed)
@@ -401,18 +401,18 @@ export class VaultWorker {
                return new Blake2b(32).update(s).update(i).digest().buffer
        }
 
-       static #encryptWallet (key: CryptoKey): Promise<NamedData<ArrayBuffer>> {
-               if (this.#type == null) {
+       static _encryptWallet (key: CryptoKey): Promise<NamedData<ArrayBuffer>> {
+               if (this.type == null) {
                        throw new Error('Invalid wallet type')
                }
-               if (this.#seed == null) {
+               if (this.seed == null) {
                        throw new Error('Wallet seed not found')
                }
-               const seed = new Uint8Array(this.#seed)
-               const mnemonic = new Uint8Array(this.#mnemonic ?? [])
+               const seed = new Uint8Array(this.seed)
+               const mnemonic = new Uint8Array(this.mnemonic ?? [])
                // restrict iv to 96 bits per GCM best practice
                const iv = crypto.getRandomValues(new Uint8Array(12)).buffer
-               const additionalData = utf8.toBytes(this.#type)
+               const additionalData = utf8.toBytes(this.type)
                const encoded = new Uint8Array([...seed, ...mnemonic])
                return crypto.subtle.encrypt({ name: 'AES-GCM', iv, additionalData }, key, encoded)
                        .then(encrypted => {
@@ -424,7 +424,7 @@ export class VaultWorker {
        /**
        * Parse inbound message from main thread into typechecked variables.
        */
-       static #extractData (message: unknown) {
+       static _extractData (message: unknown) {
                try {
                        // Message itself
                        if (message == null) {
@@ -479,7 +479,7 @@ export class VaultWorker {
                                : crypto.getRandomValues(new Uint8Array(32)).buffer
 
                        // CryptoKey from password, decryption key if unlocking else encryption key
-                       return this.#createAesKey(action === 'unlock' ? 'decrypt' : 'encrypt', keySalt, password)
+                       return this._createAesKey(action === 'unlock' ? 'decrypt' : 'encrypt', keySalt, password)
                                .then(key => {
                                        if (password?.detached === false) {
                                                new Uint8Array(password).fill(0)
@@ -582,9 +582,9 @@ export class VaultWorker {
        * Encrypts an existing seed or mnemonic+salt and returns the initialization
        * vector, salt, and encrypted data representing the wallet in a locked state.
        */
-       static #load (type?: 'BIP-44' | 'BLAKE2b', key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise<NamedData<ArrayBuffer>> {
+       static _load (type?: 'BIP-44' | 'BLAKE2b', key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise<NamedData<ArrayBuffer>> {
                try {
-                       if (!this.#locked) {
+                       if (!this.locked) {
                                throw new Error('Wallet is in use')
                        }
                        if (key == null || keySalt == null) {
@@ -612,13 +612,13 @@ export class VaultWorker {
                                        throw new RangeError('Seed for BLAKE2b wallet must be 32 bytes')
                                }
                        }
-                       this.#type = type
+                       this.type = type
                        let seed: Promise<ArrayBuffer>
                        if (secret instanceof ArrayBuffer) {
                                if (type === 'BLAKE2b') {
                                        seed = Bip39.fromEntropy(new Uint8Array(secret))
                                                .then(bip39 => {
-                                                       this.#mnemonic = utf8.toBuffer(bip39.phrase ?? '')
+                                                       this.mnemonic = utf8.toBuffer(bip39.phrase ?? '')
                                                        return secret
                                                })
                                } else {
@@ -627,7 +627,7 @@ export class VaultWorker {
                        } else {
                                seed = Bip39.fromPhrase(secret)
                                        .then(bip39 => {
-                                               this.#mnemonic = utf8.toBuffer(bip39.phrase ?? '')
+                                               this.mnemonic = utf8.toBuffer(bip39.phrase ?? '')
                                                const derive = type === 'BIP-44'
                                                        ? bip39.toBip39Seed(mnemonicSalt ?? '')
                                                        : Promise.resolve(bip39.toBlake2bSeed())
@@ -635,8 +635,8 @@ export class VaultWorker {
                                        })
                        }
                        return seed.then(seed => {
-                               this.#seed = seed
-                               return this.#encryptWallet(key)
+                               this.seed = seed
+                               return this._encryptWallet(key)
                                        .then(({ iv, encrypted }) => {
                                                return { iv, salt: keySalt, encrypted }
                                        })