@tdxvolt/volt-client-grpc 0.15.1 → 0.16.0-beta.10
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 +195 -102
- package/package.json +3 -3
- package/protobuf/tdx/volt_api/data/v1/sqlite_database_api.proto +2 -2
- package/protobuf/tdx/volt_api/volt/v1/file_api.proto +2 -0
- package/protobuf/tdx/volt_api/volt/v1/ssi.proto +40 -0
- package/protobuf/tdx/volt_api/volt/v1/ssi_api.proto +126 -0
- package/protobuf/tdx/volt_api/volt/v1/volt.proto +117 -60
- package/protobuf/tdx/volt_api/volt/v1/volt_api.proto +127 -29
- package/src/constants.js +14 -14
- package/src/grpc-call.js +31 -33
- package/src/utils.js +22 -2
- package/src/volt-client-internal.js +101 -40
- package/src/volt-client.js +9 -5
- package/src/volt-connection.js +3 -4
- package/src/volt-credential.js +14 -5
- package/protobuf/tdx/volt_api/ssi/v1/ssi_api.proto +0 -37
package/lib/index.cjs
CHANGED
|
@@ -38,37 +38,37 @@ const constants = {
|
|
|
38
38
|
collectionOperation: {
|
|
39
39
|
add: "ADD",
|
|
40
40
|
remove: "REMOVE",
|
|
41
|
-
update: "UPDATE"
|
|
41
|
+
update: "UPDATE"
|
|
42
42
|
},
|
|
43
43
|
crypto: {
|
|
44
44
|
subjectDefaults: [
|
|
45
45
|
{
|
|
46
46
|
name: "countryName",
|
|
47
|
-
value: "GB"
|
|
47
|
+
value: "GB"
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
name: "localityName",
|
|
51
|
-
value: "Southampton"
|
|
51
|
+
value: "Southampton"
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
54
|
name: "organizationName",
|
|
55
|
-
value: "nquiringminds Ltd"
|
|
56
|
-
}
|
|
55
|
+
value: "nquiringminds Ltd"
|
|
56
|
+
}
|
|
57
57
|
],
|
|
58
|
-
userIdOID: "0.9.2342.19200300.100.1.1"
|
|
58
|
+
userIdOID: "0.9.2342.19200300.100.1.1"
|
|
59
59
|
},
|
|
60
60
|
defaultDIDHostName: "cloud.tdxvolt.com",
|
|
61
61
|
didServiceType: {
|
|
62
|
-
voltConfig: "tdx-volt-config"
|
|
62
|
+
voltConfig: "tdx-volt-config"
|
|
63
63
|
},
|
|
64
64
|
identityType: {
|
|
65
65
|
did: "did",
|
|
66
|
-
volt: "volt"
|
|
66
|
+
volt: "volt"
|
|
67
67
|
},
|
|
68
68
|
onlineStatus: {
|
|
69
69
|
offline: "offline",
|
|
70
70
|
online: "online",
|
|
71
|
-
unknown: "unknown"
|
|
71
|
+
unknown: "unknown"
|
|
72
72
|
},
|
|
73
73
|
publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
|
|
74
74
|
privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
|
|
@@ -79,14 +79,14 @@ const constants = {
|
|
|
79
79
|
proxy: "proxy",
|
|
80
80
|
proxyView: "proxy-view",
|
|
81
81
|
vc: "vc",
|
|
82
|
-
vcView: "vc-view"
|
|
82
|
+
vcView: "vc-view"
|
|
83
83
|
},
|
|
84
84
|
voltStatus: {
|
|
85
85
|
pending: "",
|
|
86
86
|
online: "online",
|
|
87
87
|
offline: "offline",
|
|
88
88
|
deleted: "deleted",
|
|
89
|
-
revoked: "revoked"
|
|
89
|
+
revoked: "revoked"
|
|
90
90
|
},
|
|
91
91
|
/**
|
|
92
92
|
* These top-level service types should map to a protobuf definition
|
|
@@ -96,16 +96,16 @@ const constants = {
|
|
|
96
96
|
fileAPI: "tdx.volt_api.volt.v1.FileAPI",
|
|
97
97
|
sqliteServerAPI: "tdx.volt_api.data.v1.SqliteServerAPI",
|
|
98
98
|
sqliteDatabaseAPI: "tdx.volt_api.data.v1.SqliteDatabaseAPI",
|
|
99
|
-
ssiAPI: "tdx.volt_api.
|
|
99
|
+
ssiAPI: "tdx.volt_api.volt.v1.SsiAPI",
|
|
100
100
|
relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
|
|
101
101
|
voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
|
|
102
|
-
wireAPI: "tdx.volt_api.volt.v1.WireAPI"
|
|
102
|
+
wireAPI: "tdx.volt_api.volt.v1.WireAPI"
|
|
103
103
|
},
|
|
104
104
|
tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
|
|
105
105
|
tunnelPingInterval: 10000,
|
|
106
106
|
tunnelPingTimeout: 2500,
|
|
107
107
|
tunnelReconnectAfter: 30000,
|
|
108
|
-
tunnelTimeoutInterval: 10000 * 2
|
|
108
|
+
tunnelTimeoutInterval: 10000 * 2 // This must always be greater than `tunnelPingInterval`.
|
|
109
109
|
};
|
|
110
110
|
|
|
111
111
|
const { pki: pki$2 } = forge__default["default"];
|
|
@@ -167,11 +167,16 @@ const createPublicKeyFingerprint = (key) => {
|
|
|
167
167
|
return createBase58Hash(removeCarriageReturn(key));
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
+
const base64ToBase64Url = (base64) => {
|
|
171
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
172
|
+
};
|
|
173
|
+
|
|
170
174
|
const signBase64 = (key, msg) => {
|
|
171
175
|
const md = forge__default["default"].md.sha256.create();
|
|
172
176
|
md.update(msg);
|
|
173
177
|
const signature = key.sign(md);
|
|
174
|
-
|
|
178
|
+
const base64Standard = forge__default["default"].util.encode64(signature);
|
|
179
|
+
return base64ToBase64Url(base64Standard);
|
|
175
180
|
};
|
|
176
181
|
|
|
177
182
|
const verifyBase64 = (key, msg, signature) => {
|
|
@@ -240,7 +245,8 @@ const createRSAKey = (passphrase = "") => {
|
|
|
240
245
|
const sha256Base64 = (msg) => {
|
|
241
246
|
const md = forge__default["default"].md.sha256.create();
|
|
242
247
|
md.update(msg);
|
|
243
|
-
|
|
248
|
+
const base64Standard = forge__default["default"].util.encode64(md.digest().bytes());
|
|
249
|
+
return base64ToBase64Url(base64Standard);
|
|
244
250
|
};
|
|
245
251
|
|
|
246
252
|
const decryptPrivateKey = (pem, passphrase) => {
|
|
@@ -248,6 +254,20 @@ const decryptPrivateKey = (pem, passphrase) => {
|
|
|
248
254
|
return forgePrivateKeyToPem(decryptedKey);
|
|
249
255
|
};
|
|
250
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;
|
|
269
|
+
};
|
|
270
|
+
|
|
251
271
|
var utils = /*#__PURE__*/Object.freeze({
|
|
252
272
|
__proto__: null,
|
|
253
273
|
createSecureContextOptions: createSecureContextOptions,
|
|
@@ -258,12 +278,14 @@ var utils = /*#__PURE__*/Object.freeze({
|
|
|
258
278
|
removeCarriageReturn: removeCarriageReturn,
|
|
259
279
|
createBase58Hash: createBase58Hash,
|
|
260
280
|
createPublicKeyFingerprint: createPublicKeyFingerprint,
|
|
281
|
+
base64ToBase64Url: base64ToBase64Url,
|
|
261
282
|
signBase64: signBase64,
|
|
262
283
|
verifyBase64: verifyBase64,
|
|
263
284
|
getDIDResolutionURL: getDIDResolutionURL,
|
|
264
285
|
createRSAKey: createRSAKey,
|
|
265
286
|
sha256Base64: sha256Base64,
|
|
266
|
-
decryptPrivateKey: decryptPrivateKey
|
|
287
|
+
decryptPrivateKey: decryptPrivateKey,
|
|
288
|
+
jwsSignature: jwsSignature
|
|
267
289
|
});
|
|
268
290
|
|
|
269
291
|
/* eslint-disable no-underscore-dangle */
|
|
@@ -567,8 +589,16 @@ class VoltCredential {
|
|
|
567
589
|
return this._cryptoCache;
|
|
568
590
|
}
|
|
569
591
|
|
|
570
|
-
get
|
|
571
|
-
return this._cryptoCache.
|
|
592
|
+
get certificate() {
|
|
593
|
+
return this._cryptoCache.cert;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
get sessionId() {
|
|
597
|
+
return this._cryptoCache.session_id;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
get identityId() {
|
|
601
|
+
return this._cryptoCache.identity_id;
|
|
572
602
|
}
|
|
573
603
|
|
|
574
604
|
get voltPublicKey() {
|
|
@@ -656,7 +686,7 @@ class VoltCredential {
|
|
|
656
686
|
aud: audience,
|
|
657
687
|
iat: Math.floor(Date.now() / 1000) - ttl,
|
|
658
688
|
exp: Math.floor(Date.now() / 1000) + ttl,
|
|
659
|
-
sub: this._cryptoCache.
|
|
689
|
+
sub: this._cryptoCache.session_id
|
|
660
690
|
};
|
|
661
691
|
|
|
662
692
|
let sharedKey;
|
|
@@ -738,12 +768,13 @@ class VoltCredential {
|
|
|
738
768
|
}
|
|
739
769
|
|
|
740
770
|
static rsaEncrypt(key, buffer) {
|
|
741
|
-
|
|
771
|
+
const cipher = crypto__default["default"].publicEncrypt(key, buffer);
|
|
772
|
+
return cipher.toString("base64url");
|
|
742
773
|
}
|
|
743
774
|
|
|
744
775
|
static rsaDecrypt(key, buffer) {
|
|
745
776
|
const keyObj = crypto__default["default"].createPrivateKey(key);
|
|
746
|
-
const cipher = Buffer.from(buffer, "
|
|
777
|
+
const cipher = Buffer.from(buffer, "base64url");
|
|
747
778
|
return Buffer.from(crypto__default["default"].privateDecrypt(keyObj, cipher));
|
|
748
779
|
}
|
|
749
780
|
}
|
|
@@ -881,7 +912,7 @@ const log$4 = debug__default["default"]("volt-client-grpc:grpc-call");
|
|
|
881
912
|
function _prepareInvokeRequest(
|
|
882
913
|
request,
|
|
883
914
|
method,
|
|
884
|
-
methodType = "METHOD_TYPE_UNARY"
|
|
915
|
+
methodType = "METHOD_TYPE_UNARY"
|
|
885
916
|
) {
|
|
886
917
|
const isServiceRelayed =
|
|
887
918
|
this._service?.service_description.host_type ===
|
|
@@ -894,7 +925,7 @@ function _prepareInvokeRequest(
|
|
|
894
925
|
this._voltClient.voltConfig.id,
|
|
895
926
|
this._service?.service_description.host_public_key ||
|
|
896
927
|
this._voltClient.credential.voltPublicKey,
|
|
897
|
-
true
|
|
928
|
+
true
|
|
898
929
|
);
|
|
899
930
|
|
|
900
931
|
let invokeRequest;
|
|
@@ -925,42 +956,40 @@ function _prepareInvokeRequest(
|
|
|
925
956
|
method_name: this._grpcClient[method].path,
|
|
926
957
|
method_type: methodType,
|
|
927
958
|
token: methodToken.token,
|
|
928
|
-
request: requestPayload
|
|
929
|
-
}
|
|
959
|
+
request: requestPayload
|
|
960
|
+
}
|
|
930
961
|
};
|
|
931
962
|
|
|
932
|
-
let
|
|
963
|
+
let targetDID = [];
|
|
933
964
|
let invokePayload;
|
|
934
965
|
if (this._voltClient.isVoltRelay || isServiceRelayed) {
|
|
935
966
|
// When sending via a relay we need to encrypt the payload.
|
|
936
967
|
invokePayload = VoltCredential.aesEncrypt(
|
|
937
968
|
tunnelMethod.responseSerialize(tunnelResp),
|
|
938
969
|
methodToken.sharedKey.key,
|
|
939
|
-
methodToken.sharedKey.iv
|
|
970
|
+
methodToken.sharedKey.iv
|
|
940
971
|
);
|
|
941
972
|
} else {
|
|
942
973
|
invokePayload = tunnelMethod.responseSerialize(tunnelResp);
|
|
943
974
|
}
|
|
944
975
|
|
|
945
976
|
if (this._voltClient.isVoltRelay) {
|
|
946
|
-
|
|
977
|
+
targetDID.push(this._voltClient.voltConfig.id);
|
|
947
978
|
}
|
|
948
979
|
|
|
949
980
|
if (isServiceRelayed) {
|
|
950
981
|
// Add another relay hop if the target service is relayed.
|
|
951
982
|
log$4("target service is relayed");
|
|
952
|
-
|
|
953
|
-
this._service.service_description.host_fingerprint,
|
|
954
|
-
);
|
|
983
|
+
targetDID.push(this._service.service_description.host_client_id);
|
|
955
984
|
}
|
|
956
985
|
|
|
957
|
-
log$4("target is %j",
|
|
986
|
+
log$4("target is %j", targetDID);
|
|
958
987
|
|
|
959
988
|
invokeRequest = {
|
|
960
|
-
|
|
989
|
+
target_did: targetDID,
|
|
961
990
|
token: methodToken.token,
|
|
962
991
|
payload: invokePayload,
|
|
963
|
-
target_service_id: isServiceRelayed ? this._service.id : undefined
|
|
992
|
+
target_service_id: isServiceRelayed ? this._service.id : undefined
|
|
964
993
|
};
|
|
965
994
|
|
|
966
995
|
// Replace the target method with a call to Invoke on the relay Volt.
|
|
@@ -973,7 +1002,7 @@ function _prepareInvokeRequest(
|
|
|
973
1002
|
meta: methodToken,
|
|
974
1003
|
method: callMethod,
|
|
975
1004
|
methodName: method,
|
|
976
|
-
request: invokeRequest
|
|
1005
|
+
request: invokeRequest
|
|
977
1006
|
};
|
|
978
1007
|
}
|
|
979
1008
|
|
|
@@ -993,18 +1022,18 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
993
1022
|
|
|
994
1023
|
const tunnelResp = {
|
|
995
1024
|
method_payload: {
|
|
996
|
-
payload: requestPayload
|
|
997
|
-
}
|
|
1025
|
+
payload: requestPayload
|
|
1026
|
+
}
|
|
998
1027
|
};
|
|
999
1028
|
|
|
1000
1029
|
const invokePayload = VoltCredential.aesEncrypt(
|
|
1001
1030
|
tunnelMethod.responseSerialize(tunnelResp),
|
|
1002
1031
|
invokeInfo.meta.sharedKey.key,
|
|
1003
|
-
invokeInfo.meta.sharedKey.iv
|
|
1032
|
+
invokeInfo.meta.sharedKey.iv
|
|
1004
1033
|
);
|
|
1005
1034
|
|
|
1006
1035
|
payloadRequest = {
|
|
1007
|
-
payload: invokePayload
|
|
1036
|
+
payload: invokePayload
|
|
1008
1037
|
};
|
|
1009
1038
|
} else {
|
|
1010
1039
|
payloadRequest = request;
|
|
@@ -1028,7 +1057,7 @@ function _parseResponse(method, meta, response) {
|
|
|
1028
1057
|
decryptedPayload = VoltCredential.aesDecrypt(
|
|
1029
1058
|
response.payload,
|
|
1030
1059
|
meta.sharedKey.key,
|
|
1031
|
-
meta.sharedKey.iv
|
|
1060
|
+
meta.sharedKey.iv
|
|
1032
1061
|
);
|
|
1033
1062
|
} else {
|
|
1034
1063
|
decryptedPayload = response.payload;
|
|
@@ -1039,12 +1068,12 @@ function _parseResponse(method, meta, response) {
|
|
|
1039
1068
|
if (responsePayload.payload === "method_payload") {
|
|
1040
1069
|
try {
|
|
1041
1070
|
invokeResponse.payload = this._grpcClient[method].responseDeserialize(
|
|
1042
|
-
responsePayload.method_payload.payload
|
|
1071
|
+
responsePayload.method_payload.payload
|
|
1043
1072
|
);
|
|
1044
1073
|
} catch (err) {
|
|
1045
1074
|
log$4("failure deserialising payload for %s", this._methodName);
|
|
1046
1075
|
throw new Error(
|
|
1047
|
-
"failure deserialising payload - check protobuf definition matches with data being sent"
|
|
1076
|
+
"failure deserialising payload - check protobuf definition matches with data being sent"
|
|
1048
1077
|
);
|
|
1049
1078
|
}
|
|
1050
1079
|
} else if (responsePayload.payload === "method_end") {
|
|
@@ -1058,11 +1087,11 @@ function _parseResponse(method, meta, response) {
|
|
|
1058
1087
|
"Tunnel status received: %s, code %d, description: %s",
|
|
1059
1088
|
response.status.message,
|
|
1060
1089
|
response.status.code,
|
|
1061
|
-
response.status.description || "n/a"
|
|
1090
|
+
response.status.description || "n/a"
|
|
1062
1091
|
);
|
|
1063
1092
|
invokeResponse = {
|
|
1064
1093
|
ended: true,
|
|
1065
|
-
error: `Error in tunnel: ${response.status.message}
|
|
1094
|
+
error: `Error in tunnel: ${response.status.message}`
|
|
1066
1095
|
};
|
|
1067
1096
|
} else {
|
|
1068
1097
|
invokeResponse.methodId = response.invoke_id;
|
|
@@ -1072,11 +1101,11 @@ function _parseResponse(method, meta, response) {
|
|
|
1072
1101
|
const decryptedPayload = VoltCredential.aesDecrypt(
|
|
1073
1102
|
response,
|
|
1074
1103
|
meta.sharedKey.key,
|
|
1075
|
-
meta.sharedKey.iv
|
|
1104
|
+
meta.sharedKey.iv
|
|
1076
1105
|
);
|
|
1077
1106
|
invokeResponse = {
|
|
1078
1107
|
payload:
|
|
1079
|
-
this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)
|
|
1108
|
+
this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)
|
|
1080
1109
|
};
|
|
1081
1110
|
} else {
|
|
1082
1111
|
invokeResponse = { payload: response };
|
|
@@ -1106,7 +1135,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1106
1135
|
this,
|
|
1107
1136
|
request,
|
|
1108
1137
|
this._methodName,
|
|
1109
|
-
this._methodType
|
|
1138
|
+
this._methodType
|
|
1110
1139
|
);
|
|
1111
1140
|
|
|
1112
1141
|
const isServiceRelayed =
|
|
@@ -1121,7 +1150,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1121
1150
|
}
|
|
1122
1151
|
|
|
1123
1152
|
this._call = callClient[this._initialRequest.method](
|
|
1124
|
-
this._initialRequest.meta.metadata
|
|
1153
|
+
this._initialRequest.meta.metadata
|
|
1125
1154
|
);
|
|
1126
1155
|
|
|
1127
1156
|
this._call.on("data", (streamResponse) => {
|
|
@@ -1130,14 +1159,14 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1130
1159
|
this,
|
|
1131
1160
|
this._methodName,
|
|
1132
1161
|
this._initialRequest.meta,
|
|
1133
|
-
streamResponse
|
|
1162
|
+
streamResponse
|
|
1134
1163
|
);
|
|
1135
1164
|
if (parsedResponse.payload) {
|
|
1136
1165
|
// Interpret a non-empty status message as an error.
|
|
1137
1166
|
if (parsedResponse.payload?.status?.message) {
|
|
1138
1167
|
this.emit(
|
|
1139
1168
|
"error",
|
|
1140
|
-
new Error(parsedResponse.payload.status.message)
|
|
1169
|
+
new Error(parsedResponse.payload.status.message)
|
|
1141
1170
|
);
|
|
1142
1171
|
} else {
|
|
1143
1172
|
this.emit("data", parsedResponse.payload);
|
|
@@ -1156,7 +1185,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1156
1185
|
log$4(
|
|
1157
1186
|
"failure processing %s call response: %s",
|
|
1158
1187
|
this._methodName,
|
|
1159
|
-
err.message
|
|
1188
|
+
err.message
|
|
1160
1189
|
);
|
|
1161
1190
|
this.emit("error", err);
|
|
1162
1191
|
}
|
|
@@ -1178,7 +1207,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1178
1207
|
log$4(
|
|
1179
1208
|
"ended call %s, method id: %s",
|
|
1180
1209
|
this._methodName,
|
|
1181
|
-
this._initialRequest.methodId || "n/a - not tunnelling"
|
|
1210
|
+
this._initialRequest.methodId || "n/a - not tunnelling"
|
|
1182
1211
|
);
|
|
1183
1212
|
this.emit("end");
|
|
1184
1213
|
});
|
|
@@ -1211,7 +1240,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1211
1240
|
const payloadRequest = _preparePayloadRequest.call(
|
|
1212
1241
|
this,
|
|
1213
1242
|
request,
|
|
1214
|
-
this._initialRequest
|
|
1243
|
+
this._initialRequest
|
|
1215
1244
|
);
|
|
1216
1245
|
return this._call.write(payloadRequest);
|
|
1217
1246
|
} else {
|
|
@@ -1219,7 +1248,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1219
1248
|
this,
|
|
1220
1249
|
request,
|
|
1221
1250
|
this._methodName,
|
|
1222
|
-
this._methodType
|
|
1251
|
+
this._methodType
|
|
1223
1252
|
);
|
|
1224
1253
|
|
|
1225
1254
|
return this._call.write(this._initialRequest.request);
|
|
@@ -1244,10 +1273,9 @@ function _cleanUp(immediateReconnect) {
|
|
|
1244
1273
|
this._pingTimeoutTimer = 0;
|
|
1245
1274
|
}
|
|
1246
1275
|
|
|
1247
|
-
if (
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
) {
|
|
1276
|
+
if (this._dying) {
|
|
1277
|
+
clearTimeout(this._reconnectTimer);
|
|
1278
|
+
} else if (immediateReconnect || (this._autoRetry && !this._reconnectTimer)) {
|
|
1251
1279
|
this._reconnectTimer = setTimeout(
|
|
1252
1280
|
() => {
|
|
1253
1281
|
this._reconnectTimer = 0;
|
|
@@ -1691,30 +1719,31 @@ const voltServices = [
|
|
|
1691
1719
|
constants.serviceType.wireAPI
|
|
1692
1720
|
];
|
|
1693
1721
|
|
|
1694
|
-
function
|
|
1722
|
+
function issueAuthenticate(authenticateRequest, ttl) {
|
|
1695
1723
|
// eslint-disable-next-line no-use-before-define
|
|
1696
1724
|
return unaryCallInternal
|
|
1697
|
-
.call(this, "
|
|
1698
|
-
.then((
|
|
1699
|
-
log$1("got
|
|
1700
|
-
if (
|
|
1701
|
-
log$1("error in bind response: %s",
|
|
1702
|
-
return Promise.reject(new Error(
|
|
1725
|
+
.call(this, "Authenticate", authenticateRequest)
|
|
1726
|
+
.then((authenticateResponse) => {
|
|
1727
|
+
log$1("got authenticate response %j", authenticateResponse);
|
|
1728
|
+
if (authenticateResponse.status?.code) {
|
|
1729
|
+
log$1("error in bind response: %s", authenticateResponse.status.message);
|
|
1730
|
+
return Promise.reject(new Error(authenticateResponse.status.message));
|
|
1703
1731
|
} else {
|
|
1704
|
-
switch (
|
|
1732
|
+
switch (authenticateResponse.decision) {
|
|
1705
1733
|
case "POLICY_DECISION_PERMIT": {
|
|
1706
1734
|
//
|
|
1707
1735
|
// The request status is permit => cache the bind response info.
|
|
1708
1736
|
//
|
|
1709
1737
|
|
|
1710
1738
|
// This is the certificate assigned to us by the volt.
|
|
1711
|
-
this._credential.cache.cert =
|
|
1739
|
+
this._credential.cache.cert = authenticateResponse.cert;
|
|
1712
1740
|
|
|
1713
1741
|
// This is the signing CA used by the volt.
|
|
1714
|
-
this._credential.cache.ca =
|
|
1742
|
+
this._credential.cache.ca = authenticateResponse.chain;
|
|
1715
1743
|
|
|
1716
1744
|
// Identity resource id is assigned by the volt.
|
|
1717
|
-
this._credential.cache.
|
|
1745
|
+
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
1746
|
+
|
|
1718
1747
|
this._credential.saveCache();
|
|
1719
1748
|
break;
|
|
1720
1749
|
}
|
|
@@ -1728,12 +1757,15 @@ function issueBind(bindRequest, ttl) {
|
|
|
1728
1757
|
break;
|
|
1729
1758
|
}
|
|
1730
1759
|
default:
|
|
1731
|
-
log$1(
|
|
1760
|
+
log$1(
|
|
1761
|
+
"ignoring unknown bind descision %s",
|
|
1762
|
+
authenticateResponse.status
|
|
1763
|
+
);
|
|
1732
1764
|
break;
|
|
1733
1765
|
}
|
|
1734
1766
|
}
|
|
1735
1767
|
|
|
1736
|
-
return
|
|
1768
|
+
return authenticateResponse.decision;
|
|
1737
1769
|
});
|
|
1738
1770
|
}
|
|
1739
1771
|
|
|
@@ -1746,7 +1778,7 @@ function findDIDDocumentService(document, serviceType) {
|
|
|
1746
1778
|
return matches;
|
|
1747
1779
|
}
|
|
1748
1780
|
|
|
1749
|
-
async function
|
|
1781
|
+
async function authenticateInternal(useDID = true) {
|
|
1750
1782
|
try {
|
|
1751
1783
|
// Check if we need to resolve a Relay volt.
|
|
1752
1784
|
if (
|
|
@@ -1760,6 +1792,7 @@ async function bindInternal() {
|
|
|
1760
1792
|
this,
|
|
1761
1793
|
`${relayURL}/discovery`
|
|
1762
1794
|
);
|
|
1795
|
+
|
|
1763
1796
|
if (this._voltConfig.relay.ca_pem) {
|
|
1764
1797
|
log$1(
|
|
1765
1798
|
"Auto-fetched Relay CA %s, remote address %s",
|
|
@@ -1782,7 +1815,9 @@ async function bindInternal() {
|
|
|
1782
1815
|
}
|
|
1783
1816
|
|
|
1784
1817
|
const keyPair = this._credential.getKey();
|
|
1785
|
-
const publicKeyPem =
|
|
1818
|
+
const publicKeyPem = removeCarriageReturn(
|
|
1819
|
+
pki.publicKeyToPem(keyPair.publicKey)
|
|
1820
|
+
);
|
|
1786
1821
|
|
|
1787
1822
|
if (!this._credential.cache.bindIp) {
|
|
1788
1823
|
// Default the IP address if none is specified. This is used to set the SAN in the
|
|
@@ -1793,21 +1828,13 @@ async function bindInternal() {
|
|
|
1793
1828
|
this._credential.saveCache();
|
|
1794
1829
|
|
|
1795
1830
|
// Form the request message.
|
|
1796
|
-
const
|
|
1831
|
+
const authenticateRequest = {
|
|
1797
1832
|
binding_name: this._config.client_name,
|
|
1798
|
-
|
|
1799
|
-
host: this._credential.cache.bindIp,
|
|
1800
|
-
x509_credential: []
|
|
1833
|
+
host: this._credential.cache.bindIp
|
|
1801
1834
|
};
|
|
1802
1835
|
|
|
1803
|
-
if (this._credential.cache.cert) {
|
|
1804
|
-
bindRequest.x509_credential = [
|
|
1805
|
-
this._credential.cache.cert + this._credential.cache.ca
|
|
1806
|
-
];
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
1836
|
if (this._voltConfig.challenge_code) {
|
|
1810
|
-
|
|
1837
|
+
authenticateRequest.challenge = signBase64(
|
|
1811
1838
|
keyPair.privateKey,
|
|
1812
1839
|
this._voltConfig.challenge_code
|
|
1813
1840
|
);
|
|
@@ -1817,15 +1844,71 @@ async function bindInternal() {
|
|
|
1817
1844
|
);
|
|
1818
1845
|
}
|
|
1819
1846
|
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1847
|
+
if (!useDID) {
|
|
1848
|
+
authenticateRequest.public_key = publicKeyPem;
|
|
1849
|
+
} else if (!this._credential.cache.identity_id) {
|
|
1850
|
+
// If we don't have an identity id, then we need to create a DID.
|
|
1851
|
+
this._credential.cache.identity_id = `did:volt:${uuid.v4()}`;
|
|
1852
|
+
|
|
1853
|
+
const did = this._credential.cache.identity_id;
|
|
1854
|
+
|
|
1855
|
+
// Order of properties in the DID document is important,
|
|
1856
|
+
// they must be in alphabetical order throughout the document.
|
|
1857
|
+
// This is because the DID document is signed and the signature
|
|
1858
|
+
// is over the entire document.
|
|
1859
|
+
const didDocument = {
|
|
1860
|
+
"@context": "https://www.w3.org/ns/did/v1",
|
|
1861
|
+
authentication: [`${did}#key-1`],
|
|
1862
|
+
description: this._config.client_name,
|
|
1863
|
+
id: did,
|
|
1864
|
+
verificationMethod: [
|
|
1865
|
+
{
|
|
1866
|
+
controller: did,
|
|
1867
|
+
id: `${did}#key-1`,
|
|
1868
|
+
publicKeyPem: publicKeyPem,
|
|
1869
|
+
type: "RsaSignature2018"
|
|
1870
|
+
}
|
|
1871
|
+
]
|
|
1872
|
+
};
|
|
1873
|
+
|
|
1874
|
+
const signDocument = JSON.stringify(didDocument, null, 2);
|
|
1875
|
+
log$1("signing did document");
|
|
1876
|
+
log$1(signDocument);
|
|
1877
|
+
|
|
1878
|
+
// Sign the DID document.
|
|
1879
|
+
const documentDigest = signBase64(
|
|
1880
|
+
keyPair.privateKey,
|
|
1881
|
+
signDocument
|
|
1824
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
|
+
};
|
|
1894
|
+
|
|
1895
|
+
// Set the DID document with proof in the authenticate request.
|
|
1896
|
+
authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
|
|
1897
|
+
|
|
1898
|
+
// Now sign the entire DID document, including the proof and set it in the
|
|
1899
|
+
// bind request. This is required by the Volt to verify that we have the private key for the DID.
|
|
1900
|
+
authenticateRequest.did_document_signature = signBase64(
|
|
1901
|
+
keyPair.privateKey,
|
|
1902
|
+
authenticateRequest.did_document
|
|
1903
|
+
);
|
|
1904
|
+
|
|
1905
|
+
log$1("did document is %s", authenticateRequest.did_document);
|
|
1906
|
+
log$1("did signature is %s", authenticateRequest.did_document_signature);
|
|
1907
|
+
} else {
|
|
1908
|
+
authenticateRequest.did = this._credential.cache.identity_id;
|
|
1825
1909
|
}
|
|
1826
1910
|
|
|
1827
|
-
log$1("
|
|
1828
|
-
log$1("bind TTL is ", this._config.bindRequestTTL);
|
|
1911
|
+
log$1("authenticate TTL is ", this._config.bindRequestTTL);
|
|
1829
1912
|
|
|
1830
1913
|
// This will block while the request is pending.
|
|
1831
1914
|
const pollInterval = 10000;
|
|
@@ -1837,12 +1920,12 @@ async function bindInternal() {
|
|
|
1837
1920
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1838
1921
|
}
|
|
1839
1922
|
// eslint-disable-next-line no-await-in-loop
|
|
1840
|
-
decision = await
|
|
1923
|
+
decision = await issueAuthenticate.call(
|
|
1841
1924
|
this,
|
|
1842
|
-
|
|
1925
|
+
authenticateRequest,
|
|
1843
1926
|
this._voltConfig.bindRequestTTL
|
|
1844
1927
|
);
|
|
1845
|
-
log$1("
|
|
1928
|
+
log$1("authenticate decision: %s", decision);
|
|
1846
1929
|
} while (
|
|
1847
1930
|
decision === "POLICY_DECISION_PROMPT" ||
|
|
1848
1931
|
decision === "POLICY_DECISION_PENDING"
|
|
@@ -1895,6 +1978,15 @@ function connectInternal(helloPayload) {
|
|
|
1895
1978
|
onDisconnected();
|
|
1896
1979
|
this.emit("connected", false);
|
|
1897
1980
|
}
|
|
1981
|
+
|
|
1982
|
+
if (err.message.endsWith("session invalid")) {
|
|
1983
|
+
log$1("Volt session invalid - clearing session");
|
|
1984
|
+
delete this._credential.cache.session_id;
|
|
1985
|
+
delete this._credential.cache.cert;
|
|
1986
|
+
this._credential.saveCache();
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
this.emit("connectionError", err);
|
|
1898
1990
|
});
|
|
1899
1991
|
|
|
1900
1992
|
this._voltConnection.on("evt", (evt) => {
|
|
@@ -1964,8 +2056,6 @@ function getVoltAPIClientInternal() {
|
|
|
1964
2056
|
|
|
1965
2057
|
function getAPIClientInternal(service) {
|
|
1966
2058
|
if (!this._cachedService[service.id]) {
|
|
1967
|
-
service.service_description.host_type === "SERVICE_HOST_TYPE_RELAYED";
|
|
1968
|
-
|
|
1969
2059
|
const serviceAddress = this.isRemote
|
|
1970
2060
|
? this._voltConfig.relay.address
|
|
1971
2061
|
: service.service_description.host_address;
|
|
@@ -2013,9 +2103,8 @@ function getServiceClient(service) {
|
|
|
2013
2103
|
}
|
|
2014
2104
|
|
|
2015
2105
|
function unaryCallInternal(method, request, service) {
|
|
2016
|
-
const grpcClient = getServiceClient.call(this, service);
|
|
2017
|
-
|
|
2018
2106
|
return new Promise((resolve, reject) => {
|
|
2107
|
+
const grpcClient = getServiceClient.call(this, service);
|
|
2019
2108
|
const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY", service);
|
|
2020
2109
|
|
|
2021
2110
|
let response = null;
|
|
@@ -2264,14 +2353,14 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2264
2353
|
if (this._credential._cryptoCache.cert) {
|
|
2265
2354
|
isBound = true;
|
|
2266
2355
|
} else {
|
|
2267
|
-
// No certificate present in the cache yet => we need to
|
|
2356
|
+
// No certificate present in the cache yet => we need to authenticate.
|
|
2268
2357
|
try {
|
|
2269
|
-
isBound = await
|
|
2358
|
+
isBound = await authenticateInternal.call(this);
|
|
2270
2359
|
} catch (bindErr) {
|
|
2271
2360
|
if (bindErr.message === "invalid arguments") {
|
|
2272
2361
|
// This is usually the result of a missing challenge signature or credential.
|
|
2273
2362
|
log(
|
|
2274
|
-
"failure
|
|
2363
|
+
"failure authenticating on Volt - did you supply a challenge code or verified credential?"
|
|
2275
2364
|
);
|
|
2276
2365
|
throw bindErr;
|
|
2277
2366
|
}
|
|
@@ -2280,7 +2369,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2280
2369
|
|
|
2281
2370
|
if (!isBound) {
|
|
2282
2371
|
// This is usually because of request being denied.
|
|
2283
|
-
throw new Error("Volt
|
|
2372
|
+
throw new Error("Volt authentication failed");
|
|
2284
2373
|
}
|
|
2285
2374
|
|
|
2286
2375
|
return Promise.resolve();
|
|
@@ -2506,6 +2595,10 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2506
2595
|
* VoltAPI - volt management
|
|
2507
2596
|
*/
|
|
2508
2597
|
|
|
2598
|
+
Authenticate(request) {
|
|
2599
|
+
return unaryCallInternal.call(this, "Authenticate", request);
|
|
2600
|
+
}
|
|
2601
|
+
|
|
2509
2602
|
Bind(request) {
|
|
2510
2603
|
return unaryCallInternal.call(this, "Bind", request);
|
|
2511
2604
|
}
|