Skip to content

Commit 2493c9c

Browse files
PatStLouisBigBlueHat
authored andcommitted
add negative test generation and helper functions
Signed-off-by: PatStLouis <[email protected]>
1 parent 2aa135d commit 2493c9c

7 files changed

+199
-107
lines changed

tests/4.13.2-envelopes.js

Lines changed: 127 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,45 @@
44
* SPDX-License-Identifier: LicenseRef-w3c-3-clause-bsd-license-2008 OR LicenseRef-w3c-test-suite-license-2023
55
*/
66

7-
import {addPerTestMetadata, extractIfEnveloped, setupMatrix}
8-
from './helpers.js';
7+
import {
8+
addPerTestMetadata,
9+
generateCredential,
10+
generateEnvelope,
11+
secureCredential,
12+
setupMatrix
13+
} from './helpers.js';
14+
import {
15+
vc_jwt,
16+
vp_jwt
17+
} from './fixtures.js';
918
import assert from 'node:assert/strict';
1019
import chai from 'chai';
11-
import {createRequire} from 'module';
1220
import {filterByTag} from 'vc-test-suite-implementations';
13-
import {shouldBeCredential} from './assertions.js';
1421
import {TestEndpoints} from './TestEndpoints.js';
1522

16-
// eslint-disable-next-line no-unused-vars
1723
const should = chai.should();
1824

19-
const require = createRequire(import.meta.url);
20-
21-
const tag = 'vc2.0';
25+
const tag = 'EnvelopingProof';
2226
const {match} = filterByTag({tags: [tag]});
2327

