@tdxvolt/volt-client-grpc 0.16.0-beta.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 CHANGED
@@ -589,8 +589,16 @@ class VoltCredential {
589
589
  return this._cryptoCache;
590
590
  }
591
591
 
592
- get clientId() {
593
- return this._cryptoCache.client_id;
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;
594
602
  }
595
603
 
596
604
  get voltPublicKey() {
@@ -678,7 +686,7 @@ class VoltCredential {
678
686
  aud: audience,
679
687
  iat: Math.floor(Date.now() / 1000) - ttl,
680
688
  exp: Math.floor(Date.now() / 1000) + ttl,
681
- sub: this._cryptoCache.client_id
689
+ sub: this._cryptoCache.session_id
682
690
  };
683
691
 
684
692
  let sharedKey;
@@ -1265,10 +1273,9 @@ function _cleanUp(immediateReconnect) {
1265
1273
  this._pingTimeoutTimer = 0;
1266
1274
  }
1267
1275
 
1268
- if (
1269
- !this._dying &&
1270
- (immediateReconnect || (this._autoRetry && !this._reconnectTimer))
1271
- ) {
1276
+ if (this._dying) {
1277
+ clearTimeout(this._reconnectTimer);
1278
+ } else if (immediateReconnect || (this._autoRetry && !this._reconnectTimer)) {
1272
1279
  this._reconnectTimer = setTimeout(
1273
1280
  () => {
1274
1281
  this._reconnectTimer = 0;
@@ -1735,7 +1742,8 @@ function issueAuthenticate(authenticateRequest, ttl) {
1735
1742
  this._credential.cache.ca = authenticateResponse.chain;
1736
1743
 
1737
1744
  // Identity resource id is assigned by the volt.
1738
- this._credential.cache.client_id = authenticateResponse.session_id;
1745
+ this._credential.cache.session_id = authenticateResponse.session_id;
1746
+
1739
1747
  this._credential.saveCache();
1740
1748
  break;
1741
1749
  }
@@ -1770,8 +1778,7 @@ function findDIDDocumentService(document, serviceType) {
1770
1778
  return matches;
1771
1779
  }
1772
1780
 
1773
-
1774
- async function authenticateInternal() {
1781
+ async function authenticateInternal(useDID = true) {
1775
1782
  try {
1776
1783
  // Check if we need to resolve a Relay volt.
1777
1784
  if (
@@ -1808,7 +1815,9 @@ async function authenticateInternal() {
1808
1815
  }
1809
1816
 
1810
1817
  const keyPair = this._credential.getKey();
1811
- const publicKeyPem = pki.publicKeyToPem(keyPair.publicKey);
1818
+ const publicKeyPem = removeCarriageReturn(
1819
+ pki.publicKeyToPem(keyPair.publicKey)
1820
+ );
1812
1821
 
1813
1822
  if (!this._credential.cache.bindIp) {
1814
1823
  // Default the IP address if none is specified. This is used to set the SAN in the
@@ -1821,8 +1830,7 @@ async function authenticateInternal() {
1821
1830
  // Form the request message.
1822
1831
  const authenticateRequest = {
1823
1832
  binding_name: this._config.client_name,
1824
- public_key: publicKeyPem,
1825
- host: this._credential.cache.bindIp,
1833
+ host: this._credential.cache.bindIp
1826
1834
  };
1827
1835
 
1828
1836
  if (this._voltConfig.challenge_code) {
@@ -1836,6 +1844,70 @@ async function authenticateInternal() {
1836
1844
  );
1837
1845
  }
1838
1846
 
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
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;
1909
+ }
1910
+
1839
1911
  log$1("authenticate TTL is ", this._config.bindRequestTTL);
1840
1912
 
1841
1913
  // This will block while the request is pending.
@@ -1906,6 +1978,15 @@ function connectInternal(helloPayload) {
1906
1978
  onDisconnected();
1907
1979
  this.emit("connected", false);
1908
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);
1909
1990
  });
1910
1991
 
1911
1992
  this._voltConnection.on("evt", (evt) => {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.16.0-beta.1",
6
+ "version": "0.16.0-beta.10",
7
7
  "description": "tdx Volt library for nodejs clients",
8
8
  "type": "module",
9
9
  "exports": {
@@ -55,7 +55,8 @@ function issueAuthenticate(authenticateRequest, ttl) {
55
55
  this._credential.cache.ca = authenticateResponse.chain;
56
56
 
57
57
  // Identity resource id is assigned by the volt.
58
- this._credential.cache.client_id = authenticateResponse.session_id;
58
+ this._credential.cache.session_id = authenticateResponse.session_id;
59
+
59
60
  this._credential.saveCache();
60
61
  break;
61
62
  }
@@ -81,52 +82,6 @@ function issueAuthenticate(authenticateRequest, ttl) {
81
82
  });
82
83
  }
83
84
 
84
- function issueBind(bindRequest, ttl) {
85
- // eslint-disable-next-line no-use-before-define
86
- return unaryCallInternal
87
- .call(this, "Bind", bindRequest)
88
- .then((bindResponse) => {
89
- log("got binding request response %j", bindResponse);
90
- if (bindResponse.status?.code) {
91
- log("error in bind response: %s", bindResponse.status.message);
92
- return Promise.reject(new Error(bindResponse.status.message));
93
- } else {
94
- switch (bindResponse.decision) {
95
- case "POLICY_DECISION_PERMIT": {
96
- //
97
- // The request status is permit => cache the bind response info.
98
- //
99
-
100
- // This is the certificate assigned to us by the volt.
101
- this._credential.cache.cert = bindResponse.cert;
102
-
103
- // This is the signing CA used by the volt.
104
- this._credential.cache.ca = bindResponse.chain;
105
-
106
- // Identity resource id is assigned by the volt.
107
- this._credential.cache.client_id = bindResponse.identity_id;
108
- this._credential.saveCache();
109
- break;
110
- }
111
- case "POLICY_DECISION_DENY": {
112
- log(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
113
- break;
114
- }
115
- case "POLICY_DECISION_PROMPT":
116
- case "POLICY_DECISION_PENDING": {
117
- log("access pending approval - waiting...");
118
- break;
119
- }
120
- default:
121
- log("ignoring unknown bind descision %s", bindResponse.status);
122
- break;
123
- }
124
- }
125
-
126
- return bindResponse.decision;
127
- });
128
- }
129
-
130
85
  function findDIDDocumentService(document, serviceType) {
131
86
  if (!document.service) {
132
87
  return [];
@@ -136,8 +91,7 @@ function findDIDDocumentService(document, serviceType) {
136
91
  return matches;
137
92
  }
138
93
 
139
-
140
- export async function authenticateInternal() {
94
+ export async function authenticateInternal(useDID = true) {
141
95
  try {
142
96
  // Check if we need to resolve a Relay volt.
143
97
  if (
@@ -174,7 +128,9 @@ export async function authenticateInternal() {
174
128
  }
175
129
 
176
130
  const keyPair = this._credential.getKey();
177
- const publicKeyPem = pki.publicKeyToPem(keyPair.publicKey);
131
+ const publicKeyPem = voltUtils.removeCarriageReturn(
132
+ pki.publicKeyToPem(keyPair.publicKey)
133
+ );
178
134
 
179
135
  if (!this._credential.cache.bindIp) {
180
136
  // Default the IP address if none is specified. This is used to set the SAN in the
@@ -187,8 +143,7 @@ export async function authenticateInternal() {
187
143
  // Form the request message.
188
144
  const authenticateRequest = {
189
145
  binding_name: this._config.client_name,
190
- public_key: publicKeyPem,
191
- host: this._credential.cache.bindIp,
146
+ host: this._credential.cache.bindIp
192
147
  };
193
148
 
194
149
  if (this._voltConfig.challenge_code) {
@@ -202,161 +157,71 @@ export async function authenticateInternal() {
202
157
  );
203
158
  }
204
159
 
205
- log("authenticate TTL is ", this._config.bindRequestTTL);
206
-
207
- // This will block while the request is pending.
208
- const pollInterval = 10000;
209
- let decision = "";
210
- do {
211
- if (decision) {
212
- // Wait a while before retrying.
213
- // eslint-disable-next-line no-await-in-loop
214
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
215
- }
216
- // eslint-disable-next-line no-await-in-loop
217
- decision = await issueAuthenticate.call(
218
- this,
219
- authenticateRequest,
220
- this._voltConfig.bindRequestTTL
221
- );
222
- log("authenticate decision: %s", decision);
223
- } while (
224
- decision === "POLICY_DECISION_PROMPT" ||
225
- decision === "POLICY_DECISION_PENDING"
226
- );
227
-
228
- return decision === "POLICY_DECISION_PERMIT";
229
- } catch (err) {
230
- log("failure binding to Volt [%s]", err.message);
231
- throw err;
232
- }
233
- }
234
-
235
- export async function bindInternal() {
236
- try {
237
- // Check if we need to resolve a Relay volt.
238
- if (
239
- typeof this._voltConfig?.relay === "string" ||
240
- this._voltConfig?.relay?.http_address
241
- ) {
242
- const relayURL =
243
- this._voltConfig.relay?.http_address || this._voltConfig.relay;
244
- log("attempting to retrieve Relay information from %s", relayURL);
245
- this._voltConfig.relay = await fetchVoltConfig.call(
246
- this,
247
- `${relayURL}/discovery`
248
- );
249
- if (this._voltConfig.relay.ca_pem) {
250
- log(
251
- "Auto-fetched Relay CA %s, remote address %s",
252
- this._voltConfig.relay.ca_pem,
253
- this._voltConfig.relay.address
254
- );
255
- } else {
256
- throw new Error("Unable to fetch Relay CA - cannot securely connect.");
257
- }
258
- }
160
+ if (!useDID) {
161
+ authenticateRequest.public_key = publicKeyPem;
162
+ } else if (!this._credential.cache.identity_id) {
163
+ // If we don't have an identity id, then we need to create a DID.
164
+ this._credential.cache.identity_id = `did:volt:${generateId()}`;
165
+
166
+ const did = this._credential.cache.identity_id;
167
+
168
+ // Order of properties in the DID document is important,
169
+ // they must be in alphabetical order throughout the document.
170
+ // This is because the DID document is signed and the signature
171
+ // is over the entire document.
172
+ const didDocument = {
173
+ "@context": "https://www.w3.org/ns/did/v1",
174
+ authentication: [`${did}#key-1`],
175
+ description: this._config.client_name,
176
+ id: did,
177
+ verificationMethod: [
178
+ {
179
+ controller: did,
180
+ id: `${did}#key-1`,
181
+ publicKeyPem: publicKeyPem,
182
+ type: "RsaSignature2018"
183
+ }
184
+ ]
185
+ };
259
186
 
260
- if (!this._credential.cache.ca) {
261
- this._credential.cache.ca = this._voltConfig.ca_pem;
262
- }
187
+ const signDocument = JSON.stringify(didDocument, null, 2);
188
+ log("signing did document");
189
+ log(signDocument);
263
190
 
264
- if (!this._credential.cache.ca) {
265
- throw new Error(
266
- "No certificate authority found in configuration for target Volt - check configuration"
191
+ // Sign the DID document.
192
+ const documentDigest = voltUtils.signBase64(
193
+ keyPair.privateKey,
194
+ signDocument
267
195
  );
268
- }
269
196
 
270
- const keyPair = this._credential.getKey();
271
- const publicKeyPem = voltUtils.removeCarriageReturn(
272
- pki.publicKeyToPem(keyPair.publicKey)
273
- );
197
+ const proofSignature = voltUtils.jwsSignature(documentDigest);
274
198
 
275
- if (!this._credential.cache.bindIp) {
276
- // Default the IP address if none is specified. This is used to set the SAN in the
277
- // certificate issued by the Volt.
278
- this._credential.cache.bindIp = ip.address();
279
- }
199
+ // Create the DID document proof.
200
+ didDocument.proof = {
201
+ created: new Date().toISOString(),
202
+ jws: proofSignature,
203
+ proofPurpose: "assertionMethod",
204
+ type: "RsaSignature2018",
205
+ verificationMethod: `${did}#key-1`
206
+ };
280
207
 
281
- this._credential.saveCache();
208
+ // Set the DID document with proof in the authenticate request.
209
+ authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
282
210
 
283
- // Form the request message.
284
- const bindRequest = {
285
- binding_name: this._config.client_name,
286
- host: this._credential.cache.bindIp
287
- };
288
-
289
- if (this._voltConfig.challenge_code) {
290
- bindRequest.challenge = voltUtils.signBase64(
211
+ // Now sign the entire DID document, including the proof and set it in the
212
+ // bind request. This is required by the Volt to verify that we have the private key for the DID.
213
+ authenticateRequest.did_document_signature = voltUtils.signBase64(
291
214
  keyPair.privateKey,
292
- this._voltConfig.challenge_code
293
- );
294
- } else {
295
- log(
296
- "****no Volt challenge code available**** => not sending challenge signature"
215
+ authenticateRequest.did_document
297
216
  );
298
- }
299
217
 
300
- if (!this._credential.cache.client_id) {
301
- // If we don't have a client id, then we need to create a DID.
302
- this._credential.cache.client_id = `did:volt:${generateId()}`;
218
+ log("did document is %s", authenticateRequest.did_document);
219
+ log("did signature is %s", authenticateRequest.did_document_signature);
220
+ } else {
221
+ authenticateRequest.did = this._credential.cache.identity_id;
303
222
  }
304
223
 
305
- const did = this._credential.cache.client_id;
306
-
307
- // Order of properties in the DID document is important,
308
- // they must be in alphabetical order throughout the document.
309
- // This is because the DID document is signed and the signature
310
- // is over the entire document.
311
- const didDocument = {
312
- "@context": "https://www.w3.org/ns/did/v1",
313
- authentication: [`${did}#key-1`],
314
- description: this._config.client_name,
315
- id: did,
316
- verificationMethod: [
317
- {
318
- controller: did,
319
- id: `${did}#key-1`,
320
- publicKeyPem: publicKeyPem,
321
- type: "RsaSignature2018"
322
- }
323
- ]
324
- };
325
-
326
- const signDocument = JSON.stringify(didDocument, null, 2);
327
- log("signing did document");
328
- log(signDocument);
329
-
330
- // Sign the DID document.
331
- const documentDigest = voltUtils.signBase64(
332
- keyPair.privateKey,
333
- signDocument
334
- );
335
-
336
- const proofSignature = voltUtils.jwsSignature(documentDigest);
337
-
338
- // Create the DID document proof.
339
- didDocument.proof = {
340
- created: new Date().toISOString(),
341
- jws: proofSignature,
342
- proofPurpose: "assertionMethod",
343
- type: "RsaSignature2018",
344
- verificationMethod: `${did}#key-1`
345
- };
346
-
347
- // Set the DID document with proof in the bind request.
348
- bindRequest.did_document = JSON.stringify(didDocument, null, 2);
349
-
350
- // Now sign the entire DID document, including the proof and set it in the
351
- // bind request. This is required by the Volt to verify that we have the private key for the DID.
352
- bindRequest.did_document_signature = voltUtils.signBase64(
353
- keyPair.privateKey,
354
- bindRequest.did_document
355
- );
356
-
357
- log("did document is %s", bindRequest.did_document);
358
- log("did signature is %s", bindRequest.did_document_signature);
359
- log("bind TTL is ", this._config.bindRequestTTL);
224
+ log("authenticate TTL is ", this._config.bindRequestTTL);
360
225
 
361
226
  // This will block while the request is pending.
362
227
  const pollInterval = 10000;
@@ -368,12 +233,12 @@ export async function bindInternal() {
368
233
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
369
234
  }
370
235
  // eslint-disable-next-line no-await-in-loop
371
- decision = await issueBind.call(
236
+ decision = await issueAuthenticate.call(
372
237
  this,
373
- bindRequest,
238
+ authenticateRequest,
374
239
  this._voltConfig.bindRequestTTL
375
240
  );
376
- log("binding decision: %s", decision);
241
+ log("authenticate decision: %s", decision);
377
242
  } while (
378
243
  decision === "POLICY_DECISION_PROMPT" ||
379
244
  decision === "POLICY_DECISION_PENDING"
@@ -426,6 +291,15 @@ export function connectInternal(helloPayload) {
426
291
  onDisconnected();
427
292
  this.emit("connected", false);
428
293
  }
294
+
295
+ if (err.message.endsWith("session invalid")) {
296
+ log("Volt session invalid - clearing session");
297
+ delete this._credential.cache.session_id;
298
+ delete this._credential.cache.cert;
299
+ this._credential.saveCache();
300
+ }
301
+
302
+ this.emit("connectionError", err);
429
303
  });
430
304
 
431
305
  this._voltConnection.on("evt", (evt) => {
@@ -6,7 +6,6 @@ import { getServiceDescriptors } from "./proto-utils.js";
6
6
  import { createClient as createGrpcClient } from "./grpc-utils.js";
7
7
  import {
8
8
  authenticateInternal,
9
- bindInternal,
10
9
  connectInternal,
11
10
  fetchVoltConfig,
12
11
  fetchVoltConfigFromDID,
@@ -19,10 +19,9 @@ function _cleanUp(immediateReconnect) {
19
19
  this._pingTimeoutTimer = 0;
20
20
  }
21
21
 
22
- if (
23
- !this._dying &&
24
- (immediateReconnect || (this._autoRetry && !this._reconnectTimer))
25
- ) {
22
+ if (this._dying) {
23
+ clearTimeout(this._reconnectTimer);
24
+ } else if (immediateReconnect || (this._autoRetry && !this._reconnectTimer)) {
26
25
  this._reconnectTimer = setTimeout(
27
26
  () => {
28
27
  this._reconnectTimer = 0;
@@ -69,8 +69,16 @@ export class VoltCredential {
69
69
  return this._cryptoCache;
70
70
  }
71
71
 
72
- get clientId() {
73
- return this._cryptoCache.client_id;
72
+ get certificate() {
73
+ return this._cryptoCache.cert;
74
+ }
75
+
76
+ get sessionId() {
77
+ return this._cryptoCache.session_id;
78
+ }
79
+
80
+ get identityId() {
81
+ return this._cryptoCache.identity_id;
74
82
  }
75
83
 
76
84
  get voltPublicKey() {
@@ -159,7 +167,7 @@ export class VoltCredential {
159
167
  aud: audience,
160
168
  iat: Math.floor(Date.now() / 1000) - ttl,
161
169
  exp: Math.floor(Date.now() / 1000) + ttl,
162
- sub: this._cryptoCache.client_id
170
+ sub: this._cryptoCache.session_id
163
171
  };
164
172
 
165
173
  let sharedKey;