From 193eaa052f8f5045b7df097f4c64b0dc38c82469 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Sat, 4 Jul 2026 02:04:08 -0700 Subject: [PATCH] Extract codec set to tools. --- src/lib/convert/bytes.ts | 5 ++--- src/lib/convert/utf8.ts | 5 ++--- src/lib/crypto/bip39.ts | 8 ++++---- src/lib/crypto/bip44.ts | 4 ++-- src/lib/crypto/wallet-aes-gcm.ts | 7 +++---- src/lib/ledger/open.ts | 5 +++-- src/lib/tools.ts | 11 +++++++++++ src/lib/vault/vault-worker.ts | 3 +-- 8 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/lib/convert/bytes.ts b/src/lib/convert/bytes.ts index 7b693bc..3ff0855 100644 --- a/src/lib/convert/bytes.ts +++ b/src/lib/convert/bytes.ts @@ -2,8 +2,7 @@ //! SPDX-License-Identifier: GPL-3.0-or-later import { ALPHABET, HEXCHAR } from '../constants' - -const decoder: TextDecoder = new TextDecoder() +import { decode } from '../tools' export const bytes = Object.freeze({ /** @@ -105,6 +104,6 @@ export const bytes = Object.freeze({ * @returns {string} UTF-8 encoded text string */ toUtf8 (bytes: Bytes): string { - return decoder.decode(bytes) + return decode(bytes) }, }) diff --git a/src/lib/convert/utf8.ts b/src/lib/convert/utf8.ts index 0a9d1b1..2223be4 100644 --- a/src/lib/convert/utf8.ts +++ b/src/lib/convert/utf8.ts @@ -1,10 +1,9 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later +import { encode } from '../tools' import { bytes } from './bytes' -const encoder: TextEncoder = new TextEncoder() - export const utf8 = Object.freeze({ /** * Convert a UTF-8 text string to an ArrayBuffer. @@ -23,7 +22,7 @@ export const utf8 = Object.freeze({ * @returns {Uint8Array} Byte array representation of the input string */ toBytes (utf8: string): Bytes { - return encoder.encode(utf8) + return encode(utf8) }, /** diff --git a/src/lib/crypto/bip39.ts b/src/lib/crypto/bip39.ts index 9805510..19e2c0f 100644 --- a/src/lib/crypto/bip39.ts +++ b/src/lib/crypto/bip39.ts @@ -2,13 +2,13 @@ //! SPDX-License-Identifier: GPL-3.0-or-later import { bytes } from '../convert' +import { encode } from '../tools' import { wordlist } from './wordlist' + /** * Represents a mnemonic phrase that identifies a wallet as defined by BIP-39. */ export class Bip39 { - encoder: TextEncoder = new TextEncoder() - /** * SHA-256 hash of entropy that is appended to the entropy and subsequently * used to generate the mnemonic phrase. @@ -194,13 +194,13 @@ export class Bip39 { return Promise.resolve(seed) } else { const salt = (typeof passphrase === 'string') ? passphrase : '' - const keyData = this.encoder.encode(this.phrase) + const keyData = encode(this.phrase) return crypto.subtle.importKey('raw', keyData, 'PBKDF2', false, ['deriveBits', 'deriveKey']) .then(phraseKey => { const algorithm: Pbkdf2Params = { name: 'PBKDF2', hash: 'SHA-512', - salt: this.encoder.encode(`mnemonic${salt.normalize('NFKD')}`), + salt: encode(`mnemonic${salt.normalize('NFKD')}`), iterations: 2048 } return crypto.subtle.deriveBits(algorithm, phraseKey, 512) diff --git a/src/lib/crypto/bip44.ts b/src/lib/crypto/bip44.ts index e831369..11de053 100644 --- a/src/lib/crypto/bip44.ts +++ b/src/lib/crypto/bip44.ts @@ -3,6 +3,7 @@ import { Point, getPublicKey as secp256k1_getPublicKey } from '@noble/secp256k1' import { dec } from '../convert' +import { encode } from '../tools' type Curve = 'Bitcoin seed' | 'ed25519 seed' type ExtendedKey = { @@ -12,7 +13,6 @@ type ExtendedKey = { } const BIP44_PURPOSE: 44 = 44 const HARDENED_OFFSET: 0x80000000 = 0x80000000 -const ENCODER: TextEncoder = new TextEncoder() /** * Derives a private child key for a coin by following the specified BIP-32 and @@ -76,7 +76,7 @@ function ckd (curve: Curve, seed: ArrayBuffer, coin: number, account: number, ch } function slip10 (curve: string, S: ArrayBuffer): Promise { - const key = ENCODER.encode(curve).buffer + const key = encode(curve).buffer const data = S return hmac(key, data) .then(I => { diff --git a/src/lib/crypto/wallet-aes-gcm.ts b/src/lib/crypto/wallet-aes-gcm.ts index 914309e..7a3869d 100644 --- a/src/lib/crypto/wallet-aes-gcm.ts +++ b/src/lib/crypto/wallet-aes-gcm.ts @@ -3,12 +3,11 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later +import { encode } from "../tools" export class WalletAesGcm { - static encoder: TextEncoder = new TextEncoder() - static decrypt (type: string, key: CryptoKey, iv: ArrayBuffer, encrypted: ArrayBuffer): Promise> { const seedLength = type === 'BLAKE2b' ? 32 : 64 - const additionalData = this.encoder.encode(type) + const additionalData = encode(type) return crypto.subtle .decrypt({ name: 'AES-GCM', iv, additionalData }, key, encrypted) .then(decrypted => { @@ -31,7 +30,7 @@ export class WalletAesGcm { } // restrict iv to 96 bits per GCM best practice const iv = crypto.getRandomValues(new Uint8Array(12)).buffer - const additionalData = this.encoder.encode(type) + const additionalData = encode(type) const encoded = new Uint8Array([...new Uint8Array(seed), ...new Uint8Array(mnemonic ?? [])]) return crypto.subtle .encrypt({ name: 'AES-GCM', iv, additionalData }, key, encoded) diff --git a/src/lib/ledger/open.ts b/src/lib/ledger/open.ts index f2f4c0d..59edc68 100644 --- a/src/lib/ledger/open.ts +++ b/src/lib/ledger/open.ts @@ -2,11 +2,12 @@ //! SPDX-License-Identifier: GPL-3.0-or-later import { TransportStatusError } from '@ledgerhq/errors' -import { APDU_CODES, LedgerResponse, LedgerTransport, LISTEN_TIMEOUT, OPEN_TIMEOUT, STATUS_CODES } from '.' +import { APDU_CODES, LISTEN_TIMEOUT, LedgerResponse, LedgerTransport, OPEN_TIMEOUT, STATUS_CODES } from '.' import { bytes } from '../convert' +import { encode } from '../tools' export async function _open (transport: LedgerTransport): Promise { - const name = new TextEncoder().encode('Nano') + const name = encode('Nano') const t = await transport.create(OPEN_TIMEOUT, LISTEN_TIMEOUT) const response = await t .send(0xe0, 0xd8, APDU_CODES.paramUnused, APDU_CODES.paramUnused, name as Buffer) diff --git a/src/lib/tools.ts b/src/lib/tools.ts index 60410ce..b48d722 100644 --- a/src/lib/tools.ts +++ b/src/lib/tools.ts @@ -15,6 +15,17 @@ type SweepResult = { message: string } +const decoder = Object.freeze(new TextDecoder()) +const encoder = Object.freeze(new TextEncoder()) + +export function decode (input?: AllowSharedBufferSource, options?: TextDecodeOptions): string { + return decoder.decode(input, options) +} + +export function encode (input?: string): Bytes { + return encoder.encode(input) +} + export class Tools { static #normalize (input: string | ArrayBuffer | Bytes): Bytes { return (typeof input === 'string') diff --git a/src/lib/vault/vault-worker.ts b/src/lib/vault/vault-worker.ts index 621493c..514471a 100644 --- a/src/lib/vault/vault-worker.ts +++ b/src/lib/vault/vault-worker.ts @@ -6,13 +6,12 @@ import { parentPort, threadId } from 'node:worker_threads' import { BIP44_COIN_NANO } from '../constants' import { dec } from '../convert' import { Bip39, Bip44, Blake2b, WalletAesGcm } from '../crypto' +import { encode } from '../tools' import { WalletType } from '../wallet' import { parseAction, parseData, parseIv, parseKeySalt, parseType } from './parsers' import { Passkey } from './passkey' import { VaultTimer } from './vault-timer' -const encoder = new TextEncoder() -const encode = (input?: string): Bytes => encoder.encode(input) let _locked: boolean = true let _timeout: number = 120_000 let _timer: VaultTimer = new VaultTimer(() => { }, 0) -- 2.52.0