diff --git a/package.json b/package.json index 45743a6..6f196ab 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@digitalcredentials/did-context": "^1.0.0", "base58-universal": "^2.0.0", "base64url-universal": "^2.0.0", + "cbor": "^10.0.3", "chai": "^4.3.7", "chai-string": "^1.5.0", "data-integrity-test-suite-assertion": "github:w3c-ccg/data-integrity-test-suite-assertion", @@ -61,6 +62,7 @@ "json-canon": "^1.0.1", "klona": "^2.0.6", "multibase": "^4.0.6", + "multiformats": "^13.3.1", "mocha": "^10.2.0", "uuid": "^9.0.0", "varint": "^6.0.0", diff --git a/tests/90-algorithms-sd.js b/tests/90-algorithms-sd.js index 4fb3b41..da6db34 100644 --- a/tests/90-algorithms-sd.js +++ b/tests/90-algorithms-sd.js @@ -9,6 +9,7 @@ import { } from './assertions.js'; import { generateCredential, + inspectSdProofValue, isValidDatetime, proofExists, secureCredential, @@ -43,16 +44,17 @@ describe('Algorithms - Create Base Proof (ecdsa-sd-2023)', function() { const [issuer] = endpoints; let securedCredential; before(async function() { + const mandatoryPointers = ['/credentialSubject/name']; securedCredential = await secureCredential( - {issuer, vc: generateCredential()}); + {issuer, vc: generateCredential(), mandatoryPointers}); }); beforeEach(setupRow); it('A data integrity proof (map), or an error, is produced as output.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#create-proof-ecdsa-sd-2023'; const proof = proofExists(securedCredential); - assertDataIntegrityProof(proof, 'ecdsa-sd-2023'); - // Since we are not sending proof options, we only do a positive test + assertDataIntegrityProof(proof); + // We only do a positive test }); it('Let proof.proofValue be a base64-url-encoded ' + 'Multibase value of the proofBytes.', @@ -77,8 +79,9 @@ describe('Algorithms - Base Proof Transformation (ecdsa-sd-2023)', function() { const [issuer] = endpoints; let securedCredential; before(async function() { + const mandatoryPointers = ['/credentialSubject/name']; securedCredential = await secureCredential( - {issuer, vc: generateCredential()}); + {issuer, vc: generateCredential(), mandatoryPointers}); }); beforeEach(setupRow); it('The transformation options MUST contain a type identifier for the ' + @@ -99,7 +102,14 @@ describe('Algorithms - Base Proof Transformation (ecdsa-sd-2023)', function() { 'options, such as a JSON-LD document loader.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#transformation-ecdsa-sd-2023'; - this.skip(); + // Send an issuance request without mandatoryPointers + const securedCredentialNoPointers = await secureCredential( + {issuer, vc: generateCredential()}); + const proof = proofExists(securedCredentialNoPointers); + const decodedProof = + await inspectSdProofValue(proof); + should.exist(decodedProof.mandatoryPointers, + 'Expected mandatoryPointers to be included in the proofValue.'); }); it('Whenever this algorithm encodes strings, it MUST use UTF-8 encoding.', async function() { @@ -112,7 +122,11 @@ describe('Algorithms - Base Proof Transformation (ecdsa-sd-2023)', function() { 'or 32 bytes.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#transformation-ecdsa-sd-2023'; - this.skip(); + const proof = proofExists(securedCredential); + const decodedProof = await inspectSdProofValue(proof); + decodedProof.hmacKey.length.should.equal(32, + 'Expected HMAC key to be the same length as the digest size.' + ); }); }); } @@ -126,8 +140,9 @@ describe('Algorithms - Base Proof Configuration (ecdsa-sd-2023)', function() { const [issuer] = endpoints; let securedCredential; before(async function() { + const mandatoryPointers = ['/credentialSubject/name']; securedCredential = await secureCredential( - {issuer, vc: generateCredential()}); + {issuer, vc: generateCredential(), mandatoryPointers}); }); it('The proof options MUST contain a type identifier for the ' + 'cryptographic suite (type) and MUST contain a cryptosuite ' + @@ -174,8 +189,9 @@ describe('Algorithms - Base Proof Serialization (ecdsa-sd-2023)', function() { const [issuer] = endpoints; let securedCredential; before(async function() { + const mandatoryPointers = ['/credentialSubject/name']; securedCredential = await secureCredential( - {issuer, vc: generateCredential()}); + {issuer, vc: generateCredential(), mandatoryPointers}); }); beforeEach(setupRow); it('The proof options MUST contain a type identifier for the ' + diff --git a/tests/helpers.js b/tests/helpers.js index 0d946ca..88fb346 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -6,10 +6,15 @@ 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 {base64url} from 'multiformats/bases/base64'; +import {bases} from 'multiformats/basics'; import {CachedResolver} from '@digitalbazaar/did-io'; +import cbor from 'cbor'; import chai from 'chai'; +import {CID} from 'multiformats/cid'; import {createRequire} from 'node:module'; import {contexts as credContexts} from '@digitalbazaar/credentials-context'; +import {expect} from 'chai'; import {isUtf8} from 'node:buffer'; import {JsonLdDocumentLoader} from 'jsonld-document-loader'; import {klona} from 'klona'; @@ -344,3 +349,23 @@ export async function multikeyFromVerificationMethod( } return null; } + +export async function inspectSdProofValue(proof) { + const proofValue = proof.proofValue; + expect(proof.proofValue.startsWith('u')).to.be.true; + const cborProof = bases.base64url.decode(proofValue); + const decodedProof = await cbor.decodeFirst(cborProof, (error, obj) => { + return obj; + }); + const decodedProofValues = decodedProof.value; + decodedProofValues.length.should.equal(5, + 'Expected decoded proof value to be of length 5.' + ); + return { + baseSignature: decodedProofValues[0], + publicKey: decodedProofValues[1], + hmacKey: decodedProofValues[2], + signatures: decodedProofValues[3], + mandatoryPointers: decodedProofValues[4] + }; +}