@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.
@@ -4,6 +4,7 @@ package tdx.volt_api.volt.v1;
4
4
 
5
5
  import "tdx/volt_api/volt/v1/status.proto";
6
6
  import "tdx/volt_api/volt/v1/remote.proto";
7
+ import "tdx/volt_api/volt/v1/ssi.proto";
7
8
  import "tdx/volt_api/volt/v1/volt.proto";
8
9
 
9
10
  // The top-level volt management service.
@@ -11,9 +12,11 @@ import "tdx/volt_api/volt/v1/volt.proto";
11
12
  // A client certificate can be obtained from the `Bind` method.
12
13
  // All methods include a `tdx.volt_api.volt.v1.Status` in the response. If the status contains a non-OK error code the client should assume the remainder of the message is invalid, unless otherwise indicated in the response documentation.
13
14
  service VoltAPI {
15
+ rpc Authenticate(AuthenticateRequest) returns (AuthenticateResponse);
16
+
14
17
  // Issues a request to bind to the volt.
15
18
  // All clients must successfully bind to the Volt in order to gain any kind of access.
16
- // A client certificate is optional for this RPC, in which case a JWT must be provided in the call metadata.
19
+ // A client certificate is optional for this RPC, if omitted a JWT must be provided in the call metadata.
17
20
  // If the client plans to register one or more services with the Volt, it should use the 'host' field of the request so that the returned certificate has the appropriate SAN extension in place. Note that if the client IP address changes it will be necessary to re-Bind the client and obtain a new certificate.
18
21
  // If the bind decision is 'permit', the bind response contains various details that should be persisted by the client, including the unique identifier assigned by the Volt and a signed client certificate along with the Volt CA certificate.
19
22
  rpc Bind(BindRequest) returns (BindResponse);
@@ -122,9 +125,6 @@ service VoltAPI {
122
125
  // Save a session.
123
126
  rpc SaveSession(SaveSessionRequest) returns (SaveSessionResponse);
124
127
 
125
- // Experimental - please ignore.
126
- rpc Session(SessionRequest) returns (SessionResponse);
127
-
128
128
  // Set Volt access request decision.
129
129
  // This is a privileged call that requires Volt root access.
130
130
  rpc SetAccessRequestDecision(SetAccessRequestDecisionRequest) returns (SetAccessRequestDecisionResponse);
@@ -133,6 +133,8 @@ service VoltAPI {
133
133
  // This is a privileged call that requires Volt root access.
134
134
  rpc SetBindingDecision(SetBindingDecisionRequest) returns (SetBindingDecisionResponse);
135
135
 
136
+ rpc SetPolicy(SetPolicyRequest) returns (SetPolicyResponse);
137
+
136
138
  // Set the status of a Volt service.
137
139
  rpc SetServiceStatus(SetServiceStatusRequest) returns (SetServiceStatusResponse);
138
140
 
@@ -145,31 +147,112 @@ service VoltAPI {
145
147
  }
146
148
 
147
149
  // Describes a request to bind to a volt.
148
- message BindRequest {
150
+ message AuthenticateRequest {
151
+
152
+ oneof client_identifier {
153
+ // An existing DID owned by the client.
154
+ // The JWT presented with the Bind call must be signed by the private key corresponding to this DID.
155
+ string did = 1;
156
+
157
+ // If the client doesn't have an existing DID, a DID document can be provided here.
158
+ // The document must include a proof that the client owns the public key given in `public_key`.
159
+ // The Volt will register the DID on behalf of the client.
160
+ // The JWT presented with the Bind call must be signed by the private key corresponding to this document.
161
+ string did_document = 2;
162
+
163
+ // The client public key - must be in PEM format. If the client DID is lost or unknown for some reason, providing the public key here will allow the Volt to match it with the previously registered DID.
164
+ // Note this is only valid when a DID has previously been registered using this public key.
165
+ string public_key = 3;
166
+ }
149
167
 
150
- // The client public key - must be in PEM format.
151
- string public_key = 1;
168
+ // A base64-encoded signature of the DID document, only required if `did_document` is provided above.
169
+ string did_document_signature = 4;
152
170
 
153
171
  // A human-readable name of the entity requesting to bind.
154
- string binding_name = 2;
172
+ string binding_name = 5;
155
173
 
156
174
  // The volt challenge code, signed by the private key component of the `public_key` field above, and base64 encoded.
157
175
  // This is optional.
158
- string challenge = 3;
176
+ string challenge = 6;
159
177
 
160
178
  // The host name to add as a SAN to the issued certificate.
161
179
  // This is optional, if you don't intend to host services with the certificate this can be omitted.
162
- string host = 4;
180
+ string host = 7;
181
+
182
+ // Optional verifiable credentials describing the client.
183
+ // This is a work in progress.
184
+ repeated VerifiablePresentation verifiable_presentation = 8;
185
+
186
+ // Reserved for internal use.
187
+ bool purge_aliases = 9;
188
+ }
189
+
190
+ message AuthenticateResponse {
191
+ // Details of any error that occurred on the call.
192
+ tdx.volt_api.volt.v1.Status status = 1;
193
+
194
+ string session_id = 2;
195
+
196
+ // The identity id assigned to this binding.
197
+ // Only valid for PERMIT bind decisions.
198
+ string identity_id = 3;
199
+
200
+ // A certificate issued by the volt CA, binding the request public key to the identity.
201
+ // Only valid for PERMIT bind decisions.
202
+ string cert = 4;
203
+
204
+ // The volt CA chain. This is used by the client in subsequent API calls to secure the connection.
205
+ string chain = 5;
206
+
207
+ // The bind decision.
208
+ PolicyDecision decision = 6;
209
+
210
+ // Reserved for internal use.
211
+ int64 request_time = 7;
163
212
 
164
- // DO NOT USE
165
- // Additional credentials to support the bind request.
166
- // If a certificate chain is presented (in the form of a concatenated list of PEM format x509 certificates) that includes a certificate with a public key matching the `public_key` field and that certificate is signed by another certificate for which the volt policy has a trust rule, this can be used to automatically permit/deny the request.
167
- // This is probably deprecated in favour of verifiable credentials.
168
- repeated string x509_credential = 5;
213
+ // Reserved for internal use.
214
+ int64 decision_time = 8;
215
+ }
216
+
217
+ // Describes a request to bind to a volt.
218
+ message BindRequest {
219
+
220
+ oneof client_identifier {
221
+ // An existing DID owned by the client.
222
+ // The JWT presented with the Bind call must be signed by the private key corresponding to this DID.
223
+ string did = 1;
224
+
225
+ // If the client doesn't have an existing DID, a DID document can be provided here.
226
+ // The document must include a proof that the client owns the public key given in `public_key`.
227
+ // The Volt will register the DID on behalf of the client.
228
+ // The JWT presented with the Bind call must be signed by the private key corresponding to this document.
229
+ string did_document = 2;
230
+
231
+ // The client public key - must be in PEM format. If the client DID is lost or unknown for some reason, providing the public key here will allow the Volt to match it with the previously registered DID.
232
+ // Note this is only valid when a DID has previously been registered using this public key.
233
+ string public_key = 3;
234
+ }
235
+
236
+ // A base64-encoded signature of the DID document, only required if `did_document` is provided above.
237
+ string did_document_signature = 4;
238
+
239
+ // A human-readable name of the entity requesting to bind.
240
+ string binding_name = 5;
241
+
242
+ // The volt challenge code, signed by the private key component of the `public_key` field above, and base64 encoded.
243
+ // This is optional.
244
+ string challenge = 6;
245
+
246
+ // The host name to add as a SAN to the issued certificate.
247
+ // This is optional, if you don't intend to host services with the certificate this can be omitted.
248
+ string host = 7;
169
249
 
170
250
  // Optional verifiable credentials describing the client.
171
251
  // This is a work in progress.
172
- repeated VerifiablePresentation verifiable_presentation = 6;
252
+ repeated VerifiablePresentation verifiable_presentation = 8;
253
+
254
+ // Reserved for internal use.
255
+ bool purge_aliases = 9;
173
256
  }
174
257
 
175
258
  message BindResponse {
@@ -278,13 +361,15 @@ message ConnectHello {
278
361
  bool subscribe_auth_requests = 8;
279
362
 
280
363
  bool accept_invocation = 9;
364
+
365
+ bool subscribe_did_registry_updates = 10;
281
366
  }
282
367
 
283
368
  message ConnectAcknowledge {
284
369
  string connection_id = 1;
285
370
  }
286
371
 
287
- // A session ping message.
372
+ // A connection ping message.
288
373
  message ConnectPing {
289
374
  // The time on the originating server.
290
375
  uint64 timestamp = 1;
@@ -343,6 +428,7 @@ enum ConnectResourceEvent {
343
428
  CONNECT_RESOURCE_EVENT_DELETE = 3;
344
429
  CONNECT_RESOURCE_EVENT_CREATE_CHILD = 4;
345
430
  CONNECT_RESOURCE_EVENT_DELETE_CHILD = 5;
431
+ CONNECT_RESOURCE_EVENT_DATABASE_WRITE = 6;
346
432
  }
347
433
 
348
434
  message ConnectResource {
@@ -363,12 +449,18 @@ message ConnectAuthRequest {
363
449
  uint64 timestamp = 7;
364
450
  }
365
451
 
452
+ message ConnectDIDRegistryUpdate {
453
+ DIDRegistryUpdate update = 1;
454
+ }
455
+
366
456
  message ConnectEvent {
367
457
  oneof event {
368
458
  ConnectAuthRequest connect_auth_request = 1;
369
459
 
370
460
  // A resource event notification, such as updated or deleted.
371
461
  ConnectResource connect_resource = 2;
462
+
463
+ ConnectDIDRegistryUpdate did_registry_update = 3;
372
464
  }
373
465
  }
374
466
 
@@ -620,6 +712,8 @@ message GetParametersResponse {
620
712
 
621
713
  // This an empty message.
622
714
  message GetPolicyRequest {
715
+ // Set to only retrieve the custom policy document.
716
+ bool custom_policy = 1;
623
717
  }
624
718
 
625
719
  message GetPolicyResponse {
@@ -771,10 +865,9 @@ message InvokeRequest {
771
865
  // This contains the key and iv used to encrypt the payload, which only the token audience can access because the key and iv are themselves encrypted using the public key of the invocation target.
772
866
  string token = 2;
773
867
 
774
- // Key fingerprint of the target Volt.
868
+ // The DID of the target identity hosting the service.
775
869
  // This is used by Relays to route the request.
776
- // We can't (currently) use the target Volt id here, since there is currently no way to universally 'reason about' a given identity (Volt). For example, Alice may have assigned Bob an identity of 123, whereas Carol assigned Bob an identity of 456.
777
- repeated string target_fingerprint = 3;
870
+ repeated string target_did = 3;
778
871
 
779
872
  oneof request_payload {
780
873
  // A serialised and encrypted instance of `RemoteResponse` in pure binary format.
@@ -916,6 +1009,17 @@ message SaveIdentityRequest {
916
1009
 
917
1010
  // Reserved for system use.
918
1011
  string create_in_parent_id = 4;
1012
+
1013
+ // Set to indicate the identity aliases should be purged before saving the identity.
1014
+ // If the `identity` field contains aliases they will be saved after the purge.
1015
+ // If the `identity` field does not contain aliases this effectively deletes all aliases for this identity.
1016
+ // This allows you to selectively update aliases if required, i.e. don't set this flag and include a single alias in the update.
1017
+ bool purge_aliases = 5;
1018
+
1019
+ string did_document = 6;
1020
+
1021
+ // The signature of the identity did document, if present.
1022
+ string did_update_signature = 7;
919
1023
  }
920
1024
 
921
1025
  message SaveIdentityResponse {
@@ -985,19 +1089,13 @@ message SaveSessionResponse {
985
1089
  Session session = 2;
986
1090
  }
987
1091
 
988
- message SessionRequest {
989
- string session_id = 1;
990
- oneof payload {
991
- string fingerprint = 2;
992
- string signature = 3;
993
- }
1092
+ message SetPolicyRequest {
1093
+ string custom_policy = 1;
994
1094
  }
995
1095
 
996
- message SessionResponse {
1096
+ message SetPolicyResponse {
997
1097
  // Details of any error that occurred on the call.
998
1098
  tdx.volt_api.volt.v1.Status status = 1;
999
- string proof = 2;
1000
- string session_id = 3;
1001
1099
  }
1002
1100
 
1003
1101
  message SetServiceStatusRequest {
package/src/constants.js CHANGED
@@ -4,37 +4,37 @@ export const constants = {
4
4
  collectionOperation: {
5
5
  add: "ADD",
6
6
  remove: "REMOVE",
7
- update: "UPDATE",
7
+ update: "UPDATE"
8
8
  },
9
9
  crypto: {
10
10
  subjectDefaults: [
11
11
  {
12
12
  name: "countryName",
13
- value: "GB",
13
+ value: "GB"
14
14
  },
15
15
  {
16
16
  name: "localityName",
17
- value: "Southampton",
17
+ value: "Southampton"
18
18
  },
19
19
  {
20
20
  name: "organizationName",
21
- value: "nquiringminds Ltd",
22
- },
21
+ value: "nquiringminds Ltd"
22
+ }
23
23
  ],
24
- userIdOID: "0.9.2342.19200300.100.1.1",
24
+ userIdOID: "0.9.2342.19200300.100.1.1"
25
25
  },
26
26
  defaultDIDHostName: "cloud.tdxvolt.com",
27
27
  didServiceType: {
28
- voltConfig: "tdx-volt-config",
28
+ voltConfig: "tdx-volt-config"
29
29
  },
30
30
  identityType: {
31
31
  did: "did",
32
- volt: "volt",
32
+ volt: "volt"
33
33
  },
34
34
  onlineStatus: {
35
35
  offline: "offline",
36
36
  online: "online",
37
- unknown: "unknown",
37
+ unknown: "unknown"
38
38
  },
39
39
  publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
40
40
  privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
@@ -45,14 +45,14 @@ export const constants = {
45
45
  proxy: "proxy",
46
46
  proxyView: "proxy-view",
47
47
  vc: "vc",
48
- vcView: "vc-view",
48
+ vcView: "vc-view"
49
49
  },
50
50
  voltStatus: {
51
51
  pending: "",
52
52
  online: "online",
53
53
  offline: "offline",
54
54
  deleted: "deleted",
55
- revoked: "revoked",
55
+ revoked: "revoked"
56
56
  },
57
57
  /**
58
58
  * These top-level service types should map to a protobuf definition
@@ -62,14 +62,14 @@ export const constants = {
62
62
  fileAPI: "tdx.volt_api.volt.v1.FileAPI",
63
63
  sqliteServerAPI: "tdx.volt_api.data.v1.SqliteServerAPI",
64
64
  sqliteDatabaseAPI: "tdx.volt_api.data.v1.SqliteDatabaseAPI",
65
- ssiAPI: "tdx.volt_api.ssi.v1.SsiAPI",
65
+ ssiAPI: "tdx.volt_api.volt.v1.SsiAPI",
66
66
  relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
67
67
  voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
68
- wireAPI: "tdx.volt_api.volt.v1.WireAPI",
68
+ wireAPI: "tdx.volt_api.volt.v1.WireAPI"
69
69
  },
70
70
  tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
71
71
  tunnelPingInterval: 10000,
72
72
  tunnelPingTimeout: 2500,
73
73
  tunnelReconnectAfter: 30000,
74
- tunnelTimeoutInterval: 10000 * 2, // This must always be greater than `tunnelPingInterval`.
74
+ tunnelTimeoutInterval: 10000 * 2 // This must always be greater than `tunnelPingInterval`.
75
75
  };
package/src/grpc-call.js CHANGED
@@ -8,7 +8,7 @@ const log = debug("volt-client-grpc:grpc-call");
8
8
  function _prepareInvokeRequest(
9
9
  request,
10
10
  method,
11
- methodType = "METHOD_TYPE_UNARY",
11
+ methodType = "METHOD_TYPE_UNARY"
12
12
  ) {
13
13
  const isServiceRelayed =
14
14
  this._service?.service_description.host_type ===
@@ -21,7 +21,7 @@ function _prepareInvokeRequest(
21
21
  this._voltClient.voltConfig.id,
22
22
  this._service?.service_description.host_public_key ||
23
23
  this._voltClient.credential.voltPublicKey,
24
- true,
24
+ true
25
25
  );
26
26
 
27
27
  let invokeRequest;
@@ -52,42 +52,40 @@ function _prepareInvokeRequest(
52
52
  method_name: this._grpcClient[method].path,
53
53
  method_type: methodType,
54
54
  token: methodToken.token,
55
- request: requestPayload,
56
- },
55
+ request: requestPayload
56
+ }
57
57
  };
58
58
 
59
- let targetFingerprint = [];
59
+ let targetDID = [];
60
60
  let invokePayload;
61
61
  if (this._voltClient.isVoltRelay || isServiceRelayed) {
62
62
  // When sending via a relay we need to encrypt the payload.
63
63
  invokePayload = VoltCredential.aesEncrypt(
64
64
  tunnelMethod.responseSerialize(tunnelResp),
65
65
  methodToken.sharedKey.key,
66
- methodToken.sharedKey.iv,
66
+ methodToken.sharedKey.iv
67
67
  );
68
68
  } else {
69
69
  invokePayload = tunnelMethod.responseSerialize(tunnelResp);
70
70
  }
71
71
 
72
72
  if (this._voltClient.isVoltRelay) {
73
- targetFingerprint.push(this._voltClient.credential.voltFingerprint);
73
+ targetDID.push(this._voltClient.voltConfig.id);
74
74
  }
75
75
 
76
76
  if (isServiceRelayed) {
77
77
  // Add another relay hop if the target service is relayed.
78
78
  log("target service is relayed");
79
- targetFingerprint.push(
80
- this._service.service_description.host_fingerprint,
81
- );
79
+ targetDID.push(this._service.service_description.host_client_id);
82
80
  }
83
81
 
84
- log("target is %j", targetFingerprint);
82
+ log("target is %j", targetDID);
85
83
 
86
84
  invokeRequest = {
87
- target_fingerprint: targetFingerprint,
85
+ target_did: targetDID,
88
86
  token: methodToken.token,
89
87
  payload: invokePayload,
90
- target_service_id: isServiceRelayed ? this._service.id : undefined,
88
+ target_service_id: isServiceRelayed ? this._service.id : undefined
91
89
  };
92
90
 
93
91
  // Replace the target method with a call to Invoke on the relay Volt.
@@ -100,7 +98,7 @@ function _prepareInvokeRequest(
100
98
  meta: methodToken,
101
99
  method: callMethod,
102
100
  methodName: method,
103
- request: invokeRequest,
101
+ request: invokeRequest
104
102
  };
105
103
  }
106
104
 
@@ -120,18 +118,18 @@ function _preparePayloadRequest(request, invokeInfo) {
120
118
 
121
119
  const tunnelResp = {
122
120
  method_payload: {
123
- payload: requestPayload,
124
- },
121
+ payload: requestPayload
122
+ }
125
123
  };
126
124
 
127
125
  const invokePayload = VoltCredential.aesEncrypt(
128
126
  tunnelMethod.responseSerialize(tunnelResp),
129
127
  invokeInfo.meta.sharedKey.key,
130
- invokeInfo.meta.sharedKey.iv,
128
+ invokeInfo.meta.sharedKey.iv
131
129
  );
132
130
 
133
131
  payloadRequest = {
134
- payload: invokePayload,
132
+ payload: invokePayload
135
133
  };
136
134
  } else {
137
135
  payloadRequest = request;
@@ -155,7 +153,7 @@ function _parseResponse(method, meta, response) {
155
153
  decryptedPayload = VoltCredential.aesDecrypt(
156
154
  response.payload,
157
155
  meta.sharedKey.key,
158
- meta.sharedKey.iv,
156
+ meta.sharedKey.iv
159
157
  );
160
158
  } else {
161
159
  decryptedPayload = response.payload;
@@ -166,12 +164,12 @@ function _parseResponse(method, meta, response) {
166
164
  if (responsePayload.payload === "method_payload") {
167
165
  try {
168
166
  invokeResponse.payload = this._grpcClient[method].responseDeserialize(
169
- responsePayload.method_payload.payload,
167
+ responsePayload.method_payload.payload
170
168
  );
171
169
  } catch (err) {
172
170
  log("failure deserialising payload for %s", this._methodName);
173
171
  throw new Error(
174
- "failure deserialising payload - check protobuf definition matches with data being sent",
172
+ "failure deserialising payload - check protobuf definition matches with data being sent"
175
173
  );
176
174
  }
177
175
  } else if (responsePayload.payload === "method_end") {
@@ -185,11 +183,11 @@ function _parseResponse(method, meta, response) {
185
183
  "Tunnel status received: %s, code %d, description: %s",
186
184
  response.status.message,
187
185
  response.status.code,
188
- response.status.description || "n/a",
186
+ response.status.description || "n/a"
189
187
  );
190
188
  invokeResponse = {
191
189
  ended: true,
192
- error: `Error in tunnel: ${response.status.message}`,
190
+ error: `Error in tunnel: ${response.status.message}`
193
191
  };
194
192
  } else {
195
193
  invokeResponse.methodId = response.invoke_id;
@@ -199,11 +197,11 @@ function _parseResponse(method, meta, response) {
199
197
  const decryptedPayload = VoltCredential.aesDecrypt(
200
198
  response,
201
199
  meta.sharedKey.key,
202
- meta.sharedKey.iv,
200
+ meta.sharedKey.iv
203
201
  );
204
202
  invokeResponse = {
205
203
  payload:
206
- this._grpcClient[method].responseDeserializeOriginal(decryptedPayload),
204
+ this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)
207
205
  };
208
206
  } else {
209
207
  invokeResponse = { payload: response };
@@ -233,7 +231,7 @@ export default class GRPCCall extends EventEmitter {
233
231
  this,
234
232
  request,
235
233
  this._methodName,
236
- this._methodType,
234
+ this._methodType
237
235
  );
238
236
 
239
237
  const isServiceRelayed =
@@ -248,7 +246,7 @@ export default class GRPCCall extends EventEmitter {
248
246
  }
249
247
 
250
248
  this._call = callClient[this._initialRequest.method](
251
- this._initialRequest.meta.metadata,
249
+ this._initialRequest.meta.metadata
252
250
  );
253
251
 
254
252
  this._call.on("data", (streamResponse) => {
@@ -257,14 +255,14 @@ export default class GRPCCall extends EventEmitter {
257
255
  this,
258
256
  this._methodName,
259
257
  this._initialRequest.meta,
260
- streamResponse,
258
+ streamResponse
261
259
  );
262
260
  if (parsedResponse.payload) {
263
261
  // Interpret a non-empty status message as an error.
264
262
  if (parsedResponse.payload?.status?.message) {
265
263
  this.emit(
266
264
  "error",
267
- new Error(parsedResponse.payload.status.message),
265
+ new Error(parsedResponse.payload.status.message)
268
266
  );
269
267
  } else {
270
268
  this.emit("data", parsedResponse.payload);
@@ -283,7 +281,7 @@ export default class GRPCCall extends EventEmitter {
283
281
  log(
284
282
  "failure processing %s call response: %s",
285
283
  this._methodName,
286
- err.message,
284
+ err.message
287
285
  );
288
286
  this.emit("error", err);
289
287
  }
@@ -305,7 +303,7 @@ export default class GRPCCall extends EventEmitter {
305
303
  log(
306
304
  "ended call %s, method id: %s",
307
305
  this._methodName,
308
- this._initialRequest.methodId || "n/a - not tunnelling",
306
+ this._initialRequest.methodId || "n/a - not tunnelling"
309
307
  );
310
308
  this.emit("end");
311
309
  });
@@ -338,7 +336,7 @@ export default class GRPCCall extends EventEmitter {
338
336
  const payloadRequest = _preparePayloadRequest.call(
339
337
  this,
340
338
  request,
341
- this._initialRequest,
339
+ this._initialRequest
342
340
  );
343
341
  return this._call.write(payloadRequest);
344
342
  } else {
@@ -346,7 +344,7 @@ export default class GRPCCall extends EventEmitter {
346
344
  this,
347
345
  request,
348
346
  this._methodName,
349
- this._methodType,
347
+ this._methodType
350
348
  );
351
349
 
352
350
  return this._call.write(this._initialRequest.request);
package/src/utils.js CHANGED
@@ -64,11 +64,16 @@ export const createPublicKeyFingerprint = (key) => {
64
64
  return createBase58Hash(removeCarriageReturn(key));
65
65
  };
66
66
 
67
+ export const base64ToBase64Url = (base64) => {
68
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
69
+ };
70
+
67
71
  export const signBase64 = (key, msg) => {
68
72
  const md = forge.md.sha256.create();
69
73
  md.update(msg);
70
74
  const signature = key.sign(md);
71
- return forge.util.encode64(signature);
75
+ const base64Standard = forge.util.encode64(signature);
76
+ return base64ToBase64Url(base64Standard);
72
77
  };
73
78
 
74
79
  export const verifyBase64 = (key, msg, signature) => {
@@ -137,10 +142,25 @@ export const createRSAKey = (passphrase = "") => {
137
142
  export const sha256Base64 = (msg) => {
138
143
  const md = forge.md.sha256.create();
139
144
  md.update(msg);
140
- return forge.util.encode64(md.digest().bytes());
145
+ const base64Standard = forge.util.encode64(md.digest().bytes());
146
+ return base64ToBase64Url(base64Standard);
141
147
  };
142
148
 
143
149
  export const decryptPrivateKey = (pem, passphrase) => {
144
150
  const decryptedKey = pki.decryptRsaPrivateKey(pem, passphrase);
145
151
  return forgePrivateKeyToPem(decryptedKey);
146
152
  };
153
+
154
+ export const jwsSignature = (signature) => {
155
+ //
156
+ // Use an unattached payload JWS signature for the DID document proof.
157
+ // This is a JWS with an empty payload and a detached payload digest.
158
+ //
159
+ // (https://tools.ietf.org/html/rfc7797#appendix-F) as used in the VC spec
160
+ // (https://www.w3.org/TR/vc-data-model/).
161
+ //
162
+ const jwsHeader = base64ToBase64Url(
163
+ forge.util.encode64('{"alg":"RS256","b64": false}')
164
+ );
165
+ return jwsHeader + ".." + signature;
166
+ };