@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.
Files changed (225) hide show
  1. package/.eslintrc.js +33 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/.unimportedrc.json +4 -0
  4. package/CHANGELOG.md +126 -0
  5. package/LICENSE.txt +21 -0
  6. package/README.md +3 -0
  7. package/jest.config.js +13 -0
  8. package/lib/ApduDevice.d.ts +99 -0
  9. package/lib/ApduDevice.d.ts.map +1 -0
  10. package/lib/ApduDevice.js +532 -0
  11. package/lib/ApduDevice.js.map +1 -0
  12. package/lib/BigEndian.d.ts +7 -0
  13. package/lib/BigEndian.d.ts.map +1 -0
  14. package/lib/BigEndian.js +26 -0
  15. package/lib/BigEndian.js.map +1 -0
  16. package/lib/CommandBlock.d.ts +114 -0
  17. package/lib/CommandBlock.d.ts.map +1 -0
  18. package/lib/CommandBlock.js +173 -0
  19. package/lib/CommandBlock.js.map +1 -0
  20. package/lib/CommandStream.d.ts +38 -0
  21. package/lib/CommandStream.d.ts.map +1 -0
  22. package/lib/CommandStream.js +197 -0
  23. package/lib/CommandStream.js.map +1 -0
  24. package/lib/CommandStreamDecoder.d.ts +15 -0
  25. package/lib/CommandStreamDecoder.d.ts.map +1 -0
  26. package/lib/CommandStreamDecoder.js +101 -0
  27. package/lib/CommandStreamDecoder.js.map +1 -0
  28. package/lib/CommandStreamEncoder.d.ts +16 -0
  29. package/lib/CommandStreamEncoder.d.ts.map +1 -0
  30. package/lib/CommandStreamEncoder.js +131 -0
  31. package/lib/CommandStreamEncoder.js.map +1 -0
  32. package/lib/CommandStreamJsonifier.d.ts +6 -0
  33. package/lib/CommandStreamJsonifier.d.ts.map +1 -0
  34. package/lib/CommandStreamJsonifier.js +75 -0
  35. package/lib/CommandStreamJsonifier.js.map +1 -0
  36. package/lib/CommandStreamResolver.d.ts +53 -0
  37. package/lib/CommandStreamResolver.d.ts.map +1 -0
  38. package/lib/CommandStreamResolver.js +221 -0
  39. package/lib/CommandStreamResolver.js.map +1 -0
  40. package/lib/Crypto.d.ts +38 -0
  41. package/lib/Crypto.d.ts.map +1 -0
  42. package/lib/Crypto.js +47 -0
  43. package/lib/Crypto.js.map +1 -0
  44. package/lib/Device.d.ts +43 -0
  45. package/lib/Device.d.ts.map +1 -0
  46. package/lib/Device.js +203 -0
  47. package/lib/Device.js.map +1 -0
  48. package/lib/IndexedTree.d.ts +13 -0
  49. package/lib/IndexedTree.d.ts.map +1 -0
  50. package/lib/IndexedTree.js +75 -0
  51. package/lib/IndexedTree.js.map +1 -0
  52. package/lib/NobleCrypto.d.ts +41 -0
  53. package/lib/NobleCrypto.d.ts.map +1 -0
  54. package/lib/NobleCrypto.js +298 -0
  55. package/lib/NobleCrypto.js.map +1 -0
  56. package/lib/PublicKey.d.ts +5 -0
  57. package/lib/PublicKey.d.ts.map +1 -0
  58. package/lib/PublicKey.js +10 -0
  59. package/lib/PublicKey.js.map +1 -0
  60. package/lib/SeedId.d.ts +80 -0
  61. package/lib/SeedId.d.ts.map +1 -0
  62. package/lib/SeedId.js +244 -0
  63. package/lib/SeedId.js.map +1 -0
  64. package/lib/StreamTree.d.ts +50 -0
  65. package/lib/StreamTree.d.ts.map +1 -0
  66. package/lib/StreamTree.js +169 -0
  67. package/lib/StreamTree.js.map +1 -0
  68. package/lib/StreamTreeCipher.d.ts +46 -0
  69. package/lib/StreamTreeCipher.d.ts.map +1 -0
  70. package/lib/StreamTreeCipher.js +179 -0
  71. package/lib/StreamTreeCipher.js.map +1 -0
  72. package/lib/__tests__/codec.d.ts +2 -0
  73. package/lib/__tests__/codec.d.ts.map +1 -0
  74. package/lib/__tests__/codec.js +108 -0
  75. package/lib/__tests__/codec.js.map +1 -0
  76. package/lib/__tests__/crypto.d.ts +2 -0
  77. package/lib/__tests__/crypto.d.ts.map +1 -0
  78. package/lib/__tests__/crypto.js +46 -0
  79. package/lib/__tests__/crypto.js.map +1 -0
  80. package/lib/__tests__/indexed_tree.d.ts +2 -0
  81. package/lib/__tests__/indexed_tree.d.ts.map +1 -0
  82. package/lib/__tests__/indexed_tree.js +45 -0
  83. package/lib/__tests__/indexed_tree.js.map +1 -0
  84. package/lib/__tests__/key_exchange.d.ts +2 -0
  85. package/lib/__tests__/key_exchange.d.ts.map +1 -0
  86. package/lib/__tests__/key_exchange.js +129 -0
  87. package/lib/__tests__/key_exchange.js.map +1 -0
  88. package/lib/__tests__/seedId.d.ts +2 -0
  89. package/lib/__tests__/seedId.d.ts.map +1 -0
  90. package/lib/__tests__/seedId.js +92 -0
  91. package/lib/__tests__/seedId.js.map +1 -0
  92. package/lib/__tests__/shared_object.d.ts +2 -0
  93. package/lib/__tests__/shared_object.d.ts.map +1 -0
  94. package/lib/__tests__/shared_object.js +78 -0
  95. package/lib/__tests__/shared_object.js.map +1 -0
  96. package/lib/index.d.ts +35 -0
  97. package/lib/index.d.ts.map +1 -0
  98. package/lib/index.js +81 -0
  99. package/lib/index.js.map +1 -0
  100. package/lib/tlv.d.ts +99 -0
  101. package/lib/tlv.d.ts.map +1 -0
  102. package/lib/tlv.js +150 -0
  103. package/lib/tlv.js.map +1 -0
  104. package/lib-es/ApduDevice.d.ts +99 -0
  105. package/lib-es/ApduDevice.d.ts.map +1 -0
  106. package/lib-es/ApduDevice.js +526 -0
  107. package/lib-es/ApduDevice.js.map +1 -0
  108. package/lib-es/BigEndian.d.ts +7 -0
  109. package/lib-es/BigEndian.d.ts.map +1 -0
  110. package/lib-es/BigEndian.js +23 -0
  111. package/lib-es/BigEndian.js.map +1 -0
  112. package/lib-es/CommandBlock.d.ts +114 -0
  113. package/lib-es/CommandBlock.d.ts.map +1 -0
  114. package/lib-es/CommandBlock.js +160 -0
  115. package/lib-es/CommandBlock.js.map +1 -0
  116. package/lib-es/CommandStream.d.ts +38 -0
  117. package/lib-es/CommandStream.d.ts.map +1 -0
  118. package/lib-es/CommandStream.js +189 -0
  119. package/lib-es/CommandStream.js.map +1 -0
  120. package/lib-es/CommandStreamDecoder.d.ts +15 -0
  121. package/lib-es/CommandStreamDecoder.d.ts.map +1 -0
  122. package/lib-es/CommandStreamDecoder.js +97 -0
  123. package/lib-es/CommandStreamDecoder.js.map +1 -0
  124. package/lib-es/CommandStreamEncoder.d.ts +16 -0
  125. package/lib-es/CommandStreamEncoder.d.ts.map +1 -0
  126. package/lib-es/CommandStreamEncoder.js +127 -0
  127. package/lib-es/CommandStreamEncoder.js.map +1 -0
  128. package/lib-es/CommandStreamJsonifier.d.ts +6 -0
  129. package/lib-es/CommandStreamJsonifier.d.ts.map +1 -0
  130. package/lib-es/CommandStreamJsonifier.js +72 -0
  131. package/lib-es/CommandStreamJsonifier.js.map +1 -0
  132. package/lib-es/CommandStreamResolver.d.ts +53 -0
  133. package/lib-es/CommandStreamResolver.d.ts.map +1 -0
  134. package/lib-es/CommandStreamResolver.js +216 -0
  135. package/lib-es/CommandStreamResolver.js.map +1 -0
  136. package/lib-es/Crypto.d.ts +38 -0
  137. package/lib-es/Crypto.d.ts.map +1 -0
  138. package/lib-es/Crypto.js +43 -0
  139. package/lib-es/Crypto.js.map +1 -0
  140. package/lib-es/Device.d.ts +43 -0
  141. package/lib-es/Device.d.ts.map +1 -0
  142. package/lib-es/Device.js +195 -0
  143. package/lib-es/Device.js.map +1 -0
  144. package/lib-es/IndexedTree.d.ts +13 -0
  145. package/lib-es/IndexedTree.d.ts.map +1 -0
  146. package/lib-es/IndexedTree.js +71 -0
  147. package/lib-es/IndexedTree.js.map +1 -0
  148. package/lib-es/NobleCrypto.d.ts +41 -0
  149. package/lib-es/NobleCrypto.d.ts.map +1 -0
  150. package/lib-es/NobleCrypto.js +267 -0
  151. package/lib-es/NobleCrypto.js.map +1 -0
  152. package/lib-es/PublicKey.d.ts +5 -0
  153. package/lib-es/PublicKey.d.ts.map +1 -0
  154. package/lib-es/PublicKey.js +6 -0
  155. package/lib-es/PublicKey.js.map +1 -0
  156. package/lib-es/SeedId.d.ts +80 -0
  157. package/lib-es/SeedId.d.ts.map +1 -0
  158. package/lib-es/SeedId.js +235 -0
  159. package/lib-es/SeedId.js.map +1 -0
  160. package/lib-es/StreamTree.d.ts +50 -0
  161. package/lib-es/StreamTree.d.ts.map +1 -0
  162. package/lib-es/StreamTree.js +165 -0
  163. package/lib-es/StreamTree.js.map +1 -0
  164. package/lib-es/StreamTreeCipher.d.ts +46 -0
  165. package/lib-es/StreamTreeCipher.d.ts.map +1 -0
  166. package/lib-es/StreamTreeCipher.js +175 -0
  167. package/lib-es/StreamTreeCipher.js.map +1 -0
  168. package/lib-es/__tests__/codec.d.ts +2 -0
  169. package/lib-es/__tests__/codec.d.ts.map +1 -0
  170. package/lib-es/__tests__/codec.js +106 -0
  171. package/lib-es/__tests__/codec.js.map +1 -0
  172. package/lib-es/__tests__/crypto.d.ts +2 -0
  173. package/lib-es/__tests__/crypto.d.ts.map +1 -0
  174. package/lib-es/__tests__/crypto.js +44 -0
  175. package/lib-es/__tests__/crypto.js.map +1 -0
  176. package/lib-es/__tests__/indexed_tree.d.ts +2 -0
  177. package/lib-es/__tests__/indexed_tree.d.ts.map +1 -0
  178. package/lib-es/__tests__/indexed_tree.js +43 -0
  179. package/lib-es/__tests__/indexed_tree.js.map +1 -0
  180. package/lib-es/__tests__/key_exchange.d.ts +2 -0
  181. package/lib-es/__tests__/key_exchange.d.ts.map +1 -0
  182. package/lib-es/__tests__/key_exchange.js +124 -0
  183. package/lib-es/__tests__/key_exchange.js.map +1 -0
  184. package/lib-es/__tests__/seedId.d.ts +2 -0
  185. package/lib-es/__tests__/seedId.d.ts.map +1 -0
  186. package/lib-es/__tests__/seedId.js +90 -0
  187. package/lib-es/__tests__/seedId.js.map +1 -0
  188. package/lib-es/__tests__/shared_object.d.ts +2 -0
  189. package/lib-es/__tests__/shared_object.d.ts.map +1 -0
  190. package/lib-es/__tests__/shared_object.js +76 -0
  191. package/lib-es/__tests__/shared_object.js.map +1 -0
  192. package/lib-es/index.d.ts +35 -0
  193. package/lib-es/index.d.ts.map +1 -0
  194. package/lib-es/index.js +32 -0
  195. package/lib-es/index.js.map +1 -0
  196. package/lib-es/tlv.d.ts +99 -0
  197. package/lib-es/tlv.d.ts.map +1 -0
  198. package/lib-es/tlv.js +144 -0
  199. package/lib-es/tlv.js.map +1 -0
  200. package/package.json +63 -0
  201. package/src/ApduDevice.ts +692 -0
  202. package/src/BigEndian.ts +25 -0
  203. package/src/CommandBlock.ts +247 -0
  204. package/src/CommandStream.ts +262 -0
  205. package/src/CommandStreamDecoder.ts +142 -0
  206. package/src/CommandStreamEncoder.ts +144 -0
  207. package/src/CommandStreamJsonifier.ts +82 -0
  208. package/src/CommandStreamResolver.ts +284 -0
  209. package/src/Crypto.ts +78 -0
  210. package/src/Device.ts +254 -0
  211. package/src/IndexedTree.ts +80 -0
  212. package/src/NobleCrypto.ts +294 -0
  213. package/src/PublicKey.ts +6 -0
  214. package/src/SeedId.ts +338 -0
  215. package/src/StreamTree.ts +212 -0
  216. package/src/StreamTreeCipher.ts +207 -0
  217. package/src/__tests__/codec.ts +146 -0
  218. package/src/__tests__/crypto.ts +44 -0
  219. package/src/__tests__/indexed_tree.ts +51 -0
  220. package/src/__tests__/key_exchange.ts +167 -0
  221. package/src/__tests__/seedId.ts +120 -0
  222. package/src/__tests__/shared_object.ts +118 -0
  223. package/src/index.ts +43 -0
  224. package/src/tlv.ts +210 -0
  225. 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
+ }