@tdxvolt/volt-client-grpc 0.16.0-beta.10 → 0.16.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/lib/index.cjs +431 -455
- package/package.json +2 -4
- package/protobuf/tdx/volt_api/data/v1/sqlite_database_api.proto +49 -13
- package/protobuf/tdx/volt_api/data/v1/sqlite_server_api.proto +3 -0
- package/protobuf/tdx/volt_api/volt/v1/discovery_api.proto +0 -4
- package/protobuf/tdx/volt_api/volt/v1/ssi.proto +31 -5
- package/protobuf/tdx/volt_api/volt/v1/ssi_api.proto +119 -31
- package/protobuf/tdx/volt_api/volt/v1/terminal_api.proto +42 -0
- package/protobuf/tdx/volt_api/volt/v1/volt.proto +146 -139
- package/protobuf/tdx/volt_api/volt/v1/volt_api.proto +165 -270
- package/src/constants.js +3 -5
- package/src/grpc-call.js +142 -51
- package/src/grpc-utils.js +8 -4
- package/src/rpc-invocation.js +94 -46
- package/src/utils.js +5 -119
- package/src/volt-client-internal.js +106 -73
- package/src/volt-client.js +33 -12
- package/src/volt-connection.js +22 -12
- package/src/volt-credential.js +37 -124
package/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
578
|
|
|
751
|
-
|
|
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
|
-
|
|
775
|
-
static rsaDecrypt(key, buffer) {
|
|
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,
|
|
@@ -1301,13 +1183,18 @@ function _doConnect(connectHello) {
|
|
|
1301
1183
|
|
|
1302
1184
|
const grpcClient = this._voltClient.getVoltAPIClient();
|
|
1303
1185
|
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
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);
|
|
1311
1198
|
|
|
1312
1199
|
this.call.on("error", (err) => {
|
|
1313
1200
|
log$3("error on connection stream [%s]", err.message);
|
|
@@ -1325,17 +1212,22 @@ function _doConnect(connectHello) {
|
|
|
1325
1212
|
clearTimeout(this._pingTimeoutTimer);
|
|
1326
1213
|
}
|
|
1327
1214
|
|
|
1328
|
-
// Schedule the next ping.
|
|
1329
|
-
_startPingTimer.call(this);
|
|
1330
|
-
|
|
1331
1215
|
this.emit("ping", response.ping.timestamp);
|
|
1332
1216
|
break;
|
|
1333
1217
|
}
|
|
1334
1218
|
case "acknowledge": {
|
|
1335
1219
|
if (!this._connectionId) {
|
|
1336
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
|
+
|
|
1337
1226
|
log$3("connection id is %s", this._connectionId);
|
|
1338
1227
|
this.emit("connected", this._connectionId);
|
|
1228
|
+
|
|
1229
|
+
// Schedule the next ping.
|
|
1230
|
+
_startPingTimer.call(this);
|
|
1339
1231
|
}
|
|
1340
1232
|
break;
|
|
1341
1233
|
}
|
|
@@ -1363,8 +1255,6 @@ function _doConnect(connectHello) {
|
|
|
1363
1255
|
});
|
|
1364
1256
|
|
|
1365
1257
|
this.call.start(grpcClient, helloPayload);
|
|
1366
|
-
|
|
1367
|
-
_startPingTimer.call(this);
|
|
1368
1258
|
}
|
|
1369
1259
|
|
|
1370
1260
|
function _startPingTimer() {
|
|
@@ -1391,6 +1281,8 @@ function _startPingTimer() {
|
|
|
1391
1281
|
_connectionTimeoutHandler.bind(this),
|
|
1392
1282
|
this._timeoutInterval
|
|
1393
1283
|
);
|
|
1284
|
+
|
|
1285
|
+
_startPingTimer.call(this);
|
|
1394
1286
|
}, this._pingInterval);
|
|
1395
1287
|
}
|
|
1396
1288
|
|
|
@@ -1438,13 +1330,13 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1438
1330
|
#voltClient = null;
|
|
1439
1331
|
#voltConnection = null;
|
|
1440
1332
|
#token = null;
|
|
1441
|
-
#encryptKey = null;
|
|
1442
|
-
#encryptIV = null;
|
|
1443
1333
|
#invokeId = null;
|
|
1444
1334
|
#payload = null;
|
|
1445
1335
|
#isJSON = false;
|
|
1446
1336
|
#methodDescriptor = null;
|
|
1447
1337
|
#methodName = null;
|
|
1338
|
+
#derivedKey = null;
|
|
1339
|
+
#started = false;
|
|
1448
1340
|
|
|
1449
1341
|
constructor(voltClient, voltConnection) {
|
|
1450
1342
|
super();
|
|
@@ -1488,51 +1380,89 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1488
1380
|
this.#methodDescriptor = methodDescriptor;
|
|
1489
1381
|
}
|
|
1490
1382
|
|
|
1491
|
-
|
|
1383
|
+
get started() {
|
|
1384
|
+
return this.#started;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
#keyExchange(token) {
|
|
1492
1388
|
// We don't verify the token at this point, as we don't know the public key.
|
|
1493
1389
|
// The rpc implementation should do the verification using the Volt API (e.g. CanAccessResource).
|
|
1494
1390
|
this.#token = jwt__default["default"].decode(token);
|
|
1495
1391
|
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
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
|
+
);
|
|
1507
1407
|
|
|
1508
|
-
|
|
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
|
+
});
|
|
1432
|
+
|
|
1433
|
+
return Promise.resolve(keyExchangeResponse);
|
|
1509
1434
|
}
|
|
1510
1435
|
|
|
1511
1436
|
initialise(invoke_request) {
|
|
1512
1437
|
this.#invokeId = invoke_request.invoke_id;
|
|
1513
1438
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
return this.parsePayload(invoke_request);
|
|
1518
|
-
})
|
|
1519
|
-
.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) => {
|
|
1520
1442
|
log$2("failure initialising invocation: %s", err.message);
|
|
1521
1443
|
return Promise.reject(err);
|
|
1522
1444
|
});
|
|
1445
|
+
} else {
|
|
1446
|
+
// We already have the derived key, so we can just parse the payload.
|
|
1447
|
+
return this.parsePayload(invoke_request);
|
|
1448
|
+
}
|
|
1523
1449
|
}
|
|
1524
1450
|
|
|
1525
1451
|
parsePayload(invoke_request) {
|
|
1452
|
+
this.#started = true;
|
|
1453
|
+
|
|
1526
1454
|
return new Promise((resolve, reject) => {
|
|
1455
|
+
const encryptIV = invoke_request.iv;
|
|
1456
|
+
|
|
1527
1457
|
if (invoke_request.payload) {
|
|
1528
1458
|
let decryptedPayload;
|
|
1529
1459
|
|
|
1530
|
-
if (
|
|
1460
|
+
if (encryptIV) {
|
|
1531
1461
|
// Decrypt the actual payload using the shared key and iv.
|
|
1532
|
-
decryptedPayload =
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1462
|
+
decryptedPayload = voltClientWeb.aesDecrypt(
|
|
1463
|
+
this.#derivedKey,
|
|
1464
|
+
encryptIV,
|
|
1465
|
+
invoke_request.payload
|
|
1536
1466
|
);
|
|
1537
1467
|
} else {
|
|
1538
1468
|
// No encryption => just use the payload.
|
|
@@ -1568,12 +1498,12 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1568
1498
|
|
|
1569
1499
|
this.#isJSON = true;
|
|
1570
1500
|
|
|
1571
|
-
if (this.#
|
|
1501
|
+
if (this.#derivedKey) {
|
|
1572
1502
|
// Decrypt the actual payload using the shared key and iv.
|
|
1573
|
-
decryptedPayload =
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1503
|
+
decryptedPayload = voltClientWeb.aesDecrypt(
|
|
1504
|
+
this.#derivedKey,
|
|
1505
|
+
invoke_request.iv,
|
|
1506
|
+
Buffer.from(invoke_request.json_payload, "base64")
|
|
1577
1507
|
);
|
|
1578
1508
|
} else {
|
|
1579
1509
|
// No encryption => just use the payload.
|
|
@@ -1632,17 +1562,15 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1632
1562
|
});
|
|
1633
1563
|
}
|
|
1634
1564
|
|
|
1635
|
-
|
|
1565
|
+
const iv = voltClientWeb.randomBytes(16);
|
|
1566
|
+
if (this.#derivedKey) {
|
|
1636
1567
|
// Encrypt the response using the shared key and iv.
|
|
1637
|
-
responsePayload =
|
|
1638
|
-
responsePayload,
|
|
1639
|
-
this.#encryptKey,
|
|
1640
|
-
this.#encryptIV
|
|
1641
|
-
);
|
|
1568
|
+
responsePayload = voltClientWeb.aesEncrypt(this.#derivedKey, iv, responsePayload);
|
|
1642
1569
|
}
|
|
1643
1570
|
|
|
1644
1571
|
const invokeResponse = {
|
|
1645
|
-
invoke_id: this.#invokeId
|
|
1572
|
+
invoke_id: this.#invokeId,
|
|
1573
|
+
iv
|
|
1646
1574
|
};
|
|
1647
1575
|
|
|
1648
1576
|
if (this.#isJSON) {
|
|
@@ -1677,18 +1605,16 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1677
1605
|
endPayload = tunnelMethod.requestSerialize(endPayload);
|
|
1678
1606
|
}
|
|
1679
1607
|
|
|
1680
|
-
|
|
1608
|
+
const iv = voltClientWeb.randomBytes(16);
|
|
1609
|
+
if (this.#derivedKey) {
|
|
1681
1610
|
// Encrypt the response using the shared key and iv.
|
|
1682
|
-
endPayload =
|
|
1683
|
-
endPayload,
|
|
1684
|
-
this.#encryptKey,
|
|
1685
|
-
this.#encryptIV
|
|
1686
|
-
);
|
|
1611
|
+
endPayload = voltClientWeb.aesEncrypt(this.#derivedKey, iv, endPayload);
|
|
1687
1612
|
}
|
|
1688
1613
|
|
|
1689
1614
|
const invokeResponse = {
|
|
1690
1615
|
invoke_id: this.#invokeId,
|
|
1691
|
-
server_end: true
|
|
1616
|
+
server_end: true,
|
|
1617
|
+
iv
|
|
1692
1618
|
};
|
|
1693
1619
|
|
|
1694
1620
|
if (this.#isJSON) {
|
|
@@ -1705,7 +1631,6 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1705
1631
|
|
|
1706
1632
|
/* eslint-disable no-underscore-dangle */
|
|
1707
1633
|
|
|
1708
|
-
const { pki } = forge__default["default"];
|
|
1709
1634
|
const { filter, pick } = lodash__default["default"];
|
|
1710
1635
|
|
|
1711
1636
|
const log$1 = debug__default["default"]("volt-client-grpc:volt-client-internal");
|
|
@@ -1741,9 +1666,15 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
1741
1666
|
// This is the signing CA used by the volt.
|
|
1742
1667
|
this._credential.cache.ca = authenticateResponse.chain;
|
|
1743
1668
|
|
|
1744
|
-
//
|
|
1669
|
+
// The session id is assigned by the volt.
|
|
1745
1670
|
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
1746
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
|
+
|
|
1747
1678
|
this._credential.saveCache();
|
|
1748
1679
|
break;
|
|
1749
1680
|
}
|
|
@@ -1754,6 +1685,20 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
1754
1685
|
case "POLICY_DECISION_PROMPT":
|
|
1755
1686
|
case "POLICY_DECISION_PENDING": {
|
|
1756
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();
|
|
1757
1702
|
break;
|
|
1758
1703
|
}
|
|
1759
1704
|
default:
|
|
@@ -1778,7 +1723,10 @@ function findDIDDocumentService(document, serviceType) {
|
|
|
1778
1723
|
return matches;
|
|
1779
1724
|
}
|
|
1780
1725
|
|
|
1781
|
-
async function authenticateInternal(
|
|
1726
|
+
async function authenticateInternal(
|
|
1727
|
+
sessionOnly = false,
|
|
1728
|
+
ownDID = false
|
|
1729
|
+
) {
|
|
1782
1730
|
try {
|
|
1783
1731
|
// Check if we need to resolve a Relay volt.
|
|
1784
1732
|
if (
|
|
@@ -1790,7 +1738,7 @@ async function authenticateInternal(useDID = true) {
|
|
|
1790
1738
|
log$1("attempting to retrieve Relay information from %s", relayURL);
|
|
1791
1739
|
this._voltConfig.relay = await fetchVoltConfig.call(
|
|
1792
1740
|
this,
|
|
1793
|
-
`${relayURL}/
|
|
1741
|
+
`${relayURL}/discover`
|
|
1794
1742
|
);
|
|
1795
1743
|
|
|
1796
1744
|
if (this._voltConfig.relay.ca_pem) {
|
|
@@ -1815,8 +1763,8 @@ async function authenticateInternal(useDID = true) {
|
|
|
1815
1763
|
}
|
|
1816
1764
|
|
|
1817
1765
|
const keyPair = this._credential.getKey();
|
|
1818
|
-
const publicKeyPem = removeCarriageReturn(
|
|
1819
|
-
|
|
1766
|
+
const publicKeyPem = voltClientWeb.removeCarriageReturn(
|
|
1767
|
+
voltClientWeb.publicKeyToPem(keyPair.publicKey)
|
|
1820
1768
|
);
|
|
1821
1769
|
|
|
1822
1770
|
if (!this._credential.cache.bindIp) {
|
|
@@ -1829,12 +1777,12 @@ async function authenticateInternal(useDID = true) {
|
|
|
1829
1777
|
|
|
1830
1778
|
// Form the request message.
|
|
1831
1779
|
const authenticateRequest = {
|
|
1832
|
-
|
|
1780
|
+
client_name: this._config.client_name,
|
|
1833
1781
|
host: this._credential.cache.bindIp
|
|
1834
1782
|
};
|
|
1835
1783
|
|
|
1836
1784
|
if (this._voltConfig.challenge_code) {
|
|
1837
|
-
authenticateRequest.challenge = signBase64(
|
|
1785
|
+
authenticateRequest.challenge = voltClientWeb.signBase64(
|
|
1838
1786
|
keyPair.privateKey,
|
|
1839
1787
|
this._voltConfig.challenge_code
|
|
1840
1788
|
);
|
|
@@ -1844,68 +1792,58 @@ async function authenticateInternal(useDID = true) {
|
|
|
1844
1792
|
);
|
|
1845
1793
|
}
|
|
1846
1794
|
|
|
1847
|
-
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");
|
|
1848
1799
|
authenticateRequest.public_key = publicKeyPem;
|
|
1849
|
-
} else if (!this._credential.cache.
|
|
1850
|
-
|
|
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
|
-
// Sign the DID document.
|
|
1879
|
-
const documentDigest = signBase64(
|
|
1880
|
-
keyPair.privateKey,
|
|
1881
|
-
signDocument
|
|
1882
|
-
);
|
|
1883
|
-
|
|
1884
|
-
const proofSignature = jwsSignature(documentDigest);
|
|
1885
|
-
|
|
1886
|
-
// Create the DID document proof.
|
|
1887
|
-
didDocument.proof = {
|
|
1888
|
-
created: new Date().toISOString(),
|
|
1889
|
-
jws: proofSignature,
|
|
1890
|
-
proofPurpose: "assertionMethod",
|
|
1891
|
-
type: "RsaSignature2018",
|
|
1892
|
-
verificationMethod: `${did}#key-1`
|
|
1893
|
-
};
|
|
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
|
+
};
|
|
1894
1829
|
|
|
1895
|
-
|
|
1896
|
-
|
|
1830
|
+
// Set the DID document with proof in the authenticate request.
|
|
1831
|
+
authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
|
|
1897
1832
|
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
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
|
+
);
|
|
1904
1839
|
|
|
1905
|
-
|
|
1906
|
-
|
|
1840
|
+
log$1("did document is %s", authenticateRequest.did_document);
|
|
1841
|
+
log$1("did signature is %s", authenticateRequest.did_document_signature);
|
|
1842
|
+
}
|
|
1907
1843
|
} else {
|
|
1908
|
-
|
|
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;
|
|
1909
1847
|
}
|
|
1910
1848
|
|
|
1911
1849
|
log$1("authenticate TTL is ", this._config.bindRequestTTL);
|
|
@@ -1999,10 +1937,24 @@ function connectInternal(helloPayload) {
|
|
|
1999
1937
|
|
|
2000
1938
|
this._voltConnection.on("invoke_request", (invoke_request) => {
|
|
2001
1939
|
const invokeId = invoke_request.invoke_id;
|
|
2002
|
-
|
|
2003
|
-
|
|
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
|
+
}
|
|
2004
1956
|
} else {
|
|
2005
|
-
|
|
1957
|
+
rpcInvocation = new RpcInvocation(this, this._voltConnection);
|
|
2006
1958
|
|
|
2007
1959
|
rpcInvocation.on("end", () => {
|
|
2008
1960
|
log$1("removing active RPC [%s]", invokeId);
|
|
@@ -2011,9 +1963,11 @@ function connectInternal(helloPayload) {
|
|
|
2011
1963
|
|
|
2012
1964
|
rpcInvocation
|
|
2013
1965
|
.initialise(invoke_request)
|
|
2014
|
-
.then(() => {
|
|
1966
|
+
.then((response) => {
|
|
2015
1967
|
this._activeRPC[invokeId] = rpcInvocation;
|
|
2016
|
-
|
|
1968
|
+
if (!response.key_exchange) {
|
|
1969
|
+
this.emit("invoke_request", rpcInvocation);
|
|
1970
|
+
}
|
|
2017
1971
|
})
|
|
2018
1972
|
.catch((err) => {
|
|
2019
1973
|
log$1("invoke_request - error [%s]", err.message);
|
|
@@ -2159,6 +2113,7 @@ async function fetchVoltConfig(discovery_url) {
|
|
|
2159
2113
|
"public_key",
|
|
2160
2114
|
"fingerprint",
|
|
2161
2115
|
"address",
|
|
2116
|
+
"http_address",
|
|
2162
2117
|
"ca_pem",
|
|
2163
2118
|
"challenge_code",
|
|
2164
2119
|
"cloud",
|
|
@@ -2246,6 +2201,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2246
2201
|
/**
|
|
2247
2202
|
* Initialises a binding and connection to the volt.
|
|
2248
2203
|
* @param {string} [config] - the location of the Volt configuration file, or a configuration object
|
|
2204
|
+
* @param {object} [extras] - additional configuration properties
|
|
2249
2205
|
*/
|
|
2250
2206
|
async initialise(config, extras = {}) {
|
|
2251
2207
|
try {
|
|
@@ -2261,10 +2217,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2261
2217
|
configPath = path__default["default"].resolve(config);
|
|
2262
2218
|
log("Attempt to load config from file %s", configPath);
|
|
2263
2219
|
try {
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
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
|
+
}
|
|
2268
2229
|
} catch (err) {
|
|
2269
2230
|
log("failure loading config file %s [%s]", configPath, err.message);
|
|
2270
2231
|
throw new Error(
|
|
@@ -2281,15 +2242,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2281
2242
|
throw new Error("config argument is required to be an object");
|
|
2282
2243
|
}
|
|
2283
2244
|
|
|
2245
|
+
this._config = { ...extras, ...configJSON };
|
|
2246
|
+
|
|
2284
2247
|
if (
|
|
2285
|
-
!
|
|
2286
|
-
typeof
|
|
2248
|
+
!this._config.client_name ||
|
|
2249
|
+
typeof this._config.client_name !== "string"
|
|
2287
2250
|
) {
|
|
2288
2251
|
throw new Error("client_name property required in config");
|
|
2289
2252
|
}
|
|
2290
2253
|
|
|
2291
|
-
this._config = { ...configJSON, ...extras };
|
|
2292
|
-
|
|
2293
2254
|
let voltDID = "";
|
|
2294
2255
|
let voltHttpAddress = "";
|
|
2295
2256
|
if (typeof this._config.volt === "string") {
|
|
@@ -2298,6 +2259,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2298
2259
|
} else {
|
|
2299
2260
|
voltHttpAddress = this._config.volt;
|
|
2300
2261
|
}
|
|
2262
|
+
delete this._config.volt;
|
|
2301
2263
|
} else if (typeof this._config.volt !== "object") {
|
|
2302
2264
|
throw new Error("configuration is missing the 'volt' object");
|
|
2303
2265
|
} else {
|
|
@@ -2312,7 +2274,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2312
2274
|
} else if (voltHttpAddress && !this._config?.volt?.id) {
|
|
2313
2275
|
const discoConfig = await fetchVoltConfig.call(
|
|
2314
2276
|
this,
|
|
2315
|
-
`${voltHttpAddress}/
|
|
2277
|
+
`${voltHttpAddress}/discover`
|
|
2316
2278
|
);
|
|
2317
2279
|
this._config = {
|
|
2318
2280
|
...this._config,
|
|
@@ -2321,8 +2283,12 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2321
2283
|
}
|
|
2322
2284
|
|
|
2323
2285
|
this._voltConfig = this._config.volt;
|
|
2324
|
-
|
|
2325
|
-
|
|
2286
|
+
if (voltDID) {
|
|
2287
|
+
this._voltConfig.did = voltDID;
|
|
2288
|
+
}
|
|
2289
|
+
if (voltHttpAddress) {
|
|
2290
|
+
this._voltConfig.http_address = voltHttpAddress;
|
|
2291
|
+
}
|
|
2326
2292
|
|
|
2327
2293
|
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
2328
2294
|
throw new Error("'id' property is missing in Volt configuration");
|
|
@@ -2370,9 +2336,12 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2370
2336
|
if (!isBound) {
|
|
2371
2337
|
// This is usually because of request being denied.
|
|
2372
2338
|
throw new Error("Volt authentication failed");
|
|
2339
|
+
} else {
|
|
2340
|
+
// Perist the configuration.
|
|
2341
|
+
this._config = this._credential.saveCache();
|
|
2373
2342
|
}
|
|
2374
2343
|
|
|
2375
|
-
return Promise.resolve();
|
|
2344
|
+
return Promise.resolve(this._config);
|
|
2376
2345
|
} catch (err) {
|
|
2377
2346
|
log("Failure starting Volt client [%s]", err.message);
|
|
2378
2347
|
throw err;
|
|
@@ -2839,6 +2808,13 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2839
2808
|
});
|
|
2840
2809
|
}
|
|
2841
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
|
+
|
|
2842
2818
|
/**
|
|
2843
2819
|
* WireAPI
|
|
2844
2820
|
*/
|