]> git.codecow.com Git - libnemo.git/commitdiff
Extract wallet import to private method called by both create and import API methods.
authorChris Duncan <chris@zoso.dev>
Fri, 8 Aug 2025 18:29:25 +0000 (11:29 -0700)
committerChris Duncan <chris@zoso.dev>
Fri, 8 Aug 2025 18:29:25 +0000 (11:29 -0700)
src/lib/safe.ts

index 55c8b29769c79a1d602a84e99cdc8d427176e7f2..aea0ea9f4a7bfb4d200ca8c01d5dda00cbcc4c69 100644 (file)
@@ -112,13 +112,15 @@ export class Safe {
                try {
                        const entropy = crypto.getRandomValues(new Uint8Array(32))
                        const { phrase: mnemonicPhrase } = await Bip39.fromEntropy(entropy)
-                       const record = await this.import(type, key, keySalt, mnemonicPhrase, mnemonicSalt)
+                       const record = await this.#import(type, key, keySalt, mnemonicPhrase, mnemonicSalt)
                        if (this.#seed == null || this.#mnemonic == null) {
                                throw new Error('Failed to generate seed and mnemonic')
                        }
                        return { ...record, seed: this.#seed.slice(), mnemonic: utf8.toBuffer(this.#mnemonic) }
                } catch (err) {
                        throw new Error('Failed to create wallet', { cause: err })
+               } finally {
+                       this.lock()
                }
        }
 
@@ -157,52 +159,15 @@ export class Safe {
        */
        static async import (type?: 'BIP-44' | 'BLAKE2b', key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise<NamedData<ArrayBuffer>> {
                try {
-                       if (!this.#locked) {
-                               throw new Error('Wallet is in use')
-                       }
-                       if (key == null || keySalt == null) {
-                               throw new Error('Wallet password is required')
-                       }
-                       if (type == null) {
-                               throw new TypeError('Wallet type is required')
-                       }
-                       if (type !== 'BIP-44' && type !== 'BLAKE2b') {
-                               throw new TypeError('Invalid wallet type')
-                       }
-                       if (secret == null) {
-                               throw new TypeError('Seed or mnemonic is required')
-                       }
-                       if (typeof secret !== 'string' && mnemonicSalt !== undefined) {
-                               throw new TypeError('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')
-                               }
-                       }
-                       if (type === 'BLAKE2b') {
-                               if (secret instanceof ArrayBuffer && secret.byteLength !== 32) {
-                                       throw new RangeError('Seed for BLAKE2b wallet must be 32 bytes')
-                               }
-                       }
-                       this.#type = type
-                       if (secret instanceof ArrayBuffer) {
-                               this.#seed = secret
-                               if (type === 'BLAKE2b') {
-                                       this.#mnemonic = (await Bip39.fromEntropy(new Uint8Array(secret))).phrase
-                               }
-                       } else {
-                               const mnemonic = await Bip39.fromPhrase(secret)
-                               this.#mnemonic = mnemonic.phrase
-                               this.#seed = type === 'BIP-44'
-                                       ? (await mnemonic.toBip39Seed(mnemonicSalt ?? '')).buffer
-                                       : (await mnemonic.toBlake2bSeed()).buffer
+                       const record = await this.#import(type, key, keySalt, secret, mnemonicSalt)
+                       if (this.#seed == null) {
+                               throw new Error('Wallet seed not found')
                        }
-                       const { iv, encrypted } = await this.#encryptWallet(key)
-                       return { iv, salt: keySalt, encrypted }
+                       return record
                } catch (err) {
-                       this.lock()
                        throw new Error('Failed to import wallet', { cause: err })
+               } finally {
+                       this.lock()
                }
        }
 
@@ -522,6 +487,60 @@ export class Safe {
 
                return { action, type, key, iv, keySalt, seed, mnemonicPhrase, mnemonicSalt, encrypted, index, data }
        }
+
+       /**
+       * Encrypts an existing seed or mnemonic+salt and returns the initialization
+       * vector, salt, and encrypted data representing the wallet in a locked state.
+       */
+       static async #import (type?: 'BIP-44' | 'BLAKE2b', key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise<NamedData<ArrayBuffer>> {
+               try {
+                       if (!this.#locked) {
+                               throw new Error('Wallet is in use')
+                       }
+                       if (key == null || keySalt == null) {
+                               throw new Error('Wallet password is required')
+                       }
+                       if (type == null) {
+                               throw new TypeError('Wallet type is required')
+                       }
+                       if (type !== 'BIP-44' && type !== 'BLAKE2b') {
+                               throw new TypeError('Invalid wallet type')
+                       }
+                       if (secret == null) {
+                               throw new TypeError('Seed or mnemonic is required')
+                       }
+                       if (typeof secret !== 'string' && mnemonicSalt !== undefined) {
+                               throw new TypeError('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')
+                               }
+                       }
+                       if (type === 'BLAKE2b') {
+                               if (secret instanceof ArrayBuffer && secret.byteLength !== 32) {
+                                       throw new RangeError('Seed for BLAKE2b wallet must be 32 bytes')
+                               }
+                       }
+                       this.#type = type
+                       if (secret instanceof ArrayBuffer) {
+                               this.#seed = secret
+                               if (type === 'BLAKE2b') {
+                                       this.#mnemonic = (await Bip39.fromEntropy(new Uint8Array(secret))).phrase
+                               }
+                       } else {
+                               const mnemonic = await Bip39.fromPhrase(secret)
+                               this.#mnemonic = mnemonic.phrase
+                               this.#seed = type === 'BIP-44'
+                                       ? (await mnemonic.toBip39Seed(mnemonicSalt ?? '')).buffer
+                                       : (await mnemonic.toBlake2bSeed()).buffer
+                       }
+                       const { iv, encrypted } = await this.#encryptWallet(key)
+                       return { iv, salt: keySalt, encrypted }
+               } catch (err) {
+                       throw new Error('Failed to import wallet', { cause: err })
+               }
+       }
 }
 
 let importWorkerThreads = ''