diff --git a/tests/90-algorithms-sd.js b/tests/90-algorithms-sd.js index da6db34..7992cc3 100644 --- a/tests/90-algorithms-sd.js +++ b/tests/90-algorithms-sd.js @@ -8,15 +8,20 @@ import { assertDataIntegrityProof } from './assertions.js'; import { + encodeSdDerivedProofValue, generateCredential, - inspectSdProofValue, + inspectSdBaseProofValue, + inspectSdDerivedProofValue, isValidDatetime, proofExists, secureCredential, setupReportableTestSuite, setupRow, + verifyError, + verifySuccess } from './helpers.js'; import chai from 'chai'; +import {ecdsaSdVectors} from './vectors.js'; import {endpoints} from 'vc-test-suite-implementations'; import {expect} from 'chai'; @@ -107,7 +112,7 @@ describe('Algorithms - Base Proof Transformation (ecdsa-sd-2023)', function() { {issuer, vc: generateCredential()}); const proof = proofExists(securedCredentialNoPointers); const decodedProof = - await inspectSdProofValue(proof); + await inspectSdBaseProofValue(proof); should.exist(decodedProof.mandatoryPointers, 'Expected mandatoryPointers to be included in the proofValue.'); }); @@ -123,7 +128,7 @@ describe('Algorithms - Base Proof Transformation (ecdsa-sd-2023)', function() { async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#transformation-ecdsa-sd-2023'; const proof = proofExists(securedCredential); - const decodedProof = await inspectSdProofValue(proof); + const decodedProof = await inspectSdBaseProofValue(proof); decodedProof.hmacKey.length.should.equal(32, 'Expected HMAC key to be the same length as the digest size.' ); @@ -220,8 +225,22 @@ describe('Algorithms - Verify Derived Proof (ecdsa-sd-2023)', function() { 'signature count does not match the non-mandatory message count.', async function() { this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#proof-serialization-ecdsa-sd-2023'; - verifier; - this.skip(); + + // const validBaseProof = structuredClone(ecdsaSdVectors.baseProof); + // await verifySuccess(verifier, validBaseProof); + + const validDerivedProof = structuredClone(ecdsaSdVectors.derivedProof); + await verifySuccess(verifier, validDerivedProof); + + // Instanciate a new signed credential and remove a signature + const invalidDerivedProof = structuredClone(validDerivedProof); + const decodedDerivedProofValue = + await inspectSdDerivedProofValue(invalidDerivedProof.proof); + decodedDerivedProofValue.signatures = + decodedDerivedProofValue.signatures.slice(1); + invalidDerivedProof.proof.proofValue = + encodeSdDerivedProofValue(decodedDerivedProofValue); + await verifyError(verifier, invalidDerivedProof); }); }); } diff --git a/tests/helpers.js b/tests/helpers.js index 88fb346..53865d5 100644 --- a/tests/helpers.js +++ b/tests/helpers.js @@ -6,12 +6,10 @@ 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'; @@ -350,7 +348,7 @@ export async function multikeyFromVerificationMethod( return null; } -export async function inspectSdProofValue(proof) { +export async function inspectSdBaseProofValue(proof) { const proofValue = proof.proofValue; expect(proof.proofValue.startsWith('u')).to.be.true; const cborProof = bases.base64url.decode(proofValue); @@ -369,3 +367,29 @@ export async function inspectSdProofValue(proof) { mandatoryPointers: decodedProofValues[4] }; } + +export async function inspectSdDerivedProofValue(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], + signatures: decodedProofValues[2], + labelMap: decodedProofValues[3], + mandatoryIndexes: decodedProofValues[4] + }; +} + +export async function encodeSdDerivedProofValue(decodedPproof) { + const cborProof = await cbor.encode(decodedPproof); + const proofValue = bases.base64url.encode(cborProof); + return proofValue; +} diff --git a/tests/vectors.js b/tests/vectors.js index 112c700..1869f74 100644 --- a/tests/vectors.js +++ b/tests/vectors.js @@ -7,6 +7,112 @@ test vectors. */ /* eslint-disable max-len */ /* eslint-disable quote-props */ /* eslint-disable quotes */ +export const ecdsaSdVectors = { + baseProof: { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + { + "@vocab": "https://windsurf.grotto-networking.com/selective#" + } + ], + "type": [ + "VerifiableCredential" + ], + "issuer": "https://vc.example/windsurf/racecommittee", + "credentialSubject": { + "sailNumber": "Earth101", + "sails": [ + { + "size": 5.5, + "sailName": "Kihei", + "year": 2023 + }, + { + "size": 6.1, + "sailName": "Lahaina", + "year": 2023 + }, + { + "size": 7, + "sailName": "Lahaina", + "year": 2020 + }, + { + "size": 7.8, + "sailName": "Lahaina", + "year": 2023 + } + ], + "boards": [ + { + "boardName": "CompFoil170", + "brand": "Wailea", + "year": 2022 + }, + { + "boardName": "Kanaha Custom", + "brand": "Wailea", + "year": 2019 + } + ] + }, + "proof": { + "type": "DataIntegrityProof", + "cryptosuite": "ecdsa-sd-2023", + "created": "2023-08-15T23:36:38Z", + "verificationMethod": "did:key:zDnaepBuvsQ8cpsWrVKw8fbpGpvPeNSjVPTWoq6cRqaYzBKVP#zDnaepBuvsQ8cpsWrVKw8fbpGpvPeNSjVPTWoq6cRqaYzBKVP", + "proofPurpose": "assertionMethod", + "proofValue": "u2V0AhVhAkWKMO8zpRmfcUMksHUMtZM7cJt8PmNLsljKTYhSi8gZ7wAWnK4BrOZkrH3dZvxKWlnxGG_0xlFXmU5sa5-j71VgjgCQCKnLOGbY_FuM-ASpSkkOxsIR2E8n7Ml2q1UQ6tEwzi5NYIAARIjNEVWZ3iJmqu8zd7v8AESIzRFVmd4iZqrvM3e7_jlhAKVYKM250DDcNWOYQpUmYC1Z5NZhJRwie8vVUev94QGst83WhoW7_UM6JULsKjNVHjxZlZQyovN4xw1M_mhn6TFhAsqMSgz0EeaPe0Hmo5SN1JNZmCjiZ-CNJB4ScmyK46s7hDotNZuGHxKGaAFC43O0FxcKeUC96q_z9PGeF5C3VpVhAmeoEU8I1ZzxFyR-QMxwoSkqRG9E8_CaSrhH8TD2t-tV32HKAC4hJkKl6xHuz6XL2G-V0cm6d_rWozjhmmVaMjVhAbQMMckpcMAEo15WC6C8Mo3bCEWFGtOTkMxND-LJMdfkCSovB7RnCR7SXzk5-0YVigtJ5Fzg71AAob5yg1WNNk1hApQHlYRGlUVkv-WX1OjJYJ19Ow7ipvVwUvm90Sn3IjNRLuy9pr5DHm3wVlVMPVpLqjS-E8_jJDeJV5pY0bfK_A1hAas2wx9bcEj0Sh7t8w9Cj-2FpceGpdRhaLZxYs1ZEG8-obUjb0CHOyH8S7uwDtn7oSW2oCW2SpZvlX-2jW17rmlhAe34eQ8-gJHyQahY0EmZh8mZoy0svnpTjkdcLnroLIBsiVkfCzMKLOWeEtWZUVnIBeugT8I2C7mnmpHNjdo2d4lhAM8okCUX8F4GYx9rlnSDvr5pTHPOjOOJ47JzFdDtX_Q4bZxWwLGwqltYojDecyt4oxQHYz55ZRnhTXLHqa74B7VhAO_Hj0vxsuJZzpVGtgoMKK2ZlGKvhLX3_vUCvdL-MTlszVr2iC3XJpCbOc8B_W_On-csaLPzUSvlSDtNec1ZVk1hAdm2Ht4sv_ec3s1HRqeul--yEGx4SrpwyNQRdLa5ZKyJDgqr4h-EtVNzc-J-VllvKrHN8wBKtUqarqI4Npnrx7VhAORMLXYz3l59Ozc7SDk2ej7clrer9Bn6eaBUQG773AqQ56bc-oGXeemekwZCNHjFLOESNoNq7qetO8FRbiFHb4FhAW-otSFVlUPFmg119n3TeSE7up5hBS34AqP2TGUQA5pDGyOTetrf8qq3bWj1lpCu1Z6yEZJlQ6nrLiCoaNVhpL1hA1wW_HhsTPUfUlqMX6ZMsLem8hbWaFe_rZDpPp5NN02vMHInDjO1Gn0BrXUyVAMnTY3fGrDjsuy2sGgMzR-bo11hAvOGSXH51eRoCWtV9LlpZD10ix0IuuVCnat5fRxU7hqGs0AzM09kGsmuDMRjowp51xhiFJ3iMajIOOhWUhPxHCoVnL2lzc3VlcngdL2NyZWRlbnRpYWxTdWJqZWN0L3NhaWxOdW1iZXJ4Gi9jcmVkZW50aWFsU3ViamVjdC9zYWlscy8xeCAvY3JlZGVudGlhbFN1YmplY3QvYm9hcmRzLzAveWVhcngaL2NyZWRlbnRpYWxTdWJqZWN0L3NhaWxzLzI" + } + }, + derivedProof: { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + { + "@vocab": "https://windsurf.grotto-networking.com/selective#" + } + ], + "type": [ + "VerifiableCredential" + ], + "issuer": "https://vc.example/windsurf/racecommittee", + "credentialSubject": { + "sailNumber": "Earth101", + "sails": [ + { + "size": 6.1, + "sailName": "Lahaina", + "year": 2023 + }, + { + "size": 7, + "sailName": "Lahaina", + "year": 2020 + } + ], + "boards": [ + { + "year": 2022, + "boardName": "CompFoil170", + "brand": "Wailea" + }, + { + "boardName": "Kanaha Custom", + "brand": "Wailea", + "year": 2019 + } + ] + }, + "proof": { + "type": "DataIntegrityProof", + "cryptosuite": "ecdsa-sd-2023", + "created": "2023-08-15T23:36:38Z", + "verificationMethod": "did:key:zDnaepBuvsQ8cpsWrVKw8fbpGpvPeNSjVPTWoq6cRqaYzBKVP#zDnaepBuvsQ8cpsWrVKw8fbpGpvPeNSjVPTWoq6cRqaYzBKVP", + "proofPurpose": "assertionMethod", + "proofValue": "u2V0BhVhAkWKMO8zpRmfcUMksHUMtZM7cJt8PmNLsljKTYhSi8gZ7wAWnK4BrOZkrH3dZvxKWlnxGG_0xlFXmU5sa5-j71VgjgCQCKnLOGbY_FuM-ASpSkkOxsIR2E8n7Ml2q1UQ6tEwzi5OGWEBtAwxySlwwASjXlYLoLwyjdsIRYUa05OQzE0P4skx1-QJKi8HtGcJHtJfOTn7RhWKC0nkXODvUAChvnKDVY02TWEClAeVhEaVRWS_5ZfU6MlgnX07DuKm9XBS-b3RKfciM1Eu7L2mvkMebfBWVUw9WkuqNL4Tz-MkN4lXmljRt8r8DWEBqzbDH1twSPRKHu3zD0KP7YWlx4al1GFotnFizVkQbz6htSNvQIc7IfxLu7AO2fuhJbagJbZKlm-Vf7aNbXuuaWEA78ePS_Gy4lnOlUa2CgworZmUYq-Etff-9QK90v4xOWzNWvaILdcmkJs5zwH9b86f5yxos_NRK-VIO015zVlWTWEB2bYe3iy_95zezUdGp66X77IQbHhKunDI1BF0trlkrIkOCqviH4S1U3Nz4n5WWW8qsc3zAEq1Spquojg2mevHtWEA5EwtdjPeXn07NztIOTZ6PtyWt6v0Gfp5oFRAbvvcCpDnptz6gZd56Z6TBkI0eMUs4RI2g2rup607wVFuIUdvgpgBYIOGCDmZ9TBxEtWeCI9oVmRt0eHRGAaoOXx08gxL2IQt_AVggVkUuBrlOaELGVQWJD4M_qW5bcKEHWGNbOrPA_qAOKKwCWCBD6o5lQOWjNGwaTjq7H2Cn1-NPbwXLeDedy2YyiqL9TQNYIJEdvfdRibsv05I3pv8e6S1aUuAuBpGQHLhrYj4QX0knBFggk0AeXgJ4e6m1XsV5-xFud0L_1mUjZ9Mffhg5aZGTyDkFWCDYgT4e07o_IdCwae6qE7WZfpXtGRFESEXR3SxZmXE05o4AAQIFBggJCg4PEBESEw" + } + } +}; export const ecdsaRdfcVectors = { 'P-256': { "@context": [