@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,144 @@
|
|
|
1
|
+
import { TLV } from "./tlv";
|
|
2
|
+
import {
|
|
3
|
+
CommandBlock,
|
|
4
|
+
Command,
|
|
5
|
+
CommandType,
|
|
6
|
+
Derive,
|
|
7
|
+
Seed,
|
|
8
|
+
AddMember,
|
|
9
|
+
CloseStream,
|
|
10
|
+
PublishKey,
|
|
11
|
+
EditMember,
|
|
12
|
+
} from "./CommandBlock";
|
|
13
|
+
|
|
14
|
+
export const TLVCommandStreamEncoder = {
|
|
15
|
+
packSeed: function (b: Seed): Uint8Array {
|
|
16
|
+
let object = new Uint8Array();
|
|
17
|
+
if (b.topic) {
|
|
18
|
+
object = TLV.pushBytes(object, b.topic);
|
|
19
|
+
} else {
|
|
20
|
+
object = TLV.pushBytes(object, new Uint8Array(0));
|
|
21
|
+
}
|
|
22
|
+
object = TLV.pushInt16(object, b.protocolVersion);
|
|
23
|
+
object = TLV.pushPublicKey(object, b.groupKey);
|
|
24
|
+
object = TLV.pushBytes(object, b.initializationVector);
|
|
25
|
+
object = TLV.pushBytes(object, b.encryptedXpriv);
|
|
26
|
+
object = TLV.pushPublicKey(object, b.ephemeralPublicKey);
|
|
27
|
+
return object;
|
|
28
|
+
},
|
|
29
|
+
|
|
30
|
+
packDerive: function (b: Derive): Uint8Array {
|
|
31
|
+
let object = new Uint8Array();
|
|
32
|
+
object = TLV.pushDerivationPath(object, b.path);
|
|
33
|
+
object = TLV.pushPublicKey(object, b.groupKey);
|
|
34
|
+
object = TLV.pushBytes(object, b.initializationVector);
|
|
35
|
+
object = TLV.pushBytes(object, b.encryptedXpriv);
|
|
36
|
+
object = TLV.pushPublicKey(object, b.ephemeralPublicKey);
|
|
37
|
+
return object;
|
|
38
|
+
},
|
|
39
|
+
|
|
40
|
+
packAddMember: function (b: AddMember): Uint8Array {
|
|
41
|
+
let object = new Uint8Array();
|
|
42
|
+
object = TLV.pushString(object, b.name);
|
|
43
|
+
object = TLV.pushPublicKey(object, b.publicKey);
|
|
44
|
+
object = TLV.pushInt32(object, b.permissions);
|
|
45
|
+
return object;
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
packPublishKey: function (b: PublishKey): Uint8Array {
|
|
49
|
+
let object = new Uint8Array();
|
|
50
|
+
object = TLV.pushBytes(object, b.initializationVector);
|
|
51
|
+
object = TLV.pushBytes(object, b.encryptedXpriv);
|
|
52
|
+
object = TLV.pushPublicKey(object, b.recipient);
|
|
53
|
+
object = TLV.pushPublicKey(object, b.ephemeralPublicKey);
|
|
54
|
+
return object;
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
packCloseStream: function (b: CloseStream): Uint8Array {
|
|
58
|
+
b as CloseStream;
|
|
59
|
+
return new Uint8Array();
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
packEditMember: function (b: EditMember): Uint8Array {
|
|
63
|
+
let object = new Uint8Array();
|
|
64
|
+
object = TLV.pushPublicKey(object, b.member);
|
|
65
|
+
if (b.permissions) {
|
|
66
|
+
object = TLV.pushInt32(object, b.permissions);
|
|
67
|
+
} else {
|
|
68
|
+
object = TLV.pushNull(object);
|
|
69
|
+
}
|
|
70
|
+
if (b.name) {
|
|
71
|
+
object = TLV.pushString(object, b.name);
|
|
72
|
+
} else {
|
|
73
|
+
object = TLV.pushNull(object);
|
|
74
|
+
}
|
|
75
|
+
return object;
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
function packCommand(buffer: Uint8Array, command: Command): Uint8Array {
|
|
80
|
+
let object = new Uint8Array();
|
|
81
|
+
switch (command.getType()) {
|
|
82
|
+
case CommandType.Seed:
|
|
83
|
+
object = TLVCommandStreamEncoder.packSeed(command as Seed);
|
|
84
|
+
break;
|
|
85
|
+
case CommandType.Derive:
|
|
86
|
+
object = TLVCommandStreamEncoder.packDerive(command as Derive);
|
|
87
|
+
break;
|
|
88
|
+
case CommandType.AddMember:
|
|
89
|
+
object = TLVCommandStreamEncoder.packAddMember(command as AddMember);
|
|
90
|
+
break;
|
|
91
|
+
case CommandType.PublishKey:
|
|
92
|
+
object = TLVCommandStreamEncoder.packPublishKey(command as PublishKey);
|
|
93
|
+
break;
|
|
94
|
+
case CommandType.CloseStream:
|
|
95
|
+
object = TLVCommandStreamEncoder.packCloseStream(command as CloseStream);
|
|
96
|
+
break;
|
|
97
|
+
case CommandType.EditMember:
|
|
98
|
+
object = TLVCommandStreamEncoder.packEditMember(command as EditMember);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
buffer = TLV.pushTLV(buffer, command.getType(), object.length, object);
|
|
102
|
+
return buffer;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export class CommandStreamEncoder {
|
|
106
|
+
public static encode(stream: CommandBlock[]): Uint8Array {
|
|
107
|
+
return pack(stream);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public static encodeBlockHeader(block: CommandBlock): Uint8Array {
|
|
111
|
+
let buffer = new Uint8Array();
|
|
112
|
+
buffer = TLV.pushByte(buffer, block.version);
|
|
113
|
+
buffer = TLV.pushHash(buffer, block.parent);
|
|
114
|
+
buffer = TLV.pushPublicKey(buffer, block.issuer);
|
|
115
|
+
buffer = TLV.pushByte(buffer, block.commands.length);
|
|
116
|
+
return buffer;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public static encodeCommand(block: CommandBlock, index: number): Uint8Array {
|
|
120
|
+
if (index >= block.commands.length || index < 0) {
|
|
121
|
+
throw new Error("Index out of range");
|
|
122
|
+
}
|
|
123
|
+
let buffer = new Uint8Array();
|
|
124
|
+
buffer = packCommand(buffer, block.commands[index]);
|
|
125
|
+
return buffer;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
public static encodeSignature(block: CommandBlock): Uint8Array {
|
|
129
|
+
if (block.signature.length === 0) return new Uint8Array();
|
|
130
|
+
return TLV.pushSignature(new Uint8Array(), block.signature);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function pack(stream: CommandBlock[]): Uint8Array {
|
|
135
|
+
let buffer = new Uint8Array();
|
|
136
|
+
for (const block of stream) {
|
|
137
|
+
buffer = TLV.push(buffer, CommandStreamEncoder.encodeBlockHeader(block));
|
|
138
|
+
for (let index = 0; index < block.commands.length; index++) {
|
|
139
|
+
buffer = TLV.push(buffer, CommandStreamEncoder.encodeCommand(block, index));
|
|
140
|
+
}
|
|
141
|
+
buffer = TLV.push(buffer, CommandStreamEncoder.encodeSignature(block));
|
|
142
|
+
}
|
|
143
|
+
return buffer;
|
|
144
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { to_hex } from "./NobleCrypto";
|
|
2
|
+
import {
|
|
3
|
+
Command,
|
|
4
|
+
CommandBlock,
|
|
5
|
+
CommandType,
|
|
6
|
+
Seed,
|
|
7
|
+
AddMember,
|
|
8
|
+
PublishKey,
|
|
9
|
+
EditMember,
|
|
10
|
+
Derive,
|
|
11
|
+
} from "./CommandBlock";
|
|
12
|
+
import { DerivationPath } from "./Crypto";
|
|
13
|
+
import { CommandStreamEncoder } from "./CommandStreamEncoder";
|
|
14
|
+
import { createHash } from "crypto";
|
|
15
|
+
|
|
16
|
+
export default class CommandStreamJsonifier {
|
|
17
|
+
private static jsonifyCommand(command: Command): object {
|
|
18
|
+
switch (command.getType()) {
|
|
19
|
+
case CommandType.Seed:
|
|
20
|
+
return {
|
|
21
|
+
type: "Seed",
|
|
22
|
+
topic: to_hex((command as Seed).topic),
|
|
23
|
+
groupKey: to_hex((command as Seed).groupKey),
|
|
24
|
+
encryptedXpriv: to_hex((command as Seed).encryptedXpriv),
|
|
25
|
+
ephemeralPublicKey: to_hex((command as Seed).ephemeralPublicKey),
|
|
26
|
+
initializationVector: to_hex((command as Seed).initializationVector),
|
|
27
|
+
};
|
|
28
|
+
case CommandType.AddMember:
|
|
29
|
+
return {
|
|
30
|
+
type: "AddMember",
|
|
31
|
+
name: (command as AddMember).name,
|
|
32
|
+
publicKey: to_hex((command as AddMember).publicKey),
|
|
33
|
+
permissions: (command as AddMember).permissions,
|
|
34
|
+
};
|
|
35
|
+
case CommandType.EditMember:
|
|
36
|
+
return {
|
|
37
|
+
type: "EditMember",
|
|
38
|
+
member: to_hex((command as EditMember).member),
|
|
39
|
+
name: (command as EditMember).name,
|
|
40
|
+
permissions: (command as EditMember).permissions,
|
|
41
|
+
};
|
|
42
|
+
case CommandType.Derive:
|
|
43
|
+
return {
|
|
44
|
+
type: "Derive",
|
|
45
|
+
path: DerivationPath.toString((command as Derive).path),
|
|
46
|
+
groupKey: to_hex((command as Derive).groupKey),
|
|
47
|
+
encryptedXpriv: to_hex((command as Derive).encryptedXpriv),
|
|
48
|
+
ephemeralPublicKey: to_hex((command as Derive).ephemeralPublicKey),
|
|
49
|
+
initializationVector: to_hex((command as Derive).initializationVector),
|
|
50
|
+
};
|
|
51
|
+
case CommandType.CloseStream:
|
|
52
|
+
return {
|
|
53
|
+
type: "CloseStream",
|
|
54
|
+
};
|
|
55
|
+
case CommandType.PublishKey:
|
|
56
|
+
return {
|
|
57
|
+
type: "PublishKey",
|
|
58
|
+
encryptedXpriv: to_hex((command as PublishKey).encryptedXpriv),
|
|
59
|
+
initializationVector: to_hex((command as PublishKey).initializationVector),
|
|
60
|
+
ephemeralPublicKey: to_hex((command as PublishKey).ephemeralPublicKey),
|
|
61
|
+
recipient: to_hex((command as PublishKey).recipient),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public static jsonify(stream: CommandBlock[]): object {
|
|
67
|
+
return stream.map(block => {
|
|
68
|
+
const b = CommandStreamEncoder.encode([block]);
|
|
69
|
+
const h = createHash("sha256");
|
|
70
|
+
h.update(b);
|
|
71
|
+
return {
|
|
72
|
+
parent: to_hex(block.parent),
|
|
73
|
+
issuer: to_hex(block.issuer),
|
|
74
|
+
hash: h.digest().toString("hex"),
|
|
75
|
+
command: block.commands.map(command => {
|
|
76
|
+
return CommandStreamJsonifier.jsonifyCommand(command);
|
|
77
|
+
}),
|
|
78
|
+
signature: to_hex(block.signature),
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandBlock,
|
|
3
|
+
Command,
|
|
4
|
+
verifyCommandBlock,
|
|
5
|
+
Seed,
|
|
6
|
+
AddMember,
|
|
7
|
+
PublishKey,
|
|
8
|
+
Derive,
|
|
9
|
+
CommandType,
|
|
10
|
+
hashCommandBlock,
|
|
11
|
+
Permissions,
|
|
12
|
+
} from "./CommandBlock";
|
|
13
|
+
import { crypto } from "./Crypto";
|
|
14
|
+
|
|
15
|
+
interface PublishedKey {
|
|
16
|
+
encryptedXpriv: Uint8Array;
|
|
17
|
+
ephemeralPublicKey: Uint8Array;
|
|
18
|
+
issuer: Uint8Array;
|
|
19
|
+
initialiationVector: Uint8Array;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type MemberData = {
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
permissions: number;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
class ResolvedCommandStreamInternals {
|
|
29
|
+
public isCreated: boolean = false;
|
|
30
|
+
public members: Uint8Array[] = [];
|
|
31
|
+
public membersData: MemberData[] = [];
|
|
32
|
+
public topic: Uint8Array | null = null;
|
|
33
|
+
public keys: Map<string, PublishedKey> = new Map();
|
|
34
|
+
public permission: Map<string, number> = new Map();
|
|
35
|
+
public height: number = 0;
|
|
36
|
+
public streamId: string = "";
|
|
37
|
+
public hashes: string[] = [];
|
|
38
|
+
public names: Map<string, string> = new Map();
|
|
39
|
+
public groupPublicKey: Uint8Array = new Uint8Array();
|
|
40
|
+
public derivationPath: number[] = [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class ResolvedCommandStream {
|
|
44
|
+
private _internals: ResolvedCommandStreamInternals;
|
|
45
|
+
|
|
46
|
+
constructor(internals: ResolvedCommandStreamInternals) {
|
|
47
|
+
this._internals = internals;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public isCreated(): boolean {
|
|
51
|
+
return this._internals.isCreated;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public getMembers(): Uint8Array[] {
|
|
55
|
+
return this._internals.members;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public getMembersData(): MemberData[] {
|
|
59
|
+
return this._internals.membersData;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public getTopic(): Uint8Array | null {
|
|
63
|
+
return this._internals.topic;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public isOwner(publicKey: Uint8Array): boolean {
|
|
67
|
+
return this._internals.permission.get(crypto.to_hex(publicKey)) === Permissions.OWNER;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public isKeyCreator(publicKey: Uint8Array): boolean {
|
|
71
|
+
return (
|
|
72
|
+
(this._internals.permission.get(crypto.to_hex(publicKey))! & Permissions.KEY_CREATOR) ===
|
|
73
|
+
Permissions.KEY_CREATOR
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public ownsKey(publicKey: Uint8Array): boolean {
|
|
78
|
+
return this._internals.keys.get(crypto.to_hex(publicKey)) !== undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
public isMemberAdder(publicKey: Uint8Array): boolean {
|
|
82
|
+
return (
|
|
83
|
+
(this._internals.permission.get(crypto.to_hex(publicKey))! & Permissions.ADD_MEMBER) ===
|
|
84
|
+
Permissions.ADD_MEMBER
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public isMemberRemover(publicKey: Uint8Array): boolean {
|
|
89
|
+
return (
|
|
90
|
+
(this._internals.permission.get(crypto.to_hex(publicKey))! & Permissions.REMOVE_MEMBER) ===
|
|
91
|
+
Permissions.REMOVE_MEMBER
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public keyCount(): number {
|
|
96
|
+
return this._internals.keys.size;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public getEncryptedKey(publicKey: Uint8Array): PublishedKey | null {
|
|
100
|
+
const key = this._internals.keys.get(crypto.to_hex(publicKey));
|
|
101
|
+
if (key) return key;
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public getGroupPublicKey(): Uint8Array {
|
|
106
|
+
return this._internals.groupPublicKey;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public getStreamDerivationPath(): number[] {
|
|
110
|
+
return this._internals.derivationPath;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function exists(list: Uint8Array[], obj: Uint8Array): boolean {
|
|
115
|
+
for (const item of list) {
|
|
116
|
+
if (obj.length !== item.length) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
for (let i = 0; i < item.length; i++) {
|
|
120
|
+
if (item[i] !== obj[i]) {
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export default class CommandStreamResolver {
|
|
130
|
+
private static assertIssuerCanPublish(
|
|
131
|
+
issuer: Uint8Array,
|
|
132
|
+
internals: ResolvedCommandStreamInternals,
|
|
133
|
+
): void {
|
|
134
|
+
if (!exists(internals.members, issuer)) {
|
|
135
|
+
throw new Error("Issuer is not a member of the group at height " + internals.height);
|
|
136
|
+
}
|
|
137
|
+
if ((internals.permission.get(crypto.to_hex(issuer))! & 0x02) === Permissions.KEY_READER) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
"Issuer does not have permission to publish keys at height " + internals.height,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (
|
|
143
|
+
internals.keys.get(crypto.to_hex(issuer)) === undefined &&
|
|
144
|
+
(internals.permission.get(crypto.to_hex(issuer))! & Permissions.KEY_CREATOR) !=
|
|
145
|
+
Permissions.KEY_CREATOR
|
|
146
|
+
) {
|
|
147
|
+
throw new Error("Issuer does not have a key to publish at height " + internals.height);
|
|
148
|
+
}
|
|
149
|
+
if (
|
|
150
|
+
!internals.keys.has(crypto.to_hex(issuer)) &&
|
|
151
|
+
(internals.permission.get(crypto.to_hex(issuer))! & Permissions.KEY_CREATOR) !==
|
|
152
|
+
Permissions.KEY_CREATOR &&
|
|
153
|
+
internals.keys.keys.length > 0
|
|
154
|
+
) {
|
|
155
|
+
throw new Error("Issuer is trying to publish a new key at height " + internals.height);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private static assertIssuerCanAddMember(
|
|
160
|
+
issuer: Uint8Array,
|
|
161
|
+
internals: ResolvedCommandStreamInternals,
|
|
162
|
+
): void {
|
|
163
|
+
if (!exists(internals.members, issuer)) {
|
|
164
|
+
throw new Error("Issuer is not a member of the group at height " + internals.height);
|
|
165
|
+
}
|
|
166
|
+
if (
|
|
167
|
+
(internals.permission.get(crypto.to_hex(issuer))! & Permissions.ADD_MEMBER) !==
|
|
168
|
+
Permissions.ADD_MEMBER
|
|
169
|
+
) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
"Issuer does not have permission to add members at height " + internals.height,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private static assertStreamIsCreated(internals: ResolvedCommandStreamInternals): void {
|
|
177
|
+
if (internals.isCreated === false) {
|
|
178
|
+
throw new Error("The stream is not created at height " + internals.height);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
private static replayCommand(
|
|
183
|
+
command: Command,
|
|
184
|
+
block: CommandBlock,
|
|
185
|
+
blockHash: string,
|
|
186
|
+
height: number,
|
|
187
|
+
internals: ResolvedCommandStreamInternals,
|
|
188
|
+
): ResolvedCommandStreamInternals {
|
|
189
|
+
switch (command.getType()) {
|
|
190
|
+
case CommandType.Seed:
|
|
191
|
+
internals.isCreated = true;
|
|
192
|
+
internals.topic = (command as Seed).topic;
|
|
193
|
+
internals.members.push(block.issuer);
|
|
194
|
+
internals.permission.set(crypto.to_hex(block.issuer), Permissions.OWNER);
|
|
195
|
+
internals.streamId = blockHash;
|
|
196
|
+
internals.keys.set(crypto.to_hex(block.issuer), {
|
|
197
|
+
encryptedXpriv: (command as Seed).encryptedXpriv,
|
|
198
|
+
issuer: block.issuer,
|
|
199
|
+
ephemeralPublicKey: (command as Seed).ephemeralPublicKey,
|
|
200
|
+
initialiationVector: (command as Seed).initializationVector,
|
|
201
|
+
});
|
|
202
|
+
internals.groupPublicKey = (command as Seed).groupKey;
|
|
203
|
+
break;
|
|
204
|
+
case CommandType.Derive:
|
|
205
|
+
internals.isCreated = true;
|
|
206
|
+
internals.members.push(block.issuer);
|
|
207
|
+
internals.permission.set(crypto.to_hex(block.issuer), Permissions.OWNER);
|
|
208
|
+
internals.streamId = blockHash;
|
|
209
|
+
internals.keys.set(crypto.to_hex(block.issuer), {
|
|
210
|
+
encryptedXpriv: (command as Derive).encryptedXpriv,
|
|
211
|
+
ephemeralPublicKey: (command as Derive).ephemeralPublicKey,
|
|
212
|
+
initialiationVector: (command as Derive).initializationVector,
|
|
213
|
+
issuer: block.issuer,
|
|
214
|
+
});
|
|
215
|
+
internals.groupPublicKey = (command as Derive).groupKey;
|
|
216
|
+
internals.derivationPath = (command as Derive).path;
|
|
217
|
+
break;
|
|
218
|
+
case CommandType.AddMember: {
|
|
219
|
+
this.assertStreamIsCreated(internals);
|
|
220
|
+
this.assertIssuerCanAddMember(block.issuer, internals);
|
|
221
|
+
const { publicKey, permissions, name } = command as AddMember;
|
|
222
|
+
const id = crypto.to_hex(publicKey);
|
|
223
|
+
internals.members.push(publicKey);
|
|
224
|
+
internals.permission.set(id, permissions);
|
|
225
|
+
internals.names.set(id, name);
|
|
226
|
+
internals.membersData.push({ id, name, permissions });
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
case CommandType.PublishKey:
|
|
230
|
+
this.assertStreamIsCreated(internals);
|
|
231
|
+
this.assertIssuerCanPublish(block.issuer, internals);
|
|
232
|
+
internals.keys.set(crypto.to_hex((command as PublishKey).recipient), {
|
|
233
|
+
encryptedXpriv: (command as PublishKey).encryptedXpriv,
|
|
234
|
+
ephemeralPublicKey: (command as PublishKey).ephemeralPublicKey,
|
|
235
|
+
issuer: block.issuer,
|
|
236
|
+
initialiationVector: (command as PublishKey).initializationVector,
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
return internals;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private static async resolveBlock(
|
|
244
|
+
block: CommandBlock,
|
|
245
|
+
height: number,
|
|
246
|
+
internals: ResolvedCommandStreamInternals,
|
|
247
|
+
): Promise<ResolvedCommandStreamInternals> {
|
|
248
|
+
// Check signature
|
|
249
|
+
if ((await verifyCommandBlock(block)) === false) {
|
|
250
|
+
throw new Error("Invalid block signature at height " + height);
|
|
251
|
+
}
|
|
252
|
+
// Check if issuer is part of the group
|
|
253
|
+
if (height > 0 && !exists(internals.members, block.issuer)) {
|
|
254
|
+
throw new Error("Issuer is not part of the group at height " + height);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const blockHash = crypto.to_hex(await hashCommandBlock(block));
|
|
258
|
+
|
|
259
|
+
for (const command of block.commands) {
|
|
260
|
+
internals = CommandStreamResolver.replayCommand(command, block, blockHash, height, internals);
|
|
261
|
+
}
|
|
262
|
+
internals.hashes.push(blockHash);
|
|
263
|
+
return internals;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
public static async resolve(stream: CommandBlock[]): Promise<ResolvedCommandStream> {
|
|
267
|
+
let internals = new ResolvedCommandStreamInternals();
|
|
268
|
+
for (let height = 0; height < stream.length; height++) {
|
|
269
|
+
internals.height = height;
|
|
270
|
+
const block = stream[height];
|
|
271
|
+
if (
|
|
272
|
+
height > 0 &&
|
|
273
|
+
crypto.to_hex(block.parent) !== crypto.to_hex(await hashCommandBlock(stream[height - 1]))
|
|
274
|
+
) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
"Command stream has been tampered with (invalid parent hash) at height " + height,
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
if (block.signature.length === 0) break;
|
|
280
|
+
internals = await CommandStreamResolver.resolveBlock(block, height, internals);
|
|
281
|
+
}
|
|
282
|
+
return new ResolvedCommandStream(internals);
|
|
283
|
+
}
|
|
284
|
+
}
|
package/src/Crypto.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { NobleCryptoSecp256k1 } from "./NobleCrypto";
|
|
2
|
+
|
|
3
|
+
export interface KeyPair {
|
|
4
|
+
publicKey: Uint8Array;
|
|
5
|
+
privateKey: Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface KeyPairWithChainCode extends KeyPair {
|
|
9
|
+
chainCode: Uint8Array;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
*/
|
|
15
|
+
export interface Crypto {
|
|
16
|
+
randomKeypair(): Promise<KeyPair>;
|
|
17
|
+
keypairFromSecretKey(secretKey: Uint8Array): Promise<KeyPair>;
|
|
18
|
+
sign(message: Uint8Array, keyPair: KeyPair): Promise<Uint8Array>;
|
|
19
|
+
verify(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): Promise<boolean>;
|
|
20
|
+
encrypt(secret: Uint8Array, nonce: Uint8Array, message: Uint8Array): Promise<Uint8Array>;
|
|
21
|
+
decrypt(secret: Uint8Array, nonce: Uint8Array, ciphertext: Uint8Array): Promise<Uint8Array>;
|
|
22
|
+
randomBytes(size: number): Promise<Uint8Array>;
|
|
23
|
+
ecdh(keyPair: KeyPair, publicKey: Uint8Array): Promise<Uint8Array>;
|
|
24
|
+
hash(message: Uint8Array): Promise<Uint8Array>;
|
|
25
|
+
computeSymmetricKey(privateKey: Uint8Array, extra: Uint8Array): Promise<Uint8Array>;
|
|
26
|
+
from_hex(hex: string): Uint8Array;
|
|
27
|
+
to_hex(bytes?: Uint8Array): string;
|
|
28
|
+
derivePrivate(xpriv: Uint8Array, path: number[]): Promise<KeyPairWithChainCode>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class DerivationPath {
|
|
32
|
+
private constructor() {}
|
|
33
|
+
|
|
34
|
+
static hardenedIndex(index: number): number {
|
|
35
|
+
return index + 0x80000000;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static reverseHardenedIndex(index: number): number {
|
|
39
|
+
return index - 0x80000000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static toIndexArray(path: string | number[]): number[] {
|
|
43
|
+
if (Array.isArray(path)) {
|
|
44
|
+
return path;
|
|
45
|
+
}
|
|
46
|
+
if (path.startsWith("m/")) {
|
|
47
|
+
path = path.substring(2);
|
|
48
|
+
}
|
|
49
|
+
return path.split("/").map(s => {
|
|
50
|
+
if (s.endsWith("'") || s.endsWith("h")) {
|
|
51
|
+
return parseInt(s.substring(0, s.length - 1)) + 0x80000000;
|
|
52
|
+
}
|
|
53
|
+
return parseInt(s);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static toString(path: number[] | string): string {
|
|
58
|
+
if (typeof path === "string") {
|
|
59
|
+
return path;
|
|
60
|
+
}
|
|
61
|
+
return (
|
|
62
|
+
"m/" +
|
|
63
|
+
path
|
|
64
|
+
.map(s => {
|
|
65
|
+
if (s >= 0x80000000) {
|
|
66
|
+
return s - 0x80000000 + "'";
|
|
67
|
+
}
|
|
68
|
+
return s;
|
|
69
|
+
})
|
|
70
|
+
.join("/")
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
*
|
|
77
|
+
*/
|
|
78
|
+
export const crypto = new NobleCryptoSecp256k1();
|