const decoder: TextDecoder = new TextDecoder()
export const bytes = Object.freeze({
+ /**
+ * Type guard to narrow ArrayBufferLike to ArrayBuffer.
+ *
+ * @param {unknown} input - Variable to check
+ */
+ assert (input: unknown): Bytes {
+ if (!(input instanceof Uint8Array && input.buffer instanceof ArrayBuffer)) {
+ throw TypeError('input is not Uint8Array<ArrayBuffer>')
+ }
+ return input as Bytes
+ },
+
/**
* Write zeroes to memory to erase bytes and then transfers the buffer to
* render it inaccessible to any process.
bytes.fill(0)
},
+ /**
+ * Type guard to narrow ArrayBufferLike to ArrayBuffer.
+ *
+ * @param {unknown} input - Variable to check
+ * @returns True if input is Uint8Array<ArrayBuffer>, else false
+ */
+ is (input: unknown): input is Bytes {
+ return input instanceof Uint8Array && input.buffer instanceof ArrayBuffer
+ },
+
/**
* Convert a Uint8Aarray of bytes to a base32 string.
*
//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later AND ISC
+import { bytes } from '../convert'
+
const OUTBYTES_MIN = 1
const OUTBYTES_MAX = 64
const KEYBYTES_MIN = 1
[14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]
]
-function isBytes (a: unknown): a is Bytes {
- return a instanceof Uint8Array && a.buffer instanceof ArrayBuffer
-}
-
/**
* Implementation derived from blake2b@2.1.4. Copyright 2017 Emil Bay
* <github@tixz.dk> (https://github.com/emilbayes/blake2b). See LICENSES/ISC.txt
if (typeof length !== 'number' || length < OUTBYTES_MIN || length > OUTBYTES_MAX) {
throw new TypeError(`length is required and must be a number between ${OUTBYTES_MIN}-${OUTBYTES_MAX}`)
}
- if (key !== undefined && (!isBytes(key) || key.length < KEYBYTES_MIN || key.length > KEYBYTES_MAX)) {
+ if (key !== undefined && (!bytes.is(key) || key.length < KEYBYTES_MIN || key.length > KEYBYTES_MAX)) {
throw new RangeError(`key must be ${KEYBYTES_MIN}-${KEYBYTES_MAX} bytes`)
}
- if (salt !== undefined && (!isBytes(salt) || salt.length !== SALTBYTES)) {
+ if (salt !== undefined && (!bytes.is(salt) || salt.length !== SALTBYTES)) {
throw new RangeError(`salt must be ${SALTBYTES} bytes`)
}
- if (personal !== undefined && (!isBytes(personal) || personal.length !== PERSONALBYTES)) {
+ if (personal !== undefined && (!bytes.is(personal) || personal.length !== PERSONALBYTES)) {
throw new RangeError(`personal must be ${PERSONALBYTES} bytes`)
}
this.#blake2bInit(length, key, salt, personal)
if (input instanceof ArrayBuffer) {
input = new Uint8Array(input)
}
- if (!isBytes(input)) {
+ if (!bytes.is(input)) {
throw new TypeError('input must be bytes')
}
this.#blake2bUpdate(input)
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(res))
+ .then((res: Buffer) => bytes.toDec(bytes.assert(res)))
.catch((err: any) => err.statusCode)
.finally(async () => await t.close()) as number
//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
-import { LedgerResponse, LedgerTransport, OPEN_TIMEOUT, LISTEN_TIMEOUT, APDU_CODES, STATUS_CODES } from '.'
+import { APDU_CODES, LedgerResponse, LedgerTransport, LISTEN_TIMEOUT, OPEN_TIMEOUT, STATUS_CODES } from '.'
import { bytes } from '../convert'
export async function _close (transport: LedgerTransport): Promise<LedgerResponse> {
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(res))
+ .then((res: Buffer) => bytes.toDec(bytes.assert(res)))
.catch((err: any) => err.statusCode)
.finally(async () => await t.close()) as number
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 { LedgerResponse, LedgerTransport, OPEN_TIMEOUT, LISTEN_TIMEOUT, APDU_CODES, STATUS_CODES } from '.'
+import { APDU_CODES, LedgerResponse, LedgerTransport, LISTEN_TIMEOUT, OPEN_TIMEOUT, STATUS_CODES } from '.'
import { bytes } from '../convert'
export async function _open (transport: LedgerTransport): Promise<LedgerResponse> {
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(res))
+ .then((res: Buffer) => bytes.toDec(bytes.assert(res)))
.catch((err: any) => err.statusCode)
.finally(async () => await t.close()) as number
return new Promise(r => setTimeout(r, 1000, { status: STATUS_CODES[response] }))