@tdxvolt/volt-client-grpc 0.16.0-beta.9 → 0.18.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/README.md +39 -3
- package/lib/index.cjs +458 -515
- 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 +121 -33
- package/protobuf/tdx/volt_api/volt/v1/terminal_api.proto +42 -0
- package/protobuf/tdx/volt_api/volt/v1/volt.proto +152 -140
- package/protobuf/tdx/volt_api/volt/v1/volt_api.proto +168 -270
- package/protobuf/tdx/volt_api/volt/v1/wire_api.proto +4 -0
- package/src/constants.js +4 -58
- package/src/grpc-call.js +144 -51
- package/src/grpc-utils.js +8 -4
- package/src/rpc-invocation.js +96 -46
- package/src/utils.js +5 -119
- package/src/volt-client-internal.js +109 -78
- package/src/volt-client.js +33 -12
- package/src/volt-connection.js +25 -16
- package/src/volt-credential.js +39 -124
package/lib/index.cjs
CHANGED
|
@@ -4,90 +4,36 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var lodash = require('lodash');
|
|
6
6
|
var ip = require('ip');
|
|
7
|
-
var
|
|
8
|
-
var bs58 = require('bs58');
|
|
7
|
+
var js = require('@tdxvolt/volt-client-web/js');
|
|
9
8
|
var debug = require('debug');
|
|
10
9
|
var EventEmitter = require('events');
|
|
11
10
|
var fs = require('fs');
|
|
12
|
-
var jwt = require('jsonwebtoken');
|
|
13
|
-
var crypto = require('crypto');
|
|
14
11
|
var path = require('path');
|
|
15
12
|
var protoLoader = require('@grpc/proto-loader');
|
|
16
13
|
var url = require('url');
|
|
17
14
|
var bent = require('bent');
|
|
15
|
+
var jwt = require('jsonwebtoken');
|
|
18
16
|
var uuid = require('uuid');
|
|
19
17
|
|
|
20
18
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
21
19
|
|
|
22
20
|
var lodash__default = /*#__PURE__*/_interopDefaultLegacy(lodash);
|
|
23
21
|
var ip__default = /*#__PURE__*/_interopDefaultLegacy(ip);
|
|
24
|
-
var forge__default = /*#__PURE__*/_interopDefaultLegacy(forge);
|
|
25
|
-
var bs58__default = /*#__PURE__*/_interopDefaultLegacy(bs58);
|
|
26
22
|
var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
|
|
27
23
|
var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
|
|
28
24
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
29
|
-
var jwt__default = /*#__PURE__*/_interopDefaultLegacy(jwt);
|
|
30
|
-
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
|
|
31
25
|
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
32
26
|
var protoLoader__default = /*#__PURE__*/_interopDefaultLegacy(protoLoader);
|
|
33
27
|
var bent__default = /*#__PURE__*/_interopDefaultLegacy(bent);
|
|
28
|
+
var jwt__default = /*#__PURE__*/_interopDefaultLegacy(jwt);
|
|
34
29
|
|
|
35
|
-
// eslint-disable-next-line import/prefer-default-export
|
|
36
30
|
const constants = {
|
|
37
|
-
authTokenName: "
|
|
38
|
-
|
|
39
|
-
add: "ADD",
|
|
40
|
-
remove: "REMOVE",
|
|
41
|
-
update: "UPDATE"
|
|
42
|
-
},
|
|
43
|
-
crypto: {
|
|
44
|
-
subjectDefaults: [
|
|
45
|
-
{
|
|
46
|
-
name: "countryName",
|
|
47
|
-
value: "GB"
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: "localityName",
|
|
51
|
-
value: "Southampton"
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: "organizationName",
|
|
55
|
-
value: "nquiringminds Ltd"
|
|
56
|
-
}
|
|
57
|
-
],
|
|
58
|
-
userIdOID: "0.9.2342.19200300.100.1.1"
|
|
59
|
-
},
|
|
60
|
-
defaultDIDHostName: "cloud.tdxvolt.com",
|
|
31
|
+
authTokenName: "volt-token",
|
|
32
|
+
defaultDIDHostName: "tdxvolt.com",
|
|
61
33
|
didServiceType: {
|
|
62
|
-
voltConfig: "
|
|
63
|
-
},
|
|
64
|
-
identityType: {
|
|
65
|
-
did: "did",
|
|
66
|
-
volt: "volt"
|
|
34
|
+
voltConfig: "volt-discovery"
|
|
67
35
|
},
|
|
68
|
-
onlineStatus: {
|
|
69
|
-
offline: "offline",
|
|
70
|
-
online: "online",
|
|
71
|
-
unknown: "unknown"
|
|
72
|
-
},
|
|
73
|
-
publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
|
|
74
|
-
privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
|
|
75
36
|
privateEncryptedKeyPrefix: "-----BEGIN ENCRYPTED PRIVATE KEY-----",
|
|
76
|
-
resource: {
|
|
77
|
-
identity: "ident",
|
|
78
|
-
identityView: "ident-view",
|
|
79
|
-
proxy: "proxy",
|
|
80
|
-
proxyView: "proxy-view",
|
|
81
|
-
vc: "vc",
|
|
82
|
-
vcView: "vc-view"
|
|
83
|
-
},
|
|
84
|
-
voltStatus: {
|
|
85
|
-
pending: "",
|
|
86
|
-
online: "online",
|
|
87
|
-
offline: "offline",
|
|
88
|
-
deleted: "deleted",
|
|
89
|
-
revoked: "revoked"
|
|
90
|
-
},
|
|
91
37
|
/**
|
|
92
38
|
* These top-level service types should map to a protobuf definition
|
|
93
39
|
* in @tdxvolt/volt-proto.
|
|
@@ -100,16 +46,9 @@ const constants = {
|
|
|
100
46
|
relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
|
|
101
47
|
voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
|
|
102
48
|
wireAPI: "tdx.volt_api.volt.v1.WireAPI"
|
|
103
|
-
}
|
|
104
|
-
tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
|
|
105
|
-
tunnelPingInterval: 10000,
|
|
106
|
-
tunnelPingTimeout: 2500,
|
|
107
|
-
tunnelReconnectAfter: 30000,
|
|
108
|
-
tunnelTimeoutInterval: 10000 * 2 // This must always be greater than `tunnelPingInterval`.
|
|
49
|
+
}
|
|
109
50
|
};
|
|
110
51
|
|
|
111
|
-
const { pki: pki$2 } = forge__default["default"];
|
|
112
|
-
|
|
113
52
|
const createSecureContextOptions = (cryptoOptions) => {
|
|
114
53
|
const tlsOptions = {
|
|
115
54
|
cert: Buffer.from(cryptoOptions.cert),
|
|
@@ -139,53 +78,7 @@ const getServiceAddress = (mdnsService) => {
|
|
|
139
78
|
return address;
|
|
140
79
|
};
|
|
141
80
|
|
|
142
|
-
const
|
|
143
|
-
return pki$2.privateKeyToPem(keyPair.privateKey);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const publicKeyToPem = (keyPair) => {
|
|
147
|
-
return pki$2.publicKeyToPem(keyPair.publicKey);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const deserialisePEM = (pem) => {
|
|
151
|
-
return pem.replace(/\\r\\n/g, "\r\n");
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const removeCarriageReturn = (inp) => {
|
|
155
|
-
return inp.replace(/\r\n/g, "\n");
|
|
156
|
-
};
|
|
157
|
-
|
|
158
|
-
const createBase58Hash = (inp) => {
|
|
159
|
-
const md = forge__default["default"].md.sha256.create();
|
|
160
|
-
md.update(inp);
|
|
161
|
-
const hashBuf = Buffer.from(md.digest().getBytes(), "binary");
|
|
162
|
-
return bs58__default["default"].encode(hashBuf);
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const createPublicKeyFingerprint = (key) => {
|
|
166
|
-
// Remove carriage returns before creating hash to bring into line with openSSL / volt format.
|
|
167
|
-
return createBase58Hash(removeCarriageReturn(key));
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const base64ToBase64Url = (base64) => {
|
|
171
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
const signBase64 = (key, msg) => {
|
|
175
|
-
const md = forge__default["default"].md.sha256.create();
|
|
176
|
-
md.update(msg);
|
|
177
|
-
const signature = key.sign(md);
|
|
178
|
-
const base64Standard = forge__default["default"].util.encode64(signature);
|
|
179
|
-
return base64ToBase64Url(base64Standard);
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const verifyBase64 = (key, msg, signature) => {
|
|
183
|
-
const md = forge__default["default"].md.sha256.create();
|
|
184
|
-
md.update(msg);
|
|
185
|
-
return key.verify(md.digest().bytes(), forge__default["default"].util.decode64(signature));
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
const getDIDResolutionURL = (didIn) => {
|
|
81
|
+
const getDIDResolutionURL = (didIn, registryUrl = "") => {
|
|
189
82
|
const did = didIn.toLowerCase().split(":");
|
|
190
83
|
if (did.length < 3) {
|
|
191
84
|
return "";
|
|
@@ -195,97 +88,20 @@ const getDIDResolutionURL = (didIn) => {
|
|
|
195
88
|
return "";
|
|
196
89
|
}
|
|
197
90
|
|
|
198
|
-
if (did[1] !== "
|
|
91
|
+
if (did[1] !== "volt") {
|
|
199
92
|
return "";
|
|
200
93
|
}
|
|
201
94
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (did.length === 3) {
|
|
205
|
-
hostName = constants.defaultDIDHostName;
|
|
206
|
-
didGUID = did[2];
|
|
207
|
-
} else {
|
|
208
|
-
hostName = did[2];
|
|
209
|
-
didGUID = did[3];
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return `https://${hostName}/api/identity/${didGUID}`;
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
const forgePrivateKeyToPem = (decryptedKey) => {
|
|
216
|
-
// Convert a Forge private key to an ASN.1 RSAPrivateKey
|
|
217
|
-
const asnPrivateKey = pki$2.privateKeyToAsn1(decryptedKey);
|
|
218
|
-
|
|
219
|
-
// Wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
|
|
220
|
-
const privateKeyInfo = pki$2.wrapRsaPrivateKey(asnPrivateKey);
|
|
221
|
-
|
|
222
|
-
// Convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
|
|
223
|
-
return pki$2.privateKeyInfoToPem(privateKeyInfo);
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
const createRSAKey = (passphrase = "") => {
|
|
227
|
-
return new Promise((resolve, reject) => {
|
|
228
|
-
pki$2.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
|
|
229
|
-
if (err) {
|
|
230
|
-
reject(err);
|
|
231
|
-
} else {
|
|
232
|
-
let pem;
|
|
233
|
-
if (passphrase) {
|
|
234
|
-
pem = pki$2.encryptRsaPrivateKey(keypair.privateKey, passphrase);
|
|
235
|
-
} else {
|
|
236
|
-
pem = forgePrivateKeyToPem(keypair.privateKey);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
resolve({ keypair, pem });
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
const sha256Base64 = (msg) => {
|
|
246
|
-
const md = forge__default["default"].md.sha256.create();
|
|
247
|
-
md.update(msg);
|
|
248
|
-
const base64Standard = forge__default["default"].util.encode64(md.digest().bytes());
|
|
249
|
-
return base64ToBase64Url(base64Standard);
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
const decryptPrivateKey = (pem, passphrase) => {
|
|
253
|
-
const decryptedKey = pki$2.decryptRsaPrivateKey(pem, passphrase);
|
|
254
|
-
return forgePrivateKeyToPem(decryptedKey);
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
const jwsSignature = (signature) => {
|
|
258
|
-
//
|
|
259
|
-
// Use an unattached payload JWS signature for the DID document proof.
|
|
260
|
-
// This is a JWS with an empty payload and a detached payload digest.
|
|
261
|
-
//
|
|
262
|
-
// (https://tools.ietf.org/html/rfc7797#appendix-F) as used in the VC spec
|
|
263
|
-
// (https://www.w3.org/TR/vc-data-model/).
|
|
264
|
-
//
|
|
265
|
-
const jwsHeader = base64ToBase64Url(
|
|
266
|
-
forge__default["default"].util.encode64('{"alg":"RS256","b64": false}')
|
|
267
|
-
);
|
|
268
|
-
return jwsHeader + ".." + signature;
|
|
95
|
+
const lookupUrl = registryUrl || `https://${constants.defaultDIDHostName}`;
|
|
96
|
+
return `${lookupUrl}/did/${didIn}`;
|
|
269
97
|
};
|
|
270
98
|
|
|
271
99
|
var utils = /*#__PURE__*/Object.freeze({
|
|
272
100
|
__proto__: null,
|
|
273
101
|
createSecureContextOptions: createSecureContextOptions,
|
|
274
102
|
getServiceAddress: getServiceAddress,
|
|
275
|
-
privateKeyToPem: privateKeyToPem,
|
|
276
|
-
publicKeyToPem: publicKeyToPem,
|
|
277
|
-
deserialisePEM: deserialisePEM,
|
|
278
|
-
removeCarriageReturn: removeCarriageReturn,
|
|
279
|
-
createBase58Hash: createBase58Hash,
|
|
280
|
-
createPublicKeyFingerprint: createPublicKeyFingerprint,
|
|
281
|
-
base64ToBase64Url: base64ToBase64Url,
|
|
282
|
-
signBase64: signBase64,
|
|
283
|
-
verifyBase64: verifyBase64,
|
|
284
103
|
getDIDResolutionURL: getDIDResolutionURL,
|
|
285
|
-
|
|
286
|
-
sha256Base64: sha256Base64,
|
|
287
|
-
decryptPrivateKey: decryptPrivateKey,
|
|
288
|
-
jwsSignature: jwsSignature
|
|
104
|
+
crypto: js.voltUtils
|
|
289
105
|
});
|
|
290
106
|
|
|
291
107
|
/* eslint-disable no-underscore-dangle */
|
|
@@ -307,8 +123,11 @@ function createClient(
|
|
|
307
123
|
) {
|
|
308
124
|
let grpcCredentials;
|
|
309
125
|
if (credentials) {
|
|
310
|
-
const
|
|
311
|
-
const
|
|
126
|
+
const relaying = !!credentials.config?.volt?.relay;
|
|
127
|
+
const ca = relaying
|
|
128
|
+
? credentials.config?.volt?.relay?.ca_pem
|
|
129
|
+
: credentials.cache.ca;
|
|
130
|
+
const cert = relaying ? "" : credentials.cache.cert;
|
|
312
131
|
const { key } = credentials.cache;
|
|
313
132
|
|
|
314
133
|
if (key && cert) {
|
|
@@ -320,8 +139,9 @@ function createClient(
|
|
|
320
139
|
tlsOptions.cert
|
|
321
140
|
);
|
|
322
141
|
} else {
|
|
323
|
-
// Cache has no private key, this implies we are connecting without supplying a client certificate.
|
|
324
|
-
//
|
|
142
|
+
// Cache has no private key or certificate, this implies we are connecting without supplying a client certificate.
|
|
143
|
+
// We might be connecting to a relay, or making an initial Authenticate call.
|
|
144
|
+
// Simply connect using the CA certificate, and the a JWT will be used to authenticate.
|
|
325
145
|
grpcCredentials = grpc.credentials.createSsl(Buffer.from(ca));
|
|
326
146
|
}
|
|
327
147
|
} else {
|
|
@@ -527,10 +347,16 @@ var grpcUtils = /*#__PURE__*/Object.freeze({
|
|
|
527
347
|
|
|
528
348
|
/* eslint-disable no-underscore-dangle */
|
|
529
349
|
|
|
530
|
-
const {
|
|
350
|
+
const {
|
|
351
|
+
createSigningKey,
|
|
352
|
+
fingerprintFromPem,
|
|
353
|
+
getIdentityToken,
|
|
354
|
+
keyFromPem: keyFromPem$2,
|
|
355
|
+
privateKeyToPem,
|
|
356
|
+
publicKeyToPem: publicKeyToPem$2
|
|
357
|
+
} = js.voltUtils;
|
|
358
|
+
|
|
531
359
|
const log$5 = debug__default["default"]("volt-client-grpc:volt-credential");
|
|
532
|
-
const aesAlgorithm = "aes-256-cbc";
|
|
533
|
-
const rs256Algorithm = "RS256";
|
|
534
360
|
|
|
535
361
|
class VoltCredential {
|
|
536
362
|
constructor(config, configPath) {
|
|
@@ -554,14 +380,13 @@ class VoltCredential {
|
|
|
554
380
|
}
|
|
555
381
|
|
|
556
382
|
// Need to unencrypt the private key.
|
|
557
|
-
const decrypted =
|
|
558
|
-
|
|
559
|
-
config.p
|
|
560
|
-
);
|
|
383
|
+
const decrypted = keyFromPem$2(this._cryptoCache.key, config.p);
|
|
384
|
+
|
|
561
385
|
if (!decrypted) {
|
|
562
386
|
throw new Error("failed to decrypt key - check passphrase");
|
|
563
387
|
}
|
|
564
|
-
|
|
388
|
+
|
|
389
|
+
this._cryptoCache.key = privateKeyToPem(decrypted.privateKey);
|
|
565
390
|
}
|
|
566
391
|
|
|
567
392
|
if (this._voltConfig.ca_pem) {
|
|
@@ -570,14 +395,11 @@ class VoltCredential {
|
|
|
570
395
|
this._cryptoCache.ca = this._voltConfig.ca_pem;
|
|
571
396
|
}
|
|
572
397
|
|
|
573
|
-
// Pre-cache the target Volt public key by extracting it from the signing certificate.
|
|
574
|
-
const voltCert = forge__default["default"].pki.certificateFromPem(this._voltConfig.ca_pem);
|
|
575
|
-
|
|
576
398
|
// Extract Volt public key (used for encrypting Relay payloads).
|
|
577
|
-
this._voltPublicKey =
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
);
|
|
399
|
+
this._voltPublicKey = this._voltConfig.public_key;
|
|
400
|
+
|
|
401
|
+
const voltKey = keyFromPem$2(this._voltPublicKey).publicKey;
|
|
402
|
+
this._voltFingerprint = fingerprintFromPem(voltKey);
|
|
581
403
|
}
|
|
582
404
|
}
|
|
583
405
|
|
|
@@ -598,7 +420,7 @@ class VoltCredential {
|
|
|
598
420
|
}
|
|
599
421
|
|
|
600
422
|
get identityId() {
|
|
601
|
-
return this._cryptoCache.
|
|
423
|
+
return this._cryptoCache.identity_did;
|
|
602
424
|
}
|
|
603
425
|
|
|
604
426
|
get voltPublicKey() {
|
|
@@ -606,7 +428,7 @@ class VoltCredential {
|
|
|
606
428
|
}
|
|
607
429
|
|
|
608
430
|
get publicKey() {
|
|
609
|
-
return
|
|
431
|
+
return publicKeyToPem$2(this.getKey().publicKey);
|
|
610
432
|
}
|
|
611
433
|
|
|
612
434
|
get voltFingerprint() {
|
|
@@ -614,9 +436,8 @@ class VoltCredential {
|
|
|
614
436
|
}
|
|
615
437
|
|
|
616
438
|
getKey() {
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
return { privateKey, publicKey };
|
|
439
|
+
const key = keyFromPem$2(this._cryptoCache.key);
|
|
440
|
+
return key;
|
|
620
441
|
}
|
|
621
442
|
|
|
622
443
|
/**
|
|
@@ -629,16 +450,10 @@ class VoltCredential {
|
|
|
629
450
|
return Promise.resolve(this.getKey());
|
|
630
451
|
} else {
|
|
631
452
|
log$5("creating key");
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
return keyInfo.keypair;
|
|
637
|
-
})
|
|
638
|
-
.catch((err) => {
|
|
639
|
-
log$5("failure creating key [%s]", err.message);
|
|
640
|
-
return Promise.reject(err);
|
|
641
|
-
});
|
|
453
|
+
this._cryptoCache.key = createSigningKey();
|
|
454
|
+
const key = keyFromPem$2(this._cryptoCache.key);
|
|
455
|
+
log$5("successfully created key");
|
|
456
|
+
return Promise.resolve(key);
|
|
642
457
|
}
|
|
643
458
|
}
|
|
644
459
|
|
|
@@ -663,55 +478,6 @@ class VoltCredential {
|
|
|
663
478
|
return !!this._cryptoCache.cert;
|
|
664
479
|
}
|
|
665
480
|
|
|
666
|
-
/**
|
|
667
|
-
* Signs the identity resource id using the private key.
|
|
668
|
-
* @param {*} audience the token audience (usually the target volt)
|
|
669
|
-
* @param {*} tunnelling flag indicating if the token is required for a tunnelled connection
|
|
670
|
-
* @param {*} ttl time to live in seconds (default to 1 minute)
|
|
671
|
-
*/
|
|
672
|
-
getIdentityToken(audience, publicKey, tunnelling = false, ttl = 60) {
|
|
673
|
-
if (!this._cryptoCache.key) {
|
|
674
|
-
throw new Error("crypto cache invalid");
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
const keyPair = this.getKey();
|
|
678
|
-
const publicKeyPem = pki$1.publicKeyToPem(keyPair.publicKey);
|
|
679
|
-
const base58Key = createPublicKeyFingerprint(publicKeyPem);
|
|
680
|
-
|
|
681
|
-
log$5("base58 key is %s", base58Key);
|
|
682
|
-
|
|
683
|
-
// Allow TTL either side of the current time.
|
|
684
|
-
// @todo - fix with server time sync on volt connection.
|
|
685
|
-
const payload = {
|
|
686
|
-
aud: audience,
|
|
687
|
-
iat: Math.floor(Date.now() / 1000) - ttl,
|
|
688
|
-
exp: Math.floor(Date.now() / 1000) + ttl,
|
|
689
|
-
sub: this._cryptoCache.session_id
|
|
690
|
-
};
|
|
691
|
-
|
|
692
|
-
let sharedKey;
|
|
693
|
-
if (tunnelling) {
|
|
694
|
-
// Initialise the Relay encryption key.
|
|
695
|
-
sharedKey = VoltCredential.aesCreateKey();
|
|
696
|
-
|
|
697
|
-
// Encrypt the key details using the target Volt public key and include this in the JWT payload.
|
|
698
|
-
payload.sk = VoltCredential.rsaEncrypt(publicKey, sharedKey.key);
|
|
699
|
-
payload.iv = VoltCredential.rsaEncrypt(publicKey, sharedKey.iv);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
// Sign synchronously.
|
|
703
|
-
const token = jwt__default["default"].sign(payload, this._cryptoCache.key, {
|
|
704
|
-
algorithm: rs256Algorithm,
|
|
705
|
-
keyid: base58Key
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
// Return the token along with any encryption key details.
|
|
709
|
-
return {
|
|
710
|
-
token,
|
|
711
|
-
sharedKey
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
|
|
715
481
|
/**
|
|
716
482
|
* Creates the metadata required for a grpc call, including the Volt authentication token
|
|
717
483
|
* and optionally tunnel encryption parameters.
|
|
@@ -722,9 +488,12 @@ class VoltCredential {
|
|
|
722
488
|
* @returns
|
|
723
489
|
*/
|
|
724
490
|
getIdentityMetadata(grpc, audience, publicKey, tunnelling = false, ttl = 60) {
|
|
725
|
-
const
|
|
491
|
+
const key = keyFromPem$2(this._cryptoCache.key);
|
|
492
|
+
|
|
493
|
+
const identityToken = getIdentityToken(
|
|
494
|
+
this.sessionId,
|
|
495
|
+
key,
|
|
726
496
|
audience || this._voltConfig.id,
|
|
727
|
-
publicKey,
|
|
728
497
|
tunnelling,
|
|
729
498
|
ttl
|
|
730
499
|
);
|
|
@@ -734,48 +503,18 @@ class VoltCredential {
|
|
|
734
503
|
}
|
|
735
504
|
|
|
736
505
|
saveCache() {
|
|
506
|
+
const clone = { ...this._config, credential: { ...this._cryptoCache } };
|
|
507
|
+
if (this._config.p) {
|
|
508
|
+
// A passphrase option exists => encrypt the key before writing the cache file.
|
|
509
|
+
const key = keyFromPem$2(this._cryptoCache.key);
|
|
510
|
+
clone.credential.key = privateKeyToPem(key.privateKey, this._config.p);
|
|
511
|
+
}
|
|
512
|
+
|
|
737
513
|
if (this._persistCache) {
|
|
738
|
-
const clone = { ...this._config, credential: { ...this._cryptoCache } };
|
|
739
|
-
if (this._config.p) {
|
|
740
|
-
// A passphrase option exists => encrypt the key before writing the cache file.
|
|
741
|
-
const privateKey = pki$1.privateKeyFromPem(this._cryptoCache.key);
|
|
742
|
-
clone.credential.key = pki$1.encryptRsaPrivateKey(
|
|
743
|
-
privateKey,
|
|
744
|
-
this._config.p
|
|
745
|
-
);
|
|
746
|
-
}
|
|
747
514
|
fs__default["default"].writeFileSync(this._configPath, JSON.stringify(clone, null, 2));
|
|
748
515
|
}
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
static aesCreateKey() {
|
|
752
|
-
// Generate random key for the AES-256 algorithm (32 bytes = 256 bits).
|
|
753
|
-
const key = crypto__default["default"].randomBytes(32);
|
|
754
|
-
const iv = crypto__default["default"].randomBytes(16);
|
|
755
|
-
return { key, iv };
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
static aesEncrypt(buffer, key, iv) {
|
|
759
|
-
const cipher = crypto__default["default"].createCipheriv(aesAlgorithm, key, iv);
|
|
760
|
-
const crypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
|
|
761
|
-
return crypted;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
static aesDecrypt(buffer, key, iv) {
|
|
765
|
-
const cipher = crypto__default["default"].createDecipheriv(aesAlgorithm, key, iv);
|
|
766
|
-
const decrypted = Buffer.concat([cipher.update(buffer), cipher.final()]);
|
|
767
|
-
return decrypted;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
static rsaEncrypt(key, buffer) {
|
|
771
|
-
const cipher = crypto__default["default"].publicEncrypt(key, buffer);
|
|
772
|
-
return cipher.toString("base64url");
|
|
773
|
-
}
|
|
774
516
|
|
|
775
|
-
|
|
776
|
-
const keyObj = crypto__default["default"].createPrivateKey(key);
|
|
777
|
-
const cipher = Buffer.from(buffer, "base64url");
|
|
778
|
-
return Buffer.from(crypto__default["default"].privateDecrypt(keyObj, cipher));
|
|
517
|
+
return clone;
|
|
779
518
|
}
|
|
780
519
|
}
|
|
781
520
|
|
|
@@ -907,6 +646,18 @@ var protoUtils = /*#__PURE__*/Object.freeze({
|
|
|
907
646
|
|
|
908
647
|
/* eslint-disable no-underscore-dangle */
|
|
909
648
|
|
|
649
|
+
const {
|
|
650
|
+
aesCreateKey,
|
|
651
|
+
aesEncrypt: aesEncrypt$1,
|
|
652
|
+
aesDecrypt: aesDecrypt$1,
|
|
653
|
+
toBase64,
|
|
654
|
+
formatPEM: formatPEM$1,
|
|
655
|
+
keyFromPem: keyFromPem$1,
|
|
656
|
+
verify,
|
|
657
|
+
deriveSharedKey: deriveSharedKey$1,
|
|
658
|
+
x25519: x25519$1
|
|
659
|
+
} = js.voltUtils;
|
|
660
|
+
|
|
910
661
|
const log$4 = debug__default["default"]("volt-client-grpc:grpc-call");
|
|
911
662
|
|
|
912
663
|
function _prepareInvokeRequest(
|
|
@@ -918,6 +669,21 @@ function _prepareInvokeRequest(
|
|
|
918
669
|
this._service?.service_description.host_type ===
|
|
919
670
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
920
671
|
|
|
672
|
+
const relaying = this._voltClient.isVoltRelay || isServiceRelayed;
|
|
673
|
+
|
|
674
|
+
let targetDID = [];
|
|
675
|
+
if (this._voltClient.isVoltRelay) {
|
|
676
|
+
targetDID.push(this._voltClient.voltConfig.id);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (isServiceRelayed) {
|
|
680
|
+
// Add another relay hop if the target service is relayed.
|
|
681
|
+
log$4("target service is relayed");
|
|
682
|
+
targetDID.push(this._service.service_description.host_client_id);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
log$4("target is %j", targetDID);
|
|
686
|
+
|
|
921
687
|
// Use the service's host client id and public key if the service is relayed.
|
|
922
688
|
const methodToken = this._voltClient.credential.getIdentityMetadata(
|
|
923
689
|
this._voltClient.grpc,
|
|
@@ -925,17 +691,52 @@ function _prepareInvokeRequest(
|
|
|
925
691
|
this._voltClient.voltConfig.id,
|
|
926
692
|
this._service?.service_description.host_public_key ||
|
|
927
693
|
this._voltClient.credential.voltPublicKey,
|
|
928
|
-
|
|
694
|
+
relaying
|
|
929
695
|
);
|
|
930
696
|
|
|
931
697
|
let invokeRequest;
|
|
932
698
|
let callMethod = method;
|
|
933
699
|
|
|
934
|
-
if (
|
|
700
|
+
if (relaying) {
|
|
935
701
|
//
|
|
936
702
|
// If the volt connection is via a relay (or the target service is relayed),
|
|
937
703
|
// we wrap the rpc in a RemoteRequest message and send it via a call to Invoke().
|
|
938
704
|
//
|
|
705
|
+
// Replace the target method with a call to Invoke on the relay Volt.
|
|
706
|
+
//
|
|
707
|
+
callMethod = "Invoke";
|
|
708
|
+
|
|
709
|
+
// But first we need to perform a key exchange with the target service.
|
|
710
|
+
//
|
|
711
|
+
if (!this._encryptionKey) {
|
|
712
|
+
// We haven't exchanged keys yet, so queue the payload.
|
|
713
|
+
this._pendingRequests.push(request);
|
|
714
|
+
|
|
715
|
+
if (this._keyExchangePending) {
|
|
716
|
+
// We've already sent the key exchange request, so do nothing.
|
|
717
|
+
log$4("key exchange pending, not sending payload");
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
this._keyExchangePending = true;
|
|
722
|
+
|
|
723
|
+
// Send the key exchange request.
|
|
724
|
+
invokeRequest = {
|
|
725
|
+
token: methodToken.token,
|
|
726
|
+
target_did: targetDID,
|
|
727
|
+
target_service_id: isServiceRelayed
|
|
728
|
+
? this._service.service_description.host_service_id
|
|
729
|
+
: undefined
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
return {
|
|
733
|
+
request: invokeRequest,
|
|
734
|
+
method: callMethod,
|
|
735
|
+
methodName: method,
|
|
736
|
+
meta: methodToken
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
|
|
939
740
|
let requestPayload;
|
|
940
741
|
|
|
941
742
|
// Serialise the rpc request.
|
|
@@ -955,45 +756,24 @@ function _prepareInvokeRequest(
|
|
|
955
756
|
method_invoke: {
|
|
956
757
|
method_name: this._grpcClient[method].path,
|
|
957
758
|
method_type: methodType,
|
|
958
|
-
token: methodToken.token,
|
|
959
759
|
request: requestPayload
|
|
960
760
|
}
|
|
961
761
|
};
|
|
962
762
|
|
|
963
|
-
let targetDID = [];
|
|
964
763
|
let invokePayload;
|
|
965
|
-
|
|
966
|
-
// When sending via a relay we need to encrypt the payload.
|
|
967
|
-
invokePayload = VoltCredential.aesEncrypt(
|
|
968
|
-
tunnelMethod.responseSerialize(tunnelResp),
|
|
969
|
-
methodToken.sharedKey.key,
|
|
970
|
-
methodToken.sharedKey.iv
|
|
971
|
-
);
|
|
972
|
-
} else {
|
|
973
|
-
invokePayload = tunnelMethod.responseSerialize(tunnelResp);
|
|
974
|
-
}
|
|
764
|
+
const iv = aesCreateKey().iv;
|
|
975
765
|
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
log$4("target service is relayed");
|
|
983
|
-
targetDID.push(this._service.service_description.host_client_id);
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
log$4("target is %j", targetDID);
|
|
766
|
+
// When sending via a relay we need to encrypt the payload.
|
|
767
|
+
invokePayload = aesEncrypt$1(
|
|
768
|
+
this._encryptionKey,
|
|
769
|
+
iv,
|
|
770
|
+
tunnelMethod.responseSerialize(tunnelResp)
|
|
771
|
+
);
|
|
987
772
|
|
|
988
773
|
invokeRequest = {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
payload: invokePayload,
|
|
992
|
-
target_service_id: isServiceRelayed ? this._service.id : undefined
|
|
774
|
+
iv,
|
|
775
|
+
payload: invokePayload
|
|
993
776
|
};
|
|
994
|
-
|
|
995
|
-
// Replace the target method with a call to Invoke on the relay Volt.
|
|
996
|
-
callMethod = "Invoke";
|
|
997
777
|
} else {
|
|
998
778
|
invokeRequest = request;
|
|
999
779
|
}
|
|
@@ -1013,7 +793,10 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
1013
793
|
this._service?.service_description.host_type ===
|
|
1014
794
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
1015
795
|
|
|
796
|
+
let iv;
|
|
1016
797
|
if (this._voltClient.isVoltRelay || isServiceRelayed) {
|
|
798
|
+
iv = aesCreateKey().iv;
|
|
799
|
+
|
|
1017
800
|
const requestPayload =
|
|
1018
801
|
this._grpcClient[invokeInfo.methodName].requestSerialize(request);
|
|
1019
802
|
|
|
@@ -1026,13 +809,14 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
1026
809
|
}
|
|
1027
810
|
};
|
|
1028
811
|
|
|
1029
|
-
const invokePayload =
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
812
|
+
const invokePayload = aesEncrypt$1(
|
|
813
|
+
this._encryptionKey,
|
|
814
|
+
iv,
|
|
815
|
+
tunnelMethod.responseSerialize(tunnelResp)
|
|
1033
816
|
);
|
|
1034
817
|
|
|
1035
818
|
payloadRequest = {
|
|
819
|
+
iv,
|
|
1036
820
|
payload: invokePayload
|
|
1037
821
|
};
|
|
1038
822
|
} else {
|
|
@@ -1051,13 +835,38 @@ function _parseResponse(method, meta, response) {
|
|
|
1051
835
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
1052
836
|
|
|
1053
837
|
if (this._voltClient.isVoltRelay || isServiceRelayed) {
|
|
1054
|
-
if (response.
|
|
838
|
+
if (response.key_exchange) {
|
|
839
|
+
const encryptionKey = response.key_exchange.encryption_key;
|
|
840
|
+
const nonce = response.key_exchange.nonce;
|
|
841
|
+
const signature = response.key_exchange.signature;
|
|
842
|
+
|
|
843
|
+
// The signed message is the encryption key and the nonce.
|
|
844
|
+
const message = new Uint8Array(encryptionKey.length + nonce.length);
|
|
845
|
+
message.set(encryptionKey);
|
|
846
|
+
message.set(nonce, encryptionKey.length);
|
|
847
|
+
|
|
848
|
+
// Check the signature using the target service public key.
|
|
849
|
+
const targetKey = keyFromPem$1(
|
|
850
|
+
this._service?.service_description?.host_public_key ||
|
|
851
|
+
this._voltClient.credential.voltPublicKey
|
|
852
|
+
).publicKey;
|
|
853
|
+
|
|
854
|
+
if (!verify(targetKey, message, signature)) {
|
|
855
|
+
throw new Error("signature verification failed");
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
invokeResponse = {
|
|
859
|
+
key_exchange: {
|
|
860
|
+
encryption_key: formatPEM$1(toBase64(encryptionKey), "PUBLIC KEY")
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
} else if (response.payload) {
|
|
1055
864
|
let decryptedPayload;
|
|
1056
|
-
if (
|
|
1057
|
-
decryptedPayload =
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
865
|
+
if (this._encryptionKey) {
|
|
866
|
+
decryptedPayload = aesDecrypt$1(
|
|
867
|
+
this._encryptionKey,
|
|
868
|
+
response.iv,
|
|
869
|
+
response.payload
|
|
1061
870
|
);
|
|
1062
871
|
} else {
|
|
1063
872
|
decryptedPayload = response.payload;
|
|
@@ -1097,11 +906,11 @@ function _parseResponse(method, meta, response) {
|
|
|
1097
906
|
invokeResponse.methodId = response.invoke_id;
|
|
1098
907
|
log$4("started method %d for %s", invokeResponse.methodId, method);
|
|
1099
908
|
}
|
|
1100
|
-
} else if (this._voltClient.isRemote &&
|
|
1101
|
-
const decryptedPayload =
|
|
1102
|
-
|
|
1103
|
-
meta.sharedKey.
|
|
1104
|
-
|
|
909
|
+
} else if (this._voltClient.isRemote && this._encryptionKey) {
|
|
910
|
+
const decryptedPayload = aesDecrypt$1(
|
|
911
|
+
this._encryptionKey,
|
|
912
|
+
meta.sharedKey.iv,
|
|
913
|
+
response
|
|
1105
914
|
);
|
|
1106
915
|
invokeResponse = {
|
|
1107
916
|
payload:
|
|
@@ -1122,6 +931,9 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1122
931
|
this._call = null;
|
|
1123
932
|
this._voltClient = voltClient;
|
|
1124
933
|
this._service = service;
|
|
934
|
+
this._keyExchangePending = false;
|
|
935
|
+
this._pendingRequests = [];
|
|
936
|
+
this._encryptionKey = null;
|
|
1125
937
|
}
|
|
1126
938
|
|
|
1127
939
|
start(grpcClient, request) {
|
|
@@ -1150,7 +962,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1150
962
|
}
|
|
1151
963
|
|
|
1152
964
|
this._call = callClient[this._initialRequest.method](
|
|
1153
|
-
this._initialRequest
|
|
965
|
+
this._initialRequest?.meta?.metadata
|
|
1154
966
|
);
|
|
1155
967
|
|
|
1156
968
|
this._call.on("data", (streamResponse) => {
|
|
@@ -1161,7 +973,25 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1161
973
|
this._initialRequest.meta,
|
|
1162
974
|
streamResponse
|
|
1163
975
|
);
|
|
1164
|
-
if (parsedResponse.
|
|
976
|
+
if (parsedResponse.key_exchange) {
|
|
977
|
+
const peerKey = keyFromPem$1(
|
|
978
|
+
parsedResponse.key_exchange.encryption_key
|
|
979
|
+
).publicKey;
|
|
980
|
+
|
|
981
|
+
this._encryptionKey = deriveSharedKey$1(
|
|
982
|
+
this._initialRequest.meta.sharedKey.key,
|
|
983
|
+
peerKey,
|
|
984
|
+
x25519$1
|
|
985
|
+
);
|
|
986
|
+
|
|
987
|
+
// We've exchanged keys, so send any queued payloads on the next tick.
|
|
988
|
+
process.nextTick(() => {
|
|
989
|
+
this._pendingRequests.forEach((pendingRequest) => {
|
|
990
|
+
this.write(pendingRequest);
|
|
991
|
+
});
|
|
992
|
+
this._pendingRequests = [];
|
|
993
|
+
});
|
|
994
|
+
} else if (parsedResponse.payload) {
|
|
1165
995
|
// Interpret a non-empty status message as an error.
|
|
1166
996
|
if (parsedResponse.payload?.status?.message) {
|
|
1167
997
|
this.emit(
|
|
@@ -1236,7 +1066,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1236
1066
|
if (!this._call) {
|
|
1237
1067
|
log$4("ERROR - call not initialised");
|
|
1238
1068
|
throw new Error("call not initialised");
|
|
1239
|
-
} else if (this._initialRequest) {
|
|
1069
|
+
} else if (this._initialRequest && !this._keyExchangePending) {
|
|
1240
1070
|
const payloadRequest = _preparePayloadRequest.call(
|
|
1241
1071
|
this,
|
|
1242
1072
|
request,
|
|
@@ -1244,6 +1074,8 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1244
1074
|
);
|
|
1245
1075
|
return this._call.write(payloadRequest);
|
|
1246
1076
|
} else {
|
|
1077
|
+
this._keyExchangePending = false;
|
|
1078
|
+
|
|
1247
1079
|
this._initialRequest = _prepareInvokeRequest.call(
|
|
1248
1080
|
this,
|
|
1249
1081
|
request,
|
|
@@ -1273,10 +1105,9 @@ function _cleanUp(immediateReconnect) {
|
|
|
1273
1105
|
this._pingTimeoutTimer = 0;
|
|
1274
1106
|
}
|
|
1275
1107
|
|
|
1276
|
-
if (
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
) {
|
|
1108
|
+
if (this._dying) {
|
|
1109
|
+
clearTimeout(this._reconnectTimer);
|
|
1110
|
+
} else if (immediateReconnect || (this._autoRetry && !this._reconnectTimer)) {
|
|
1280
1111
|
this._reconnectTimer = setTimeout(
|
|
1281
1112
|
() => {
|
|
1282
1113
|
this._reconnectTimer = 0;
|
|
@@ -1302,13 +1133,18 @@ function _doConnect(connectHello) {
|
|
|
1302
1133
|
|
|
1303
1134
|
const grpcClient = this._voltClient.getVoltAPIClient();
|
|
1304
1135
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1136
|
+
// Notify the Volt of our ping interval. If the Volt doesn't receive a ping from us
|
|
1137
|
+
// within this time, it will assume the connection is dead.
|
|
1138
|
+
const defaultHello = {
|
|
1139
|
+
ping_interval: this._pingInterval,
|
|
1140
|
+
timestamp: Date.now()
|
|
1141
|
+
};
|
|
1142
|
+
|
|
1143
|
+
const helloPayload = { ...connectHello } || {};
|
|
1144
|
+
|
|
1145
|
+
helloPayload.hello = { ...defaultHello, ...connectHello?.hello };
|
|
1146
|
+
|
|
1147
|
+
log$3("hello is %j", helloPayload);
|
|
1312
1148
|
|
|
1313
1149
|
this.call.on("error", (err) => {
|
|
1314
1150
|
log$3("error on connection stream [%s]", err.message);
|
|
@@ -1326,17 +1162,22 @@ function _doConnect(connectHello) {
|
|
|
1326
1162
|
clearTimeout(this._pingTimeoutTimer);
|
|
1327
1163
|
}
|
|
1328
1164
|
|
|
1329
|
-
// Schedule the next ping.
|
|
1330
|
-
_startPingTimer.call(this);
|
|
1331
|
-
|
|
1332
1165
|
this.emit("ping", response.ping.timestamp);
|
|
1333
1166
|
break;
|
|
1334
1167
|
}
|
|
1335
1168
|
case "acknowledge": {
|
|
1336
1169
|
if (!this._connectionId) {
|
|
1337
1170
|
this._connectionId = response.acknowledge.connection_id;
|
|
1171
|
+
|
|
1172
|
+
// Set the ping timeout interval to the value returned by the Volt. If we don't
|
|
1173
|
+
// hear from the Volt in this time, we'll assume the connection is dead.
|
|
1174
|
+
this._timeoutInterval = response.acknowledge.ping_interval;
|
|
1175
|
+
|
|
1338
1176
|
log$3("connection id is %s", this._connectionId);
|
|
1339
1177
|
this.emit("connected", this._connectionId);
|
|
1178
|
+
|
|
1179
|
+
// Schedule the next ping.
|
|
1180
|
+
_startPingTimer.call(this);
|
|
1340
1181
|
}
|
|
1341
1182
|
break;
|
|
1342
1183
|
}
|
|
@@ -1364,8 +1205,6 @@ function _doConnect(connectHello) {
|
|
|
1364
1205
|
});
|
|
1365
1206
|
|
|
1366
1207
|
this.call.start(grpcClient, helloPayload);
|
|
1367
|
-
|
|
1368
|
-
_startPingTimer.call(this);
|
|
1369
1208
|
}
|
|
1370
1209
|
|
|
1371
1210
|
function _startPingTimer() {
|
|
@@ -1392,6 +1231,8 @@ function _startPingTimer() {
|
|
|
1392
1231
|
_connectionTimeoutHandler.bind(this),
|
|
1393
1232
|
this._timeoutInterval
|
|
1394
1233
|
);
|
|
1234
|
+
|
|
1235
|
+
_startPingTimer.call(this);
|
|
1395
1236
|
}, this._pingInterval);
|
|
1396
1237
|
}
|
|
1397
1238
|
|
|
@@ -1433,19 +1274,35 @@ class VoltConnection extends EventEmitter__default["default"] {
|
|
|
1433
1274
|
}
|
|
1434
1275
|
}
|
|
1435
1276
|
|
|
1277
|
+
const {
|
|
1278
|
+
aesDecrypt,
|
|
1279
|
+
aesEncrypt,
|
|
1280
|
+
createEncryptionKey,
|
|
1281
|
+
deriveSharedKey,
|
|
1282
|
+
formatPEM,
|
|
1283
|
+
fromBase64,
|
|
1284
|
+
fromBase64Url,
|
|
1285
|
+
keyFromPem,
|
|
1286
|
+
publicKeyToPem: publicKeyToPem$1,
|
|
1287
|
+
randomBytes,
|
|
1288
|
+
signBase64: signBase64$1,
|
|
1289
|
+
stripPEMHeaders,
|
|
1290
|
+
x25519
|
|
1291
|
+
} = js.voltUtils;
|
|
1292
|
+
|
|
1436
1293
|
const log$2 = debug__default["default"]("rpc-invocation");
|
|
1437
1294
|
|
|
1438
1295
|
class RpcInvocation extends EventEmitter__default["default"] {
|
|
1439
1296
|
#voltClient = null;
|
|
1440
1297
|
#voltConnection = null;
|
|
1441
1298
|
#token = null;
|
|
1442
|
-
#encryptKey = null;
|
|
1443
|
-
#encryptIV = null;
|
|
1444
1299
|
#invokeId = null;
|
|
1445
1300
|
#payload = null;
|
|
1446
1301
|
#isJSON = false;
|
|
1447
1302
|
#methodDescriptor = null;
|
|
1448
1303
|
#methodName = null;
|
|
1304
|
+
#derivedKey = null;
|
|
1305
|
+
#started = false;
|
|
1449
1306
|
|
|
1450
1307
|
constructor(voltClient, voltConnection) {
|
|
1451
1308
|
super();
|
|
@@ -1489,51 +1346,89 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1489
1346
|
this.#methodDescriptor = methodDescriptor;
|
|
1490
1347
|
}
|
|
1491
1348
|
|
|
1492
|
-
|
|
1349
|
+
get started() {
|
|
1350
|
+
return this.#started;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
#keyExchange(token) {
|
|
1493
1354
|
// We don't verify the token at this point, as we don't know the public key.
|
|
1494
1355
|
// The rpc implementation should do the verification using the Volt API (e.g. CanAccessResource).
|
|
1495
1356
|
this.#token = jwt__default["default"].decode(token);
|
|
1496
1357
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1358
|
+
// Store the JWT key id (kid) for use in the response.
|
|
1359
|
+
const callerPublicKeyPem = formatPEM(this.#token.k, "PUBLIC KEY");
|
|
1360
|
+
const callerKey = keyFromPem(callerPublicKeyPem, "", x25519).publicKey;
|
|
1361
|
+
|
|
1362
|
+
const ephemeralKeyPem = createEncryptionKey();
|
|
1363
|
+
const ephemeralKey = keyFromPem(ephemeralKeyPem, "", x25519);
|
|
1364
|
+
const ephemeralPublicKey = fromBase64(
|
|
1365
|
+
stripPEMHeaders(publicKeyToPem$1(ephemeralKey.publicKey, x25519))
|
|
1366
|
+
);
|
|
1367
|
+
|
|
1368
|
+
this.#derivedKey = deriveSharedKey(
|
|
1369
|
+
ephemeralKey.privateKey,
|
|
1370
|
+
callerKey,
|
|
1371
|
+
x25519
|
|
1372
|
+
);
|
|
1373
|
+
|
|
1374
|
+
const nonce = randomBytes(16);
|
|
1508
1375
|
|
|
1509
|
-
|
|
1376
|
+
// The signed message is the encryption key and the nonce.
|
|
1377
|
+
const message = new Uint8Array(ephemeralPublicKey.length + nonce.length);
|
|
1378
|
+
message.set(ephemeralPublicKey);
|
|
1379
|
+
message.set(nonce, ephemeralPublicKey.length);
|
|
1380
|
+
|
|
1381
|
+
const signature = signBase64$1(
|
|
1382
|
+
this.#voltClient.credential.getKey().privateKey,
|
|
1383
|
+
message
|
|
1384
|
+
);
|
|
1385
|
+
|
|
1386
|
+
const keyExchangeResponse = {
|
|
1387
|
+
invoke_id: this.#invokeId,
|
|
1388
|
+
key_exchange: {
|
|
1389
|
+
nonce,
|
|
1390
|
+
encryption_key: ephemeralPublicKey,
|
|
1391
|
+
signature: fromBase64Url(signature)
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
|
|
1395
|
+
this.#voltConnection.send({
|
|
1396
|
+
invoke_response: keyExchangeResponse
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
return Promise.resolve(keyExchangeResponse);
|
|
1510
1400
|
}
|
|
1511
1401
|
|
|
1512
1402
|
initialise(invoke_request) {
|
|
1513
1403
|
this.#invokeId = invoke_request.invoke_id;
|
|
1514
1404
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
return this.parsePayload(invoke_request);
|
|
1519
|
-
})
|
|
1520
|
-
.catch((err) => {
|
|
1405
|
+
if (!this.#derivedKey) {
|
|
1406
|
+
// We don't have the derived key yet, so we need to perform the key exchange.
|
|
1407
|
+
return this.#keyExchange(invoke_request.token).catch((err) => {
|
|
1521
1408
|
log$2("failure initialising invocation: %s", err.message);
|
|
1522
1409
|
return Promise.reject(err);
|
|
1523
1410
|
});
|
|
1411
|
+
} else {
|
|
1412
|
+
// We already have the derived key, so we can just parse the payload.
|
|
1413
|
+
return this.parsePayload(invoke_request);
|
|
1414
|
+
}
|
|
1524
1415
|
}
|
|
1525
1416
|
|
|
1526
1417
|
parsePayload(invoke_request) {
|
|
1418
|
+
this.#started = true;
|
|
1419
|
+
|
|
1527
1420
|
return new Promise((resolve, reject) => {
|
|
1421
|
+
const encryptIV = invoke_request.iv;
|
|
1422
|
+
|
|
1528
1423
|
if (invoke_request.payload) {
|
|
1529
1424
|
let decryptedPayload;
|
|
1530
1425
|
|
|
1531
|
-
if (
|
|
1426
|
+
if (encryptIV) {
|
|
1532
1427
|
// Decrypt the actual payload using the shared key and iv.
|
|
1533
|
-
decryptedPayload =
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1428
|
+
decryptedPayload = aesDecrypt(
|
|
1429
|
+
this.#derivedKey,
|
|
1430
|
+
encryptIV,
|
|
1431
|
+
invoke_request.payload
|
|
1537
1432
|
);
|
|
1538
1433
|
} else {
|
|
1539
1434
|
// No encryption => just use the payload.
|
|
@@ -1569,12 +1464,12 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1569
1464
|
|
|
1570
1465
|
this.#isJSON = true;
|
|
1571
1466
|
|
|
1572
|
-
if (this.#
|
|
1467
|
+
if (this.#derivedKey) {
|
|
1573
1468
|
// Decrypt the actual payload using the shared key and iv.
|
|
1574
|
-
decryptedPayload =
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1469
|
+
decryptedPayload = aesDecrypt(
|
|
1470
|
+
this.#derivedKey,
|
|
1471
|
+
invoke_request.iv,
|
|
1472
|
+
Buffer.from(invoke_request.json_payload, "base64")
|
|
1578
1473
|
);
|
|
1579
1474
|
} else {
|
|
1580
1475
|
// No encryption => just use the payload.
|
|
@@ -1633,17 +1528,15 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1633
1528
|
});
|
|
1634
1529
|
}
|
|
1635
1530
|
|
|
1636
|
-
|
|
1531
|
+
const iv = randomBytes(16);
|
|
1532
|
+
if (this.#derivedKey) {
|
|
1637
1533
|
// Encrypt the response using the shared key and iv.
|
|
1638
|
-
responsePayload =
|
|
1639
|
-
responsePayload,
|
|
1640
|
-
this.#encryptKey,
|
|
1641
|
-
this.#encryptIV
|
|
1642
|
-
);
|
|
1534
|
+
responsePayload = aesEncrypt(this.#derivedKey, iv, responsePayload);
|
|
1643
1535
|
}
|
|
1644
1536
|
|
|
1645
1537
|
const invokeResponse = {
|
|
1646
|
-
invoke_id: this.#invokeId
|
|
1538
|
+
invoke_id: this.#invokeId,
|
|
1539
|
+
iv
|
|
1647
1540
|
};
|
|
1648
1541
|
|
|
1649
1542
|
if (this.#isJSON) {
|
|
@@ -1678,18 +1571,16 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1678
1571
|
endPayload = tunnelMethod.requestSerialize(endPayload);
|
|
1679
1572
|
}
|
|
1680
1573
|
|
|
1681
|
-
|
|
1574
|
+
const iv = randomBytes(16);
|
|
1575
|
+
if (this.#derivedKey) {
|
|
1682
1576
|
// Encrypt the response using the shared key and iv.
|
|
1683
|
-
endPayload =
|
|
1684
|
-
endPayload,
|
|
1685
|
-
this.#encryptKey,
|
|
1686
|
-
this.#encryptIV
|
|
1687
|
-
);
|
|
1577
|
+
endPayload = aesEncrypt(this.#derivedKey, iv, endPayload);
|
|
1688
1578
|
}
|
|
1689
1579
|
|
|
1690
1580
|
const invokeResponse = {
|
|
1691
1581
|
invoke_id: this.#invokeId,
|
|
1692
|
-
server_end: true
|
|
1582
|
+
server_end: true,
|
|
1583
|
+
iv
|
|
1693
1584
|
};
|
|
1694
1585
|
|
|
1695
1586
|
if (this.#isJSON) {
|
|
@@ -1706,7 +1597,8 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1706
1597
|
|
|
1707
1598
|
/* eslint-disable no-underscore-dangle */
|
|
1708
1599
|
|
|
1709
|
-
const {
|
|
1600
|
+
const { publicKeyToPem, removeCarriageReturn, signBase64 } = js.voltUtils;
|
|
1601
|
+
|
|
1710
1602
|
const { filter, pick } = lodash__default["default"];
|
|
1711
1603
|
|
|
1712
1604
|
const log$1 = debug__default["default"]("volt-client-grpc:volt-client-internal");
|
|
@@ -1742,9 +1634,15 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
1742
1634
|
// This is the signing CA used by the volt.
|
|
1743
1635
|
this._credential.cache.ca = authenticateResponse.chain;
|
|
1744
1636
|
|
|
1745
|
-
//
|
|
1637
|
+
// The session id is assigned by the volt.
|
|
1746
1638
|
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
1747
1639
|
|
|
1640
|
+
if (!this._credential.cache.identity_did) {
|
|
1641
|
+
// Identity DID has been assigned by the volt.
|
|
1642
|
+
this._credential.cache.identity_did =
|
|
1643
|
+
authenticateResponse.identity_did;
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1748
1646
|
this._credential.saveCache();
|
|
1749
1647
|
break;
|
|
1750
1648
|
}
|
|
@@ -1755,6 +1653,20 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
1755
1653
|
case "POLICY_DECISION_PROMPT":
|
|
1756
1654
|
case "POLICY_DECISION_PENDING": {
|
|
1757
1655
|
log$1("access pending approval - waiting...");
|
|
1656
|
+
|
|
1657
|
+
// This is the signing CA used by the volt.
|
|
1658
|
+
this._credential.cache.ca = authenticateResponse.chain;
|
|
1659
|
+
|
|
1660
|
+
// The session id is assigned by the volt.
|
|
1661
|
+
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
1662
|
+
|
|
1663
|
+
if (!this._credential.cache.identity_did) {
|
|
1664
|
+
// Identity DID has been assigned by the volt.
|
|
1665
|
+
this._credential.cache.identity_did =
|
|
1666
|
+
authenticateResponse.identity_did;
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
this._credential.saveCache();
|
|
1758
1670
|
break;
|
|
1759
1671
|
}
|
|
1760
1672
|
default:
|
|
@@ -1779,7 +1691,10 @@ function findDIDDocumentService(document, serviceType) {
|
|
|
1779
1691
|
return matches;
|
|
1780
1692
|
}
|
|
1781
1693
|
|
|
1782
|
-
async function authenticateInternal(
|
|
1694
|
+
async function authenticateInternal(
|
|
1695
|
+
sessionOnly = false,
|
|
1696
|
+
ownDID = false
|
|
1697
|
+
) {
|
|
1783
1698
|
try {
|
|
1784
1699
|
// Check if we need to resolve a Relay volt.
|
|
1785
1700
|
if (
|
|
@@ -1791,7 +1706,7 @@ async function authenticateInternal(useDID = true) {
|
|
|
1791
1706
|
log$1("attempting to retrieve Relay information from %s", relayURL);
|
|
1792
1707
|
this._voltConfig.relay = await fetchVoltConfig.call(
|
|
1793
1708
|
this,
|
|
1794
|
-
`${relayURL}/
|
|
1709
|
+
`${relayURL}/discover`
|
|
1795
1710
|
);
|
|
1796
1711
|
|
|
1797
1712
|
if (this._voltConfig.relay.ca_pem) {
|
|
@@ -1817,7 +1732,7 @@ async function authenticateInternal(useDID = true) {
|
|
|
1817
1732
|
|
|
1818
1733
|
const keyPair = this._credential.getKey();
|
|
1819
1734
|
const publicKeyPem = removeCarriageReturn(
|
|
1820
|
-
|
|
1735
|
+
publicKeyToPem(keyPair.publicKey)
|
|
1821
1736
|
);
|
|
1822
1737
|
|
|
1823
1738
|
if (!this._credential.cache.bindIp) {
|
|
@@ -1830,7 +1745,7 @@ async function authenticateInternal(useDID = true) {
|
|
|
1830
1745
|
|
|
1831
1746
|
// Form the request message.
|
|
1832
1747
|
const authenticateRequest = {
|
|
1833
|
-
|
|
1748
|
+
client_name: this._config.client_name,
|
|
1834
1749
|
host: this._credential.cache.bindIp
|
|
1835
1750
|
};
|
|
1836
1751
|
|
|
@@ -1845,68 +1760,58 @@ async function authenticateInternal(useDID = true) {
|
|
|
1845
1760
|
);
|
|
1846
1761
|
}
|
|
1847
1762
|
|
|
1848
|
-
if (
|
|
1763
|
+
if (sessionOnly) {
|
|
1764
|
+
// We want to create a session-based authentication based simply on our key, rather than a
|
|
1765
|
+
// fully-blown DID-based authentication.
|
|
1766
|
+
log$1("creating session-only request");
|
|
1849
1767
|
authenticateRequest.public_key = publicKeyPem;
|
|
1850
|
-
} else if (!this._credential.cache.
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
// Sign the DID document.
|
|
1880
|
-
const documentDigest = signBase64(
|
|
1881
|
-
keyPair.privateKey,
|
|
1882
|
-
signDocument
|
|
1883
|
-
);
|
|
1884
|
-
|
|
1885
|
-
const proofSignature = jwsSignature(documentDigest);
|
|
1886
|
-
|
|
1887
|
-
// Create the DID document proof.
|
|
1888
|
-
didDocument.proof = {
|
|
1889
|
-
created: new Date().toISOString(),
|
|
1890
|
-
jws: proofSignature,
|
|
1891
|
-
proofPurpose: "assertionMethod",
|
|
1892
|
-
type: "RsaSignature2018",
|
|
1893
|
-
verificationMethod: `${did}#key-1`
|
|
1894
|
-
};
|
|
1768
|
+
} else if (!this._credential.cache.identity_did) {
|
|
1769
|
+
if (!ownDID) {
|
|
1770
|
+
// We don't want to have to manage our own DID, so we ask the Volt to do it for us.
|
|
1771
|
+
// The Volt will create a new DID and return it to us in the response.
|
|
1772
|
+
log$1("creating DID-based request");
|
|
1773
|
+
authenticateRequest.did_public_key = publicKeyPem;
|
|
1774
|
+
} else {
|
|
1775
|
+
// We want to create and manage our own DID.
|
|
1776
|
+
const did =
|
|
1777
|
+
(this._credential.cache.identity_did = `did:volt:${uuid.v4()}`);
|
|
1778
|
+
|
|
1779
|
+
// Order of properties in the DID document is important,
|
|
1780
|
+
// they must be in alphabetical order throughout the document.
|
|
1781
|
+
// This is because the DID document is signed and the signature
|
|
1782
|
+
// is over the entire document.
|
|
1783
|
+
const didDocument = {
|
|
1784
|
+
"@context": "https://www.w3.org/ns/did/v1",
|
|
1785
|
+
authentication: [`${did}#key-1`],
|
|
1786
|
+
controller: did,
|
|
1787
|
+
description: this._config.client_name,
|
|
1788
|
+
id: did,
|
|
1789
|
+
verificationMethod: [
|
|
1790
|
+
{
|
|
1791
|
+
id: `${did}#key-1`,
|
|
1792
|
+
publicKeyPem: publicKeyPem,
|
|
1793
|
+
type: "Ed25519VerificationKey2018"
|
|
1794
|
+
}
|
|
1795
|
+
]
|
|
1796
|
+
};
|
|
1895
1797
|
|
|
1896
|
-
|
|
1897
|
-
|
|
1798
|
+
// Set the DID document with proof in the authenticate request.
|
|
1799
|
+
authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
|
|
1898
1800
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1801
|
+
// Now sign the entire DID document, including the proof and set it in the
|
|
1802
|
+
// bind request. This is required by the Volt to verify that we have the private key for the DID.
|
|
1803
|
+
authenticateRequest.did_document_signature = signBase64(
|
|
1804
|
+
keyPair.privateKey,
|
|
1805
|
+
authenticateRequest.did_document
|
|
1806
|
+
);
|
|
1905
1807
|
|
|
1906
|
-
|
|
1907
|
-
|
|
1808
|
+
log$1("did document is %s", authenticateRequest.did_document);
|
|
1809
|
+
log$1("did signature is %s", authenticateRequest.did_document_signature);
|
|
1810
|
+
}
|
|
1908
1811
|
} else {
|
|
1909
|
-
|
|
1812
|
+
// We already have a DID cached, so re-use it.
|
|
1813
|
+
log$1("using cached DID %s", this._credential.cache.identity_did);
|
|
1814
|
+
authenticateRequest.did = this._credential.cache.identity_did;
|
|
1910
1815
|
}
|
|
1911
1816
|
|
|
1912
1817
|
log$1("authenticate TTL is ", this._config.bindRequestTTL);
|
|
@@ -1975,6 +1880,11 @@ function connectInternal(helloPayload) {
|
|
|
1975
1880
|
this._voltConnection.on("error", (err) => {
|
|
1976
1881
|
// Should auto-reconnect.
|
|
1977
1882
|
log$1("Volt connection error [%s]", err.message);
|
|
1883
|
+
if (this._connected) {
|
|
1884
|
+
onDisconnected();
|
|
1885
|
+
this.emit("connected", false);
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1978
1888
|
if (err.message.endsWith("session invalid")) {
|
|
1979
1889
|
log$1("Volt session invalid - clearing session");
|
|
1980
1890
|
delete this._credential.cache.session_id;
|
|
@@ -1982,11 +1892,6 @@ function connectInternal(helloPayload) {
|
|
|
1982
1892
|
this._credential.saveCache();
|
|
1983
1893
|
}
|
|
1984
1894
|
|
|
1985
|
-
if (this._connected) {
|
|
1986
|
-
onDisconnected();
|
|
1987
|
-
this.emit("connected", false);
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
1895
|
this.emit("connectionError", err);
|
|
1991
1896
|
});
|
|
1992
1897
|
|
|
@@ -2000,10 +1905,24 @@ function connectInternal(helloPayload) {
|
|
|
2000
1905
|
|
|
2001
1906
|
this._voltConnection.on("invoke_request", (invoke_request) => {
|
|
2002
1907
|
const invokeId = invoke_request.invoke_id;
|
|
2003
|
-
|
|
2004
|
-
|
|
1908
|
+
|
|
1909
|
+
let rpcInvocation = this._activeRPC[invokeId];
|
|
1910
|
+
|
|
1911
|
+
if (rpcInvocation) {
|
|
1912
|
+
if (rpcInvocation.started) {
|
|
1913
|
+
rpcInvocation.parsePayload(invoke_request);
|
|
1914
|
+
} else {
|
|
1915
|
+
rpcInvocation
|
|
1916
|
+
.initialise(invoke_request)
|
|
1917
|
+
.then(() => {
|
|
1918
|
+
this.emit("invoke_request", rpcInvocation);
|
|
1919
|
+
})
|
|
1920
|
+
.catch((err) => {
|
|
1921
|
+
log$1("invoke_request - error [%s]", err.message);
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
2005
1924
|
} else {
|
|
2006
|
-
|
|
1925
|
+
rpcInvocation = new RpcInvocation(this, this._voltConnection);
|
|
2007
1926
|
|
|
2008
1927
|
rpcInvocation.on("end", () => {
|
|
2009
1928
|
log$1("removing active RPC [%s]", invokeId);
|
|
@@ -2012,9 +1931,11 @@ function connectInternal(helloPayload) {
|
|
|
2012
1931
|
|
|
2013
1932
|
rpcInvocation
|
|
2014
1933
|
.initialise(invoke_request)
|
|
2015
|
-
.then(() => {
|
|
1934
|
+
.then((response) => {
|
|
2016
1935
|
this._activeRPC[invokeId] = rpcInvocation;
|
|
2017
|
-
|
|
1936
|
+
if (!response.key_exchange) {
|
|
1937
|
+
this.emit("invoke_request", rpcInvocation);
|
|
1938
|
+
}
|
|
2018
1939
|
})
|
|
2019
1940
|
.catch((err) => {
|
|
2020
1941
|
log$1("invoke_request - error [%s]", err.message);
|
|
@@ -2160,6 +2081,7 @@ async function fetchVoltConfig(discovery_url) {
|
|
|
2160
2081
|
"public_key",
|
|
2161
2082
|
"fingerprint",
|
|
2162
2083
|
"address",
|
|
2084
|
+
"http_address",
|
|
2163
2085
|
"ca_pem",
|
|
2164
2086
|
"challenge_code",
|
|
2165
2087
|
"cloud",
|
|
@@ -2247,6 +2169,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2247
2169
|
/**
|
|
2248
2170
|
* Initialises a binding and connection to the volt.
|
|
2249
2171
|
* @param {string} [config] - the location of the Volt configuration file, or a configuration object
|
|
2172
|
+
* @param {object} [extras] - additional configuration properties
|
|
2250
2173
|
*/
|
|
2251
2174
|
async initialise(config, extras = {}) {
|
|
2252
2175
|
try {
|
|
@@ -2262,10 +2185,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2262
2185
|
configPath = path__default["default"].resolve(config);
|
|
2263
2186
|
log("Attempt to load config from file %s", configPath);
|
|
2264
2187
|
try {
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2188
|
+
if (fs__default["default"].existsSync(configPath)) {
|
|
2189
|
+
const configContents = await readFile(
|
|
2190
|
+
new URL(configPath, (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)))
|
|
2191
|
+
);
|
|
2192
|
+
configJSON = JSON.parse(configContents);
|
|
2193
|
+
} else {
|
|
2194
|
+
// There is currently no file at the given configuration path.
|
|
2195
|
+
configJSON = {};
|
|
2196
|
+
}
|
|
2269
2197
|
} catch (err) {
|
|
2270
2198
|
log("failure loading config file %s [%s]", configPath, err.message);
|
|
2271
2199
|
throw new Error(
|
|
@@ -2282,15 +2210,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2282
2210
|
throw new Error("config argument is required to be an object");
|
|
2283
2211
|
}
|
|
2284
2212
|
|
|
2213
|
+
this._config = { ...extras, ...configJSON };
|
|
2214
|
+
|
|
2285
2215
|
if (
|
|
2286
|
-
!
|
|
2287
|
-
typeof
|
|
2216
|
+
!this._config.client_name ||
|
|
2217
|
+
typeof this._config.client_name !== "string"
|
|
2288
2218
|
) {
|
|
2289
2219
|
throw new Error("client_name property required in config");
|
|
2290
2220
|
}
|
|
2291
2221
|
|
|
2292
|
-
this._config = { ...configJSON, ...extras };
|
|
2293
|
-
|
|
2294
2222
|
let voltDID = "";
|
|
2295
2223
|
let voltHttpAddress = "";
|
|
2296
2224
|
if (typeof this._config.volt === "string") {
|
|
@@ -2299,6 +2227,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2299
2227
|
} else {
|
|
2300
2228
|
voltHttpAddress = this._config.volt;
|
|
2301
2229
|
}
|
|
2230
|
+
delete this._config.volt;
|
|
2302
2231
|
} else if (typeof this._config.volt !== "object") {
|
|
2303
2232
|
throw new Error("configuration is missing the 'volt' object");
|
|
2304
2233
|
} else {
|
|
@@ -2313,7 +2242,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2313
2242
|
} else if (voltHttpAddress && !this._config?.volt?.id) {
|
|
2314
2243
|
const discoConfig = await fetchVoltConfig.call(
|
|
2315
2244
|
this,
|
|
2316
|
-
`${voltHttpAddress}/
|
|
2245
|
+
`${voltHttpAddress}/discover`
|
|
2317
2246
|
);
|
|
2318
2247
|
this._config = {
|
|
2319
2248
|
...this._config,
|
|
@@ -2322,8 +2251,12 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2322
2251
|
}
|
|
2323
2252
|
|
|
2324
2253
|
this._voltConfig = this._config.volt;
|
|
2325
|
-
|
|
2326
|
-
|
|
2254
|
+
if (voltDID) {
|
|
2255
|
+
this._voltConfig.did = voltDID;
|
|
2256
|
+
}
|
|
2257
|
+
if (voltHttpAddress) {
|
|
2258
|
+
this._voltConfig.http_address = voltHttpAddress;
|
|
2259
|
+
}
|
|
2327
2260
|
|
|
2328
2261
|
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
2329
2262
|
throw new Error("'id' property is missing in Volt configuration");
|
|
@@ -2371,9 +2304,12 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2371
2304
|
if (!isBound) {
|
|
2372
2305
|
// This is usually because of request being denied.
|
|
2373
2306
|
throw new Error("Volt authentication failed");
|
|
2307
|
+
} else {
|
|
2308
|
+
// Perist the configuration.
|
|
2309
|
+
this._config = this._credential.saveCache();
|
|
2374
2310
|
}
|
|
2375
2311
|
|
|
2376
|
-
return Promise.resolve();
|
|
2312
|
+
return Promise.resolve(this._config);
|
|
2377
2313
|
} catch (err) {
|
|
2378
2314
|
log("Failure starting Volt client [%s]", err.message);
|
|
2379
2315
|
throw err;
|
|
@@ -2840,6 +2776,13 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2840
2776
|
});
|
|
2841
2777
|
}
|
|
2842
2778
|
|
|
2779
|
+
SqlTransaction(request) {
|
|
2780
|
+
const grpcClient = this.getVoltAPIClient();
|
|
2781
|
+
|
|
2782
|
+
const call = new GRPCCall(this, "Transaction", "METHOD_TYPE_BIDI");
|
|
2783
|
+
return call.start(grpcClient, request);
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2843
2786
|
/**
|
|
2844
2787
|
* WireAPI
|
|
2845
2788
|
*/
|