From 8c034d20abc2cf7d6be5a7f6be5b90c12b11b2cd Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 18 Sep 2024 14:26:08 +0000 Subject: [PATCH 1/5] Default optional tests to false. --- suites/create.js | 6 +++--- suites/verify.js | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/suites/create.js b/suites/create.js index e90276c..70e0611 100644 --- a/suites/create.js +++ b/suites/create.js @@ -18,9 +18,9 @@ export function runDataIntegrityProofFormatTests({ endpoints, expectedProofTypes, testDescription, vendorName, cryptosuiteName, credential, optionalTests = { - dates: true, - contextInjection: true, - domain: true + dates: false, + contextInjection: false, + domain: false } }) { return describe(testDescription, function() { diff --git a/suites/verify.js b/suites/verify.js index bb6709f..3f07f0b 100644 --- a/suites/verify.js +++ b/suites/verify.js @@ -10,7 +10,10 @@ export function runDataIntegrityProofVerifyTests({ testDescription, vendorName, credentials, - optionalTests + optionalTests = { + dates: false, + authentication: false + } }) { return describe(testDescription, function() { const [verifier] = endpoints; From d441c16e9ea7d0fc201fb9018244de0e90d74f16 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 18 Sep 2024 14:47:01 +0000 Subject: [PATCH 2/5] Remove redudant defaults for optional tests. --- index.js | 10 +++++----- suites/create.js | 6 +----- suites/verify.js | 5 +---- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/index.js b/index.js index 0fe4d5b..b5184fb 100644 --- a/index.js +++ b/index.js @@ -37,9 +37,9 @@ export function checkDataIntegrityProofFormat({ cryptosuiteName, isEcdsaTests = false, credential = validVc, testDescription = 'Data Integrity (issuer)', optionalTests = { - dates: true, - contextInjection: true, - domain: true + dates: false, + contextInjection: false, + domain: false } } = {}) { return describe(testDescription, function() { @@ -105,8 +105,8 @@ export function checkDataIntegrityProofVerifyErrors({ isEcdsaTests = false, testDescription = 'Data Integrity (verifier)', testDataOptions, optionalTests = { - created: true, - authentication: true + dates: false, + authentication: false } } = {}) { return describe(testDescription, async function() { diff --git a/suites/create.js b/suites/create.js index 70e0611..b22c6be 100644 --- a/suites/create.js +++ b/suites/create.js @@ -17,11 +17,7 @@ const should = chai.should(); export function runDataIntegrityProofFormatTests({ endpoints, expectedProofTypes, testDescription, vendorName, cryptosuiteName, credential, - optionalTests = { - dates: false, - contextInjection: false, - domain: false - } + optionalTests }) { return describe(testDescription, function() { const columnId = testDescription; diff --git a/suites/verify.js b/suites/verify.js index 3f07f0b..bb6709f 100644 --- a/suites/verify.js +++ b/suites/verify.js @@ -10,10 +10,7 @@ export function runDataIntegrityProofVerifyTests({ testDescription, vendorName, credentials, - optionalTests = { - dates: false, - authentication: false - } + optionalTests }) { return describe(testDescription, function() { const [verifier] = endpoints; From 6609986ce890a943b64d28f6efacb4163d9fa0c4 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 18 Sep 2024 15:00:35 +0000 Subject: [PATCH 3/5] Do not run optional issuer tests unless specified. --- suites/create.js | 225 +++++++++++++++++++++++------------------------ 1 file changed, 110 insertions(+), 115 deletions(-) diff --git a/suites/create.js b/suites/create.js index b22c6be..08c187e 100644 --- a/suites/create.js +++ b/suites/create.js @@ -306,125 +306,120 @@ export function runDataIntegrityProofFormatTests({ } }); } - it('When an application is securing a document, if an @context ' + - 'property is not provided in the document or the Data Integrity ' + - 'terms used in the document are not mapped by existing values in ' + - 'the @context property, implementations SHOULD inject or append an ' + - '@context property with a value of https://w3id.org/security/data' + - '-integrity/v2 or one or more contexts with at least the same ' + - 'declarations, such as the Verifiable Credential Data Model v2.0 ' + - 'context (https://www.w3.org/ns/credentials/v2).', async function() { - if(!issuer) { - throw new Error(`Expected ${vendorName} to have an issuer.`); - } - this.test.link = 'https://w3c.github.io/vc-data-integrity/#context-injection:~:text=if%20an%20%40context%20property%20is%20not%20provided%20in%20the%20document%20or%20the%20Data%20Integrity%20terms%20used%20in%20the%20document%20are%20not%20mapped%20by%20existing%20values%20in%20the%20%40context%20property%2C%20implementations%20SHOULD%20inject%20or%20append'; - if(optionalTests.contextInjection === false) { - this.test.cell.skipMessage = 'Optional Test Skipped'; - this.skip(); - } - const _credential = structuredClone(credential); - const expectedContexts = [ - 'https://w3id.org/security/data-integrity/v2', - 'https://www.w3.org/ns/credentials/v2' - ]; - // remove the vc's context and expect context injection to occur - delete _credential['@context']; - let err; - let data; - try { - data = await createInitialVc({issuer, credential: _credential}); - } catch(e) { - err = e; - } - should.not.exist( - err, - `Expected issuer ${vendorName} to perform context injection on a ` + - `VC with out an "@context" property`); - should.exist(data, `Expected issuer ${vendorName} to return data.`); - data.should.be.an('object', 'Expected response data to be an object.'); - should.exist(data['@context'], - 'Expected data to have an injected "@context" property.'); - if(Array.isArray(data['@context'])) { - const hasExpectedContext = expectedContexts.some( - ctx => data['@context'].includes(ctx)); - return hasExpectedContext.should.equal(true, - `Expected injected context to contain one of ${expectedContexts}`); - } - data['@context'].should.be.oneOf(expectedContexts); - }); - it('The date and time the proof was created is OPTIONAL and, if ' + - 'included, MUST be specified as an [XMLSCHEMA11-2] dateTimeStamp ' + - 'string, either in Universal Coordinated Time (UTC), denoted by a Z at ' + - 'the end of the value, or with a time zone offset relative to UTC.', - function() { - this.test.link = 'https://www.w3.org/TR/vc-data-integrity/#:~:text=The%20date%20and%20time%20the%20proof%20was%20created%20is%20OPTIONAL%20and%2C%20if%20included%2C%20MUST%20be%20specified%20as%20an%20%5BXMLSCHEMA11%2D2%5D%20dateTimeStamp%20string%2C%20either%20in%20Universal%20Coordinated%20Time%20(UTC)%2C%20denoted%20by%20a%20Z%20at%20the%20end%20of%20the%20value%2C%20or%20with%20a%20time%20zone%20offset%20relative%20to%20UTC.'; - if(optionalTests.dates === false) { - this.test.cell.skipMessage = 'Optional Test Skipped'; - this.skip(); - } - for(const proof of proofs) { - if(proof.created) { - // check if "created" is a valid XML Schema 1.1 dateTimeStamp - // value - proof.created.should.match(dateRegex); + if(optionalTests.contextInjection) { + it('When an application is securing a document, if an @context ' + + 'property is not provided in the document or the Data Integrity ' + + 'terms used in the document are not mapped by existing values in ' + + 'the @context property, implementations SHOULD inject or append an ' + + '@context property with a value of https://w3id.org/security/data' + + '-integrity/v2 or one or more contexts with at least the same ' + + 'declarations, such as the Verifiable Credential Data Model v2.0 ' + + 'context (https://www.w3.org/ns/credentials/v2).', async function() { + if(!issuer) { + throw new Error(`Expected ${vendorName} to have an issuer.`); } - } - }); - it('The expires property is OPTIONAL and, if present, specifies when ' + - 'the proof expires. If present, it MUST be an [XMLSCHEMA11-2] ' + - 'dateTimeStamp string, either in Universal Coordinated Time (UTC), ' + - 'denoted by a Z at the end of the value, or with a time zone offset ' + - 'relative to UTC.', function() { - this.test.link = 'https://w3c.github.io/vc-data-integrity/#proofs:~:text=MUST%20be%20an%20%5BXMLSCHEMA11%2D2%5D%20dateTimeStamp%20string%2C%20either%20in%20Universal%20Coordinated%20Time'; - if(optionalTests.dates === false) { - this.test.cell.skipMessage = 'Optional Test Skipped'; - this.skip(); - } - for(const proof of proofs) { - if(proof.expires) { - // check if "created" is a valid XML Schema 1.1 dateTimeStamp - // value - proof.expires.should.match(dateRegex); + this.test.link = 'https://w3c.github.io/vc-data-integrity/#context-injection:~:text=if%20an%20%40context%20property%20is%20not%20provided%20in%20the%20document%20or%20the%20Data%20Integrity%20terms%20used%20in%20the%20document%20are%20not%20mapped%20by%20existing%20values%20in%20the%20%40context%20property%2C%20implementations%20SHOULD%20inject%20or%20append'; + const _credential = structuredClone(credential); + const expectedContexts = [ + 'https://w3id.org/security/data-integrity/v2', + 'https://www.w3.org/ns/credentials/v2' + ]; + // remove the vc's context and expect context injection to occur + delete _credential['@context']; + let err; + let data; + try { + data = await createInitialVc({issuer, credential: _credential}); + } catch(e) { + err = e; } - } - }); - it('The domain property is OPTIONAL. It conveys one or more security ' + - 'domains in which the proof is meant to be used. If specified, the ' + - 'associated value MUST be either a string, or an unordered set of ' + - 'strings. A verifier SHOULD use the value to ensure that the proof ' + - 'was intended to be used in the security domain in which the ' + - 'verifier is operating.', function() { - this.test.link = 'https://w3c.github.io/vc-data-integrity/#verify-proof:~:text=The%20domain%20property%20is%20OPTIONAL.%20It%20conveys%20one%20or%20more%20security%20domains%20in%20which%20the%20proof%20is%20meant%20to%20be%20used.%20If%20specified%2C%20the%20associated%20value%20MUST'; - if(optionalTests.domain === false) { - this.test.cell.skipMessage = 'Optional Test Skipped'; - this.skip(); - } - for(const proof of proofs) { - if(proof.domain) { - const validType = isStringOrArrayOfStrings(proof.domain); - validType.should.equal(true, 'Expected ' + - '"proof.domain" to be either a string or an unordered ' + - 'set of strings.'); + should.not.exist( + err, + `Expected issuer ${vendorName} to perform context injection on a ` + + `VC with out an "@context" property`); + should.exist(data, `Expected issuer ${vendorName} to return data.`); + data.should.be.an('object', 'Expected response data to be an object.'); + should.exist(data['@context'], + 'Expected data to have an injected "@context" property.'); + if(Array.isArray(data['@context'])) { + const hasExpectedContext = expectedContexts.some( + ctx => data['@context'].includes(ctx)); + return hasExpectedContext.should.equal(true, + `Expected injected context to contain one of ${expectedContexts}`); } - } - }); - it('(challenge) A string value that SHOULD be included in a proof if a ' + - 'domain is specified.', function() { - this.test.link = 'https://w3c.github.io/vc-data-integrity/#verify-proof:~:text=A%20string%20value%20that%20SHOULD%20be%20included%20in%20a%20proof%20if%20a%20domain%20is%20specified.'; - if(optionalTests.domain === false) { - this.test.cell.skipMessage = 'Optional Test Skipped'; - this.skip(); - } - for(const proof of proofs) { - if(proof.challenge) { - // domain must be specified - should.exist(proof.domain, 'Expected "proof.domain" ' + - 'to be specified.'); - proof.challenge.should.be.a('string', 'Expected ' + - '"proof.challenge" to be a string.'); + data['@context'].should.be.oneOf(expectedContexts); + }); + } + if(optionalTests.dates) { + it('The date and time the proof was created is OPTIONAL and, if ' + + 'included, MUST be specified as an [XMLSCHEMA11-2] dateTimeStamp ' + + 'string, either in Universal Coordinated Time (UTC), denoted by a ' + + 'Z at the end of the value, or with a time zone offset relative ' + + 'to UTC.', + function() { + this.test.link = 'https://www.w3.org/TR/vc-data-integrity/#:~:text=The%20date%20and%20time%20the%20proof%20was%20created%20is%20OPTIONAL%20and%2C%20if%20included%2C%20MUST%20be%20specified%20as%20an%20%5BXMLSCHEMA11%2D2%5D%20dateTimeStamp%20string%2C%20either%20in%20Universal%20Coordinated%20Time%20(UTC)%2C%20denoted%20by%20a%20Z%20at%20the%20end%20of%20the%20value%2C%20or%20with%20a%20time%20zone%20offset%20relative%20to%20UTC.'; + for(const proof of proofs) { + if(proof.created) { + // check if "created" is a valid XML Schema 1.1 dateTimeStamp + // value + proof.created.should.match(dateRegex); + } } - } - }); + }); + it('The expires property is OPTIONAL and, if present, specifies when ' + + 'the proof expires. If present, it MUST be an [XMLSCHEMA11-2] ' + + 'dateTimeStamp string, either in Universal Coordinated Time (UTC), ' + + 'denoted by a Z at the end of the value, or with a time zone offset ' + + 'relative to UTC.', function() { + this.test.link = 'https://w3c.github.io/vc-data-integrity/#proofs:~:text=MUST%20be%20an%20%5BXMLSCHEMA11%2D2%5D%20dateTimeStamp%20string%2C%20either%20in%20Universal%20Coordinated%20Time'; + for(const proof of proofs) { + if(proof.expires) { + // check if "created" is a valid XML Schema 1.1 dateTimeStamp + // value + proof.expires.should.match(dateRegex); + } + } + }); + } + if(optionalTests.domain) { + it('The domain property is OPTIONAL. It conveys one or more security ' + + 'domains in which the proof is meant to be used. If specified, the ' + + 'associated value MUST be either a string, or an unordered set of ' + + 'strings. A verifier SHOULD use the value to ensure that the proof ' + + 'was intended to be used in the security domain in which the ' + + 'verifier is operating.', function() { + this.test.link = 'https://w3c.github.io/vc-data-integrity/#verify-proof:~:text=The%20domain%20property%20is%20OPTIONAL.%20It%20conveys%20one%20or%20more%20security%20domains%20in%20which%20the%20proof%20is%20meant%20to%20be%20used.%20If%20specified%2C%20the%20associated%20value%20MUST'; + if(optionalTests.domain === false) { + this.test.cell.skipMessage = 'Optional Test Skipped'; + this.skip(); + } + for(const proof of proofs) { + if(proof.domain) { + const validType = isStringOrArrayOfStrings(proof.domain); + validType.should.equal(true, 'Expected ' + + '"proof.domain" to be either a string or an unordered ' + + 'set of strings.'); + } + } + }); + it('(challenge) A string value that SHOULD be included in a proof if a ' + + 'domain is specified.', function() { + this.test.link = 'https://w3c.github.io/vc-data-integrity/#verify-proof:~:text=A%20string%20value%20that%20SHOULD%20be%20included%20in%20a%20proof%20if%20a%20domain%20is%20specified.'; + if(optionalTests.domain === false) { + this.test.cell.skipMessage = 'Optional Test Skipped'; + this.skip(); + } + for(const proof of proofs) { + if(proof.challenge) { + // domain must be specified + should.exist(proof.domain, 'Expected "proof.domain" ' + + 'to be specified.'); + proof.challenge.should.be.a('string', 'Expected ' + + '"proof.challenge" to be a string.'); + } + } + }); + } }); } From 28c25cfeee9155aec7a226552bf0e53b4a724e60 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 18 Sep 2024 15:35:49 +0000 Subject: [PATCH 4/5] Add missing parameters to READM examples. --- README.md | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f6f9477..d0808fe 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,100 @@ const { property: 'verifiers' }); +// an optional parameter for +// testing optional features +const optionalTests = { + // turns on contextInjection tests + contextInjection: true, + // turns on tests related to dates + dates: true, + // turns on tests related to authentication + authentication: true +}; +// an optional parameter that +// specifies a credential to test against +const credential = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + { + "@protected": true, + "DriverLicenseCredential": "urn:example:DriverLicenseCredential", + "DriverLicense": { + "@id": "urn:example:DriverLicense", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "documentIdentifier": "urn:example:documentIdentifier", + "dateOfBirth": "urn:example:dateOfBirth", + "expirationDate": "urn:example:expiration", + "issuingAuthority": "urn:example:issuingAuthority" + } + }, + "driverLicense": { + "@id": "urn:example:driverLicense", + "@type": "@id" + } + } + ], + "id": "urn:uuid:36245ee9-9074-4b05-a777-febff2e69758", + "type": ["VerifiableCredential", "DriverLicenseCredential"], + "credentialSubject": { + "id": "urn:uuid:1a0e4ef5-091f-4060-842e-18e519ab9440", + "driverLicense": { + "type": "DriverLicense", + "documentIdentifier": "T21387yc328c7y32h23f23", + "dateOfBirth": "01-01-1990", + "expirationDate": "01-01-2030", + "issuingAuthority": "VA" + } + } +}; +// if the suite is data integrity complaint +// cryptosuiteName is mandatory +const cryptosuiteName = 'ecdsa-sd-2023'; + +// an optional list of proof types VC +// issued by an implementation will have +const expectedProofTypes = [ + 'DataIntegrityProof', + 'MyProofType' +] + checkDataIntegrityProofFormat({ - implemented: matchingIssuers + implemented: matchingIssuers, + optionalTests, + credential, + cryptosuiteName, + expectedProofTypes }); +// a parameter for how the verifier +// tests will produce VCs for the verifier +const testDataOptions = { + suiteName: cryptosuiteName, + // a key the suite can issue with + key, + // the cryptosuite being tests + cryptosuite = myCryptosuite, + // optional parameter for selective disclosure tests + // using JSON pointers + mandatoryPointers, + // optional parameter for selective disclosure tests + // using JSON pointers + selectivePointers, + // an optional parameter specifying a documentLoader + // used to issue VCs + documentLoader, + // the testVector to use to produce VCs + // for the verifier + testVector: credential +}; checkDataIntegrityProofVerifyErrors({ - implemented: matchingVerifiers + implemented: matchingVerifiers, + optionalTests, + // an optional parameter to specify the expected proof type + expectedProofType: 'DataIntegrityProof', + testDataOptions }); ``` From c56bc91b42066d85fddf25f22a5a31fdb8c0b322 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 18 Sep 2024 15:37:43 +0000 Subject: [PATCH 5/5] Update cryptosuiteName to eddsa-2022. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d0808fe..e1c744e 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ const credential = { }; // if the suite is data integrity complaint // cryptosuiteName is mandatory -const cryptosuiteName = 'ecdsa-sd-2023'; +const cryptosuiteName = 'eddsa-2022'; // an optional list of proof types VC // issued by an implementation will have