diff --git a/tests/suites/algorithms-sd.js b/tests/suites/algorithms-sd.js index 4343431c..a6173a61 100644 --- a/tests/suites/algorithms-sd.js +++ b/tests/suites/algorithms-sd.js @@ -12,6 +12,10 @@ import { encodeBs64Url, getBs64UrlBytes } from '../helpers.js'; +import { + parseDisclosureProofValue, + serializeProofValue +} from './stubs.js'; import {expect} from 'chai'; import {getMultiKey} from '../vc-generator/key-gen.js'; import {getSuites} from './helpers.js'; @@ -179,8 +183,11 @@ export function sd2023Algorithms({ 'MUST be raised and SHOULD convey an error type of ' + 'PROOF_VERIFICATION_ERROR.', async function() { this.test.link = 'https://w3c.github.io/vc-di-ecdsa/#selective-disclosure-functions:~:text=array%20of%20integers%20%E2%80%94-,an%20error%20MUST%20be%20raised%20and%20SHOULD%20convey%20an%20error%20type%20of%20PROOF_VERIFICATION_ERROR.,-Replace%20the%20fourth'; - this.test.cell.skipMessage = 'Not Implemented'; - this.skip(); + await assertions.verificationFail({ + verifier, + credential: fixtures.get(keyType).get('invalidProofArray'), + reason: 'Should not verify proofValue array missing elements' + }); }); it('The transformation options MUST contain a type identifier for ' + 'the cryptographic suite (type), a cryptosuite identifier ' + @@ -351,5 +358,13 @@ async function _setup({ // invalid 3 byte header invalidProofValueHeader.proof.proofValue = `u${encodeBs64Url(invalidBuffer)}`; credentials.set('invalidDisclosureProofHeader', invalidProofValueHeader); + const invalidProofArray = structuredClone(securedCredential); + // parse the existing disclosure proofValue + const params = parseDisclosureProofValue({proof: invalidProofArray.proof}); + // create a new proofValue missing 3 elements + invalidProofArray.proof.proofValue = serializeProofValue({ + payload: [params.baseSignature, params.publicKey] + }); + credentials.set('invalidProofArray', invalidProofArray); return credentials; } diff --git a/tests/suites/proxies.js b/tests/suites/proxies.js index 81b97723..fad69bc4 100644 --- a/tests/suites/proxies.js +++ b/tests/suites/proxies.js @@ -2,8 +2,9 @@ * Copyright 2024 Digital Bazaar, Inc. * SPDX-License-Identifier: BSD-3-Clause */ +import {Token, Type} from 'cborg'; import crypto from 'node:crypto'; -import {stubDerive} from './stubs.js'; +import {DeriveStub} from './stubs.js'; /** * Creates a proxy of an object with stubs. * @@ -154,7 +155,17 @@ async function _canonizeProof(proof, { } export function invalidCborTagProxy(suite) { - const stubs = {derive: stubDerive}; + const typeEncoders = { + Uint8Array(uint8Array) { + return [ + new Token(Type.tag, 2), + new Token(Type.bytes, uint8Array.map(b => b + 1)) + ]; + } + }; + const stubs = {derive({...args}) { + return new DeriveStub({typeEncoders}).derive({...args}); + }}; if(suite._cryptosuite) { suite._cryptosuite = createProxy({ original: suite._cryptosuite, diff --git a/tests/suites/stubs.js b/tests/suites/stubs.js index 63b95458..f87b658a 100644 --- a/tests/suites/stubs.js +++ b/tests/suites/stubs.js @@ -12,7 +12,6 @@ import { selectJsonLd, stripBlankNodePrefixes } from '@digitalbazaar/di-sd-primitives'; -import {Token, Type} from 'cborg'; const CBOR_PREFIX_BASE = new Uint8Array([0xd9, 0x5d, 0x00]); const CBOR_PREFIX_DERIVED = new Uint8Array([0xd9, 0x5d, 0x01]); @@ -21,43 +20,44 @@ const CBOR_PREFIX_DERIVED = new Uint8Array([0xd9, 0x5d, 0x01]); const TAGS = []; TAGS[64] = bytes => bytes; -// Stubs the ecdsa-sd-2023 derive function -export async function stubDerive({ - cryptosuite, document, proofSet, - documentLoader, dataIntegrityProof -}) { - // find matching base `proof` in `proofSet` - const {options: {proofId}} = cryptosuite; - const baseProof = await _findProof({proofId, proofSet, dataIntegrityProof}); - // generate data for disclosure - const { - baseSignature, publicKey, signatures, labelMap, mandatoryIndexes, revealDoc - } = await _createDisclosureData( - {cryptosuite, document, proof: baseProof, documentLoader}); - - // create new disclosure proof - const newProof = {...baseProof}; - newProof.proofValue = await invalidSerializeDisclosureProofValue( - {baseSignature, publicKey, signatures, labelMap, mandatoryIndexes}); - - // attach proof to reveal doc w/o context - delete newProof['@context']; - revealDoc.proof = newProof; - return revealDoc; +export class DeriveStub { + constructor({typeEncoders}) { + this.typeEncoders = typeEncoders; + } + async derive({ + cryptosuite, document, proofSet, + documentLoader, dataIntegrityProof + }) { + // get test specific options + const {typeEncoders} = this; + // find matching base `proof` in `proofSet` + const {options: {proofId}} = cryptosuite; + const baseProof = await _findProof({proofId, proofSet, dataIntegrityProof}); + // generate data for disclosure + const { + baseSignature, publicKey, signatures, + labelMap, mandatoryIndexes, revealDoc + } = await _createDisclosureData( + {cryptosuite, document, proof: baseProof, documentLoader}); + + // create new disclosure proof + const newProof = {...baseProof}; + newProof.proofValue = await serializeDisclosureProofValue({ + baseSignature, publicKey, signatures, + labelMap, mandatoryIndexes, typeEncoders + }); + // attach proof to reveal doc w/o context + delete newProof['@context']; + revealDoc.proof = newProof; + return revealDoc; + } } // ecdsa-sd-2023 method that uses invalid cbor tags -function invalidSerializeDisclosureProofValue({ - baseSignature, publicKey, signatures, labelMap, mandatoryIndexes +function serializeDisclosureProofValue({ + baseSignature, publicKey, signatures, + labelMap, mandatoryIndexes, typeEncoders } = {}) { - const typeEncoders = { - Uint8Array(uint8Array) { - return [ - new Token(Type.tag, 2), - new Token(Type.bytes, uint8Array.map(b => b + 1)) - ]; - } - }; // encode as multibase (base64url no pad) CBOR const payload = [ // Uint8Array @@ -71,8 +71,21 @@ function invalidSerializeDisclosureProofValue({ // array of numbers mandatoryIndexes ]; + return serializeProofValue({ + prefix: CBOR_PREFIX_DERIVED, + payload, + typeEncoders + }); +} + +// ecdsa-sd-2023 test data creation function +export function serializeProofValue({ + prefix = CBOR_PREFIX_DERIVED, + payload, + typeEncoders +}) { const cbor = _concatBuffers([ - CBOR_PREFIX_DERIVED, cborg.encode(payload, {useMaps: true, typeEncoders}) + prefix, cborg.encode(payload, {useMaps: true, typeEncoders}) ]); return `u${base64url.encode(cbor)}`; } @@ -241,3 +254,40 @@ async function _findProof({proofId, proofSet, dataIntegrityProof}) { } return proof; } + +// ecdsa-sd-2023 proofValue +export function parseDisclosureProofValue({proof} = {}) { + try { + // decode from base64url + const proofValue = base64url.decode(proof.proofValue.slice(1)); + + const payload = proofValue.subarray(CBOR_PREFIX_DERIVED.length); + const [ + baseSignature, + publicKey, + signatures, + compressedLabelMap, + mandatoryIndexes + ] = cborg.decode(payload, {useMaps: true, tags: TAGS}); + + const labelMap = _decompressLabelMap(compressedLabelMap); + const params = { + baseSignature, publicKey, signatures, labelMap, mandatoryIndexes + }; + return params; + } catch(e) { + const err = new TypeError( + 'The proof does not include a valid "proofValue" property.'); + err.cause = e; + throw err; + } +} +// ecdsa-sd-2023 proofValue +function _decompressLabelMap(compressedLabelMap) { + const map = new Map(); + for(const [k, v] of compressedLabelMap.entries()) { + map.set(`c14n${k}`, `u${base64url.encode(v)}`); + } + return map; +} +