@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 +21 -0
- package/lib/oid4/authorizationRequest.js +48 -25
- package/lib/oid4/oid4vci.js +4 -23
- package/package.json +1 -1
- package/schemas/bedrock-vc-workflow.js +16 -2
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
|
|
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
|
|
249
|
-
'workflow configuration to use to sign
|
|
250
|
-
|
|
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
|
-
//
|
|
265
|
-
const
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
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
|
|
298
|
+
const protectedHeader = {typ: OID4VP_JWT_TYP, alg, kid, x5c};
|
|
276
299
|
|
|
277
300
|
// create the JWT
|
|
278
301
|
return signJWT({payload, protectedHeader, signer});
|
package/lib/oid4/oid4vci.js
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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
|
@@ -547,8 +547,22 @@ const oid4vpClientProfile = {
|
|
|
547
547
|
required: ['x5c'],
|
|
548
548
|
additionalProperties: false,
|
|
549
549
|
properties: {
|
|
550
|
-
|
|
551
|
-
|
|
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,
|