Skip to content

Commit 13d0011

Browse files
committed
Add an algorithm test file and helper functions for ecdsa-jcs-2019
Signed-off-by: PatStLouis <[email protected]>
1 parent 492f6e5 commit 13d0011

File tree

2 files changed

+248
-0
lines changed

2 files changed

+248
-0
lines changed

tests/90-algorithms-jcs.js

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*!
2+
* Copyright 2024 Digital Bazaar, Inc.
3+
* SPDX-License-Identifier: BSD-3-Clause
4+
*/
5+
import {
6+
config,
7+
createInitialVc,
8+
createValidCredential,
9+
getProofs,
10+
isValidDatetime,
11+
isValidUtf8,
12+
setupReportableTestSuite,
13+
setupRow
14+
} from '../helpers.js';
15+
import chai from 'chai';
16+
import {endpoints} from 'vc-test-suite-implementations';
17+
18+
const should = chai.should();
19+
20+
const cryptosuite = 'ecdsa-jcs-2019';
21+
const {tags} = config.suites[
22+
cryptosuite
23+
];
24+
const {match: issuers} = endpoints.filterByTag({
25+
tags: [...tags],
26+
property: 'issuers'
27+
});
28+
29+
describe('ecdsa-jcs-2019 - Algorithms - Transformation', function() {
30+
setupReportableTestSuite(this);
31+
this.implemented = [...issuers.keys()];
32+
let validCredential;
33+
before(async function() {
34+
validCredential = await createValidCredential();
35+
});
36+
for(const [columnId, {endpoints}] of issuers) {
37+
describe(columnId, function() {
38+
const [issuer] = endpoints;
39+
let issuedVc;
40+
let proofs;
41+
let jcs2019Proofs = [];
42+
before(async function() {
43+
issuedVc = await createInitialVc({issuer, vc: validCredential});
44+
proofs = getProofs(issuedVc);
45+
if(proofs?.length) {
46+
jcs2019Proofs = proofs.filter(
47+
proof => proof?.cryptosuite === cryptosuite);
48+
}
49+
});
50+
beforeEach(setupRow);
51+
const assertBefore = () => {
52+
should.exist(issuedVc, 'Expected issuer to have issued a ' +
53+
'credential.');
54+
should.exist(proofs, 'Expected credential to have a proof.');
55+
jcs2019Proofs.length.should.be.gte(1, 'Expected at least one ' +
56+
'ecdsa-jcs-2019 cryptosuite.');
57+
};
58+
it('The proof options MUST contain a type identifier for the ' +
59+
'cryptographic suite (type) and MAY contain a cryptosuite ' +
60+
'identifier (cryptosuite).',
61+
async function() {
62+
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#proof-serialization-ecdsa-jcs-2019';
63+
assertBefore();
64+
for(const proof of jcs2019Proofs) {
65+
should.exist(proof.type,
66+
'Expected a type identifier on the proof.');
67+
}
68+
});
69+
it('The transformation options MUST contain a type identifier ' +
70+
'for the cryptographic suite (type) and a cryptosuite identifier ' +
71+
'(cryptosuite).',
72+
async function() {
73+
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#transformation-ecdsa-jcs-2019';
74+
assertBefore();
75+
for(const proof of jcs2019Proofs) {
76+
should.exist(proof.type, 'Expected a type identifier on ' +
77+
'the proof.');
78+
should.exist(proof.cryptosuite,
79+
'Expected a cryptosuite identifier on the proof.');
80+
}
81+
});
82+
it('Whenever this algorithm encodes strings, ' +
83+
'it MUST use UTF-8 encoding.',
84+
async function() {
85+
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#transformation-ecdsa-jcs-2019';
86+
assertBefore();
87+
for(const proof of jcs2019Proofs) {
88+
should.exist(proof?.proofValue,
89+
'Expected proofValue to exist.');
90+
isValidUtf8(proof.proofValue).should.equal(
91+
true,
92+
'Expected proofValue value to be a valid UTF-8 encoded string.'
93+
);
94+
}
95+
});
96+
it('If options.type is not set to the string DataIntegrityProof or ' +
97+
'options.cryptosuite is not set to the string ecdsa-jcs-2019, ' +
98+
'an error MUST be raised and SHOULD convey an error type ' +
99+
'of PROOF_TRANSFORMATION_ERROR.',
100+
async function() {
101+
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#transformation-ecdsa-jcs-2019';
102+
assertBefore();
103+
for(const proof of jcs2019Proofs) {
104+
should.exist(proof.type,
105+
'Expected a type identifier on the proof.');
106+
should.exist(proof.cryptosuite,
107+
'Expected a cryptosuite identifier on the proof.');
108+
proof.type.should.equal('DataIntegrityProof',
109+
'Expected DataIntegrityProof type.');
110+
proof.cryptosuite.should.equal('ecdsa-jcs-2019',
111+
'Expected ecdsa-jcs-2019 cryptosuite.');
112+
}
113+
});
114+
});
115+
}
116+
});
117+
118+
describe('ecdsa-jcs-2019 - Algorithms - Proof Configuration', function() {
119+
setupReportableTestSuite(this);
120+
this.implemented = [...issuers.keys()];
121+
let validCredential;
122+
before(async function() {
123+
validCredential = await createValidCredential();
124+
});
125+
for(const [columnId, {endpoints}] of issuers) {
126+
describe(columnId, function() {
127+
const [issuer] = endpoints;
128+
let issuedVc;
129+
let proofs;
130+
let jcs2019Proofs = [];
131+
before(async function() {
132+
issuedVc = await createInitialVc({issuer, vc: validCredential});
133+
proofs = getProofs(issuedVc);
134+
if(proofs?.length) {
135+
jcs2019Proofs = proofs.filter(
136+
proof => proof?.cryptosuite === cryptosuite);
137+
}
138+
});
139+
beforeEach(setupRow);
140+
const assertBefore = () => {
141+
should.exist(issuedVc, 'Expected issuer to have issued a ' +
142+
'credential.');
143+
should.exist(proofs, 'Expected credential to have a proof.');
144+
jcs2019Proofs.length.should.be.gte(1, 'Expected at least one ' +
145+
'ecdsa-jcs-2019 cryptosuite.');
146+
};
147+
it('The proof options MUST contain a type identifier for the ' +
148+
'cryptographic suite (type) and MUST contain a cryptosuite ' +
149+
'identifier (cryptosuite).',
150+
async function() {
151+
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#proof-configuration-ecdsa-jcs-2019';
152+
assertBefore();
153+
for(const proof of jcs2019Proofs) {
154+
should.exist(proof.type,
155+
'Expected a type identifier on the proof.');
156+
should.exist(proof.cryptosuite,
157+
'Expected a cryptosuite identifier on the proof.');
158+
}
159+
});
160+
it('If proofConfig.type is not set to DataIntegrityProof ' +
161+
'and/or proofConfig.cryptosuite is not set to ecdsa-jcs-2019, ' +
162+
'an error MUST be raised and SHOULD convey an error type ' +
163+
'of PROOF_GENERATION_ERROR.',
164+
async function() {
165+
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#proof-configuration-ecdsa-jcs-2019';
166+
assertBefore();
167+
for(const proof of jcs2019Proofs) {
168+
should.exist(proof.type,
169+
'Expected a type identifier on the proof.');
170+
should.exist(proof.cryptosuite,
171+
'Expected a cryptosuite identifier on the proof.');
172+
proof.type.should.equal('DataIntegrityProof',
173+
'Expected DataIntegrityProof type.');
174+
proof.cryptosuite.should.equal('ecdsa-jcs-2019',
175+
'Expected ecdsa-jcs-2019 cryptosuite.');
176+
}
177+
});
178+
it('If proofConfig.created is set and if the value is not a ' +
179+
'valid [XMLSCHEMA11-2] datetime, an error MUST be raised and ' +
180+
'SHOULD convey an error type of PROOF_GENERATION_ERROR.',
181+
async function() {
182+
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#proof-configuration-ecdsa-jcs-2019';
183+
for(const proof of jcs2019Proofs) {
184+
if(proof?.created) {
185+
isValidDatetime(proof.created).should.equal(
186+
true,
187+
'Expected created value to be a valid datetime string.'
188+
);
189+
}
190+
}
191+
});
192+
});
193+
}
194+
});

