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