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

Data model proof representation #107

Merged
merged 4 commits into from
Nov 21, 2024
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"chai": "^4.3.7",
"chai-string": "^1.5.0",
"data-integrity-test-suite-assertion": "github:w3c-ccg/data-integrity-test-suite-assertion",
"jsonld-document-loader": "^2.0.0",
"jsonld-document-loader": "^2.2.0",
"json-canon": "^1.0.1",
"klona": "^2.0.6",
"multibase": "^4.0.6",
Expand Down
116 changes: 116 additions & 0 deletions tests/70-data-model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*!
* Copyright 2024 Digital Bazaar, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
import {
generateCredential,
multikeyFromVerificationMethod,
proofExists,
secureCredential,
setupReportableTestSuite,
setupRow
} from './helpers.js';
import {
assertDataIntegrityProof
} from './assertions.js';
import chai from 'chai';
import {endpoints} from 'vc-test-suite-implementations';
import {expect} from 'chai';

const should = chai.should();

const cryptosuites = [
'ecdsa-sd-2023',
'ecdsa-jcs-2019',
'ecdsa-rdfc-2019',
];

const {match: issuers} = endpoints.filterByTag({
tags: cryptosuites,
property: 'issuers'
});

describe('Data Model - Verification Methods (Multikey)', function() {
setupReportableTestSuite(this);
this.implemented = [...issuers.keys()];
for(const [columnId, {endpoints}] of issuers) {
describe(columnId, function() {
const [issuer] = endpoints;
let securedCredential;
before(async function() {
securedCredential = await secureCredential(
{issuer, vc: generateCredential()});
});
beforeEach(setupRow);
it('The publicKeyMultibase value of the verification method ' +
'MUST start with the base-58-btc prefix (z), as defined in ' +
'the Multibase section of Controller Documents 1.0.',
async function() {
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof';
const proof = proofExists(securedCredential);
const verificationMethod = proof.verificationMethod;
// Only did key is supported
const keyType = issuer.settings.supportedEcdsaKeyTypes[0];
const multikey =
await multikeyFromVerificationMethod(verificationMethod, keyType);
expect(multikey.startsWith('z')).to.be.true;
});
it('A Multibase-encoded ECDSA 256-bit public key value or an ' +
'ECDSA 384-bit public key value follows, as defined in the Multikey ' +
'section of Controller Documents 1.0. Any other encoding ' +
'MUST NOT be allowed.',
async function() {
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof';
const proof = proofExists(securedCredential);
const verificationMethod = proof.verificationMethod;
// Only did key is supported
const keyType = issuer.settings.supportedEcdsaKeyTypes[0];
const multikey =
await multikeyFromVerificationMethod(verificationMethod, keyType);
expect(multikey).to.exist;
});
});
}
});

describe('Data Model - Proof Representations', function() {
setupReportableTestSuite(this);
this.implemented = [...issuers.keys()];
for(const [columnId, {endpoints}] of issuers) {
describe(columnId, function() {
const [issuer] = endpoints;
let securedCredential;
before(async function() {
securedCredential = await secureCredential(
{issuer, vc: generateCredential()});
});
beforeEach(setupRow);
it('A proof contains the attributes specified in the ' +
'Proofs section of [VC-DATA-INTEGRITY].',
async function() {
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof';
const proof = proofExists(securedCredential);
assertDataIntegrityProof(proof);
});
it('The type property MUST be DataIntegrityProof.',
async function() {
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof';
const proof = proofExists(securedCredential);
should.exist(proof.type,
'Expected a type on the proof.');
proof.type.should.equal('DataIntegrityProof',
'Expected DataIntegrityProof type.');
});
it('The cryptosuite property MUST be ecdsa-rdfc-2019, ' +
'ecdsa-jcs-2019, or ecdsa-sd-2023.',
async function() {
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#dataintegrityproof';
const proof = proofExists(securedCredential);
should.exist(proof.cryptosuite,
'Expected a cryptosuite identifier on the proof.');
proof.cryptosuite.should.be.oneOf(cryptosuites,
`Expected cryptosuite for be one of ${cryptosuites}.`);
});
});
}
});
2 changes: 1 addition & 1 deletion tests/90-algorithms-jcs.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('Algorithms - Create Proof (ecdsa-jcs-2019)', function() {
async function() {
this.test.link = 'https://www.w3.org/TR/vc-di-ecdsa/#create-proof-ecdsa-jcs-2019';
const proof = proofExists(securedCredential);
assertDataIntegrityProof(proof, 'ecdsa-jcs-2019');
assertDataIntegrityProof(proof);
// Since we are not sending proof options, we only do a positive test
});
it('If unsecuredDocument.@context is present, ' +
Expand Down
4 changes: 1 addition & 3 deletions tests/assertions.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export function assertAllUtf8(proof) {
}
}

export function assertDataIntegrityProof(proof, cryptosuite) {
export function assertDataIntegrityProof(proof) {
if(proof?.id) {
}
should.exist(proof.type,
Expand All @@ -230,8 +230,6 @@ export function assertDataIntegrityProof(proof, cryptosuite) {
}
should.exist(proof.cryptosuite,
'Expected a cryptosuite identifier on the proof.');
proof.cryptosuite.should.equal(cryptosuite,
`Expected {cryptosuite} cryptosuite.`);
isValidUtf8(proof.cryptosuite).should.equal(
true,
'Expected cryptosuite value to be a valid UTF-8 encoded string.'
Expand Down
31 changes: 31 additions & 0 deletions tests/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
*/
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 {CachedResolver} from '@digitalbazaar/did-io';
import chai from 'chai';
import {createRequire} from 'node:module';
import {contexts as credContexts} from '@digitalbazaar/credentials-context';
import {isUtf8} from 'node:buffer';
import {JsonLdDocumentLoader} from 'jsonld-document-loader';
import {klona} from 'klona';
import {readFileSync} from 'fs';
import {v4 as uuidv4} from 'uuid';
Expand Down Expand Up @@ -313,3 +318,29 @@ export async function verifyError(verifier, securedCredential) {
const response = await verifier.post({json: body});
should.exist(response.error, 'Expected an error from verifier.');
}

export async function multikeyFromVerificationMethod(
verificationMethod, keyType) {
const cachedResolver = new CachedResolver();
const jdl = new JsonLdDocumentLoader();
jdl.addDocuments({documents: credContexts});
jdl.setDidResolver(cachedResolver);
const didKeyDriverMultikey = didKey.driver();
const prefixes = {
Ed25519: 'z6Mk',
'P-256': 'zDna',
'P-384': 'z82L'
};
try {
didKeyDriverMultikey.use({
multibaseMultikeyHeader: prefixes[keyType],
fromMultibase: EcdsaMultikey.from
});
cachedResolver.use(didKeyDriverMultikey);
const response = await jdl.documentLoader(verificationMethod);
return response.document.publicKeyMultibase;
} catch(error) {
// Do nothing on error
}
return null;
}