2428
// 4.12.1 Enveloped Verifiable Credentials https://w3c.github.io/vc-data-model/#enveloped-verifiable-credentials
25-
describe('VP - Enveloped Verifiable Credentials', function() {
29+
describe('Enveloped Verifiable Credentials', function() {
2630
setupMatrix.call(this, match);
2731
for(const [name, implementation] of match) {
2832
const endpoints = new TestEndpoints({implementation, tag});
29-
const issuerEnvelopeSupport =
30-
endpoints.issuer.settings.tags.includes(
31-
'EnvelopingProof');
32-
const vpVerifierEnvelopeSupport = endpoints.vpVerifier &&
33-
endpoints.vpVerifier.settings.tags.includes(
34-
'EnvelopingProof');
33+
const issuer = implementation.issuers?.find(
34+
issuer => issuer.tags.has(tag)) || null;
35+
const verifier = implementation.verifiers?.find(
36+
verifier => verifier.tags.has(tag)) || null;
3537

3638
describe(name, function() {
37-
let issuedVc;
39+
let envelopedCredential;
40+
let negativeFixture;
3841
before(async function() {
39-
if(issuerEnvelopeSupport) {
40-
try {
41-
issuedVc = await endpoints.issue(require(
42-
'./input/credential-ok.json'));
43-
} catch(e) {
44-
console.error(
45-
`Issuer: ${name} failed to issue "credential-ok.json".`,
46-
e
47-
);
48-
}
49-
} else {
50-
issuedVc = null;
51-
}
42+
envelopedCredential = generateEnvelope({
43+
type: 'EnvelopedVerifiableCredential',
44+
id: `data:application/vc+jwt,${vc_jwt}`
45+
});
5246
});
5347
beforeEach(addPerTestMetadata);
5448

@@ -58,18 +52,27 @@ describe('VP - Enveloped Verifiable Credentials', function() {
5852
'terms as defined by the base context provided by this specification.',
5953
async function() {
6054
this.test.link = `https://w3c.github.io/vc-data-model/#enveloped-verifiable-credentials:~: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%20EnvelopedVerifiableCredential%20terms%20as%20defined%20by%20the%20base%20context%20provided%20by%20this%20specification.`;
61-
if(!vpVerifierEnvelopeSupport) {
62-
this.test.cell.skipMessage = 'No envelope support.';
63-
this.skip();
64-
} else {
65-
await assert.doesNotReject(endpoints.verifyVp(require(
66-
'./input/presentation-enveloped-vc-ok.json')),
67-
'Failed to accept a VP containing a enveloped VC.');
68-
// TODO: add more `@context` variations to test handling?
69-
await assert.rejects(endpoints.verifyVp(require(
70-
'./input/presentation-enveloped-vc-missing-type-fail.json')),
71-
'Failed to reject a VP containing an enveloped VC with a missing ' +
72-
'`type`.');
55+
if(issuer) {
56+
const issuedVc = await secureCredential(
57+
{issuer, credential: generateCredential()});
58+
should.exist(issuedVc, 'Expected credential to be issued.');
59+
issuedVc.should.have.property('@context');
60+
}
61+
if(verifier) {
62+
await assert.doesNotReject(endpoints.verify(envelopedCredential),
63+
'Failed to accept an enveloped VC.');
64+
65+
// Replace context with an empty array
66+
negativeFixture = structuredClone(envelopedCredential);
67+
negativeFixture['@context'] = [];
68+
await assert.rejects(endpoints.verify(negativeFixture),
69+
'Failed to reject an enveloped VC with an empty context.');
70+
71+
// Replace context with an invalid value
72+
negativeFixture = structuredClone(envelopedCredential);
73+
negativeFixture['@context'] = 'https://www.w3.org/ns/credentials/examples/v2';
74+
await assert.rejects(endpoints.verify(negativeFixture),
75+
'Failed to reject an enveloped VC with an invalid context.');
7376
}
7477
});
7578

@@ -78,44 +81,77 @@ describe('VP - Enveloped Verifiable Credentials', function() {
7881
'security scheme, such as Securing Verifiable Credentials using JOSE ' +
7982
'and COSE [VC-JOSE-COSE].', async function() {
8083
this.test.link = `https://w3c.github.io/vc-data-model/#enveloped-verifiable-credentials:~:text=The%20id%20value%20of%20the%20object%20MUST%20be%20a%20data%3A%20URL%20%5BRFC2397%5D%20that%20expresses%20a%20secured%20verifiable%20credential%20using%20an%20enveloping%20security%20scheme%2C%20such%20as%20Securing%20Verifiable%20Credentials%20using%20JOSE%20and%20COSE%20%5BVC%2DJOSE%2DCOSE%5D.`;
81-
if(!issuerEnvelopeSupport) {
82-
this.test.cell.skipMessage = 'No envelope support.';
83-
this.skip();
84-
} else {
84+
if(issuer) {
85+
const issuedVc = await secureCredential(
86+
{issuer, credential: generateCredential()});
87+
should.exist(issuedVc, 'Expected credential to be issued.');
8588
issuedVc.should.have.property('id').that.does
8689
.include('data:',
8790
`Expecting id field to be a 'data:' scheme URL [RFC2397].`);
88-
const extractedCredential = extractIfEnveloped(issuedVc);
89-
shouldBeCredential(extractedCredential);
91+
}
92+
if(verifier) {
93+
await assert.doesNotReject(endpoints.verify(envelopedCredential),
94+
'Failed to accept an enveloped VC.');
95+
96+
// Remove data uri portion of the id field
97+
negativeFixture = structuredClone(envelopedCredential);
98+
negativeFixture.id = negativeFixture.id.split(',').pop();
99+
await assert.rejects(endpoints.verify(negativeFixture),
100+
'Failed to reject an enveloped VC with an invalid data url id.');
90101
}
91102
});
92103

93104
it('The type value of the object MUST be EnvelopedVerifiableCredential.',
94105
async function() {
95106
this.test.link = `https://w3c.github.io/vc-data-model/#enveloped-verifiable-credentials:~:text=The%20type%20value%20of%20the%20object%20MUST%20be%20EnvelopedVerifiableCredential.`;
96-
if(!issuerEnvelopeSupport) {
97-
this.test.cell.skipMessage = 'No envelope support.';
98-
this.skip();
99-
} else {
100-
issuedVc.should.have.property('type').that.does
101-
.include('EnvelopedVerifiableCredential',
102-
`Expecting type field to be EnvelopedVerifiableCredential`);
107+
if(issuer) {
108+
const issuedVc = await secureCredential(
109+
{issuer, credential: generateCredential()});
110+
should.exist(issuedVc, 'Expected credential to be issued.');
111+
issuedVc.should.have.property('type').that.is.equal(
112+
'EnvelopedVerifiableCredential',
113+
`Expecting type field to be EnvelopedVerifiableCredential`);
114+
}
115+
if(verifier) {
116+
await assert.doesNotReject(endpoints.verify(envelopedCredential),
117+
'Failed to accept an enveloped VC.');
118+
119+
// Remove type field
120+
negativeFixture = structuredClone(envelopedCredential);
121+
delete negativeFixture.type;
122+
await assert.rejects(endpoints.verify(negativeFixture),
123+
'Failed to reject an enveloped VC with an enveloped VC with a ' +
124+
'missing `type`.');
125+
126+
// Replace type field
127+
negativeFixture = structuredClone(envelopedCredential);
128+
negativeFixture.type = ['VerifiableCredential'];
129+
await assert.rejects(endpoints.verify(negativeFixture),
130+
'Failed to reject an enveloped VC with an ' +
131+
'invalid `type`.');
103132
}
104133
});
105134
});
106135
}
107136
});
108137

109138
// 4.12.2 Enveloped Verifiable Presentations https://w3c.github.io/vc-data-model/#enveloped-verifiable-presentations
110-
describe('VP - Enveloped Verifiable Presentations', function() {
139+
describe('Enveloped Verifiable Presentations', function() {
111140
setupMatrix.call(this, match);
112141
for(const [name, implementation] of match) {
113142
const endpoints = new TestEndpoints({implementation, tag});
114-
const vpVerifierEnvelopeSupport = endpoints.vpVerifier &&
115-
endpoints.vpVerifier.settings.tags.includes(
116-
'EnvelopingProof');
143+
const vpVerifier = implementation.vpVerifiers?.find(
144+
vpVerifier => vpVerifier.tags.has(tag)) || null;
117145

118146
describe(name, function() {
147+
let envelopedPresentation;
148+
let negativeFixture;
149+
before(async function() {
150+
envelopedPresentation = generateEnvelope({
151+
type: 'EnvelopedVerifiablePresentation',
152+
id: `data:application/vp+jwt,${vp_jwt}`
153+
});
154+
});
119155
beforeEach(addPerTestMetadata);
120156

121157
it('The @context property of the object MUST be present and include a ' +
@@ -124,14 +160,23 @@ describe('VP - Enveloped Verifiable Presentations', function() {
124160
'terms as defined by the base context provided by this specification.',
125161
async function() {
126162
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.`;
127-
if(!vpVerifierEnvelopeSupport) {
128-
this.test.cell.skipMessage = 'No envelope support.';
129-
this.skip();
130-
} else {
163+
164+
if(vpVerifier) {
165+
await assert.doesNotReject(endpoints.verifyVp(envelopedPresentation),
166+
'Failed to accept an enveloped VP.');
167+
168+
// Replace context field with empty array
169+
negativeFixture = structuredClone(envelopedPresentation);
170+
negativeFixture['@context'] = [];
131171
await assert.rejects(
132-
endpoints.verifyVp(require(
133-
'./input/enveloped-presentation-context-fail.json')),
172+
endpoints.verifyVp(negativeFixture),
173+
'Failed to reject Enveloped VP missing contexts.');
134174

175+
// Replace context field with invalid context
176+
negativeFixture = structuredClone(envelopedPresentation);
177+
negativeFixture['@context'] = ['https://www.w3.org/ns/credentials/examples/v2'];
178+
await assert.rejects(
179+
endpoints.verifyVp(negativeFixture),
135180
'Failed to reject Enveloped VP missing contexts.');
136181
}
137182
});
@@ -141,29 +186,33 @@ describe('VP - Enveloped Verifiable Presentations', function() {
141186
'securing mechanism, such as Securing Verifiable Credentials using ' +
142187
'JOSE and COSE [VC-JOSE-COSE].', async function() {
143188
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.`;
144-
if(!vpVerifierEnvelopeSupport) {
145-
this.test.cell.skipMessage = 'No envelope support.';
146-
this.skip();
147-
} else {
148-
await assert.rejects(
149-
endpoints.verifyVp(require(
150-
'./input/enveloped-presentation-id-fail.json')),
151189

190+
if(vpVerifier) {
191+
await assert.doesNotReject(endpoints.verifyVp(envelopedPresentation),
192+
'Failed to accept an enveloped VP.');
193+
194+
// Remove data uri portion from id field
195+
negativeFixture = structuredClone(envelopedPresentation);
196+
negativeFixture.id = negativeFixture.id.split(',').pop();
197+
await assert.rejects(
198+
endpoints.verifyVp(negativeFixture),
152199
'Failed to reject Enveloped VP with an id that is not a data url.');
153200
}
154201
});
155202

156203
it('The type value of the object MUST be ' +
157204
'EnvelopedVerifiablePresentation.', async function() {
158205
this.test.link = `https://w3c.github.io/vc-data-model/#enveloped-verifiable-presentations:~:text=The%20type%20value%20of%20the%20object%20MUST%20be%20EnvelopedVerifiablePresentation.`;
159-
if(!vpVerifierEnvelopeSupport) {
160-
this.test.cell.skipMessage = 'No envelope support.';
161-
this.skip();
162-
} else {
163-
await assert.rejects(
164-
endpoints.verifyVp(require(
165-
'./input/enveloped-presentation-type-fail.json')),
166206

207+
if(vpVerifier) {
208+
await assert.doesNotReject(endpoints.verifyVp(envelopedPresentation),
209+
'Failed to accept an enveloped VP.');
210+
211+
// Replace type field
212+
negativeFixture = structuredClone(envelopedPresentation);
213+
negativeFixture.type = ['VerifiablePresentation'];
214+
await assert.rejects(
215+
endpoints.verifyVp(negativeFixture),
167216
'Failed to reject VP w/o type "EnvelopedVerifiablePresentation".');
168217
}
169218
});

tests/fixtures.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/* eslint-disable max-len */
2+
export const vc_jwt = 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvdjIiLCJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjIiXSwiaWQiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMzczMiIsInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJVbml2ZXJzaXR5RGVncmVlQ3JlZGVudGlhbCJdLCJpc3N1ZXIiOiJodHRwczovL2V4YW1wbGUuZWR1L2lzc3VlcnMvNTY1MDQ5IiwidmFsaWRGcm9tIjoiMjAxMC0wMS0wMVQwMDowMDowMFoiLCJjcmVkZW50aWFsU3ViamVjdCI6eyJpZCI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSIsImRlZ3JlZSI6eyJ0eXBlIjoiQmFjaGVsb3JEZWdyZWUiLCJuYW1lIjoiQmFjaGVsb3Igb2YgU2NpZW5jZSBhbmQgQXJ0cyJ9fX0sImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5lZHUvaXNzdWVycy81NjUwNDkiLCJqdGkiOiJodHRwOi8vZXhhbXBsZS5lZHUvY3JlZGVudGlhbHMvMzczMiIsInN1YiI6ImRpZDpleGFtcGxlOmViZmViMWY3MTJlYmM2ZjFjMjc2ZTEyZWMyMSJ9.0fkQPZOKlD0Sl0A798KBUNMOdGq90McQQIEtKU9tgSd9K1kRcxWVKDXQJcn_FJqLvo2bk793EHk-RTeEL1HyAQ';
3+
export const vp_jwt = 'eyJraWQiOiJFeEhrQk1XOWZtYmt2VjI2Nm1ScHVQMnNVWV9OX0VXSU4xbGFwVXpPOHJvIiwiYWxnIjoiRVMzODQifQ.eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvdjIiLCJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvZXhhbXBsZXMvdjIiXSwiaWQiOiJodHRwOi8vdW5pdmVyc2l0eS5leGFtcGxlL2NyZWRlbnRpYWxzLzE4NzIiLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIiwiRXhhbXBsZUFsdW1uaUNyZWRlbnRpYWwiXSwiaXNzdWVyIjoiaHR0cHM6Ly91bml2ZXJzaXR5LmV4YW1wbGUvaXNzdWVycy81NjUwNDkiLCJ2YWxpZEZyb20iOiIyMDEwLTAxLTAxVDE5OjIzOjI0WiIsImNyZWRlbnRpYWxTY2hlbWEiOnsiaWQiOiJodHRwczovL2V4YW1wbGUub3JnL2V4YW1wbGVzL2RlZ3JlZS5qc29uIiwidHlwZSI6Ikpzb25TY2hlbWEifSwiY3JlZGVudGlhbFN1YmplY3QiOnsiaWQiOiJkaWQ6ZXhhbXBsZToxMjMiLCJkZWdyZWUiOnsidHlwZSI6IkJhY2hlbG9yRGVncmVlIiwibmFtZSI6IkJhY2hlbG9yIG9mIFNjaWVuY2UgYW5kIEFydHMifX19.d2k4O3FytQJf83kLh-HsXuPvh6yeOlhJELVo5TF71gu7elslQyOf2ZItAXrtbXF4Kz9WivNdztOayz4VUQ0Mwa8yCDZkP9B2pH-9S_tcAFxeoeJ6Z4XnFuL_DOfkR1fP';
4+
export const credential = {
5+
'@context': ['https://www.w3.org/ns/credentials/v2'],
6+
type: ['VerifiableCredential'],
7+
credentialSubject: {
8+
name: 'Alice'
9+
}
10+
};
11+
export const envelopedCredential = {
12+
'@context': 'https://www.w3.org/ns/credentials/v2',
13+
type: 'EnvelopedVerifiableCredential',
14+
id: `data:application/vc+jwt,${vc_jwt}`
15+
};
16+
export const presentation = {
17+
'@context': ['https://www.w3.org/ns/credentials/v2'],
18+
type: ['VerifiablePresentation']
19+
};
20+
export const envelopedPresentation = {
21+
'@context': 'https://www.w3.org/ns/credentials/v2',
22+
type: 'EnvelopedVerifiablePresentation',
23+
id: `data:application/vp+jwt,${vp_jwt}`
24+
};

tests/helpers.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,51 @@ export function extractIfEnveloped(input) {
3636
return input;
3737
}
3838
}
39+
40+
export const secureCredential = async ({
41+
issuer,
42+
credential,
43+
}) => {
44+
const {settings: {id: issuerId, options = {}}} = issuer;
45+
credential.issuer = issuerId;
46+
const body = {credential, options};
47+
const {data, result, error} = await issuer.post({json: body});
48+
if(!result || !result.ok) {
49+
// console.warn(
50+
// `initial vc creation failed for ${(result || error)?.requestUrl}`,
51+
// error,
52+
// JSON.stringify(data, null, 2)
53+
// );
54+
return null;
55+
}
56+
return data;
57+
};
58+
59+
export function generateCredential({
60+
context = ['https://www.w3.org/ns/credentials/v2'],
61+
type = ['VerifiableCredential'],
62+
credentialSubject = {
63+
id: 'did:example:alice',
64+
name: 'Alice'
65+
}
66+
} = {}) {
67+
const credential = {
68+
'@context': context,
69+
type,
70+
credentialSubject
71+
};
72+
return credential;
73+
}
74+
75+
export function generateEnvelope({
76+
context = 'https://www.w3.org/ns/credentials/v2',
77+
type,
78+
id
79+
} = {}) {
80+
const envelopeCredential = {
81+
'@context': context,
82+
type,
83+
id
84+
};
85+
return envelopeCredential;
86+
}

tests/input/enveloped-presentation-context-fail.json

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/input/enveloped-presentation-id-fail.json

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)