]> git.codecow.com Git - libnemo.git/commitdiff
Store mnemonic as buffer and convert as needed.
authorChris Duncan <chris@zoso.dev>
Fri, 8 Aug 2025 21:34:30 +0000 (14:34 -0700)
committerChris Duncan <chris@zoso.dev>
Fri, 8 Aug 2025 21:34:30 +0000 (14:34 -0700)
src/lib/safe.ts

index 08e6cebd5d5af68b4609d24e34b37dc281a05555..5b965bcae1d0d7107f1a45fc1298b9155e65040e 100644 (file)
@@ -19,7 +19,7 @@ export class Safe {
        static #locked: boolean = true
        static #type?: 'BIP-44' | 'BLAKE2b'
        static #seed?: ArrayBuffer
-       static #mnemonic?: string
+       static #mnemonic?: ArrayBuffer
        static #parentPort?: any
        static {
                NODE: this.#parentPort = parentPort
@@ -116,7 +116,7 @@ export class Safe {
                        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) }
+                       return { ...record, seed: this.#seed.slice(), mnemonic: this.#mnemonic.slice() }
                } catch (err) {
                        throw new Error('Failed to create wallet', { cause: err })
                } finally {
@@ -227,8 +227,8 @@ export class Safe {
                        if (!(this.#seed instanceof ArrayBuffer)) {
                                throw new TypeError('Invalid seed')
                        }
-                       if (this.#mnemonic != null && typeof this.#mnemonic !== 'string') {
-                               throw new TypeError('Invalid seed')
+                       if (this.#mnemonic != null && !(this.#mnemonic instanceof ArrayBuffer)) {
+                               throw new TypeError('Invalid mnemonic')
                        }
                        this.#locked = false
                        return { isUnlocked: !this.#locked }
@@ -283,13 +283,19 @@ export class Safe {
                                let diff = 0
                                const userSeed = new Uint8Array(seed)
                                const thisSeed = new Uint8Array(this.#seed)
-                               for (let i = 0; i < seed.byteLength; i++) {
+                               for (let i = 0; i < userSeed.byteLength; i++) {
                                        diff |= userSeed[i] ^ thisSeed[i]
                                }
                                isVerified = diff === 0
                        }
-                       if (mnemonicPhrase != null && mnemonicPhrase === this.#mnemonic) {
-                               isVerified = true
+                       if (mnemonicPhrase != null) {
+                               let diff = 0
+                               const userMnemonic = utf8.toBytes(mnemonicPhrase)
+                               const thisMnemonic = new Uint8Array(this.#mnemonic ?? [])
+                               for (let i = 0; i < userMnemonic.byteLength; i++) {
+                                       diff |= userMnemonic[i] ^ thisMnemonic[i]
+                               }
+                               isVerified = diff === 0
                        }
                        return { isVerified }
                } catch (err) {
@@ -318,7 +324,7 @@ export class Safe {
                const additionalData = utf8.toBytes(type)
                const decrypted = new Uint8Array(await crypto.subtle.decrypt({ name: 'AES-GCM', iv, additionalData }, key, encrypted))
                this.#seed = decrypted.buffer.slice(0, seedLength)
-               this.#mnemonic = bytes.toUtf8(decrypted.slice(seedLength))
+               this.#mnemonic = decrypted.buffer.slice(seedLength)
                decrypted.fill(0)
        }
 
@@ -351,7 +357,7 @@ export class Safe {
                        throw new Error('Wallet seed not found')
                }
                const seed = new Uint8Array(this.#seed)
-               const mnemonic = utf8.toBytes(this.#mnemonic ?? '')
+               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)
@@ -527,11 +533,11 @@ export class Safe {
                        if (secret instanceof ArrayBuffer) {
                                this.#seed = secret
                                if (type === 'BLAKE2b') {
-                                       this.#mnemonic = (await Bip39.fromEntropy(new Uint8Array(secret))).phrase
+                                       this.#mnemonic = utf8.toBuffer((await Bip39.fromEntropy(new Uint8Array(secret))).phrase ?? '')
                                }
                        } else {
                                const mnemonic = await Bip39.fromPhrase(secret)
-                               this.#mnemonic = mnemonic.phrase
+                               this.#mnemonic = utf8.toBuffer(mnemonic.phrase ?? '')
                                this.#seed = type === 'BIP-44'
                                        ? (await mnemonic.toBip39Seed(mnemonicSalt ?? '')).buffer
                                        : (await mnemonic.toBlake2bSeed()).buffer