@ledgerhq/hw-ledger-key-ring-protocol 0.2.1-nightly.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +33 -0
- package/.turbo/turbo-build.log +4 -0
- package/.unimportedrc.json +4 -0
- package/CHANGELOG.md +126 -0
- package/LICENSE.txt +21 -0
- package/README.md +3 -0
- package/jest.config.js +13 -0
- package/lib/ApduDevice.d.ts +99 -0
- package/lib/ApduDevice.d.ts.map +1 -0
- package/lib/ApduDevice.js +532 -0
- package/lib/ApduDevice.js.map +1 -0
- package/lib/BigEndian.d.ts +7 -0
- package/lib/BigEndian.d.ts.map +1 -0
- package/lib/BigEndian.js +26 -0
- package/lib/BigEndian.js.map +1 -0
- package/lib/CommandBlock.d.ts +114 -0
- package/lib/CommandBlock.d.ts.map +1 -0
- package/lib/CommandBlock.js +173 -0
- package/lib/CommandBlock.js.map +1 -0
- package/lib/CommandStream.d.ts +38 -0
- package/lib/CommandStream.d.ts.map +1 -0
- package/lib/CommandStream.js +197 -0
- package/lib/CommandStream.js.map +1 -0
- package/lib/CommandStreamDecoder.d.ts +15 -0
- package/lib/CommandStreamDecoder.d.ts.map +1 -0
- package/lib/CommandStreamDecoder.js +101 -0
- package/lib/CommandStreamDecoder.js.map +1 -0
- package/lib/CommandStreamEncoder.d.ts +16 -0
- package/lib/CommandStreamEncoder.d.ts.map +1 -0
- package/lib/CommandStreamEncoder.js +131 -0
- package/lib/CommandStreamEncoder.js.map +1 -0
- package/lib/CommandStreamJsonifier.d.ts +6 -0
- package/lib/CommandStreamJsonifier.d.ts.map +1 -0
- package/lib/CommandStreamJsonifier.js +75 -0
- package/lib/CommandStreamJsonifier.js.map +1 -0
- package/lib/CommandStreamResolver.d.ts +53 -0
- package/lib/CommandStreamResolver.d.ts.map +1 -0
- package/lib/CommandStreamResolver.js +221 -0
- package/lib/CommandStreamResolver.js.map +1 -0
- package/lib/Crypto.d.ts +38 -0
- package/lib/Crypto.d.ts.map +1 -0
- package/lib/Crypto.js +47 -0
- package/lib/Crypto.js.map +1 -0
- package/lib/Device.d.ts +43 -0
- package/lib/Device.d.ts.map +1 -0
- package/lib/Device.js +203 -0
- package/lib/Device.js.map +1 -0
- package/lib/IndexedTree.d.ts +13 -0
- package/lib/IndexedTree.d.ts.map +1 -0
- package/lib/IndexedTree.js +75 -0
- package/lib/IndexedTree.js.map +1 -0
- package/lib/NobleCrypto.d.ts +41 -0
- package/lib/NobleCrypto.d.ts.map +1 -0
- package/lib/NobleCrypto.js +298 -0
- package/lib/NobleCrypto.js.map +1 -0
- package/lib/PublicKey.d.ts +5 -0
- package/lib/PublicKey.d.ts.map +1 -0
- package/lib/PublicKey.js +10 -0
- package/lib/PublicKey.js.map +1 -0
- package/lib/SeedId.d.ts +80 -0
- package/lib/SeedId.d.ts.map +1 -0
- package/lib/SeedId.js +244 -0
- package/lib/SeedId.js.map +1 -0
- package/lib/StreamTree.d.ts +50 -0
- package/lib/StreamTree.d.ts.map +1 -0
- package/lib/StreamTree.js +169 -0
- package/lib/StreamTree.js.map +1 -0
- package/lib/StreamTreeCipher.d.ts +46 -0
- package/lib/StreamTreeCipher.d.ts.map +1 -0
- package/lib/StreamTreeCipher.js +179 -0
- package/lib/StreamTreeCipher.js.map +1 -0
- package/lib/__tests__/codec.d.ts +2 -0
- package/lib/__tests__/codec.d.ts.map +1 -0
- package/lib/__tests__/codec.js +108 -0
- package/lib/__tests__/codec.js.map +1 -0
- package/lib/__tests__/crypto.d.ts +2 -0
- package/lib/__tests__/crypto.d.ts.map +1 -0
- package/lib/__tests__/crypto.js +46 -0
- package/lib/__tests__/crypto.js.map +1 -0
- package/lib/__tests__/indexed_tree.d.ts +2 -0
- package/lib/__tests__/indexed_tree.d.ts.map +1 -0
- package/lib/__tests__/indexed_tree.js +45 -0
- package/lib/__tests__/indexed_tree.js.map +1 -0
- package/lib/__tests__/key_exchange.d.ts +2 -0
- package/lib/__tests__/key_exchange.d.ts.map +1 -0
- package/lib/__tests__/key_exchange.js +129 -0
- package/lib/__tests__/key_exchange.js.map +1 -0
- package/lib/__tests__/seedId.d.ts +2 -0
- package/lib/__tests__/seedId.d.ts.map +1 -0
- package/lib/__tests__/seedId.js +92 -0
- package/lib/__tests__/seedId.js.map +1 -0
- package/lib/__tests__/shared_object.d.ts +2 -0
- package/lib/__tests__/shared_object.d.ts.map +1 -0
- package/lib/__tests__/shared_object.js +78 -0
- package/lib/__tests__/shared_object.js.map +1 -0
- package/lib/index.d.ts +35 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +81 -0
- package/lib/index.js.map +1 -0
- package/lib/tlv.d.ts +99 -0
- package/lib/tlv.d.ts.map +1 -0
- package/lib/tlv.js +150 -0
- package/lib/tlv.js.map +1 -0
- package/lib-es/ApduDevice.d.ts +99 -0
- package/lib-es/ApduDevice.d.ts.map +1 -0
- package/lib-es/ApduDevice.js +526 -0
- package/lib-es/ApduDevice.js.map +1 -0
- package/lib-es/BigEndian.d.ts +7 -0
- package/lib-es/BigEndian.d.ts.map +1 -0
- package/lib-es/BigEndian.js +23 -0
- package/lib-es/BigEndian.js.map +1 -0
- package/lib-es/CommandBlock.d.ts +114 -0
- package/lib-es/CommandBlock.d.ts.map +1 -0
- package/lib-es/CommandBlock.js +160 -0
- package/lib-es/CommandBlock.js.map +1 -0
- package/lib-es/CommandStream.d.ts +38 -0
- package/lib-es/CommandStream.d.ts.map +1 -0
- package/lib-es/CommandStream.js +189 -0
- package/lib-es/CommandStream.js.map +1 -0
- package/lib-es/CommandStreamDecoder.d.ts +15 -0
- package/lib-es/CommandStreamDecoder.d.ts.map +1 -0
- package/lib-es/CommandStreamDecoder.js +97 -0
- package/lib-es/CommandStreamDecoder.js.map +1 -0
- package/lib-es/CommandStreamEncoder.d.ts +16 -0
- package/lib-es/CommandStreamEncoder.d.ts.map +1 -0
- package/lib-es/CommandStreamEncoder.js +127 -0
- package/lib-es/CommandStreamEncoder.js.map +1 -0
- package/lib-es/CommandStreamJsonifier.d.ts +6 -0
- package/lib-es/CommandStreamJsonifier.d.ts.map +1 -0
- package/lib-es/CommandStreamJsonifier.js +72 -0
- package/lib-es/CommandStreamJsonifier.js.map +1 -0
- package/lib-es/CommandStreamResolver.d.ts +53 -0
- package/lib-es/CommandStreamResolver.d.ts.map +1 -0
- package/lib-es/CommandStreamResolver.js +216 -0
- package/lib-es/CommandStreamResolver.js.map +1 -0
- package/lib-es/Crypto.d.ts +38 -0
- package/lib-es/Crypto.d.ts.map +1 -0
- package/lib-es/Crypto.js +43 -0
- package/lib-es/Crypto.js.map +1 -0
- package/lib-es/Device.d.ts +43 -0
- package/lib-es/Device.d.ts.map +1 -0
- package/lib-es/Device.js +195 -0
- package/lib-es/Device.js.map +1 -0
- package/lib-es/IndexedTree.d.ts +13 -0
- package/lib-es/IndexedTree.d.ts.map +1 -0
- package/lib-es/IndexedTree.js +71 -0
- package/lib-es/IndexedTree.js.map +1 -0
- package/lib-es/NobleCrypto.d.ts +41 -0
- package/lib-es/NobleCrypto.d.ts.map +1 -0
- package/lib-es/NobleCrypto.js +267 -0
- package/lib-es/NobleCrypto.js.map +1 -0
- package/lib-es/PublicKey.d.ts +5 -0
- package/lib-es/PublicKey.d.ts.map +1 -0
- package/lib-es/PublicKey.js +6 -0
- package/lib-es/PublicKey.js.map +1 -0
- package/lib-es/SeedId.d.ts +80 -0
- package/lib-es/SeedId.d.ts.map +1 -0
- package/lib-es/SeedId.js +235 -0
- package/lib-es/SeedId.js.map +1 -0
- package/lib-es/StreamTree.d.ts +50 -0
- package/lib-es/StreamTree.d.ts.map +1 -0
- package/lib-es/StreamTree.js +165 -0
- package/lib-es/StreamTree.js.map +1 -0
- package/lib-es/StreamTreeCipher.d.ts +46 -0
- package/lib-es/StreamTreeCipher.d.ts.map +1 -0
- package/lib-es/StreamTreeCipher.js +175 -0
- package/lib-es/StreamTreeCipher.js.map +1 -0
- package/lib-es/__tests__/codec.d.ts +2 -0
- package/lib-es/__tests__/codec.d.ts.map +1 -0
- package/lib-es/__tests__/codec.js +106 -0
- package/lib-es/__tests__/codec.js.map +1 -0
- package/lib-es/__tests__/crypto.d.ts +2 -0
- package/lib-es/__tests__/crypto.d.ts.map +1 -0
- package/lib-es/__tests__/crypto.js +44 -0
- package/lib-es/__tests__/crypto.js.map +1 -0
- package/lib-es/__tests__/indexed_tree.d.ts +2 -0
- package/lib-es/__tests__/indexed_tree.d.ts.map +1 -0
- package/lib-es/__tests__/indexed_tree.js +43 -0
- package/lib-es/__tests__/indexed_tree.js.map +1 -0
- package/lib-es/__tests__/key_exchange.d.ts +2 -0
- package/lib-es/__tests__/key_exchange.d.ts.map +1 -0
- package/lib-es/__tests__/key_exchange.js +124 -0
- package/lib-es/__tests__/key_exchange.js.map +1 -0
- package/lib-es/__tests__/seedId.d.ts +2 -0
- package/lib-es/__tests__/seedId.d.ts.map +1 -0
- package/lib-es/__tests__/seedId.js +90 -0
- package/lib-es/__tests__/seedId.js.map +1 -0
- package/lib-es/__tests__/shared_object.d.ts +2 -0
- package/lib-es/__tests__/shared_object.d.ts.map +1 -0
- package/lib-es/__tests__/shared_object.js +76 -0
- package/lib-es/__tests__/shared_object.js.map +1 -0
- package/lib-es/index.d.ts +35 -0
- package/lib-es/index.d.ts.map +1 -0
- package/lib-es/index.js +32 -0
- package/lib-es/index.js.map +1 -0
- package/lib-es/tlv.d.ts +99 -0
- package/lib-es/tlv.d.ts.map +1 -0
- package/lib-es/tlv.js +144 -0
- package/lib-es/tlv.js.map +1 -0
- package/package.json +63 -0
- package/src/ApduDevice.ts +692 -0
- package/src/BigEndian.ts +25 -0
- package/src/CommandBlock.ts +247 -0
- package/src/CommandStream.ts +262 -0
- package/src/CommandStreamDecoder.ts +142 -0
- package/src/CommandStreamEncoder.ts +144 -0
- package/src/CommandStreamJsonifier.ts +82 -0
- package/src/CommandStreamResolver.ts +284 -0
- package/src/Crypto.ts +78 -0
- package/src/Device.ts +254 -0
- package/src/IndexedTree.ts +80 -0
- package/src/NobleCrypto.ts +294 -0
- package/src/PublicKey.ts +6 -0
- package/src/SeedId.ts +338 -0
- package/src/StreamTree.ts +212 -0
- package/src/StreamTreeCipher.ts +207 -0
- package/src/__tests__/codec.ts +146 -0
- package/src/__tests__/crypto.ts +44 -0
- package/src/__tests__/indexed_tree.ts +51 -0
- package/src/__tests__/key_exchange.ts +167 -0
- package/src/__tests__/seedId.ts +120 -0
- package/src/__tests__/shared_object.ts +118 -0
- package/src/index.ts +43 -0
- package/src/tlv.ts +210 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Command,
|
|
3
|
+
CommandBlock,
|
|
4
|
+
CommandType,
|
|
5
|
+
AddMember,
|
|
6
|
+
Derive,
|
|
7
|
+
EditMember,
|
|
8
|
+
PublishKey,
|
|
9
|
+
Seed,
|
|
10
|
+
} from "./CommandBlock";
|
|
11
|
+
import { Device } from "./Device";
|
|
12
|
+
import { PublicKey } from "./PublicKey";
|
|
13
|
+
import Transport from "@ledgerhq/hw-transport";
|
|
14
|
+
import { CommandStreamEncoder } from "./CommandStreamEncoder";
|
|
15
|
+
import { KeyPair, crypto } from "./Crypto";
|
|
16
|
+
import { StreamTree } from "./StreamTree";
|
|
17
|
+
import { TLV, TLVField } from "./tlv";
|
|
18
|
+
import { SeedIdResult, parseSeedIdResult } from "./SeedId";
|
|
19
|
+
|
|
20
|
+
export const TRUSTCHAIN_APP_NAME = "Ledger Sync";
|
|
21
|
+
|
|
22
|
+
enum ParseStreamMode {
|
|
23
|
+
BlockHeader = 0x00,
|
|
24
|
+
Command = 0x01,
|
|
25
|
+
Signature = 0x02,
|
|
26
|
+
Empty = 0x03,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
enum OutputDataMode {
|
|
30
|
+
None = 0x00,
|
|
31
|
+
TrustedParam = 0x01,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const TP_ENCRYPT = 1 << 7;
|
|
35
|
+
|
|
36
|
+
enum TrustedPropertiesTLV {
|
|
37
|
+
IV = 0x00,
|
|
38
|
+
IssuerPublicKey = 0x01 | TP_ENCRYPT,
|
|
39
|
+
Xpriv = 0x02 | TP_ENCRYPT,
|
|
40
|
+
EphemeralPublicKey = 0x03,
|
|
41
|
+
CommandIV = 0x04,
|
|
42
|
+
GroupKey = 0x05,
|
|
43
|
+
TrustedMember = 0x06 | TP_ENCRYPT,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface TrustedMember {
|
|
47
|
+
iv: Uint8Array;
|
|
48
|
+
data: Uint8Array;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
interface TrustedParams {
|
|
52
|
+
members: Map<string, TrustedMember>;
|
|
53
|
+
lastTrustedMember: string | undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface SignatureResponse {
|
|
57
|
+
signature: Uint8Array;
|
|
58
|
+
sessionKey: Uint8Array;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface SignBlockHeaderResponse {
|
|
62
|
+
iv: Uint8Array;
|
|
63
|
+
issuer: Uint8Array;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface SeedCommandResponse {
|
|
67
|
+
iv: Uint8Array;
|
|
68
|
+
xpriv: Uint8Array;
|
|
69
|
+
commandIv: Uint8Array;
|
|
70
|
+
ephemeralPublicKey: Uint8Array;
|
|
71
|
+
groupKey: Uint8Array;
|
|
72
|
+
trustedMember: Uint8Array | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
interface EmptyCommandResponse {}
|
|
76
|
+
|
|
77
|
+
interface AddMemberCommandResponse {
|
|
78
|
+
iv: Uint8Array;
|
|
79
|
+
trustedMember: Uint8Array;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
interface PublishKeyCommandResponse {
|
|
83
|
+
trustedMember: Uint8Array | null;
|
|
84
|
+
iv: Uint8Array;
|
|
85
|
+
xpriv: Uint8Array;
|
|
86
|
+
commandIv: Uint8Array;
|
|
87
|
+
ephemeralPublicKey: Uint8Array;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type CommandResponse =
|
|
91
|
+
| SeedCommandResponse
|
|
92
|
+
| AddMemberCommandResponse
|
|
93
|
+
| PublishKeyCommandResponse
|
|
94
|
+
| EmptyCommandResponse;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
*
|
|
98
|
+
*/
|
|
99
|
+
export class APDU {
|
|
100
|
+
static CLA = 0xe0;
|
|
101
|
+
|
|
102
|
+
static INS_GET_PUBLIC_KEY = 0x05;
|
|
103
|
+
static INS_PARSE_STREAM = 0x08;
|
|
104
|
+
static INS_SIGN_BLOCK = 0x07;
|
|
105
|
+
static INS_INIT = 0x06;
|
|
106
|
+
static INS_SET_TRUSTED_MEMBER = 0x09;
|
|
107
|
+
|
|
108
|
+
static async setTrustedMember(transport: Transport, member: TrustedMember): Promise<void> {
|
|
109
|
+
const payload = new Uint8Array([
|
|
110
|
+
TrustedPropertiesTLV.IV,
|
|
111
|
+
member.iv.length,
|
|
112
|
+
...member.iv,
|
|
113
|
+
TrustedPropertiesTLV.TrustedMember,
|
|
114
|
+
member.data.length,
|
|
115
|
+
...member.data,
|
|
116
|
+
]);
|
|
117
|
+
await transport.send(APDU.CLA, APDU.INS_SET_TRUSTED_MEMBER, 0, 0, Buffer.from(payload));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static async parseBlockHeader(transport: Transport, header: Uint8Array) {
|
|
121
|
+
const result = await transport.send(
|
|
122
|
+
APDU.CLA,
|
|
123
|
+
APDU.INS_PARSE_STREAM,
|
|
124
|
+
ParseStreamMode.BlockHeader,
|
|
125
|
+
OutputDataMode.None,
|
|
126
|
+
Buffer.from(header),
|
|
127
|
+
);
|
|
128
|
+
return APDU.getResponseData(result);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static async parseCommand(
|
|
132
|
+
transport: Transport,
|
|
133
|
+
command: Uint8Array,
|
|
134
|
+
outputTrustedParam: boolean = false,
|
|
135
|
+
) {
|
|
136
|
+
return await transport.send(
|
|
137
|
+
APDU.CLA,
|
|
138
|
+
APDU.INS_PARSE_STREAM,
|
|
139
|
+
ParseStreamMode.Command,
|
|
140
|
+
outputTrustedParam ? OutputDataMode.TrustedParam : OutputDataMode.None,
|
|
141
|
+
Buffer.from(command),
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static async parseSignature(transport: Transport, signature: Uint8Array) {
|
|
146
|
+
return await transport.send(
|
|
147
|
+
APDU.CLA,
|
|
148
|
+
APDU.INS_PARSE_STREAM,
|
|
149
|
+
ParseStreamMode.Signature,
|
|
150
|
+
OutputDataMode.None,
|
|
151
|
+
Buffer.from(signature),
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
static async initFlow(transport: Transport, sessionKey: Uint8Array): Promise<void> {
|
|
156
|
+
await transport.send(APDU.CLA, APDU.INS_INIT, 0x00, 0x00, Buffer.from(sessionKey));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
static async parseEmptyStream(transport: Transport): Promise<void> {
|
|
160
|
+
await transport.send(
|
|
161
|
+
APDU.CLA,
|
|
162
|
+
APDU.INS_PARSE_STREAM,
|
|
163
|
+
ParseStreamMode.Empty,
|
|
164
|
+
OutputDataMode.None,
|
|
165
|
+
Buffer.alloc(0),
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
static async signBlockHeader(
|
|
170
|
+
transport: Transport,
|
|
171
|
+
header: Uint8Array,
|
|
172
|
+
): Promise<SignBlockHeaderResponse> {
|
|
173
|
+
const data = await transport.send(
|
|
174
|
+
APDU.CLA,
|
|
175
|
+
APDU.INS_SIGN_BLOCK,
|
|
176
|
+
ParseStreamMode.BlockHeader,
|
|
177
|
+
OutputDataMode.None,
|
|
178
|
+
Buffer.from(header),
|
|
179
|
+
);
|
|
180
|
+
const resp = APDU.getResponseData(data);
|
|
181
|
+
const tlvs = TLV.readAllTLV(resp, 0);
|
|
182
|
+
let iv: Uint8Array | null = null;
|
|
183
|
+
let issuer: Uint8Array | null = null;
|
|
184
|
+
for (const tlv of tlvs) {
|
|
185
|
+
if (tlv.type === TrustedPropertiesTLV.IV) {
|
|
186
|
+
iv = tlv.value;
|
|
187
|
+
}
|
|
188
|
+
if (tlv.type === TrustedPropertiesTLV.IssuerPublicKey) {
|
|
189
|
+
issuer = tlv.value;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (iv === null) {
|
|
193
|
+
throw new Error("No IV in response");
|
|
194
|
+
}
|
|
195
|
+
if (issuer === null) {
|
|
196
|
+
throw new Error("No issuer in response");
|
|
197
|
+
}
|
|
198
|
+
return { iv, issuer };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static async signCommand(transport: Transport, command: Uint8Array): Promise<Uint8Array> {
|
|
202
|
+
const data = await transport.send(
|
|
203
|
+
APDU.CLA,
|
|
204
|
+
APDU.INS_SIGN_BLOCK,
|
|
205
|
+
ParseStreamMode.Command,
|
|
206
|
+
OutputDataMode.None,
|
|
207
|
+
Buffer.from(command),
|
|
208
|
+
);
|
|
209
|
+
return APDU.getResponseData(data);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
static async finalizeSignature(transport: Transport): Promise<SignatureResponse> {
|
|
213
|
+
const response = await transport.send(
|
|
214
|
+
APDU.CLA,
|
|
215
|
+
APDU.INS_SIGN_BLOCK,
|
|
216
|
+
ParseStreamMode.Signature,
|
|
217
|
+
OutputDataMode.None,
|
|
218
|
+
Buffer.alloc(0),
|
|
219
|
+
);
|
|
220
|
+
const data = APDU.getResponseData(response);
|
|
221
|
+
const sigLen = data[0];
|
|
222
|
+
const signature = data.slice(1, sigLen + 1);
|
|
223
|
+
const sessionKey = data.slice(sigLen + 2);
|
|
224
|
+
|
|
225
|
+
return { signature, sessionKey };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
static async getPublicKey(transport: Transport): Promise<Uint8Array> {
|
|
229
|
+
const response = await transport.send(
|
|
230
|
+
APDU.CLA,
|
|
231
|
+
APDU.INS_GET_PUBLIC_KEY,
|
|
232
|
+
0x00,
|
|
233
|
+
0x00,
|
|
234
|
+
Buffer.alloc(0),
|
|
235
|
+
);
|
|
236
|
+
return APDU.getResponseData(response);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* allows to sign a challenge and get the seed id
|
|
241
|
+
*/
|
|
242
|
+
static async getSeedId(transport: Transport, challenge: Uint8Array): Promise<SeedIdResult> {
|
|
243
|
+
const response = await transport.send(
|
|
244
|
+
APDU.CLA,
|
|
245
|
+
APDU.INS_GET_PUBLIC_KEY,
|
|
246
|
+
0x00,
|
|
247
|
+
0x00,
|
|
248
|
+
Buffer.from(challenge),
|
|
249
|
+
);
|
|
250
|
+
const result = parseSeedIdResult(APDU.getResponseData(response));
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static getResponseData(response: Buffer): Uint8Array {
|
|
255
|
+
return Uint8Array.prototype.slice.call(response, 0, response.length - 2);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
static getStatusWord(response: Buffer): number {
|
|
259
|
+
return response.readUInt16BE(response.length - 2);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
static parseTrustedSeed(tlvs: TLVField[]): SeedCommandResponse {
|
|
263
|
+
let iv: Uint8Array | null = null;
|
|
264
|
+
let xpriv: Uint8Array | null = null;
|
|
265
|
+
let ephemeralPublicKey: Uint8Array | null = null;
|
|
266
|
+
let commandIv: Uint8Array | null = null;
|
|
267
|
+
let groupKey: Uint8Array | null = null;
|
|
268
|
+
let trustedMember: Uint8Array | null = null;
|
|
269
|
+
|
|
270
|
+
for (const tlv of tlvs) {
|
|
271
|
+
switch (tlv.type) {
|
|
272
|
+
case TrustedPropertiesTLV.IV:
|
|
273
|
+
iv = tlv.value;
|
|
274
|
+
break;
|
|
275
|
+
case TrustedPropertiesTLV.Xpriv:
|
|
276
|
+
xpriv = tlv.value;
|
|
277
|
+
break;
|
|
278
|
+
case TrustedPropertiesTLV.EphemeralPublicKey:
|
|
279
|
+
ephemeralPublicKey = tlv.value;
|
|
280
|
+
break;
|
|
281
|
+
case TrustedPropertiesTLV.CommandIV:
|
|
282
|
+
commandIv = tlv.value;
|
|
283
|
+
break;
|
|
284
|
+
case TrustedPropertiesTLV.GroupKey:
|
|
285
|
+
groupKey = tlv.value;
|
|
286
|
+
break;
|
|
287
|
+
case TrustedPropertiesTLV.TrustedMember:
|
|
288
|
+
trustedMember = tlv.value;
|
|
289
|
+
break;
|
|
290
|
+
default:
|
|
291
|
+
throw new Error("Unknown trusted property");
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (iv === null) {
|
|
295
|
+
throw new Error("No IV in response");
|
|
296
|
+
}
|
|
297
|
+
if (xpriv === null) {
|
|
298
|
+
throw new Error("No xpriv in response");
|
|
299
|
+
}
|
|
300
|
+
if (ephemeralPublicKey === null) {
|
|
301
|
+
throw new Error("No ephemeral public key in response");
|
|
302
|
+
}
|
|
303
|
+
if (commandIv === null) {
|
|
304
|
+
throw new Error("No command IV in response");
|
|
305
|
+
}
|
|
306
|
+
if (groupKey === null) {
|
|
307
|
+
throw new Error("No group key in response");
|
|
308
|
+
}
|
|
309
|
+
return { iv, xpriv, ephemeralPublicKey, commandIv, groupKey, trustedMember };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static parseTrustedAddMember(tlvs: TLVField[]): AddMemberCommandResponse {
|
|
313
|
+
let iv: Uint8Array | null = null;
|
|
314
|
+
let trustedMember: Uint8Array | null = null;
|
|
315
|
+
for (const tlv of tlvs) {
|
|
316
|
+
if (tlv.type === TrustedPropertiesTLV.TrustedMember) {
|
|
317
|
+
trustedMember = tlv.value;
|
|
318
|
+
} else if (tlv.type === TrustedPropertiesTLV.IV) {
|
|
319
|
+
iv = tlv.value;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (iv === null) {
|
|
323
|
+
throw new Error("No IV in response");
|
|
324
|
+
}
|
|
325
|
+
if (trustedMember === null) {
|
|
326
|
+
throw new Error("No trusted member in response");
|
|
327
|
+
}
|
|
328
|
+
return { trustedMember, iv };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
static parseTrustedPublishKey(tlvs: TLVField[]): PublishKeyCommandResponse {
|
|
332
|
+
let iv: Uint8Array | null = null;
|
|
333
|
+
let ephemeralPublicKey: Uint8Array | null = null;
|
|
334
|
+
let commandIv: Uint8Array | null = null;
|
|
335
|
+
let trustedMember: Uint8Array | null = null;
|
|
336
|
+
let xpriv: Uint8Array | null = null;
|
|
337
|
+
|
|
338
|
+
for (const tlv of tlvs) {
|
|
339
|
+
switch (tlv.type) {
|
|
340
|
+
case TrustedPropertiesTLV.IV:
|
|
341
|
+
iv = tlv.value;
|
|
342
|
+
break;
|
|
343
|
+
case TrustedPropertiesTLV.EphemeralPublicKey:
|
|
344
|
+
ephemeralPublicKey = tlv.value;
|
|
345
|
+
break;
|
|
346
|
+
case TrustedPropertiesTLV.CommandIV:
|
|
347
|
+
commandIv = tlv.value;
|
|
348
|
+
break;
|
|
349
|
+
case TrustedPropertiesTLV.TrustedMember:
|
|
350
|
+
trustedMember = tlv.value;
|
|
351
|
+
break;
|
|
352
|
+
case TrustedPropertiesTLV.Xpriv:
|
|
353
|
+
xpriv = tlv.value;
|
|
354
|
+
break;
|
|
355
|
+
default:
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (iv === null) {
|
|
360
|
+
throw new Error("No IV in response");
|
|
361
|
+
}
|
|
362
|
+
if (ephemeralPublicKey === null) {
|
|
363
|
+
throw new Error("No ephemeral public key in response");
|
|
364
|
+
}
|
|
365
|
+
if (commandIv === null) {
|
|
366
|
+
throw new Error("No command IV in response");
|
|
367
|
+
}
|
|
368
|
+
if (xpriv === null) {
|
|
369
|
+
throw new Error("No xpriv in response");
|
|
370
|
+
}
|
|
371
|
+
return { iv, ephemeralPublicKey, commandIv, trustedMember, xpriv };
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
static parseTrustedProperties(command: Command, rawProperties: Uint8Array): CommandResponse {
|
|
375
|
+
const tlvs = TLV.readAllTLV(rawProperties, 0);
|
|
376
|
+
switch (command.getType()) {
|
|
377
|
+
case CommandType.Derive:
|
|
378
|
+
case CommandType.Seed:
|
|
379
|
+
return APDU.parseTrustedSeed(tlvs);
|
|
380
|
+
case CommandType.AddMember:
|
|
381
|
+
return APDU.parseTrustedAddMember(tlvs);
|
|
382
|
+
case CommandType.PublishKey:
|
|
383
|
+
return APDU.parseTrustedPublishKey(tlvs);
|
|
384
|
+
case CommandType.CloseStream:
|
|
385
|
+
return {};
|
|
386
|
+
default:
|
|
387
|
+
throw new Error("Unsupported command type");
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
async function injectTrustedProperties(
|
|
393
|
+
command: Command,
|
|
394
|
+
properties: CommandResponse,
|
|
395
|
+
secret: Uint8Array,
|
|
396
|
+
): Promise<Command> {
|
|
397
|
+
switch (command.getType()) {
|
|
398
|
+
case CommandType.Seed: {
|
|
399
|
+
const seedCommand = command as Seed;
|
|
400
|
+
const seedProperties = properties as SeedCommandResponse;
|
|
401
|
+
seedCommand.encryptedXpriv = await crypto.decrypt(
|
|
402
|
+
secret,
|
|
403
|
+
seedProperties.iv,
|
|
404
|
+
seedProperties.xpriv,
|
|
405
|
+
);
|
|
406
|
+
seedCommand.ephemeralPublicKey = seedProperties.ephemeralPublicKey;
|
|
407
|
+
seedCommand.initializationVector = seedProperties.commandIv;
|
|
408
|
+
seedCommand.groupKey = seedProperties.groupKey;
|
|
409
|
+
return seedCommand;
|
|
410
|
+
}
|
|
411
|
+
case CommandType.Derive: {
|
|
412
|
+
const deriveCommand = command as Derive;
|
|
413
|
+
const deriveProperties = properties as SeedCommandResponse;
|
|
414
|
+
deriveCommand.encryptedXpriv = await crypto.decrypt(
|
|
415
|
+
secret,
|
|
416
|
+
deriveProperties.iv,
|
|
417
|
+
deriveProperties.xpriv,
|
|
418
|
+
);
|
|
419
|
+
deriveCommand.ephemeralPublicKey = deriveProperties.ephemeralPublicKey;
|
|
420
|
+
deriveCommand.initializationVector = deriveProperties.commandIv;
|
|
421
|
+
deriveCommand.groupKey = deriveProperties.groupKey;
|
|
422
|
+
return deriveCommand;
|
|
423
|
+
}
|
|
424
|
+
case CommandType.AddMember:
|
|
425
|
+
return command; // No properties to inject
|
|
426
|
+
case CommandType.PublishKey: {
|
|
427
|
+
const publishKeyCommand = command as PublishKey;
|
|
428
|
+
const publishKeyProperties = properties as PublishKeyCommandResponse;
|
|
429
|
+
publishKeyCommand.ephemeralPublicKey = publishKeyProperties.ephemeralPublicKey;
|
|
430
|
+
publishKeyCommand.initializationVector = publishKeyProperties.commandIv;
|
|
431
|
+
publishKeyCommand.encryptedXpriv = await crypto.decrypt(
|
|
432
|
+
secret,
|
|
433
|
+
publishKeyProperties.iv,
|
|
434
|
+
publishKeyProperties.xpriv,
|
|
435
|
+
);
|
|
436
|
+
return publishKeyCommand;
|
|
437
|
+
}
|
|
438
|
+
case CommandType.CloseStream:
|
|
439
|
+
return command; // No properties to inject
|
|
440
|
+
default:
|
|
441
|
+
throw new Error("Unsupported command type");
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function findTrustedMember(params: TrustedParams, member: Uint8Array): TrustedMember | null {
|
|
446
|
+
return params.members.get(crypto.to_hex(member)) || null;
|
|
447
|
+
}
|
|
448
|
+
findTrustedMember;
|
|
449
|
+
|
|
450
|
+
export class ApduDevice implements Device {
|
|
451
|
+
private transport: Transport;
|
|
452
|
+
private sessionKeyPair: Promise<KeyPair>;
|
|
453
|
+
|
|
454
|
+
constructor(transport: Transport) {
|
|
455
|
+
this.transport = transport;
|
|
456
|
+
this.sessionKeyPair = crypto.randomKeypair();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
isPublicKeyAvailable(): boolean {
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
async getPublicKey(): Promise<PublicKey> {
|
|
464
|
+
const publicKey = await APDU.getPublicKey(this.transport);
|
|
465
|
+
return new PublicKey(publicKey);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async getSeedId(data: Uint8Array): Promise<SeedIdResult> {
|
|
469
|
+
return APDU.getSeedId(this.transport, data);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private assertStreamIsValid(stream: CommandBlock[]) {
|
|
473
|
+
const blockToSign = stream.filter(block => block.signature.length == 0).length;
|
|
474
|
+
if (blockToSign !== 1)
|
|
475
|
+
throw new Error(
|
|
476
|
+
"Stream must contain exactly one block to sign. Found " + blockToSign + " blocks to sign.",
|
|
477
|
+
);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
private recordTrustedMember(
|
|
481
|
+
trustedParams: TrustedParams,
|
|
482
|
+
publicKey: Uint8Array,
|
|
483
|
+
responseData: Uint8Array,
|
|
484
|
+
) {
|
|
485
|
+
// Parse an APDU result as TLV and find IV and trusted member data.
|
|
486
|
+
// The data is then assigned to a public key. The parsing must set the
|
|
487
|
+
// public key depending on the current step in the flow (e.g add member
|
|
488
|
+
// will issue a trusted member for the added member)
|
|
489
|
+
const tlvs = TLV.readAllTLV(responseData, 0);
|
|
490
|
+
let member: Uint8Array | null = null;
|
|
491
|
+
let iv: Uint8Array | null = null;
|
|
492
|
+
|
|
493
|
+
if (publicKey.length == 0 || (publicKey[0] != 0x02 && publicKey[0] != 0x03)) {
|
|
494
|
+
// The public key is not set if it's the device itself
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
for (const tlv of tlvs) {
|
|
499
|
+
if (tlv.type == TrustedPropertiesTLV.TrustedMember) {
|
|
500
|
+
member = tlv.value;
|
|
501
|
+
}
|
|
502
|
+
if (tlv.type == TrustedPropertiesTLV.IV) {
|
|
503
|
+
iv = tlv.value;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (member === null || iv === null) {
|
|
507
|
+
return; // Do nothing trusted member is optional in some cases
|
|
508
|
+
// (e.g. if the trusted member is the device itself)
|
|
509
|
+
}
|
|
510
|
+
trustedParams.members.set(crypto.to_hex(publicKey), { iv, data: member });
|
|
511
|
+
// Set the last trusted member. This is used to prevent sending the same current trusted member
|
|
512
|
+
// to the device again.
|
|
513
|
+
trustedParams.lastTrustedMember = crypto.to_hex(publicKey);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
private hasTrustedMember(trustedParams: TrustedParams, publicKey: Uint8Array): boolean {
|
|
517
|
+
return trustedParams.members.has(crypto.to_hex(publicKey));
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Throws if the trusted member is not found
|
|
521
|
+
private getTrustedMember(trustedParams: TrustedParams, publicKey: Uint8Array): TrustedMember {
|
|
522
|
+
const member = trustedParams.members.get(crypto.to_hex(publicKey));
|
|
523
|
+
if (member === undefined) {
|
|
524
|
+
throw new Error("Trusted member not found");
|
|
525
|
+
}
|
|
526
|
+
return member;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Set the trusted member on the device if it's not already set
|
|
530
|
+
private async setTrustedMember(params: TrustedParams, publicKey: Uint8Array): Promise<void> {
|
|
531
|
+
// Check if the trusted member is already set on device
|
|
532
|
+
if (params.lastTrustedMember == crypto.to_hex(publicKey)) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Verify we actually have the trusted member
|
|
537
|
+
if (this.hasTrustedMember(params, publicKey) == false) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
// console.log("Setting trusted member: ", crypto.to_hex(publicKey));
|
|
541
|
+
return APDU.setTrustedMember(this.transport, this.getTrustedMember(params, publicKey));
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
private async parseBlock(block: CommandBlock, trustedParams: TrustedParams): Promise<void> {
|
|
545
|
+
let result: Uint8Array;
|
|
546
|
+
|
|
547
|
+
// Parse the block header
|
|
548
|
+
await this.setTrustedMember(trustedParams, block.issuer);
|
|
549
|
+
result = await APDU.parseBlockHeader(
|
|
550
|
+
this.transport,
|
|
551
|
+
CommandStreamEncoder.encodeBlockHeader(block),
|
|
552
|
+
);
|
|
553
|
+
// Record potential trusted member
|
|
554
|
+
this.recordTrustedMember(trustedParams, block.issuer, result);
|
|
555
|
+
for (const index in block.commands) {
|
|
556
|
+
// Parse the command
|
|
557
|
+
const command = block.commands[index];
|
|
558
|
+
|
|
559
|
+
// Set the trusted member depending on the command
|
|
560
|
+
switch (command.getType()) {
|
|
561
|
+
case CommandType.AddMember:
|
|
562
|
+
await this.setTrustedMember(trustedParams, block.issuer);
|
|
563
|
+
break;
|
|
564
|
+
case CommandType.PublishKey:
|
|
565
|
+
await this.setTrustedMember(trustedParams, (command as PublishKey).recipient);
|
|
566
|
+
break;
|
|
567
|
+
case CommandType.EditMember:
|
|
568
|
+
await this.setTrustedMember(trustedParams, (command as EditMember).member);
|
|
569
|
+
break;
|
|
570
|
+
default:
|
|
571
|
+
// Do nothing
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
result = await APDU.parseCommand(
|
|
575
|
+
this.transport,
|
|
576
|
+
CommandStreamEncoder.encodeCommand(block, parseInt(index)),
|
|
577
|
+
true,
|
|
578
|
+
);
|
|
579
|
+
// Record potential trusted member
|
|
580
|
+
switch (command.getType()) {
|
|
581
|
+
case CommandType.Seed:
|
|
582
|
+
this.recordTrustedMember(trustedParams, block.issuer, result);
|
|
583
|
+
break;
|
|
584
|
+
case CommandType.AddMember:
|
|
585
|
+
this.recordTrustedMember(trustedParams, (command as AddMember).publicKey, result);
|
|
586
|
+
break;
|
|
587
|
+
case CommandType.PublishKey:
|
|
588
|
+
this.recordTrustedMember(trustedParams, (command as PublishKey).recipient, result);
|
|
589
|
+
break;
|
|
590
|
+
case CommandType.Derive:
|
|
591
|
+
this.recordTrustedMember(trustedParams, block.issuer, result);
|
|
592
|
+
break;
|
|
593
|
+
case CommandType.EditMember:
|
|
594
|
+
this.recordTrustedMember(trustedParams, (command as EditMember).member, result);
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
// Parse the block signature
|
|
599
|
+
await APDU.parseSignature(this.transport, CommandStreamEncoder.encodeSignature(block));
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
private async parseStream(stream: CommandBlock[]): Promise<TrustedParams> {
|
|
603
|
+
const trustedParams: TrustedParams = {
|
|
604
|
+
members: new Map<string, TrustedMember>(),
|
|
605
|
+
lastTrustedMember: undefined,
|
|
606
|
+
};
|
|
607
|
+
if (stream.length == 0) {
|
|
608
|
+
await APDU.parseEmptyStream(this.transport);
|
|
609
|
+
}
|
|
610
|
+
for (const block of stream.slice(0, stream.length - 1)) {
|
|
611
|
+
await this.parseBlock(block, trustedParams);
|
|
612
|
+
}
|
|
613
|
+
return trustedParams;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
async sign(stream: CommandBlock[]): Promise<CommandBlock> {
|
|
617
|
+
const sessionKey = await this.sessionKeyPair;
|
|
618
|
+
const trustedProperties: CommandResponse[] = [];
|
|
619
|
+
|
|
620
|
+
// We expect the stream to have a single block to sign (the last one)
|
|
621
|
+
this.assertStreamIsValid(stream);
|
|
622
|
+
|
|
623
|
+
// Init signature flow
|
|
624
|
+
await APDU.initFlow(this.transport, sessionKey.publicKey);
|
|
625
|
+
|
|
626
|
+
// Before signing we need to parse the stream on device and get trusted params
|
|
627
|
+
const trustedParams = await this.parseStream(stream);
|
|
628
|
+
trustedParams;
|
|
629
|
+
|
|
630
|
+
// Create the new block to sign
|
|
631
|
+
const blockToSign = stream[stream.length - 1];
|
|
632
|
+
const trustedIssuer = await APDU.signBlockHeader(
|
|
633
|
+
this.transport,
|
|
634
|
+
CommandStreamEncoder.encodeBlockHeader(blockToSign),
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
// Pass all commands to device
|
|
638
|
+
for (let commandIndex = 0; commandIndex < blockToSign.commands.length; commandIndex++) {
|
|
639
|
+
// Pass the trusted param allowing the command to the device
|
|
640
|
+
// If we have no trusted param we need an explicit approval
|
|
641
|
+
const tp = await APDU.signCommand(
|
|
642
|
+
this.transport,
|
|
643
|
+
CommandStreamEncoder.encodeCommand(blockToSign, commandIndex),
|
|
644
|
+
);
|
|
645
|
+
trustedProperties.push(APDU.parseTrustedProperties(blockToSign.commands[commandIndex], tp));
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Finalize block signature
|
|
649
|
+
const signature = await APDU.finalizeSignature(this.transport);
|
|
650
|
+
|
|
651
|
+
// Decrypt and inject trusted issuer
|
|
652
|
+
const secret = await crypto.ecdh(sessionKey, signature.sessionKey);
|
|
653
|
+
const issuer = await crypto.decrypt(secret, trustedIssuer.iv, trustedIssuer.issuer);
|
|
654
|
+
|
|
655
|
+
// Inject trusted properties for commands
|
|
656
|
+
for (let commandIndex = 0; commandIndex < blockToSign.commands.length; commandIndex++) {
|
|
657
|
+
blockToSign.commands[commandIndex] = await injectTrustedProperties(
|
|
658
|
+
blockToSign.commands[commandIndex],
|
|
659
|
+
trustedProperties[commandIndex],
|
|
660
|
+
secret,
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
blockToSign.issuer = issuer;
|
|
664
|
+
blockToSign.signature = signature.signature;
|
|
665
|
+
return blockToSign;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
async readKey(tree: StreamTree, path: number[]): Promise<Uint8Array> {
|
|
669
|
+
tree as StreamTree;
|
|
670
|
+
path as number[];
|
|
671
|
+
throw new Error("readKey is not supported on hardware devices");
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
async isConnected(): Promise<boolean> {
|
|
675
|
+
const response = await this.transport.send(0xe0, 0x04, 0x00, 0x00);
|
|
676
|
+
const sw = response.readUInt16BE(response.length - 2);
|
|
677
|
+
if (sw !== 0x9000) return false;
|
|
678
|
+
const appName = response.subarray(0, response.length - 2).toString();
|
|
679
|
+
return appName === TRUSTCHAIN_APP_NAME;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
async close(): Promise<void> {
|
|
683
|
+
await this.transport.close();
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
*
|
|
689
|
+
*/
|
|
690
|
+
export function createApduDevice(transport: Transport): ApduDevice {
|
|
691
|
+
return new ApduDevice(transport);
|
|
692
|
+
}
|
package/src/BigEndian.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export default class BigEndian {
|
|
2
|
+
public static shortToArray(n: number): Uint8Array {
|
|
3
|
+
const array = new Uint8Array(2);
|
|
4
|
+
const view = new DataView(array.buffer);
|
|
5
|
+
view.setUint16(0, n, false);
|
|
6
|
+
return array;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
public static arrayToShort(array: Uint8Array): number {
|
|
10
|
+
const view = new DataView(array.buffer);
|
|
11
|
+
return view.getUint16(0, false);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public static numberToArray(n: number): Uint8Array {
|
|
15
|
+
const array = new Uint8Array(4);
|
|
16
|
+
const view = new DataView(array.buffer);
|
|
17
|
+
view.setUint32(0, n, false);
|
|
18
|
+
return array;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public static arrayToNumber(array: Uint8Array): number {
|
|
22
|
+
const view = new DataView(array.buffer);
|
|
23
|
+
return view.getUint32(0, false);
|
|
24
|
+
}
|
|
25
|
+
}
|