Skip to content

Commit d7b4bf4

Browse files
PatStLouisaljones15
authored andcommitted
ecdsa-jcs algorithms and helpers
Signed-off-by: PatStLouis <[email protected]>
1 parent 89fc2a3 commit d7b4bf4

File tree

3 files changed

+310
-0
lines changed

3 files changed

+310
-0
lines changed

tests/90-algorithms-jcs.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/*!
2+
* Copyright 2024 Digital Bazaar, Inc.
3+
* SPDX-License-Identifier: BSD-3-Clause
4+
*/
5+
import {ecdsaJcs2019Algorithms} from './suites/algorithms-jcs.js';
6+
7+
ecdsaJcs2019Algorithms();

tests/helpers.js

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
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';
10+
import {readFileSync} from 'fs';
911
import {v4 as uuidv4} from 'uuid';
1012

1113
export const require = createRequire(import.meta.url);
@@ -216,3 +218,76 @@ export function setupReportableTestSuite(runnerContext, name) {
216218

217219
runnerContext.implemented = [];
218220
}
221+
222+
export function isValidUtf8(string) {
223+
const textEncoder = new TextEncoder();
224+
const uint8Array = textEncoder.encode(string);
225+
if(!isUtf8(uint8Array)) {
226+
return false;
227+
} else {
228+
return true;
229+
}
230+
}
231+
232+
export function isValidDatetime(dateString) {
233+
return !isNaN(Date.parse(dateString));
234+
}
235+
236+
export function setupMatrix(match) {
237+
// this will tell the report
238+
// to make an interop matrix with this suite
239+
this.matrix = true;
240+
this.report = true;
241+
this.implemented = [...match.keys()];
242+
this.rowLabel = 'Test Name';
243+
this.columnLabel = 'Implementer';
244+
}
245+
246+
export const config = JSON.parse(readFileSync('./config/runner.json'));
247+
248+
export function createValidCredential(version = 2) {
249+
let credential = {
250+
type: ['VerifiableCredential'],
251+
id: `urn:uuid:${uuidv4()}`,
252+
credentialSubject: {id: 'did:example:alice'}
253+
};
254+
if(version === 1) {
255+
// add v1.1 context and issuanceDate
256+
credential = Object.assign({}, {
257+
'@context': [
258+
'https://www.w3.org/2018/credentials/v1',
259+
'https://w3id.org/security/data-integrity/v2'
260+
],
261+
issuanceDate: ISOTimeStamp()
262+
}, credential);
263+
} else if(version === 2) {
264+
// add v2 context
265+
credential = Object.assign({}, {
266+
'@context': [
267+
'https://www.w3.org/ns/credentials/v2'
268+
]
269+
}, credential);
270+
} else {
271+
return null;
272+
}
273+
return credential;
274+
}
275+
276+
export function setupRow() {
277+
// append test meta data to the it/test this.
278+
this.currentTest.cell = {
279+
columnId: this.currentTest.parent.title,
280+
rowId: this.currentTest.title
281+
};
282+
}
283+
284+
export function getProofs(issuedVc) {
285+
// if the implementation failed to issue a VC or to sign the VC, return
286+
// an empty array
287+
if(!issuedVc?.proof) {
288+
return [];
289+
}
290+
const proofs = Array.isArray(issuedVc?.proof) ?
291+
issuedVc.proof : [issuedVc?.proof];
292+
return proofs;
293+
}

tests/suites/algorithms-jcs.js

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

0 commit comments

Comments
 (0)