@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,247 @@
|
|
|
1
|
+
import { CommandStreamEncoder } from "./CommandStreamEncoder";
|
|
2
|
+
import { crypto } from "./Crypto";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
export interface Command {
|
|
8
|
+
getType(): CommandType;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
*
|
|
13
|
+
*/
|
|
14
|
+
export enum CommandType {
|
|
15
|
+
Seed = 0x10,
|
|
16
|
+
AddMember = 0x11,
|
|
17
|
+
PublishKey = 0x12,
|
|
18
|
+
EditMember = 0x14,
|
|
19
|
+
Derive = 0x15,
|
|
20
|
+
CloseStream = 0x13,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
export const Permissions = {
|
|
27
|
+
KEY_READER: 0x01,
|
|
28
|
+
KEY_CREATOR: 0x02,
|
|
29
|
+
KEY_REVOKER: 0x04,
|
|
30
|
+
ADD_MEMBER: 0x08,
|
|
31
|
+
REMOVE_MEMBER: 0x16,
|
|
32
|
+
CHANGE_MEMBER_PERMISSIONS: 0x32,
|
|
33
|
+
CHANGE_MEMBER_NAME: 0x64,
|
|
34
|
+
|
|
35
|
+
MEMBER: 0,
|
|
36
|
+
OWNER: 0xffffffff,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
*
|
|
41
|
+
*/
|
|
42
|
+
export class Seed implements Command {
|
|
43
|
+
topic: Uint8Array | null;
|
|
44
|
+
protocolVersion: number;
|
|
45
|
+
groupKey: Uint8Array;
|
|
46
|
+
initializationVector: Uint8Array;
|
|
47
|
+
encryptedXpriv: Uint8Array;
|
|
48
|
+
ephemeralPublicKey: Uint8Array;
|
|
49
|
+
|
|
50
|
+
constructor(
|
|
51
|
+
topic: Uint8Array | null,
|
|
52
|
+
protocolVersion: number,
|
|
53
|
+
groupKey: Uint8Array,
|
|
54
|
+
initializationVector: Uint8Array,
|
|
55
|
+
encryptedXpriv: Uint8Array,
|
|
56
|
+
ephemerPublicKey: Uint8Array,
|
|
57
|
+
) {
|
|
58
|
+
this.topic = topic;
|
|
59
|
+
this.protocolVersion = protocolVersion;
|
|
60
|
+
this.groupKey = groupKey.length == 0 ? new Uint8Array(33) : groupKey;
|
|
61
|
+
this.initializationVector =
|
|
62
|
+
initializationVector.length == 0 ? new Uint8Array(16) : initializationVector;
|
|
63
|
+
this.encryptedXpriv = encryptedXpriv.length == 0 ? new Uint8Array(64) : encryptedXpriv;
|
|
64
|
+
this.ephemeralPublicKey = ephemerPublicKey.length == 0 ? new Uint8Array(33) : ephemerPublicKey;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getType(): CommandType {
|
|
68
|
+
return CommandType.Seed;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
*
|
|
74
|
+
*/
|
|
75
|
+
export class Derive implements Command {
|
|
76
|
+
path: number[];
|
|
77
|
+
groupKey: Uint8Array;
|
|
78
|
+
initializationVector: Uint8Array;
|
|
79
|
+
encryptedXpriv: Uint8Array;
|
|
80
|
+
ephemeralPublicKey: Uint8Array;
|
|
81
|
+
|
|
82
|
+
constructor(
|
|
83
|
+
path: number[],
|
|
84
|
+
groupKey: Uint8Array,
|
|
85
|
+
initializationVector: Uint8Array,
|
|
86
|
+
encryptedXpriv: Uint8Array,
|
|
87
|
+
ephemeralPublicKey: Uint8Array,
|
|
88
|
+
) {
|
|
89
|
+
this.path = path;
|
|
90
|
+
this.groupKey = groupKey;
|
|
91
|
+
this.initializationVector = initializationVector;
|
|
92
|
+
this.encryptedXpriv = encryptedXpriv;
|
|
93
|
+
this.ephemeralPublicKey = ephemeralPublicKey;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
getType(): CommandType {
|
|
97
|
+
return CommandType.Derive;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
*
|
|
103
|
+
*/
|
|
104
|
+
export class AddMember implements Command {
|
|
105
|
+
name: string;
|
|
106
|
+
publicKey: Uint8Array;
|
|
107
|
+
permissions: number;
|
|
108
|
+
|
|
109
|
+
constructor(name: string, publicKey: Uint8Array, permissions: number) {
|
|
110
|
+
this.name = name;
|
|
111
|
+
this.publicKey = publicKey;
|
|
112
|
+
this.permissions = permissions;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
getType(): CommandType {
|
|
116
|
+
return CommandType.AddMember;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
*
|
|
122
|
+
*/
|
|
123
|
+
export class PublishKey implements Command {
|
|
124
|
+
initializationVector: Uint8Array;
|
|
125
|
+
encryptedXpriv: Uint8Array;
|
|
126
|
+
recipient: Uint8Array;
|
|
127
|
+
ephemeralPublicKey: Uint8Array;
|
|
128
|
+
|
|
129
|
+
constructor(
|
|
130
|
+
initializationVector: Uint8Array,
|
|
131
|
+
encryptedXpriv: Uint8Array,
|
|
132
|
+
recipient: Uint8Array,
|
|
133
|
+
ephemeralPublicKey: Uint8Array,
|
|
134
|
+
) {
|
|
135
|
+
this.encryptedXpriv = encryptedXpriv;
|
|
136
|
+
this.initializationVector = initializationVector;
|
|
137
|
+
this.recipient = recipient;
|
|
138
|
+
this.ephemeralPublicKey = ephemeralPublicKey;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
getType(): CommandType {
|
|
142
|
+
return CommandType.PublishKey;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
*
|
|
148
|
+
*/
|
|
149
|
+
export class EditMember implements Command {
|
|
150
|
+
member: Uint8Array;
|
|
151
|
+
name: string | null;
|
|
152
|
+
permissions: number | null;
|
|
153
|
+
|
|
154
|
+
constructor(member: Uint8Array, name: string | null, permissions: number | null) {
|
|
155
|
+
this.name = name;
|
|
156
|
+
this.permissions = permissions;
|
|
157
|
+
this.member = member;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
getType(): CommandType {
|
|
161
|
+
return CommandType.EditMember;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
*
|
|
167
|
+
*/
|
|
168
|
+
export class CloseStream implements Command {
|
|
169
|
+
constructor() {}
|
|
170
|
+
|
|
171
|
+
getType(): CommandType {
|
|
172
|
+
return CommandType.CloseStream;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/*
|
|
177
|
+
Version | VARINT | 1 # Format version
|
|
178
|
+
Parent | HASH | 32 # The hash of the parent command block (Random 32 bytes for the first command block)
|
|
179
|
+
Issuer | HASH | 32 # Hash of the issuer public key
|
|
180
|
+
Length | VARINT | 1 # Number of command in this command block
|
|
181
|
+
....
|
|
182
|
+
Signature | SIG | var # Signature of the command block
|
|
183
|
+
*/
|
|
184
|
+
export interface CommandBlock {
|
|
185
|
+
// The version of the command block format
|
|
186
|
+
version: number;
|
|
187
|
+
// The hash of the parent command block. For the first command block, parent must be a random 32 bytes number
|
|
188
|
+
parent: Uint8Array;
|
|
189
|
+
// The public key of the command block issuer
|
|
190
|
+
issuer: Uint8Array;
|
|
191
|
+
// The list of commands in this command block
|
|
192
|
+
commands: Command[];
|
|
193
|
+
// The signature of the command block
|
|
194
|
+
signature: Uint8Array;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Create a new command block.
|
|
199
|
+
* @param issuer The public key of the command block issuer
|
|
200
|
+
* @param commands The list of commands in this command block
|
|
201
|
+
* @param signature The signature of the command block (by default the block is not signed)
|
|
202
|
+
* @param parent The parent command block hash (if null, the block is the first block and a parent will be generated)
|
|
203
|
+
* @returns
|
|
204
|
+
*/
|
|
205
|
+
export async function createCommandBlock(
|
|
206
|
+
issuer: Uint8Array,
|
|
207
|
+
commands: Command[],
|
|
208
|
+
signature: Uint8Array = new Uint8Array(),
|
|
209
|
+
parent: Uint8Array | null = null,
|
|
210
|
+
): Promise<CommandBlock> {
|
|
211
|
+
if (parent === null) {
|
|
212
|
+
parent = parent = await crypto.randomBytes(32);
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
version: 1,
|
|
216
|
+
issuer,
|
|
217
|
+
parent,
|
|
218
|
+
commands,
|
|
219
|
+
signature,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export async function signCommandBlock(
|
|
224
|
+
block: CommandBlock,
|
|
225
|
+
issuer: Uint8Array,
|
|
226
|
+
secretKey: Uint8Array,
|
|
227
|
+
): Promise<CommandBlock> {
|
|
228
|
+
const signature = await crypto.sign(
|
|
229
|
+
await hashCommandBlock(block),
|
|
230
|
+
await crypto.keypairFromSecretKey(secretKey),
|
|
231
|
+
);
|
|
232
|
+
return {
|
|
233
|
+
...block,
|
|
234
|
+
signature,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export async function hashCommandBlock(block: CommandBlock): Promise<Uint8Array> {
|
|
239
|
+
return await crypto.hash(CommandStreamEncoder.encode([block]));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export async function verifyCommandBlock(block: CommandBlock): Promise<boolean> {
|
|
243
|
+
const unsignedBlock = { ...block };
|
|
244
|
+
unsignedBlock.signature = new Uint8Array();
|
|
245
|
+
const hash = await hashCommandBlock(unsignedBlock);
|
|
246
|
+
return await crypto.verify(hash, block.signature, block.issuer);
|
|
247
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandBlock,
|
|
3
|
+
Command,
|
|
4
|
+
createCommandBlock,
|
|
5
|
+
hashCommandBlock,
|
|
6
|
+
CommandType,
|
|
7
|
+
Seed,
|
|
8
|
+
Derive,
|
|
9
|
+
AddMember,
|
|
10
|
+
PublishKey,
|
|
11
|
+
CloseStream,
|
|
12
|
+
} from "./CommandBlock";
|
|
13
|
+
import CommandStreamResolver, { ResolvedCommandStream } from "./CommandStreamResolver";
|
|
14
|
+
import { DerivationPath } from "./Crypto";
|
|
15
|
+
import { Device, ISSUER_PLACEHOLDER } from "./Device";
|
|
16
|
+
import { StreamTree } from "./StreamTree";
|
|
17
|
+
|
|
18
|
+
const EMPTY = new Uint8Array();
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
*/
|
|
23
|
+
export type CommandIssuer = (
|
|
24
|
+
device: Device,
|
|
25
|
+
tempStream: CommandStream,
|
|
26
|
+
streamTree?: StreamTree | null,
|
|
27
|
+
) => Promise<Command[]>;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
export class CommandStreamIssuer {
|
|
33
|
+
private _stream: CommandStream;
|
|
34
|
+
private _steps: CommandIssuer[] = [];
|
|
35
|
+
|
|
36
|
+
constructor(stream: CommandStream) {
|
|
37
|
+
this._stream = stream;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public seed(topic: Uint8Array | null = null): CommandStreamIssuer {
|
|
41
|
+
const step: CommandIssuer = async (
|
|
42
|
+
device: Device,
|
|
43
|
+
tempStream: CommandStream,
|
|
44
|
+
streamTree?: StreamTree | null,
|
|
45
|
+
) => {
|
|
46
|
+
device as Device;
|
|
47
|
+
tempStream as CommandStream;
|
|
48
|
+
streamTree as StreamTree;
|
|
49
|
+
return [new Seed(topic, 0, EMPTY, EMPTY, EMPTY, EMPTY)];
|
|
50
|
+
};
|
|
51
|
+
this._steps.push(step);
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public derive(path: number[]): CommandStreamIssuer {
|
|
56
|
+
const step: CommandIssuer = async (
|
|
57
|
+
device: Device,
|
|
58
|
+
tempStream: CommandStream,
|
|
59
|
+
streamTree?: StreamTree | null,
|
|
60
|
+
) => {
|
|
61
|
+
device as Device;
|
|
62
|
+
tempStream as CommandStream;
|
|
63
|
+
streamTree as StreamTree;
|
|
64
|
+
const derivationPath = DerivationPath.toIndexArray(path);
|
|
65
|
+
return [new Derive(derivationPath, EMPTY, EMPTY, EMPTY, EMPTY)];
|
|
66
|
+
};
|
|
67
|
+
this._steps.push(step);
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public addMember(
|
|
72
|
+
name: string,
|
|
73
|
+
publicKey: Uint8Array,
|
|
74
|
+
permissions: number,
|
|
75
|
+
publishKey: boolean = true,
|
|
76
|
+
): CommandStreamIssuer {
|
|
77
|
+
const step: CommandIssuer = async (
|
|
78
|
+
device: Device,
|
|
79
|
+
tempStream: CommandStream,
|
|
80
|
+
streamTree?: StreamTree | null,
|
|
81
|
+
) => {
|
|
82
|
+
device as Device;
|
|
83
|
+
tempStream as CommandStream;
|
|
84
|
+
streamTree as StreamTree;
|
|
85
|
+
if (publishKey) {
|
|
86
|
+
return [
|
|
87
|
+
new AddMember(name, publicKey, permissions),
|
|
88
|
+
new PublishKey(EMPTY, EMPTY, publicKey, EMPTY),
|
|
89
|
+
];
|
|
90
|
+
} else {
|
|
91
|
+
return [new AddMember(name, publicKey, permissions)];
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
this._steps.push(step);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public publishKey(publicKey: Uint8Array): CommandStreamIssuer {
|
|
99
|
+
const step: CommandIssuer = async (
|
|
100
|
+
device: Device,
|
|
101
|
+
tempStream: CommandStream,
|
|
102
|
+
streamTree?: StreamTree | null,
|
|
103
|
+
) => {
|
|
104
|
+
device as Device;
|
|
105
|
+
tempStream as CommandStream;
|
|
106
|
+
streamTree as StreamTree;
|
|
107
|
+
return [new PublishKey(EMPTY, EMPTY, publicKey, EMPTY)];
|
|
108
|
+
};
|
|
109
|
+
this._steps.push(step);
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public close(): CommandStreamIssuer {
|
|
114
|
+
const step: CommandIssuer = async (
|
|
115
|
+
device: Device,
|
|
116
|
+
tempStream: CommandStream,
|
|
117
|
+
streamTree?: StreamTree | null,
|
|
118
|
+
) => {
|
|
119
|
+
device as Device;
|
|
120
|
+
tempStream as CommandStream;
|
|
121
|
+
streamTree as StreamTree;
|
|
122
|
+
return [new CloseStream()];
|
|
123
|
+
};
|
|
124
|
+
this._steps.push(step);
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public async issue(
|
|
129
|
+
device: Device,
|
|
130
|
+
streamTree?: StreamTree | null,
|
|
131
|
+
parentHash?: Uint8Array | null,
|
|
132
|
+
): Promise<CommandStream> {
|
|
133
|
+
const lastBlockHash =
|
|
134
|
+
this._stream.blocks.length > 0
|
|
135
|
+
? await hashCommandBlock(this._stream.blocks[this._stream.blocks.length - 1])
|
|
136
|
+
: null;
|
|
137
|
+
const block = await createCommandBlock(
|
|
138
|
+
ISSUER_PLACEHOLDER,
|
|
139
|
+
[],
|
|
140
|
+
new Uint8Array(),
|
|
141
|
+
parentHash || lastBlockHash,
|
|
142
|
+
);
|
|
143
|
+
const stream = new CommandStream(this._stream.blocks.concat([]));
|
|
144
|
+
const tempStream = new CommandStream(this._stream.blocks.concat([block]));
|
|
145
|
+
let commands: Command[] = [];
|
|
146
|
+
for (const step of this._steps) {
|
|
147
|
+
const newCommands = await step(device, tempStream, streamTree);
|
|
148
|
+
commands = commands.concat(newCommands);
|
|
149
|
+
tempStream.blocks[tempStream.blocks.length - 1].commands = commands;
|
|
150
|
+
}
|
|
151
|
+
return await stream.issue(device, commands, streamTree, parentHash);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
*
|
|
157
|
+
*/
|
|
158
|
+
export default class CommandStream {
|
|
159
|
+
private _blocks: CommandBlock[] = [];
|
|
160
|
+
|
|
161
|
+
constructor(blocks: CommandBlock[] = []) {
|
|
162
|
+
this._blocks = blocks;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public resolve(incomplete: boolean = false): Promise<ResolvedCommandStream> {
|
|
166
|
+
incomplete as boolean;
|
|
167
|
+
return CommandStreamResolver.resolve(this._blocks);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public getRootHash(): Promise<Uint8Array> {
|
|
171
|
+
return hashCommandBlock(this._blocks[0]);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public getStreamPath(): string | null {
|
|
175
|
+
if (this._blocks.length === 0) return null;
|
|
176
|
+
if (this._blocks[0].commands[0].getType() === CommandType.Seed) {
|
|
177
|
+
return "";
|
|
178
|
+
} else if (this._blocks[0].commands[0].getType() === CommandType.Derive) {
|
|
179
|
+
return DerivationPath.toString((this._blocks[0].commands[0] as Derive).path);
|
|
180
|
+
} else {
|
|
181
|
+
throw new Error("Malformed CommandStream");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public async push(
|
|
186
|
+
block: CommandBlock,
|
|
187
|
+
issuer: Device,
|
|
188
|
+
tree: StreamTree | null,
|
|
189
|
+
): Promise<CommandStream> {
|
|
190
|
+
let stream: CommandBlock[] = [];
|
|
191
|
+
|
|
192
|
+
if (block.commands.length === 0) {
|
|
193
|
+
throw new Error("Attempts to create an empty block");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// If the first command of the new block is not a seed and the first command of the stream is not a seed either, prepend the root block
|
|
197
|
+
|
|
198
|
+
if (
|
|
199
|
+
(this._blocks.length == 0 || this._blocks[0].commands[0].getType() !== CommandType.Seed) &&
|
|
200
|
+
block.commands[0].getType() !== CommandType.Seed
|
|
201
|
+
) {
|
|
202
|
+
const root = tree?.getRoot();
|
|
203
|
+
if (!root || root.blocks.length === 0) {
|
|
204
|
+
throw new Error("Null or empty tree cannot be used to sign the new block");
|
|
205
|
+
}
|
|
206
|
+
stream = [root.blocks[0]].concat(this._blocks);
|
|
207
|
+
} else {
|
|
208
|
+
stream = this._blocks;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (block.commands[0].getType() === CommandType.Derive) {
|
|
212
|
+
// Set the parent hash of the block to the root hash
|
|
213
|
+
const b = { ...block };
|
|
214
|
+
b.parent = await hashCommandBlock(stream[0]);
|
|
215
|
+
stream = stream.concat([b]);
|
|
216
|
+
} else {
|
|
217
|
+
stream = stream.concat([block]);
|
|
218
|
+
}
|
|
219
|
+
const signedBlock = await issuer.sign(stream, tree || undefined);
|
|
220
|
+
return new CommandStream(this._blocks.concat([signedBlock]));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
public async issue(
|
|
224
|
+
device: Device,
|
|
225
|
+
commands: Command[],
|
|
226
|
+
tree: StreamTree | null = null,
|
|
227
|
+
parentHash: Uint8Array | null = null,
|
|
228
|
+
): Promise<CommandStream> {
|
|
229
|
+
const lastBlockHash =
|
|
230
|
+
this._blocks.length > 0
|
|
231
|
+
? await hashCommandBlock(this._blocks[this._blocks.length - 1])
|
|
232
|
+
: null;
|
|
233
|
+
const block = await createCommandBlock(
|
|
234
|
+
ISSUER_PLACEHOLDER,
|
|
235
|
+
commands,
|
|
236
|
+
new Uint8Array(),
|
|
237
|
+
parentHash || lastBlockHash,
|
|
238
|
+
);
|
|
239
|
+
return this.push(block, device, tree);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
public edit(): CommandStreamIssuer {
|
|
243
|
+
return new CommandStreamIssuer(this);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
public async getStreamPublicKey(): Promise<Uint8Array> {
|
|
247
|
+
// Group public must be the first command in the first block othwerwise it is malformed
|
|
248
|
+
if (this._blocks.length === 0 || this._blocks[0].commands.length === 0)
|
|
249
|
+
throw new Error("Empty CommandStream");
|
|
250
|
+
if (this._blocks[0].commands[0].getType() === CommandType.Seed) {
|
|
251
|
+
return (this._blocks[0].commands[0] as Seed).groupKey;
|
|
252
|
+
}
|
|
253
|
+
if (this._blocks[0].commands[0].getType() === CommandType.Derive) {
|
|
254
|
+
return (this._blocks[0].commands[0] as Derive).groupKey;
|
|
255
|
+
}
|
|
256
|
+
throw new Error("Malformed CommandStream");
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
get blocks(): CommandBlock[] {
|
|
260
|
+
return this._blocks;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Command,
|
|
3
|
+
CommandBlock,
|
|
4
|
+
Seed,
|
|
5
|
+
AddMember,
|
|
6
|
+
CloseStream,
|
|
7
|
+
Derive,
|
|
8
|
+
EditMember,
|
|
9
|
+
PublishKey,
|
|
10
|
+
CommandType,
|
|
11
|
+
} from "./CommandBlock";
|
|
12
|
+
import { TLV, TLVField } from "./tlv";
|
|
13
|
+
|
|
14
|
+
export const TLVCommandStreamDecoder = {
|
|
15
|
+
// Read command from TLV
|
|
16
|
+
|
|
17
|
+
readCommand: function (tlv: TLVField): Command {
|
|
18
|
+
switch (tlv.type) {
|
|
19
|
+
case CommandType.Seed:
|
|
20
|
+
return TLVCommandStreamDecoder.readSeedCommand(tlv.value);
|
|
21
|
+
case CommandType.Derive:
|
|
22
|
+
return TLVCommandStreamDecoder.readDeriveCommand(tlv.value);
|
|
23
|
+
case CommandType.AddMember:
|
|
24
|
+
return TLVCommandStreamDecoder.readAddMemberCommand(tlv.value);
|
|
25
|
+
case CommandType.PublishKey:
|
|
26
|
+
return TLVCommandStreamDecoder.readPublishKeyCommand(tlv.value);
|
|
27
|
+
case CommandType.EditMember:
|
|
28
|
+
return TLVCommandStreamDecoder.readEditMemberCommand(tlv.value);
|
|
29
|
+
case CommandType.CloseStream:
|
|
30
|
+
return TLVCommandStreamDecoder.readCloseStreamCommand(tlv.value);
|
|
31
|
+
default:
|
|
32
|
+
throw new Error("Unknown command type");
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
readSeedCommand: function (buffer: Uint8Array): Command {
|
|
37
|
+
const readTopic = TLV.readNullOr(TLV.readTLV(buffer, 0), TLV.readBytes);
|
|
38
|
+
const readProtocolVersion = TLV.readVarInt(TLV.readTLV(buffer, readTopic.offset));
|
|
39
|
+
const readGroupKey = TLV.readPublicKey(TLV.readTLV(buffer, readProtocolVersion.offset));
|
|
40
|
+
const readIV = TLV.readBytes(TLV.readTLV(buffer, readGroupKey.offset));
|
|
41
|
+
const readEncryptedXpriv = TLV.readBytes(TLV.readTLV(buffer, readIV.offset));
|
|
42
|
+
const readEphemeralPublicKey = TLV.readPublicKey(
|
|
43
|
+
TLV.readTLV(buffer, readEncryptedXpriv.offset),
|
|
44
|
+
);
|
|
45
|
+
return new Seed(
|
|
46
|
+
readTopic.value,
|
|
47
|
+
readProtocolVersion.value,
|
|
48
|
+
readGroupKey.value,
|
|
49
|
+
readIV.value,
|
|
50
|
+
readEncryptedXpriv.value,
|
|
51
|
+
readEphemeralPublicKey.value,
|
|
52
|
+
);
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
readDeriveCommand: function (buffer: Uint8Array): Command {
|
|
56
|
+
const readPath = TLV.readDerivationPath(TLV.readTLV(buffer, 0));
|
|
57
|
+
const readGroupKey = TLV.readPublicKey(TLV.readTLV(buffer, readPath.offset));
|
|
58
|
+
const readIV = TLV.readBytes(TLV.readTLV(buffer, readGroupKey.offset));
|
|
59
|
+
const readEncryptedXpriv = TLV.readBytes(TLV.readTLV(buffer, readIV.offset));
|
|
60
|
+
const readEphemeralPublicKey = TLV.readPublicKey(
|
|
61
|
+
TLV.readTLV(buffer, readEncryptedXpriv.offset),
|
|
62
|
+
);
|
|
63
|
+
return new Derive(
|
|
64
|
+
readPath.value,
|
|
65
|
+
readGroupKey.value,
|
|
66
|
+
readIV.value,
|
|
67
|
+
readEncryptedXpriv.value,
|
|
68
|
+
readEphemeralPublicKey.value,
|
|
69
|
+
);
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
readAddMemberCommand: function (buffer: Uint8Array): Command {
|
|
73
|
+
const readName = TLV.readString(TLV.readTLV(buffer, 0));
|
|
74
|
+
const pubkey = TLV.readPublicKey(TLV.readTLV(buffer, readName.offset));
|
|
75
|
+
const permissions = TLV.readVarInt(TLV.readTLV(buffer, pubkey.offset));
|
|
76
|
+
return new AddMember(readName.value, pubkey.value, permissions.value);
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
readPublishKeyCommand: function (buffer: Uint8Array): Command {
|
|
80
|
+
const IV = TLV.readBytes(TLV.readTLV(buffer, 0));
|
|
81
|
+
const encryptedXpriv = TLV.readBytes(TLV.readTLV(buffer, IV.offset));
|
|
82
|
+
const recipient = TLV.readPublicKey(TLV.readTLV(buffer, encryptedXpriv.offset));
|
|
83
|
+
const ephemeralPublicKey = TLV.readPublicKey(TLV.readTLV(buffer, recipient.offset));
|
|
84
|
+
|
|
85
|
+
return new PublishKey(
|
|
86
|
+
IV.value,
|
|
87
|
+
encryptedXpriv.value,
|
|
88
|
+
recipient.value,
|
|
89
|
+
ephemeralPublicKey.value,
|
|
90
|
+
);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
readCloseStreamCommand: function (buffer: Uint8Array): Command {
|
|
94
|
+
buffer as Uint8Array;
|
|
95
|
+
return new CloseStream();
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
readEditMemberCommand: function (buffer: Uint8Array): Command {
|
|
99
|
+
const member = TLV.readPublicKey(TLV.readTLV(buffer, 0));
|
|
100
|
+
const permissions = TLV.readNullOr(TLV.readTLV(buffer, member.offset), TLV.readVarInt);
|
|
101
|
+
const name = TLV.readNullOr(TLV.readTLV(buffer, permissions.offset), TLV.readString);
|
|
102
|
+
return new EditMember(member.value, name.value, permissions.value);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function unpack(buffer: Uint8Array): CommandBlock[] {
|
|
107
|
+
const stream: CommandBlock[] = [];
|
|
108
|
+
let offset = 0;
|
|
109
|
+
|
|
110
|
+
while (offset < buffer.length) {
|
|
111
|
+
const version = TLV.readVarInt(TLV.readTLV(buffer, offset));
|
|
112
|
+
const parent = TLV.readHash(TLV.readTLV(buffer, version.offset));
|
|
113
|
+
const issuer = TLV.readPublicKey(TLV.readTLV(buffer, parent.offset));
|
|
114
|
+
const length = TLV.readVarInt(TLV.readTLV(buffer, issuer.offset));
|
|
115
|
+
offset = length.offset;
|
|
116
|
+
|
|
117
|
+
const commands: Command[] = [];
|
|
118
|
+
for (let index = 0; index < length.value; index++) {
|
|
119
|
+
const commandBuffer = TLV.readTLV(buffer, offset);
|
|
120
|
+
const command = TLVCommandStreamDecoder.readCommand(commandBuffer.tlv);
|
|
121
|
+
commands.push(command);
|
|
122
|
+
offset = commandBuffer.offset;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const signature = TLV.readSignature(TLV.readTLV(buffer, offset));
|
|
126
|
+
offset = signature.offset;
|
|
127
|
+
stream.push({
|
|
128
|
+
version: version.value,
|
|
129
|
+
parent: parent.value,
|
|
130
|
+
issuer: issuer.value,
|
|
131
|
+
commands,
|
|
132
|
+
signature: signature.value,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return stream;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export class CommandStreamDecoder {
|
|
139
|
+
public static decode(buffer: Uint8Array): CommandBlock[] {
|
|
140
|
+
return unpack(buffer);
|
|
141
|
+
}
|
|
142
|
+
}
|