From 32a8209e2324f9953cb191bdb1236d21e1d162ab Mon Sep 17 00:00:00 2001 From: PatStLouis Date: Tue, 19 Nov 2024 06:57:22 +0000 Subject: [PATCH 1/4] Add proof representation test block Signed-off-by: PatStLouis --- tests/70-data-model.js | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 tests/70-data-model.js diff --git a/tests/70-data-model.js b/tests/70-data-model.js new file mode 100644 index 0000000..45fc870 --- /dev/null +++ b/tests/70-data-model.js @@ -0,0 +1,71 @@ +/*! + * Copyright 2024 Digital Bazaar, Inc. + * SPDX-License-Identifier: BSD-3-Clause + */ +import { + createInitialVc, + createValidCredential, + getProofs, + setupReportableTestSuite, + setupRow +} from './helpers.js'; +import chai from 'chai'; +import {endpoints} from 'vc-test-suite-implementations'; + +const should = chai.should(); + +const cryptosuites = [ + 'ecdsa-sd-2023', + 'ecdsa-jcs-2019', + 'ecdsa-rdfc-2019', +]; + +const {match: issuers} = endpoints.filterByTag({ + tags: cryptosuites, + property: 'issuers' +}); + +describe('Data Model - Proof Representations', function() { + setupReportableTestSuite(this); + this.implemented = [...issuers.keys()]; + let validCredential; + before(async function() { + validCredential = await createValidCredential(); + }); + for(const [columnId, {endpoints}] of issuers) { + describe(columnId, function() { + const [issuer] = endpoints; + let issuedVc; + let proofs; + let ecdsaProofs = []; + before(async function() { + issuedVc = await createInitialVc({issuer, vc: validCredential}); + proofs = getProofs(issuedVc); + if(proofs?.length) { + ecdsaProofs = proofs.filter( + proof => cryptosuites.includes(proof?.cryptosuite)); + } + }); + beforeEach(setupRow); + const assertBefore = () => { + should.exist(issuedVc, + 'Expected issuer to have issued a credential.'); + should.exist(proofs, + 'Expected credential to have a proof.'); + ecdsaProofs.length.should.be.gte(1, + 'Expected at least one ecdsa cryptosuite.'); + }; + it('The type property MUST be DataIntegrityProof.', + async function() { + this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; + assertBefore(); + }); + it('The cryptosuite property MUST be ecdsa-rdfc-2019, ' + + 'ecdsa-jcs-2019, or ecdsa-sd-2023.', + async function() { + this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; + assertBefore(); + }); + }); + } +}); From 8a870fc81ba898c723187bf3e7e2cf30a87a2ab7 Mon Sep 17 00:00:00 2001 From: PatStLouis Date: Tue, 19 Nov 2024 16:26:57 +0000 Subject: [PATCH 2/4] add data model tests Signed-off-by: PatStLouis --- tests/70-data-model.js | 54 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/tests/70-data-model.js b/tests/70-data-model.js index 45fc870..693dc96 100644 --- a/tests/70-data-model.js +++ b/tests/70-data-model.js @@ -3,6 +3,7 @@ * SPDX-License-Identifier: BSD-3-Clause */ import { + assertIssuedVc, createInitialVc, createValidCredential, getProofs, @@ -25,6 +26,47 @@ const {match: issuers} = endpoints.filterByTag({ property: 'issuers' }); +describe('Data Model - Verification Methods (Multikey)', function() { + setupReportableTestSuite(this); + this.implemented = [...issuers.keys()]; + let validCredential; + before(async function() { + validCredential = await createValidCredential(); + }); + for(const [columnId, {endpoints}] of issuers) { + describe(columnId, function() { + const [issuer] = endpoints; + let issuedVc; + let proofs; + let ecdsaProofs = []; + before(async function() { + issuedVc = await createInitialVc({issuer, vc: validCredential}); + proofs = getProofs(issuedVc); + if(proofs?.length) { + ecdsaProofs = proofs.filter( + proof => cryptosuites.includes(proof?.cryptosuite)); + } + }); + beforeEach(setupRow); + it('The publicKeyMultibase value of the verification method ' + + 'MUST start with the base-58-btc prefix (z), as defined in ' + + 'the Multibase section of Controller Documents 1.0.', + async function() { + this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; + assertIssuedVc(issuedVc, proofs, ecdsaProofs); + }); + it('A Multibase-encoded ECDSA 256-bit public key value or an ' + + 'ECDSA 384-bit public key value follows, as defined in the Multikey ' + + 'section of Controller Documents 1.0. Any other encoding ' + + 'MUST NOT be allowed.', + async function() { + this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; + assertIssuedVc(issuedVc, proofs, ecdsaProofs); + }); + }); + } +}); + describe('Data Model - Proof Representations', function() { setupReportableTestSuite(this); this.implemented = [...issuers.keys()]; @@ -47,24 +89,16 @@ describe('Data Model - Proof Representations', function() { } }); beforeEach(setupRow); - const assertBefore = () => { - should.exist(issuedVc, - 'Expected issuer to have issued a credential.'); - should.exist(proofs, - 'Expected credential to have a proof.'); - ecdsaProofs.length.should.be.gte(1, - 'Expected at least one ecdsa cryptosuite.'); - }; it('The type property MUST be DataIntegrityProof.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; - assertBefore(); + assertIssuedVc(issuedVc, proofs, ecdsaProofs); }); it('The cryptosuite property MUST be ecdsa-rdfc-2019, ' + 'ecdsa-jcs-2019, or ecdsa-sd-2023.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; - assertBefore(); + assertIssuedVc(issuedVc, proofs, ecdsaProofs); }); }); } From 6a913085c2d0463192990ccf776d3334c282ba0f Mon Sep 17 00:00:00 2001 From: PatStLouis Date: Wed, 20 Nov 2024 21:22:10 +0000 Subject: [PATCH 3/4] add data model testing Signed-off-by: PatStLouis --- package.json | 2 +- tests/70-data-model.js | 81 ++++++++++++++++++++++---------------- tests/90-algorithms-jcs.js | 2 +- tests/assertions.js | 4 +- tests/helpers.js | 31 +++++++++++++++ 5 files changed, 80 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 6e2c091..45743a6 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "chai": "^4.3.7", "chai-string": "^1.5.0", "data-integrity-test-suite-assertion": "github:w3c-ccg/data-integrity-test-suite-assertion", - "jsonld-document-loader": "^2.0.0", + "jsonld-document-loader": "^2.2.0", "json-canon": "^1.0.1", "klona": "^2.0.6", "multibase": "^4.0.6", diff --git a/tests/70-data-model.js b/tests/70-data-model.js index 693dc96..7f7eaf9 100644 --- a/tests/70-data-model.js +++ b/tests/70-data-model.js @@ -3,15 +3,19 @@ * SPDX-License-Identifier: BSD-3-Clause */ import { - assertIssuedVc, - createInitialVc, - createValidCredential, - getProofs, + generateCredential, + multikeyFromVerificationMethod, + proofExists, + secureCredential, setupReportableTestSuite, setupRow } from './helpers.js'; +import { + assertDataIntegrityProof +} from './assertions.js'; import chai from 'chai'; import {endpoints} from 'vc-test-suite-implementations'; +import {expect} from 'chai'; const should = chai.should(); @@ -29,23 +33,13 @@ const {match: issuers} = endpoints.filterByTag({ describe('Data Model - Verification Methods (Multikey)', function() { setupReportableTestSuite(this); this.implemented = [...issuers.keys()]; - let validCredential; - before(async function() { - validCredential = await createValidCredential(); - }); for(const [columnId, {endpoints}] of issuers) { describe(columnId, function() { const [issuer] = endpoints; - let issuedVc; - let proofs; - let ecdsaProofs = []; + let securedCredential; before(async function() { - issuedVc = await createInitialVc({issuer, vc: validCredential}); - proofs = getProofs(issuedVc); - if(proofs?.length) { - ecdsaProofs = proofs.filter( - proof => cryptosuites.includes(proof?.cryptosuite)); - } + securedCredential = await secureCredential( + {issuer, vc: generateCredential()}); }); beforeEach(setupRow); it('The publicKeyMultibase value of the verification method ' + @@ -53,7 +47,13 @@ describe('Data Model - Verification Methods (Multikey)', function() { 'the Multibase section of Controller Documents 1.0.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; - assertIssuedVc(issuedVc, proofs, ecdsaProofs); + const proof = proofExists(securedCredential); + const verificationMethod = proof.verificationMethod; + // Only did key is supported + const keyType = issuer.settings.supportedEcdsaKeyTypes[0]; + const multikey = + await multikeyFromVerificationMethod(verificationMethod, keyType); + expect(multikey.startsWith('z')).to.be.true; }); it('A Multibase-encoded ECDSA 256-bit public key value or an ' + 'ECDSA 384-bit public key value follows, as defined in the Multikey ' + @@ -61,7 +61,13 @@ describe('Data Model - Verification Methods (Multikey)', function() { 'MUST NOT be allowed.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; - assertIssuedVc(issuedVc, proofs, ecdsaProofs); + const proof = proofExists(securedCredential); + const verificationMethod = proof.verificationMethod; + // Only did key is supported + const keyType = issuer.settings.supportedEcdsaKeyTypes[0]; + const multikey = + await multikeyFromVerificationMethod(verificationMethod, keyType); + expect(multikey).to.be.exist; }); }); } @@ -70,35 +76,40 @@ describe('Data Model - Verification Methods (Multikey)', function() { describe('Data Model - Proof Representations', function() { setupReportableTestSuite(this); this.implemented = [...issuers.keys()]; - let validCredential; - before(async function() { - validCredential = await createValidCredential(); - }); for(const [columnId, {endpoints}] of issuers) { describe(columnId, function() { const [issuer] = endpoints; - let issuedVc; - let proofs; - let ecdsaProofs = []; + let securedCredential; before(async function() { - issuedVc = await createInitialVc({issuer, vc: validCredential}); - proofs = getProofs(issuedVc); - if(proofs?.length) { - ecdsaProofs = proofs.filter( - proof => cryptosuites.includes(proof?.cryptosuite)); - } + securedCredential = await secureCredential( + {issuer, vc: generateCredential()}); }); beforeEach(setupRow); + it('A proof contains the attributes specified in the ' + + 'Proofs section of [VC-DATA-INTEGRITY].', + async function() { + this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; + const proof = proofExists(securedCredential); + assertDataIntegrityProof(proof); + }); it('The type property MUST be DataIntegrityProof.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; - assertIssuedVc(issuedVc, proofs, ecdsaProofs); + const proof = proofExists(securedCredential); + should.exist(proof.type, + 'Expected a type on the proof.'); + proof.type.should.equal('DataIntegrityProof', + 'Expected DataIntegrityProof type.'); }); it('The cryptosuite property MUST be ecdsa-rdfc-2019, ' + - 'ecdsa-jcs-2019, or ecdsa-sd-2023.', + 'ecdsa-jcs-2019, or ecdsa-sd-2023.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof'; - assertIssuedVc(issuedVc, proofs, ecdsaProofs); + const proof = proofExists(securedCredential); + should.exist(proof.cryptosuite, + 'Expected a cryptosuite identifier on the proof.'); + proof.cryptosuite.should.be.oneOf(cryptosuites, + `Expected cryptosuite for be one of ${cryptosuites}.`); }); }); } diff --git a/tests/90-algorithms-jcs.js b/tests/90-algorithms-jcs.js index 4ac1812..be69bfe 100644 --- a/tests/90-algorithms-jcs.js +++ b/tests/90-algorithms-jcs.js @@ -54,7 +54,7 @@ describe('Algorithms - Create Proof (ecdsa-jcs-2019)', function() { async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#create-proof-ecdsa-jcs-2019'; const proof = proofExists(securedCredential); - assertDataIntegrityProof(proof, 'ecdsa-jcs-2019'); + assertDataIntegrityProof(proof); // Since we are not sending proof options, we only do a positive test }); it('If unsecuredDocument.@context is present, ' + diff --git a/tests/assertions.js b/tests/assertions.js index 0c4ff09..e7869fb 100644 --- a/tests/assertions.js +++ b/tests/assertions.js @@ -205,7 +205,7 @@ export function assertAllUtf8(proof) { } } -export function assertDataIntegrityProof(proof, cryptosuite) { +export function assertDataIntegrityProof(proof) { if(proof?.id) { } should.exist(proof.type, @@ -230,8 +230,6 @@ export function assertDataIntegrityProof(proof, cryptosuite) { } should.exist(proof.cryptosuite, 'Expected a cryptosuite identifier on the proof.'); - proof.cryptosuite.should.equal(cryptosuite, - `Expected {cryptosuite} cryptosuite.`); isValidUtf8(proof.cryptosuite).should.equal( true, 'Expected cryptosuite value to be a valid UTF-8 encoded string.' diff --git a/tests/helpers.js b/tests/helpers.js index 4ee6446..0d946ca 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -4,9 +4,14 @@ */ import * as bs58 from 'base58-universal'; import * as bs64 from 'base64url-universal'; +import * as didKey from '@digitalbazaar/did-method-key'; +import * as EcdsaMultikey from '@digitalbazaar/ecdsa-multikey'; +import {CachedResolver} from '@digitalbazaar/did-io'; import chai from 'chai'; import {createRequire} from 'node:module'; +import {contexts as credContexts} from '@digitalbazaar/credentials-context'; import {isUtf8} from 'node:buffer'; +import {JsonLdDocumentLoader} from 'jsonld-document-loader'; import {klona} from 'klona'; import {readFileSync} from 'fs'; import {v4 as uuidv4} from 'uuid'; @@ -313,3 +318,29 @@ export async function verifyError(verifier, securedCredential) { const response = await verifier.post({json: body}); should.exist(response.error, 'Expected an error from verifier.'); } + +export async function multikeyFromVerificationMethod( + verificationMethod, keyType) { + const cachedResolver = new CachedResolver(); + const jdl = new JsonLdDocumentLoader(); + jdl.addDocuments({documents: credContexts}); + jdl.setDidResolver(cachedResolver); + const didKeyDriverMultikey = didKey.driver(); + const prefixes = { + Ed25519: 'z6Mk', + 'P-256': 'zDna', + 'P-384': 'z82L' + }; + try { + didKeyDriverMultikey.use({ + multibaseMultikeyHeader: prefixes[keyType], + fromMultibase: EcdsaMultikey.from + }); + cachedResolver.use(didKeyDriverMultikey); + const response = await jdl.documentLoader(verificationMethod); + return response.document.publicKeyMultibase; + } catch(error) { + // Do nothing on error + } + return null; +} From 86a184414d6fa4975799a64a055723aeca8db948 Mon Sep 17 00:00:00 2001 From: PatStLouis Date: Wed, 20 Nov 2024 21:23:40 +0000 Subject: [PATCH 4/4] fix typo Signed-off-by: PatStLouis --- tests/70-data-model.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/70-data-model.js b/tests/70-data-model.js index 7f7eaf9..2d8d79b 100644 --- a/tests/70-data-model.js +++ b/tests/70-data-model.js @@ -67,7 +67,7 @@ describe('Data Model - Verification Methods (Multikey)', function() { const keyType = issuer.settings.supportedEcdsaKeyTypes[0]; const multikey = await multikeyFromVerificationMethod(verificationMethod, keyType); - expect(multikey).to.be.exist; + expect(multikey).to.exist; }); }); }