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.
*
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.
*
//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>\r
//! SPDX-License-Identifier: GPL-3.0-or-later\r
\r
-import { wordlist } from "./wordlist"\r
+import { bytes } from '../convert'\r
+import { wordlist } from './wordlist'\r
/**\r
* Represents a mnemonic phrase that identifies a wallet as defined by BIP-39.\r
*/\r
* @param {Bytes} entropy - Cryptographically strong pseudorandom data of length N bits\r
* @returns {Promise<bigint>} First N/32 bits of the hash as a bigint\r
*/\r
- static checksum (entropy: Bytes): Promise<bigint> {\r
+ static checksum (entropy: ArrayBuffer | Bytes): Promise<bigint> {\r
const checksumBitLength = (entropy.byteLength / 4) | 0\r
return crypto.subtle.digest('SHA-256', entropy)\r
.then(hash => {\r
if (entropy instanceof ArrayBuffer) {\r
entropy = new Uint8Array(entropy)\r
}\r
- if (!(entropy instanceof Uint8Array)) {\r
+ if (!bytes.is(entropy)) {\r
throw new TypeError('Invalid entropy')\r
}\r
if (entropy.byteLength < 16 || entropy.byteLength > 32) {\r
const phraseLength = 0.75 * entropy.byteLength\r
return this.checksum(entropy)\r
.then(checksum => {\r
- const bigEntropy = entropy.reduce((a, b) => a = (a << 8n) | BigInt(b), 0n)\r
+ const bigEntropy = bytes.toInt(entropy)\r
\r
let concatenation: bigint = (bigEntropy << BigInt(entropy.byteLength) / 4n) | checksum\r
const words: string[] = []\r
}\r
if (this.#bip39Seed != null) {\r
const seed = format === 'hex'\r
- ? [...this.#bip39Seed].map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase()\r
+ ? bytes.toHex(this.#bip39Seed)\r
: this.#bip39Seed\r
return Promise.resolve(seed)\r
} else {\r
}\r
}\r
return format === 'hex'\r
- ? [...this.#blake2bSeed].map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase()\r
+ ? bytes.toHex(this.#blake2bSeed)\r
: this.#blake2bSeed\r
}\r
}\r
//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! 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'
.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 }
//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! 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'
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') {
//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! 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'
const t = await transport.create(OPEN_TIMEOUT, LISTEN_TIMEOUT)
const response = await t
.send(0xb0, 0xa7, APDU_CODES.paramUnused, APDU_CODES.paramUnused)
- .then((res: Buffer) => 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] }))
}
//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! 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'
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)
- .then((res: Buffer) => 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] }))
}
.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 })
.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, name: null, version: null }