@hazae41/ledger
Version:
Private and supply-chain hardened Ledger controller for TypeScript
98 lines (95 loc) • 3.27 kB
JavaScript
import { Opaque, Readable } from '@hazae41/binary';
import { Cursor } from '@hazae41/cursor';
class InvalidHidTagError extends Error {
tag;
#class = InvalidHidTagError;
name = this.#class.name;
constructor(tag) {
super(`Invalid HID tag ${tag}`);
this.tag = tag;
}
}
class HidFrame {
channel;
fragment;
index;
#class = HidFrame;
static tag = 0x05;
constructor(channel, fragment, index) {
this.channel = channel;
this.fragment = fragment;
this.index = index;
}
sizeOrThrow() {
return 2 + 1 + 2 + this.fragment.sizeOrThrow();
}
writeOrThrow(cursor) {
cursor.writeUint16OrThrow(this.channel);
cursor.writeUint8OrThrow(this.#class.tag);
cursor.writeUint16OrThrow(this.index);
this.fragment.writeOrThrow(cursor);
}
static readOrThrow(cursor) {
const channel = cursor.readUint16OrThrow();
const tag = cursor.readUint8OrThrow();
if (tag !== this.tag)
throw new InvalidHidTagError(tag);
const index = cursor.readUint16OrThrow();
const bytes = cursor.readAndCopyOrThrow(cursor.remaining);
const fragment = new Opaque(bytes);
return new HidFrame(channel, fragment, index);
}
static *splitOrThrow(channel, bytes) {
const chunks = new Cursor(bytes).splitOrThrow(59);
let chunk = chunks.next();
for (let i = 0; !chunk.done; chunk = chunks.next(), i++)
yield new HidFrame(channel, new Opaque(chunk.value), i);
return chunk.value;
}
static async unsplitOrThrow(channel, generator) {
const first = await generator.next();
if (first.done)
return first.value;
const frames = Readable.readFromBytesOrThrow(HidContainer, first.value.fragment.bytes);
const bytes = new Uint8Array(frames.length);
const cursor = new Cursor(bytes);
cursor.writeOrThrow(frames.fragment.bytes.slice(0, cursor.remaining));
if (!cursor.remaining)
return cursor.bytes;
let frame = await generator.next();
for (; !frame.done; frame = await generator.next()) {
cursor.writeOrThrow(frame.value.fragment.bytes.slice(0, cursor.remaining));
if (!cursor.remaining)
return cursor.bytes;
continue;
}
return frame.value;
}
}
class HidContainer {
length;
fragment;
constructor(length, fragment) {
this.length = length;
this.fragment = fragment;
}
static newOrThrow(fragment) {
return new HidContainer(fragment.sizeOrThrow(), fragment);
}
sizeOrThrow() {
return Math.ceil((2 + this.length) / 59) * 59;
}
writeOrThrow(cursor) {
cursor.writeUint16OrThrow(this.length);
this.fragment.writeOrThrow(cursor);
cursor.fillOrThrow(0, cursor.remaining);
}
static readOrThrow(cursor) {
const length = cursor.readUint16OrThrow();
const bytes = cursor.readAndCopyOrThrow(cursor.remaining);
const fragment = new Opaque(bytes);
return new HidContainer(length, fragment);
}
}
export { HidContainer, HidFrame, InvalidHidTagError };
//# sourceMappingURL=index.mjs.map