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 proof sets and did web tests. #142

Merged
merged 8 commits into from
Apr 15, 2024
Prev Previous commit
Next Next commit
Add did:web issuance tests with proof sets.
  • Loading branch information
dlongley committed Apr 15, 2024
commit dafc38cf7d657413f72df6d00e9dc9023a5bfd49
247 changes: 247 additions & 0 deletions test/mocha/40-did-web-issuer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/*!
* Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
*/
import * as bedrock from '@bedrock/core';
import * as helpers from './helpers.js';
import {agent} from '@bedrock/https-agent';
import {createRequire} from 'node:module';
import {httpClient} from '@digitalbazaar/http-client';
import {klona} from 'klona';
import {mockData} from './mock.data.js';
import {v4 as uuid} from 'uuid';

const require = createRequire(import.meta.url);

const {baseUrl} = mockData;
const serviceType = 'vc-issuer';

// NOTE: using embedded context in mockCredential:
// https://www.w3.org/2018/credentials/examples/v1
const mockCredential = require('./mock-credential.json');
const mockCredentialV2 = require('./mock-credential-v2.json');

describe('issue using "did:web" issuer', () => {
let suites;
let capabilityAgent;
let noStatusListIssuerId;
let noStatusListIssuerRootZcap;
beforeEach(async () => {
// generate a proof set using all of these suites in each test
suites = [{
name: 'Ed25519Signature2020',
algorithm: 'Ed25519'
}, {
name: 'eddsa-rdfc-2022',
algorithm: 'Ed25519'
}, {
name: 'ecdsa-rdfc-2019',
algorithm: 'P-256'
}, {
name: 'ecdsa-sd-2023',
algorithm: 'P-256'
}, {
name: 'ecdsa-xi-2023',
algorithm: 'P-256'
}, {
name: 'bbs-2023',
algorithm: 'Bls12381G2'
}];

// provision dependencies
({capabilityAgent} = await helpers.provisionDependencies({status: false}));

// create keystore for capability agent
const keystoreAgent = await helpers.createKeystoreAgent({capabilityAgent});

// generate a `did:web` DID for the issuer
const {host} = bedrock.config.server;
const localId = uuid();
const did = `did:web:${encodeURIComponent(host)}:did-web:${localId}`;

// generate an assertion method key for each suite to use
for(const suite of suites) {
const {algorithm} = suite;
let assertionMethodKey;
const publicAliasTemplate = `${did}#{publicKeyMultibase}`;
if(['P-256', 'P-384', 'Bls12381G2'].includes(algorithm)) {
assertionMethodKey = await helpers._generateMultikey({
keystoreAgent,
type: `urn:webkms:multikey:${algorithm}`,
publicAliasTemplate
});
} else {
assertionMethodKey = await keystoreAgent.generateKey({
type: 'asymmetric',
publicAliasTemplate
});
}
suite.assertionMethodKey = assertionMethodKey;
}

// create EDV for storage (creating hmac and kak in the process)
const {
edvConfig,
hmac,
keyAgreementKey
} = await helpers.createEdv({capabilityAgent, keystoreAgent});

// get service agent to delegate to
const serviceAgentUrl =
`${baseUrl}/service-agents/${encodeURIComponent(serviceType)}`;
const {data: serviceAgent} = await httpClient.get(serviceAgentUrl, {agent});

// delegate edv, hmac, and key agreement key zcaps to service agent
const {id: edvId} = edvConfig;
const zcaps = {};
zcaps.edv = await helpers.delegate({
controller: serviceAgent.id,
delegator: capabilityAgent,
invocationTarget: edvId
});
const {keystoreId} = keystoreAgent;
zcaps.hmac = await helpers.delegate({
capability: `urn:zcap:root:${encodeURIComponent(keystoreId)}`,
controller: serviceAgent.id,
invocationTarget: hmac.id,
delegator: capabilityAgent
});
zcaps.keyAgreementKey = await helpers.delegate({
capability: `urn:zcap:root:${encodeURIComponent(keystoreId)}`,
controller: serviceAgent.id,
invocationTarget: keyAgreementKey.kmsId,
delegator: capabilityAgent
});

// delegate assertion method keys
for(const suite of suites) {
const {assertionMethodKey} = suite;
const zcap = await helpers.delegate({
capability: `urn:zcap:root:${encodeURIComponent(keystoreId)}`,
controller: serviceAgent.id,
invocationTarget: assertionMethodKey.kmsId,
delegator: capabilityAgent
});
suite.zcapReferenceIds = {
assertionMethod: uuid()
};
zcaps[suite.zcapReferenceIds.assertionMethod] = zcap;
}

// create issuer options
const issuerOptions = {
issuer: did,
cryptosuites: suites.map(
({name, zcapReferenceIds}) => ({name, zcapReferenceIds}))
};

// create `did:web` DID document for issuer
const didDocument = {
'@context': [
'https://www.w3.org/ns/did/v1',
'https://w3id.org/security/suites/ed25519-2020/v1',
'https://w3id.org/security/multikey/v1'
],
id: did,
verificationMethod: [],
assertionMethod: []
};
for(const {assertionMethodKey} of suites) {
const description = await assertionMethodKey.getKeyDescription();
delete description['@context'];
didDocument.verificationMethod.push(description);
didDocument.assertionMethod.push(description.id);
}
// add DID doc to map with DID docs to be served
mockData.didWebDocuments.set(localId, didDocument);

// create issuer instance w/ no status list options
const noStatusListIssuerConfig = await helpers.createIssuerConfig({
capabilityAgent, zcaps, issuerOptions
});
noStatusListIssuerId = noStatusListIssuerConfig.id;
noStatusListIssuerRootZcap =
`urn:zcap:root:${encodeURIComponent(noStatusListIssuerId)}`;
});
describe('/credentials/issue', () => {
it('issues a VC 1.1 credential with a proof set', async () => {
const credential = klona(mockCredential);
let error;
let result;
try {
const zcapClient = helpers.createZcapClient({capabilityAgent});
result = await zcapClient.write({
url: `${noStatusListIssuerId}/credentials/issue`,
capability: noStatusListIssuerRootZcap,
json: {
credential,
options: {
extraInformation: 'abc'
}
}
});
} catch(e) {
error = e;
}
assertNoError(error);
should.exist(result.data);
should.exist(result.data.verifiableCredential);
const {verifiableCredential} = result.data;
verifiableCredential.should.be.an('object');
should.exist(verifiableCredential['@context']);
should.exist(verifiableCredential.id);
should.exist(verifiableCredential.type);
should.exist(verifiableCredential.issuer);
should.exist(verifiableCredential.issuanceDate);
should.exist(verifiableCredential.credentialSubject);
verifiableCredential.credentialSubject.should.be.an('object');
should.not.exist(verifiableCredential.credentialStatus);
should.exist(verifiableCredential.proof);
verifiableCredential.proof.should.be.an('array');
verifiableCredential.proof.length.should.equal(suites.length);
const parsedCryptosuites = verifiableCredential.proof.map(
({type, cryptosuite}) => cryptosuite ?? type);
const expectedCryptosuites = suites.map(({name}) => name);
parsedCryptosuites.should.deep.equal(expectedCryptosuites);
});
it('issues a VC 2.0 credential with a proof set', async () => {
const credential = klona(mockCredentialV2);
let error;
let result;
try {
const zcapClient = helpers.createZcapClient({capabilityAgent});
result = await zcapClient.write({
url: `${noStatusListIssuerId}/credentials/issue`,
capability: noStatusListIssuerRootZcap,
json: {
credential,
options: {
extraInformation: 'abc',
mandatoryPointers: ['issuer']
}
}
});
} catch(e) {
error = e;
}
assertNoError(error);
should.exist(result.data);
should.exist(result.data.verifiableCredential);
const {verifiableCredential} = result.data;
verifiableCredential.should.be.an('object');
should.exist(verifiableCredential['@context']);
should.exist(verifiableCredential.id);
should.exist(verifiableCredential.type);
should.exist(verifiableCredential.issuer);
should.exist(verifiableCredential.credentialSubject);
verifiableCredential.credentialSubject.should.be.an('object');
should.not.exist(verifiableCredential.credentialStatus);
should.exist(verifiableCredential.proof);
verifiableCredential.proof.should.be.an('array');
verifiableCredential.proof.length.should.equal(suites.length);
const parsedCryptosuites = verifiableCredential.proof.map(
({type, cryptosuite}) => cryptosuite ?? type);
const expectedCryptosuites = suites.map(({name}) => name);
parsedCryptosuites.should.deep.equal(expectedCryptosuites);
});
});
});
13 changes: 6 additions & 7 deletions test/mocha/helpers.js
Original file line number Diff line number Diff line change
@@ -340,24 +340,23 @@ async function keyResolver({id}) {
return data;
}

export async function provisionDependencies({suiteOptions}) {
export async function provisionDependencies({suiteOptions, status = true}) {
const secret = '53ad64ce-8e1d-11ec-bb12-10bf48838a41';
const handle = 'test';
const capabilityAgent = await CapabilityAgent.fromSecret({secret, handle});
if(!status) {
return {capabilityAgent};
}

// create keystore for capability agent
const keystoreAgent = await createKeystoreAgent({capabilityAgent});

const {
statusConfig,
issuerCreateStatusListZcap
} = await provisionStatus({
capabilityAgent, keystoreAgent, suiteOptions
});
} = await provisionStatus({capabilityAgent, keystoreAgent, suiteOptions});

return {
statusConfig, issuerCreateStatusListZcap, capabilityAgent
};
return {statusConfig, issuerCreateStatusListZcap, capabilityAgent};
}

export async function provisionIssuerForStatus({
Loading