@ledgerhq/hw-ledger-key-ring-protocol 0.2.1-fix-build-number-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +33 -0
- package/.turbo/turbo-build.log +4 -0
- package/CHANGELOG.md +126 -0
- package/LICENSE.txt +21 -0
- package/README.md +3 -0
- package/jest.config.js +13 -0
- package/lib/ApduDevice.d.ts +99 -0
- package/lib/ApduDevice.d.ts.map +1 -0
- package/lib/ApduDevice.js +528 -0
- package/lib/ApduDevice.js.map +1 -0
- package/lib/BigEndian.d.ts +7 -0
- package/lib/BigEndian.d.ts.map +1 -0
- package/lib/BigEndian.js +26 -0
- package/lib/BigEndian.js.map +1 -0
- package/lib/CommandBlock.d.ts +114 -0
- package/lib/CommandBlock.d.ts.map +1 -0
- package/lib/CommandBlock.js +156 -0
- package/lib/CommandBlock.js.map +1 -0
- package/lib/CommandStream.d.ts +38 -0
- package/lib/CommandStream.d.ts.map +1 -0
- package/lib/CommandStream.js +195 -0
- package/lib/CommandStream.js.map +1 -0
- package/lib/CommandStreamDecoder.d.ts +15 -0
- package/lib/CommandStreamDecoder.d.ts.map +1 -0
- package/lib/CommandStreamDecoder.js +101 -0
- package/lib/CommandStreamDecoder.js.map +1 -0
- package/lib/CommandStreamEncoder.d.ts +16 -0
- package/lib/CommandStreamEncoder.d.ts.map +1 -0
- package/lib/CommandStreamEncoder.js +131 -0
- package/lib/CommandStreamEncoder.js.map +1 -0
- package/lib/CommandStreamJsonifier.d.ts +6 -0
- package/lib/CommandStreamJsonifier.d.ts.map +1 -0
- package/lib/CommandStreamJsonifier.js +75 -0
- package/lib/CommandStreamJsonifier.js.map +1 -0
- package/lib/CommandStreamResolver.d.ts +53 -0
- package/lib/CommandStreamResolver.d.ts.map +1 -0
- package/lib/CommandStreamResolver.js +221 -0
- package/lib/CommandStreamResolver.js.map +1 -0
- package/lib/Crypto.d.ts +38 -0
- package/lib/Crypto.d.ts.map +1 -0
- package/lib/Crypto.js +47 -0
- package/lib/Crypto.js.map +1 -0
- package/lib/Device.d.ts +43 -0
- package/lib/Device.d.ts.map +1 -0
- package/lib/Device.js +195 -0
- package/lib/Device.js.map +1 -0
- package/lib/IndexedTree.d.ts +13 -0
- package/lib/IndexedTree.d.ts.map +1 -0
- package/lib/IndexedTree.js +75 -0
- package/lib/IndexedTree.js.map +1 -0
- package/lib/NobleCrypto.d.ts +39 -0
- package/lib/NobleCrypto.d.ts.map +1 -0
- package/lib/NobleCrypto.js +240 -0
- package/lib/NobleCrypto.js.map +1 -0
- package/lib/PublicKey.d.ts +5 -0
- package/lib/PublicKey.d.ts.map +1 -0
- package/lib/PublicKey.js +10 -0
- package/lib/PublicKey.js.map +1 -0
- package/lib/SeedId.d.ts +80 -0
- package/lib/SeedId.d.ts.map +1 -0
- package/lib/SeedId.js +244 -0
- package/lib/SeedId.js.map +1 -0
- package/lib/StreamTree.d.ts +50 -0
- package/lib/StreamTree.d.ts.map +1 -0
- package/lib/StreamTree.js +169 -0
- package/lib/StreamTree.js.map +1 -0
- package/lib/StreamTreeCipher.d.ts +46 -0
- package/lib/StreamTreeCipher.d.ts.map +1 -0
- package/lib/StreamTreeCipher.js +175 -0
- package/lib/StreamTreeCipher.js.map +1 -0
- package/lib/__tests__/codec.d.ts +2 -0
- package/lib/__tests__/codec.d.ts.map +1 -0
- package/lib/__tests__/codec.js +108 -0
- package/lib/__tests__/codec.js.map +1 -0
- package/lib/__tests__/crypto.d.ts +2 -0
- package/lib/__tests__/crypto.d.ts.map +1 -0
- package/lib/__tests__/crypto.js +46 -0
- package/lib/__tests__/crypto.js.map +1 -0
- package/lib/__tests__/indexed_tree.d.ts +2 -0
- package/lib/__tests__/indexed_tree.d.ts.map +1 -0
- package/lib/__tests__/indexed_tree.js +45 -0
- package/lib/__tests__/indexed_tree.js.map +1 -0
- package/lib/__tests__/key_exchange.d.ts +2 -0
- package/lib/__tests__/key_exchange.d.ts.map +1 -0
- package/lib/__tests__/key_exchange.js +129 -0
- package/lib/__tests__/key_exchange.js.map +1 -0
- package/lib/__tests__/seedId.d.ts +2 -0
- package/lib/__tests__/seedId.d.ts.map +1 -0
- package/lib/__tests__/seedId.js +92 -0
- package/lib/__tests__/seedId.js.map +1 -0
- package/lib/__tests__/shared_object.d.ts +2 -0
- package/lib/__tests__/shared_object.d.ts.map +1 -0
- package/lib/__tests__/shared_object.js +78 -0
- package/lib/__tests__/shared_object.js.map +1 -0
- package/lib/index.d.ts +35 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +81 -0
- package/lib/index.js.map +1 -0
- package/lib/tlv.d.ts +99 -0
- package/lib/tlv.d.ts.map +1 -0
- package/lib/tlv.js +150 -0
- package/lib/tlv.js.map +1 -0
- package/lib-es/ApduDevice.d.ts +99 -0
- package/lib-es/ApduDevice.d.ts.map +1 -0
- package/lib-es/ApduDevice.js +522 -0
- package/lib-es/ApduDevice.js.map +1 -0
- package/lib-es/BigEndian.d.ts +7 -0
- package/lib-es/BigEndian.d.ts.map +1 -0
- package/lib-es/BigEndian.js +23 -0
- package/lib-es/BigEndian.js.map +1 -0
- package/lib-es/CommandBlock.d.ts +114 -0
- package/lib-es/CommandBlock.d.ts.map +1 -0
- package/lib-es/CommandBlock.js +143 -0
- package/lib-es/CommandBlock.js.map +1 -0
- package/lib-es/CommandStream.d.ts +38 -0
- package/lib-es/CommandStream.d.ts.map +1 -0
- package/lib-es/CommandStream.js +187 -0
- package/lib-es/CommandStream.js.map +1 -0
- package/lib-es/CommandStreamDecoder.d.ts +15 -0
- package/lib-es/CommandStreamDecoder.d.ts.map +1 -0
- package/lib-es/CommandStreamDecoder.js +97 -0
- package/lib-es/CommandStreamDecoder.js.map +1 -0
- package/lib-es/CommandStreamEncoder.d.ts +16 -0
- package/lib-es/CommandStreamEncoder.d.ts.map +1 -0
- package/lib-es/CommandStreamEncoder.js +127 -0
- package/lib-es/CommandStreamEncoder.js.map +1 -0
- package/lib-es/CommandStreamJsonifier.d.ts +6 -0
- package/lib-es/CommandStreamJsonifier.d.ts.map +1 -0
- package/lib-es/CommandStreamJsonifier.js +72 -0
- package/lib-es/CommandStreamJsonifier.js.map +1 -0
- package/lib-es/CommandStreamResolver.d.ts +53 -0
- package/lib-es/CommandStreamResolver.d.ts.map +1 -0
- package/lib-es/CommandStreamResolver.js +216 -0
- package/lib-es/CommandStreamResolver.js.map +1 -0
- package/lib-es/Crypto.d.ts +38 -0
- package/lib-es/Crypto.d.ts.map +1 -0
- package/lib-es/Crypto.js +43 -0
- package/lib-es/Crypto.js.map +1 -0
- package/lib-es/Device.d.ts +43 -0
- package/lib-es/Device.d.ts.map +1 -0
- package/lib-es/Device.js +187 -0
- package/lib-es/Device.js.map +1 -0
- package/lib-es/IndexedTree.d.ts +13 -0
- package/lib-es/IndexedTree.d.ts.map +1 -0
- package/lib-es/IndexedTree.js +71 -0
- package/lib-es/IndexedTree.js.map +1 -0
- package/lib-es/NobleCrypto.d.ts +39 -0
- package/lib-es/NobleCrypto.d.ts.map +1 -0
- package/lib-es/NobleCrypto.js +209 -0
- package/lib-es/NobleCrypto.js.map +1 -0
- package/lib-es/PublicKey.d.ts +5 -0
- package/lib-es/PublicKey.d.ts.map +1 -0
- package/lib-es/PublicKey.js +6 -0
- package/lib-es/PublicKey.js.map +1 -0
- package/lib-es/SeedId.d.ts +80 -0
- package/lib-es/SeedId.d.ts.map +1 -0
- package/lib-es/SeedId.js +235 -0
- package/lib-es/SeedId.js.map +1 -0
- package/lib-es/StreamTree.d.ts +50 -0
- package/lib-es/StreamTree.d.ts.map +1 -0
- package/lib-es/StreamTree.js +165 -0
- package/lib-es/StreamTree.js.map +1 -0
- package/lib-es/StreamTreeCipher.d.ts +46 -0
- package/lib-es/StreamTreeCipher.d.ts.map +1 -0
- package/lib-es/StreamTreeCipher.js +171 -0
- package/lib-es/StreamTreeCipher.js.map +1 -0
- package/lib-es/__tests__/codec.d.ts +2 -0
- package/lib-es/__tests__/codec.d.ts.map +1 -0
- package/lib-es/__tests__/codec.js +106 -0
- package/lib-es/__tests__/codec.js.map +1 -0
- package/lib-es/__tests__/crypto.d.ts +2 -0
- package/lib-es/__tests__/crypto.d.ts.map +1 -0
- package/lib-es/__tests__/crypto.js +44 -0
- package/lib-es/__tests__/crypto.js.map +1 -0
- package/lib-es/__tests__/indexed_tree.d.ts +2 -0
- package/lib-es/__tests__/indexed_tree.d.ts.map +1 -0
- package/lib-es/__tests__/indexed_tree.js +43 -0
- package/lib-es/__tests__/indexed_tree.js.map +1 -0
- package/lib-es/__tests__/key_exchange.d.ts +2 -0
- package/lib-es/__tests__/key_exchange.d.ts.map +1 -0
- package/lib-es/__tests__/key_exchange.js +124 -0
- package/lib-es/__tests__/key_exchange.js.map +1 -0
- package/lib-es/__tests__/seedId.d.ts +2 -0
- package/lib-es/__tests__/seedId.d.ts.map +1 -0
- package/lib-es/__tests__/seedId.js +90 -0
- package/lib-es/__tests__/seedId.js.map +1 -0
- package/lib-es/__tests__/shared_object.d.ts +2 -0
- package/lib-es/__tests__/shared_object.d.ts.map +1 -0
- package/lib-es/__tests__/shared_object.js +76 -0
- package/lib-es/__tests__/shared_object.js.map +1 -0
- package/lib-es/index.d.ts +35 -0
- package/lib-es/index.d.ts.map +1 -0
- package/lib-es/index.js +32 -0
- package/lib-es/index.js.map +1 -0
- package/lib-es/tlv.d.ts +99 -0
- package/lib-es/tlv.d.ts.map +1 -0
- package/lib-es/tlv.js +144 -0
- package/lib-es/tlv.js.map +1 -0
- package/package.json +63 -0
- package/src/ApduDevice.ts +688 -0
- package/src/BigEndian.ts +25 -0
- package/src/CommandBlock.ts +244 -0
- package/src/CommandStream.ts +260 -0
- package/src/CommandStreamDecoder.ts +142 -0
- package/src/CommandStreamEncoder.ts +144 -0
- package/src/CommandStreamJsonifier.ts +82 -0
- package/src/CommandStreamResolver.ts +284 -0
- package/src/Crypto.ts +78 -0
- package/src/Device.ts +246 -0
- package/src/IndexedTree.ts +80 -0
- package/src/NobleCrypto.ts +255 -0
- package/src/PublicKey.ts +6 -0
- package/src/SeedId.ts +338 -0
- package/src/StreamTree.ts +212 -0
- package/src/StreamTreeCipher.ts +207 -0
- package/src/__tests__/codec.ts +146 -0
- package/src/__tests__/crypto.ts +44 -0
- package/src/__tests__/indexed_tree.ts +51 -0
- package/src/__tests__/key_exchange.ts +167 -0
- package/src/__tests__/seedId.ts +120 -0
- package/src/__tests__/shared_object.ts +118 -0
- package/src/index.ts +43 -0
- package/src/tlv.ts +210 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { crypto } from "../Crypto";
|
|
2
|
+
import { Challenge, PubKeyCredential } from "../SeedId";
|
|
3
|
+
|
|
4
|
+
describe("PubKeyCredential regular cases", () => {
|
|
5
|
+
const rpPubKeyCredential = new PubKeyCredential({
|
|
6
|
+
version: 0,
|
|
7
|
+
curveId: 0x21,
|
|
8
|
+
signAlgorithm: 0x01,
|
|
9
|
+
publicKey: crypto.from_hex(
|
|
10
|
+
"02b2c29ab36022219967cc21a306599ecaf51ce9f2998da6982388d52c8c69a6a5",
|
|
11
|
+
),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("assertValidity", () => {
|
|
15
|
+
expect(() => rpPubKeyCredential.assertValidity()).not.toThrow();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("toJSON", () => {
|
|
19
|
+
expect(rpPubKeyCredential.toJSON()).toEqual({
|
|
20
|
+
version: 0,
|
|
21
|
+
curveId: 33,
|
|
22
|
+
signAlgorithm: 1,
|
|
23
|
+
publicKey: "02b2c29ab36022219967cc21a306599ecaf51ce9f2998da6982388d52c8c69a6a5",
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("toBytes", () => {
|
|
28
|
+
expect(crypto.to_hex(rpPubKeyCredential.toBytes())).toEqual(
|
|
29
|
+
"0021012102b2c29ab36022219967cc21a306599ecaf51ce9f2998da6982388d52c8c69a6a5",
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("fromBytes", () => {
|
|
34
|
+
expect(
|
|
35
|
+
PubKeyCredential.fromBytes(
|
|
36
|
+
crypto.from_hex(
|
|
37
|
+
"0021012102b2c29ab36022219967cc21a306599ecaf51ce9f2998da6982388d52c8c69a6a5",
|
|
38
|
+
),
|
|
39
|
+
),
|
|
40
|
+
).toEqual([rpPubKeyCredential, 37]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("toBytes into fromBytes", () => {
|
|
44
|
+
const bytes = rpPubKeyCredential.toBytes();
|
|
45
|
+
const [pubKeyCredential, _] = PubKeyCredential.fromBytes(bytes);
|
|
46
|
+
expect(pubKeyCredential).toEqual(rpPubKeyCredential);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("Challenge regular cases", () => {
|
|
51
|
+
const rpPubKeyCredential = new PubKeyCredential({
|
|
52
|
+
version: 0,
|
|
53
|
+
curveId: 0x21,
|
|
54
|
+
signAlgorithm: 0x01,
|
|
55
|
+
publicKey: crypto.from_hex(
|
|
56
|
+
"02b2c29ab36022219967cc21a306599ecaf51ce9f2998da6982388d52c8c69a6a5",
|
|
57
|
+
),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const challengeData = crypto.from_hex("53cafde60e5395b164eb867213bc05f6");
|
|
61
|
+
const rpSignature = crypto.from_hex(
|
|
62
|
+
"3045022025d130d7ae5c48a6cf09781d04a08e9a2d07ce1bd17e84637f6ede4a043c5dcc022100a846ececf20eb53ffc2dc502ce8074ba40b241bfd13edaf1e8575559a9b2b4ea",
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const challenge = new Challenge({
|
|
66
|
+
payloadType: 0x07,
|
|
67
|
+
version: 0,
|
|
68
|
+
protocolVersion: {
|
|
69
|
+
major: 1,
|
|
70
|
+
minor: 0,
|
|
71
|
+
patch: 0,
|
|
72
|
+
},
|
|
73
|
+
challengeData: challengeData,
|
|
74
|
+
challengeExpiry: new Date(1708678950000),
|
|
75
|
+
host: "localhost",
|
|
76
|
+
rpCredential: rpPubKeyCredential,
|
|
77
|
+
rpSignature: rpSignature,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("parse one example from backend", () => {
|
|
81
|
+
const hex =
|
|
82
|
+
"01010702010012102d4492d50ef878a450d9bd6e8862690014010115483046022100beb271fa9752c377251005e125cc115737e427d5259a2dbbc9a38f7a36430c3f0221008826b3d3c1b491b41908d7cc89d71b92e9428c52a927e95341d555252f687b0f16046654aea120096c6f63616c686f7374320121332103cb7628e7248ddf9c07da54b979f16bf081fb3d173aac0992ad2a44ef6a388ae2600401000000";
|
|
83
|
+
const bytes = crypto.from_hex(hex);
|
|
84
|
+
const [challengeFromBytes, l] = Challenge.fromBytes(bytes);
|
|
85
|
+
expect(l).toEqual(bytes.length);
|
|
86
|
+
|
|
87
|
+
expect(challengeFromBytes.toJSON()).toEqual({
|
|
88
|
+
payloadType: 7,
|
|
89
|
+
version: 0,
|
|
90
|
+
challenge: { data: "2d4492d50ef878a450d9bd6e88626900", expiry: "2024-05-27T16:02:41.000Z" },
|
|
91
|
+
host: "localhost",
|
|
92
|
+
rp: [
|
|
93
|
+
{
|
|
94
|
+
credential: {
|
|
95
|
+
version: 0,
|
|
96
|
+
curveId: 33,
|
|
97
|
+
signAlgorithm: 1,
|
|
98
|
+
publicKey: "03cb7628e7248ddf9c07da54b979f16bf081fb3d173aac0992ad2a44ef6a388ae2",
|
|
99
|
+
},
|
|
100
|
+
signature:
|
|
101
|
+
"3046022100beb271fa9752c377251005e125cc115737e427d5259a2dbbc9a38f7a36430c3f0221008826b3d3c1b491b41908d7cc89d71b92e9428c52a927e95341d555252f687b0f",
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
protocolVersion: { major: 1, minor: 0, patch: 0 },
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("toBytes", () => {
|
|
109
|
+
expect(crypto.to_hex(challenge.toBytes())).toEqual(
|
|
110
|
+
"010107020100121053cafde60e5395b164eb867213bc05f614010115473045022025d130d7ae5c48a6cf09781d04a08e9a2d07ce1bd17e84637f6ede4a043c5dcc022100a846ececf20eb53ffc2dc502ce8074ba40b241bfd13edaf1e8575559a9b2b4ea160465d85f2620096c6f63616c686f7374320121332102b2c29ab36022219967cc21a306599ecaf51ce9f2998da6982388d52c8c69a6a5600401000000",
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("toBytes <> fromBytes", () => {
|
|
115
|
+
const bytes = challenge.toBytes();
|
|
116
|
+
const [challengeFromBytes, _] = Challenge.fromBytes(bytes);
|
|
117
|
+
expect(challengeFromBytes).toEqual(challenge);
|
|
118
|
+
expect(challengeFromBytes.toBytes()).toEqual(bytes);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This test suite simulates an application generating data (using a fixed shchema) and sharing part of the
|
|
3
|
+
data to specific users. The data is encrypted using a StreamTree.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { StreamTree } from "../StreamTree";
|
|
7
|
+
import { Device, device } from "..";
|
|
8
|
+
import { Permissions } from "../CommandBlock";
|
|
9
|
+
import { StreamTreeCipher } from "../StreamTreeCipher";
|
|
10
|
+
import { DerivationPath, crypto } from "../Crypto";
|
|
11
|
+
|
|
12
|
+
interface SharedObject {
|
|
13
|
+
name: string;
|
|
14
|
+
age: number;
|
|
15
|
+
email: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface EncryptedSharedObject {
|
|
19
|
+
name: string;
|
|
20
|
+
age: string;
|
|
21
|
+
email: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const APPLICATION_ID = 12;
|
|
25
|
+
|
|
26
|
+
async function encryptSharedObject(
|
|
27
|
+
device: Device,
|
|
28
|
+
tree: StreamTree,
|
|
29
|
+
sharedObject: SharedObject,
|
|
30
|
+
mapping: Map<string, string> = new Map(),
|
|
31
|
+
): Promise<EncryptedSharedObject> {
|
|
32
|
+
const cipher = StreamTreeCipher.create(device);
|
|
33
|
+
const encrypt = async (key: keyof typeof sharedObject, defaultPath: string): Promise<string> => {
|
|
34
|
+
return crypto.to_hex(
|
|
35
|
+
await cipher.encrypt(
|
|
36
|
+
tree,
|
|
37
|
+
DerivationPath.toIndexArray(mapping.get(key) || defaultPath),
|
|
38
|
+
new TextEncoder().encode((sharedObject[key] as string | number).toString()),
|
|
39
|
+
),
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
const encryptedSharedObject = {
|
|
43
|
+
name: await encrypt("name", `0h/${APPLICATION_ID}h/0h/0h/0h`),
|
|
44
|
+
age: await encrypt("age", `0h/${APPLICATION_ID}h/0h/1h/0h`),
|
|
45
|
+
email: await encrypt("email", `0h/${APPLICATION_ID}h/0h/2h/0h`),
|
|
46
|
+
};
|
|
47
|
+
return encryptedSharedObject;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function decryptSharedObject(
|
|
51
|
+
device: Device,
|
|
52
|
+
tree: StreamTree,
|
|
53
|
+
encryptedSharedObject: EncryptedSharedObject,
|
|
54
|
+
mapping: Map<string, string> = new Map(),
|
|
55
|
+
): Promise<SharedObject> {
|
|
56
|
+
const cipher = StreamTreeCipher.create(device);
|
|
57
|
+
const decrypt = async (key: string, defaultPath: string): Promise<string> => {
|
|
58
|
+
const path = DerivationPath.toIndexArray(mapping.get(key) || defaultPath);
|
|
59
|
+
const bytes = await cipher.decrypt(
|
|
60
|
+
tree,
|
|
61
|
+
path,
|
|
62
|
+
crypto.from_hex(encryptedSharedObject[key as keyof typeof encryptedSharedObject] as string),
|
|
63
|
+
);
|
|
64
|
+
return new TextDecoder().decode(bytes);
|
|
65
|
+
};
|
|
66
|
+
const sharedObject = {
|
|
67
|
+
name: await decrypt("name", `0h/${APPLICATION_ID}h/0h/0h/0h`),
|
|
68
|
+
age: parseInt(await decrypt("age", `0h/${APPLICATION_ID}h/0h/1h/0h`)),
|
|
69
|
+
email: await decrypt("email", `0h/${APPLICATION_ID}h/0h/2h/0h`),
|
|
70
|
+
};
|
|
71
|
+
return sharedObject;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
describe("Shared object scenario using StreamTree", () => {
|
|
75
|
+
it("should create a tree with 3 members, one member encrypt a shared object and another one decrypts it", async () => {
|
|
76
|
+
const alice = await device.software();
|
|
77
|
+
const bob = await device.software();
|
|
78
|
+
const carol = await device.software();
|
|
79
|
+
|
|
80
|
+
// Create a new tree owned by alice
|
|
81
|
+
let tree = await StreamTree.createNewTree(alice);
|
|
82
|
+
|
|
83
|
+
// Share the application node with bob
|
|
84
|
+
tree = await tree.share(
|
|
85
|
+
tree.getApplicationRootPath(APPLICATION_ID),
|
|
86
|
+
alice,
|
|
87
|
+
(await bob.getPublicKey()).publicKey,
|
|
88
|
+
"Bob",
|
|
89
|
+
Permissions.OWNER,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Bob creates a shared object and encrypt it (1 value -> 1 encryption key)
|
|
93
|
+
const sharedObject = {
|
|
94
|
+
name: "Bob",
|
|
95
|
+
age: 42,
|
|
96
|
+
email: "bob@box.com",
|
|
97
|
+
};
|
|
98
|
+
//console.dir(sharedObject, { depth: null });
|
|
99
|
+
const encryptedObject = await encryptSharedObject(bob, tree, sharedObject);
|
|
100
|
+
|
|
101
|
+
// Share the application node with carol
|
|
102
|
+
tree = await tree.share(
|
|
103
|
+
tree.getApplicationRootPath(APPLICATION_ID),
|
|
104
|
+
alice,
|
|
105
|
+
(await carol.getPublicKey()).publicKey,
|
|
106
|
+
"Carol",
|
|
107
|
+
Permissions.OWNER,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// Decrypt with Carol
|
|
111
|
+
const decryptedObject = await decryptSharedObject(carol, tree, encryptedObject);
|
|
112
|
+
|
|
113
|
+
//console.dir(encryptedObject, { depth: null });
|
|
114
|
+
//console.dir(decryptedObject, { depth: null });
|
|
115
|
+
|
|
116
|
+
expect(decryptedObject).toEqual(sharedObject);
|
|
117
|
+
});
|
|
118
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createDevice, SoftwareDevice } from "./Device";
|
|
2
|
+
import * as CS from "./CommandStream";
|
|
3
|
+
import { createApduDevice } from "./ApduDevice";
|
|
4
|
+
import Jsonifier from "./CommandStreamJsonifier";
|
|
5
|
+
|
|
6
|
+
export type { Device } from "./Device";
|
|
7
|
+
export type { StreamTreeCipherMode } from "./StreamTreeCipher";
|
|
8
|
+
export { StreamTreeCipher } from "./StreamTreeCipher";
|
|
9
|
+
export { crypto, DerivationPath } from "./Crypto";
|
|
10
|
+
export { PublicKey } from "./PublicKey";
|
|
11
|
+
export type { CommandType, Command, CommandBlock } from "./CommandBlock";
|
|
12
|
+
export {
|
|
13
|
+
AddMember,
|
|
14
|
+
CloseStream,
|
|
15
|
+
Derive,
|
|
16
|
+
EditMember,
|
|
17
|
+
PublishKey,
|
|
18
|
+
Seed,
|
|
19
|
+
Permissions,
|
|
20
|
+
} from "./CommandBlock";
|
|
21
|
+
export { APDU, TRUSTCHAIN_APP_NAME } from "./ApduDevice";
|
|
22
|
+
export { CommandStreamEncoder } from "./CommandStreamEncoder";
|
|
23
|
+
export { CommandStreamDecoder } from "./CommandStreamDecoder";
|
|
24
|
+
export { Challenge, PubKeyCredential } from "./SeedId";
|
|
25
|
+
export { StreamTree } from "./StreamTree";
|
|
26
|
+
export { SoftwareDevice };
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
export class CommandStream extends CS.default {}
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
export class CommandStreamJsonifier extends Jsonifier {}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
*/
|
|
40
|
+
export const device = {
|
|
41
|
+
software: createDevice,
|
|
42
|
+
apdu: createApduDevice,
|
|
43
|
+
};
|
package/src/tlv.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import BigEndian from "./BigEndian";
|
|
2
|
+
|
|
3
|
+
export enum TLVTypes {
|
|
4
|
+
Null = 0,
|
|
5
|
+
VarInt = 1,
|
|
6
|
+
Hash = 2,
|
|
7
|
+
Signature = 3,
|
|
8
|
+
String = 4,
|
|
9
|
+
Bytes = 5,
|
|
10
|
+
PublicKey = 6,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface TLVField {
|
|
14
|
+
type: number;
|
|
15
|
+
value: Uint8Array;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type TLVReaderParams = { tlv: TLVField; offset: number };
|
|
19
|
+
|
|
20
|
+
function pushTLV(a: Uint8Array, t: number, l: number, v: Uint8Array): Uint8Array {
|
|
21
|
+
const c = new Uint8Array(a.length + 2 + l);
|
|
22
|
+
c.set(a);
|
|
23
|
+
c.set(new Uint8Array([t, l]), a.length);
|
|
24
|
+
c.set(v, a.length + 2);
|
|
25
|
+
return c;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function push(a: Uint8Array, b: Uint8Array): Uint8Array {
|
|
29
|
+
const c = new Uint8Array(a.length + b.length);
|
|
30
|
+
c.set(a);
|
|
31
|
+
c.set(b, a.length);
|
|
32
|
+
return c;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Generic part of the TLV encoding/decoding
|
|
36
|
+
export const TLV = {
|
|
37
|
+
readTLV: function (buffer: Uint8Array, offset: number): { tlv: TLVField; offset: number } {
|
|
38
|
+
const type = buffer[offset];
|
|
39
|
+
offset += 1;
|
|
40
|
+
const length = buffer[offset];
|
|
41
|
+
offset += 1;
|
|
42
|
+
const value = buffer.slice(offset, offset + length);
|
|
43
|
+
offset += length;
|
|
44
|
+
return { tlv: { type, value }, offset };
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
readAllTLV: function (buffer: Uint8Array, offset: number): TLVField[] {
|
|
48
|
+
const result: TLVField[] = [];
|
|
49
|
+
|
|
50
|
+
while (offset < buffer.length) {
|
|
51
|
+
const tlv = TLV.readTLV(buffer, offset);
|
|
52
|
+
offset = tlv.offset;
|
|
53
|
+
result.push(tlv.tlv);
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
readVarInt: function (read: { tlv: TLVField; offset: number }): {
|
|
59
|
+
value: number;
|
|
60
|
+
offset: number;
|
|
61
|
+
} {
|
|
62
|
+
if (read.tlv.type != TLVTypes.VarInt) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`Invalid type for var int (at offset ${read.offset - 2 - read.tlv.value.length}))`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const fill = 4 - read.tlv.value.length;
|
|
68
|
+
const normalized = TLV.push(new Uint8Array([0, 0, 0, 0].slice(0, fill)), read.tlv.value);
|
|
69
|
+
const value = BigEndian.arrayToNumber(normalized);
|
|
70
|
+
return { value, offset: read.offset };
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
readBytes: function (read: { tlv: TLVField; offset: number }): {
|
|
74
|
+
value: Uint8Array;
|
|
75
|
+
offset: number;
|
|
76
|
+
} {
|
|
77
|
+
if (read.tlv.type != TLVTypes.Bytes) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`Invalid type for bytes (at offset ${read.offset - 2 - read.tlv.value.length}))`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
const value = read.tlv.value;
|
|
83
|
+
return { value, offset: read.offset };
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
readDerivationPath: function (read: { tlv: TLVField; offset: number }): {
|
|
87
|
+
value: number[];
|
|
88
|
+
offset: number;
|
|
89
|
+
} {
|
|
90
|
+
const bytes = TLV.readBytes(read);
|
|
91
|
+
const view = new DataView(bytes.value.buffer);
|
|
92
|
+
const value: number[] = [];
|
|
93
|
+
for (let offset = 0; offset < bytes.value.length; offset += 4) {
|
|
94
|
+
value.push(view.getUint32(offset, false));
|
|
95
|
+
}
|
|
96
|
+
return { value, offset: bytes.offset };
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
readString: function (read: { tlv: TLVField; offset: number }): {
|
|
100
|
+
value: string;
|
|
101
|
+
offset: number;
|
|
102
|
+
} {
|
|
103
|
+
if (read.tlv.type != TLVTypes.String) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Invalid type for string (at offset ${read.offset - 2 - read.tlv.value.length}))`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
const value = new TextDecoder().decode(read.tlv.value);
|
|
109
|
+
return { value, offset: read.offset };
|
|
110
|
+
},
|
|
111
|
+
|
|
112
|
+
readHash: function (read: { tlv: TLVField; offset: number }): {
|
|
113
|
+
value: Uint8Array;
|
|
114
|
+
offset: number;
|
|
115
|
+
} {
|
|
116
|
+
if (read.tlv.type != TLVTypes.Hash) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Invalid type for hash (at offset ${read.offset - 2 - read.tlv.value.length}))`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
const value = read.tlv.value;
|
|
122
|
+
return { value, offset: read.offset };
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
readSignature: function (read: { tlv: TLVField; offset: number }): {
|
|
126
|
+
value: Uint8Array;
|
|
127
|
+
offset: number;
|
|
128
|
+
} {
|
|
129
|
+
if (read.tlv.type != TLVTypes.Signature) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Invalid type for signature (at offset ${read.offset - 2 - read.tlv.value.length}))`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
const value = read.tlv.value;
|
|
135
|
+
return { value, offset: read.offset };
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
readPublicKey: function (read: { tlv: TLVField; offset: number }): {
|
|
139
|
+
value: Uint8Array;
|
|
140
|
+
offset: number;
|
|
141
|
+
} {
|
|
142
|
+
if (read.tlv.type != TLVTypes.PublicKey) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Invalid type for public key (at offset ${read.offset - 2 - read.tlv.value.length}))`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
const value = read.tlv.value;
|
|
148
|
+
return { value, offset: read.offset };
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
readNullOr: function <Type>(
|
|
152
|
+
read: { tlv: TLVField; offset: number },
|
|
153
|
+
func: (read: TLVReaderParams) => { value: Type; offset: number },
|
|
154
|
+
): { value: Type | null; offset: number } {
|
|
155
|
+
if (read.tlv.type == TLVTypes.Null) {
|
|
156
|
+
return { value: null, offset: read.offset };
|
|
157
|
+
}
|
|
158
|
+
return func(read);
|
|
159
|
+
},
|
|
160
|
+
|
|
161
|
+
push,
|
|
162
|
+
pushTLV,
|
|
163
|
+
|
|
164
|
+
pushString: function (a: Uint8Array, b: string): Uint8Array {
|
|
165
|
+
const encoded = new TextEncoder().encode(b);
|
|
166
|
+
return pushTLV(a, 0x04, encoded.length, encoded);
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
pushByte: function (a: Uint8Array, b: number): Uint8Array {
|
|
170
|
+
return pushTLV(a, 0x01, 1, new Uint8Array([b]));
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
pushInt16: function (a: Uint8Array, b: number): Uint8Array {
|
|
174
|
+
const bytes = BigEndian.shortToArray(b);
|
|
175
|
+
return pushTLV(a, 0x01, 2, bytes);
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
pushInt32: function (a: Uint8Array, b: number): Uint8Array {
|
|
179
|
+
const bytes = BigEndian.numberToArray(b);
|
|
180
|
+
return pushTLV(a, 0x01, 4, bytes);
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
pushHash: function (a: Uint8Array, b: Uint8Array): Uint8Array {
|
|
184
|
+
return pushTLV(a, 0x02, b.length, b);
|
|
185
|
+
},
|
|
186
|
+
|
|
187
|
+
pushSignature: function (a: Uint8Array, b: Uint8Array): Uint8Array {
|
|
188
|
+
return pushTLV(a, 0x03, b.length, b);
|
|
189
|
+
},
|
|
190
|
+
|
|
191
|
+
pushBytes: function (a: Uint8Array, b: Uint8Array): Uint8Array {
|
|
192
|
+
return pushTLV(a, 0x05, b.length, b);
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
pushNull: function (a: Uint8Array): Uint8Array {
|
|
196
|
+
return pushTLV(a, 0x00, 0, new Uint8Array(0));
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
pushPublicKey: function (a: Uint8Array, b: Uint8Array): Uint8Array {
|
|
200
|
+
return pushTLV(a, 0x06, b.length, b);
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
pushDerivationPath: function (a: Uint8Array, b: number[]): Uint8Array {
|
|
204
|
+
let bytes = new Uint8Array();
|
|
205
|
+
for (let i = 0; i < b.length; i++) {
|
|
206
|
+
bytes = push(bytes, BigEndian.numberToArray(b[i]));
|
|
207
|
+
}
|
|
208
|
+
return TLV.pushBytes(a, bytes);
|
|
209
|
+
},
|
|
210
|
+
};
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"declaration": true,
|
|
5
|
+
"declarationMap": true,
|
|
6
|
+
"noImplicitAny": false,
|
|
7
|
+
"noImplicitThis": false,
|
|
8
|
+
"downlevelIteration": true,
|
|
9
|
+
"module": "commonjs",
|
|
10
|
+
"lib": ["es2020", "dom"],
|
|
11
|
+
"outDir": "lib"
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"]
|
|
14
|
+
}
|