From: Chris Duncan Date: Tue, 9 Sep 2025 21:01:19 +0000 (-0700) Subject: Add support for Ledger psuedo-unlock in Vault so that Wallet isLocked still works... X-Git-Tag: v0.10.5~22^2~9 X-Git-Url: https://git.codecow.com/?a=commitdiff_plain;h=93a68f62a50488301a7c390a4decc966ea714c67;p=libnemo.git Add support for Ledger psuedo-unlock in Vault so that Wallet isLocked still works regardless of wallet type. --- diff --git a/src/lib/vault/vault-worker.ts b/src/lib/vault/vault-worker.ts index c229a91..fb837d7 100644 --- a/src/lib/vault/vault-worker.ts +++ b/src/lib/vault/vault-worker.ts @@ -1,7 +1,7 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -import { NamedData } from '#types' +import { NamedData, WalletType } from '#types' import { BIP44_COIN_NANO } from '../constants' import { Bip39, Bip44, Blake2b, NanoNaCl, WalletAesGcm } from '../crypto' import { Passkey } from './passkey' @@ -111,7 +111,10 @@ export class VaultWorker { * Generates a new mnemonic and seed and then returns the initialization vector * vector, salt, and encrypted data representing the wallet in a locked state. */ - create (type?: 'BIP-44' | 'BLAKE2b', key?: CryptoKey, keySalt?: ArrayBuffer, mnemonicSalt?: string): Promise> { + create (type?: WalletType, key?: CryptoKey, keySalt?: ArrayBuffer, mnemonicSalt?: string): Promise> { + if (type !== 'BIP-44' && type !== 'BLAKE2b') { + throw new TypeError('Unsupported software wallet algorithm', { cause: type }) + } try { const entropy = crypto.getRandomValues(new Uint8Array(32)) return Bip39.fromEntropy(entropy) @@ -170,7 +173,10 @@ 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. */ - load (type?: 'BIP-44' | 'BLAKE2b', key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise> { + load (type?: WalletType, key?: CryptoKey, keySalt?: ArrayBuffer, secret?: string | ArrayBuffer, mnemonicSalt?: string): Promise> { + if (type !== 'BIP-44' && type !== 'BLAKE2b') { + throw new TypeError('Unsupported software wallet algorithm', { cause: type }) + } return this.#load(type, key, keySalt, secret, mnemonicSalt) .then(record => { if (this.#seed == null) { @@ -236,6 +242,12 @@ export class VaultWorker { if (type == null) { throw new TypeError('Wallet type is required') } + if (type === 'Ledger') { + this.#locked = false + BROWSER: postMessage('unlocked') + NODE: this.#parentPort?.postMessage('unlocked') + return Promise.resolve() + } if (key == null) { throw new TypeError('Wallet password is required') } @@ -554,7 +566,7 @@ export class VaultWorker { // Algorithm used for wallet functions #parseType (action: string, data: { [key: string]: unknown }) { if (['create', 'load', 'unlock'].includes(action)) { - if (data.type !== 'BIP-44' && data.type !== 'BLAKE2b') { + if (data.type !== 'BIP-44' && data.type !== 'BLAKE2b' && data.type !== 'Ledger') { throw new TypeError(`Type is required to ${action} wallet`) } } else if (data.type !== undefined) { diff --git a/src/lib/wallet/unlock.ts b/src/lib/wallet/unlock.ts index c53d5fd..bf5ba37 100644 --- a/src/lib/wallet/unlock.ts +++ b/src/lib/wallet/unlock.ts @@ -1,6 +1,7 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later +import { NamedData } from '#types' import { utf8 } from '../convert' import { Vault } from '../vault' import { Wallet } from '../wallet' @@ -9,28 +10,33 @@ import { _get } from './get' export async function _unlock (wallet: Wallet, vault: Vault, password?: string): Promise export async function _unlock (wallet: Wallet, vault: Vault, password: unknown): Promise { try { + const data: NamedData = { + action: 'unlock', + type: wallet.type + } if (wallet.type === 'Ledger') { const { Ledger } = await import('./ledger') const status = await Ledger.connect() if (await status !== 'CONNECTED') { throw new Error('Failed to unlock wallet', { cause: status }) } + data.password = new ArrayBuffer(0) + data.iv = new ArrayBuffer(0) + data.keySalt = new ArrayBuffer(0) + data.encrypted = new ArrayBuffer(0) } else { if (typeof password !== 'string') { throw new TypeError('Password must be a string') } const { iv, salt, encrypted } = await _get(wallet.id) - await vault.request({ - action: 'unlock', - type: wallet.type, - password: utf8.toBuffer(password), - iv, - keySalt: salt, - encrypted - }) - if (wallet.isLocked) { - throw new Error('Unlock request to Vault failed') - } + data.password = utf8.toBuffer(password) + data.iv = iv + data.keySalt = salt + data.encrypted = encrypted + } + await vault.request(data) + if (wallet.isLocked) { + throw new Error('Unlock request failed') } } catch (err) { throw new Error('Failed to unlock wallet', { cause: err }) diff --git a/test/test.ledger.mjs b/test/test.ledger.mjs index 8736eea..3b39498 100644 --- a/test/test.ledger.mjs +++ b/test/test.ledger.mjs @@ -49,6 +49,7 @@ await Promise.all([ await test('request permissions', async () => { await assert.rejects(wallet.unlock(), 'expect DISCONNECTED') + assert.equal(wallet.isLocked, true) await assert.rejects(async () => { await click( @@ -56,6 +57,7 @@ await Promise.all([ async () => wallet.unlock() ) }, 'expect BUSY') + assert.equal(wallet.isLocked, true) await assert.rejects(async () => { await click( @@ -63,6 +65,7 @@ await Promise.all([ async () => wallet.unlock() ) }, 'expect LOCKED') + assert.equal(wallet.isLocked, true) await assert.resolves(async () => { await click( @@ -70,6 +73,7 @@ await Promise.all([ async () => wallet.unlock() ) }, 'expect CONNECTED') + assert.equal(wallet.isLocked, false) }) await test('get first account', async () => {