]> git.codecow.com Git - libnemo.git/commitdiff
Extract codec set to tools.
authorChris Duncan <chris@codecow.com>
Sat, 4 Jul 2026 09:04:08 +0000 (02:04 -0700)
committerChris Duncan <chris@codecow.com>
Sat, 4 Jul 2026 09:04:08 +0000 (02:04 -0700)
src/lib/convert/bytes.ts
src/lib/convert/utf8.ts
src/lib/crypto/bip39.ts
src/lib/crypto/bip44.ts
src/lib/crypto/wallet-aes-gcm.ts
src/lib/ledger/open.ts
src/lib/tools.ts
src/lib/vault/vault-worker.ts

index 7b693bc8e60ae723d169b78aa9a528a6fa746bb6..3ff0855ca32f8cb5f6eed720a5aa65b23823b87c 100644 (file)
@@ -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)
        },
 })
index 0a9d1b1cb341703ed6574f06c765b92160feccc7..2223be46cc1e8916204da6e891851586549e9b15 100644 (file)
@@ -1,10 +1,9 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
 //! 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)
        },
 
        /**
index 98055102fa15147881c036b342f3a416e2a8cae5..19e2c0f346b8959dc8c569a885dbbdafddbc4cb3 100644 (file)
@@ -2,13 +2,13 @@
 //! SPDX-License-Identifier: GPL-3.0-or-later\r
 \r
 import { bytes } from '../convert'\r
+import { encode } from '../tools'\r
 import { wordlist } from './wordlist'\r
+\r
 /**\r
 * Represents a mnemonic phrase that identifies a wallet as defined by BIP-39.\r
 */\r
 export class Bip39 {\r
-       encoder: TextEncoder = new TextEncoder()\r
-\r
        /**\r
         * SHA-256 hash of entropy that is appended to the entropy and subsequently\r
         * used to generate the mnemonic phrase.\r
@@ -194,13 +194,13 @@ export class Bip39 {
                        return Promise.resolve(seed)\r
                } else {\r
                        const salt = (typeof passphrase === 'string') ? passphrase : ''\r
-                       const keyData = this.encoder.encode(this.phrase)\r
+                       const keyData = encode(this.phrase)\r
                        return crypto.subtle.importKey('raw', keyData, 'PBKDF2', false, ['deriveBits', 'deriveKey'])\r
                                .then(phraseKey => {\r
                                        const algorithm: Pbkdf2Params = {\r
                                                name: 'PBKDF2',\r
                                                hash: 'SHA-512',\r
-                                               salt: this.encoder.encode(`mnemonic${salt.normalize('NFKD')}`),\r
+                                               salt: encode(`mnemonic${salt.normalize('NFKD')}`),\r
                                                iterations: 2048\r
                                        }\r
                                        return crypto.subtle.deriveBits(algorithm, phraseKey, 512)\r
index e8313690f3d45b74b61dfdf3e8d87b5e2fff407b..11de053a8b6ecc9c822c138f1f454cd4c0c13c79 100644 (file)
@@ -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<ExtendedKey> {
-       const key = ENCODER.encode(curve).buffer
+       const key = encode(curve).buffer
        const data = S
        return hmac(key, data)
                .then(I => {
index 914309eade2f28b15e167763e73a7202c33412f1..7a3869dde482e7ca52e2689d09938eac51ca18b0 100644 (file)
@@ -3,12 +3,11 @@
 //! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
 //! 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<Record<string, ArrayBuffer>> {
                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)
index f2f4c0d47499fbc7baec574c0fca43c599b90454..59edc68a31ce4a9baa018b91d66c6078dcd42599 100644 (file)
@@ -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<LedgerResponse> {
-       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)
index 60410cef563790b6c204d2c1b1919f1655285fa3..b48d7226d717e9d55a6f9962cb92568e3dfd1c3c 100644 (file)
@@ -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')
index 621493c440b26f6b19effdd028c719c1fb6fc712..514471a3a3fd0a0be97c7b78839803db2c573447 100644 (file)
@@ -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)