@tdxvolt/volt-client-grpc 0.14.61 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.cjs +785 -224
- package/package.json +1 -1
- package/protobuf/tdx/volt_api/volt/v1/file_api.proto +1 -1
- package/protobuf/tdx/volt_api/volt/v1/volt_api.proto +2 -0
- package/protobuf/tdx/volt_api/volt/v1/wire_api.proto +1 -1
- package/src/grpc-call.js +110 -49
- package/src/grpc-utils.js +9 -9
- package/src/proto-utils.js +37 -5
- package/src/rpc-invocation.js +277 -0
- package/src/utils.js +43 -2
- package/src/volt-client-internal.js +155 -58
- package/src/volt-client.js +95 -46
- package/src/volt-connection.js +16 -5
- package/src/volt-credential.js +32 -26
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import debug from "debug";
|
|
2
|
+
import { VoltCredential } from "./volt-credential.js";
|
|
3
|
+
import jwt from "jsonwebtoken";
|
|
4
|
+
import EventEmitter from "events";
|
|
5
|
+
|
|
6
|
+
const log = debug("rpc-invocation");
|
|
7
|
+
|
|
8
|
+
class RpcInvocation extends EventEmitter {
|
|
9
|
+
#voltClient = null;
|
|
10
|
+
#voltConnection = null;
|
|
11
|
+
#token = null;
|
|
12
|
+
#encryptKey = null;
|
|
13
|
+
#encryptIV = null;
|
|
14
|
+
#invokeId = null;
|
|
15
|
+
#payload = null;
|
|
16
|
+
#isJSON = false;
|
|
17
|
+
#methodDescriptor = null;
|
|
18
|
+
#methodName = null;
|
|
19
|
+
|
|
20
|
+
constructor(voltClient, voltConnection) {
|
|
21
|
+
super();
|
|
22
|
+
this.#voltClient = voltClient;
|
|
23
|
+
this.#voltConnection = voltConnection;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get id() {
|
|
27
|
+
return this.#invokeId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
get payload() {
|
|
31
|
+
if (!this.#payload) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let deserialisedPayload = null;
|
|
36
|
+
|
|
37
|
+
if (this.#isJSON) {
|
|
38
|
+
// Don't need to deserialise JSON payload.
|
|
39
|
+
deserialisedPayload = this.#payload;
|
|
40
|
+
} else if (this.#methodDescriptor) {
|
|
41
|
+
deserialisedPayload = this.#methodDescriptor.requestDeserialize(
|
|
42
|
+
this.#payload
|
|
43
|
+
);
|
|
44
|
+
} else {
|
|
45
|
+
log("unexpected: no method descriptor");
|
|
46
|
+
throw new Error(
|
|
47
|
+
"no method descriptor - set this before accessing payload"
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return deserialisedPayload;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
get methodName() {
|
|
55
|
+
return this.#methodName;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
set methodDescriptor(methodDescriptor) {
|
|
59
|
+
this.#methodDescriptor = methodDescriptor;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#decodeToken(token) {
|
|
63
|
+
// We don't verify the token at this point, as we don't know the public key.
|
|
64
|
+
// The rpc implementation should do the verification using the Volt API (e.g. CanAccessResource).
|
|
65
|
+
this.#token = jwt.decode(token);
|
|
66
|
+
|
|
67
|
+
if (this.#token.sk) {
|
|
68
|
+
// Decrypt the shared key and iv using the private key.
|
|
69
|
+
this.#encryptKey = VoltCredential.rsaDecrypt(
|
|
70
|
+
this.#voltClient.credential.cache.key,
|
|
71
|
+
this.#token.sk
|
|
72
|
+
);
|
|
73
|
+
this.#encryptIV = VoltCredential.rsaDecrypt(
|
|
74
|
+
this.#voltClient.credential.cache.key,
|
|
75
|
+
this.#token.iv
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return Promise.resolve(this.#token);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
initialise(invoke_request) {
|
|
83
|
+
this.#invokeId = invoke_request.invoke_id;
|
|
84
|
+
|
|
85
|
+
return this.#decodeToken(invoke_request.token)
|
|
86
|
+
.then(() => {
|
|
87
|
+
// Parse the initial payload.
|
|
88
|
+
return this.parsePayload(invoke_request);
|
|
89
|
+
})
|
|
90
|
+
.catch((err) => {
|
|
91
|
+
log("failure initialising invocation: %s", err.message);
|
|
92
|
+
return Promise.reject(err);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
parsePayload(invoke_request) {
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
if (invoke_request.payload) {
|
|
99
|
+
let decryptedPayload;
|
|
100
|
+
|
|
101
|
+
if (this.#encryptKey) {
|
|
102
|
+
// Decrypt the actual payload using the shared key and iv.
|
|
103
|
+
decryptedPayload = VoltCredential.aesDecrypt(
|
|
104
|
+
invoke_request.payload,
|
|
105
|
+
this.#encryptKey,
|
|
106
|
+
this.#encryptIV
|
|
107
|
+
);
|
|
108
|
+
} else {
|
|
109
|
+
// No encryption => just use the payload.
|
|
110
|
+
decryptedPayload = invoke_request.payload;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const tunnelMethod = this.#voltClient.getVoltAPIClient().Tunnel;
|
|
114
|
+
const wrappedPayload =
|
|
115
|
+
tunnelMethod.responseDeserialize(decryptedPayload);
|
|
116
|
+
|
|
117
|
+
if (wrappedPayload.method_invoke) {
|
|
118
|
+
// This is the initial request payload.
|
|
119
|
+
this.#methodName = wrappedPayload.method_invoke.method_name;
|
|
120
|
+
this.#payload = wrappedPayload.method_invoke.request;
|
|
121
|
+
this.emit("payload", this);
|
|
122
|
+
} else if (wrappedPayload.method_payload) {
|
|
123
|
+
// This is a subsequent payload, e.g. for streaming rpcs.
|
|
124
|
+
this.#payload = wrappedPayload.method_payload.payload;
|
|
125
|
+
this.emit("payload", this);
|
|
126
|
+
} else if (wrappedPayload.method_end) {
|
|
127
|
+
this.#payload = wrappedPayload.method_end;
|
|
128
|
+
if (wrappedPayload.method_end.error) {
|
|
129
|
+
// The client has sent an error.
|
|
130
|
+
this.emit("error", this);
|
|
131
|
+
} else {
|
|
132
|
+
this.emit("end", this);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
resolve();
|
|
137
|
+
} else if (invoke_request.json_payload) {
|
|
138
|
+
let decryptedPayload;
|
|
139
|
+
|
|
140
|
+
this.#isJSON = true;
|
|
141
|
+
|
|
142
|
+
if (this.#encryptKey) {
|
|
143
|
+
// Decrypt the actual payload using the shared key and iv.
|
|
144
|
+
decryptedPayload = VoltCredential.aesDecrypt(
|
|
145
|
+
Buffer.from(invoke_request.json_payload, "base64"),
|
|
146
|
+
this.#encryptKey,
|
|
147
|
+
this.#encryptIV
|
|
148
|
+
);
|
|
149
|
+
} else {
|
|
150
|
+
// No encryption => just use the payload.
|
|
151
|
+
decryptedPayload = invoke_request.json_payload;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const wrappedPayload = JSON.parse(decryptedPayload.toString());
|
|
156
|
+
|
|
157
|
+
if (wrappedPayload.method_invoke) {
|
|
158
|
+
this.#methodName = wrappedPayload.method_invoke.method_name;
|
|
159
|
+
this.#payload = JSON.parse(
|
|
160
|
+
wrappedPayload.method_invoke.json_request
|
|
161
|
+
);
|
|
162
|
+
this.emit("payload", this);
|
|
163
|
+
} else if (wrappedPayload.method_payload) {
|
|
164
|
+
this.#payload = JSON.parse(
|
|
165
|
+
wrappedPayload.method_payload.json_payload
|
|
166
|
+
);
|
|
167
|
+
this.emit("payload", this);
|
|
168
|
+
} else if (wrappedPayload.method_end) {
|
|
169
|
+
// We don't need to do anything here, since we notify the client when we receive the client_end.
|
|
170
|
+
log("method_end received");
|
|
171
|
+
}
|
|
172
|
+
} catch (err) {
|
|
173
|
+
log("JSON.parse failure parsing json_payload: %s", err.message);
|
|
174
|
+
reject(err);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
resolve();
|
|
178
|
+
} else if (invoke_request.client_end) {
|
|
179
|
+
this.emit("end", this);
|
|
180
|
+
} else {
|
|
181
|
+
// Do we need to support json_payload here?
|
|
182
|
+
reject(new Error("No payload in invoke request"));
|
|
183
|
+
}
|
|
184
|
+
}).catch((err) => {
|
|
185
|
+
log("failure parsing payload: %s", err.message);
|
|
186
|
+
return Promise.reject(err);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
sendResponse(response) {
|
|
191
|
+
let responsePayload;
|
|
192
|
+
if (this.#isJSON) {
|
|
193
|
+
responsePayload = JSON.stringify({
|
|
194
|
+
method_payload: { json_payload: JSON.stringify(response) }
|
|
195
|
+
});
|
|
196
|
+
} else {
|
|
197
|
+
// We need to serialise the response, and then wrap it in a RemoteRequest.
|
|
198
|
+
const serialisedResponse =
|
|
199
|
+
this.#methodDescriptor.responseSerialize(response);
|
|
200
|
+
const tunnelMethod = this.#voltClient.getVoltAPIClient().Tunnel;
|
|
201
|
+
responsePayload = tunnelMethod.requestSerialize({
|
|
202
|
+
method_payload: { payload: serialisedResponse }
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (this.#token.sk) {
|
|
207
|
+
// Encrypt the response using the shared key and iv.
|
|
208
|
+
responsePayload = VoltCredential.aesEncrypt(
|
|
209
|
+
responsePayload,
|
|
210
|
+
this.#encryptKey,
|
|
211
|
+
this.#encryptIV
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const invokeResponse = {
|
|
216
|
+
invoke_id: this.#invokeId
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
if (this.#isJSON) {
|
|
220
|
+
invokeResponse.json_payload =
|
|
221
|
+
Buffer.from(responsePayload).toString("base64");
|
|
222
|
+
} else {
|
|
223
|
+
invokeResponse.payload = responsePayload;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
this.#voltConnection.send({
|
|
227
|
+
invoke_response: invokeResponse
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
sendEnd(errorMessage) {
|
|
232
|
+
const tunnelMethod = this.#voltClient.getVoltAPIClient().Tunnel;
|
|
233
|
+
|
|
234
|
+
let endPayload;
|
|
235
|
+
if (errorMessage) {
|
|
236
|
+
endPayload = {
|
|
237
|
+
method_end: { error: errorMessage, ended: true }
|
|
238
|
+
};
|
|
239
|
+
} else {
|
|
240
|
+
endPayload = {
|
|
241
|
+
method_end: { ended: true }
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (this.#isJSON) {
|
|
246
|
+
endPayload = JSON.stringify(endPayload);
|
|
247
|
+
} else {
|
|
248
|
+
endPayload = tunnelMethod.requestSerialize(endPayload);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (this.#token.sk) {
|
|
252
|
+
// Encrypt the response using the shared key and iv.
|
|
253
|
+
endPayload = VoltCredential.aesEncrypt(
|
|
254
|
+
endPayload,
|
|
255
|
+
this.#encryptKey,
|
|
256
|
+
this.#encryptIV
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const invokeResponse = {
|
|
261
|
+
invoke_id: this.#invokeId,
|
|
262
|
+
server_end: true
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
if (this.#isJSON) {
|
|
266
|
+
invokeResponse.json_payload = Buffer.from(endPayload).toString("base64");
|
|
267
|
+
} else {
|
|
268
|
+
invokeResponse.payload = endPayload;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
this.#voltConnection.send({
|
|
272
|
+
invoke_response: invokeResponse
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export default RpcInvocation;
|
package/src/utils.js
CHANGED
|
@@ -11,7 +11,7 @@ export const createSecureContextOptions = (cryptoOptions) => {
|
|
|
11
11
|
const tlsOptions = {
|
|
12
12
|
cert: Buffer.from(cryptoOptions.cert),
|
|
13
13
|
ca: Buffer.from(cryptoOptions.ca),
|
|
14
|
-
key: Buffer.from(cryptoOptions.key)
|
|
14
|
+
key: Buffer.from(cryptoOptions.key)
|
|
15
15
|
};
|
|
16
16
|
return tlsOptions;
|
|
17
17
|
};
|
|
@@ -27,7 +27,7 @@ export const getServiceAddress = (mdnsService) => {
|
|
|
27
27
|
const ipv4 = _.find(mdnsService.addresses, (addy) => ip.isV4Format(addy));
|
|
28
28
|
const serviceDetails = {
|
|
29
29
|
host: ipv4,
|
|
30
|
-
port: mdnsService.port
|
|
30
|
+
port: mdnsService.port
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
address = `${serviceDetails.host}:${serviceDetails.port}`;
|
|
@@ -103,3 +103,44 @@ export const getDIDResolutionURL = (didIn) => {
|
|
|
103
103
|
|
|
104
104
|
return `https://${hostName}/api/identity/${didGUID}`;
|
|
105
105
|
};
|
|
106
|
+
|
|
107
|
+
const forgePrivateKeyToPem = (decryptedKey) => {
|
|
108
|
+
// Convert a Forge private key to an ASN.1 RSAPrivateKey
|
|
109
|
+
const asnPrivateKey = pki.privateKeyToAsn1(decryptedKey);
|
|
110
|
+
|
|
111
|
+
// Wrap an RSAPrivateKey ASN.1 object in a PKCS#8 ASN.1 PrivateKeyInfo
|
|
112
|
+
const privateKeyInfo = pki.wrapRsaPrivateKey(asnPrivateKey);
|
|
113
|
+
|
|
114
|
+
// Convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM
|
|
115
|
+
return pki.privateKeyInfoToPem(privateKeyInfo);
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const createRSAKey = (passphrase = "") => {
|
|
119
|
+
return new Promise((resolve, reject) => {
|
|
120
|
+
pki.rsa.generateKeyPair({ bits: 2048, workers: -1 }, (err, keypair) => {
|
|
121
|
+
if (err) {
|
|
122
|
+
reject(err);
|
|
123
|
+
} else {
|
|
124
|
+
let pem;
|
|
125
|
+
if (passphrase) {
|
|
126
|
+
pem = pki.encryptRsaPrivateKey(keypair.privateKey, passphrase);
|
|
127
|
+
} else {
|
|
128
|
+
pem = forgePrivateKeyToPem(keypair.privateKey);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
resolve({ keypair, pem });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export const sha256Base64 = (msg) => {
|
|
138
|
+
const md = forge.md.sha256.create();
|
|
139
|
+
md.update(msg);
|
|
140
|
+
return forge.util.encode64(md.digest().bytes());
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
export const decryptPrivateKey = (pem, passphrase) => {
|
|
144
|
+
const decryptedKey = pki.decryptRsaPrivateKey(pem, passphrase);
|
|
145
|
+
return forgePrivateKeyToPem(decryptedKey);
|
|
146
|
+
};
|
|
@@ -5,11 +5,17 @@ import bent from "bent";
|
|
|
5
5
|
import forge from "node-forge";
|
|
6
6
|
import lodash from "lodash";
|
|
7
7
|
import VoltConnection from "./volt-connection.js";
|
|
8
|
-
import {
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import {
|
|
10
|
+
createServiceProtobufFiles,
|
|
11
|
+
getServiceDescriptors,
|
|
12
|
+
getServiceDescriptorsFromPath
|
|
13
|
+
} from "./proto-utils.js";
|
|
9
14
|
import { createClient as createGrpcClient } from "./grpc-utils.js";
|
|
10
15
|
import * as voltUtils from "./utils.js";
|
|
11
16
|
import { constants } from "./constants.js";
|
|
12
17
|
import GRPCCall from "./grpc-call.js";
|
|
18
|
+
import RpcInvocation from "./rpc-invocation.js";
|
|
13
19
|
|
|
14
20
|
const { pki } = forge;
|
|
15
21
|
const { filter, pick } = lodash;
|
|
@@ -22,51 +28,53 @@ const voltServices = [
|
|
|
22
28
|
constants.serviceType.sqliteServerAPI,
|
|
23
29
|
constants.serviceType.ssiAPI,
|
|
24
30
|
constants.serviceType.relayAPI,
|
|
25
|
-
constants.serviceType.wireAPI
|
|
31
|
+
constants.serviceType.wireAPI
|
|
26
32
|
];
|
|
27
33
|
|
|
28
34
|
function issueBind(bindRequest, ttl) {
|
|
29
35
|
// eslint-disable-next-line no-use-before-define
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
log("
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
36
|
+
return unaryCallInternal
|
|
37
|
+
.call(this, "Bind", bindRequest)
|
|
38
|
+
.then((bindResponse) => {
|
|
39
|
+
log("got binding request response %j", bindResponse);
|
|
40
|
+
if (bindResponse.status?.code) {
|
|
41
|
+
log("error in bind response: %s", bindResponse.status.message);
|
|
42
|
+
return Promise.reject(new Error(bindResponse.status.message));
|
|
43
|
+
} else {
|
|
44
|
+
switch (bindResponse.decision) {
|
|
45
|
+
case "POLICY_DECISION_PERMIT": {
|
|
46
|
+
//
|
|
47
|
+
// The request status is permit => cache the bind response info.
|
|
48
|
+
//
|
|
49
|
+
|
|
50
|
+
// This is the certificate assigned to us by the volt.
|
|
51
|
+
this._credential.cache.cert = bindResponse.cert;
|
|
52
|
+
|
|
53
|
+
// This is the signing CA used by the volt.
|
|
54
|
+
this._credential.cache.ca = bindResponse.chain;
|
|
55
|
+
|
|
56
|
+
// Identity resource id is assigned by the volt.
|
|
57
|
+
this._credential.cache.client_id = bindResponse.identity_id;
|
|
58
|
+
this._credential.saveCache();
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "POLICY_DECISION_DENY": {
|
|
62
|
+
log(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
case "POLICY_DECISION_PROMPT":
|
|
66
|
+
case "POLICY_DECISION_PENDING": {
|
|
67
|
+
log("access pending approval - waiting...");
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
default:
|
|
71
|
+
log("ignoring unknown bind descision %s", bindResponse.status);
|
|
72
|
+
break;
|
|
61
73
|
}
|
|
62
|
-
default:
|
|
63
|
-
log("ignoring unknown bind descision %s", bindResponse.status);
|
|
64
|
-
break;
|
|
65
74
|
}
|
|
66
|
-
}
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
return bindResponse.decision;
|
|
77
|
+
});
|
|
70
78
|
}
|
|
71
79
|
|
|
72
80
|
function findDIDDocumentService(document, serviceType) {
|
|
@@ -90,13 +98,13 @@ export async function bindInternal() {
|
|
|
90
98
|
log("attempting to retrieve Relay information from %s", relayURL);
|
|
91
99
|
this._voltConfig.relay = await fetchVoltConfig.call(
|
|
92
100
|
this,
|
|
93
|
-
`${relayURL}/discovery
|
|
101
|
+
`${relayURL}/discovery`
|
|
94
102
|
);
|
|
95
103
|
if (this._voltConfig.relay.ca_pem) {
|
|
96
104
|
log(
|
|
97
105
|
"Auto-fetched Relay CA %s, remote address %s",
|
|
98
106
|
this._voltConfig.relay.ca_pem,
|
|
99
|
-
this._voltConfig.relay.address
|
|
107
|
+
this._voltConfig.relay.address
|
|
100
108
|
);
|
|
101
109
|
} else {
|
|
102
110
|
throw new Error("Unable to fetch Relay CA - cannot securely connect.");
|
|
@@ -109,7 +117,7 @@ export async function bindInternal() {
|
|
|
109
117
|
|
|
110
118
|
if (!this._credential.cache.ca) {
|
|
111
119
|
throw new Error(
|
|
112
|
-
"No certificate authority found in configuration for target Volt - check configuration"
|
|
120
|
+
"No certificate authority found in configuration for target Volt - check configuration"
|
|
113
121
|
);
|
|
114
122
|
}
|
|
115
123
|
|
|
@@ -129,30 +137,30 @@ export async function bindInternal() {
|
|
|
129
137
|
binding_name: this._config.client_name,
|
|
130
138
|
public_key: publicKeyPem,
|
|
131
139
|
host: this._credential.cache.bindIp,
|
|
132
|
-
x509_credential: []
|
|
140
|
+
x509_credential: []
|
|
133
141
|
};
|
|
134
142
|
|
|
135
143
|
if (this._credential.cache.cert) {
|
|
136
144
|
bindRequest.x509_credential = [
|
|
137
|
-
this._credential.cache.cert + this._credential.cache.ca
|
|
145
|
+
this._credential.cache.cert + this._credential.cache.ca
|
|
138
146
|
];
|
|
139
147
|
}
|
|
140
148
|
|
|
141
149
|
if (this._voltConfig.challenge_code) {
|
|
142
150
|
bindRequest.challenge = voltUtils.signBase64(
|
|
143
151
|
keyPair.privateKey,
|
|
144
|
-
this._voltConfig.challenge_code
|
|
152
|
+
this._voltConfig.challenge_code
|
|
145
153
|
);
|
|
146
154
|
} else {
|
|
147
155
|
log(
|
|
148
|
-
"****no Volt challenge code available**** => not sending challenge signature"
|
|
156
|
+
"****no Volt challenge code available**** => not sending challenge signature"
|
|
149
157
|
);
|
|
150
158
|
}
|
|
151
159
|
|
|
152
160
|
// For remote connections, add our cloud-issued certificate as an additional credential.
|
|
153
161
|
if (this._voltConfig?.relay?.ca_pem && this._credential.cache.cloud_cert) {
|
|
154
162
|
bindRequest.x509_credential = bindRequest.x509_credential.concat(
|
|
155
|
-
this._credential.cache.cloud_cert + this._voltConfig.relay.ca_pem
|
|
163
|
+
this._credential.cache.cloud_cert + this._voltConfig.relay.ca_pem
|
|
156
164
|
);
|
|
157
165
|
}
|
|
158
166
|
|
|
@@ -172,7 +180,7 @@ export async function bindInternal() {
|
|
|
172
180
|
decision = await issueBind.call(
|
|
173
181
|
this,
|
|
174
182
|
bindRequest,
|
|
175
|
-
this._voltConfig.bindRequestTTL
|
|
183
|
+
this._voltConfig.bindRequestTTL
|
|
176
184
|
);
|
|
177
185
|
log("binding decision: %s", decision);
|
|
178
186
|
} while (
|
|
@@ -233,6 +241,34 @@ export function connectInternal(helloPayload) {
|
|
|
233
241
|
this.emit("evt", evt);
|
|
234
242
|
});
|
|
235
243
|
|
|
244
|
+
this._voltConnection.on("ping", (ping) => {
|
|
245
|
+
this.emit("ping", ping);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
this._voltConnection.on("invoke_request", (invoke_request) => {
|
|
249
|
+
const invokeId = invoke_request.invoke_id;
|
|
250
|
+
if (this._activeRPC[invokeId]) {
|
|
251
|
+
this._activeRPC[invokeId].parsePayload(invoke_request);
|
|
252
|
+
} else {
|
|
253
|
+
const rpcInvocation = new RpcInvocation(this, this._voltConnection);
|
|
254
|
+
|
|
255
|
+
rpcInvocation.on("end", () => {
|
|
256
|
+
log("removing active RPC [%s]", invokeId);
|
|
257
|
+
this._activeRPC[invokeId] = undefined;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
rpcInvocation
|
|
261
|
+
.initialise(invoke_request)
|
|
262
|
+
.then(() => {
|
|
263
|
+
this._activeRPC[invokeId] = rpcInvocation;
|
|
264
|
+
this.emit("invoke_request", rpcInvocation);
|
|
265
|
+
})
|
|
266
|
+
.catch((err) => {
|
|
267
|
+
log("invoke_request - error [%s]", err.message);
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
236
272
|
return this._voltConnection.connect(helloPayload);
|
|
237
273
|
} catch (err) {
|
|
238
274
|
log("connect - error [%s]", err.message);
|
|
@@ -259,18 +295,69 @@ export function getVoltAPIClientInternal() {
|
|
|
259
295
|
this._credential,
|
|
260
296
|
this.isRemote && !this.isVoltRelay,
|
|
261
297
|
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
262
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
298
|
+
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
263
299
|
);
|
|
264
300
|
}
|
|
265
301
|
|
|
266
302
|
return this._cachedClient;
|
|
267
303
|
}
|
|
268
304
|
|
|
269
|
-
export function
|
|
270
|
-
|
|
271
|
-
const
|
|
305
|
+
export function getAPIClientInternal(service) {
|
|
306
|
+
if (!this._cachedService[service.id]) {
|
|
307
|
+
const isRelayedService =
|
|
308
|
+
service.service_description.host_type === "SERVICE_HOST_TYPE_RELAYED";
|
|
309
|
+
|
|
310
|
+
const serviceAddress = this.isRemote
|
|
311
|
+
? this._voltConfig.relay.address
|
|
312
|
+
: service.service_description.host_address;
|
|
313
|
+
|
|
314
|
+
createServiceProtobufFiles(service, "./service-proto");
|
|
315
|
+
|
|
316
|
+
log("creating API service client on %s", serviceAddress);
|
|
317
|
+
let serviceDescriptors = {};
|
|
318
|
+
const fullProtoPath = join(process.cwd(), "./service-proto");
|
|
319
|
+
for (let api of service.service_description.service_api) {
|
|
320
|
+
const packageDescriptors = getServiceDescriptorsFromPath(
|
|
321
|
+
this._grpc,
|
|
322
|
+
fullProtoPath,
|
|
323
|
+
api
|
|
324
|
+
);
|
|
325
|
+
serviceDescriptors = { ...serviceDescriptors, ...packageDescriptors };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
this._cachedService[service.id] = createGrpcClient(
|
|
329
|
+
this._grpc,
|
|
330
|
+
serviceDescriptors,
|
|
331
|
+
serviceAddress,
|
|
332
|
+
this._credential,
|
|
333
|
+
this.isRemote && !this.isVoltRelay,
|
|
334
|
+
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
335
|
+
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
336
|
+
);
|
|
337
|
+
}
|
|
272
338
|
|
|
273
|
-
|
|
339
|
+
return this._cachedService[service.id];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function getServiceClient(service) {
|
|
343
|
+
let grpcClient;
|
|
344
|
+
if (
|
|
345
|
+
service &&
|
|
346
|
+
service?.service_description.host_type !== "SERVICE_HOST_TYPE_BUILTIN"
|
|
347
|
+
) {
|
|
348
|
+
grpcClient = getAPIClientInternal.call(this, service);
|
|
349
|
+
} else {
|
|
350
|
+
grpcClient = getVoltAPIClientInternal.call(this);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return grpcClient;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function unaryCallInternal(method, request, service) {
|
|
357
|
+
const grpcClient = getServiceClient.call(this, service);
|
|
358
|
+
|
|
359
|
+
return new Promise((resolve, reject) => {
|
|
360
|
+
const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY", service);
|
|
274
361
|
|
|
275
362
|
let response = null;
|
|
276
363
|
|
|
@@ -298,6 +385,16 @@ export function unaryCall(method, request) {
|
|
|
298
385
|
});
|
|
299
386
|
}
|
|
300
387
|
|
|
388
|
+
export function streamingCallInternal(methodType, method, request, service) {
|
|
389
|
+
const grpcClient = getServiceClient.call(this, service);
|
|
390
|
+
|
|
391
|
+
const call = new GRPCCall(this, method, methodType, service);
|
|
392
|
+
|
|
393
|
+
call.start(grpcClient, request);
|
|
394
|
+
|
|
395
|
+
return call;
|
|
396
|
+
}
|
|
397
|
+
|
|
301
398
|
export async function fetchVoltConfig(discovery_url) {
|
|
302
399
|
try {
|
|
303
400
|
const getJSON = bent("json");
|
|
@@ -317,13 +414,13 @@ export async function fetchVoltConfig(discovery_url) {
|
|
|
317
414
|
"ca_pem",
|
|
318
415
|
"challenge_code",
|
|
319
416
|
"cloud",
|
|
320
|
-
"relay"
|
|
417
|
+
"relay"
|
|
321
418
|
);
|
|
322
419
|
|
|
323
420
|
return voltConfig;
|
|
324
421
|
} catch (err) {
|
|
325
422
|
throw new Error(
|
|
326
|
-
`Failure loading config from ${discovery_url}: ${err.message}
|
|
423
|
+
`Failure loading config from ${discovery_url}: ${err.message}`
|
|
327
424
|
);
|
|
328
425
|
}
|
|
329
426
|
}
|
|
@@ -339,13 +436,13 @@ export async function fetchVoltConfigFromDID(volt_did) {
|
|
|
339
436
|
const didDocument = await getJSON(didResolution);
|
|
340
437
|
const voltConfigServices = findDIDDocumentService(
|
|
341
438
|
didDocument,
|
|
342
|
-
constants.didServiceType.voltConfig
|
|
439
|
+
constants.didServiceType.voltConfig
|
|
343
440
|
);
|
|
344
441
|
if (voltConfigServices.length !== 1) {
|
|
345
442
|
log(
|
|
346
443
|
"Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
|
|
347
444
|
volt_did,
|
|
348
|
-
voltConfigServices.length
|
|
445
|
+
voltConfigServices.length
|
|
349
446
|
);
|
|
350
447
|
}
|
|
351
448
|
|
|
@@ -353,7 +450,7 @@ export async function fetchVoltConfigFromDID(volt_did) {
|
|
|
353
450
|
return await fetchVoltConfig.call(this, serviceEndpoint);
|
|
354
451
|
} catch (err) {
|
|
355
452
|
throw new Error(
|
|
356
|
-
`Failure fetching Volt config from DID document ${volt_did}: ${err.message}
|
|
453
|
+
`Failure fetching Volt config from DID document ${volt_did}: ${err.message}`
|
|
357
454
|
);
|
|
358
455
|
}
|
|
359
456
|
}
|