@bedrock/vc-delivery 7.18.2 → 7.19.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/helpers.js CHANGED
@@ -175,6 +175,27 @@ export async function generateRandom() {
175
175
  });
176
176
  }
177
177
 
178
+ export function getAlgFromJwk({jwk} = {}) {
179
+ if(jwk.alg) {
180
+ return jwk.alg;
181
+ }
182
+ if(jwk.kty === 'EC' && jwk.crv) {
183
+ if(jwk.crv.startsWith('P-')) {
184
+ return `ES${jwk.crv.slice(2)}`;
185
+ }
186
+ if(jwk.crv === 'secp256k1') {
187
+ return 'ES256K';
188
+ }
189
+ }
190
+ if(jwk.kty === 'OKP' && jwk.crv?.startsWith('Ed')) {
191
+ return 'EdDSA';
192
+ }
193
+ if(jwk.kty === 'RSA') {
194
+ return 'PS256';
195
+ }
196
+ return 'invalid';
197
+ }
198
+
178
199
  export function getWorkflowIssuerInstances({workflow} = {}) {
179
200
  let {issuerInstances} = workflow;
180
201
  if(!issuerInstances && workflow.zcaps.issue) {
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import * as bedrock from '@bedrock/core';
5
5
  import {AsymmetricKey, KmsClient} from '@digitalbazaar/webkms-client';
6
- import {exportJWK, generateKeyPair} from 'jose';
6
+ import {exportJWK, generateKeyPair, importJWK} from 'jose';
7
7
  import {oid4vp, signJWT} from '@digitalbazaar/oid4-client';
8
8
  import {getClientBaseUrl} from './clientProfiles.js';
9
9
  import {getZcapClient} from '../helpers.js';
@@ -237,42 +237,65 @@ async function _createJwt({workflow, clientProfile, authorizationRequest}) {
237
237
  // create zcap client
238
238
  const {zcapClient, zcaps} = await getZcapClient({workflow});
239
239
 
240
- // get any `x5c` and the zcap to use to sign the authz request via
241
- // the client profile
240
+ // get any `x5c` and either `privateKeyJwk` or the zcap to use to sign the
241
+ // authz request via the client profile
242
242
  const {
243
- authorizationRequestSigningParameters: {x5c} = {},
243
+ authorizationRequestSigningParameters: {privateKeyJwk, x5c} = {},
244
244
  zcapReferenceIds: {signAuthorizationRequest: refId} = {}
245
245
  } = clientProfile;
246
- if(refId === undefined) {
246
+ if(privateKeyJwk === undefined && refId === undefined) {
247
247
  throw new BedrockError(
248
- 'The OID4VP client profile does not specify which capability in the ' +
249
- 'workflow configuration to use to sign authorization requests.', {
250
- name: 'DataError',
251
- details: {httpStatusCode: 500, public: true}
252
- });
253
- }
254
- const capability = zcaps[refId];
255
- if(capability === undefined) {
256
- throw new BedrockError(
257
- 'The capability specified by the OID4VP client profile for signing ' +
258
- 'authorization requests was not found in the workflow configuration.', {
248
+ 'The OID4VP client profile does not specify the private key or ' +
249
+ 'capability in the workflow configuration to use to sign ' +
250
+ 'authorization requests.', {
259
251
  name: 'DataError',
260
252
  details: {httpStatusCode: 500, public: true}
261
253
  });
262
254
  }
263
255
 
264
- // create a WebKMS `signer` interface
265
- const {invocationSigner} = zcapClient;
266
- const kmsClient = new KmsClient({httpsAgent});
267
- const signer = await AsymmetricKey.fromCapability({
268
- capability, invocationSigner, kmsClient
269
- });
270
- const keyDescription = await signer.getKeyDescription();
271
- const kid = keyDescription.id;
256
+ // code assumed ES256 (ECDSA P-256 + SHA-256) support only
257
+ const alg = 'ES256';
258
+ let kid;
259
+ let signer;
260
+ if(privateKeyJwk) {
261
+ kid = privateKeyJwk.kid;
262
+
263
+ // import `privateKeyJwk`
264
+ const privateKey = await importJWK(privateKeyJwk, alg);
265
+
266
+ // create signer API
267
+ const algorithm = {name: 'ECDSA', hash: {name: 'SHA-256'}};
268
+ signer = {
269
+ async sign({data}) {
270
+ return new Uint8Array(
271
+ await crypto.subtle.sign(algorithm, privateKey, data));
272
+ }
273
+ };
274
+ } else {
275
+ const capability = zcaps[refId];
276
+ if(capability === undefined) {
277
+ throw new BedrockError(
278
+ 'The capability specified by the OID4VP client profile for signing ' +
279
+ 'authorization requests was not found in the workflow ' +
280
+ 'configuration.', {
281
+ name: 'DataError',
282
+ details: {httpStatusCode: 500, public: true}
283
+ });
284
+ }
285
+
286
+ // create a WebKMS `signer` interface
287
+ const {invocationSigner} = zcapClient;
288
+ const kmsClient = new KmsClient({httpsAgent});
289
+ signer = await AsymmetricKey.fromCapability({
290
+ capability, invocationSigner, kmsClient
291
+ });
292
+ const keyDescription = await signer.getKeyDescription();
293
+ kid = keyDescription.id;
294
+ }
272
295
 
273
296
  // create the JWT payload and header to be signed
274
297
  const payload = {...authorizationRequest};
275
- const protectedHeader = {typ: OID4VP_JWT_TYP, alg: 'ES256', kid, x5c};
298
+ const protectedHeader = {typ: OID4VP_JWT_TYP, alg, kid, x5c};
276
299
 
277
300
  // create the JWT
278
301
  return signJWT({payload, protectedHeader, signer});
@@ -4,7 +4,9 @@
4
4
  import * as bedrock from '@bedrock/core';
5
5
  import * as draft13 from './oid4vciDraft13.js';
6
6
  import {issue as defaultIssue, getIssueRequestsParams} from '../issue.js';
7
- import {getWorkflowIssuerInstances, setVariable} from '../helpers.js';
7
+ import {
8
+ getAlgFromJwk, getWorkflowIssuerInstances, setVariable
9
+ } from '../helpers.js';
8
10
  import {importJWK, SignJWT} from 'jose';
9
11
  import {timingSafeEqual, randomUUID as uuid} from 'node:crypto';
10
12
  import {
@@ -648,7 +650,7 @@ function _createSupportedCredentialRequests({
648
650
  async function _createOAuth2AccessToken({
649
651
  privateKeyJwk, audience, action, target, exp, iss, nbf, typ = 'at+jwt'
650
652
  }) {
651
- const alg = _getAlgFromPrivateKey({privateKeyJwk});
653
+ const alg = getAlgFromJwk({jwk: privateKeyJwk});
652
654
  const scope = `${action}:${target}`;
653
655
  const builder = new SignJWT({scope})
654
656
  .setProtectedHeader({alg, typ})
@@ -671,27 +673,6 @@ async function _createOAuth2AccessToken({
671
673
  return {accessToken, ttl};
672
674
  }
673
675
 
674
- function _getAlgFromPrivateKey({privateKeyJwk}) {
675
- if(privateKeyJwk.alg) {
676
- return privateKeyJwk.alg;
677
- }
678
- if(privateKeyJwk.kty === 'EC' && privateKeyJwk.crv) {
679
- if(privateKeyJwk.crv.startsWith('P-')) {
680
- return `ES${privateKeyJwk.crv.slice(2)}`;
681
- }
682
- if(privateKeyJwk.crv === 'secp256k1') {
683
- return 'ES256K';
684
- }
685
- }
686
- if(privateKeyJwk.kty === 'OKP' && privateKeyJwk.crv?.startsWith('Ed')) {
687
- return 'EdDSA';
688
- }
689
- if(privateKeyJwk.kty === 'RSA') {
690
- return 'PS256';
691
- }
692
- return 'invalid';
693
- }
694
-
695
676
  function _getSupportedCredentialConfigurations({workflow, exchange, step}) {
696
677
  // get all OID4VCI credential configuration IDs from issue requests in step
697
678
  const issueRequestsParams = getIssueRequestsParams({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrock/vc-delivery",
3
- "version": "7.18.2",
3
+ "version": "7.19.0",
4
4
  "type": "module",
5
5
  "description": "Bedrock Verifiable Credential Delivery",
6
6
  "main": "./lib/index.js",
@@ -547,8 +547,22 @@ const oid4vpClientProfile = {
547
547
  required: ['x5c'],
548
548
  additionalProperties: false,
549
549
  properties: {
550
- // FIXME: consider adding `privateKeyJwk` as an alternative to using
551
- // a zcap for a WebKMS key
550
+ privateKeyJwk: {
551
+ type: 'object',
552
+ required: ['kid', 'kty', 'crv', 'x', 'y', 'd'],
553
+ properties: {
554
+ // only ES256 supported at this time
555
+ alg: {enum: ['ES256']},
556
+ kid: {type: 'string'},
557
+ kty: {enum: ['EC']},
558
+ crv: {enum: ['P-256']},
559
+ x: {type: 'string'},
560
+ y: {type: 'string'},
561
+ d: {type: 'string'},
562
+ use: {const: 'sig'},
563
+ key_ops: {const: ['sign']}
564
+ }
565
+ },
552
566
  x5c: {
553
567
  type: 'array',
554
568
  minItems: 1,