@ledgerhq/ledger-key-ring-protocol 0.5.3 → 0.5.4-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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +10 -0
- package/lib/HWDeviceProvider.js +49 -51
- package/lib/HWDeviceProvider.js.map +1 -1
- package/lib/api.js +79 -108
- package/lib/api.js.map +1 -1
- package/lib/auth.js +29 -40
- package/lib/auth.js.map +1 -1
- package/lib/mockSdk.js +95 -115
- package/lib/mockSdk.js.map +1 -1
- package/lib/qrcode/index.js +155 -168
- package/lib/qrcode/index.js.map +1 -1
- package/lib/qrcode/index.test.js +13 -22
- package/lib/qrcode/index.test.js.map +1 -1
- package/lib/sdk.js +231 -270
- package/lib/sdk.js.map +1 -1
- package/lib/store.js +3 -3
- package/lib/store.js.map +1 -1
- package/lib-es/HWDeviceProvider.js +49 -51
- package/lib-es/HWDeviceProvider.js.map +1 -1
- package/lib-es/api.js +79 -108
- package/lib-es/api.js.map +1 -1
- package/lib-es/auth.js +29 -40
- package/lib-es/auth.js.map +1 -1
- package/lib-es/mockSdk.js +95 -115
- package/lib-es/mockSdk.js.map +1 -1
- package/lib-es/qrcode/index.js +155 -168
- package/lib-es/qrcode/index.js.map +1 -1
- package/lib-es/qrcode/index.test.js +13 -22
- package/lib-es/qrcode/index.test.js.map +1 -1
- package/lib-es/sdk.js +231 -270
- package/lib-es/sdk.js.map +1 -1
- package/lib-es/store.js +3 -3
- package/lib-es/store.js.map +1 -1
- package/package.json +7 -7
- package/tsconfig.json +0 -1
package/lib/sdk.js
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
4
|
};
|
|
@@ -21,14 +12,18 @@ const errors_1 = require("@ledgerhq/errors");
|
|
|
21
12
|
const errors_2 = require("./errors");
|
|
22
13
|
const auth_1 = require("./auth");
|
|
23
14
|
class SDK {
|
|
15
|
+
context;
|
|
16
|
+
hwDeviceProvider;
|
|
17
|
+
lifecycle;
|
|
18
|
+
api;
|
|
24
19
|
constructor(context, hwDeviceProvider, lifecyle) {
|
|
25
|
-
this.jwt = undefined;
|
|
26
|
-
this.jwtHash = "";
|
|
27
20
|
this.context = context;
|
|
28
21
|
this.hwDeviceProvider = hwDeviceProvider;
|
|
29
22
|
this.lifecycle = lifecyle;
|
|
30
23
|
this.api = (0, api_1.default)(context.apiBaseUrl);
|
|
31
24
|
}
|
|
25
|
+
jwt = undefined;
|
|
26
|
+
jwtHash = "";
|
|
32
27
|
withAuth(trustchain, memberCredentials, job, policy, ignorePermissionsChecks) {
|
|
33
28
|
const hash = trustchain.rootId + " " + memberCredentials.pubkey;
|
|
34
29
|
if (this.jwtHash !== hash) {
|
|
@@ -50,267 +45,237 @@ class SDK {
|
|
|
50
45
|
return job(jwt);
|
|
51
46
|
}, this.jwt, () => this.auth(trustchain, memberCredentials), jwt => this.api.refreshAuth(jwt), policy);
|
|
52
47
|
}
|
|
53
|
-
initMemberCredentials() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return convertKeyPairToLiveCredentials(kp);
|
|
57
|
-
});
|
|
48
|
+
async initMemberCredentials() {
|
|
49
|
+
const kp = hw_ledger_key_ring_protocol_1.crypto.randomKeypair();
|
|
50
|
+
return convertKeyPairToLiveCredentials(kp);
|
|
58
51
|
}
|
|
59
|
-
getOrCreateTrustchain(deviceId, memberCredentials, callbacks, topic, currentTrustchain) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (path === trustchainRootPath) {
|
|
94
|
-
trustchainRootId = trustchainId;
|
|
95
|
-
}
|
|
52
|
+
async getOrCreateTrustchain(deviceId, memberCredentials, callbacks, topic, currentTrustchain) {
|
|
53
|
+
this.invalidateJwt();
|
|
54
|
+
let type = types_1.TrustchainResultType.restored;
|
|
55
|
+
const withJwt = job => this.hwDeviceProvider.withJwt(deviceId, job, "cache", callbacks);
|
|
56
|
+
const withHw = job => this.hwDeviceProvider.withHw(deviceId, job, callbacks);
|
|
57
|
+
let trustchains = await withJwt(this.api.getTrustchains);
|
|
58
|
+
callbacks?.onInitialResponse?.(trustchains);
|
|
59
|
+
if (currentTrustchain) {
|
|
60
|
+
await this.restoreTrustchain(currentTrustchain, memberCredentials).then(() => {
|
|
61
|
+
throw Object.keys(trustchains).includes(currentTrustchain.rootId)
|
|
62
|
+
? new errors_2.TrustchainAlreadyInitialized()
|
|
63
|
+
: new errors_2.TrustchainAlreadyInitializedWithOtherSeed();
|
|
64
|
+
}, () => {
|
|
65
|
+
// The user was ejected from the trustchain therefore we can continue
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (Object.keys(trustchains).length === 0) {
|
|
69
|
+
(0, logs_1.log)("trustchain", "getOrCreateTrustchain: no trustchain yet, let's create one");
|
|
70
|
+
type = types_1.TrustchainResultType.created;
|
|
71
|
+
const streamTree = await this.hwDeviceProvider.withHw(deviceId, hw => hw_ledger_key_ring_protocol_1.StreamTree.createNewTree(hw, { topic }));
|
|
72
|
+
await streamTree.getRoot().resolve(); // double checks the signatures are correct before sending to the backend
|
|
73
|
+
const commandStream = hw_ledger_key_ring_protocol_1.CommandStreamEncoder.encode(streamTree.getRoot().blocks);
|
|
74
|
+
await withJwt(jwt => this.api.postSeed(jwt, hw_ledger_key_ring_protocol_1.crypto.to_hex(commandStream)));
|
|
75
|
+
// deviceJwt have changed, proactively refresh it
|
|
76
|
+
await this.hwDeviceProvider.refreshJwt(deviceId, callbacks);
|
|
77
|
+
trustchains = await withJwt(this.api.getTrustchains);
|
|
78
|
+
}
|
|
79
|
+
// we find our trustchain root id
|
|
80
|
+
let trustchainRootId;
|
|
81
|
+
const trustchainRootPath = "m/";
|
|
82
|
+
for (const [trustchainId, info] of Object.entries(trustchains)) {
|
|
83
|
+
for (const path in info) {
|
|
84
|
+
if (path === trustchainRootPath) {
|
|
85
|
+
trustchainRootId = trustchainId;
|
|
96
86
|
}
|
|
97
87
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
name: this.context.name,
|
|
116
|
-
permissions: hw_ledger_key_ring_protocol_1.Permissions.OWNER,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
const walletSyncEncryptionKey = yield extractEncryptionKey(streamTree, path, memberCredentials);
|
|
120
|
-
const trustchain = {
|
|
121
|
-
rootId: trustchainRootId,
|
|
122
|
-
walletSyncEncryptionKey,
|
|
123
|
-
applicationPath: path,
|
|
124
|
-
};
|
|
125
|
-
return { type, trustchain };
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
restoreTrustchain(trustchain, memberCredentials) {
|
|
129
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
130
|
-
const { streamTree, applicationRootPath } = yield this.withAuth(trustchain, memberCredentials, jwt => this.fetchTrustchainAndResolve(jwt, trustchain.rootId, this.context.applicationId), "refresh", true);
|
|
131
|
-
const walletSyncEncryptionKey = yield extractEncryptionKey(streamTree, applicationRootPath, memberCredentials);
|
|
132
|
-
return {
|
|
133
|
-
rootId: trustchain.rootId,
|
|
134
|
-
walletSyncEncryptionKey,
|
|
135
|
-
applicationPath: applicationRootPath,
|
|
136
|
-
};
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
|
-
getMembers(trustchain, memberCredentials) {
|
|
140
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
141
|
-
const { resolved } = yield this.withAuth(trustchain, memberCredentials, jwt => this.fetchTrustchainAndResolve(jwt, trustchain.rootId, this.context.applicationId));
|
|
142
|
-
const members = resolved.getMembersData();
|
|
143
|
-
if (!members.some(m => m.id === memberCredentials.pubkey)) {
|
|
144
|
-
throw new errors_2.TrustchainEjected("Not a member of trustchain");
|
|
145
|
-
}
|
|
146
|
-
return members;
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
removeMember(deviceId, trustchain, memberCredentials, member, callbacks) {
|
|
150
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
-
var _a;
|
|
152
|
-
this.invalidateJwt();
|
|
153
|
-
const withJwt = job => this.hwDeviceProvider.withJwt(deviceId, job, "cache", callbacks);
|
|
154
|
-
const withHw = job => this.hwDeviceProvider.withHw(deviceId, job, callbacks);
|
|
155
|
-
// invariant because the sdk does not support this case, and the UI should not allows it.
|
|
156
|
-
invariant(memberCredentials.pubkey !== member.id, "removeMember must not be used to remove the current member.");
|
|
157
|
-
const afterRotation = yield ((_a = this.lifecycle) === null || _a === void 0 ? void 0 : _a.onTrustchainRotation(this, trustchain, memberCredentials));
|
|
158
|
-
const applicationId = this.context.applicationId;
|
|
159
|
-
const trustchainId = trustchain.rootId;
|
|
160
|
-
// eslint-disable-next-line prefer-const
|
|
161
|
-
let { resolved, streamTree, applicationRootPath } = yield withJwt(jwt => this.fetchTrustchainAndResolve(jwt, trustchainId, applicationId));
|
|
162
|
-
const members = resolved.getMembersData();
|
|
163
|
-
const withoutMember = members.filter(m => m.id !== member.id);
|
|
164
|
-
invariant(withoutMember.length < members.length, "member not found"); // invariant because the UI should not allow this case.
|
|
165
|
-
const withoutMemberOrMe = withoutMember.filter(m => m.id !== memberCredentials.pubkey);
|
|
166
|
-
const softwareDevice = getSoftwareDevice(memberCredentials);
|
|
167
|
-
const newPath = streamTree.getApplicationRootPath(applicationId, 1);
|
|
168
|
-
// We close the current trustchain with the hardware wallet in order to get a user confirmation of the action
|
|
169
|
-
const sendCloseStreamToAPI = yield this.closeStream(streamTree, applicationRootPath, trustchainId, withJwt, withHw);
|
|
170
|
-
// derive a new branch of the tree on the new path
|
|
171
|
-
streamTree = yield this.pushMember(streamTree, newPath, trustchainId, withJwt, withHw, {
|
|
88
|
+
}
|
|
89
|
+
invariant(trustchainRootId, "trustchainRootId should be defined");
|
|
90
|
+
(0, logs_1.log)("trustchain", "getOrCreateTrustchain rootId=" + trustchainRootId);
|
|
91
|
+
// make a stream tree from all the trustchains associated to this root id
|
|
92
|
+
let { streamTree } = await withJwt(jwt => this.fetchTrustchain(jwt, trustchainRootId));
|
|
93
|
+
const path = streamTree.getApplicationRootPath(this.context.applicationId);
|
|
94
|
+
const child = streamTree.getChild(path);
|
|
95
|
+
let shouldShare = true;
|
|
96
|
+
if (child) {
|
|
97
|
+
const resolved = await child.resolve();
|
|
98
|
+
const members = resolved.getMembers();
|
|
99
|
+
shouldShare = !members.some(m => hw_ledger_key_ring_protocol_1.crypto.to_hex(m) === memberCredentials.pubkey); // not already a member
|
|
100
|
+
}
|
|
101
|
+
if (shouldShare) {
|
|
102
|
+
if (type === types_1.TrustchainResultType.restored)
|
|
103
|
+
type = types_1.TrustchainResultType.updated;
|
|
104
|
+
streamTree = await this.pushMember(streamTree, path, trustchainRootId, withJwt, withHw, {
|
|
172
105
|
id: memberCredentials.pubkey,
|
|
173
106
|
name: this.context.name,
|
|
174
107
|
permissions: hw_ledger_key_ring_protocol_1.Permissions.OWNER,
|
|
175
108
|
});
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
109
|
+
}
|
|
110
|
+
const walletSyncEncryptionKey = await extractEncryptionKey(streamTree, path, memberCredentials);
|
|
111
|
+
const trustchain = {
|
|
112
|
+
rootId: trustchainRootId,
|
|
113
|
+
walletSyncEncryptionKey,
|
|
114
|
+
applicationPath: path,
|
|
115
|
+
};
|
|
116
|
+
return { type, trustchain };
|
|
117
|
+
}
|
|
118
|
+
async restoreTrustchain(trustchain, memberCredentials) {
|
|
119
|
+
const { streamTree, applicationRootPath } = await this.withAuth(trustchain, memberCredentials, jwt => this.fetchTrustchainAndResolve(jwt, trustchain.rootId, this.context.applicationId), "refresh", true);
|
|
120
|
+
const walletSyncEncryptionKey = await extractEncryptionKey(streamTree, applicationRootPath, memberCredentials);
|
|
121
|
+
return {
|
|
122
|
+
rootId: trustchain.rootId,
|
|
123
|
+
walletSyncEncryptionKey,
|
|
124
|
+
applicationPath: applicationRootPath,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async getMembers(trustchain, memberCredentials) {
|
|
128
|
+
const { resolved } = await this.withAuth(trustchain, memberCredentials, jwt => this.fetchTrustchainAndResolve(jwt, trustchain.rootId, this.context.applicationId));
|
|
129
|
+
const members = resolved.getMembersData();
|
|
130
|
+
if (!members.some(m => m.id === memberCredentials.pubkey)) {
|
|
131
|
+
throw new errors_2.TrustchainEjected("Not a member of trustchain");
|
|
132
|
+
}
|
|
133
|
+
return members;
|
|
197
134
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
135
|
+
async removeMember(deviceId, trustchain, memberCredentials, member, callbacks) {
|
|
136
|
+
this.invalidateJwt();
|
|
137
|
+
const withJwt = job => this.hwDeviceProvider.withJwt(deviceId, job, "cache", callbacks);
|
|
138
|
+
const withHw = job => this.hwDeviceProvider.withHw(deviceId, job, callbacks);
|
|
139
|
+
// invariant because the sdk does not support this case, and the UI should not allows it.
|
|
140
|
+
invariant(memberCredentials.pubkey !== member.id, "removeMember must not be used to remove the current member.");
|
|
141
|
+
const afterRotation = await this.lifecycle?.onTrustchainRotation(this, trustchain, memberCredentials);
|
|
142
|
+
const applicationId = this.context.applicationId;
|
|
143
|
+
const trustchainId = trustchain.rootId;
|
|
144
|
+
// eslint-disable-next-line prefer-const
|
|
145
|
+
let { resolved, streamTree, applicationRootPath } = await withJwt(jwt => this.fetchTrustchainAndResolve(jwt, trustchainId, applicationId));
|
|
146
|
+
const members = resolved.getMembersData();
|
|
147
|
+
const withoutMember = members.filter(m => m.id !== member.id);
|
|
148
|
+
invariant(withoutMember.length < members.length, "member not found"); // invariant because the UI should not allow this case.
|
|
149
|
+
const withoutMemberOrMe = withoutMember.filter(m => m.id !== memberCredentials.pubkey);
|
|
150
|
+
const softwareDevice = getSoftwareDevice(memberCredentials);
|
|
151
|
+
const newPath = streamTree.getApplicationRootPath(applicationId, 1);
|
|
152
|
+
// We close the current trustchain with the hardware wallet in order to get a user confirmation of the action
|
|
153
|
+
const sendCloseStreamToAPI = await this.closeStream(streamTree, applicationRootPath, trustchainId, withJwt, withHw);
|
|
154
|
+
// derive a new branch of the tree on the new path
|
|
155
|
+
streamTree = await this.pushMember(streamTree, newPath, trustchainId, withJwt, withHw, {
|
|
156
|
+
id: memberCredentials.pubkey,
|
|
157
|
+
name: this.context.name,
|
|
158
|
+
permissions: hw_ledger_key_ring_protocol_1.Permissions.OWNER,
|
|
205
159
|
});
|
|
160
|
+
// add the remaining members
|
|
161
|
+
const withSw = (job) => job(softwareDevice);
|
|
162
|
+
for (const m of withoutMemberOrMe) {
|
|
163
|
+
streamTree = await this.pushMember(streamTree, newPath, trustchainId, withJwt, withSw, m);
|
|
164
|
+
}
|
|
165
|
+
const walletSyncEncryptionKey = await extractEncryptionKey(streamTree, newPath, memberCredentials);
|
|
166
|
+
// we send the close stream to the API only after the new stream is created in case user cancelled the process in the middle.
|
|
167
|
+
await sendCloseStreamToAPI();
|
|
168
|
+
// deviceJwt have changed, proactively refresh it
|
|
169
|
+
await this.hwDeviceProvider.refreshJwt(deviceId, callbacks);
|
|
170
|
+
const newTrustchain = {
|
|
171
|
+
rootId: trustchainId,
|
|
172
|
+
walletSyncEncryptionKey,
|
|
173
|
+
applicationPath: newPath,
|
|
174
|
+
};
|
|
175
|
+
if (afterRotation)
|
|
176
|
+
await afterRotation(newTrustchain);
|
|
177
|
+
// refresh the jwt with the new trustchain
|
|
178
|
+
this.jwt = await this.withAuth(newTrustchain, memberCredentials, jwt => Promise.resolve(jwt), "refresh");
|
|
179
|
+
return newTrustchain;
|
|
180
|
+
}
|
|
181
|
+
async addMember(trustchain, memberCredentials, member) {
|
|
182
|
+
const withJwt = f => this.withAuth(trustchain, memberCredentials, f);
|
|
183
|
+
const { streamTree, applicationRootPath } = await withJwt(jwt => this.fetchTrustchainAndResolve(jwt, trustchain.rootId, this.context.applicationId));
|
|
184
|
+
const softwareDevice = getSoftwareDevice(memberCredentials);
|
|
185
|
+
const withSw = (job) => job(softwareDevice);
|
|
186
|
+
await this.pushMember(streamTree, applicationRootPath, trustchain.rootId, withJwt, withSw, member);
|
|
206
187
|
}
|
|
207
188
|
invalidateJwt() {
|
|
208
189
|
this.jwt = undefined;
|
|
209
190
|
this.hwDeviceProvider.clearJwt();
|
|
210
191
|
}
|
|
211
|
-
destroyTrustchain(trustchain, memberCredentials) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
this.invalidateJwt();
|
|
215
|
-
});
|
|
192
|
+
async destroyTrustchain(trustchain, memberCredentials) {
|
|
193
|
+
await this.withAuth(trustchain, memberCredentials, jwt => this.api.deleteTrustchain(jwt, trustchain.rootId));
|
|
194
|
+
this.invalidateJwt();
|
|
216
195
|
}
|
|
217
|
-
encryptUserData(trustchain, input) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return encrypted;
|
|
222
|
-
});
|
|
196
|
+
async encryptUserData(trustchain, input) {
|
|
197
|
+
const key = hw_ledger_key_ring_protocol_1.crypto.from_hex(trustchain.walletSyncEncryptionKey);
|
|
198
|
+
const encrypted = await hw_ledger_key_ring_protocol_1.crypto.encryptUserData(key, input);
|
|
199
|
+
return encrypted;
|
|
223
200
|
}
|
|
224
|
-
decryptUserData(trustchain, data) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
return decrypted;
|
|
229
|
-
});
|
|
201
|
+
async decryptUserData(trustchain, data) {
|
|
202
|
+
const key = hw_ledger_key_ring_protocol_1.crypto.from_hex(trustchain.walletSyncEncryptionKey);
|
|
203
|
+
const decrypted = await hw_ledger_key_ring_protocol_1.crypto.decryptUserData(key, data);
|
|
204
|
+
return decrypted;
|
|
230
205
|
}
|
|
231
|
-
fetchTrustchain(jwt, trustchainId) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return { streamTree };
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
fetchTrustchainAndResolve(jwt, trustchainId, applicationId) {
|
|
239
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
240
|
-
const { streamTree } = yield this.fetchTrustchain(jwt, trustchainId);
|
|
241
|
-
const applicationRootPath = streamTree.getApplicationRootPath(applicationId);
|
|
242
|
-
const applicationNode = streamTree.getChild(applicationRootPath);
|
|
243
|
-
invariant(applicationNode, "could not find the application stream.");
|
|
244
|
-
const resolved = yield applicationNode.resolve();
|
|
245
|
-
return { resolved, streamTree, applicationRootPath, applicationNode };
|
|
246
|
-
});
|
|
206
|
+
async fetchTrustchain(jwt, trustchainId) {
|
|
207
|
+
const trustchainData = await this.api.getTrustchain(jwt, trustchainId);
|
|
208
|
+
const streamTree = hw_ledger_key_ring_protocol_1.StreamTree.deserialize(trustchainData);
|
|
209
|
+
return { streamTree };
|
|
247
210
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const response = yield this.api
|
|
256
|
-
.postChallengeResponse({
|
|
257
|
-
challenge: challenge.json,
|
|
258
|
-
signature: {
|
|
259
|
-
credential: credentialForPubKey(memberCredentials.pubkey),
|
|
260
|
-
signature: hw_ledger_key_ring_protocol_1.crypto.to_hex(yield hw_ledger_key_ring_protocol_1.crypto.sign(hash, keypair)),
|
|
261
|
-
attestation: hw_ledger_key_ring_protocol_1.crypto.to_hex(liveAuthentication(trustchain.rootId)),
|
|
262
|
-
},
|
|
263
|
-
})
|
|
264
|
-
.catch(e => {
|
|
265
|
-
if (e instanceof errors_1.LedgerAPI4xx &&
|
|
266
|
-
(e.message.includes("Not a member of trustchain") ||
|
|
267
|
-
e.message.includes("You are not member"))) {
|
|
268
|
-
throw new errors_2.TrustchainEjected(e.message);
|
|
269
|
-
}
|
|
270
|
-
throw e;
|
|
271
|
-
});
|
|
272
|
-
return response;
|
|
273
|
-
});
|
|
211
|
+
async fetchTrustchainAndResolve(jwt, trustchainId, applicationId) {
|
|
212
|
+
const { streamTree } = await this.fetchTrustchain(jwt, trustchainId);
|
|
213
|
+
const applicationRootPath = streamTree.getApplicationRootPath(applicationId);
|
|
214
|
+
const applicationNode = streamTree.getChild(applicationRootPath);
|
|
215
|
+
invariant(applicationNode, "could not find the application stream.");
|
|
216
|
+
const resolved = await applicationNode.resolve();
|
|
217
|
+
return { resolved, streamTree, applicationRootPath, applicationNode };
|
|
274
218
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
};
|
|
296
|
-
yield withJwt(jwt => this.api.putCommands(jwt, trustchainId, request));
|
|
219
|
+
async auth(trustchain, memberCredentials) {
|
|
220
|
+
const challenge = await this.api.getAuthenticationChallenge();
|
|
221
|
+
const data = hw_ledger_key_ring_protocol_1.crypto.from_hex(challenge.tlv);
|
|
222
|
+
const [parsed, _] = hw_ledger_key_ring_protocol_1.Challenge.fromBytes(data);
|
|
223
|
+
const hash = await hw_ledger_key_ring_protocol_1.crypto.hash(parsed.getUnsignedTLV());
|
|
224
|
+
const keypair = convertLiveCredentialsToKeyPair(memberCredentials);
|
|
225
|
+
const response = await this.api
|
|
226
|
+
.postChallengeResponse({
|
|
227
|
+
challenge: challenge.json,
|
|
228
|
+
signature: {
|
|
229
|
+
credential: credentialForPubKey(memberCredentials.pubkey),
|
|
230
|
+
signature: hw_ledger_key_ring_protocol_1.crypto.to_hex(await hw_ledger_key_ring_protocol_1.crypto.sign(hash, keypair)),
|
|
231
|
+
attestation: hw_ledger_key_ring_protocol_1.crypto.to_hex(liveAuthentication(trustchain.rootId)),
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
.catch(e => {
|
|
235
|
+
if (e instanceof errors_1.LedgerAPI4xx &&
|
|
236
|
+
(e.message.includes("Not a member of trustchain") ||
|
|
237
|
+
e.message.includes("You are not member"))) {
|
|
238
|
+
throw new errors_2.TrustchainEjected(e.message);
|
|
297
239
|
}
|
|
298
|
-
|
|
240
|
+
throw e;
|
|
299
241
|
});
|
|
242
|
+
return response;
|
|
300
243
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
244
|
+
async pushMember(streamTree, path, trustchainId, withJwt, withDevice, member) {
|
|
245
|
+
const isMemberAlreadyInStreamTree = await isMemberInStreamTree(streamTree, path, member);
|
|
246
|
+
if (isMemberAlreadyInStreamTree) {
|
|
247
|
+
return streamTree;
|
|
248
|
+
}
|
|
249
|
+
const isNewDerivation = !streamTree.getChild(path);
|
|
250
|
+
streamTree = await withDevice(device => streamTree.share(path, device, hw_ledger_key_ring_protocol_1.crypto.from_hex(member.id), member.name, member.permissions));
|
|
251
|
+
const child = streamTree.getChild(path);
|
|
252
|
+
invariant(child, "StreamTree.share failed to create the child stream.");
|
|
253
|
+
await child.resolve(); // double checks the signatures are correct before sending to the backend
|
|
254
|
+
if (isNewDerivation) {
|
|
255
|
+
const commandStream = hw_ledger_key_ring_protocol_1.CommandStreamEncoder.encode(child.blocks);
|
|
256
|
+
await withJwt(jwt => this.api.postDerivation(jwt, trustchainId, hw_ledger_key_ring_protocol_1.crypto.to_hex(commandStream)));
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
307
259
|
const commandStream = hw_ledger_key_ring_protocol_1.CommandStreamEncoder.encode([child.blocks[child.blocks.length - 1]]);
|
|
308
260
|
const request = {
|
|
309
261
|
path,
|
|
310
262
|
blocks: [hw_ledger_key_ring_protocol_1.crypto.to_hex(commandStream)],
|
|
311
263
|
};
|
|
312
|
-
|
|
313
|
-
}
|
|
264
|
+
await withJwt(jwt => this.api.putCommands(jwt, trustchainId, request));
|
|
265
|
+
}
|
|
266
|
+
return streamTree;
|
|
267
|
+
}
|
|
268
|
+
async closeStream(streamTree, path, trustchainId, withJwt, withDevice) {
|
|
269
|
+
streamTree = await withDevice(device => streamTree.close(path, device));
|
|
270
|
+
const child = streamTree.getChild(path);
|
|
271
|
+
invariant(child, "StreamTree.close failed to create the child stream.");
|
|
272
|
+
await child.resolve(); // double checks the signatures are correct before sending to the backend
|
|
273
|
+
const commandStream = hw_ledger_key_ring_protocol_1.CommandStreamEncoder.encode([child.blocks[child.blocks.length - 1]]);
|
|
274
|
+
const request = {
|
|
275
|
+
path,
|
|
276
|
+
blocks: [hw_ledger_key_ring_protocol_1.crypto.to_hex(commandStream)],
|
|
277
|
+
};
|
|
278
|
+
return () => withJwt(jwt => this.api.putCommands(jwt, trustchainId, request));
|
|
314
279
|
}
|
|
315
280
|
}
|
|
316
281
|
exports.SDK = SDK;
|
|
@@ -332,22 +297,20 @@ function getSoftwareDevice(memberCredentials) {
|
|
|
332
297
|
const kp = convertLiveCredentialsToKeyPair(memberCredentials);
|
|
333
298
|
return new hw_ledger_key_ring_protocol_1.SoftwareDevice(kp);
|
|
334
299
|
}
|
|
335
|
-
function extractEncryptionKey(streamTree, path, memberCredentials) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
throw new errors_2.TrustchainEjected(e.message);
|
|
347
|
-
}
|
|
348
|
-
throw e;
|
|
300
|
+
async function extractEncryptionKey(streamTree, path, memberCredentials) {
|
|
301
|
+
const softwareDevice = getSoftwareDevice(memberCredentials);
|
|
302
|
+
const pathNumbers = hw_ledger_key_ring_protocol_1.DerivationPath.toIndexArray(path);
|
|
303
|
+
try {
|
|
304
|
+
const key = await softwareDevice.readKey(streamTree, pathNumbers);
|
|
305
|
+
// private key is in the first 32 bytes
|
|
306
|
+
return hw_ledger_key_ring_protocol_1.crypto.to_hex(key.slice(0, 32));
|
|
307
|
+
}
|
|
308
|
+
catch (e) {
|
|
309
|
+
if (e instanceof Error) {
|
|
310
|
+
throw new errors_2.TrustchainEjected(e.message);
|
|
349
311
|
}
|
|
350
|
-
|
|
312
|
+
throw e;
|
|
313
|
+
}
|
|
351
314
|
}
|
|
352
315
|
// spec https://ledgerhq.atlassian.net/wiki/spaces/TA/pages/4335960138/ARCH+LedgerLive+Auth+specifications
|
|
353
316
|
function liveAuthentication(rootId) {
|
|
@@ -366,15 +329,13 @@ function invariant(condition, message) {
|
|
|
366
329
|
throw new Error(message);
|
|
367
330
|
}
|
|
368
331
|
}
|
|
369
|
-
function isMemberInStreamTree(streamTree, path, member) {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
return members.some(m => m.id === member.id);
|
|
378
|
-
});
|
|
332
|
+
async function isMemberInStreamTree(streamTree, path, member) {
|
|
333
|
+
const child = streamTree.getChild(path);
|
|
334
|
+
if (!child) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
const resolved = await child.resolve();
|
|
338
|
+
const members = resolved.getMembersData();
|
|
339
|
+
return members.some(m => m.id === member.id);
|
|
379
340
|
}
|
|
380
341
|
//# sourceMappingURL=sdk.js.map
|