From 6a422394de413646d9f487f643e5c94cca26e455 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Fri, 8 Aug 2025 14:34:30 -0700 Subject: [PATCH] Store mnemonic as buffer and convert as needed. --- src/lib/safe.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/lib/safe.ts b/src/lib/safe.ts index 08e6ceb..5b965bc 100644 --- a/src/lib/safe.ts +++ b/src/lib/safe.ts @@ -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 -- 2.47.3