@tdxvolt/volt-client-grpc 0.15.1 → 0.16.0-beta.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 +176 -97
- 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 +89 -39
- package/src/volt-client.js +9 -5
- package/src/volt-credential.js +10 -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,12 @@ class VoltCredential {
|
|
|
567
589
|
return this._cryptoCache;
|
|
568
590
|
}
|
|
569
591
|
|
|
570
|
-
get
|
|
571
|
-
return this._cryptoCache.
|
|
592
|
+
get sessionId() {
|
|
593
|
+
return this._cryptoCache.session_id;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
get identityId() {
|
|
597
|
+
return this._cryptoCache.identity_id;
|
|
572
598
|
}
|
|
573
599
|
|
|
574
600
|
get voltPublicKey() {
|
|
@@ -656,7 +682,7 @@ class VoltCredential {
|
|
|
656
682
|
aud: audience,
|
|
657
683
|
iat: Math.floor(Date.now() / 1000) - ttl,
|
|
658
684
|
exp: Math.floor(Date.now() / 1000) + ttl,
|
|
659
|
-
sub: this._cryptoCache.
|
|
685
|
+
sub: this._cryptoCache.session_id
|
|
660
686
|
};
|
|
661
687
|
|
|
662
688
|
let sharedKey;
|
|
@@ -738,12 +764,13 @@ class VoltCredential {
|
|
|
738
764
|
}
|
|
739
765
|
|
|
740
766
|
static rsaEncrypt(key, buffer) {
|
|
741
|
-
|
|
767
|
+
const cipher = crypto__default["default"].publicEncrypt(key, buffer);
|
|
768
|
+
return cipher.toString("base64url");
|
|
742
769
|
}
|
|
743
770
|
|
|
744
771
|
static rsaDecrypt(key, buffer) {
|
|
745
772
|
const keyObj = crypto__default["default"].createPrivateKey(key);
|
|
746
|
-
const cipher = Buffer.from(buffer, "
|
|
773
|
+
const cipher = Buffer.from(buffer, "base64url");
|
|
747
774
|
return Buffer.from(crypto__default["default"].privateDecrypt(keyObj, cipher));
|
|
748
775
|
}
|
|
749
776
|
}
|
|
@@ -881,7 +908,7 @@ const log$4 = debug__default["default"]("volt-client-grpc:grpc-call");
|
|
|
881
908
|
function _prepareInvokeRequest(
|
|
882
909
|
request,
|
|
883
910
|
method,
|
|
884
|
-
methodType = "METHOD_TYPE_UNARY"
|
|
911
|
+
methodType = "METHOD_TYPE_UNARY"
|
|
885
912
|
) {
|
|
886
913
|
const isServiceRelayed =
|
|
887
914
|
this._service?.service_description.host_type ===
|
|
@@ -894,7 +921,7 @@ function _prepareInvokeRequest(
|
|
|
894
921
|
this._voltClient.voltConfig.id,
|
|
895
922
|
this._service?.service_description.host_public_key ||
|
|
896
923
|
this._voltClient.credential.voltPublicKey,
|
|
897
|
-
true
|
|
924
|
+
true
|
|
898
925
|
);
|
|
899
926
|
|
|
900
927
|
let invokeRequest;
|
|
@@ -925,42 +952,40 @@ function _prepareInvokeRequest(
|
|
|
925
952
|
method_name: this._grpcClient[method].path,
|
|
926
953
|
method_type: methodType,
|
|
927
954
|
token: methodToken.token,
|
|
928
|
-
request: requestPayload
|
|
929
|
-
}
|
|
955
|
+
request: requestPayload
|
|
956
|
+
}
|
|
930
957
|
};
|
|
931
958
|
|
|
932
|
-
let
|
|
959
|
+
let targetDID = [];
|
|
933
960
|
let invokePayload;
|
|
934
961
|
if (this._voltClient.isVoltRelay || isServiceRelayed) {
|
|
935
962
|
// When sending via a relay we need to encrypt the payload.
|
|
936
963
|
invokePayload = VoltCredential.aesEncrypt(
|
|
937
964
|
tunnelMethod.responseSerialize(tunnelResp),
|
|
938
965
|
methodToken.sharedKey.key,
|
|
939
|
-
methodToken.sharedKey.iv
|
|
966
|
+
methodToken.sharedKey.iv
|
|
940
967
|
);
|
|
941
968
|
} else {
|
|
942
969
|
invokePayload = tunnelMethod.responseSerialize(tunnelResp);
|
|
943
970
|
}
|
|
944
971
|
|
|
945
972
|
if (this._voltClient.isVoltRelay) {
|
|
946
|
-
|
|
973
|
+
targetDID.push(this._voltClient.voltConfig.id);
|
|
947
974
|
}
|
|
948
975
|
|
|
949
976
|
if (isServiceRelayed) {
|
|
950
977
|
// Add another relay hop if the target service is relayed.
|
|
951
978
|
log$4("target service is relayed");
|
|
952
|
-
|
|
953
|
-
this._service.service_description.host_fingerprint,
|
|
954
|
-
);
|
|
979
|
+
targetDID.push(this._service.service_description.host_client_id);
|
|
955
980
|
}
|
|
956
981
|
|
|
957
|
-
log$4("target is %j",
|
|
982
|
+
log$4("target is %j", targetDID);
|
|
958
983
|
|
|
959
984
|
invokeRequest = {
|
|
960
|
-
|
|
985
|
+
target_did: targetDID,
|
|
961
986
|
token: methodToken.token,
|
|
962
987
|
payload: invokePayload,
|
|
963
|
-
target_service_id: isServiceRelayed ? this._service.id : undefined
|
|
988
|
+
target_service_id: isServiceRelayed ? this._service.id : undefined
|
|
964
989
|
};
|
|
965
990
|
|
|
966
991
|
// Replace the target method with a call to Invoke on the relay Volt.
|
|
@@ -973,7 +998,7 @@ function _prepareInvokeRequest(
|
|
|
973
998
|
meta: methodToken,
|
|
974
999
|
method: callMethod,
|
|
975
1000
|
methodName: method,
|
|
976
|
-
request: invokeRequest
|
|
1001
|
+
request: invokeRequest
|
|
977
1002
|
};
|
|
978
1003
|
}
|
|
979
1004
|
|
|
@@ -993,18 +1018,18 @@ function _preparePayloadRequest(request, invokeInfo) {
|
|
|
993
1018
|
|
|
994
1019
|
const tunnelResp = {
|
|
995
1020
|
method_payload: {
|
|
996
|
-
payload: requestPayload
|
|
997
|
-
}
|
|
1021
|
+
payload: requestPayload
|
|
1022
|
+
}
|
|
998
1023
|
};
|
|
999
1024
|
|
|
1000
1025
|
const invokePayload = VoltCredential.aesEncrypt(
|
|
1001
1026
|
tunnelMethod.responseSerialize(tunnelResp),
|
|
1002
1027
|
invokeInfo.meta.sharedKey.key,
|
|
1003
|
-
invokeInfo.meta.sharedKey.iv
|
|
1028
|
+
invokeInfo.meta.sharedKey.iv
|
|
1004
1029
|
);
|
|
1005
1030
|
|
|
1006
1031
|
payloadRequest = {
|
|
1007
|
-
payload: invokePayload
|
|
1032
|
+
payload: invokePayload
|
|
1008
1033
|
};
|
|
1009
1034
|
} else {
|
|
1010
1035
|
payloadRequest = request;
|
|
@@ -1028,7 +1053,7 @@ function _parseResponse(method, meta, response) {
|
|
|
1028
1053
|
decryptedPayload = VoltCredential.aesDecrypt(
|
|
1029
1054
|
response.payload,
|
|
1030
1055
|
meta.sharedKey.key,
|
|
1031
|
-
meta.sharedKey.iv
|
|
1056
|
+
meta.sharedKey.iv
|
|
1032
1057
|
);
|
|
1033
1058
|
} else {
|
|
1034
1059
|
decryptedPayload = response.payload;
|
|
@@ -1039,12 +1064,12 @@ function _parseResponse(method, meta, response) {
|
|
|
1039
1064
|
if (responsePayload.payload === "method_payload") {
|
|
1040
1065
|
try {
|
|
1041
1066
|
invokeResponse.payload = this._grpcClient[method].responseDeserialize(
|
|
1042
|
-
responsePayload.method_payload.payload
|
|
1067
|
+
responsePayload.method_payload.payload
|
|
1043
1068
|
);
|
|
1044
1069
|
} catch (err) {
|
|
1045
1070
|
log$4("failure deserialising payload for %s", this._methodName);
|
|
1046
1071
|
throw new Error(
|
|
1047
|
-
"failure deserialising payload - check protobuf definition matches with data being sent"
|
|
1072
|
+
"failure deserialising payload - check protobuf definition matches with data being sent"
|
|
1048
1073
|
);
|
|
1049
1074
|
}
|
|
1050
1075
|
} else if (responsePayload.payload === "method_end") {
|
|
@@ -1058,11 +1083,11 @@ function _parseResponse(method, meta, response) {
|
|
|
1058
1083
|
"Tunnel status received: %s, code %d, description: %s",
|
|
1059
1084
|
response.status.message,
|
|
1060
1085
|
response.status.code,
|
|
1061
|
-
response.status.description || "n/a"
|
|
1086
|
+
response.status.description || "n/a"
|
|
1062
1087
|
);
|
|
1063
1088
|
invokeResponse = {
|
|
1064
1089
|
ended: true,
|
|
1065
|
-
error: `Error in tunnel: ${response.status.message}
|
|
1090
|
+
error: `Error in tunnel: ${response.status.message}`
|
|
1066
1091
|
};
|
|
1067
1092
|
} else {
|
|
1068
1093
|
invokeResponse.methodId = response.invoke_id;
|
|
@@ -1072,11 +1097,11 @@ function _parseResponse(method, meta, response) {
|
|
|
1072
1097
|
const decryptedPayload = VoltCredential.aesDecrypt(
|
|
1073
1098
|
response,
|
|
1074
1099
|
meta.sharedKey.key,
|
|
1075
|
-
meta.sharedKey.iv
|
|
1100
|
+
meta.sharedKey.iv
|
|
1076
1101
|
);
|
|
1077
1102
|
invokeResponse = {
|
|
1078
1103
|
payload:
|
|
1079
|
-
this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)
|
|
1104
|
+
this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)
|
|
1080
1105
|
};
|
|
1081
1106
|
} else {
|
|
1082
1107
|
invokeResponse = { payload: response };
|
|
@@ -1106,7 +1131,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1106
1131
|
this,
|
|
1107
1132
|
request,
|
|
1108
1133
|
this._methodName,
|
|
1109
|
-
this._methodType
|
|
1134
|
+
this._methodType
|
|
1110
1135
|
);
|
|
1111
1136
|
|
|
1112
1137
|
const isServiceRelayed =
|
|
@@ -1121,7 +1146,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1121
1146
|
}
|
|
1122
1147
|
|
|
1123
1148
|
this._call = callClient[this._initialRequest.method](
|
|
1124
|
-
this._initialRequest.meta.metadata
|
|
1149
|
+
this._initialRequest.meta.metadata
|
|
1125
1150
|
);
|
|
1126
1151
|
|
|
1127
1152
|
this._call.on("data", (streamResponse) => {
|
|
@@ -1130,14 +1155,14 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1130
1155
|
this,
|
|
1131
1156
|
this._methodName,
|
|
1132
1157
|
this._initialRequest.meta,
|
|
1133
|
-
streamResponse
|
|
1158
|
+
streamResponse
|
|
1134
1159
|
);
|
|
1135
1160
|
if (parsedResponse.payload) {
|
|
1136
1161
|
// Interpret a non-empty status message as an error.
|
|
1137
1162
|
if (parsedResponse.payload?.status?.message) {
|
|
1138
1163
|
this.emit(
|
|
1139
1164
|
"error",
|
|
1140
|
-
new Error(parsedResponse.payload.status.message)
|
|
1165
|
+
new Error(parsedResponse.payload.status.message)
|
|
1141
1166
|
);
|
|
1142
1167
|
} else {
|
|
1143
1168
|
this.emit("data", parsedResponse.payload);
|
|
@@ -1156,7 +1181,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1156
1181
|
log$4(
|
|
1157
1182
|
"failure processing %s call response: %s",
|
|
1158
1183
|
this._methodName,
|
|
1159
|
-
err.message
|
|
1184
|
+
err.message
|
|
1160
1185
|
);
|
|
1161
1186
|
this.emit("error", err);
|
|
1162
1187
|
}
|
|
@@ -1178,7 +1203,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1178
1203
|
log$4(
|
|
1179
1204
|
"ended call %s, method id: %s",
|
|
1180
1205
|
this._methodName,
|
|
1181
|
-
this._initialRequest.methodId || "n/a - not tunnelling"
|
|
1206
|
+
this._initialRequest.methodId || "n/a - not tunnelling"
|
|
1182
1207
|
);
|
|
1183
1208
|
this.emit("end");
|
|
1184
1209
|
});
|
|
@@ -1211,7 +1236,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1211
1236
|
const payloadRequest = _preparePayloadRequest.call(
|
|
1212
1237
|
this,
|
|
1213
1238
|
request,
|
|
1214
|
-
this._initialRequest
|
|
1239
|
+
this._initialRequest
|
|
1215
1240
|
);
|
|
1216
1241
|
return this._call.write(payloadRequest);
|
|
1217
1242
|
} else {
|
|
@@ -1219,7 +1244,7 @@ class GRPCCall extends EventEmitter__default["default"] {
|
|
|
1219
1244
|
this,
|
|
1220
1245
|
request,
|
|
1221
1246
|
this._methodName,
|
|
1222
|
-
this._methodType
|
|
1247
|
+
this._methodType
|
|
1223
1248
|
);
|
|
1224
1249
|
|
|
1225
1250
|
return this._call.write(this._initialRequest.request);
|
|
@@ -1691,30 +1716,31 @@ const voltServices = [
|
|
|
1691
1716
|
constants.serviceType.wireAPI
|
|
1692
1717
|
];
|
|
1693
1718
|
|
|
1694
|
-
function
|
|
1719
|
+
function issueAuthenticate(authenticateRequest, ttl) {
|
|
1695
1720
|
// eslint-disable-next-line no-use-before-define
|
|
1696
1721
|
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(
|
|
1722
|
+
.call(this, "Authenticate", authenticateRequest)
|
|
1723
|
+
.then((authenticateResponse) => {
|
|
1724
|
+
log$1("got authenticate response %j", authenticateResponse);
|
|
1725
|
+
if (authenticateResponse.status?.code) {
|
|
1726
|
+
log$1("error in bind response: %s", authenticateResponse.status.message);
|
|
1727
|
+
return Promise.reject(new Error(authenticateResponse.status.message));
|
|
1703
1728
|
} else {
|
|
1704
|
-
switch (
|
|
1729
|
+
switch (authenticateResponse.decision) {
|
|
1705
1730
|
case "POLICY_DECISION_PERMIT": {
|
|
1706
1731
|
//
|
|
1707
1732
|
// The request status is permit => cache the bind response info.
|
|
1708
1733
|
//
|
|
1709
1734
|
|
|
1710
1735
|
// This is the certificate assigned to us by the volt.
|
|
1711
|
-
this._credential.cache.cert =
|
|
1736
|
+
this._credential.cache.cert = authenticateResponse.cert;
|
|
1712
1737
|
|
|
1713
1738
|
// This is the signing CA used by the volt.
|
|
1714
|
-
this._credential.cache.ca =
|
|
1739
|
+
this._credential.cache.ca = authenticateResponse.chain;
|
|
1715
1740
|
|
|
1716
1741
|
// Identity resource id is assigned by the volt.
|
|
1717
|
-
this._credential.cache.
|
|
1742
|
+
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
1743
|
+
|
|
1718
1744
|
this._credential.saveCache();
|
|
1719
1745
|
break;
|
|
1720
1746
|
}
|
|
@@ -1728,12 +1754,15 @@ function issueBind(bindRequest, ttl) {
|
|
|
1728
1754
|
break;
|
|
1729
1755
|
}
|
|
1730
1756
|
default:
|
|
1731
|
-
log$1(
|
|
1757
|
+
log$1(
|
|
1758
|
+
"ignoring unknown bind descision %s",
|
|
1759
|
+
authenticateResponse.status
|
|
1760
|
+
);
|
|
1732
1761
|
break;
|
|
1733
1762
|
}
|
|
1734
1763
|
}
|
|
1735
1764
|
|
|
1736
|
-
return
|
|
1765
|
+
return authenticateResponse.decision;
|
|
1737
1766
|
});
|
|
1738
1767
|
}
|
|
1739
1768
|
|
|
@@ -1746,7 +1775,7 @@ function findDIDDocumentService(document, serviceType) {
|
|
|
1746
1775
|
return matches;
|
|
1747
1776
|
}
|
|
1748
1777
|
|
|
1749
|
-
async function
|
|
1778
|
+
async function authenticateInternal(createDID = true) {
|
|
1750
1779
|
try {
|
|
1751
1780
|
// Check if we need to resolve a Relay volt.
|
|
1752
1781
|
if (
|
|
@@ -1760,6 +1789,7 @@ async function bindInternal() {
|
|
|
1760
1789
|
this,
|
|
1761
1790
|
`${relayURL}/discovery`
|
|
1762
1791
|
);
|
|
1792
|
+
|
|
1763
1793
|
if (this._voltConfig.relay.ca_pem) {
|
|
1764
1794
|
log$1(
|
|
1765
1795
|
"Auto-fetched Relay CA %s, remote address %s",
|
|
@@ -1793,21 +1823,13 @@ async function bindInternal() {
|
|
|
1793
1823
|
this._credential.saveCache();
|
|
1794
1824
|
|
|
1795
1825
|
// Form the request message.
|
|
1796
|
-
const
|
|
1826
|
+
const authenticateRequest = {
|
|
1797
1827
|
binding_name: this._config.client_name,
|
|
1798
|
-
|
|
1799
|
-
host: this._credential.cache.bindIp,
|
|
1800
|
-
x509_credential: []
|
|
1828
|
+
host: this._credential.cache.bindIp
|
|
1801
1829
|
};
|
|
1802
1830
|
|
|
1803
|
-
if (this._credential.cache.cert) {
|
|
1804
|
-
bindRequest.x509_credential = [
|
|
1805
|
-
this._credential.cache.cert + this._credential.cache.ca
|
|
1806
|
-
];
|
|
1807
|
-
}
|
|
1808
|
-
|
|
1809
1831
|
if (this._voltConfig.challenge_code) {
|
|
1810
|
-
|
|
1832
|
+
authenticateRequest.challenge = signBase64(
|
|
1811
1833
|
keyPair.privateKey,
|
|
1812
1834
|
this._voltConfig.challenge_code
|
|
1813
1835
|
);
|
|
@@ -1817,15 +1839,71 @@ async function bindInternal() {
|
|
|
1817
1839
|
);
|
|
1818
1840
|
}
|
|
1819
1841
|
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1842
|
+
if (!createDID) {
|
|
1843
|
+
authenticateRequest.public_key = publicKeyPem;
|
|
1844
|
+
} else {
|
|
1845
|
+
if (!this._credential.cache.identity_id) {
|
|
1846
|
+
// If we don't have an identity id, then we need to create a DID.
|
|
1847
|
+
this._credential.cache.identity_id = `did:volt:${uuid.v4()}`;
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
const did = this._credential.cache.identity_id;
|
|
1851
|
+
|
|
1852
|
+
// Order of properties in the DID document is important,
|
|
1853
|
+
// they must be in alphabetical order throughout the document.
|
|
1854
|
+
// This is because the DID document is signed and the signature
|
|
1855
|
+
// is over the entire document.
|
|
1856
|
+
const didDocument = {
|
|
1857
|
+
"@context": "https://www.w3.org/ns/did/v1",
|
|
1858
|
+
authentication: [`${did}#key-1`],
|
|
1859
|
+
description: this._config.client_name,
|
|
1860
|
+
id: did,
|
|
1861
|
+
verificationMethod: [
|
|
1862
|
+
{
|
|
1863
|
+
controller: did,
|
|
1864
|
+
id: `${did}#key-1`,
|
|
1865
|
+
publicKeyPem: publicKeyPem,
|
|
1866
|
+
type: "RsaSignature2018"
|
|
1867
|
+
}
|
|
1868
|
+
]
|
|
1869
|
+
};
|
|
1870
|
+
|
|
1871
|
+
const signDocument = JSON.stringify(didDocument, null, 2);
|
|
1872
|
+
log$1("signing did document");
|
|
1873
|
+
log$1(signDocument);
|
|
1874
|
+
|
|
1875
|
+
// Sign the DID document.
|
|
1876
|
+
const documentDigest = signBase64(
|
|
1877
|
+
keyPair.privateKey,
|
|
1878
|
+
signDocument
|
|
1879
|
+
);
|
|
1880
|
+
|
|
1881
|
+
const proofSignature = jwsSignature(documentDigest);
|
|
1882
|
+
|
|
1883
|
+
// Create the DID document proof.
|
|
1884
|
+
didDocument.proof = {
|
|
1885
|
+
created: new Date().toISOString(),
|
|
1886
|
+
jws: proofSignature,
|
|
1887
|
+
proofPurpose: "assertionMethod",
|
|
1888
|
+
type: "RsaSignature2018",
|
|
1889
|
+
verificationMethod: `${did}#key-1`
|
|
1890
|
+
};
|
|
1891
|
+
|
|
1892
|
+
// Set the DID document with proof in the authenticate request.
|
|
1893
|
+
authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
|
|
1894
|
+
|
|
1895
|
+
// Now sign the entire DID document, including the proof and set it in the
|
|
1896
|
+
// bind request. This is required by the Volt to verify that we have the private key for the DID.
|
|
1897
|
+
authenticateRequest.did_document_signature = signBase64(
|
|
1898
|
+
keyPair.privateKey,
|
|
1899
|
+
authenticateRequest.did_document
|
|
1824
1900
|
);
|
|
1901
|
+
|
|
1902
|
+
log$1("did document is %s", authenticateRequest.did_document);
|
|
1903
|
+
log$1("did signature is %s", authenticateRequest.did_document_signature);
|
|
1825
1904
|
}
|
|
1826
1905
|
|
|
1827
|
-
log$1("
|
|
1828
|
-
log$1("bind TTL is ", this._config.bindRequestTTL);
|
|
1906
|
+
log$1("authenticate TTL is ", this._config.bindRequestTTL);
|
|
1829
1907
|
|
|
1830
1908
|
// This will block while the request is pending.
|
|
1831
1909
|
const pollInterval = 10000;
|
|
@@ -1837,12 +1915,12 @@ async function bindInternal() {
|
|
|
1837
1915
|
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1838
1916
|
}
|
|
1839
1917
|
// eslint-disable-next-line no-await-in-loop
|
|
1840
|
-
decision = await
|
|
1918
|
+
decision = await issueAuthenticate.call(
|
|
1841
1919
|
this,
|
|
1842
|
-
|
|
1920
|
+
authenticateRequest,
|
|
1843
1921
|
this._voltConfig.bindRequestTTL
|
|
1844
1922
|
);
|
|
1845
|
-
log$1("
|
|
1923
|
+
log$1("authenticate decision: %s", decision);
|
|
1846
1924
|
} while (
|
|
1847
1925
|
decision === "POLICY_DECISION_PROMPT" ||
|
|
1848
1926
|
decision === "POLICY_DECISION_PENDING"
|
|
@@ -1964,8 +2042,6 @@ function getVoltAPIClientInternal() {
|
|
|
1964
2042
|
|
|
1965
2043
|
function getAPIClientInternal(service) {
|
|
1966
2044
|
if (!this._cachedService[service.id]) {
|
|
1967
|
-
service.service_description.host_type === "SERVICE_HOST_TYPE_RELAYED";
|
|
1968
|
-
|
|
1969
2045
|
const serviceAddress = this.isRemote
|
|
1970
2046
|
? this._voltConfig.relay.address
|
|
1971
2047
|
: service.service_description.host_address;
|
|
@@ -2013,9 +2089,8 @@ function getServiceClient(service) {
|
|
|
2013
2089
|
}
|
|
2014
2090
|
|
|
2015
2091
|
function unaryCallInternal(method, request, service) {
|
|
2016
|
-
const grpcClient = getServiceClient.call(this, service);
|
|
2017
|
-
|
|
2018
2092
|
return new Promise((resolve, reject) => {
|
|
2093
|
+
const grpcClient = getServiceClient.call(this, service);
|
|
2019
2094
|
const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY", service);
|
|
2020
2095
|
|
|
2021
2096
|
let response = null;
|
|
@@ -2264,14 +2339,14 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2264
2339
|
if (this._credential._cryptoCache.cert) {
|
|
2265
2340
|
isBound = true;
|
|
2266
2341
|
} else {
|
|
2267
|
-
// No certificate present in the cache yet => we need to
|
|
2342
|
+
// No certificate present in the cache yet => we need to authenticate.
|
|
2268
2343
|
try {
|
|
2269
|
-
isBound = await
|
|
2344
|
+
isBound = await authenticateInternal.call(this);
|
|
2270
2345
|
} catch (bindErr) {
|
|
2271
2346
|
if (bindErr.message === "invalid arguments") {
|
|
2272
2347
|
// This is usually the result of a missing challenge signature or credential.
|
|
2273
2348
|
log(
|
|
2274
|
-
"failure
|
|
2349
|
+
"failure authenticating on Volt - did you supply a challenge code or verified credential?"
|
|
2275
2350
|
);
|
|
2276
2351
|
throw bindErr;
|
|
2277
2352
|
}
|
|
@@ -2280,7 +2355,7 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2280
2355
|
|
|
2281
2356
|
if (!isBound) {
|
|
2282
2357
|
// This is usually because of request being denied.
|
|
2283
|
-
throw new Error("Volt
|
|
2358
|
+
throw new Error("Volt authentication failed");
|
|
2284
2359
|
}
|
|
2285
2360
|
|
|
2286
2361
|
return Promise.resolve();
|
|
@@ -2506,6 +2581,10 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
2506
2581
|
* VoltAPI - volt management
|
|
2507
2582
|
*/
|
|
2508
2583
|
|
|
2584
|
+
Authenticate(request) {
|
|
2585
|
+
return unaryCallInternal.call(this, "Authenticate", request);
|
|
2586
|
+
}
|
|
2587
|
+
|
|
2509
2588
|
Bind(request) {
|
|
2510
2589
|
return unaryCallInternal.call(this, "Bind", request);
|
|
2511
2590
|
}
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.
|
|
6
|
+
"version": "0.16.0-beta.2",
|
|
7
7
|
"description": "tdx Volt library for nodejs clients",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"jsonwebtoken": "^8.5.1",
|
|
46
46
|
"lodash": "^4.17.15",
|
|
47
47
|
"minimist": "^1.2.0",
|
|
48
|
-
"node-forge": "^
|
|
48
|
+
"node-forge": "^1.3.1",
|
|
49
49
|
"uuid": "^8.1.0"
|
|
50
50
|
}
|
|
51
|
-
}
|
|
51
|
+
}
|
|
@@ -50,9 +50,9 @@ message SqlExecuteRequest {
|
|
|
50
50
|
// This can be used to limit the number of rows returned by `SELECT` statements to avoid overloading a client. It is similar to using 'LIMIT/OFFSET' clauses on the 'SELECT' statement, but this method is easier to manage and the entire result set will be prepared on the Volt, and the client can then page through it by sending successive messages.
|
|
51
51
|
uint32 page_size = 3;
|
|
52
52
|
|
|
53
|
-
// The
|
|
53
|
+
// The values to set for each parameter defined in a database query.
|
|
54
54
|
// This field is only relevant to 'query' databases, i.e. those with kind `tdx:sqlite-query`.
|
|
55
|
-
// The values in the
|
|
55
|
+
// The values in the map should be keyed by parameter name.
|
|
56
56
|
// A query may have no parameters defined, in which case leave this field empty.
|
|
57
57
|
map<string, tdx.volt_api.volt.v1.AttributeValue> parameter = 4;
|
|
58
58
|
}
|