//! 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({
/**
* @returns {string} UTF-8 encoded text string
*/
toUtf8 (bytes: Bytes): string {
- return decoder.decode(bytes)
+ return decode(bytes)
},
})
//! 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.
* @returns {Uint8Array} Byte array representation of the input string
*/
toBytes (utf8: string): Bytes {
- return encoder.encode(utf8)
+ return encode(utf8)
},
/**
//! 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
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
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 = {
}
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
}
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 => {
//! 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 => {
}
// 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)
//! 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)
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')
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)