Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update dependencies. #84

Merged
merged 12 commits into from
Aug 5, 2024
12 changes: 6 additions & 6 deletions .github/workflows/main.yml → .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ jobs:
matrix:
node-version: [20.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm install
Expand All @@ -31,9 +31,9 @@ jobs:
matrix:
node-version: [18.x, 20.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: |
Expand All @@ -57,9 +57,9 @@ jobs:
matrix:
node-version: [20.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: |
Expand Down
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
# bedrock-vc-delivery ChangeLog

## 4.8.1 - 2024-07-dd
## 5.0.0 - 2024-08-xx

### Added
- Add support for ECDSA keys (with `ES256` and `ES384` algs) for
DID JWT proofs.
- Add support for VC 2.0 contexts in JSON schemas.

### Changed
- **BREAKING**: Update peer dependencies.
- `@bedrock/[email protected]`
- `@bedrock/[email protected]`
- `@bedrock/[email protected]`
- `@bedrock/[email protected]`
- `@bedrock/[email protected]`
- `@bedrock/[email protected]`
- `@bedrock/[email protected]`
- `@bedrock/[email protected]`
- Update minor, test, and dev dependencies.

### Fixed
- Fix JSON schema to allow VCs with only a single type.
Expand Down
28 changes: 28 additions & 0 deletions lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import * as bedrock from '@bedrock/core';
import {decodeId, generateId} from 'bnid';
import {decodeJwt} from 'jose';
import {Ed25519Signature2020} from '@digitalbazaar/ed25519-signature-2020';
import {httpsAgent} from '@bedrock/https-agent';
import jsonata from 'jsonata';
Expand Down Expand Up @@ -102,3 +103,30 @@ export function decodeLocalId({localId} = {}) {
expectedSize: 16
}));
}

export async function unenvelopeCredential({envelopedCredential} = {}) {
let credential;
const {id} = envelopedCredential;
if(id?.startsWith('data:application/jwt,')) {
const format = 'application/jwt';
const jwt = id.slice('data:application/jwt,'.length);
const claimset = decodeJwt(jwt);
// FIXME: perform various field mappings as needed
console.log('VC-JWT claimset', credential);
return {credential: claimset.vc, format};
}
throw new Error('Not implemented.');
}

