@tdxvolt/volt-client-grpc 0.18.0 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +257 -107
- package/package.json +2 -2
- package/src/constants.js +1 -1
- package/src/grpc-call.js +105 -54
- package/src/grpc-utils.js +3 -3
- package/src/rpc-invocation.js +47 -11
- package/src/volt-client-internal.js +36 -21
- package/src/volt-client.js +43 -13
- package/src/volt-connection.js +16 -2
- package/src/volt-credential.js +6 -2
package/lib/index.cjs
CHANGED
|
@@ -29,7 +29,7 @@ var jwt__default = /*#__PURE__*/_interopDefaultLegacy(jwt);
|
|
|
29
29
|
|
|
30
30
|
const constants = {
|
|
31
31
|
authTokenName: "volt-token",
|
|
32
|
-
defaultDIDHostName: "
|
|
32
|
+
defaultDIDHostName: "coreid.com",
|
|
33
33
|
didServiceType: {
|
|
34
34
|
voltConfig: "volt-discovery"
|
|
35
35
|
},
|
|
@@ -123,7 +123,7 @@ function createClient(
|
|
|
123
123
|
) {
|
|
124
124
|
let grpcCredentials;
|
|
125
125
|
if (credentials) {
|
|
126
|
-
const relaying = !!credentials.config?.volt?.relay;
|
|
126
|
+
const relaying = !!credentials.config?.volt?.relay && !Array.isArray(credentials.config?.volt?.relay);
|
|
127
127
|
const ca = relaying
|
|
128
128
|
? credentials.config?.volt?.relay?.ca_pem
|
|
129
129
|
: credentials.cache.ca;
|
|
@@ -131,8 +131,8 @@ function createClient(
|
|
|
131
131
|
const { key } = credentials.cache;
|
|
132
132
|
|
|
133
133
|
if (key && cert) {
|
|
134
|
-
const
|
|
135
|
-
const tlsOptions = createSecureContextOptions(
|
|
134
|
+
const cryptoOptions = { ca, cert, key };
|
|
135
|
+
const tlsOptions = createSecureContextOptions(cryptoOptions);
|
|
136
136
|
grpcCredentials = grpc.credentials.createSsl(
|
|
137
137
|
tlsOptions.ca,
|
|
138
138
|
tlsOptions.key,
|
|
@@ -395,7 +395,7 @@ class VoltCredential {
|
|
|
395
395
|
this._cryptoCache.ca = this._voltConfig.ca_pem;
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
// Extract Volt public key
|
|
398
|
+
// Extract Volt public key.
|
|
399
399
|
this._voltPublicKey = this._voltConfig.public_key;
|
|
400
400
|
|
|
401
401
|
const voltKey = keyFromPem$2(this._voltPublicKey).publicKey;
|
|
@@ -435,6 +435,10 @@ class VoltCredential {
|
|
|
435
435
|
return this._voltFingerprint;
|
|
436
436
|
}
|
|
437
437
|
|
|
438
|
+
get key() {
|
|
439
|
+
return this.getKey();
|
|
440
|
+
}
|
|
441
|
+
|
|
438
442
|
getKey() {
|
|
439
443
|
const key = keyFromPem$2(this._cryptoCache.key);
|
|
440
444
|
return key;
|
|
@@ -450,7 +454,7 @@ class VoltCredential {
|
|
|
450
454
|
return Promise.resolve(this.getKey());
|
|
451
455
|
} else {
|
|
452
456
|
log$5("creating key");
|
|
453
|
-
this._cryptoCache.key = createSigningKey();
|
|
457
|
+
this._cryptoCache.key = JSON.parse(createSigningKey()).private_pem;
|
|
454
458
|
const key = keyFromPem$2(this._cryptoCache.key);
|
|
455
459
|
log$5("successfully created key");
|
|
456
460
|
return Promise.resolve(key);
|
|
@@ -658,6 +662,8 @@ const {
|
|
|
658
662
|
x25519: x25519$1
|
|
659
663
|
} = js.voltUtils;
|
|
660
664
|
|
|
665
|
+
const invokeMethod = "Invoke";
|
|
666
|
+
|
|
661
667
|
const log$4 = debug__default["default"]("volt-client-grpc:grpc-call");
|
|
662
668
|
|
|
663
669
|
function _prepareInvokeRequest(
|
|
@@ -669,10 +675,10 @@ function _prepareInvokeRequest(
|
|
|
669
675
|
this._service?.service_description.host_type ===
|
|
670
676
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
671
677
|
|
|
672
|
-
const relaying = this._voltClient.
|
|
678
|
+
const relaying = this._voltClient.isRelayed || isServiceRelayed;
|
|
673
679
|
|
|
674
680
|
let targetDID = [];
|
|
675
|
-
if (this._voltClient.
|
|
681
|
+
if (this._voltClient.isRelayed) {
|
|
676
682
|
targetDID.push(this._voltClient.voltConfig.id);
|
|
677
683
|
}
|
|
678
684
|
|
|
@@ -697,20 +703,23 @@ function _prepareInvokeRequest(
|
|
|
697
703
|
let invokeRequest;
|
|
698
704
|
let callMethod = method;
|
|
699
705
|
|
|
706
|
+
const voltGrpcClient = this._voltClient.getVoltAPIClient();
|
|
707
|
+
|
|
700
708
|
if (relaying) {
|
|
701
709
|
//
|
|
702
710
|
// If the volt connection is via a relay (or the target service is relayed),
|
|
703
711
|
// we wrap the rpc in a RemoteRequest message and send it via a call to Invoke().
|
|
704
712
|
//
|
|
713
|
+
|
|
705
714
|
// Replace the target method with a call to Invoke on the relay Volt.
|
|
706
|
-
|
|
707
|
-
callMethod = "Invoke";
|
|
715
|
+
callMethod = invokeMethod;
|
|
708
716
|
|
|
709
717
|
// But first we need to perform a key exchange with the target service.
|
|
710
|
-
//
|
|
711
718
|
if (!this._encryptionKey) {
|
|
712
719
|
// We haven't exchanged keys yet, so queue the payload.
|
|
713
|
-
|
|
720
|
+
if (request) {
|
|
721
|
+
this._pendingRequests.push(request);
|
|
722
|
+
}
|
|
714
723
|
|
|
715
724
|
if (this._keyExchangePending) {
|
|
716
725
|
// We've already sent the key exchange request, so do nothing.
|
|
@@ -729,6 +738,13 @@ function _prepareInvokeRequest(
|
|
|
729
738
|
: undefined
|
|
730
739
|
};
|
|
731
740
|
|
|
741
|
+
if (this._voltClient.isRelayed) {
|
|
742
|
+
// When tunnelling, we serialise the request using the original serialisation
|
|
743
|
+
// method for Invoke.
|
|
744
|
+
invokeRequest =
|
|
745
|
+
voltGrpcClient.Invoke.requestSerializeOriginal(invokeRequest);
|
|
746
|
+
}
|
|
747
|
+
|
|
732
748
|
return {
|
|
733
749
|
request: invokeRequest,
|
|
734
750
|
method: callMethod,
|
|
@@ -739,41 +755,45 @@ function _prepareInvokeRequest(
|
|
|
739
755
|
|
|
740
756
|
let requestPayload;
|
|
741
757
|
|
|
742
|
-
// Serialise the rpc request.
|
|
758
|
+
// Serialise the rpc request using the original serialisation method.
|
|
743
759
|
if (request) {
|
|
744
|
-
|
|
760
|
+
if (this._voltClient.isRelayed) {
|
|
761
|
+
requestPayload =
|
|
762
|
+
this._grpcClient[method].requestSerializeOriginal(request);
|
|
763
|
+
} else {
|
|
764
|
+
requestPayload = this._grpcClient[method].requestSerialize(request);
|
|
765
|
+
}
|
|
745
766
|
} else if (request !== null) {
|
|
746
767
|
log$4("****************LOOKOUT****************** - empty request");
|
|
747
768
|
} else {
|
|
748
769
|
log$4("explicit empty request payload");
|
|
749
770
|
}
|
|
750
771
|
|
|
751
|
-
//
|
|
752
|
-
const
|
|
753
|
-
const tunnelMethod = voltGrpcClient.Tunnel;
|
|
754
|
-
|
|
755
|
-
const tunnelResp = {
|
|
772
|
+
// Create a tunnel message containing the method call metadata and request payload.
|
|
773
|
+
const tunnelResp = voltGrpcClient.Tunnel.responseSerialize({
|
|
756
774
|
method_invoke: {
|
|
757
775
|
method_name: this._grpcClient[method].path,
|
|
758
776
|
method_type: methodType,
|
|
759
777
|
request: requestPayload
|
|
760
778
|
}
|
|
761
|
-
};
|
|
762
|
-
|
|
763
|
-
let invokePayload;
|
|
764
|
-
const iv = aesCreateKey().iv;
|
|
779
|
+
});
|
|
765
780
|
|
|
766
781
|
// When sending via a relay we need to encrypt the payload.
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
iv,
|
|
770
|
-
tunnelMethod.responseSerialize(tunnelResp)
|
|
771
|
-
);
|
|
782
|
+
const iv = aesCreateKey().iv;
|
|
783
|
+
const invokePayload = aesEncrypt$1(this._encryptionKey, iv, tunnelResp);
|
|
772
784
|
|
|
785
|
+
// Construct the Invoke request.
|
|
773
786
|
invokeRequest = {
|
|
774
787
|
iv,
|
|
775
788
|
payload: invokePayload
|
|
776
789
|
};
|
|
790
|
+
|
|
791
|
+
if (this._voltClient.isRelayed) {
|
|
792
|
+
// When tunnelling, we serialise the request using the original serialisation
|
|
793
|
+
// method for Invoke.
|
|
794
|
+
invokeRequest =
|
|
795
|
+
voltGrpcClient.Invoke.requestSerializeOriginal(invokeRequest);
|
|
796
|
+
}
|
|
777
797
|
} else {
|
|
778
798
|
invokeRequest = request;
|
|
779
799
|
}
|
|
@@ -793,32 +813,45 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
793
813
|
this._service?.service_description.host_type ===
|
|
794
814
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
795
815
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
816
|
+
if (this._voltClient.isRelayed || isServiceRelayed) {
|
|
817
|
+
let requestPayload;
|
|
818
|
+
if (this._voltClient.isRelayed) {
|
|
819
|
+
// Serialise the rpc request using the original serialisation method.
|
|
820
|
+
requestPayload =
|
|
821
|
+
this._grpcClient[invokeInfo.methodName].requestSerializeOriginal(
|
|
822
|
+
request
|
|
823
|
+
);
|
|
824
|
+
} else {
|
|
825
|
+
requestPayload =
|
|
826
|
+
this._grpcClient[invokeInfo.methodName].requestSerialize(request);
|
|
827
|
+
}
|
|
802
828
|
|
|
829
|
+
// Use the Tunnel method to serialise the relay request wrapper, this
|
|
803
830
|
const voltGrpcClient = this._voltClient.getVoltAPIClient();
|
|
804
|
-
const tunnelMethod = voltGrpcClient.Tunnel;
|
|
805
831
|
|
|
806
|
-
|
|
832
|
+
// Create a tunnel message containing the method call metadata and request payload.
|
|
833
|
+
const tunnelResp = voltGrpcClient.Tunnel.responseSerialize({
|
|
807
834
|
method_payload: {
|
|
808
835
|
payload: requestPayload
|
|
809
836
|
}
|
|
810
|
-
};
|
|
837
|
+
});
|
|
811
838
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
tunnelMethod.responseSerialize(tunnelResp)
|
|
816
|
-
);
|
|
839
|
+
// Encrypt the payload using the shared key.
|
|
840
|
+
const iv = aesCreateKey().iv;
|
|
841
|
+
const invokePayload = aesEncrypt$1(this._encryptionKey, iv, tunnelResp);
|
|
817
842
|
|
|
843
|
+
// We then serialise the entire request using the original serialisation method for Invoke.
|
|
818
844
|
payloadRequest = {
|
|
819
845
|
iv,
|
|
820
846
|
payload: invokePayload
|
|
821
847
|
};
|
|
848
|
+
|
|
849
|
+
if (this._voltClient.isRelayed) {
|
|
850
|
+
// When tunnelling, we serialise the request using the original serialisation
|
|
851
|
+
// method for Invoke.
|
|
852
|
+
payloadRequest =
|
|
853
|
+
voltGrpcClient.Invoke.requestSerializeOriginal(payloadRequest);
|
|
854
|
+
}
|
|
822
855
|
} else {
|
|
823
856
|
payloadRequest = request;
|
|
824
857
|
}
|
|
@@ -834,7 +867,14 @@ function _parseResponse(method, meta, response) {
|
|
|
834
867
|
this._service?.service_description.host_type ===
|
|
835
868
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
836
869
|
|
|
837
|
-
|
|
870
|
+
const voltGrpcClient = this._voltClient.getVoltAPIClient();
|
|
871
|
+
|
|
872
|
+
if (this._voltClient.isRelayed || isServiceRelayed) {
|
|
873
|
+
if (this._voltClient.isRelayed) {
|
|
874
|
+
// Deserialise the incoming response using the original deserialisation method for Invoke.
|
|
875
|
+
response = voltGrpcClient.Invoke.responseDeserializeOriginal(response);
|
|
876
|
+
}
|
|
877
|
+
|
|
838
878
|
if (response.key_exchange) {
|
|
839
879
|
const encryptionKey = response.key_exchange.encryption_key;
|
|
840
880
|
const nonce = response.key_exchange.nonce;
|
|
@@ -872,13 +912,24 @@ function _parseResponse(method, meta, response) {
|
|
|
872
912
|
decryptedPayload = response.payload;
|
|
873
913
|
}
|
|
874
914
|
|
|
875
|
-
|
|
876
|
-
const responsePayload =
|
|
915
|
+
// Deserialise the decrypted payload using the Tunnel deserialisation method.
|
|
916
|
+
const responsePayload =
|
|
917
|
+
voltGrpcClient.Tunnel.requestDeserialize(decryptedPayload);
|
|
877
918
|
if (responsePayload.payload === "method_payload") {
|
|
878
919
|
try {
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
920
|
+
// Deserialise the method payload.
|
|
921
|
+
if (this._voltClient.isRelayed) {
|
|
922
|
+
// Use the original deserialisation method for the target method.
|
|
923
|
+
invokeResponse.payload = this._grpcClient[
|
|
924
|
+
method
|
|
925
|
+
].responseDeserializeOriginal(
|
|
926
|
+
responsePayload.method_payload.payload
|
|
927
|
+
);
|
|
928
|
+
} else {
|
|
929
|
+
invokeResponse.payload = this._grpcClient[
|
|
930
|
+
method
|
|
931
|
+
].responseDeserialize(responsePayload.method_payload.payload);
|
|
932
|
+
}
|
|
882
933
|
} catch (err) {
|
|
883
934
|
log$4("failure deserialising payload for %s", this._methodName);
|
|
884
935
|
throw new Error(
|
|
@@ -906,16 +957,6 @@ function _parseResponse(method, meta, response) {
|
|
|
906
957
|
invokeResponse.methodId = response.invoke_id;
|
|
907
958
|
log$4("started method %d for %s", invokeResponse.methodId, method);
|
|
908
959
|
}
|
|
909
|
-
} else if (this._voltClient.isRemote && this._encryptionKey) {
|
|
910
|
-
const decryptedPayload = aesDecrypt$1(
|
|
911
|
-
this._encryptionKey,
|
|
912
|
-
meta.sharedKey.iv,
|
|
913
|
-
response
|
|
914
|
-
);
|
|
915
|
-
invokeResponse = {
|
|
916
|
-
payload:
|
|
917
|
-
this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)
|
|
918
|
-
};
|
|
919
960
|
} else {
|
|
920
961
|
invokeResponse = { payload: response };
|
|
921
962
|
}
|
|
@@ -955,7 +996,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
955
996
|
"SERVICE_HOST_TYPE_RELAYED";
|
|
956
997
|
|
|
957
998
|
let callClient;
|
|
958
|
-
if (this._voltClient.
|
|
999
|
+
if (this._voltClient.isRelayed || isServiceRelayed) {
|
|
959
1000
|
callClient = this._voltClient.getVoltAPIClient();
|
|
960
1001
|
} else {
|
|
961
1002
|
callClient = this._grpcClient;
|
|
@@ -1022,7 +1063,11 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1022
1063
|
});
|
|
1023
1064
|
|
|
1024
1065
|
this._call.on("error", (err) => {
|
|
1025
|
-
log$4(
|
|
1066
|
+
log$4(
|
|
1067
|
+
"ERROR - intercepted stream error on %s: %s",
|
|
1068
|
+
this._methodName,
|
|
1069
|
+
err.message
|
|
1070
|
+
);
|
|
1026
1071
|
this.emit("error", err);
|
|
1027
1072
|
});
|
|
1028
1073
|
|
|
@@ -1058,6 +1103,16 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1058
1103
|
}
|
|
1059
1104
|
}
|
|
1060
1105
|
|
|
1106
|
+
pause() {
|
|
1107
|
+
log$4("pausing call %s", this._methodName);
|
|
1108
|
+
return this._call?.pause();
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
resume() {
|
|
1112
|
+
log$4("resuming call %s", this._methodName);
|
|
1113
|
+
return this._call?.resume();
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1061
1116
|
get writable() {
|
|
1062
1117
|
return this._call?.writable;
|
|
1063
1118
|
}
|
|
@@ -1182,8 +1237,18 @@ function _doConnect(connectHello) {
|
|
|
1182
1237
|
break;
|
|
1183
1238
|
}
|
|
1184
1239
|
case "invoke_request": {
|
|
1185
|
-
log$3("received invoke request");
|
|
1186
|
-
|
|
1240
|
+
log$3("received invoke request %s", response.invoke_request.invoke_id);
|
|
1241
|
+
// Pause the call while we process the invoke request to avoid getting
|
|
1242
|
+
// CANCELLED errors from grpc - we should perhaps leave this to the
|
|
1243
|
+
// client to manage as it may be overkill in some cases.
|
|
1244
|
+
this.call.pause();
|
|
1245
|
+
try {
|
|
1246
|
+
this.emit("invoke_request", response.invoke_request);
|
|
1247
|
+
} catch (err) {
|
|
1248
|
+
log$3("error processing invoke request [%s]", err.message);
|
|
1249
|
+
} finally {
|
|
1250
|
+
this.call.resume();
|
|
1251
|
+
}
|
|
1187
1252
|
break;
|
|
1188
1253
|
}
|
|
1189
1254
|
case "evt": {
|
|
@@ -1255,6 +1320,10 @@ class VoltConnection extends EventEmitter__default["default"] {
|
|
|
1255
1320
|
this._helloPayload = undefined;
|
|
1256
1321
|
}
|
|
1257
1322
|
|
|
1323
|
+
get connectionId() {
|
|
1324
|
+
return this._connectionId;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1258
1327
|
connect(helloPayload, throwOnFail) {
|
|
1259
1328
|
this._helloPayload = helloPayload;
|
|
1260
1329
|
if (throwOnFail) {
|
|
@@ -1286,23 +1355,26 @@ const {
|
|
|
1286
1355
|
publicKeyToPem: publicKeyToPem$1,
|
|
1287
1356
|
randomBytes,
|
|
1288
1357
|
signBase64: signBase64$1,
|
|
1289
|
-
|
|
1358
|
+
stripPemHeaders,
|
|
1290
1359
|
x25519
|
|
1291
1360
|
} = js.voltUtils;
|
|
1292
1361
|
|
|
1293
|
-
const log$2 = debug__default["default"]("rpc-invocation");
|
|
1362
|
+
const log$2 = debug__default["default"]("volt-client-grpc:rpc-invocation");
|
|
1294
1363
|
|
|
1295
1364
|
class RpcInvocation extends EventEmitter__default["default"] {
|
|
1296
1365
|
#voltClient = null;
|
|
1297
1366
|
#voltConnection = null;
|
|
1298
1367
|
#token = null;
|
|
1368
|
+
#callerPublicKeyPem = null;
|
|
1299
1369
|
#invokeId = null;
|
|
1370
|
+
#serviceId = null;
|
|
1300
1371
|
#payload = null;
|
|
1301
1372
|
#isJSON = false;
|
|
1302
1373
|
#methodDescriptor = null;
|
|
1303
1374
|
#methodName = null;
|
|
1304
1375
|
#derivedKey = null;
|
|
1305
1376
|
#started = false;
|
|
1377
|
+
#endEmitted = false;
|
|
1306
1378
|
|
|
1307
1379
|
constructor(voltClient, voltConnection) {
|
|
1308
1380
|
super();
|
|
@@ -1314,6 +1386,18 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1314
1386
|
return this.#invokeId;
|
|
1315
1387
|
}
|
|
1316
1388
|
|
|
1389
|
+
get serviceId() {
|
|
1390
|
+
return this.#serviceId;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
get token() {
|
|
1394
|
+
return this.#token;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
get callerPublicKeyPem() {
|
|
1398
|
+
return this.#callerPublicKeyPem;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1317
1401
|
get payload() {
|
|
1318
1402
|
if (!this.#payload) {
|
|
1319
1403
|
return null;
|
|
@@ -1355,14 +1439,23 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1355
1439
|
// The rpc implementation should do the verification using the Volt API (e.g. CanAccessResource).
|
|
1356
1440
|
this.#token = jwt__default["default"].decode(token);
|
|
1357
1441
|
|
|
1442
|
+
if (!this.#token) {
|
|
1443
|
+
log$2("failed to decode token %s", token || "null");
|
|
1444
|
+
return Promise.reject(new Error("failed to decode token"));
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1358
1447
|
// Store the JWT key id (kid) for use in the response.
|
|
1359
|
-
|
|
1360
|
-
const callerKey = keyFromPem(
|
|
1448
|
+
this.#callerPublicKeyPem = formatPEM(this.#token.k, "PUBLIC KEY");
|
|
1449
|
+
const callerKey = keyFromPem(
|
|
1450
|
+
this.#callerPublicKeyPem,
|
|
1451
|
+
"",
|
|
1452
|
+
x25519
|
|
1453
|
+
).publicKey;
|
|
1361
1454
|
|
|
1362
1455
|
const ephemeralKeyPem = createEncryptionKey();
|
|
1363
1456
|
const ephemeralKey = keyFromPem(ephemeralKeyPem, "", x25519);
|
|
1364
1457
|
const ephemeralPublicKey = fromBase64(
|
|
1365
|
-
|
|
1458
|
+
stripPemHeaders(publicKeyToPem$1(ephemeralKey.publicKey, x25519))
|
|
1366
1459
|
);
|
|
1367
1460
|
|
|
1368
1461
|
this.#derivedKey = deriveSharedKey(
|
|
@@ -1379,7 +1472,7 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1379
1472
|
message.set(nonce, ephemeralPublicKey.length);
|
|
1380
1473
|
|
|
1381
1474
|
const signature = signBase64$1(
|
|
1382
|
-
this.#voltClient.credential.
|
|
1475
|
+
this.#voltClient.credential.cache.key,
|
|
1383
1476
|
message
|
|
1384
1477
|
);
|
|
1385
1478
|
|
|
@@ -1400,7 +1493,10 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1400
1493
|
}
|
|
1401
1494
|
|
|
1402
1495
|
initialise(invoke_request) {
|
|
1403
|
-
this.#invokeId
|
|
1496
|
+
if (!this.#invokeId) {
|
|
1497
|
+
this.#invokeId = invoke_request.invoke_id;
|
|
1498
|
+
this.#serviceId = invoke_request.target_service_id;
|
|
1499
|
+
}
|
|
1404
1500
|
|
|
1405
1501
|
if (!this.#derivedKey) {
|
|
1406
1502
|
// We don't have the derived key yet, so we need to perform the key exchange.
|
|
@@ -1422,8 +1518,7 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1422
1518
|
|
|
1423
1519
|
if (invoke_request.payload) {
|
|
1424
1520
|
let decryptedPayload;
|
|
1425
|
-
|
|
1426
|
-
if (encryptIV) {
|
|
1521
|
+
if (encryptIV?.length) {
|
|
1427
1522
|
// Decrypt the actual payload using the shared key and iv.
|
|
1428
1523
|
decryptedPayload = aesDecrypt(
|
|
1429
1524
|
this.#derivedKey,
|
|
@@ -1491,8 +1586,13 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1491
1586
|
);
|
|
1492
1587
|
this.emit("payload", this);
|
|
1493
1588
|
} else if (wrappedPayload.method_end) {
|
|
1494
|
-
// We don't need to do anything here, since we notify the client when we receive the client_end.
|
|
1495
1589
|
log$2("method_end received");
|
|
1590
|
+
if (!this.#endEmitted) {
|
|
1591
|
+
this.#endEmitted = true;
|
|
1592
|
+
this.emit("end", this);
|
|
1593
|
+
} else {
|
|
1594
|
+
log$2("method_end: end already emitted");
|
|
1595
|
+
}
|
|
1496
1596
|
}
|
|
1497
1597
|
} catch (err) {
|
|
1498
1598
|
log$2("JSON.parse failure parsing json_payload: %s", err.message);
|
|
@@ -1501,7 +1601,12 @@ class RpcInvocation extends EventEmitter__default["default"] {
|
|
|
1501
1601
|
|
|
1502
1602
|
resolve();
|
|
1503
1603
|
} else if (invoke_request.client_end) {
|
|
1504
|
-
|
|
1604
|
+
if (!this.#endEmitted) {
|
|
1605
|
+
this.#endEmitted = true;
|
|
1606
|
+
this.emit("end", this);
|
|
1607
|
+
} else {
|
|
1608
|
+
log$2("client_end: end already emitted");
|
|
1609
|
+
}
|
|
1505
1610
|
} else {
|
|
1506
1611
|
// Do we need to support json_payload here?
|
|
1507
1612
|
reject(new Error("No payload in invoke request"));
|
|
@@ -1751,7 +1856,7 @@ async function authenticateInternal(
|
|
|
1751
1856
|
|
|
1752
1857
|
if (this._voltConfig.challenge_code) {
|
|
1753
1858
|
authenticateRequest.challenge = signBase64(
|
|
1754
|
-
|
|
1859
|
+
this._credential.cache.key,
|
|
1755
1860
|
this._voltConfig.challenge_code
|
|
1756
1861
|
);
|
|
1757
1862
|
} else {
|
|
@@ -1781,16 +1886,18 @@ async function authenticateInternal(
|
|
|
1781
1886
|
// This is because the DID document is signed and the signature
|
|
1782
1887
|
// is over the entire document.
|
|
1783
1888
|
const didDocument = {
|
|
1784
|
-
"@context":
|
|
1889
|
+
"@context": [
|
|
1890
|
+
"https://www.w3.org/ns/did/v1",
|
|
1891
|
+
"https://tdxvolt.com/ns/did/v1"
|
|
1892
|
+
],
|
|
1785
1893
|
authentication: [`${did}#key-1`],
|
|
1786
1894
|
controller: did,
|
|
1787
|
-
description: this._config.client_name,
|
|
1788
1895
|
id: did,
|
|
1789
1896
|
verificationMethod: [
|
|
1790
1897
|
{
|
|
1791
1898
|
id: `${did}#key-1`,
|
|
1792
1899
|
publicKeyPem: publicKeyPem,
|
|
1793
|
-
type: "
|
|
1900
|
+
type: "Ed25519Signature2018"
|
|
1794
1901
|
}
|
|
1795
1902
|
]
|
|
1796
1903
|
};
|
|
@@ -1801,7 +1908,7 @@ async function authenticateInternal(
|
|
|
1801
1908
|
// Now sign the entire DID document, including the proof and set it in the
|
|
1802
1909
|
// bind request. This is required by the Volt to verify that we have the private key for the DID.
|
|
1803
1910
|
authenticateRequest.did_document_signature = signBase64(
|
|
1804
|
-
|
|
1911
|
+
this._credential.cache.key,
|
|
1805
1912
|
authenticateRequest.did_document
|
|
1806
1913
|
);
|
|
1807
1914
|
|
|
@@ -1839,7 +1946,7 @@ async function authenticateInternal(
|
|
|
1839
1946
|
|
|
1840
1947
|
return decision === "POLICY_DECISION_PERMIT";
|
|
1841
1948
|
} catch (err) {
|
|
1842
|
-
log$1("failure
|
|
1949
|
+
log$1("failure authenticating with Volt [%s]", err.message);
|
|
1843
1950
|
throw err;
|
|
1844
1951
|
}
|
|
1845
1952
|
}
|
|
@@ -1855,15 +1962,7 @@ function connectInternal(helloPayload) {
|
|
|
1855
1962
|
this._connected = false;
|
|
1856
1963
|
this._session = null;
|
|
1857
1964
|
|
|
1858
|
-
|
|
1859
|
-
this._grpc.closeClient(this._cachedClient);
|
|
1860
|
-
this._cachedClient = null;
|
|
1861
|
-
}
|
|
1862
|
-
|
|
1863
|
-
if (this._cachedRemoteClient) {
|
|
1864
|
-
this._grpc.closeClient(this._cachedRemoteClient);
|
|
1865
|
-
this._cachedRemoteClient = null;
|
|
1866
|
-
}
|
|
1965
|
+
this.close();
|
|
1867
1966
|
};
|
|
1868
1967
|
|
|
1869
1968
|
this._voltConnection = new VoltConnection(this, this._grpc);
|
|
@@ -1952,7 +2051,7 @@ function connectInternal(helloPayload) {
|
|
|
1952
2051
|
|
|
1953
2052
|
function getVoltAPIClientInternal() {
|
|
1954
2053
|
if (!this._cachedClient) {
|
|
1955
|
-
const serviceAddress = this.
|
|
2054
|
+
const serviceAddress = this.isRelayed
|
|
1956
2055
|
? this._voltConfig.relay.address
|
|
1957
2056
|
: this._voltConfig.address;
|
|
1958
2057
|
log$1("creating cached VoltAPI service client on %s", serviceAddress);
|
|
@@ -1967,7 +2066,7 @@ function getVoltAPIClientInternal() {
|
|
|
1967
2066
|
serviceDescriptors,
|
|
1968
2067
|
serviceAddress,
|
|
1969
2068
|
this._credential,
|
|
1970
|
-
this.
|
|
2069
|
+
this.isRelayed,
|
|
1971
2070
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
1972
2071
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
1973
2072
|
);
|
|
@@ -1978,7 +2077,7 @@ function getVoltAPIClientInternal() {
|
|
|
1978
2077
|
|
|
1979
2078
|
function getAPIClientInternal(service) {
|
|
1980
2079
|
if (!this._cachedService[service.id]) {
|
|
1981
|
-
const serviceAddress = this.
|
|
2080
|
+
const serviceAddress = this.isRelayed
|
|
1982
2081
|
? this._voltConfig.relay.address
|
|
1983
2082
|
: service.service_description.host_address;
|
|
1984
2083
|
|
|
@@ -2001,7 +2100,7 @@ function getAPIClientInternal(service) {
|
|
|
2001
2100
|
serviceDescriptors,
|
|
2002
2101
|
serviceAddress,
|
|
2003
2102
|
this._credential,
|
|
2004
|
-
this.
|
|
2103
|
+
this.isRelayed,
|
|
2005
2104
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
2006
2105
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
2007
2106
|
);
|
|
@@ -2096,9 +2195,9 @@ async function fetchVoltConfig(discovery_url) {
|
|
|
2096
2195
|
}
|
|
2097
2196
|
}
|
|
2098
2197
|
|
|
2099
|
-
async function
|
|
2198
|
+
async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
|
|
2100
2199
|
try {
|
|
2101
|
-
const didResolution = getDIDResolutionURL(volt_did);
|
|
2200
|
+
const didResolution = getDIDResolutionURL(volt_did, registryUrl);
|
|
2102
2201
|
if (!didResolution) {
|
|
2103
2202
|
throw new Error(`Invalid Volt DID: ${volt_did}`);
|
|
2104
2203
|
}
|
|
@@ -2117,6 +2216,8 @@ async function fetchVoltConfigFromDID(volt_did) {
|
|
|
2117
2216
|
);
|
|
2118
2217
|
}
|
|
2119
2218
|
|
|
2219
|
+
log$1("resolved DID from %s", didResolution);
|
|
2220
|
+
|
|
2120
2221
|
const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
|
|
2121
2222
|
return await fetchVoltConfig.call(this, serviceEndpoint);
|
|
2122
2223
|
} catch (err) {
|
|
@@ -2126,6 +2227,25 @@ async function fetchVoltConfigFromDID(volt_did) {
|
|
|
2126
2227
|
}
|
|
2127
2228
|
}
|
|
2128
2229
|
|
|
2230
|
+
async function fetchVoltConfigFromDID(
|
|
2231
|
+
volt_did,
|
|
2232
|
+
registryList = [constants.defaultDIDHostName]
|
|
2233
|
+
) {
|
|
2234
|
+
for (let registryUrl of registryList) {
|
|
2235
|
+
try {
|
|
2236
|
+
return await fetchVoltConfigFromDIDInternal.call(
|
|
2237
|
+
this,
|
|
2238
|
+
volt_did,
|
|
2239
|
+
registryUrl
|
|
2240
|
+
);
|
|
2241
|
+
} catch (err) {
|
|
2242
|
+
log$1("Failed to fetch Volt config from %s: %s", registryUrl, err.message);
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2246
|
+
throw new Error("Failed to fetch Volt config from any registry");
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2129
2249
|
const { readFile } = fs__default["default"].promises;
|
|
2130
2250
|
const log = debug__default["default"]("volt-client-grpc:volt-client");
|
|
2131
2251
|
|
|
@@ -2141,7 +2261,6 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2141
2261
|
}
|
|
2142
2262
|
this._grpc = grpc;
|
|
2143
2263
|
this._cachedClient = null;
|
|
2144
|
-
this._cachedRemoteClient = null;
|
|
2145
2264
|
this._session = null;
|
|
2146
2265
|
this._config = null;
|
|
2147
2266
|
this._voltConfig = null;
|
|
@@ -2166,12 +2285,36 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2166
2285
|
return this._voltConfig;
|
|
2167
2286
|
}
|
|
2168
2287
|
|
|
2288
|
+
get connection() {
|
|
2289
|
+
return this._voltConnection;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
close() {
|
|
2293
|
+
if (this._cachedClient) {
|
|
2294
|
+
this._grpc.closeClient(this._cachedClient);
|
|
2295
|
+
this._cachedClient = null;
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
for (let serviceKey in Object.keys(this._cachedService)) {
|
|
2299
|
+
if (this._cachedService[serviceKey]) {
|
|
2300
|
+
this._grpc.closeClient(this._cachedService[serviceKey]);
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
this._cachedService = {};
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2169
2307
|
/**
|
|
2170
2308
|
* Initialises a binding and connection to the volt.
|
|
2171
2309
|
* @param {string} [config] - the location of the Volt configuration file, or a configuration object
|
|
2172
2310
|
* @param {object} [extras] - additional configuration properties
|
|
2173
2311
|
*/
|
|
2174
|
-
async initialise(
|
|
2312
|
+
async initialise(
|
|
2313
|
+
config,
|
|
2314
|
+
extras = {},
|
|
2315
|
+
ownDID = false,
|
|
2316
|
+
didRegistryList = undefined
|
|
2317
|
+
) {
|
|
2175
2318
|
try {
|
|
2176
2319
|
if (!config) {
|
|
2177
2320
|
throw new Error(
|
|
@@ -2236,7 +2379,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2236
2379
|
}
|
|
2237
2380
|
|
|
2238
2381
|
if (voltDID) {
|
|
2239
|
-
const didConfig = await fetchVoltConfigFromDID.call(
|
|
2382
|
+
const didConfig = await fetchVoltConfigFromDID.call(
|
|
2383
|
+
this,
|
|
2384
|
+
voltDID,
|
|
2385
|
+
didRegistryList
|
|
2386
|
+
);
|
|
2387
|
+
|
|
2388
|
+
// Remove the relay list from the DID config, as it would overwrite the relay in the supplied config.
|
|
2389
|
+
delete didConfig.relay;
|
|
2390
|
+
|
|
2240
2391
|
this._config = { ...this._config, volt: didConfig };
|
|
2241
2392
|
log("resolved config from DID: %s", JSON.stringify(didConfig, null, 2));
|
|
2242
2393
|
} else if (voltHttpAddress && !this._config?.volt?.id) {
|
|
@@ -2266,7 +2417,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2266
2417
|
|
|
2267
2418
|
await this._credential.initialise();
|
|
2268
2419
|
|
|
2269
|
-
if (!this.
|
|
2420
|
+
if (!this.isRelayed) {
|
|
2270
2421
|
// If we're not connecting via a Relay, we must have the address and CA.
|
|
2271
2422
|
if (
|
|
2272
2423
|
!this._voltConfig.address ||
|
|
@@ -2289,15 +2440,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2289
2440
|
} else {
|
|
2290
2441
|
// No certificate present in the cache yet => we need to authenticate.
|
|
2291
2442
|
try {
|
|
2292
|
-
isBound = await authenticateInternal.call(this);
|
|
2443
|
+
isBound = await authenticateInternal.call(this, false, ownDID);
|
|
2293
2444
|
} catch (bindErr) {
|
|
2294
2445
|
if (bindErr.message === "invalid arguments") {
|
|
2295
2446
|
// This is usually the result of a missing challenge signature or credential.
|
|
2296
2447
|
log(
|
|
2297
2448
|
"failure authenticating on Volt - did you supply a challenge code or verified credential?"
|
|
2298
2449
|
);
|
|
2299
|
-
throw bindErr;
|
|
2300
2450
|
}
|
|
2451
|
+
throw bindErr;
|
|
2301
2452
|
}
|
|
2302
2453
|
}
|
|
2303
2454
|
|
|
@@ -2306,7 +2457,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2306
2457
|
throw new Error("Volt authentication failed");
|
|
2307
2458
|
} else {
|
|
2308
2459
|
// Perist the configuration.
|
|
2309
|
-
this.
|
|
2460
|
+
this._credential.saveCache();
|
|
2310
2461
|
}
|
|
2311
2462
|
|
|
2312
2463
|
return Promise.resolve(this._config);
|
|
@@ -2357,7 +2508,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2357
2508
|
});
|
|
2358
2509
|
}
|
|
2359
2510
|
|
|
2360
|
-
if (this.
|
|
2511
|
+
if (this.isRelayed) {
|
|
2361
2512
|
if (!this._voltConfig?.relay?.address) {
|
|
2362
2513
|
throw new Error(
|
|
2363
2514
|
"Remote connection enabled but no Relay address - have you called start()?"
|
|
@@ -2384,14 +2535,10 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2384
2535
|
}
|
|
2385
2536
|
}
|
|
2386
2537
|
|
|
2387
|
-
get
|
|
2538
|
+
get isRelayed() {
|
|
2388
2539
|
return !!(this._voltConfig?.relay && this._voltConfig.id);
|
|
2389
2540
|
}
|
|
2390
2541
|
|
|
2391
|
-
get isVoltRelay() {
|
|
2392
|
-
return this.isRemote && !this._voltConfig?.relay.cloud;
|
|
2393
|
-
}
|
|
2394
|
-
|
|
2395
2542
|
connect(helloPayload) {
|
|
2396
2543
|
return connectInternal.call(this, helloPayload);
|
|
2397
2544
|
}
|
|
@@ -2721,6 +2868,9 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2721
2868
|
*/
|
|
2722
2869
|
SqlExecuteJSON(request) {
|
|
2723
2870
|
return new Promise((resolve, reject) => {
|
|
2871
|
+
if (!request.start) {
|
|
2872
|
+
request = { start: request };
|
|
2873
|
+
}
|
|
2724
2874
|
const call = this.SqlExecute(request);
|
|
2725
2875
|
|
|
2726
2876
|
let header;
|