1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | import {MessageDecoder} from "./protobuf/message_decoder.js";
|
7 | import {ByteBuffer} from "protobufjs";
|
8 | import type {Messages} from "./protobuf/messages.js";
|
9 | import type {MessageFromTrezor} from "../transport";
|
10 |
|
11 | const MESSAGE_HEADER_BYTE: number = 0x23;
|
12 |
|
13 |
|
14 | class PartiallyParsedInput {
|
15 |
|
16 | typeNumber: number;
|
17 |
|
18 | expectedLength: number;
|
19 |
|
20 |
|
21 | buffer: ByteBuffer;
|
22 | constructor(typeNumber: number, length: number) {
|
23 | this.typeNumber = typeNumber;
|
24 | this.expectedLength = length;
|
25 | this.buffer = new ByteBuffer(length);
|
26 | }
|
27 | isDone(): boolean {
|
28 | return (this.buffer.offset >= this.expectedLength);
|
29 | }
|
30 | append(buffer: ByteBuffer):void {
|
31 | this.buffer.append(buffer);
|
32 | }
|
33 | arrayBuffer(): ArrayBuffer {
|
34 | const byteBuffer: ByteBuffer = this.buffer;
|
35 | byteBuffer.reset();
|
36 | return byteBuffer.toArrayBuffer();
|
37 | }
|
38 | }
|
39 |
|
40 |
|
41 | function parseFirstInput(bytes: ArrayBuffer): PartiallyParsedInput {
|
42 |
|
43 | const byteBuffer: ByteBuffer = ByteBuffer.concat([bytes]);
|
44 |
|
45 |
|
46 | const sharp1: number = byteBuffer.readByte();
|
47 | const sharp2: number = byteBuffer.readByte();
|
48 | if (sharp1 !== MESSAGE_HEADER_BYTE || sharp2 !== MESSAGE_HEADER_BYTE) {
|
49 | throw new Error(`Didn't receive expected header signature.`);
|
50 | }
|
51 |
|
52 |
|
53 | const type: number = byteBuffer.readUint16();
|
54 | const length: number = byteBuffer.readUint32();
|
55 |
|
56 |
|
57 | const res: PartiallyParsedInput = new PartiallyParsedInput(type, length);
|
58 | res.append(byteBuffer);
|
59 | return res;
|
60 | }
|
61 |
|
62 |
|
63 |
|
64 | async function receiveRest(
|
65 | parsedInput: PartiallyParsedInput,
|
66 | receiver: () => Promise<ArrayBuffer>
|
67 | ): Promise<void> {
|
68 | if (parsedInput.isDone()) {
|
69 | return;
|
70 | }
|
71 | const data = await receiver();
|
72 |
|
73 |
|
74 | if (data == null) {
|
75 | throw new Error(`Received no data.`);
|
76 | }
|
77 |
|
78 | parsedInput.append(data);
|
79 | return receiveRest(parsedInput, receiver);
|
80 | }
|
81 |
|
82 |
|
83 | async function receiveBuffer(
|
84 | receiver: () => Promise<ArrayBuffer>
|
85 | ): Promise<PartiallyParsedInput> {
|
86 | const data = await receiver();
|
87 | const partialInput: PartiallyParsedInput = parseFirstInput(data);
|
88 |
|
89 | await receiveRest(partialInput, receiver);
|
90 | return partialInput;
|
91 | }
|
92 |
|
93 |
|
94 | export async function receiveAndParse(
|
95 | messages: Messages,
|
96 | receiver: () => Promise<ArrayBuffer>
|
97 | ): Promise<MessageFromTrezor> {
|
98 | const received = await receiveBuffer(receiver);
|
99 | const typeId: number = received.typeNumber;
|
100 | const buffer: ArrayBuffer = received.arrayBuffer();
|
101 | const decoder: MessageDecoder = new MessageDecoder(messages, typeId, buffer);
|
102 | return {
|
103 | message: decoder.decodedJSON(),
|
104 | type: decoder.messageName(),
|
105 | };
|
106 | }
|