@ledgerhq/hw-ledger-key-ring-protocol 0.2.1-fix-build-number-pre.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/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 +528 -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 +156 -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 +195 -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 +195 -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 +39 -0
- package/lib/NobleCrypto.d.ts.map +1 -0
- package/lib/NobleCrypto.js +240 -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 +175 -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 +522 -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 +143 -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 +187 -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 +187 -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 +39 -0
- package/lib-es/NobleCrypto.d.ts.map +1 -0
- package/lib-es/NobleCrypto.js +209 -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 +171 -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 +688 -0
- package/src/BigEndian.ts +25 -0
- package/src/CommandBlock.ts +244 -0
- package/src/CommandStream.ts +260 -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 +246 -0
- package/src/IndexedTree.ts +80 -0
- package/src/NobleCrypto.ts +255 -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
package/src/SeedId.ts
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import BigEndian from "./BigEndian";
|
|
2
|
+
import { crypto } from "./Crypto";
|
|
3
|
+
import { TLV } from "./tlv";
|
|
4
|
+
|
|
5
|
+
export class PubKeyCredential {
|
|
6
|
+
version: number;
|
|
7
|
+
curveId: number;
|
|
8
|
+
signAlgorithm: number;
|
|
9
|
+
publicKey: Uint8Array;
|
|
10
|
+
|
|
11
|
+
constructor({
|
|
12
|
+
version,
|
|
13
|
+
curveId,
|
|
14
|
+
signAlgorithm,
|
|
15
|
+
publicKey,
|
|
16
|
+
}: {
|
|
17
|
+
version: number;
|
|
18
|
+
curveId: number;
|
|
19
|
+
signAlgorithm: number;
|
|
20
|
+
publicKey: Uint8Array;
|
|
21
|
+
}) {
|
|
22
|
+
this.version = version;
|
|
23
|
+
this.curveId = curveId;
|
|
24
|
+
this.signAlgorithm = signAlgorithm;
|
|
25
|
+
this.publicKey = publicKey;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static fromBytes(data, offset = 0): [PubKeyCredential, number] {
|
|
29
|
+
const view = new DataView(data.buffer, data.byteOffset + offset);
|
|
30
|
+
const version = view.getUint8(0);
|
|
31
|
+
const curveId = view.getUint8(1);
|
|
32
|
+
const signAlgorithm = view.getUint8(2);
|
|
33
|
+
const publicKeyLength = view.getUint8(3);
|
|
34
|
+
const publicKey = new Uint8Array(data.buffer, data.byteOffset + offset + 4, publicKeyLength);
|
|
35
|
+
|
|
36
|
+
return [
|
|
37
|
+
new PubKeyCredential({
|
|
38
|
+
version,
|
|
39
|
+
curveId,
|
|
40
|
+
signAlgorithm,
|
|
41
|
+
publicKey,
|
|
42
|
+
}),
|
|
43
|
+
4 + publicKeyLength,
|
|
44
|
+
];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
toBytes(): Uint8Array {
|
|
48
|
+
const result = new Uint8Array(4 + this.publicKey.length);
|
|
49
|
+
const view = new DataView(result.buffer);
|
|
50
|
+
view.setUint8(0, this.version);
|
|
51
|
+
view.setUint8(1, this.curveId);
|
|
52
|
+
view.setUint8(2, this.signAlgorithm);
|
|
53
|
+
view.setUint8(3, this.publicKey.length);
|
|
54
|
+
result.set(this.publicKey, 4);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
toJSON() {
|
|
59
|
+
return {
|
|
60
|
+
version: this.version,
|
|
61
|
+
curveId: this.curveId,
|
|
62
|
+
signAlgorithm: this.signAlgorithm,
|
|
63
|
+
publicKey: crypto.to_hex(this.publicKey),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
assertValidity() {
|
|
68
|
+
if (this.version !== 0x00) {
|
|
69
|
+
throw new Error(`PubKeyCredential: Wrong version: ${this.version}`);
|
|
70
|
+
}
|
|
71
|
+
if (this.curveId !== 0x21) {
|
|
72
|
+
throw new Error(`PubKeyCredential: Wrong curve id: ${this.curveId}`);
|
|
73
|
+
}
|
|
74
|
+
if (this.signAlgorithm !== 0x01) {
|
|
75
|
+
throw new Error(`PubKeyCredential: Wrong sign algorithm: ${this.signAlgorithm}`);
|
|
76
|
+
}
|
|
77
|
+
if (this.publicKey.length !== 0x21) {
|
|
78
|
+
throw new Error(`PubKeyCredential: Wrong pubkey len: ${this.publicKey.length}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type SemVer = {
|
|
84
|
+
major: number;
|
|
85
|
+
minor: number;
|
|
86
|
+
patch: number;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export class Challenge {
|
|
90
|
+
payloadType: number;
|
|
91
|
+
version: number;
|
|
92
|
+
protocolVersion: SemVer;
|
|
93
|
+
challengeData: Uint8Array;
|
|
94
|
+
challengeExpiry: Date;
|
|
95
|
+
host: string;
|
|
96
|
+
rpCredential: PubKeyCredential;
|
|
97
|
+
rpSignature: Uint8Array;
|
|
98
|
+
|
|
99
|
+
constructor({
|
|
100
|
+
payloadType,
|
|
101
|
+
version,
|
|
102
|
+
protocolVersion,
|
|
103
|
+
challengeData,
|
|
104
|
+
challengeExpiry,
|
|
105
|
+
host,
|
|
106
|
+
rpCredential,
|
|
107
|
+
rpSignature,
|
|
108
|
+
}: {
|
|
109
|
+
payloadType: number;
|
|
110
|
+
version: number;
|
|
111
|
+
protocolVersion: SemVer;
|
|
112
|
+
challengeData: Uint8Array;
|
|
113
|
+
challengeExpiry: Date;
|
|
114
|
+
host: string;
|
|
115
|
+
rpCredential: PubKeyCredential;
|
|
116
|
+
rpSignature: Uint8Array;
|
|
117
|
+
}) {
|
|
118
|
+
this.payloadType = payloadType;
|
|
119
|
+
this.version = version;
|
|
120
|
+
this.protocolVersion = protocolVersion;
|
|
121
|
+
this.challengeData = challengeData;
|
|
122
|
+
this.challengeExpiry = challengeExpiry;
|
|
123
|
+
this.host = host;
|
|
124
|
+
this.rpCredential = rpCredential;
|
|
125
|
+
this.rpSignature = rpSignature;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
static fromBytes(data: Uint8Array, offset = 0): [Challenge, number] {
|
|
129
|
+
let index = offset;
|
|
130
|
+
const all = TLV.readAllTLV(data, offset);
|
|
131
|
+
const byType: Record<number, Uint8Array | undefined> = {};
|
|
132
|
+
for (const tlv of all) {
|
|
133
|
+
index += tlv.value.length + 2;
|
|
134
|
+
byType[tlv.type] = tlv.value;
|
|
135
|
+
// console.log(tlv.type.toString(16), "(" + tlv.value.length + ")", crypto.to_hex(tlv.value));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const payloadTypeField = byType[0x01];
|
|
139
|
+
if (payloadTypeField === undefined) {
|
|
140
|
+
throw new Error("Missing payloadType");
|
|
141
|
+
}
|
|
142
|
+
const payloadType = payloadTypeField[0];
|
|
143
|
+
|
|
144
|
+
const versionField = byType[0x02];
|
|
145
|
+
if (versionField === undefined) {
|
|
146
|
+
throw new Error("Missing version");
|
|
147
|
+
}
|
|
148
|
+
const version = versionField[0];
|
|
149
|
+
|
|
150
|
+
const protocolVersionField = byType[0x60];
|
|
151
|
+
if (protocolVersionField === undefined) {
|
|
152
|
+
throw new Error("Missing protocolVersion");
|
|
153
|
+
}
|
|
154
|
+
const protocolVersion = {
|
|
155
|
+
major: protocolVersionField[0],
|
|
156
|
+
minor: protocolVersionField[1],
|
|
157
|
+
patch: protocolVersionField[2],
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const challengeDataField = byType[0x12];
|
|
161
|
+
if (challengeDataField === undefined) {
|
|
162
|
+
throw new Error("Missing challengeData");
|
|
163
|
+
}
|
|
164
|
+
const challengeData = challengeDataField;
|
|
165
|
+
|
|
166
|
+
const challengeExpiryField = byType[0x16];
|
|
167
|
+
if (challengeExpiryField === undefined) {
|
|
168
|
+
throw new Error("Missing challengeExpiry");
|
|
169
|
+
}
|
|
170
|
+
const challengeExpiry = new Date(1000 * BigEndian.arrayToNumber(challengeExpiryField));
|
|
171
|
+
|
|
172
|
+
const hostField = byType[0x20];
|
|
173
|
+
if (hostField === undefined) {
|
|
174
|
+
throw new Error("Missing host");
|
|
175
|
+
}
|
|
176
|
+
const host = new TextDecoder().decode(hostField);
|
|
177
|
+
|
|
178
|
+
const signAlgorithmField = byType[0x14];
|
|
179
|
+
if (signAlgorithmField === undefined) {
|
|
180
|
+
throw new Error("Missing signAlgorithm");
|
|
181
|
+
}
|
|
182
|
+
const signAlgorithm = signAlgorithmField[0];
|
|
183
|
+
|
|
184
|
+
const publicKey = byType[0x33];
|
|
185
|
+
if (publicKey === undefined) {
|
|
186
|
+
throw new Error("Missing rpCredential");
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const curveIdField = byType[0x32];
|
|
190
|
+
if (curveIdField === undefined) {
|
|
191
|
+
throw new Error("Missing curveId");
|
|
192
|
+
}
|
|
193
|
+
const curveId = curveIdField[0];
|
|
194
|
+
|
|
195
|
+
const rpCredential = new PubKeyCredential({
|
|
196
|
+
version,
|
|
197
|
+
curveId,
|
|
198
|
+
signAlgorithm,
|
|
199
|
+
publicKey,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const rpSignatureField = byType[0x15];
|
|
203
|
+
if (rpSignatureField === undefined) {
|
|
204
|
+
throw new Error("Missing rpSignature");
|
|
205
|
+
}
|
|
206
|
+
const rpSignature = rpSignatureField;
|
|
207
|
+
|
|
208
|
+
const challenge = new Challenge({
|
|
209
|
+
payloadType,
|
|
210
|
+
protocolVersion,
|
|
211
|
+
version,
|
|
212
|
+
challengeData,
|
|
213
|
+
challengeExpiry,
|
|
214
|
+
host,
|
|
215
|
+
rpCredential,
|
|
216
|
+
rpSignature,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return [challenge, index - offset];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
toBytes(): Uint8Array {
|
|
223
|
+
let buffer = new Uint8Array();
|
|
224
|
+
|
|
225
|
+
buffer = TLV.pushTLV(buffer, 0x01, 1, new Uint8Array([this.payloadType]));
|
|
226
|
+
buffer = TLV.pushTLV(buffer, 0x02, 1, new Uint8Array([this.version]));
|
|
227
|
+
buffer = TLV.pushTLV(buffer, 0x12, this.challengeData.length, this.challengeData);
|
|
228
|
+
buffer = TLV.pushTLV(buffer, 0x14, 1, new Uint8Array([this.rpCredential.signAlgorithm]));
|
|
229
|
+
buffer = TLV.pushTLV(buffer, 0x15, this.rpSignature.length, this.rpSignature);
|
|
230
|
+
buffer = TLV.pushTLV(buffer, 0x16, 4, BigEndian.numberToArray(this.getChallengeExpireValue()));
|
|
231
|
+
buffer = TLV.pushTLV(buffer, 0x20, this.host.length, new TextEncoder().encode(this.host));
|
|
232
|
+
buffer = TLV.pushTLV(buffer, 0x32, 1, new Uint8Array([this.rpCredential.curveId]));
|
|
233
|
+
buffer = TLV.pushTLV(
|
|
234
|
+
buffer,
|
|
235
|
+
0x33,
|
|
236
|
+
this.rpCredential.publicKey.length,
|
|
237
|
+
this.rpCredential.publicKey,
|
|
238
|
+
);
|
|
239
|
+
const data = this.getProtocolVersionData();
|
|
240
|
+
buffer = TLV.pushTLV(buffer, 0x60, data.length, data);
|
|
241
|
+
|
|
242
|
+
return buffer;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
getUnsignedTLV(): Uint8Array {
|
|
246
|
+
let buffer = new Uint8Array();
|
|
247
|
+
buffer = TLV.pushTLV(buffer, 0x01, 1, new Uint8Array([this.payloadType]));
|
|
248
|
+
buffer = TLV.pushTLV(buffer, 0x02, 1, new Uint8Array([this.version]));
|
|
249
|
+
buffer = TLV.pushTLV(buffer, 0x12, this.challengeData.length, this.challengeData);
|
|
250
|
+
buffer = TLV.pushTLV(buffer, 0x16, 4, BigEndian.numberToArray(this.getChallengeExpireValue()));
|
|
251
|
+
buffer = TLV.pushTLV(buffer, 0x20, this.host.length, new TextEncoder().encode(this.host));
|
|
252
|
+
const data = this.getProtocolVersionData();
|
|
253
|
+
buffer = TLV.pushTLV(buffer, 0x60, data.length, data);
|
|
254
|
+
return buffer;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
toJSON() {
|
|
258
|
+
return {
|
|
259
|
+
payloadType: this.payloadType,
|
|
260
|
+
version: this.version,
|
|
261
|
+
protocolVersion: this.protocolVersion,
|
|
262
|
+
challenge: {
|
|
263
|
+
data: crypto.to_hex(this.challengeData),
|
|
264
|
+
expiry: this.challengeExpiry.toISOString(),
|
|
265
|
+
},
|
|
266
|
+
host: this.host,
|
|
267
|
+
rp: [
|
|
268
|
+
{
|
|
269
|
+
credential: this.rpCredential.toJSON(),
|
|
270
|
+
signature: crypto.to_hex(this.rpSignature),
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
getProtocolVersionData(): Uint8Array {
|
|
277
|
+
return new Uint8Array([
|
|
278
|
+
this.protocolVersion.major,
|
|
279
|
+
this.protocolVersion.minor,
|
|
280
|
+
this.protocolVersion.patch,
|
|
281
|
+
0,
|
|
282
|
+
]);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
getChallengeExpireValue(): number {
|
|
286
|
+
return Math.floor(this.challengeExpiry.getTime() / 1000);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export type SeedIdResult = {
|
|
291
|
+
pubkeyCredential: PubKeyCredential;
|
|
292
|
+
signature: Uint8Array;
|
|
293
|
+
attestationType: number;
|
|
294
|
+
attestationPubkeyCredential: PubKeyCredential;
|
|
295
|
+
attestation: Uint8Array;
|
|
296
|
+
attestationResult: Uint8Array;
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
export function parseSeedIdResult(result: Uint8Array): SeedIdResult {
|
|
300
|
+
let offset = 0;
|
|
301
|
+
const [pubkeyCredential, pubkeyCredentialLength] = PubKeyCredential.fromBytes(result, offset);
|
|
302
|
+
pubkeyCredential.assertValidity();
|
|
303
|
+
|
|
304
|
+
offset += pubkeyCredentialLength;
|
|
305
|
+
|
|
306
|
+
const signatureLen = result[offset];
|
|
307
|
+
offset += 1;
|
|
308
|
+
|
|
309
|
+
const signature = new Uint8Array(result.buffer, result.byteOffset + offset, signatureLen);
|
|
310
|
+
|
|
311
|
+
offset += signatureLen;
|
|
312
|
+
|
|
313
|
+
const attestationResult = new Uint8Array(result.slice(offset));
|
|
314
|
+
|
|
315
|
+
const attestationType = result[offset];
|
|
316
|
+
offset += 1;
|
|
317
|
+
|
|
318
|
+
const [attestationPubkeyCredential, attestationPubkeyCredentialLength] =
|
|
319
|
+
PubKeyCredential.fromBytes(result, offset);
|
|
320
|
+
|
|
321
|
+
attestationPubkeyCredential.assertValidity();
|
|
322
|
+
|
|
323
|
+
offset += attestationPubkeyCredentialLength;
|
|
324
|
+
|
|
325
|
+
const attestationLen = result[offset];
|
|
326
|
+
offset += 1;
|
|
327
|
+
|
|
328
|
+
const attestation = new Uint8Array(result.buffer, result.byteOffset + offset, attestationLen);
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
pubkeyCredential,
|
|
332
|
+
signature,
|
|
333
|
+
attestationType,
|
|
334
|
+
attestationPubkeyCredential,
|
|
335
|
+
attestation,
|
|
336
|
+
attestationResult,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { CommandStream, CommandStreamDecoder, CommandStreamEncoder, Device } from ".";
|
|
2
|
+
import { crypto, DerivationPath } from "./Crypto";
|
|
3
|
+
import { IndexedTree } from "./IndexedTree";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
export interface ApplicationStreams {
|
|
9
|
+
appStream: CommandStream;
|
|
10
|
+
appRootStream: CommandStream;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
export interface StreamTreeCreateOpts {
|
|
17
|
+
topic?: Uint8Array;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
export interface PublishKeyEvent {
|
|
24
|
+
stream: CommandStream;
|
|
25
|
+
encryptedXpriv: Uint8Array;
|
|
26
|
+
groupPublicKey: Uint8Array;
|
|
27
|
+
ephemeralPublicKey: Uint8Array;
|
|
28
|
+
nonce: Uint8Array;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
*
|
|
33
|
+
*/
|
|
34
|
+
export class StreamTree {
|
|
35
|
+
private tree: IndexedTree<CommandStream>;
|
|
36
|
+
|
|
37
|
+
constructor(tree: IndexedTree<CommandStream>) {
|
|
38
|
+
if (tree.getValue() === null) {
|
|
39
|
+
throw new Error("Root of the tree cannot be null");
|
|
40
|
+
}
|
|
41
|
+
this.tree = tree;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public getApplicationRootPath(applicationId: number, increment: number = 0): string {
|
|
45
|
+
// tree index is always 0 in the current implementation
|
|
46
|
+
const treeIndex = 0;
|
|
47
|
+
// for application index, we have key rotation that is possible so we need to find the last index
|
|
48
|
+
const child = this.tree
|
|
49
|
+
.getChild(DerivationPath.hardenedIndex(treeIndex))
|
|
50
|
+
?.getChild(DerivationPath.hardenedIndex(applicationId));
|
|
51
|
+
const applicationIndex = child
|
|
52
|
+
? DerivationPath.reverseHardenedIndex(child.getHighestIndex())
|
|
53
|
+
: 0;
|
|
54
|
+
return `m/${treeIndex}'/${applicationId}'/${applicationIndex + increment}'`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public async getPublishKeyEvent(
|
|
58
|
+
member: Uint8Array,
|
|
59
|
+
path: number[],
|
|
60
|
+
): Promise<PublishKeyEvent | null> {
|
|
61
|
+
// Iterate over the tree from leaf to root
|
|
62
|
+
const leaf = this.tree.findChild(path);
|
|
63
|
+
if (!leaf || leaf!.getValue() === null) {
|
|
64
|
+
if (path.length === 0) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return this.getPublishKeyEvent(member, path.slice(0, path.length - 1));
|
|
68
|
+
}
|
|
69
|
+
const resolved = await leaf.getValue()!.resolve();
|
|
70
|
+
const key = resolved.getEncryptedKey(member);
|
|
71
|
+
if (!key) {
|
|
72
|
+
if (path.length === 0) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return this.getPublishKeyEvent(member, path.slice(0, path.length - 1));
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
stream: leaf.getValue()!,
|
|
79
|
+
encryptedXpriv: key.encryptedXpriv,
|
|
80
|
+
ephemeralPublicKey: key.ephemeralPublicKey,
|
|
81
|
+
nonce: key.initialiationVector,
|
|
82
|
+
groupPublicKey: resolved.getGroupPublicKey(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public getChild(path: string | number[]): CommandStream | null {
|
|
87
|
+
const indexes =
|
|
88
|
+
typeof path === "string" ? DerivationPath.toIndexArray(path) : (path as number[]);
|
|
89
|
+
const subtree = this.tree.findChild(indexes);
|
|
90
|
+
if (subtree === undefined) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
return subtree.getValue()!;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public getRoot(): CommandStream {
|
|
97
|
+
return this.tree.getValue()!;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Share a private key with a member
|
|
102
|
+
*/
|
|
103
|
+
public async share(
|
|
104
|
+
path: string | number[],
|
|
105
|
+
owner: Device,
|
|
106
|
+
member: Uint8Array,
|
|
107
|
+
name: string,
|
|
108
|
+
permission: number,
|
|
109
|
+
): Promise<StreamTree> {
|
|
110
|
+
const indexes =
|
|
111
|
+
typeof path === "string" ? DerivationPath.toIndexArray(path) : (path as number[]);
|
|
112
|
+
let stream = this.getChild(indexes) || new CommandStream();
|
|
113
|
+
if (stream.blocks.length === 0 && indexes.length > 0) {
|
|
114
|
+
const root = await this.getRoot().getRootHash();
|
|
115
|
+
stream = await stream
|
|
116
|
+
.edit()
|
|
117
|
+
.derive(indexes)
|
|
118
|
+
.addMember(name, member, permission, true)
|
|
119
|
+
.issue(owner, this, root);
|
|
120
|
+
return this.update(stream);
|
|
121
|
+
} else if (stream.blocks.length === 0) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
"StreamTree.share cannot add a member if the root was not previously created",
|
|
124
|
+
);
|
|
125
|
+
} else {
|
|
126
|
+
const newStream = await stream.edit().addMember(name, member, permission).issue(owner, this);
|
|
127
|
+
return this.update(newStream);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Close a stream
|
|
133
|
+
*/
|
|
134
|
+
public async close(path: string | number[], owner: Device): Promise<StreamTree> {
|
|
135
|
+
const indexes =
|
|
136
|
+
typeof path === "string" ? DerivationPath.toIndexArray(path) : (path as number[]);
|
|
137
|
+
let stream = this.getChild(indexes) || new CommandStream();
|
|
138
|
+
stream = await stream.edit().close().issue(owner, this);
|
|
139
|
+
return this.update(stream);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public update(stream: CommandStream): StreamTree {
|
|
143
|
+
const path = stream.getStreamPath();
|
|
144
|
+
|
|
145
|
+
if (path === null) throw new Error("Stream path cannot be null");
|
|
146
|
+
const indexes = DerivationPath.toIndexArray(path);
|
|
147
|
+
const newTree = this.tree.updateChild(indexes, stream);
|
|
148
|
+
|
|
149
|
+
return new StreamTree(newTree);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
public serialize(): Record<string, string> {
|
|
153
|
+
const streamEntries = serializeTree(this.tree, []);
|
|
154
|
+
const entries = streamEntries.flatMap(([path, stream]) =>
|
|
155
|
+
stream ? [[path, crypto.to_hex(CommandStreamEncoder.encode(stream.blocks))]] : [],
|
|
156
|
+
);
|
|
157
|
+
return Object.fromEntries(entries);
|
|
158
|
+
|
|
159
|
+
function serializeTree(
|
|
160
|
+
tree: IndexedTree<CommandStream>,
|
|
161
|
+
path: number[],
|
|
162
|
+
): [string, CommandStream | null][] {
|
|
163
|
+
const stream = tree.getValue();
|
|
164
|
+
const childrens = tree.getChildren();
|
|
165
|
+
|
|
166
|
+
return [
|
|
167
|
+
[DerivationPath.toString(path), stream],
|
|
168
|
+
...Array.from(childrens.entries()).flatMap(([index, child]) =>
|
|
169
|
+
serializeTree(child, [...path, index]),
|
|
170
|
+
),
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static deserialize(data: Record<string, string>): StreamTree {
|
|
176
|
+
const streams = Object.values(data).map(
|
|
177
|
+
data => new CommandStream(CommandStreamDecoder.decode(crypto.from_hex(data))),
|
|
178
|
+
);
|
|
179
|
+
return StreamTree.from(...streams);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static async createNewTree(owner: Device, opts: StreamTreeCreateOpts = {}): Promise<StreamTree> {
|
|
183
|
+
let stream = new CommandStream();
|
|
184
|
+
const streamToIssue = stream.edit().seed(opts.topic);
|
|
185
|
+
|
|
186
|
+
stream = await streamToIssue.issue(owner);
|
|
187
|
+
|
|
188
|
+
const tree = new IndexedTree(stream);
|
|
189
|
+
return new StreamTree(tree);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
static from(...streams: CommandStream[]): StreamTree {
|
|
193
|
+
// Map all stream with their path
|
|
194
|
+
const streamMap = new Map<string, CommandStream>();
|
|
195
|
+
streams.forEach(stream => {
|
|
196
|
+
const path = stream.getStreamPath();
|
|
197
|
+
if (path === null) throw new Error("Stream path cannot be null");
|
|
198
|
+
streamMap.set(path!, stream);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Create tree if the list contains the root
|
|
202
|
+
const root = streamMap.get("");
|
|
203
|
+
if (root === undefined) throw new Error("StreamTree.from requires the root of the tree");
|
|
204
|
+
let tree = new IndexedTree(root);
|
|
205
|
+
streamMap.delete("");
|
|
206
|
+
streamMap.forEach((stream, path) => {
|
|
207
|
+
const p = DerivationPath.toIndexArray(path);
|
|
208
|
+
tree = tree.addChild(p, new IndexedTree(stream));
|
|
209
|
+
});
|
|
210
|
+
return new StreamTree(tree);
|
|
211
|
+
}
|
|
212
|
+
}
|