tests/helpers.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import * as bs58 from 'base58-universal';
66
import * as bs64 from 'base64url-universal';
77
import {createRequire} from 'node:module';
8+
import {isUtf8} from 'node:buffer';
89
import {klona} from 'klona';
910
import {v4 as uuidv4} from 'uuid';
1011

@@ -216,3 +217,56 @@ export function setupReportableTestSuite(runnerContext, name) {
216217

217218
runnerContext.implemented = [];
218219
}
220+
221+
export function isValidUtf8(string) {
222+
const textEncoder = new TextEncoder();
223+
const uint8Array = textEncoder.encode(string);
224+
if(!isUtf8(uint8Array)) {
225+
return false;
226+
} else {
227+
return true;
228+
}
229+
}
230+
231+
export function isValidDatetime(dateString) {
232+
return !isNaN(Date.parse(dateString));
233+
}
234+
235+
export function getProofs(issuedVc) {
236+
// if the implementation failed to issue a VC or to sign the VC,
237+
// return an empty array
238+
if(!issuedVc?.proof) {
239+
return [];
240+
}
241+
const proofs = Array.isArray(issuedVc?.proof) ?
242+
issuedVc.proof : [issuedVc?.proof];
243+
return proofs;
244+
}
245+
246+
export function createValidCredential(version = 2) {
247+
let credential = {
248+
type: ['VerifiableCredential'],
249+
id: `urn:uuid:${uuidv4()}`,
250+
credentialSubject: {id: 'did:example:alice'}
251+
};
252+
if(version === 1) {
253+
// add v1.1 context and issuanceDate
254+
credential = Object.assign({}, {
255+
'@context': [
256+
'https://www.w3.org/2018/credentials/v1',
257+
'https://w3id.org/security/data-integrity/v2'
258+
],
259+
issuanceDate: ISOTimeStamp()
260+
}, credential);
261+
} else if(version === 2) {
262+
// add v2 context
263+
credential = Object.assign({}, {
264+
'@context': [
265+
'https://www.w3.org/ns/credentials/v2'
266+
]
267+
}, credential);
268+
} else {
269+
return null;
270+
}
271+
return credential;
272+
}

0 commit comments

Comments
 (0)