@tdxvolt/volt-client-grpc 0.18.0 → 0.18.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/lib/index.cjs +257 -107
- package/package.json +2 -2
- package/src/constants.js +1 -1
- package/src/grpc-call.js +105 -54
- package/src/grpc-utils.js +3 -3
- package/src/rpc-invocation.js +47 -11
- package/src/volt-client-internal.js +36 -21
- package/src/volt-client.js +43 -13
- package/src/volt-connection.js +16 -2
- package/src/volt-credential.js +6 -2
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.18.
|
|
6
|
+
"version": "0.18.2",
|
|
7
7
|
"description": "tdx Volt library for nodejs clients",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"license": "ISC",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@grpc/proto-loader": "^0.6.4",
|
|
40
|
-
"@tdxvolt/volt-client-web": "^0.18.
|
|
40
|
+
"@tdxvolt/volt-client-web": "^0.18.1",
|
|
41
41
|
"bent": "^7.3.12",
|
|
42
42
|
"builtin-modules": "^3.3.0",
|
|
43
43
|
"debug": "^4.1.1",
|
package/src/constants.js
CHANGED
package/src/grpc-call.js
CHANGED
|
@@ -15,6 +15,8 @@ const {
|
|
|
15
15
|
x25519
|
|
16
16
|
} = voltClientUtils;
|
|
17
17
|
|
|
18
|
+
const invokeMethod = "Invoke";
|
|
19
|
+
|
|
18
20
|
const log = debug("volt-client-grpc:grpc-call");
|
|
19
21
|
|
|
20
22
|
function _prepareInvokeRequest(
|
|
@@ -26,10 +28,10 @@ function _prepareInvokeRequest(
|
|
|
26
28
|
this._service?.service_description.host_type ===
|
|
27
29
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
28
30
|
|
|
29
|
-
const relaying = this._voltClient.
|
|
31
|
+
const relaying = this._voltClient.isRelayed || isServiceRelayed;
|
|
30
32
|
|
|
31
33
|
let targetDID = [];
|
|
32
|
-
if (this._voltClient.
|
|
34
|
+
if (this._voltClient.isRelayed) {
|
|
33
35
|
targetDID.push(this._voltClient.voltConfig.id);
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -54,20 +56,23 @@ function _prepareInvokeRequest(
|
|
|
54
56
|
let invokeRequest;
|
|
55
57
|
let callMethod = method;
|
|
56
58
|
|
|
59
|
+
const voltGrpcClient = this._voltClient.getVoltAPIClient();
|
|
60
|
+
|
|
57
61
|
if (relaying) {
|
|
58
62
|
//
|
|
59
63
|
// If the volt connection is via a relay (or the target service is relayed),
|
|
60
64
|
// we wrap the rpc in a RemoteRequest message and send it via a call to Invoke().
|
|
61
65
|
//
|
|
66
|
+
|
|
62
67
|
// Replace the target method with a call to Invoke on the relay Volt.
|
|
63
|
-
|
|
64
|
-
callMethod = "Invoke";
|
|
68
|
+
callMethod = invokeMethod;
|
|
65
69
|
|
|
66
70
|
// But first we need to perform a key exchange with the target service.
|
|
67
|
-
//
|
|
68
71
|
if (!this._encryptionKey) {
|
|
69
72
|
// We haven't exchanged keys yet, so queue the payload.
|
|
70
|
-
|
|
73
|
+
if (request) {
|
|
74
|
+
this._pendingRequests.push(request);
|
|
75
|
+
}
|
|
71
76
|
|
|
72
77
|
if (this._keyExchangePending) {
|
|
73
78
|
// We've already sent the key exchange request, so do nothing.
|
|
@@ -86,6 +91,13 @@ function _prepareInvokeRequest(
|
|
|
86
91
|
: undefined
|
|
87
92
|
};
|
|
88
93
|
|
|
94
|
+
if (this._voltClient.isRelayed) {
|
|
95
|
+
// When tunnelling, we serialise the request using the original serialisation
|
|
96
|
+
// method for Invoke.
|
|
97
|
+
invokeRequest =
|
|
98
|
+
voltGrpcClient.Invoke.requestSerializeOriginal(invokeRequest);
|
|
99
|
+
}
|
|
100
|
+
|
|
89
101
|
return {
|
|
90
102
|
request: invokeRequest,
|
|
91
103
|
method: callMethod,
|
|
@@ -96,41 +108,45 @@ function _prepareInvokeRequest(
|
|
|
96
108
|
|
|
97
109
|
let requestPayload;
|
|
98
110
|
|
|
99
|
-
// Serialise the rpc request.
|
|
111
|
+
// Serialise the rpc request using the original serialisation method.
|
|
100
112
|
if (request) {
|
|
101
|
-
|
|
113
|
+
if (this._voltClient.isRelayed) {
|
|
114
|
+
requestPayload =
|
|
115
|
+
this._grpcClient[method].requestSerializeOriginal(request);
|
|
116
|
+
} else {
|
|
117
|
+
requestPayload = this._grpcClient[method].requestSerialize(request);
|
|
118
|
+
}
|
|
102
119
|
} else if (request !== null) {
|
|
103
120
|
log("****************LOOKOUT****************** - empty request");
|
|
104
121
|
} else {
|
|
105
122
|
log("explicit empty request payload");
|
|
106
123
|
}
|
|
107
124
|
|
|
108
|
-
//
|
|
109
|
-
const
|
|
110
|
-
const tunnelMethod = voltGrpcClient.Tunnel;
|
|
111
|
-
|
|
112
|
-
const tunnelResp = {
|
|
125
|
+
// Create a tunnel message containing the method call metadata and request payload.
|
|
126
|
+
const tunnelResp = voltGrpcClient.Tunnel.responseSerialize({
|
|
113
127
|
method_invoke: {
|
|
114
128
|
method_name: this._grpcClient[method].path,
|
|
115
129
|
method_type: methodType,
|
|
116
130
|
request: requestPayload
|
|
117
131
|
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
let invokePayload;
|
|
121
|
-
const iv = aesCreateKey().iv;
|
|
132
|
+
});
|
|
122
133
|
|
|
123
134
|
// When sending via a relay we need to encrypt the payload.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
iv,
|
|
127
|
-
tunnelMethod.responseSerialize(tunnelResp)
|
|
128
|
-
);
|
|
135
|
+
const iv = aesCreateKey().iv;
|
|
136
|
+
const invokePayload = aesEncrypt(this._encryptionKey, iv, tunnelResp);
|
|
129
137
|
|
|
138
|
+
// Construct the Invoke request.
|
|
130
139
|
invokeRequest = {
|
|
131
140
|
iv,
|
|
132
141
|
payload: invokePayload
|
|
133
142
|
};
|
|
143
|
+
|
|
144
|
+
if (this._voltClient.isRelayed) {
|
|
145
|
+
// When tunnelling, we serialise the request using the original serialisation
|
|
146
|
+
// method for Invoke.
|
|
147
|
+
invokeRequest =
|
|
148
|
+
voltGrpcClient.Invoke.requestSerializeOriginal(invokeRequest);
|
|
149
|
+
}
|
|
134
150
|
} else {
|
|
135
151
|
invokeRequest = request;
|
|
136
152
|
}
|
|
@@ -150,32 +166,45 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
150
166
|
this._service?.service_description.host_type ===
|
|
151
167
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
152
168
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
169
|
+
if (this._voltClient.isRelayed || isServiceRelayed) {
|
|
170
|
+
let requestPayload;
|
|
171
|
+
if (this._voltClient.isRelayed) {
|
|
172
|
+
// Serialise the rpc request using the original serialisation method.
|
|
173
|
+
requestPayload =
|
|
174
|
+
this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(
|
|
175
|
+
request
|
|
176
|
+
);
|
|
177
|
+
} else {
|
|
178
|
+
requestPayload =
|
|
179
|
+
this._grpcClient[invokeInfo.methodName].requestSerialize(request);
|
|
180
|
+
}
|
|
159
181
|
|
|
182
|
+
// Use the Tunnel method to serialise the relay request wrapper, this
|
|
160
183
|
const voltGrpcClient = this._voltClient.getVoltAPIClient();
|
|
161
|
-
const tunnelMethod = voltGrpcClient.Tunnel;
|
|
162
184
|
|
|
163
|
-
|
|
185
|
+
// Create a tunnel message containing the method call metadata and request payload.
|
|
186
|
+
const tunnelResp = voltGrpcClient.Tunnel.responseSerialize({
|
|
164
187
|
method_payload: {
|
|
165
188
|
payload: requestPayload
|
|
166
189
|
}
|
|
167
|
-
};
|
|
190
|
+
});
|
|
168
191
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
tunnelMethod.responseSerialize(tunnelResp)
|
|
173
|
-
);
|
|
192
|
+
// Encrypt the payload using the shared key.
|
|
193
|
+
const iv = aesCreateKey().iv;
|
|
194
|
+
const invokePayload = aesEncrypt(this._encryptionKey, iv, tunnelResp);
|
|
174
195
|
|
|
196
|
+
// We then serialise the entire request using the original serialisation method for Invoke.
|
|
175
197
|
payloadRequest = {
|
|
176
198
|
iv,
|
|
177
199
|
payload: invokePayload
|
|
178
200
|
};
|
|
201
|
+
|
|
202
|
+
if (this._voltClient.isRelayed) {
|
|
203
|
+
// When tunnelling, we serialise the request using the original serialisation
|
|
204
|
+
// method for Invoke.
|
|
205
|
+
payloadRequest =
|
|
206
|
+
voltGrpcClient.Invoke.requestSerializeOriginal(payloadRequest);
|
|
207
|
+
}
|
|
179
208
|
} else {
|
|
180
209
|
payloadRequest = request;
|
|
181
210
|
}
|
|
@@ -191,7 +220,14 @@ function _parseResponse(method, meta, response) {
|
|
|
191
220
|
this._service?.service_description.host_type ===
|
|
192
221
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
193
222
|
|
|
194
|
-
|
|
223
|
+
const voltGrpcClient = this._voltClient.getVoltAPIClient();
|
|
224
|
+
|
|
225
|
+
if (this._voltClient.isRelayed || isServiceRelayed) {
|
|
226
|
+
if (this._voltClient.isRelayed) {
|
|
227
|
+
// Deserialise the incoming response using the original deserialisation method for Invoke.
|
|
228
|
+
response = voltGrpcClient.Invoke.responseDeserializeOriginal(response);
|
|
229
|
+
}
|
|
230
|
+
|
|
195
231
|
if (response.key_exchange) {
|
|
196
232
|
const encryptionKey = response.key_exchange.encryption_key;
|
|
197
233
|
const nonce = response.key_exchange.nonce;
|
|
@@ -229,13 +265,24 @@ function _parseResponse(method, meta, response) {
|
|
|
229
265
|
decryptedPayload = response.payload;
|
|
230
266
|
}
|
|
231
267
|
|
|
232
|
-
|
|
233
|
-
const responsePayload =
|
|
268
|
+
// Deserialise the decrypted payload using the Tunnel deserialisation method.
|
|
269
|
+
const responsePayload =
|
|
270
|
+
voltGrpcClient.Tunnel.requestDeserialize(decryptedPayload);
|
|
234
271
|
if (responsePayload.payload === "method_payload") {
|
|
235
272
|
try {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
273
|
+
// Deserialise the method payload.
|
|
274
|
+
if (this._voltClient.isRelayed) {
|
|
275
|
+
// Use the original deserialisation method for the target method.
|
|
276
|
+
invokeResponse.payload = this._grpcClient[
|
|
277
|
+
method
|
|
278
|
+
].responseDeserializeOriginal(
|
|
279
|
+
responsePayload.method_payload.payload
|
|
280
|
+
);
|
|
281
|
+
} else {
|
|
282
|
+
invokeResponse.payload = this._grpcClient[
|
|
283
|
+
method
|
|
284
|
+
].responseDeserialize(responsePayload.method_payload.payload);
|
|
285
|
+
}
|
|
239
286
|
} catch (err) {
|
|
240
287
|
log("failure deserialising payload for %s", this._methodName);
|
|
241
288
|
throw new Error(
|
|
@@ -263,16 +310,6 @@ function _parseResponse(method, meta, response) {
|
|
|
263
310
|
invokeResponse.methodId = response.invoke_id;
|
|
264
311
|
log("started method %d for %s", invokeResponse.methodId, method);
|
|
265
312
|
}
|
|
266
|
-
} else if (this._voltClient.isRemote && this._encryptionKey) {
|
|
267
|
-
const decryptedPayload = aesDecrypt(
|
|
268
|
-
this._encryptionKey,
|
|
269
|
-
meta.sharedKey.iv,
|
|
270
|
-
response
|
|
271
|
-
);
|
|
272
|
-
invokeResponse = {
|
|
273
|
-
payload:
|
|
274
|
-
this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)
|
|
275
|
-
};
|
|
276
313
|
} else {
|
|
277
314
|
invokeResponse = { payload: response };
|
|
278
315
|
}
|
|
@@ -312,7 +349,7 @@ export default class GRPCCall extends EventEmitter {
|
|
|
312
349
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
313
350
|
|
|
314
351
|
let callClient;
|
|
315
|
-
if (this._voltClient.
|
|
352
|
+
if (this._voltClient.isRelayed || isServiceRelayed) {
|
|
316
353
|
callClient = this._voltClient.getVoltAPIClient();
|
|
317
354
|
} else {
|
|
318
355
|
callClient = this._grpcClient;
|
|
@@ -379,7 +416,11 @@ export default class GRPCCall extends EventEmitter {
|
|
|
379
416
|
});
|
|
380
417
|
|
|
381
418
|
this._call.on("error", (err) => {
|
|
382
|
-
log(
|
|
419
|
+
log(
|
|
420
|
+
"ERROR - intercepted stream error on %s: %s",
|
|
421
|
+
this._methodName,
|
|
422
|
+
err.message
|
|
423
|
+
);
|
|
383
424
|
this.emit("error", err);
|
|
384
425
|
});
|
|
385
426
|
|
|
@@ -415,6 +456,16 @@ export default class GRPCCall extends EventEmitter {
|
|
|
415
456
|
}
|
|
416
457
|
}
|
|
417
458
|
|
|
459
|
+
pause() {
|
|
460
|
+
log("pausing call %s", this._methodName);
|
|
461
|
+
return this._call?.pause();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
resume() {
|
|
465
|
+
log("resuming call %s", this._methodName);
|
|
466
|
+
return this._call?.resume();
|
|
467
|
+
}
|
|
468
|
+
|
|
418
469
|
get writable() {
|
|
419
470
|
return this._call?.writable;
|
|
420
471
|
}
|
package/src/grpc-utils.js
CHANGED
|
@@ -20,7 +20,7 @@ export function createClient(
|
|
|
20
20
|
) {
|
|
21
21
|
let grpcCredentials;
|
|
22
22
|
if (credentials) {
|
|
23
|
-
const relaying = !!credentials.config?.volt?.relay;
|
|
23
|
+
const relaying = !!credentials.config?.volt?.relay && !Array.isArray(credentials.config?.volt?.relay);
|
|
24
24
|
const ca = relaying
|
|
25
25
|
? credentials.config?.volt?.relay?.ca_pem
|
|
26
26
|
: credentials.cache.ca;
|
|
@@ -28,8 +28,8 @@ export function createClient(
|
|
|
28
28
|
const { key } = credentials.cache;
|
|
29
29
|
|
|
30
30
|
if (key && cert) {
|
|
31
|
-
const
|
|
32
|
-
const tlsOptions = createSecureContextOptions(
|
|
31
|
+
const cryptoOptions = { ca, cert, key };
|
|
32
|
+
const tlsOptions = createSecureContextOptions(cryptoOptions);
|
|
33
33
|
grpcCredentials = grpc.credentials.createSsl(
|
|
34
34
|
tlsOptions.ca,
|
|
35
35
|
tlsOptions.key,
|
package/src/rpc-invocation.js
CHANGED
|
@@ -15,23 +15,26 @@ const {
|
|
|
15
15
|
publicKeyToPem,
|
|
16
16
|
randomBytes,
|
|
17
17
|
signBase64,
|
|
18
|
-
|
|
18
|
+
stripPemHeaders,
|
|
19
19
|
x25519
|
|
20
20
|
} = voltClientUtils;
|
|
21
21
|
|
|
22
|
-
const log = debug("rpc-invocation");
|
|
22
|
+
const log = debug("volt-client-grpc:rpc-invocation");
|
|
23
23
|
|
|
24
24
|
class RpcInvocation extends EventEmitter {
|
|
25
25
|
#voltClient = null;
|
|
26
26
|
#voltConnection = null;
|
|
27
27
|
#token = null;
|
|
28
|
+
#callerPublicKeyPem = null;
|
|
28
29
|
#invokeId = null;
|
|
30
|
+
#serviceId = null;
|
|
29
31
|
#payload = null;
|
|
30
32
|
#isJSON = false;
|
|
31
33
|
#methodDescriptor = null;
|
|
32
34
|
#methodName = null;
|
|
33
35
|
#derivedKey = null;
|
|
34
36
|
#started = false;
|
|
37
|
+
#endEmitted = false;
|
|
35
38
|
|
|
36
39
|
constructor(voltClient, voltConnection) {
|
|
37
40
|
super();
|
|
@@ -43,6 +46,18 @@ class RpcInvocation extends EventEmitter {
|
|
|
43
46
|
return this.#invokeId;
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
get serviceId() {
|
|
50
|
+
return this.#serviceId;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get token() {
|
|
54
|
+
return this.#token;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get callerPublicKeyPem() {
|
|
58
|
+
return this.#callerPublicKeyPem;
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
get payload() {
|
|
47
62
|
if (!this.#payload) {
|
|
48
63
|
return null;
|
|
@@ -84,14 +99,23 @@ class RpcInvocation extends EventEmitter {
|
|
|
84
99
|
// The rpc implementation should do the verification using the Volt API (e.g. CanAccessResource).
|
|
85
100
|
this.#token = jwt.decode(token);
|
|
86
101
|
|
|
102
|
+
if (!this.#token) {
|
|
103
|
+
log("failed to decode token %s", token || "null");
|
|
104
|
+
return Promise.reject(new Error("failed to decode token"));
|
|
105
|
+
}
|
|
106
|
+
|
|
87
107
|
// Store the JWT key id (kid) for use in the response.
|
|
88
|
-
|
|
89
|
-
const callerKey = keyFromPem(
|
|
108
|
+
this.#callerPublicKeyPem = formatPEM(this.#token.k, "PUBLIC KEY");
|
|
109
|
+
const callerKey = keyFromPem(
|
|
110
|
+
this.#callerPublicKeyPem,
|
|
111
|
+
"",
|
|
112
|
+
x25519
|
|
113
|
+
).publicKey;
|
|
90
114
|
|
|
91
115
|
const ephemeralKeyPem = createEncryptionKey();
|
|
92
116
|
const ephemeralKey = keyFromPem(ephemeralKeyPem, "", x25519);
|
|
93
117
|
const ephemeralPublicKey = fromBase64(
|
|
94
|
-
|
|
118
|
+
stripPemHeaders(publicKeyToPem(ephemeralKey.publicKey, x25519))
|
|
95
119
|
);
|
|
96
120
|
|
|
97
121
|
this.#derivedKey = deriveSharedKey(
|
|
@@ -108,7 +132,7 @@ class RpcInvocation extends EventEmitter {
|
|
|
108
132
|
message.set(nonce, ephemeralPublicKey.length);
|
|
109
133
|
|
|
110
134
|
const signature = signBase64(
|
|
111
|
-
this.#voltClient.credential.
|
|
135
|
+
this.#voltClient.credential.cache.key,
|
|
112
136
|
message
|
|
113
137
|
);
|
|
114
138
|
|
|
@@ -129,7 +153,10 @@ class RpcInvocation extends EventEmitter {
|
|
|
129
153
|
}
|
|
130
154
|
|
|
131
155
|
initialise(invoke_request) {
|
|
132
|
-
this.#invokeId
|
|
156
|
+
if (!this.#invokeId) {
|
|
157
|
+
this.#invokeId = invoke_request.invoke_id;
|
|
158
|
+
this.#serviceId = invoke_request.target_service_id;
|
|
159
|
+
}
|
|
133
160
|
|
|
134
161
|
if (!this.#derivedKey) {
|
|
135
162
|
// We don't have the derived key yet, so we need to perform the key exchange.
|
|
@@ -151,8 +178,7 @@ class RpcInvocation extends EventEmitter {
|
|
|
151
178
|
|
|
152
179
|
if (invoke_request.payload) {
|
|
153
180
|
let decryptedPayload;
|
|
154
|
-
|
|
155
|
-
if (encryptIV) {
|
|
181
|
+
if (encryptIV?.length) {
|
|
156
182
|
// Decrypt the actual payload using the shared key and iv.
|
|
157
183
|
decryptedPayload = aesDecrypt(
|
|
158
184
|
this.#derivedKey,
|
|
@@ -220,8 +246,13 @@ class RpcInvocation extends EventEmitter {
|
|
|
220
246
|
);
|
|
221
247
|
this.emit("payload", this);
|
|
222
248
|
} else if (wrappedPayload.method_end) {
|
|
223
|
-
// We don't need to do anything here, since we notify the client when we receive the client_end.
|
|
224
249
|
log("method_end received");
|
|
250
|
+
if (!this.#endEmitted) {
|
|
251
|
+
this.#endEmitted = true;
|
|
252
|
+
this.emit("end", this);
|
|
253
|
+
} else {
|
|
254
|
+
log("method_end: end already emitted");
|
|
255
|
+
}
|
|
225
256
|
}
|
|
226
257
|
} catch (err) {
|
|
227
258
|
log("JSON.parse failure parsing json_payload: %s", err.message);
|
|
@@ -230,7 +261,12 @@ class RpcInvocation extends EventEmitter {
|
|
|
230
261
|
|
|
231
262
|
resolve();
|
|
232
263
|
} else if (invoke_request.client_end) {
|
|
233
|
-
|
|
264
|
+
if (!this.#endEmitted) {
|
|
265
|
+
this.#endEmitted = true;
|
|
266
|
+
this.emit("end", this);
|
|
267
|
+
} else {
|
|
268
|
+
log("client_end: end already emitted");
|
|
269
|
+
}
|
|
234
270
|
} else {
|
|
235
271
|
// Do we need to support json_payload here?
|
|
236
272
|
reject(new Error("No payload in invoke request"));
|
|
@@ -172,7 +172,7 @@ export async function authenticateInternal(
|
|
|
172
172
|
|
|
173
173
|
if (this._voltConfig.challenge_code) {
|
|
174
174
|
authenticateRequest.challenge = signBase64(
|
|
175
|
-
|
|
175
|
+
this._credential.cache.key,
|
|
176
176
|
this._voltConfig.challenge_code
|
|
177
177
|
);
|
|
178
178
|
} else {
|
|
@@ -202,16 +202,18 @@ export async function authenticateInternal(
|
|
|
202
202
|
// This is because the DID document is signed and the signature
|
|
203
203
|
// is over the entire document.
|
|
204
204
|
const didDocument = {
|
|
205
|
-
"@context":
|
|
205
|
+
"@context": [
|
|
206
|
+
"https://www.w3.org/ns/did/v1",
|
|
207
|
+
"https://tdxvolt.com/ns/did/v1"
|
|
208
|
+
],
|
|
206
209
|
authentication: [`${did}#key-1`],
|
|
207
210
|
controller: did,
|
|
208
|
-
description: this._config.client_name,
|
|
209
211
|
id: did,
|
|
210
212
|
verificationMethod: [
|
|
211
213
|
{
|
|
212
214
|
id: `${did}#key-1`,
|
|
213
215
|
publicKeyPem: publicKeyPem,
|
|
214
|
-
type: "
|
|
216
|
+
type: "Ed25519Signature2018"
|
|
215
217
|
}
|
|
216
218
|
]
|
|
217
219
|
};
|
|
@@ -222,7 +224,7 @@ export async function authenticateInternal(
|
|
|
222
224
|
// Now sign the entire DID document, including the proof and set it in the
|
|
223
225
|
// bind request. This is required by the Volt to verify that we have the private key for the DID.
|
|
224
226
|
authenticateRequest.did_document_signature = signBase64(
|
|
225
|
-
|
|
227
|
+
this._credential.cache.key,
|
|
226
228
|
authenticateRequest.did_document
|
|
227
229
|
);
|
|
228
230
|
|
|
@@ -260,7 +262,7 @@ export async function authenticateInternal(
|
|
|
260
262
|
|
|
261
263
|
return decision === "POLICY_DECISION_PERMIT";
|
|
262
264
|
} catch (err) {
|
|
263
|
-
log("failure
|
|
265
|
+
log("failure authenticating with Volt [%s]", err.message);
|
|
264
266
|
throw err;
|
|
265
267
|
}
|
|
266
268
|
}
|
|
@@ -276,15 +278,7 @@ export function connectInternal(helloPayload) {
|
|
|
276
278
|
this._connected = false;
|
|
277
279
|
this._session = null;
|
|
278
280
|
|
|
279
|
-
|
|
280
|
-
this._grpc.closeClient(this._cachedClient);
|
|
281
|
-
this._cachedClient = null;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
if (this._cachedRemoteClient) {
|
|
285
|
-
this._grpc.closeClient(this._cachedRemoteClient);
|
|
286
|
-
this._cachedRemoteClient = null;
|
|
287
|
-
}
|
|
281
|
+
this.close();
|
|
288
282
|
};
|
|
289
283
|
|
|
290
284
|
this._voltConnection = new VoltConnection(this, this._grpc);
|
|
@@ -373,7 +367,7 @@ export function connectInternal(helloPayload) {
|
|
|
373
367
|
|
|
374
368
|
export function getVoltAPIClientInternal() {
|
|
375
369
|
if (!this._cachedClient) {
|
|
376
|
-
const serviceAddress = this.
|
|
370
|
+
const serviceAddress = this.isRelayed
|
|
377
371
|
? this._voltConfig.relay.address
|
|
378
372
|
: this._voltConfig.address;
|
|
379
373
|
log("creating cached VoltAPI service client on %s", serviceAddress);
|
|
@@ -388,7 +382,7 @@ export function getVoltAPIClientInternal() {
|
|
|
388
382
|
serviceDescriptors,
|
|
389
383
|
serviceAddress,
|
|
390
384
|
this._credential,
|
|
391
|
-
this.
|
|
385
|
+
this.isRelayed,
|
|
392
386
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
393
387
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
394
388
|
);
|
|
@@ -399,7 +393,7 @@ export function getVoltAPIClientInternal() {
|
|
|
399
393
|
|
|
400
394
|
export function getAPIClientInternal(service) {
|
|
401
395
|
if (!this._cachedService[service.id]) {
|
|
402
|
-
const serviceAddress = this.
|
|
396
|
+
const serviceAddress = this.isRelayed
|
|
403
397
|
? this._voltConfig.relay.address
|
|
404
398
|
: service.service_description.host_address;
|
|
405
399
|
|
|
@@ -422,7 +416,7 @@ export function getAPIClientInternal(service) {
|
|
|
422
416
|
serviceDescriptors,
|
|
423
417
|
serviceAddress,
|
|
424
418
|
this._credential,
|
|
425
|
-
this.
|
|
419
|
+
this.isRelayed,
|
|
426
420
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
427
421
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
428
422
|
);
|
|
@@ -517,9 +511,9 @@ export async function fetchVoltConfig(discovery_url) {
|
|
|
517
511
|
}
|
|
518
512
|
}
|
|
519
513
|
|
|
520
|
-
|
|
514
|
+
async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
|
|
521
515
|
try {
|
|
522
|
-
const didResolution = voltUtils.getDIDResolutionURL(volt_did);
|
|
516
|
+
const didResolution = voltUtils.getDIDResolutionURL(volt_did, registryUrl);
|
|
523
517
|
if (!didResolution) {
|
|
524
518
|
throw new Error(`Invalid Volt DID: ${volt_did}`);
|
|
525
519
|
}
|
|
@@ -538,6 +532,8 @@ export async function fetchVoltConfigFromDID(volt_did) {
|
|
|
538
532
|
);
|
|
539
533
|
}
|
|
540
534
|
|
|
535
|
+
log("resolved DID from %s", didResolution);
|
|
536
|
+
|
|
541
537
|
const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
|
|
542
538
|
return await fetchVoltConfig.call(this, serviceEndpoint);
|
|
543
539
|
} catch (err) {
|
|
@@ -546,3 +542,22 @@ export async function fetchVoltConfigFromDID(volt_did) {
|
|
|
546
542
|
);
|
|
547
543
|
}
|
|
548
544
|
}
|
|
545
|
+
|
|
546
|
+
export async function fetchVoltConfigFromDID(
|
|
547
|
+
volt_did,
|
|
548
|
+
registryList = [constants.defaultDIDHostName]
|
|
549
|
+
) {
|
|
550
|
+
for (let registryUrl of registryList) {
|
|
551
|
+
try {
|
|
552
|
+
return await fetchVoltConfigFromDIDInternal.call(
|
|
553
|
+
this,
|
|
554
|
+
volt_did,
|
|
555
|
+
registryUrl
|
|
556
|
+
);
|
|
557
|
+
} catch (err) {
|
|
558
|
+
log("Failed to fetch Volt config from %s: %s", registryUrl, err.message);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
throw new Error("Failed to fetch Volt config from any registry");
|
|
563
|
+
}
|