@tdxvolt/volt-client-grpc 0.16.0-beta.10 → 0.16.0-beta.13
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/README.md +1 -1
- package/lib/index.cjs +431 -455
- package/package.json +2 -4
- package/protobuf/tdx/volt_api/data/v1/sqlite_database_api.proto +49 -13
- package/protobuf/tdx/volt_api/data/v1/sqlite_server_api.proto +3 -0
- package/protobuf/tdx/volt_api/volt/v1/discovery_api.proto +0 -4
- package/protobuf/tdx/volt_api/volt/v1/ssi.proto +31 -5
- package/protobuf/tdx/volt_api/volt/v1/ssi_api.proto +119 -31
- package/protobuf/tdx/volt_api/volt/v1/terminal_api.proto +42 -0
- package/protobuf/tdx/volt_api/volt/v1/volt.proto +146 -139
- package/protobuf/tdx/volt_api/volt/v1/volt_api.proto +165 -270
- package/src/constants.js +3 -5
- package/src/grpc-call.js +142 -51
- package/src/grpc-utils.js +8 -4
- package/src/rpc-invocation.js +94 -46
- package/src/utils.js +5 -119
- package/src/volt-client-internal.js +106 -73
- package/src/volt-client.js +33 -12
- package/src/volt-connection.js +22 -12
- package/src/volt-credential.js +37 -124
package/src/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// eslint-disable-next-line import/prefer-default-export
|
|
2
2
|
export const constants = {
|
|
3
|
-
authTokenName: "
|
|
3
|
+
authTokenName: "volt-token",
|
|
4
4
|
collectionOperation: {
|
|
5
5
|
add: "ADD",
|
|
6
6
|
remove: "REMOVE",
|
|
@@ -23,9 +23,9 @@ export const constants = {
|
|
|
23
23
|
],
|
|
24
24
|
userIdOID: "0.9.2342.19200300.100.1.1"
|
|
25
25
|
},
|
|
26
|
-
defaultDIDHostName: "
|
|
26
|
+
defaultDIDHostName: "tdxvolt.com",
|
|
27
27
|
didServiceType: {
|
|
28
|
-
voltConfig: "
|
|
28
|
+
voltConfig: "volt-discovery"
|
|
29
29
|
},
|
|
30
30
|
identityType: {
|
|
31
31
|
did: "did",
|
|
@@ -36,8 +36,6 @@ export const constants = {
|
|
|
36
36
|
online: "online",
|
|
37
37
|
unknown: "unknown"
|
|
38
38
|
},
|
|
39
|
-
publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
|
|
40
|
-
privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
|
|
41
39
|
privateEncryptedKeyPrefix: "-----BEGIN ENCRYPTED PRIVATE KEY-----",
|
|
42
40
|
resource: {
|
|
43
41
|
identity: "ident",
|
package/src/grpc-call.js
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
/* eslint-disable no-underscore-dangle */
|
|
2
2
|
import debug from "debug";
|
|
3
3
|
import EventEmitter from "events";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
aesCreateKey,
|
|
6
|
+
aesEncrypt,
|
|
7
|
+
aesDecrypt,
|
|
8
|
+
toBase64,
|
|
9
|
+
formatPEM,
|
|
10
|
+
keyFromPem,
|
|
11
|
+
verify,
|
|
12
|
+
deriveSharedKey,
|
|
13
|
+
x25519
|
|
14
|
+
} from "@tdxvolt/volt-client-web";
|
|
5
15
|
|
|
6
16
|
const log = debug("volt-client-grpc:grpc-call");
|
|
7
17
|
|
|
@@ -14,6 +24,21 @@ function _prepareInvokeRequest(
|
|
|
14
24
|
this._service?.service_description.host_type ===
|
|
15
25
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
16
26
|
|
|
27
|
+
const relaying = this._voltClient.isVoltRelay || isServiceRelayed;
|
|
28
|
+
|
|
29
|
+
let targetDID = [];
|
|
30
|
+
if (this._voltClient.isVoltRelay) {
|
|
31
|
+
targetDID.push(this._voltClient.voltConfig.id);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (isServiceRelayed) {
|
|
35
|
+
// Add another relay hop if the target service is relayed.
|
|
36
|
+
log("target service is relayed");
|
|
37
|
+
targetDID.push(this._service.service_description.host_client_id);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
log("target is %j", targetDID);
|
|
41
|
+
|
|
17
42
|
// Use the service's host client id and public key if the service is relayed.
|
|
18
43
|
const methodToken = this._voltClient.credential.getIdentityMetadata(
|
|
19
44
|
this._voltClient.grpc,
|
|
@@ -21,17 +46,52 @@ function _prepareInvokeRequest(
|
|
|
21
46
|
this._voltClient.voltConfig.id,
|
|
22
47
|
this._service?.service_description.host_public_key ||
|
|
23
48
|
this._voltClient.credential.voltPublicKey,
|
|
24
|
-
|
|
49
|
+
relaying
|
|
25
50
|
);
|
|
26
51
|
|
|
27
52
|
let invokeRequest;
|
|
28
53
|
let callMethod = method;
|
|
29
54
|
|
|
30
|
-
if (
|
|
55
|
+
if (relaying) {
|
|
31
56
|
//
|
|
32
57
|
// If the volt connection is via a relay (or the target service is relayed),
|
|
33
58
|
// we wrap the rpc in a RemoteRequest message and send it via a call to Invoke().
|
|
34
59
|
//
|
|
60
|
+
// Replace the target method with a call to Invoke on the relay Volt.
|
|
61
|
+
//
|
|
62
|
+
callMethod = "Invoke";
|
|
63
|
+
|
|
64
|
+
// But first we need to perform a key exchange with the target service.
|
|
65
|
+
//
|
|
66
|
+
if (!this._encryptionKey) {
|
|
67
|
+
// We haven't exchanged keys yet, so queue the payload.
|
|
68
|
+
this._pendingRequests.push(request);
|
|
69
|
+
|
|
70
|
+
if (this._keyExchangePending) {
|
|
71
|
+
// We've already sent the key exchange request, so do nothing.
|
|
72
|
+
log("key exchange pending, not sending payload");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this._keyExchangePending = true;
|
|
77
|
+
|
|
78
|
+
// Send the key exchange request.
|
|
79
|
+
invokeRequest = {
|
|
80
|
+
token: methodToken.token,
|
|
81
|
+
target_did: targetDID,
|
|
82
|
+
target_service_id: isServiceRelayed
|
|
83
|
+
? this._service.service_description.host_service_id
|
|
84
|
+
: undefined
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
request: invokeRequest,
|
|
89
|
+
method: callMethod,
|
|
90
|
+
methodName: method,
|
|
91
|
+
meta: methodToken
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
35
95
|
let requestPayload;
|
|
36
96
|
|
|
37
97
|
// Serialise the rpc request.
|
|
@@ -51,45 +111,24 @@ function _prepareInvokeRequest(
|
|
|
51
111
|
method_invoke: {
|
|
52
112
|
method_name: this._grpcClient[method].path,
|
|
53
113
|
method_type: methodType,
|
|
54
|
-
token: methodToken.token,
|
|
55
114
|
request: requestPayload
|
|
56
115
|
}
|
|
57
116
|
};
|
|
58
117
|
|
|
59
|
-
let targetDID = [];
|
|
60
118
|
let invokePayload;
|
|
61
|
-
|
|
62
|
-
// When sending via a relay we need to encrypt the payload.
|
|
63
|
-
invokePayload = VoltCredential.aesEncrypt(
|
|
64
|
-
tunnelMethod.responseSerialize(tunnelResp),
|
|
65
|
-
methodToken.sharedKey.key,
|
|
66
|
-
methodToken.sharedKey.iv
|
|
67
|
-
);
|
|
68
|
-
} else {
|
|
69
|
-
invokePayload = tunnelMethod.responseSerialize(tunnelResp);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (this._voltClient.isVoltRelay) {
|
|
73
|
-
targetDID.push(this._voltClient.voltConfig.id);
|
|
74
|
-
}
|
|
119
|
+
const iv = aesCreateKey().iv;
|
|
75
120
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
log("target is %j", targetDID);
|
|
121
|
+
// When sending via a relay we need to encrypt the payload.
|
|
122
|
+
invokePayload = aesEncrypt(
|
|
123
|
+
this._encryptionKey,
|
|
124
|
+
iv,
|
|
125
|
+
tunnelMethod.responseSerialize(tunnelResp)
|
|
126
|
+
);
|
|
83
127
|
|
|
84
128
|
invokeRequest = {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
payload: invokePayload,
|
|
88
|
-
target_service_id: isServiceRelayed ? this._service.id : undefined
|
|
129
|
+
iv,
|
|
130
|
+
payload: invokePayload
|
|
89
131
|
};
|
|
90
|
-
|
|
91
|
-
// Replace the target method with a call to Invoke on the relay Volt.
|
|
92
|
-
callMethod = "Invoke";
|
|
93
132
|
} else {
|
|
94
133
|
invokeRequest = request;
|
|
95
134
|
}
|
|
@@ -109,7 +148,10 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
109
148
|
this._service?.service_description.host_type ===
|
|
110
149
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
111
150
|
|
|
151
|
+
let iv;
|
|
112
152
|
if (this._voltClient.isVoltRelay || isServiceRelayed) {
|
|
153
|
+
iv = aesCreateKey().iv;
|
|
154
|
+
|
|
113
155
|
const requestPayload =
|
|
114
156
|
this._grpcClient[invokeInfo.methodName].requestSerialize(request);
|
|
115
157
|
|
|
@@ -122,13 +164,14 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
122
164
|
}
|
|
123
165
|
};
|
|
124
166
|
|
|
125
|
-
const invokePayload =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
167
|
+
const invokePayload = aesEncrypt(
|
|
168
|
+
this._encryptionKey,
|
|
169
|
+
iv,
|
|
170
|
+
tunnelMethod.responseSerialize(tunnelResp)
|
|
129
171
|
);
|
|
130
172
|
|
|
131
173
|
payloadRequest = {
|
|
174
|
+
iv,
|
|
132
175
|
payload: invokePayload
|
|
133
176
|
};
|
|
134
177
|
} else {
|
|
@@ -147,13 +190,38 @@ function _parseResponse(method, meta, response) {
|
|
|
147
190
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
148
191
|
|
|
149
192
|
if (this._voltClient.isVoltRelay || isServiceRelayed) {
|
|
150
|
-
if (response.
|
|
193
|
+
if (response.key_exchange) {
|
|
194
|
+
const encryptionKey = response.key_exchange.encryption_key;
|
|
195
|
+
const nonce = response.key_exchange.nonce;
|
|
196
|
+
const signature = response.key_exchange.signature;
|
|
197
|
+
|
|
198
|
+
// The signed message is the encryption key and the nonce.
|
|
199
|
+
const message = new Uint8Array(encryptionKey.length + nonce.length);
|
|
200
|
+
message.set(encryptionKey);
|
|
201
|
+
message.set(nonce, encryptionKey.length);
|
|
202
|
+
|
|
203
|
+
// Check the signature using the target service public key.
|
|
204
|
+
const targetKey = keyFromPem(
|
|
205
|
+
this._service?.service_description?.host_public_key ||
|
|
206
|
+
this._voltClient.credential.voltPublicKey
|
|
207
|
+
).publicKey;
|
|
208
|
+
|
|
209
|
+
if (!verify(targetKey, message, signature)) {
|
|
210
|
+
throw new Error("signature verification failed");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
invokeResponse = {
|
|
214
|
+
key_exchange: {
|
|
215
|
+
encryption_key: formatPEM(toBase64(encryptionKey), "PUBLIC KEY")
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
} else if (response.payload) {
|
|
151
219
|
let decryptedPayload;
|
|
152
|
-
if (
|
|
153
|
-
decryptedPayload =
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
220
|
+
if (this._encryptionKey) {
|
|
221
|
+
decryptedPayload = aesDecrypt(
|
|
222
|
+
this._encryptionKey,
|
|
223
|
+
response.iv,
|
|
224
|
+
response.payload
|
|
157
225
|
);
|
|
158
226
|
} else {
|
|
159
227
|
decryptedPayload = response.payload;
|
|
@@ -193,11 +261,11 @@ function _parseResponse(method, meta, response) {
|
|
|
193
261
|
invokeResponse.methodId = response.invoke_id;
|
|
194
262
|
log("started method %d for %s", invokeResponse.methodId, method);
|
|
195
263
|
}
|
|
196
|
-
} else if (this._voltClient.isRemote &&
|
|
197
|
-
const decryptedPayload =
|
|
198
|
-
|
|
199
|
-
meta.sharedKey.
|
|
200
|
-
|
|
264
|
+
} else if (this._voltClient.isRemote && this._encryptionKey) {
|
|
265
|
+
const decryptedPayload = aesDecrypt(
|
|
266
|
+
this._encryptionKey,
|
|
267
|
+
meta.sharedKey.iv,
|
|
268
|
+
response
|
|
201
269
|
);
|
|
202
270
|
invokeResponse = {
|
|
203
271
|
payload:
|
|
@@ -218,6 +286,9 @@ export default class GRPCCall extends EventEmitter {
|
|
|
218
286
|
this._call = null;
|
|
219
287
|
this._voltClient = voltClient;
|
|
220
288
|
this._service = service;
|
|
289
|
+
this._keyExchangePending = false;
|
|
290
|
+
this._pendingRequests = [];
|
|
291
|
+
this._encryptionKey = null;
|
|
221
292
|
}
|
|
222
293
|
|
|
223
294
|
start(grpcClient, request) {
|
|
@@ -246,7 +317,7 @@ export default class GRPCCall extends EventEmitter {
|
|
|
246
317
|
}
|
|
247
318
|
|
|
248
319
|
this._call = callClient[this._initialRequest.method](
|
|
249
|
-
this._initialRequest
|
|
320
|
+
this._initialRequest?.meta?.metadata
|
|
250
321
|
);
|
|
251
322
|
|
|
252
323
|
this._call.on("data", (streamResponse) => {
|
|
@@ -257,7 +328,25 @@ export default class GRPCCall extends EventEmitter {
|
|
|
257
328
|
this._initialRequest.meta,
|
|
258
329
|
streamResponse
|
|
259
330
|
);
|
|
260
|
-
if (parsedResponse.
|
|
331
|
+
if (parsedResponse.key_exchange) {
|
|
332
|
+
const peerKey = keyFromPem(
|
|
333
|
+
parsedResponse.key_exchange.encryption_key
|
|
334
|
+
).publicKey;
|
|
335
|
+
|
|
336
|
+
this._encryptionKey = deriveSharedKey(
|
|
337
|
+
this._initialRequest.meta.sharedKey.key,
|
|
338
|
+
peerKey,
|
|
339
|
+
x25519
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
// We've exchanged keys, so send any queued payloads on the next tick.
|
|
343
|
+
process.nextTick(() => {
|
|
344
|
+
this._pendingRequests.forEach((pendingRequest) => {
|
|
345
|
+
this.write(pendingRequest);
|
|
346
|
+
});
|
|
347
|
+
this._pendingRequests = [];
|
|
348
|
+
});
|
|
349
|
+
} else if (parsedResponse.payload) {
|
|
261
350
|
// Interpret a non-empty status message as an error.
|
|
262
351
|
if (parsedResponse.payload?.status?.message) {
|
|
263
352
|
this.emit(
|
|
@@ -332,7 +421,7 @@ export default class GRPCCall extends EventEmitter {
|
|
|
332
421
|
if (!this._call) {
|
|
333
422
|
log("ERROR - call not initialised");
|
|
334
423
|
throw new Error("call not initialised");
|
|
335
|
-
} else if (this._initialRequest) {
|
|
424
|
+
} else if (this._initialRequest && !this._keyExchangePending) {
|
|
336
425
|
const payloadRequest = _preparePayloadRequest.call(
|
|
337
426
|
this,
|
|
338
427
|
request,
|
|
@@ -340,6 +429,8 @@ export default class GRPCCall extends EventEmitter {
|
|
|
340
429
|
);
|
|
341
430
|
return this._call.write(payloadRequest);
|
|
342
431
|
} else {
|
|
432
|
+
this._keyExchangePending = false;
|
|
433
|
+
|
|
343
434
|
this._initialRequest = _prepareInvokeRequest.call(
|
|
344
435
|
this,
|
|
345
436
|
request,
|
package/src/grpc-utils.js
CHANGED
|
@@ -20,8 +20,11 @@ export function createClient(
|
|
|
20
20
|
) {
|
|
21
21
|
let grpcCredentials;
|
|
22
22
|
if (credentials) {
|
|
23
|
-
const
|
|
24
|
-
const
|
|
23
|
+
const relaying = !!credentials.config?.volt?.relay;
|
|
24
|
+
const ca = relaying
|
|
25
|
+
? credentials.config?.volt?.relay?.ca_pem
|
|
26
|
+
: credentials.cache.ca;
|
|
27
|
+
const cert = relaying ? "" : credentials.cache.cert;
|
|
25
28
|
const { key } = credentials.cache;
|
|
26
29
|
|
|
27
30
|
if (key && cert) {
|
|
@@ -33,8 +36,9 @@ export function createClient(
|
|
|
33
36
|
tlsOptions.cert
|
|
34
37
|
);
|
|
35
38
|
} else {
|
|
36
|
-
// Cache has no private key, this implies we are connecting without supplying a client certificate.
|
|
37
|
-
//
|
|
39
|
+
// Cache has no private key or certificate, this implies we are connecting without supplying a client certificate.
|
|
40
|
+
// We might be connecting to a relay, or making an initial Authenticate call.
|
|
41
|
+
// Simply connect using the CA certificate, and the a JWT will be used to authenticate.
|
|
38
42
|
grpcCredentials = grpc.credentials.createSsl(Buffer.from(ca));
|
|
39
43
|
}
|
|
40
44
|
} else {
|
package/src/rpc-invocation.js
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
import debug from "debug";
|
|
2
|
-
import { VoltCredential } from "./volt-credential.js";
|
|
3
2
|
import jwt from "jsonwebtoken";
|
|
4
3
|
import EventEmitter from "events";
|
|
4
|
+
import {
|
|
5
|
+
aesDecrypt,
|
|
6
|
+
aesEncrypt,
|
|
7
|
+
createEncryptionKey,
|
|
8
|
+
deriveSharedKey,
|
|
9
|
+
formatPEM,
|
|
10
|
+
fromBase64,
|
|
11
|
+
fromBase64Url,
|
|
12
|
+
keyFromPem,
|
|
13
|
+
publicKeyToPem,
|
|
14
|
+
randomBytes,
|
|
15
|
+
signBase64,
|
|
16
|
+
stripPEMHeaders,
|
|
17
|
+
x25519
|
|
18
|
+
} from "@tdxvolt/volt-client-web";
|
|
5
19
|
|
|
6
20
|
const log = debug("rpc-invocation");
|
|
7
21
|
|
|
@@ -9,13 +23,13 @@ class RpcInvocation extends EventEmitter {
|
|
|
9
23
|
#voltClient = null;
|
|
10
24
|
#voltConnection = null;
|
|
11
25
|
#token = null;
|
|
12
|
-
#encryptKey = null;
|
|
13
|
-
#encryptIV = null;
|
|
14
26
|
#invokeId = null;
|
|
15
27
|
#payload = null;
|
|
16
28
|
#isJSON = false;
|
|
17
29
|
#methodDescriptor = null;
|
|
18
30
|
#methodName = null;
|
|
31
|
+
#derivedKey = null;
|
|
32
|
+
#started = false;
|
|
19
33
|
|
|
20
34
|
constructor(voltClient, voltConnection) {
|
|
21
35
|
super();
|
|
@@ -59,51 +73,89 @@ class RpcInvocation extends EventEmitter {
|
|
|
59
73
|
this.#methodDescriptor = methodDescriptor;
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
|
|
76
|
+
get started() {
|
|
77
|
+
return this.#started;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#keyExchange(token) {
|
|
63
81
|
// We don't verify the token at this point, as we don't know the public key.
|
|
64
82
|
// The rpc implementation should do the verification using the Volt API (e.g. CanAccessResource).
|
|
65
83
|
this.#token = jwt.decode(token);
|
|
66
84
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
// Store the JWT key id (kid) for use in the response.
|
|
86
|
+
const callerPublicKeyPem = formatPEM(this.#token.k, "PUBLIC KEY");
|
|
87
|
+
const callerKey = keyFromPem(callerPublicKeyPem, "", x25519).publicKey;
|
|
88
|
+
|
|
89
|
+
const ephemeralKeyPem = createEncryptionKey();
|
|
90
|
+
const ephemeralKey = keyFromPem(ephemeralKeyPem, "", x25519);
|
|
91
|
+
const ephemeralPublicKey = fromBase64(
|
|
92
|
+
stripPEMHeaders(publicKeyToPem(ephemeralKey.publicKey, x25519))
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
this.#derivedKey = deriveSharedKey(
|
|
96
|
+
ephemeralKey.privateKey,
|
|
97
|
+
callerKey,
|
|
98
|
+
x25519
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const nonce = randomBytes(16);
|
|
102
|
+
|
|
103
|
+
// The signed message is the encryption key and the nonce.
|
|
104
|
+
const message = new Uint8Array(ephemeralPublicKey.length + nonce.length);
|
|
105
|
+
message.set(ephemeralPublicKey);
|
|
106
|
+
message.set(nonce, ephemeralPublicKey.length);
|
|
107
|
+
|
|
108
|
+
const signature = signBase64(
|
|
109
|
+
this.#voltClient.credential.getKey().privateKey,
|
|
110
|
+
message
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
const keyExchangeResponse = {
|
|
114
|
+
invoke_id: this.#invokeId,
|
|
115
|
+
key_exchange: {
|
|
116
|
+
nonce,
|
|
117
|
+
encryption_key: ephemeralPublicKey,
|
|
118
|
+
signature: fromBase64Url(signature)
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
this.#voltConnection.send({
|
|
123
|
+
invoke_response: keyExchangeResponse
|
|
124
|
+
});
|
|
78
125
|
|
|
79
|
-
return Promise.resolve(
|
|
126
|
+
return Promise.resolve(keyExchangeResponse);
|
|
80
127
|
}
|
|
81
128
|
|
|
82
129
|
initialise(invoke_request) {
|
|
83
130
|
this.#invokeId = invoke_request.invoke_id;
|
|
84
131
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return this.parsePayload(invoke_request);
|
|
89
|
-
})
|
|
90
|
-
.catch((err) => {
|
|
132
|
+
if (!this.#derivedKey) {
|
|
133
|
+
// We don't have the derived key yet, so we need to perform the key exchange.
|
|
134
|
+
return this.#keyExchange(invoke_request.token).catch((err) => {
|
|
91
135
|
log("failure initialising invocation: %s", err.message);
|
|
92
136
|
return Promise.reject(err);
|
|
93
137
|
});
|
|
138
|
+
} else {
|
|
139
|
+
// We already have the derived key, so we can just parse the payload.
|
|
140
|
+
return this.parsePayload(invoke_request);
|
|
141
|
+
}
|
|
94
142
|
}
|
|
95
143
|
|
|
96
144
|
parsePayload(invoke_request) {
|
|
145
|
+
this.#started = true;
|
|
146
|
+
|
|
97
147
|
return new Promise((resolve, reject) => {
|
|
148
|
+
const encryptIV = invoke_request.iv;
|
|
149
|
+
|
|
98
150
|
if (invoke_request.payload) {
|
|
99
151
|
let decryptedPayload;
|
|
100
152
|
|
|
101
|
-
if (
|
|
153
|
+
if (encryptIV) {
|
|
102
154
|
// Decrypt the actual payload using the shared key and iv.
|
|
103
|
-
decryptedPayload =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
155
|
+
decryptedPayload = aesDecrypt(
|
|
156
|
+
this.#derivedKey,
|
|
157
|
+
encryptIV,
|
|
158
|
+
invoke_request.payload
|
|
107
159
|
);
|
|
108
160
|
} else {
|
|
109
161
|
// No encryption => just use the payload.
|
|
@@ -139,12 +191,12 @@ class RpcInvocation extends EventEmitter {
|
|
|
139
191
|
|
|
140
192
|
this.#isJSON = true;
|
|
141
193
|
|
|
142
|
-
if (this.#
|
|
194
|
+
if (this.#derivedKey) {
|
|
143
195
|
// Decrypt the actual payload using the shared key and iv.
|
|
144
|
-
decryptedPayload =
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
196
|
+
decryptedPayload = aesDecrypt(
|
|
197
|
+
this.#derivedKey,
|
|
198
|
+
invoke_request.iv,
|
|
199
|
+
Buffer.from(invoke_request.json_payload, "base64")
|
|
148
200
|
);
|
|
149
201
|
} else {
|
|
150
202
|
// No encryption => just use the payload.
|
|
@@ -203,17 +255,15 @@ class RpcInvocation extends EventEmitter {
|
|
|
203
255
|
});
|
|
204
256
|
}
|
|
205
257
|
|
|
206
|
-
|
|
258
|
+
const iv = randomBytes(16);
|
|
259
|
+
if (this.#derivedKey) {
|
|
207
260
|
// Encrypt the response using the shared key and iv.
|
|
208
|
-
responsePayload =
|
|
209
|
-
responsePayload,
|
|
210
|
-
this.#encryptKey,
|
|
211
|
-
this.#encryptIV
|
|
212
|
-
);
|
|
261
|
+
responsePayload = aesEncrypt(this.#derivedKey, iv, responsePayload);
|
|
213
262
|
}
|
|
214
263
|
|
|
215
264
|
const invokeResponse = {
|
|
216
|
-
invoke_id: this.#invokeId
|
|
265
|
+
invoke_id: this.#invokeId,
|
|
266
|
+
iv
|
|
217
267
|
};
|
|
218
268
|
|
|
219
269
|
if (this.#isJSON) {
|
|
@@ -248,18 +298,16 @@ class RpcInvocation extends EventEmitter {
|
|
|
248
298
|
endPayload = tunnelMethod.requestSerialize(endPayload);
|
|
249
299
|
}
|
|
250
300
|
|
|
251
|
-
|
|
301
|
+
const iv = randomBytes(16);
|
|
302
|
+
if (this.#derivedKey) {
|
|
252
303
|
// Encrypt the response using the shared key and iv.
|
|
253
|
-
endPayload =
|
|
254
|
-
endPayload,
|
|
255
|
-
this.#encryptKey,
|
|
256
|
-
this.#encryptIV
|
|
257
|
-
);
|
|
304
|
+
endPayload = aesEncrypt(this.#derivedKey, iv, endPayload);
|
|
258
305
|
}
|
|
259
306
|
|
|
260
307
|
const invokeResponse = {
|
|
261
308
|
invoke_id: this.#invokeId,
|
|
262
|
-
server_end: true
|
|
309
|
+
server_end: true,
|
|
310
|
+
iv
|
|
263
311
|
};
|
|
264
312
|
|
|
265
313
|
if (this.#isJSON) {
|