From 7eb761e7b98c7de7fde445777710876940fc4e28 Mon Sep 17 00:00:00 2001 From: Chris Duncan Date: Thu, 2 Jul 2026 11:37:15 -0700 Subject: [PATCH] Refactor bytes to decimal integer. --- src/lib/convert/bytes.ts | 34 +++++++++++++++------------------- src/lib/crypto/bip39.ts | 13 +++++++------ src/lib/ledger/account.ts | 4 ++-- src/lib/ledger/cache.ts | 7 ++++--- src/lib/ledger/close.ts | 7 ++++--- src/lib/ledger/open.ts | 7 ++++--- src/lib/ledger/sign.ts | 2 +- src/lib/ledger/version.ts | 2 +- 8 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/lib/convert/bytes.ts b/src/lib/convert/bytes.ts index 9c406c5..033a818 100644 --- a/src/lib/convert/bytes.ts +++ b/src/lib/convert/bytes.ts @@ -71,25 +71,6 @@ export const bytes = Object.freeze({ return output }, - /** - * Sum an array of bytes to a decimal integer. If the result is larger than - * Number.MAX_SAFE_INTEGER, it will be returned as a bigint. - * - * @param {Bytes} bytes - Byte array to convert - * @returns {bigint|number} Decimal sum of the literal byte values - */ - toDec (bytes: Bytes): bigint | number { - let int = 0n - for (let i = bytes.byteLength; i > 0; i--) { - int += BigInt(bytes[i - 1]) << (BigInt(bytes.byteLength - i) << 3n) - } - if (int > 9007199254740991n) { - return int - } else { - return Number(int) - } - }, - /** * Convert a Uint8Array of bytes to a hexadecimal string. * @@ -105,6 +86,21 @@ export const bytes = Object.freeze({ return hex }, + /** + * Sum an array of bytes to a big integer. + * + * @param {Bytes} bytes - Byte array to convert + * @returns {bigint} Sum of the byte values + */ + toInt (bytes: Bytes): bigint { + let int = 0n + for (let i = 0; i < bytes.byteLength; i++) { + const offset = BigInt(bytes.byteLength - 1 - i) << 3n + int |= BigInt(bytes[i]) << offset + } + return int + }, + /** * Convert a Uint8Array of bytes to a UTF-8 text string. * diff --git a/src/lib/crypto/bip39.ts b/src/lib/crypto/bip39.ts index c1dcd1e..a00c755 100644 --- a/src/lib/crypto/bip39.ts +++ b/src/lib/crypto/bip39.ts @@ -1,7 +1,8 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -import { wordlist } from "./wordlist" +import { bytes } from '../convert' +import { wordlist } from './wordlist' /** * Represents a mnemonic phrase that identifies a wallet as defined by BIP-39. */ @@ -15,7 +16,7 @@ export class Bip39 { * @param {Bytes} entropy - Cryptographically strong pseudorandom data of length N bits * @returns {Promise} First N/32 bits of the hash as a bigint */ - static checksum (entropy: Bytes): Promise { + static checksum (entropy: ArrayBuffer | Bytes): Promise { const checksumBitLength = (entropy.byteLength / 4) | 0 return crypto.subtle.digest('SHA-256', entropy) .then(hash => { @@ -39,7 +40,7 @@ export class Bip39 { if (entropy instanceof ArrayBuffer) { entropy = new Uint8Array(entropy) } - if (!(entropy instanceof Uint8Array)) { + if (!bytes.is(entropy)) { throw new TypeError('Invalid entropy') } if (entropy.byteLength < 16 || entropy.byteLength > 32) { @@ -52,7 +53,7 @@ export class Bip39 { const phraseLength = 0.75 * entropy.byteLength return this.checksum(entropy) .then(checksum => { - const bigEntropy = entropy.reduce((a, b) => a = (a << 8n) | BigInt(b), 0n) + const bigEntropy = bytes.toInt(entropy) let concatenation: bigint = (bigEntropy << BigInt(entropy.byteLength) / 4n) | checksum const words: string[] = [] @@ -191,7 +192,7 @@ export class Bip39 { } if (this.#bip39Seed != null) { const seed = format === 'hex' - ? [...this.#bip39Seed].map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase() + ? bytes.toHex(this.#bip39Seed) : this.#bip39Seed return Promise.resolve(seed) } else { @@ -248,7 +249,7 @@ export class Bip39 { } } return format === 'hex' - ? [...this.#blake2bSeed].map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase() + ? bytes.toHex(this.#blake2bSeed) : this.#blake2bSeed } } diff --git a/src/lib/ledger/account.ts b/src/lib/ledger/account.ts index ba155a6..0a651f1 100644 --- a/src/lib/ledger/account.ts +++ b/src/lib/ledger/account.ts @@ -1,7 +1,7 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! SPDX-License-Identifier: GPL-3.0-or-later -import { APDU_CODES, DERIVATION_PATH, LedgerAccountResponse, LedgerTransport, STATUS_CODES, LISTEN_TIMEOUT, OPEN_TIMEOUT } from '.' +import { APDU_CODES, DERIVATION_PATH, LedgerAccountResponse, LedgerTransport, LISTEN_TIMEOUT, OPEN_TIMEOUT, STATUS_CODES } from '.' import { HARDENED_OFFSET } from '../constants' import { bytes, dec } from '../convert' @@ -19,7 +19,7 @@ export async function _account (transport: LedgerTransport, index: number = 0, s .catch((err: any) => dec.toBytes(err.statusCode)) .finally(async () => await t.close()) as Uint8Array - const statusCode = bytes.toDec(response.slice(-2)) as number + const statusCode = Number(bytes.toInt(response.slice(-2))) const status = STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' if (status !== 'OK') { return { status, publicKey: null, address: null } diff --git a/src/lib/ledger/cache.ts b/src/lib/ledger/cache.ts index 11b4d3f..255f909 100644 --- a/src/lib/ledger/cache.ts +++ b/src/lib/ledger/cache.ts @@ -1,6 +1,7 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! 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 { Account } from '../account' import { Block } from '../block' @@ -35,9 +36,9 @@ export async function _cache (transport: LedgerTransport, index: number = 0, blo const t = await transport.create(OPEN_TIMEOUT, LISTEN_TIMEOUT) const response = await t .send(APDU_CODES.class, APDU_CODES.cacheBlock, APDU_CODES.paramUnused, APDU_CODES.paramUnused, data as Buffer) - .then((res: Buffer) => bytes.toDec(bytes.assert(res))) - .catch((err: any) => err.statusCode) - .finally(async () => await t.close()) as number + .then((res) => Number(bytes.toInt(bytes.assert(res)))) + .catch((err: TransportStatusError) => err.statusCode) + .finally(async () => await t.close()) const status = STATUS_CODES[response] if (status !== 'OK') { diff --git a/src/lib/ledger/close.ts b/src/lib/ledger/close.ts index be819cc..6626783 100644 --- a/src/lib/ledger/close.ts +++ b/src/lib/ledger/close.ts @@ -1,6 +1,7 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! 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 { bytes } from '../convert' @@ -8,8 +9,8 @@ export async function _close (transport: LedgerTransport): Promise bytes.toDec(bytes.assert(res))) - .catch((err: any) => err.statusCode) - .finally(async () => await t.close()) as number + .then((res: Buffer) => Number(bytes.toInt(bytes.assert(res)))) + .catch((err: TransportStatusError) => err.statusCode) + .finally(async () => await t.close()) return new Promise(r => setTimeout(r, 1000, { status: STATUS_CODES[response] })) } diff --git a/src/lib/ledger/open.ts b/src/lib/ledger/open.ts index 52effa8..f2f4c0d 100644 --- a/src/lib/ledger/open.ts +++ b/src/lib/ledger/open.ts @@ -1,6 +1,7 @@ //! SPDX-FileCopyrightText: 2025 Chris Duncan //! 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 { bytes } from '../convert' @@ -9,8 +10,8 @@ export async function _open (transport: LedgerTransport): Promise bytes.toDec(bytes.assert(res))) - .catch((err: any) => err.statusCode) - .finally(async () => await t.close()) as number + .then((res: Buffer) => Number(bytes.toInt(bytes.assert(res)))) + .catch((err: TransportStatusError) => err.statusCode) + .finally(async () => await t.close()) return new Promise(r => setTimeout(r, 1000, { status: STATUS_CODES[response] })) } diff --git a/src/lib/ledger/sign.ts b/src/lib/ledger/sign.ts index 8c8a553..ccd4f34 100644 --- a/src/lib/ledger/sign.ts +++ b/src/lib/ledger/sign.ts @@ -78,7 +78,7 @@ async function req (transport: LedgerTransport, command: typeof APDU_CODES.signB .send(APDU_CODES.class, command, APDU_CODES.paramUnused, APDU_CODES.paramUnused, data) .catch((err: any) => dec.toBytes(err.statusCode)) .finally(async () => await t.close()) as Uint8Array - const statusCode = bytes.toDec(response.slice(-2)) as number + const statusCode = Number(bytes.toInt(response.slice(-2))) const status = STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' if (status !== 'OK') { throw new Error('Signing with ledger failed', { cause: status }) diff --git a/src/lib/ledger/version.ts b/src/lib/ledger/version.ts index 0c6745c..f618034 100644 --- a/src/lib/ledger/version.ts +++ b/src/lib/ledger/version.ts @@ -24,7 +24,7 @@ export async function _version (transport: LedgerTransport): Promise dec.toBytes(err.statusCode)) .finally(async () => await t.close()) as Uint8Array - const statusCode = bytes.toDec(response.slice(-2)) as number + const statusCode = Number(bytes.toInt(response.slice(-2))) const status = STATUS_CODES[statusCode] ?? 'UNKNOWN_ERROR' if (status !== 'OK') { return { status, name: null, version: null } -- 2.52.0