@tdxvolt/volt-client-grpc 0.16.0-beta.9 → 0.17.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 +1 -1
- package/lib/index.cjs +439 -464
- 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 +111 -78
- package/src/volt-client.js +33 -12
- package/src/volt-connection.js +25 -16
- package/src/volt-credential.js +37 -124
package/lib/index.cjs
CHANGED
|
@@ -4,37 +4,51 @@ 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 voltClientWeb = require('@tdxvolt/volt-client-web');
|
|
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
|
|
|
20
|
+
function _interopNamespace(e) {
|
|
21
|
+
if (e && e.__esModule) return e;
|
|
22
|
+
var n = Object.create(null);
|
|
23
|
+
if (e) {
|
|
24
|
+
Object.keys(e).forEach(function (k) {
|
|
25
|
+
if (k !== 'default') {
|
|
26
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
27
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
get: function () { return e[k]; }
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
n["default"] = e;
|
|
35
|
+
return Object.freeze(n);
|
|
36
|
+
}
|
|
37
|
+
|
|
22
38
|
var lodash__default = /*#__PURE__*/_interopDefaultLegacy(lodash);
|
|
23
39
|
var ip__default = /*#__PURE__*/_interopDefaultLegacy(ip);
|
|
24
|
-
var
|
|
25
|
-
var bs58__default = /*#__PURE__*/_interopDefaultLegacy(bs58);
|
|
40
|
+
var voltClientWeb__namespace = /*#__PURE__*/_interopNamespace(voltClientWeb);
|
|
26
41
|
var debug__default = /*#__PURE__*/_interopDefaultLegacy(debug);
|
|
27
42
|
var EventEmitter__default = /*#__PURE__*/_interopDefaultLegacy(EventEmitter);
|
|
28
43
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
29
|
-
var jwt__default = /*#__PURE__*/_interopDefaultLegacy(jwt);
|
|
30
|
-
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
|
|
31
44
|
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
32
45
|
var protoLoader__default = /*#__PURE__*/_interopDefaultLegacy(protoLoader);
|
|
33
46
|
var bent__default = /*#__PURE__*/_interopDefaultLegacy(bent);
|
|
47
|
+
var jwt__default = /*#__PURE__*/_interopDefaultLegacy(jwt);
|
|
34
48
|
|
|
35
49
|
// eslint-disable-next-line import/prefer-default-export
|
|
36
50
|
const constants = {
|
|
37
|
-
authTokenName: "
|
|
51
|
+
authTokenName: "volt-token",
|
|
38
52
|
collectionOperation: {
|
|
39
53
|
add: "ADD",
|
|
40
54
|
remove: "REMOVE",
|
|
@@ -57,9 +71,9 @@ const constants = {
|
|
|
57
71
|
],
|
|
58
72
|
userIdOID: "0.9.2342.19200300.100.1.1"
|
|
59
73
|
},
|
|
60
|
-
defaultDIDHostName: "
|
|
74
|
+
defaultDIDHostName: "tdxvolt.com",
|
|
61
75
|
didServiceType: {
|
|
62
|
-
voltConfig: "
|
|
76
|
+
voltConfig: "volt-discovery"
|
|
63
77
|
},
|
|
64
78
|
identityType: {
|
|
65
79
|
did: "did",
|
|
@@ -70,8 +84,6 @@ const constants = {
|
|
|
70
84
|
online: "online",
|
|
71
85
|
unknown: "unknown"
|
|
72
86
|
},
|
|
73
|
-
publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
|
|
74
|
-
privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
|
|
75
87
|
privateEncryptedKeyPrefix: "-----BEGIN ENCRYPTED PRIVATE KEY-----",
|
|
76
88
|
resource: {
|
|
77
89
|
identity: "ident",
|
|
@@ -108,8 +120,6 @@ const constants = {
|
|
|
108
120
|
tunnelTimeoutInterval: 10000 * 2 // This must always be greater than `tunnelPingInterval`.
|
|
109
121
|
};
|
|
110
122
|
|
|
111
|
-
const { pki: pki$2 } = forge__default["default"];
|
|
112
|
-
|
|
113
123
|
const createSecureContextOptions = (cryptoOptions) => {
|
|
114
124
|
const tlsOptions = {
|
|
115
125
|
cert: Buffer.from(cryptoOptions.cert),
|
|
@@ -139,53 +149,7 @@ const getServiceAddress = (mdnsService) => {
|
|
|
139
149
|
return address;
|
|
140
150
|
};
|
|
141
151
|
|
|
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) => {
|
|
152
|
+
const getDIDResolutionURL = (didIn, registryUrl = "") => {
|
|
189
153
|
const did = didIn.toLowerCase().split(":");
|
|
190
154
|
if (did.length < 3) {
|
|
191
155
|
return "";
|
|
@@ -195,97 +159,20 @@ const getDIDResolutionURL = (didIn) => {
|
|
|
195
159
|
return "";
|
|
196
160
|
}
|
|
197
161
|
|
|
198
|
-
if (did[1] !== "
|
|
162
|
+
if (did[1] !== "volt") {
|
|
199
163
|
return "";
|
|
200
164
|
}
|
|
201
165
|
|
|
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;
|
|
166
|
+
const lookupUrl = registryUrl || `https://${constants.defaultDIDHostName}`;
|
|
167
|
+
return `${lookupUrl}/did/${didIn}`;
|
|
269
168
|
};
|
|
270
169
|
|
|
271
170
|
var utils = /*#__PURE__*/Object.freeze({
|
|
272
171
|
__proto__: null,
|
|
273
172
|
createSecureContextOptions: createSecureContextOptions,
|
|
274
173
|
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
174
|
getDIDResolutionURL: getDIDResolutionURL,
|
|
285
|
-
|
|
286
|
-
sha256Base64: sha256Base64,
|
|
287
|
-
decryptPrivateKey: decryptPrivateKey,
|
|
288
|
-
jwsSignature: jwsSignature
|
|
175
|
+
crypto: voltClientWeb__namespace
|
|
289
176
|
});
|
|
290
177
|
|
|
291
178
|
/* eslint-disable no-underscore-dangle */
|
|
@@ -307,8 +194,11 @@ function createClient(
|
|
|
307
194
|
) {
|
|
308
195
|
let grpcCredentials;
|
|
309
196
|
if (credentials) {
|
|
310
|
-
const
|
|
311
|
-
const
|
|
197
|
+
const relaying = !!credentials.config?.volt?.relay;
|
|
198
|
+
const ca = relaying
|
|
199
|
+
? credentials.config?.volt?.relay?.ca_pem
|
|
200
|
+
: credentials.cache.ca;
|
|
201
|
+
const cert = relaying ? "" : credentials.cache.cert;
|
|
312
202
|
const { key } = credentials.cache;
|
|
313
203
|
|
|
314
204
|
if (key && cert) {
|
|
@@ -320,8 +210,9 @@ function createClient(
|
|
|
320
210
|
tlsOptions.cert
|
|
321
211
|
);
|
|
322
212
|
} else {
|
|
323
|
-
// Cache has no private key, this implies we are connecting without supplying a client certificate.
|
|
324
|
-
//
|
|
213
|
+
// Cache has no private key or certificate, this implies we are connecting without supplying a client certificate.
|
|
214
|
+
// We might be connecting to a relay, or making an initial Authenticate call.
|
|
215
|
+
// Simply connect using the CA certificate, and the a JWT will be used to authenticate.
|
|
325
216
|
grpcCredentials = grpc.credentials.createSsl(Buffer.from(ca));
|
|
326
217
|
}
|
|
327
218
|
} else {
|
|
@@ -527,10 +418,7 @@ var grpcUtils = /*#__PURE__*/Object.freeze({
|
|
|
527
418
|
|
|
528
419
|
/* eslint-disable no-underscore-dangle */
|
|
529
420
|
|
|
530
|
-
const { pki: pki$1 } = forge__default["default"];
|
|
531
421
|
const log$5 = debug__default["default"]("volt-client-grpc:volt-credential");
|
|
532
|
-
const aesAlgorithm = "aes-256-cbc";
|
|
533
|
-
const rs256Algorithm = "RS256";
|
|
534
422
|
|
|
535
423
|
class VoltCredential {
|
|
536
424
|
constructor(config, configPath) {
|
|
@@ -554,14 +442,13 @@ class VoltCredential {
|
|
|
554
442
|
}
|
|
555
443
|
|
|
556
444
|
// Need to unencrypt the private key.
|
|
557
|
-
const decrypted =
|
|
558
|
-
|
|
559
|
-
config.p
|
|
560
|
-
);
|
|
445
|
+
const decrypted = voltClientWeb.keyFromPem(this._cryptoCache.key, config.p);
|
|
446
|
+
|
|
561
447
|
if (!decrypted) {
|
|
562
448
|
throw new Error("failed to decrypt key - check passphrase");
|
|
563
449
|
}
|
|
564
|
-
|
|
450
|
+
|
|
451
|
+
this._cryptoCache.key = voltClientWeb.privateKeyToPem(decrypted.privateKey);
|
|
565
452
|
}
|
|
566
453
|
|
|
567
454
|
if (this._voltConfig.ca_pem) {
|
|
@@ -570,14 +457,11 @@ class VoltCredential {
|
|
|
570
457
|
this._cryptoCache.ca = this._voltConfig.ca_pem;
|
|
571
458
|
}
|
|
572
459
|
|
|
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
460
|
// Extract Volt public key (used for encrypting Relay payloads).
|
|
577
|
-
this._voltPublicKey =
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
);
|
|
461
|
+
this._voltPublicKey = this._voltConfig.public_key;
|
|
462
|
+
|
|
463
|
+
const voltKey = voltClientWeb.keyFromPem(this._voltPublicKey).publicKey;
|
|
464
|
+
this._voltFingerprint = voltClientWeb.fingerprintFromPublicKey(voltKey);
|
|
581
465
|
}
|
|
582
466
|
}
|
|
583
467
|
|
|
@@ -598,7 +482,7 @@ class VoltCredential {
|
|
|
598
482
|
}
|
|
599
483
|
|
|
600
484
|
get identityId() {
|
|
601
|
-
return this._cryptoCache.
|
|
485
|
+
return this._cryptoCache.identity_did;
|
|
602
486
|
}
|
|
603
487
|
|
|
604
488
|
get voltPublicKey() {
|
|
@@ -606,7 +490,7 @@ class VoltCredential {
|
|
|
606
490
|
}
|
|
607
491
|
|
|
608
492
|
get publicKey() {
|
|
609
|
-
return
|
|
493
|
+
return voltClientWeb.publicKeyToPem(this.getKey().publicKey);
|
|
610
494
|
}
|
|
611
495
|
|
|
612
496
|
get voltFingerprint() {
|
|
@@ -614,9 +498,8 @@ class VoltCredential {
|
|
|
614
498
|
}
|
|
615
499
|
|
|
616
500
|
getKey() {
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
return { privateKey, publicKey };
|
|
501
|
+
const key = voltClientWeb.keyFromPem(this._cryptoCache.key);
|
|
502
|
+
return key;
|
|
620
503
|
}
|
|
621
504
|
|
|
622
505
|
/**
|
|
@@ -629,16 +512,10 @@ class VoltCredential {
|
|
|
629
512
|
return Promise.resolve(this.getKey());
|
|
630
513
|
} else {
|
|
631
514
|
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
|
-
});
|
|
515
|
+
this._cryptoCache.key = voltClientWeb.createKey();
|
|
516
|
+
const key = voltClientWeb.keyFromPem(this._cryptoCache.key);
|
|
517
|
+
log$5("successfully created key");
|
|
518
|
+
return Promise.resolve(key);
|
|
642
519
|
}
|
|
643
520
|
}
|
|
644
521
|
|
|
@@ -663,55 +540,6 @@ class VoltCredential {
|
|
|
663
540
|
return !!this._cryptoCache.cert;
|
|
664
541
|
}
|
|
665
542
|
|
|
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
543
|
/**
|
|
716
544
|
* Creates the metadata required for a grpc call, including the Volt authentication token
|
|
717
545
|
* and optionally tunnel encryption parameters.
|
|
@@ -722,9 +550,12 @@ class VoltCredential {
|
|
|
722
550
|
* @returns
|
|
723
551
|
*/
|
|
724
552
|
getIdentityMetadata(grpc, audience, publicKey, tunnelling = false, ttl = 60) {
|
|
725
|
-
const
|
|
553
|
+
const key = voltClientWeb.keyFromPem(this._cryptoCache.key);
|
|
554
|
+
|
|
555
|
+
const identityToken = voltClientWeb.getIdentityToken(
|
|
556
|
+
this.sessionId,
|
|
557
|
+
key,
|
|
726
558
|
audience || this._voltConfig.id,
|
|
727
|
-
publicKey,
|
|
728
559
|
tunnelling,
|
|
729
560
|
ttl
|
|
730
561
|
);
|
|
@@ -734,48 +565,18 @@ class VoltCredential {
|
|
|
734
565
|
}
|
|
735
566
|
|
|
736
567
|
saveCache() {
|
|
568
|
+
const clone = { ...this._config, credential: { ...this._cryptoCache } };
|
|
569
|
+
if (this._config.p) {
|
|
570
|
+
// A passphrase option exists => encrypt the key before writing the cache file.
|
|
571
|
+
const key = voltClientWeb.keyFromPem(this._cryptoCache.key);
|
|
572
|
+
clone.credential.key = voltClientWeb.privateKeyToPem(key.privateKey, this._config.p);
|
|
573
|
+
}
|
|
574
|
+
|
|
737
575
|
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
576
|
fs__default["default"].writeFileSync(this._configPath, JSON.stringify(clone, null, 2));
|
|
748
577
|
}
|
|
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
578
|
|
|
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));
|
|
579
|
+
return clone;
|
|
779
580
|
}
|
|
780
581
|
}
|
|
781
582
|
|
|
@@ -918,6 +719,21 @@ function _prepareInvokeRequest(
|
|
|
918
719
|
this._service?.service_description.host_type ===
|
|
919
720
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
920
721
|
|
|
722
|
+
const relaying = this._voltClient.isVoltRelay || isServiceRelayed;
|
|
723
|
+
|
|
724
|
+
let targetDID = [];
|
|
725
|
+
if (this._voltClient.isVoltRelay) {
|
|
726
|
+
targetDID.push(this._voltClient.voltConfig.id);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (isServiceRelayed) {
|
|
730
|
+
// Add another relay hop if the target service is relayed.
|
|
731
|
+
log$4("target service is relayed");
|
|
732
|
+
targetDID.push(this._service.service_description.host_client_id);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
log$4("target is %j", targetDID);
|
|
736
|
+
|
|
921
737
|
// Use the service's host client id and public key if the service is relayed.
|
|
922
738
|
const methodToken = this._voltClient.credential.getIdentityMetadata(
|
|
923
739
|
this._voltClient.grpc,
|
|
@@ -925,17 +741,52 @@ function _prepareInvokeRequest(
|
|
|
925
741
|
this._voltClient.voltConfig.id,
|
|
926
742
|
this._service?.service_description.host_public_key ||
|
|
927
743
|
this._voltClient.credential.voltPublicKey,
|
|
928
|
-
|
|
744
|
+
relaying
|
|
929
745
|
);
|
|
930
746
|
|
|
931
747
|
let invokeRequest;
|
|
932
748
|
let callMethod = method;
|
|
933
749
|
|
|
934
|
-
if (
|
|
750
|
+
if (relaying) {
|
|
935
751
|
//
|
|
936
752
|
// If the volt connection is via a relay (or the target service is relayed),
|
|
937
753
|
// we wrap the rpc in a RemoteRequest message and send it via a call to Invoke().
|
|
938
754
|
//
|
|
755
|
+
// Replace the target method with a call to Invoke on the relay Volt.
|
|
756
|
+
//
|
|
757
|
+
callMethod = "Invoke";
|
|
758
|
+
|
|
759
|
+
// But first we need to perform a key exchange with the target service.
|
|
760
|
+
//
|
|
761
|
+
if (!this._encryptionKey) {
|
|
762
|
+
// We haven't exchanged keys yet, so queue the payload.
|
|
763
|
+
this._pendingRequests.push(request);
|
|
764
|
+
|
|
765
|
+
if (this._keyExchangePending) {
|
|
766
|
+
// We've already sent the key exchange request, so do nothing.
|
|
767
|
+
log$4("key exchange pending, not sending payload");
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
this._keyExchangePending = true;
|
|
772
|
+
|
|
773
|
+
// Send the key exchange request.
|
|
774
|
+
invokeRequest = {
|
|
775
|
+
token: methodToken.token,
|
|
776
|
+
target_did: targetDID,
|
|
777
|
+
target_service_id: isServiceRelayed
|
|
778
|
+
? this._service.service_description.host_service_id
|
|
779
|
+
: undefined
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
return {
|
|
783
|
+
request: invokeRequest,
|
|
784
|
+
method: callMethod,
|
|
785
|
+
methodName: method,
|
|
786
|
+
meta: methodToken
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
|
|
939
790
|
let requestPayload;
|
|
940
791
|
|
|
941
792
|
// Serialise the rpc request.
|
|
@@ -955,45 +806,24 @@ function _prepareInvokeRequest(
|
|
|
955
806
|
method_invoke: {
|
|
956
807
|
method_name: this._grpcClient[method].path,
|
|
957
808
|
method_type: methodType,
|
|
958
|
-
token: methodToken.token,
|
|
959
809
|
request: requestPayload
|
|
960
810
|
}
|
|
961
811
|
};
|
|
962
812
|
|
|
963
|
-
let targetDID = [];
|
|
964
813
|
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
|
-
}
|
|
975
|
-
|
|
976
|
-
if (this._voltClient.isVoltRelay) {
|
|
977
|
-
targetDID.push(this._voltClient.voltConfig.id);
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
if (isServiceRelayed) {
|
|
981
|
-
// Add another relay hop if the target service is relayed.
|
|
982
|
-
log$4("target service is relayed");
|
|
983
|
-
targetDID.push(this._service.service_description.host_client_id);
|
|
984
|
-
}
|
|
814
|
+
const iv = voltClientWeb.aesCreateKey().iv;
|
|
985
815
|
|
|
986
|
-
|
|
816
|
+
// When sending via a relay we need to encrypt the payload.
|
|
817
|
+
invokePayload = voltClientWeb.aesEncrypt(
|
|
818
|
+
this._encryptionKey,
|
|
819
|
+
iv,
|
|
820
|
+
tunnelMethod.responseSerialize(tunnelResp)
|
|
821
|
+
);
|
|
987
822
|
|
|
988
823
|
invokeRequest = {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
payload: invokePayload,
|
|
992
|
-
target_service_id: isServiceRelayed ? this._service.id : undefined
|
|
824
|
+
iv,
|
|
825
|
+
payload: invokePayload
|
|
993
826
|
};
|
|
994
|
-
|
|
995
|
-
// Replace the target method with a call to Invoke on the relay Volt.
|
|
996
|
-
callMethod = "Invoke";
|
|
997
827
|
} else {
|
|
998
828
|
invokeRequest = request;
|
|
999
829
|
}
|
|
@@ -1013,7 +843,10 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
1013
843
|
this._service?.service_description.host_type ===
|
|
1014
844
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
1015
845
|
|
|
846
|
+
let iv;
|
|
1016
847
|
if (this._voltClient.isVoltRelay || isServiceRelayed) {
|
|
848
|
+
iv = voltClientWeb.aesCreateKey().iv;
|
|
849
|
+
|
|
1017
850
|
const requestPayload =
|
|
1018
851
|
this._grpcClient[invokeInfo.methodName].requestSerialize(request);
|
|
1019
852
|
|
|
@@ -1026,13 +859,14 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
1026
859
|
}
|
|
1027
860
|
};
|
|
1028
861
|
|
|
1029
|
-
const invokePayload =
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
862
|
+
const invokePayload = voltClientWeb.aesEncrypt(
|
|
863
|
+
this._encryptionKey,
|
|
864
|
+
iv,
|
|
865
|
+
tunnelMethod.responseSerialize(tunnelResp)
|
|
1033
866
|
);
|
|
1034
867
|
|
|
1035
868
|
payloadRequest = {
|
|
869
|
+
iv,
|
|
1036
870
|
payload: invokePayload
|
|
1037
871
|
};
|
|
1038
872
|
} else {
|
|
@@ -1051,13 +885,38 @@ function _parseResponse(method, meta, response) {
|
|
|
1051
885
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
1052
886
|
|
|
1053
887
|
if (this._voltClient.isVoltRelay || isServiceRelayed) {
|
|
1054
|
-
if (response.
|
|
888
|
+
if (response.key_exchange) {
|
|
889
|
+
const encryptionKey = response.key_exchange.encryption_key;
|
|
890
|
+
const nonce = response.key_exchange.nonce;
|
|
891
|
+
const signature = response.key_exchange.signature;
|
|
892
|
+
|
|
893
|
+
// The signed message is the encryption key and the nonce.
|
|
894
|
+
const message = new Uint8Array(encryptionKey.length + nonce.length);
|
|
895
|
+
message.set(encryptionKey);
|
|
896
|
+
message.set(nonce, encryptionKey.length);
|
|
897
|
+
|
|
898
|
+
// Check the signature using the target service public key.
|
|
899
|
+
const targetKey = voltClientWeb.keyFromPem(
|
|
900
|
+
this._service?.service_description?.host_public_key ||
|
|
901
|
+
this._voltClient.credential.voltPublicKey
|
|
902
|
+
).publicKey;
|
|
903
|
+
|
|
904
|
+
if (!voltClientWeb.verify(targetKey, message, signature)) {
|
|
905
|
+
throw new Error("signature verification failed");
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
invokeResponse = {
|
|
909
|
+
key_exchange: {
|
|
910
|
+
encryption_key: voltClientWeb.formatPEM(voltClientWeb.toBase64(encryptionKey), "PUBLIC KEY")
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
} else if (response.payload) {
|
|
1055
914
|
let decryptedPayload;
|
|
1056
|
-
if (
|
|
1057
|
-
decryptedPayload =
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
915
|
+
if (this._encryptionKey) {
|
|
916
|
+
decryptedPayload = voltClientWeb.aesDecrypt(
|
|
917
|
+
this._encryptionKey,
|
|
918
|
+
response.iv,
|
|
919
|
+
response.payload
|
|
1061
920
|
);
|
|
1062
921
|
} else {
|
|
1063
922
|
decryptedPayload = response.payload;
|
|
@@ -1097,11 +956,11 @@ function _parseResponse(method, meta, response) {
|
|
|
1097
956
|
invokeResponse.methodId = response.invoke_id;
|
|
1098
957
|
log$4("started method %d for %s", invokeResponse.methodId, method);
|
|
1099
958
|
}
|
|
1100
|
-
} else if (this._voltClient.isRemote &&
|
|
1101
|
-
const decryptedPayload =
|
|
1102
|
-
|
|
1103
|
-
meta.sharedKey.
|
|
1104
|
-
|
|
959
|
+
} else if (this._voltClient.isRemote && this._encryptionKey) {
|
|
960
|
+
const decryptedPayload = voltClientWeb.aesDecrypt(
|
|
961
|
+
this._encryptionKey,
|
|
962
|
+
meta.sharedKey.iv,
|
|
963
|
+
response
|
|
1105
964
|
);
|
|
1106
965
|
invokeResponse = {
|
|
1107
966
|
payload:
|
|
@@ -1122,6 +981,9 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1122
981
|
this._call = null;
|
|
1123
982
|
this._voltClient = voltClient;
|
|
1124
983
|
this._service = service;
|
|
984
|
+
this._keyExchangePending = false;
|
|
985
|
+
this._pendingRequests = [];
|
|
986
|
+
this._encryptionKey = null;
|
|
1125
987
|
}
|
|
1126
988
|
|
|
1127
989
|
start(grpcClient, request) {
|
|
@@ -1150,7 +1012,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1150
1012
|
}
|
|
1151
1013
|
|
|
1152
1014
|
this._call = callClient[this._initialRequest.method](
|
|
1153
|
-
this._initialRequest
|
|
1015
|
+
this._initialRequest?.meta?.metadata
|
|
1154
1016
|
);
|
|
1155
1017
|
|
|
1156
1018
|
this._call.on("data", (streamResponse) => {
|
|
@@ -1161,7 +1023,25 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1161
1023
|
this._initialRequest.meta,
|
|
1162
1024
|
streamResponse
|
|
1163
1025
|
);
|
|
1164
|
-
if (parsedResponse.
|
|
1026
|
+
if (parsedResponse.key_exchange) {
|
|
1027
|
+
const peerKey = voltClientWeb.keyFromPem(
|
|
1028
|
+
parsedResponse.key_exchange.encryption_key
|
|
1029
|
+
).publicKey;
|
|
1030
|
+
|
|
1031
|
+
this._encryptionKey = voltClientWeb.deriveSharedKey(
|
|
1032
|
+
this._initialRequest.meta.sharedKey.key,
|
|
1033
|
+
peerKey,
|
|
1034
|
+
voltClientWeb.x25519
|
|
1035
|
+
);
|
|
1036
|
+
|
|
1037
|
+
// We've exchanged keys, so send any queued payloads on the next tick.
|
|
1038
|
+
process.nextTick(() => {
|
|
1039
|
+
this._pendingRequests.forEach((pendingRequest) => {
|
|
1040
|
+
this.write(pendingRequest);
|
|
1041
|
+
});
|
|
1042
|
+
this._pendingRequests = [];
|
|
1043
|
+
});
|
|
1044
|
+
} else if (parsedResponse.payload) {
|
|
1165
1045
|
// Interpret a non-empty status message as an error.
|
|
1166
1046
|
if (parsedResponse.payload?.status?.message) {
|
|
1167
1047
|
this.emit(
|
|
@@ -1236,7 +1116,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1236
1116
|
if (!this._call) {
|
|
1237
1117
|
log$4("ERROR - call not initialised");
|
|
1238
1118
|
throw new Error("call not initialised");
|
|
1239
|
-
} else if (this._initialRequest) {
|
|
1119
|
+
} else if (this._initialRequest && !this._keyExchangePending) {
|
|
1240
1120
|
const payloadRequest = _preparePayloadRequest.call(
|
|
1241
1121
|
this,
|
|
1242
1122
|
request,
|
|
@@ -1244,6 +1124,8 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1244
1124
|
);
|
|
1245
1125
|
return this._call.write(payloadRequest);
|
|
1246
1126
|
} else {
|
|
1127
|
+
this._keyExchangePending = false;
|
|
1128
|
+
|
|
1247
1129
|
this._initialRequest = _prepareInvokeRequest.call(
|
|
1248
1130
|
this,
|
|
1249
1131
|
request,
|
|
@@ -1273,10 +1155,9 @@ function _cleanUp(immediateReconnect) {
|
|
|
1273
1155
|
this._pingTimeoutTimer = 0;
|
|
1274
1156
|
}
|
|
1275
1157
|
|
|
1276
|
-
if (
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
) {
|
|
1158
|
+
if (this._dying) {
|
|
1159
|
+
clearTimeout(this._reconnectTimer);
|
|
1160
|
+
} else if (immediateReconnect || (this._autoRetry && !this._reconnectTimer)) {
|
|
1280
1161
|
this._reconnectTimer = setTimeout(
|
|
1281
1162
|
() => {
|
|
1282
1163
|
this._reconnectTimer = 0;
|
|
@@ -1302,13 +1183,18 @@ function _doConnect(connectHello) {
|
|
|
1302
1183
|
|
|
1303
1184
|
const grpcClient = this._voltClient.getVoltAPIClient();
|
|
1304
1185
|
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1186
|
+
// Notify the Volt of our ping interval. If the Volt doesn't receive a ping from us
|
|
1187
|
+
// within this time, it will assume the connection is dead.
|
|
1188
|
+
const defaultHello = {
|
|
1189
|
+
ping_interval: this._pingInterval,
|
|
1190
|
+
timestamp: Date.now()
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
const helloPayload = { ...connectHello } || {};
|
|
1194
|
+
|
|
1195
|
+
helloPayload.hello = { ...defaultHello, ...connectHello?.hello };
|
|
1196
|
+
|
|
1197
|
+
log$3("hello is %j", helloPayload);
|
|
1312
1198
|
|
|
1313
1199
|
this.call.on("error", (err) => {
|
|
1314
1200
|
log$3("error on connection stream [%s]", err.message);
|
|
@@ -1326,17 +1212,22 @@ function _doConnect(connectHello) {
|
|
|
1326
1212
|
clearTimeout(this._pingTimeoutTimer);
|
|
1327
1213
|
}
|
|
1328
1214
|
|
|
1329
|
-
// Schedule the next ping.
|
|
1330
|
-
_startPingTimer.call(this);
|
|
1331
|
-
|
|
1332
1215
|
this.emit("ping", response.ping.timestamp);
|
|
1333
1216
|
break;
|
|
1334
1217
|
}
|
|
1335
1218
|
case "acknowledge": {
|
|
1336
1219
|
if (!this._connectionId) {
|
|
1337
1220
|
this._connectionId = response.acknowledge.connection_id;
|
|
1221
|
+
|
|
1222
|
+
// Set the ping timeout interval to the value returned by the Volt. If we don't
|
|
1223
|
+
// hear from the Volt in this time, we'll assume the connection is dead.
|
|
1224
|
+
this._timeoutInterval = response.acknowledge.ping_interval;
|
|
1225
|
+
|
|
1338
1226
|
log$3("connection id is %s", this._connectionId);
|
|
1339
1227
|
this.emit("connected", this._connectionId);
|
|
1228
|
+
|
|
1229
|
+
// Schedule the next ping.
|
|
1230
|
+
_startPingTimer.call(this);
|
|
1340
1231
|
}
|
|
1341
1232
|
break;
|
|
1342
1233
|
}
|
|
@@ -1364,8 +1255,6 @@ function _doConnect(connectHello) {
|
|
|
1364
1255
|
});
|
|
1365
1256
|
|
|
1366
1257
|
this.call.start(grpcClient, helloPayload);
|
|
1367
|
-
|
|
1368
|
-
_startPingTimer.call(this);
|
|
1369
1258
|
}
|
|
1370
1259
|
|
|
1371
1260
|
function _startPingTimer() {
|
|
@@ -1392,6 +1281,8 @@ function _startPingTimer() {
|
|
|
1392
1281
|
_connectionTimeoutHandler.bind(this),
|
|
1393
1282
|
this._timeoutInterval
|
|
1394
1283
|
);
|
|
1284
|
+
|
|
1285
|
+
_startPingTimer.call(this);
|
|
1395
1286
|
}, this._pingInterval);
|
|
1396
1287
|
}
|
|
1397
1288
|
|
|
@@ -1439,13 +1330,13 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1439
1330
|
#voltClient = null;
|
|
1440
1331
|
#voltConnection = null;
|
|
1441
1332
|
#token = null;
|
|
1442
|
-
#encryptKey = null;
|
|
1443
|
-
#encryptIV = null;
|
|
1444
1333
|
#invokeId = null;
|
|
1445
1334
|
#payload = null;
|
|
1446
1335
|
#isJSON = false;
|
|
1447
1336
|
#methodDescriptor = null;
|
|
1448
1337
|
#methodName = null;
|
|
1338
|
+
#derivedKey = null;
|
|
1339
|
+
#started = false;
|
|
1449
1340
|
|
|
1450
1341
|
constructor(voltClient, voltConnection) {
|
|
1451
1342
|
super();
|
|
@@ -1489,51 +1380,89 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1489
1380
|
this.#methodDescriptor = methodDescriptor;
|
|
1490
1381
|
}
|
|
1491
1382
|
|
|
1492
|
-
|
|
1383
|
+
get started() {
|
|
1384
|
+
return this.#started;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
#keyExchange(token) {
|
|
1493
1388
|
// We don't verify the token at this point, as we don't know the public key.
|
|
1494
1389
|
// The rpc implementation should do the verification using the Volt API (e.g. CanAccessResource).
|
|
1495
1390
|
this.#token = jwt__default["default"].decode(token);
|
|
1496
1391
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1392
|
+
// Store the JWT key id (kid) for use in the response.
|
|
1393
|
+
const callerPublicKeyPem = voltClientWeb.formatPEM(this.#token.k, "PUBLIC KEY");
|
|
1394
|
+
const callerKey = voltClientWeb.keyFromPem(callerPublicKeyPem, "", voltClientWeb.x25519).publicKey;
|
|
1395
|
+
|
|
1396
|
+
const ephemeralKeyPem = voltClientWeb.createEncryptionKey();
|
|
1397
|
+
const ephemeralKey = voltClientWeb.keyFromPem(ephemeralKeyPem, "", voltClientWeb.x25519);
|
|
1398
|
+
const ephemeralPublicKey = voltClientWeb.fromBase64(
|
|
1399
|
+
voltClientWeb.stripPEMHeaders(voltClientWeb.publicKeyToPem(ephemeralKey.publicKey, voltClientWeb.x25519))
|
|
1400
|
+
);
|
|
1401
|
+
|
|
1402
|
+
this.#derivedKey = voltClientWeb.deriveSharedKey(
|
|
1403
|
+
ephemeralKey.privateKey,
|
|
1404
|
+
callerKey,
|
|
1405
|
+
voltClientWeb.x25519
|
|
1406
|
+
);
|
|
1407
|
+
|
|
1408
|
+
const nonce = voltClientWeb.randomBytes(16);
|
|
1409
|
+
|
|
1410
|
+
// The signed message is the encryption key and the nonce.
|
|
1411
|
+
const message = new Uint8Array(ephemeralPublicKey.length + nonce.length);
|
|
1412
|
+
message.set(ephemeralPublicKey);
|
|
1413
|
+
message.set(nonce, ephemeralPublicKey.length);
|
|
1414
|
+
|
|
1415
|
+
const signature = voltClientWeb.signBase64(
|
|
1416
|
+
this.#voltClient.credential.getKey().privateKey,
|
|
1417
|
+
message
|
|
1418
|
+
);
|
|
1419
|
+
|
|
1420
|
+
const keyExchangeResponse = {
|
|
1421
|
+
invoke_id: this.#invokeId,
|
|
1422
|
+
key_exchange: {
|
|
1423
|
+
nonce,
|
|
1424
|
+
encryption_key: ephemeralPublicKey,
|
|
1425
|
+
signature: voltClientWeb.fromBase64Url(signature)
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
this.#voltConnection.send({
|
|
1430
|
+
invoke_response: keyExchangeResponse
|
|
1431
|
+
});
|
|
1508
1432
|
|
|
1509
|
-
return Promise.resolve(
|
|
1433
|
+
return Promise.resolve(keyExchangeResponse);
|
|
1510
1434
|
}
|
|
1511
1435
|
|
|
1512
1436
|
initialise(invoke_request) {
|
|
1513
1437
|
this.#invokeId = invoke_request.invoke_id;
|
|
1514
1438
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
return this.parsePayload(invoke_request);
|
|
1519
|
-
})
|
|
1520
|
-
.catch((err) => {
|
|
1439
|
+
if (!this.#derivedKey) {
|
|
1440
|
+
// We don't have the derived key yet, so we need to perform the key exchange.
|
|
1441
|
+
return this.#keyExchange(invoke_request.token).catch((err) => {
|
|
1521
1442
|
log$2("failure initialising invocation: %s", err.message);
|
|
1522
1443
|
return Promise.reject(err);
|
|
1523
1444
|
});
|
|
1445
|
+
} else {
|
|
1446
|
+
// We already have the derived key, so we can just parse the payload.
|
|
1447
|
+
return this.parsePayload(invoke_request);
|
|
1448
|
+
}
|
|
1524
1449
|
}
|
|
1525
1450
|
|
|
1526
1451
|
parsePayload(invoke_request) {
|
|
1452
|
+
this.#started = true;
|
|
1453
|
+
|
|
1527
1454
|
return new Promise((resolve, reject) => {
|
|
1455
|
+
const encryptIV = invoke_request.iv;
|
|
1456
|
+
|
|
1528
1457
|
if (invoke_request.payload) {
|
|
1529
1458
|
let decryptedPayload;
|
|
1530
1459
|
|
|
1531
|
-
if (
|
|
1460
|
+
if (encryptIV) {
|
|
1532
1461
|
// Decrypt the actual payload using the shared key and iv.
|
|
1533
|
-
decryptedPayload =
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1462
|
+
decryptedPayload = voltClientWeb.aesDecrypt(
|
|
1463
|
+
this.#derivedKey,
|
|
1464
|
+
encryptIV,
|
|
1465
|
+
invoke_request.payload
|
|
1537
1466
|
);
|
|
1538
1467
|
} else {
|
|
1539
1468
|
// No encryption => just use the payload.
|
|
@@ -1569,12 +1498,12 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1569
1498
|
|
|
1570
1499
|
this.#isJSON = true;
|
|
1571
1500
|
|
|
1572
|
-
if (this.#
|
|
1501
|
+
if (this.#derivedKey) {
|
|
1573
1502
|
// Decrypt the actual payload using the shared key and iv.
|
|
1574
|
-
decryptedPayload =
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1503
|
+
decryptedPayload = voltClientWeb.aesDecrypt(
|
|
1504
|
+
this.#derivedKey,
|
|
1505
|
+
invoke_request.iv,
|
|
1506
|
+
Buffer.from(invoke_request.json_payload, "base64")
|
|
1578
1507
|
);
|
|
1579
1508
|
} else {
|
|
1580
1509
|
// No encryption => just use the payload.
|
|
@@ -1633,17 +1562,15 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1633
1562
|
});
|
|
1634
1563
|
}
|
|
1635
1564
|
|
|
1636
|
-
|
|
1565
|
+
const iv = voltClientWeb.randomBytes(16);
|
|
1566
|
+
if (this.#derivedKey) {
|
|
1637
1567
|
// Encrypt the response using the shared key and iv.
|
|
1638
|
-
responsePayload =
|
|
1639
|
-
responsePayload,
|
|
1640
|
-
this.#encryptKey,
|
|
1641
|
-
this.#encryptIV
|
|
1642
|
-
);
|
|
1568
|
+
responsePayload = voltClientWeb.aesEncrypt(this.#derivedKey, iv, responsePayload);
|
|
1643
1569
|
}
|
|
1644
1570
|
|
|
1645
1571
|
const invokeResponse = {
|
|
1646
|
-
invoke_id: this.#invokeId
|
|
1572
|
+
invoke_id: this.#invokeId,
|
|
1573
|
+
iv
|
|
1647
1574
|
};
|
|
1648
1575
|
|
|
1649
1576
|
if (this.#isJSON) {
|
|
@@ -1678,18 +1605,16 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1678
1605
|
endPayload = tunnelMethod.requestSerialize(endPayload);
|
|
1679
1606
|
}
|
|
1680
1607
|
|
|
1681
|
-
|
|
1608
|
+
const iv = voltClientWeb.randomBytes(16);
|
|
1609
|
+
if (this.#derivedKey) {
|
|
1682
1610
|
// Encrypt the response using the shared key and iv.
|
|
1683
|
-
endPayload =
|
|
1684
|
-
endPayload,
|
|
1685
|
-
this.#encryptKey,
|
|
1686
|
-
this.#encryptIV
|
|
1687
|
-
);
|
|
1611
|
+
endPayload = voltClientWeb.aesEncrypt(this.#derivedKey, iv, endPayload);
|
|
1688
1612
|
}
|
|
1689
1613
|
|
|
1690
1614
|
const invokeResponse = {
|
|
1691
1615
|
invoke_id: this.#invokeId,
|
|
1692
|
-
server_end: true
|
|
1616
|
+
server_end: true,
|
|
1617
|
+
iv
|
|
1693
1618
|
};
|
|
1694
1619
|
|
|
1695
1620
|
if (this.#isJSON) {
|
|
@@ -1706,7 +1631,6 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1706
1631
|
|
|
1707
1632
|
/* eslint-disable no-underscore-dangle */
|
|
1708
1633
|
|
|
1709
|
-
const { pki } = forge__default["default"];
|
|
1710
1634
|
const { filter, pick } = lodash__default["default"];
|
|
1711
1635
|
|
|
1712
1636
|
const log$1 = debug__default["default"]("volt-client-grpc:volt-client-internal");
|
|
@@ -1742,9 +1666,15 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
1742
1666
|
// This is the signing CA used by the volt.
|
|
1743
1667
|
this._credential.cache.ca = authenticateResponse.chain;
|
|
1744
1668
|
|
|
1745
|
-
//
|
|
1669
|
+
// The session id is assigned by the volt.
|
|
1746
1670
|
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
1747
1671
|
|
|
1672
|
+
if (!this._credential.cache.identity_did) {
|
|
1673
|
+
// Identity DID has been assigned by the volt.
|
|
1674
|
+
this._credential.cache.identity_did =
|
|
1675
|
+
authenticateResponse.identity_did;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1748
1678
|
this._credential.saveCache();
|
|
1749
1679
|
break;
|
|
1750
1680
|
}
|
|
@@ -1755,6 +1685,20 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
1755
1685
|
case "POLICY_DECISION_PROMPT":
|
|
1756
1686
|
case "POLICY_DECISION_PENDING": {
|
|
1757
1687
|
log$1("access pending approval - waiting...");
|
|
1688
|
+
|
|
1689
|
+
// This is the signing CA used by the volt.
|
|
1690
|
+
this._credential.cache.ca = authenticateResponse.chain;
|
|
1691
|
+
|
|
1692
|
+
// The session id is assigned by the volt.
|
|
1693
|
+
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
1694
|
+
|
|
1695
|
+
if (!this._credential.cache.identity_did) {
|
|
1696
|
+
// Identity DID has been assigned by the volt.
|
|
1697
|
+
this._credential.cache.identity_did =
|
|
1698
|
+
authenticateResponse.identity_did;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
this._credential.saveCache();
|
|
1758
1702
|
break;
|
|
1759
1703
|
}
|
|
1760
1704
|
default:
|
|
@@ -1779,7 +1723,10 @@ function findDIDDocumentService(document, serviceType) {
|
|
|
1779
1723
|
return matches;
|
|
1780
1724
|
}
|
|
1781
1725
|
|
|
1782
|
-
async function authenticateInternal(
|
|
1726
|
+
async function authenticateInternal(
|
|
1727
|
+
sessionOnly = false,
|
|
1728
|
+
ownDID = false
|
|
1729
|
+
) {
|
|
1783
1730
|
try {
|
|
1784
1731
|
// Check if we need to resolve a Relay volt.
|
|
1785
1732
|
if (
|
|
@@ -1791,7 +1738,7 @@ async function authenticateInternal(useDID = true) {
|
|
|
1791
1738
|
log$1("attempting to retrieve Relay information from %s", relayURL);
|
|
1792
1739
|
this._voltConfig.relay = await fetchVoltConfig.call(
|
|
1793
1740
|
this,
|
|
1794
|
-
`${relayURL}/
|
|
1741
|
+
`${relayURL}/discover`
|
|
1795
1742
|
);
|
|
1796
1743
|
|
|
1797
1744
|
if (this._voltConfig.relay.ca_pem) {
|
|
@@ -1816,8 +1763,8 @@ async function authenticateInternal(useDID = true) {
|
|
|
1816
1763
|
}
|
|
1817
1764
|
|
|
1818
1765
|
const keyPair = this._credential.getKey();
|
|
1819
|
-
const publicKeyPem = removeCarriageReturn(
|
|
1820
|
-
|
|
1766
|
+
const publicKeyPem = voltClientWeb.removeCarriageReturn(
|
|
1767
|
+
voltClientWeb.publicKeyToPem(keyPair.publicKey)
|
|
1821
1768
|
);
|
|
1822
1769
|
|
|
1823
1770
|
if (!this._credential.cache.bindIp) {
|
|
@@ -1830,12 +1777,12 @@ async function authenticateInternal(useDID = true) {
|
|
|
1830
1777
|
|
|
1831
1778
|
// Form the request message.
|
|
1832
1779
|
const authenticateRequest = {
|
|
1833
|
-
|
|
1780
|
+
client_name: this._config.client_name,
|
|
1834
1781
|
host: this._credential.cache.bindIp
|
|
1835
1782
|
};
|
|
1836
1783
|
|
|
1837
1784
|
if (this._voltConfig.challenge_code) {
|
|
1838
|
-
authenticateRequest.challenge = signBase64(
|
|
1785
|
+
authenticateRequest.challenge = voltClientWeb.signBase64(
|
|
1839
1786
|
keyPair.privateKey,
|
|
1840
1787
|
this._voltConfig.challenge_code
|
|
1841
1788
|
);
|
|
@@ -1845,68 +1792,58 @@ async function authenticateInternal(useDID = true) {
|
|
|
1845
1792
|
);
|
|
1846
1793
|
}
|
|
1847
1794
|
|
|
1848
|
-
if (
|
|
1795
|
+
if (sessionOnly) {
|
|
1796
|
+
// We want to create a session-based authentication based simply on our key, rather than a
|
|
1797
|
+
// fully-blown DID-based authentication.
|
|
1798
|
+
log$1("creating session-only request");
|
|
1849
1799
|
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
|
-
};
|
|
1800
|
+
} else if (!this._credential.cache.identity_did) {
|
|
1801
|
+
if (!ownDID) {
|
|
1802
|
+
// We don't want to have to manage our own DID, so we ask the Volt to do it for us.
|
|
1803
|
+
// The Volt will create a new DID and return it to us in the response.
|
|
1804
|
+
log$1("creating DID-based request");
|
|
1805
|
+
authenticateRequest.did_public_key = publicKeyPem;
|
|
1806
|
+
} else {
|
|
1807
|
+
// We want to create and manage our own DID.
|
|
1808
|
+
const did =
|
|
1809
|
+
(this._credential.cache.identity_did = `did:volt:${uuid.v4()}`);
|
|
1810
|
+
|
|
1811
|
+
// Order of properties in the DID document is important,
|
|
1812
|
+
// they must be in alphabetical order throughout the document.
|
|
1813
|
+
// This is because the DID document is signed and the signature
|
|
1814
|
+
// is over the entire document.
|
|
1815
|
+
const didDocument = {
|
|
1816
|
+
"@context": "https://www.w3.org/ns/did/v1",
|
|
1817
|
+
authentication: [`${did}#key-1`],
|
|
1818
|
+
controller: did,
|
|
1819
|
+
description: this._config.client_name,
|
|
1820
|
+
id: did,
|
|
1821
|
+
verificationMethod: [
|
|
1822
|
+
{
|
|
1823
|
+
id: `${did}#key-1`,
|
|
1824
|
+
publicKeyPem: publicKeyPem,
|
|
1825
|
+
type: "Ed25519VerificationKey2018"
|
|
1826
|
+
}
|
|
1827
|
+
]
|
|
1828
|
+
};
|
|
1895
1829
|
|
|
1896
|
-
|
|
1897
|
-
|
|
1830
|
+
// Set the DID document with proof in the authenticate request.
|
|
1831
|
+
authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
|
|
1898
1832
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1833
|
+
// Now sign the entire DID document, including the proof and set it in the
|
|
1834
|
+
// bind request. This is required by the Volt to verify that we have the private key for the DID.
|
|
1835
|
+
authenticateRequest.did_document_signature = voltClientWeb.signBase64(
|
|
1836
|
+
keyPair.privateKey,
|
|
1837
|
+
authenticateRequest.did_document
|
|
1838
|
+
);
|
|
1905
1839
|
|
|
1906
|
-
|
|
1907
|
-
|
|
1840
|
+
log$1("did document is %s", authenticateRequest.did_document);
|
|
1841
|
+
log$1("did signature is %s", authenticateRequest.did_document_signature);
|
|
1842
|
+
}
|
|
1908
1843
|
} else {
|
|
1909
|
-
|
|
1844
|
+
// We already have a DID cached, so re-use it.
|
|
1845
|
+
log$1("using cached DID %s", this._credential.cache.identity_did);
|
|
1846
|
+
authenticateRequest.did = this._credential.cache.identity_did;
|
|
1910
1847
|
}
|
|
1911
1848
|
|
|
1912
1849
|
log$1("authenticate TTL is ", this._config.bindRequestTTL);
|
|
@@ -1975,6 +1912,11 @@ function connectInternal(helloPayload) {
|
|
|
1975
1912
|
this._voltConnection.on("error", (err) => {
|
|
1976
1913
|
// Should auto-reconnect.
|
|
1977
1914
|
log$1("Volt connection error [%s]", err.message);
|
|
1915
|
+
if (this._connected) {
|
|
1916
|
+
onDisconnected();
|
|
1917
|
+
this.emit("connected", false);
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1978
1920
|
if (err.message.endsWith("session invalid")) {
|
|
1979
1921
|
log$1("Volt session invalid - clearing session");
|
|
1980
1922
|
delete this._credential.cache.session_id;
|
|
@@ -1982,11 +1924,6 @@ function connectInternal(helloPayload) {
|
|
|
1982
1924
|
this._credential.saveCache();
|
|
1983
1925
|
}
|
|
1984
1926
|
|
|
1985
|
-
if (this._connected) {
|
|
1986
|
-
onDisconnected();
|
|
1987
|
-
this.emit("connected", false);
|
|
1988
|
-
}
|
|
1989
|
-
|
|
1990
1927
|
this.emit("connectionError", err);
|
|
1991
1928
|
});
|
|
1992
1929
|
|
|
@@ -2000,10 +1937,24 @@ function connectInternal(helloPayload) {
|
|
|
2000
1937
|
|
|
2001
1938
|
this._voltConnection.on("invoke_request", (invoke_request) => {
|
|
2002
1939
|
const invokeId = invoke_request.invoke_id;
|
|
2003
|
-
|
|
2004
|
-
|
|
1940
|
+
|
|
1941
|
+
let rpcInvocation = this._activeRPC[invokeId];
|
|
1942
|
+
|
|
1943
|
+
if (rpcInvocation) {
|
|
1944
|
+
if (rpcInvocation.started) {
|
|
1945
|
+
rpcInvocation.parsePayload(invoke_request);
|
|
1946
|
+
} else {
|
|
1947
|
+
rpcInvocation
|
|
1948
|
+
.initialise(invoke_request)
|
|
1949
|
+
.then(() => {
|
|
1950
|
+
this.emit("invoke_request", rpcInvocation);
|
|
1951
|
+
})
|
|
1952
|
+
.catch((err) => {
|
|
1953
|
+
log$1("invoke_request - error [%s]", err.message);
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
2005
1956
|
} else {
|
|
2006
|
-
|
|
1957
|
+
rpcInvocation = new RpcInvocation(this, this._voltConnection);
|
|
2007
1958
|
|
|
2008
1959
|
rpcInvocation.on("end", () => {
|
|
2009
1960
|
log$1("removing active RPC [%s]", invokeId);
|
|
@@ -2012,9 +1963,11 @@ function connectInternal(helloPayload) {
|
|
|
2012
1963
|
|
|
2013
1964
|
rpcInvocation
|
|
2014
1965
|
.initialise(invoke_request)
|
|
2015
|
-
.then(() => {
|
|
1966
|
+
.then((response) => {
|
|
2016
1967
|
this._activeRPC[invokeId] = rpcInvocation;
|
|
2017
|
-
|
|
1968
|
+
if (!response.key_exchange) {
|
|
1969
|
+
this.emit("invoke_request", rpcInvocation);
|
|
1970
|
+
}
|
|
2018
1971
|
})
|
|
2019
1972
|
.catch((err) => {
|
|
2020
1973
|
log$1("invoke_request - error [%s]", err.message);
|
|
@@ -2160,6 +2113,7 @@ async function fetchVoltConfig(discovery_url) {
|
|
|
2160
2113
|
"public_key",
|
|
2161
2114
|
"fingerprint",
|
|
2162
2115
|
"address",
|
|
2116
|
+
"http_address",
|
|
2163
2117
|
"ca_pem",
|
|
2164
2118
|
"challenge_code",
|
|
2165
2119
|
"cloud",
|
|
@@ -2247,6 +2201,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2247
2201
|
/**
|
|
2248
2202
|
* Initialises a binding and connection to the volt.
|
|
2249
2203
|
* @param {string} [config] - the location of the Volt configuration file, or a configuration object
|
|
2204
|
+
* @param {object} [extras] - additional configuration properties
|
|
2250
2205
|
*/
|
|
2251
2206
|
async initialise(config, extras = {}) {
|
|
2252
2207
|
try {
|
|
@@ -2262,10 +2217,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2262
2217
|
configPath = path__default["default"].resolve(config);
|
|
2263
2218
|
log("Attempt to load config from file %s", configPath);
|
|
2264
2219
|
try {
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2220
|
+
if (fs__default["default"].existsSync(configPath)) {
|
|
2221
|
+
const configContents = await readFile(
|
|
2222
|
+
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)))
|
|
2223
|
+
);
|
|
2224
|
+
configJSON = JSON.parse(configContents);
|
|
2225
|
+
} else {
|
|
2226
|
+
// There is currently no file at the given configuration path.
|
|
2227
|
+
configJSON = {};
|
|
2228
|
+
}
|
|
2269
2229
|
} catch (err) {
|
|
2270
2230
|
log("failure loading config file %s [%s]", configPath, err.message);
|
|
2271
2231
|
throw new Error(
|
|
@@ -2282,15 +2242,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2282
2242
|
throw new Error("config argument is required to be an object");
|
|
2283
2243
|
}
|
|
2284
2244
|
|
|
2245
|
+
this._config = { ...extras, ...configJSON };
|
|
2246
|
+
|
|
2285
2247
|
if (
|
|
2286
|
-
!
|
|
2287
|
-
typeof
|
|
2248
|
+
!this._config.client_name ||
|
|
2249
|
+
typeof this._config.client_name !== "string"
|
|
2288
2250
|
) {
|
|
2289
2251
|
throw new Error("client_name property required in config");
|
|
2290
2252
|
}
|
|
2291
2253
|
|
|
2292
|
-
this._config = { ...configJSON, ...extras };
|
|
2293
|
-
|
|
2294
2254
|
let voltDID = "";
|
|
2295
2255
|
let voltHttpAddress = "";
|
|
2296
2256
|
if (typeof this._config.volt === "string") {
|
|
@@ -2299,6 +2259,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2299
2259
|
} else {
|
|
2300
2260
|
voltHttpAddress = this._config.volt;
|
|
2301
2261
|
}
|
|
2262
|
+
delete this._config.volt;
|
|
2302
2263
|
} else if (typeof this._config.volt !== "object") {
|
|
2303
2264
|
throw new Error("configuration is missing the 'volt' object");
|
|
2304
2265
|
} else {
|
|
@@ -2313,7 +2274,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2313
2274
|
} else if (voltHttpAddress && !this._config?.volt?.id) {
|
|
2314
2275
|
const discoConfig = await fetchVoltConfig.call(
|
|
2315
2276
|
this,
|
|
2316
|
-
`${voltHttpAddress}/
|
|
2277
|
+
`${voltHttpAddress}/discover`
|
|
2317
2278
|
);
|
|
2318
2279
|
this._config = {
|
|
2319
2280
|
...this._config,
|
|
@@ -2322,8 +2283,12 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2322
2283
|
}
|
|
2323
2284
|
|
|
2324
2285
|
this._voltConfig = this._config.volt;
|
|
2325
|
-
|
|
2326
|
-
|
|
2286
|
+
if (voltDID) {
|
|
2287
|
+
this._voltConfig.did = voltDID;
|
|
2288
|
+
}
|
|
2289
|
+
if (voltHttpAddress) {
|
|
2290
|
+
this._voltConfig.http_address = voltHttpAddress;
|
|
2291
|
+
}
|
|
2327
2292
|
|
|
2328
2293
|
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
2329
2294
|
throw new Error("'id' property is missing in Volt configuration");
|
|
@@ -2371,9 +2336,12 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2371
2336
|
if (!isBound) {
|
|
2372
2337
|
// This is usually because of request being denied.
|
|
2373
2338
|
throw new Error("Volt authentication failed");
|
|
2339
|
+
} else {
|
|
2340
|
+
// Perist the configuration.
|
|
2341
|
+
this._config = this._credential.saveCache();
|
|
2374
2342
|
}
|
|
2375
2343
|
|
|
2376
|
-
return Promise.resolve();
|
|
2344
|
+
return Promise.resolve(this._config);
|
|
2377
2345
|
} catch (err) {
|
|
2378
2346
|
log("Failure starting Volt client [%s]", err.message);
|
|
2379
2347
|
throw err;
|
|
@@ -2840,6 +2808,13 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2840
2808
|
});
|
|
2841
2809
|
}
|
|
2842
2810
|
|
|
2811
|
+
SqlTransaction(request) {
|
|
2812
|
+
const grpcClient = this.getVoltAPIClient();
|
|
2813
|
+
|
|
2814
|
+
const call = new GRPCCall(this, "Transaction", "METHOD_TYPE_BIDI");
|
|
2815
|
+
return call.start(grpcClient, request);
|
|
2816
|
+
}
|
|
2817
|
+
|
|
2843
2818
|
/**
|
|
2844
2819
|
* WireAPI
|
|
2845
2820
|
*/
|