Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ecdsa-sd-2023 proofValue payload missing elements negative test (E -> D) #104

Open
wants to merge 5 commits into
base: add-3-sd-proofValue-tests
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions tests/suites/algorithms-sd.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 ' +
Expand Down Expand Up @@ -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;
}
15 changes: 13 additions & 2 deletions tests/suites/proxies.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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,
Expand Down
120 changes: 85 additions & 35 deletions tests/suites/stubs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand All @@ -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
Expand All @@ -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)}`;
}
Expand Down Expand Up @@ -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;
}