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

Implement jwt for DB envelope tests #152

Merged
merged 5 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"type": "module",
"scripts": {
"test": "mocha tests/ --reporter @digitalbazaar/mocha-w3c-interop-reporter --reporter-options abstract=\"$PWD/abstract.hbs\",reportDir=\"$PWD/reports\",respec=\"$PWD/respecConfig.json\",suiteLog='./suite.log',templateData=\"$PWD/reports/index.json\",title=\"VC v2.0 Interoperability Report\" --timeout 15000 --preserve-symlinks",
"lint": "eslint ."
"lint": "eslint .",
"test-issuance": "node test.mjs"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -38,6 +39,9 @@
"@digitalbazaar/data-integrity": "^2.5.0",
"@digitalbazaar/data-integrity-context": "^2.0.1",
"@digitalbazaar/ed25519-multikey": "^1.0.1",
"@digitalbazaar/did-method-key": "^5.2.0",
"@digitalbazaar/ed25519-signature-2020": "^5.4.0",
"@digitalbazaar/ezcap": "^4.1.0",
Comment on lines +41 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deps should be sorted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after reading this seventeen times

OH! It's not that these three lines are incorrectly sorted/ordered in relation to each other (because these three are correctly ordered, in isolation), but that these three lines are incorrectly sorted/ordered in relation to the lines above and below!

That's several minutes I won't get back...

"@digitalbazaar/eddsa-rdfc-2022-cryptosuite": "^1.1.0",
"@digitalbazaar/mocha-w3c-interop-reporter": "^1.5.0",
"@digitalbazaar/vc": "^7.0.0",
Expand Down
93 changes: 51 additions & 42 deletions tests/4.13.2-envelopes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import {
generateCredential,
generateEnvelope,
secureCredential,
setupMatrix
setupMatrix,
verifyCredential,
verifyPresentation
} from './helpers.js';
import {
vc_jwt,
Expand All @@ -18,7 +20,6 @@ import {
import assert from 'node:assert/strict';
import chai from 'chai';
import {filterByTag} from 'vc-test-suite-implementations';
import {TestEndpoints} from './TestEndpoints.js';

const should = chai.should();

Expand All @@ -29,17 +30,16 @@ const {match} = filterByTag({tags: [tag]});
describe('Enveloped Verifiable Credentials', function() {
setupMatrix.call(this, match);
for(const [name, implementation] of match) {
const endpoints = new TestEndpoints({implementation, tag});
const issuer = implementation.issuers?.find(
issuer => issuer.tags.has(tag)) || null;
const verifier = implementation.verifiers?.find(
verifier => verifier.tags.has(tag)) || null;

describe(name, function() {
let envelopedCredential;
let verifiableCredential;
let negativeFixture;
before(async function() {
envelopedCredential = generateEnvelope({
verifiableCredential = generateEnvelope({
type: 'EnvelopedVerifiableCredential',
id: `data:application/vc+jwt,${vc_jwt}`
});
Expand All @@ -57,22 +57,26 @@ describe('Enveloped Verifiable Credentials', function() {
{issuer, credential: generateCredential()});
should.exist(issuedVc, 'Expected credential to be issued.');
issuedVc.should.have.property('@context');
verifiableCredential = issuedVc;
}
if(verifier) {
await assert.doesNotReject(endpoints.verify(envelopedCredential),
await assert.doesNotReject(
verifyCredential({verifier, verifiableCredential}),
'Failed to accept an enveloped VC.');

// Replace context with an empty array
negativeFixture = structuredClone(envelopedCredential);
negativeFixture = structuredClone(verifiableCredential);
negativeFixture['@context'] = [];
await assert.rejects(endpoints.verify(negativeFixture),
'Failed to reject an enveloped VC with an empty context.');
await assert.rejects(
verifyCredential({verifier, negativeFixture}),
'Failed to reject an enveloped VC with invalid context.');

// Replace context with an invalid value
negativeFixture = structuredClone(envelopedCredential);
negativeFixture = structuredClone(verifiableCredential);
negativeFixture['@context'] = 'https://www.w3.org/ns/credentials/examples/v2';
await assert.rejects(endpoints.verify(negativeFixture),
'Failed to reject an enveloped VC with an invalid context.');
await assert.rejects(
verifyCredential({verifier, negativeFixture}),
'Failed to reject an enveloped VC with invalid context.');
}
});

Expand All @@ -85,18 +89,19 @@ describe('Enveloped Verifiable Credentials', function() {
const issuedVc = await secureCredential(
{issuer, credential: generateCredential()});
should.exist(issuedVc, 'Expected credential to be issued.');
issuedVc.should.have.property('id').that.does
.include('data:',
`Expecting id field to be a 'data:' scheme URL [RFC2397].`);
issuedVc.should.have.property('@context');
verifiableCredential = issuedVc;
}
if(verifier) {
await assert.doesNotReject(endpoints.verify(envelopedCredential),
await assert.doesNotReject(
verifyCredential({verifier, verifiableCredential}),
'Failed to accept an enveloped VC.');

// Remove data uri portion of the id field
negativeFixture = structuredClone(envelopedCredential);
negativeFixture = structuredClone(verifiableCredential);
negativeFixture.id = negativeFixture.id.split(',').pop();
await assert.rejects(endpoints.verify(negativeFixture),
await assert.rejects(
verifyCredential({verifier, negativeFixture}),
'Failed to reject an enveloped VC with an invalid data url id.');
}
});
Expand All @@ -108,25 +113,27 @@ describe('Enveloped Verifiable Credentials', function() {
const issuedVc = await secureCredential(
{issuer, credential: generateCredential()});
should.exist(issuedVc, 'Expected credential to be issued.');
issuedVc.should.have.property('type').that.is.equal(
'EnvelopedVerifiableCredential',
`Expecting type field to be EnvelopedVerifiableCredential`);
issuedVc.should.have.property('@context');
verifiableCredential = issuedVc;
}
if(verifier) {
await assert.doesNotReject(endpoints.verify(envelopedCredential),
await assert.doesNotReject(
verifyCredential({verifier, verifiableCredential}),
'Failed to accept an enveloped VC.');

// Remove type field
negativeFixture = structuredClone(envelopedCredential);
negativeFixture = structuredClone(verifiableCredential);
delete negativeFixture.type;
await assert.rejects(endpoints.verify(negativeFixture),
await assert.rejects(
verifyCredential({verifier, negativeFixture}),
'Failed to reject an enveloped VC with an enveloped VC with a ' +
'missing `type`.');

// Replace type field
negativeFixture = structuredClone(envelopedCredential);
negativeFixture.type = ['VerifiableCredential'];
await assert.rejects(endpoints.verify(negativeFixture),
negativeFixture = structuredClone(verifiableCredential);
negativeFixture.type = 'VerifiableCredential';
await assert.rejects(
verifyCredential({verifier, negativeFixture}),
'Failed to reject an enveloped VC with an ' +
'invalid `type`.');
}
Expand All @@ -139,17 +146,16 @@ describe('Enveloped Verifiable Credentials', function() {
describe('Enveloped Verifiable Presentations', function() {
setupMatrix.call(this, match);
for(const [name, implementation] of match) {
const endpoints = new TestEndpoints({implementation, tag});
const vpVerifier = implementation.vpVerifiers?.find(
vpVerifier => vpVerifier.tags.has(tag)) || null;

describe(name, function() {
let envelopedPresentation;
let verifiablePresentation;
let negativeFixture;
before(async function() {
envelopedPresentation = generateEnvelope({
verifiablePresentation = generateEnvelope({
type: 'EnvelopedVerifiablePresentation',
id: `data:application/vp+jwt,${vp_jwt}`
id: `data:application/jwt,${vp_jwt}`
});
});
beforeEach(addPerTestMetadata);
Expand All @@ -162,21 +168,22 @@ describe('Enveloped Verifiable Presentations', function() {
this.test.link = `https://w3c.github.io/vc-data-model/#enveloped-verifiable-presentations:~:text=The%20%40context%20property%20of%20the%20object%20MUST%20be%20present%20and%20include%20a%20context%2C%20such%20as%20the%20base%20context%20for%20this%20specification%2C%20that%20defines%20at%20least%20the%20id%2C%20type%2C%20and%20EnvelopedVerifiablePresentation%20terms%20as%20defined%20by%20the%20base%20context%20provided%20by%20this%20specification.`;

if(vpVerifier) {
await assert.doesNotReject(endpoints.verifyVp(envelopedPresentation),
await assert.doesNotReject(
verifyPresentation({vpVerifier, verifiablePresentation}),
'Failed to accept an enveloped VP.');

// Replace context field with empty array
negativeFixture = structuredClone(envelopedPresentation);
negativeFixture = structuredClone(verifiablePresentation);
negativeFixture['@context'] = [];
await assert.rejects(
endpoints.verifyVp(negativeFixture),
verifyPresentation({vpVerifier, negativeFixture}),
'Failed to reject Enveloped VP missing contexts.');

// Replace context field with invalid context
negativeFixture = structuredClone(envelopedPresentation);
negativeFixture = structuredClone(verifiablePresentation);
negativeFixture['@context'] = ['https://www.w3.org/ns/credentials/examples/v2'];
await assert.rejects(
endpoints.verifyVp(negativeFixture),
verifyPresentation({vpVerifier, negativeFixture}),
'Failed to reject Enveloped VP missing contexts.');
}
});
Expand All @@ -188,14 +195,15 @@ describe('Enveloped Verifiable Presentations', function() {
this.test.link = `https://w3c.github.io/vc-data-model/#enveloped-verifiable-presentations:~:text=The%20id%20value%20of%20the%20object%20MUST%20be%20a%20data%3A%20URL%20%5BRFC2397%5D%20that%20expresses%20a%20secured%20verifiable%20presentation%20using%20an%20enveloping%20securing%20mechanism%2C%20such%20as%20Securing%20Verifiable%20Credentials%20using%20JOSE%20and%20COSE%20%5BVC%2DJOSE%2DCOSE%5D.`;

if(vpVerifier) {
await assert.doesNotReject(endpoints.verifyVp(envelopedPresentation),
await assert.doesNotReject(
verifyPresentation({vpVerifier, verifiablePresentation}),
'Failed to accept an enveloped VP.');

// Remove data uri portion from id field
negativeFixture = structuredClone(envelopedPresentation);
negativeFixture = structuredClone(verifiablePresentation);
negativeFixture.id = negativeFixture.id.split(',').pop();
await assert.rejects(
endpoints.verifyVp(negativeFixture),
verifyPresentation({vpVerifier, negativeFixture}),
'Failed to reject Enveloped VP with an id that is not a data url.');
}
});
Expand All @@ -205,14 +213,15 @@ describe('Enveloped Verifiable Presentations', function() {
this.test.link = `https://w3c.github.io/vc-data-model/#enveloped-verifiable-presentations:~:text=The%20type%20value%20of%20the%20object%20MUST%20be%20EnvelopedVerifiablePresentation.`;

if(vpVerifier) {
await assert.doesNotReject(endpoints.verifyVp(envelopedPresentation),
await assert.doesNotReject(
verifyPresentation({vpVerifier, verifiablePresentation}),
'Failed to accept an enveloped VP.');

// Replace type field
negativeFixture = structuredClone(envelopedPresentation);
negativeFixture = structuredClone(verifiablePresentation);
negativeFixture.type = ['VerifiablePresentation'];
await assert.rejects(
endpoints.verifyVp(negativeFixture),
verifyPresentation({vpVerifier, negativeFixture}),
'Failed to reject VP w/o type "EnvelopedVerifiablePresentation".');
}
});
Expand Down
4 changes: 2 additions & 2 deletions tests/fixtures.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 55 additions & 8 deletions tests/helpers.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import {challenge} from './fixtures.js';
import {makeZcapRequest} from './zcapHandler.js';

export function setupMatrix(match) {
// this will tell the report
// to make an interop matrix with this suite
Expand Down Expand Up @@ -44,15 +47,56 @@ export const secureCredential = async ({
const {settings: {id: issuerId, options = {}}} = issuer;
credential.issuer = issuerId;
const body = {credential, options};
const {data, result, error} = await issuer.post({json: body});
if(!result || !result.ok) {
console.warn(
`initial vc creation failed for ${(result || error)?.requestUrl}`,
error
);
return null;
if(issuer.settings.zcap) {
const response = await makeZcapRequest(issuer.settings, body);
return response?.data?.verifiableCredential;
} else {
const {data, result, error} = await issuer.post({json: body});
if(!result || !result.ok) {
error;
return null;
}
return data;
}
};

export const verifyCredential = async ({
verifier,
verifiableCredential,
}) => {
const {settings: {options = {}}} = verifier;
const body = {verifiableCredential, options};
if(verifier.settings.zcap) {
const response = await makeZcapRequest(verifier.settings, body);
return response?.data?.verifiableCredential;
} else {
const {data, result, error} = await verifier.post({json: body});
if(!result || !result.ok) {
error;
return null;
}
return data;
}
};

export const verifyPresentation = async ({
vpVerifier,
verifiablePresentation,
}) => {
const {settings: {options = {}}} = vpVerifier;
options.challenge = challenge;
const body = {verifiablePresentation, options};
if(vpVerifier.settings.zcap) {
const response = await makeZcapRequest(vpVerifier.settings, body);
return response?.data?.verifiableCredential;
} else {
const {data, result, error} = await vpVerifier.post({json: body});
if(!result || !result.ok) {
error;
return null;
}
return data;
}
return data;
};

export function generateCredential({
Expand Down Expand Up @@ -83,3 +127,6 @@ export function generateEnvelope({
};
return envelopeCredential;
}

export const secureEnvelope = async ({
}) => {};
29 changes: 29 additions & 0 deletions tests/zcapHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as Ed25519Multikey from '@digitalbazaar/ed25519-multikey';
import {decodeSecretKeySeed} from 'bnid';
import {Ed25519Signature2020} from '@digitalbazaar/ed25519-signature-2020';
import {ZcapClient} from '@digitalbazaar/ezcap';

export async function makeZcapRequest(settings, body) {
const capability = JSON.parse(settings.zcap.capability);
const controller = capability.controller;
// only supports did key
const id = controller + '#' + controller.slice('did:key:'.length);
const verificationKeyPair = await Ed25519Multikey.generate({
id,
controller,
seed: decodeSecretKeySeed({
secretKeySeed: process.env[settings.zcap.keySeed]
})
});
const zcapClient = new ZcapClient({
SuiteClass: Ed25519Signature2020,
invocationSigner: verificationKeyPair.signer(),
delegationSigner: verificationKeyPair.signer(),
});
const response = await zcapClient.write({
url: settings.endpoint,
json: body,
capability: JSON.parse(settings.zcap.capability)
});
return response;
}
Loading