export async function unenvelopePresentation({envelopedPresentation} = {}) {
const {id} = envelopedPresentation;
if(id?.startsWith('data:application/jwt,')) {
const format = 'application/jwt';
const jwt = id.slice('data:application/jwt,'.length);
const claimset = decodeJwt(jwt);
// FIXME: perform various field mappings as needed
console.log('VC-JWT claimset', claimset);
return {presentation: claimset.vp, format};
}
throw new Error('Not implemented.');
}
7 changes: 4 additions & 3 deletions lib/openId.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import * as bedrock from '@bedrock/core';
import * as exchanges from './exchanges.js';
import {
compile, schemas, createValidateMiddleware as validate
compile, createValidateMiddleware as validate
} from '@bedrock/validation';
import {evaluateTemplate, getWorkflowIssuerInstances} from './helpers.js';
import {importJWK, SignJWT} from 'jose';
Expand All @@ -13,7 +13,8 @@ import {
openIdBatchCredentialBody,
openIdCredentialBody,
openIdTokenBody,
presentationSubmission as presentationSubmissionSchema
presentationSubmission as presentationSubmissionSchema,
verifiablePresentation as verifiablePresentationSchema
} from '../schemas/bedrock-vc-workflow.js';
import {verify, verifyDidProofJwt} from './verify.js';
import {asyncHandler} from '@bedrock/express';
Expand Down Expand Up @@ -84,7 +85,7 @@ export async function createRoutes({

// create validators for x-www-form-urlencoded parsed data
const validatePresentation = compile(
{schema: schemas.verifiablePresentation()});
{schema: verifiablePresentationSchema()});
const validatePresentationSubmission = compile(
{schema: presentationSubmissionSchema});

Expand Down
50 changes: 41 additions & 9 deletions lib/verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
* Copyright (c) 2022-2024 Digital Bazaar, Inc. All rights reserved.
*/
import * as bedrock from '@bedrock/core';
import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey';
import * as Ed25519Multikey from '@digitalbazaar/ed25519-multikey';
import {importJWK, jwtVerify} from 'jose';
import {didIo} from '@bedrock/did-io';
import {getZcapClient} from './helpers.js';

const {util: {BedrockError}} = bedrock;

// supported JWT algs
const ECDSA_ALGS = ['ES256', 'ES384'];
const EDDSA_ALGS = ['Ed25519', 'EdDSA'];

export async function createChallenge({workflow} = {}) {
// create zcap client for creating challenges
const {zcapClient, zcaps} = await getZcapClient({workflow});
Expand Down Expand Up @@ -134,23 +139,50 @@ export async function verifyDidProofJwt({workflow, exchange, jwt} = {}) {
const audience = exchangeId;

let issuer;
const resolveKey = async protectedHeader => {
const vm = await didIo.get({url: protectedHeader.kid});
// `resolveKey` is passed `protectedHeader`
const resolveKey = async ({alg, kid}) => {
const isEcdsa = ECDSA_ALGS.includes(alg);
const isEddsa = !isEcdsa && EDDSA_ALGS.includes(alg);
if(!(isEcdsa || isEddsa)) {
throw new BedrockError(
`Unsupported JWT "alg": "${alg}".`, {
name: 'DataError',
details: {
httpStatusCode: 400,
public: true
}
});
}

const vm = await didIo.get({url: kid});
// `vm.controller` must be the issuer of the DID JWT; also ensure that
// the specified controller authorized `vm` for the purpose of
// authentication
issuer = vm.controller;
const didDoc = await didIo.get({url: issuer});
if(!(didDoc?.authentication?.some(e => e === vm.id || e.id === vm.id))) {
let match = didDoc?.authentication?.find?.(
e => e === vm.id || e.id === vm.id);
if(typeof match === 'string') {
match = didDoc?.verificationMethod?.find?.(e => e.id === vm.id);
}
if(!(match && Array.isArray(match.controller) ?
match.controller.includes(vm.controller) :
match.controller === vm.controller)) {
throw new BedrockError(
`Verification method controller "${issuer}" did not authorize ` +
`verification method "${vm.id}" for the purpose of "authentication".`,
{name: 'NotAllowedError'});
}
// FIXME: support other key types
const keyPair = await Ed25519Multikey.from(vm);
const jwk = await Ed25519Multikey.toJwk({keyPair});
jwk.alg = 'EdDSA';
let jwk;
if(isEcdsa) {
const keyPair = await EcdsaMultikey.from(vm);
jwk = await EcdsaMultikey.toJwk({keyPair});
jwk.alg = alg;
} else {
const keyPair = await Ed25519Multikey.from(vm);
jwk = await Ed25519Multikey.toJwk({keyPair});
jwk.alg = 'EdDSA';
}
return importJWK(jwk);
};

Expand Down Expand Up @@ -186,7 +218,7 @@ export async function verifyDidProofJwt({workflow, exchange, jwt} = {}) {
}

// check `iss` claim
if(!(verifyResult?.payload?.iss === issuer)) {
if(!(issuer && verifyResult?.payload?.iss === issuer)) {
throw new BedrockError('DID proof JWT validation failed.', {
name: 'NotAllowedError',
details: {
Expand All @@ -200,7 +232,7 @@ export async function verifyDidProofJwt({workflow, exchange, jwt} = {}) {
}

// check `nonce` claim
if(!(verifyResult?.payload?.nonce === exchange.id)) {
if(verifyResult?.payload?.nonce !== exchange.id) {
throw new BedrockError('DID proof JWT validation failed.', {
name: 'NotAllowedError',
details: {
Expand Down
45 changes: 22 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"schemas/**/*.js"
],
"scripts": {
"lint": "eslint ."
"lint": "eslint --ext .cjs,.js ."
},
"repository": {
"type": "git",
Expand All @@ -35,41 +35,40 @@
},
"homepage": "https://github.com/digitalbazaar/bedrock-vc-delivery",
"dependencies": {
"@digitalbazaar/ecdsa-multikey": "^1.7.0",
"@digitalbazaar/ed25519-multikey": "^1.1.0",
"@digitalbazaar/ed25519-signature-2020": "^5.2.0",
"@digitalbazaar/ezcap": "^4.0.0",
"@digitalbazaar/oid4-client": "^3.1.0",
"@digitalbazaar/vc": "^6.0.1",
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
"@digitalbazaar/ezcap": "^4.1.0",
"@digitalbazaar/oid4-client": "^3.4.1",
"@digitalbazaar/vc": "^7.0.0",
"assert-plus": "^1.0.0",
"bnid": "^3.0.0",
"body-parser": "^1.20.1",
"body-parser": "^1.20.2",
"cors": "^2.8.5",
"jose": "^4.10.4",
"jsonata": "^2.0.3",
"klona": "^2.0.5"
"jose": "^5.6.3",
"jsonata": "^2.0.5",
"klona": "^2.0.6"
},
"peerDependencies": {
"@bedrock/app-identity": "4.0.0",
"@bedrock/core": "^6.0.1",
"@bedrock/did-io": "^10.0.0",
"@bedrock/express": "^8.0.0",
"@bedrock/https-agent": "^4.0.0",
"@bedrock/mongodb": "^10.0.0",
"@bedrock/oauth2-verifier": "^2.0.0",
"@bedrock/service-agent": "^8.0.0",
"@bedrock/service-core": "^9.0.0",
"@bedrock/core": "^6.1.3",
"@bedrock/did-io": "^10.3.1",
"@bedrock/express": "^8.3.1",
"@bedrock/https-agent": "^4.1.0",
"@bedrock/mongodb": "^10.2.0",
"@bedrock/oauth2-verifier": "^2.1.0",
"@bedrock/service-agent": "^9.0.2",
"@bedrock/service-core": "^10.0.0",
"@bedrock/validation": "^7.1.0"
},
"directories": {
"lib": "./lib"
},
"devDependencies": {
"eslint": "^8.41.0",
"eslint-config-digitalbazaar": "^5.0.1",
"eslint-plugin-jsdoc": "^46.3.0",
"eslint-plugin-unicorn": "^47.0.0",
"jsdoc": "^4.0.2",
"jsdoc-to-markdown": "^8.0.0"
"eslint": "^8.57.0",
"eslint-config-digitalbazaar": "^5.2.0",
"eslint-plugin-jsdoc": "^48.11.0",
"eslint-plugin-unicorn": "^55.0.0"
},
"engines": {
"node": ">=18"
Expand Down
Loading