diff --git a/package.json b/package.json index 633efb7..e3da87c 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "@digitalbazaar/ed25519-verification-key-2018": "^4.0.0", "@digitalbazaar/ed25519-verification-key-2020": "^4.1.0", "@digitalbazaar/multikey-context": "^2.0.1", - "@digitalbazaar/security-document-loader": "^2.0.0", + "@digitalbazaar/security-document-loader": "^3.0.0", "c8": "^7.11.3", "chai": "^4.3.6", "cross-env": "^7.0.3", diff --git a/test/documentLoader.js b/test/documentLoader.js index ee8a7f6..12d6487 100644 --- a/test/documentLoader.js +++ b/test/documentLoader.js @@ -1,5 +1,5 @@ /*! - * Copyright (c) 2023 Digital Bazaar, Inc. All rights reserved. + * Copyright (c) 2023-2024 Digital Bazaar, Inc. All rights reserved. */ import { controllerDocEd25519Multikey, @@ -30,3 +30,40 @@ loader.addStatic( multikeyContext.constants.CONTEXT_URL, multikeyContext.contexts.get(multikeyContext.constants.CONTEXT_URL) ); + +loader.addStatic( + 'https://www.w3.org/ns/credentials/examples/v2', + { + '@context': { + '@vocab': 'https://www.w3.org/ns/credentials/examples#' + } + } +); + +// controller document for test vectors +loader.addStatic( + 'https://vc.example/issuers/5678', + { + '@context': [ + 'https://www.w3.org/ns/did/v1', + 'https://w3id.org/security/multikey/v1' + ], + id: 'https://vc.example/issuers/5678', + assertionMethod: { + id: 'https://vc.example/issuers/5678#z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2', + type: 'Multikey', + controller: 'https://vc.example/issuers/5678', + publicKeyMultibase: 'z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2' + } + } +); +loader.addStatic( + 'https://vc.example/issuers/5678#z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2', + { + '@context': 'https://w3id.org/security/multikey/v1', + id: 'https://vc.example/issuers/5678#z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2', + type: 'Multikey', + controller: 'https://vc.example/issuers/5678', + publicKeyMultibase: 'z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2' + } +); diff --git a/test/test-vectors.js b/test/test-vectors.js new file mode 100644 index 0000000..8bb2758 --- /dev/null +++ b/test/test-vectors.js @@ -0,0 +1,61 @@ +/*! + * Copyright (c) 2023-2024 Digital Bazaar, Inc. All rights reserved. + */ +/* Note: This file contains data generated from the vc-di-eddsa specification +test vectors. */ + +/* eslint-disable max-len */ +/* eslint-disable quote-props */ +/* eslint-disable quotes */ +export const keyMaterial = { + "publicKeyMultibase": "z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2", + "secretKeyMultibase": "z3u2en7t5LR2WtQH5PfFqMqwVHBeXouLzo6haApm8XHqvjxq" +}; + +// public key above converted to multikey format + controller doc: +export const publicKey = { + '@context': 'https://w3id.org/security/multikey/v1', + id: 'did:key:z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2#z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2', + type: 'Multikey', + controller: 'did:key:z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2', + publicKeyMultibase: 'z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2' +}; +export const controllerDoc = { + '@context': [ + 'https://www.w3.org/ns/did/v1', + 'https://w3id.org/security/multikey/v1' + ], + id: publicKey.controller, + assertionMethod: [publicKey] +}; + +export const signedFixture = { + "@context": [ + "https://www.w3.org/ns/credentials/v2", + "https://www.w3.org/ns/credentials/examples/v2" + ], + "id": "urn:uuid:58172aac-d8ba-11ed-83dd-0b3aef56cc33", + "type": [ + "VerifiableCredential", + "AlumniCredential" + ], + "name": "Alumni Credential", + "description": "A minimum viable example of an Alumni Credential.", + "issuer": "https://vc.example/issuers/5678", + "validFrom": "2023-01-01T00:00:00Z", + "credentialSubject": { + "id": "did:example:abcdefgh", + "alumniOf": "The School of Examples" + }, + "proof": { + "type": "DataIntegrityProof", + "cryptosuite": "eddsa-rdfc-2022", + "created": "2023-02-24T23:36:38Z", + "verificationMethod": "https://vc.example/issuers/5678#z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2", + "proofPurpose": "assertionMethod", + "proofValue": "z21EVs3eXERqTn4acNHT9viboqgzUaQ3kTmhPT3eA8qrVPE7CrQq78WkzctnMX5W4CrzcKnHw8V6dvy5pgWYCU5e9" + } +}; +/* eslint-enable quotes */ +/* eslint-enable quote-props */ +/* eslint-enable max-len */ diff --git a/test/test-vectors.spec.js b/test/test-vectors.spec.js new file mode 100644 index 0000000..3a4ac09 --- /dev/null +++ b/test/test-vectors.spec.js @@ -0,0 +1,66 @@ +/*! + * Copyright (c) 2023-2024 Digital Bazaar, Inc. All rights reserved. + */ +import * as Ed25519Multikey from '@digitalbazaar/ed25519-multikey'; +import {cryptosuite} from '../lib/index.js'; +import {DataIntegrityProof} from '@digitalbazaar/data-integrity'; +import {expect} from 'chai'; +import jsigs from 'jsonld-signatures'; +import {loader} from './documentLoader.js'; + +import * as testVectors from './test-vectors.js'; + +const {purposes: {AssertionProofPurpose}} = jsigs; + +const documentLoader = loader.build(); + +describe('test vectors', () => { + let keyPair; + before(async () => { + const {keyMaterial} = testVectors; + keyPair = await Ed25519Multikey.from(keyMaterial); + keyPair.controller = `did:key:${keyPair.publicKeyMultibase}`; + keyPair.id = `${keyPair.controller}#${keyPair.publicKeyMultibase}`; + + // FIXME: test fixture should be updated to use `did:key` + keyPair.controller = 'https://vc.example/issuers/5678'; + keyPair.id = keyPair.controller + + '#z6MkrJVnaZkeFzdQyMZu1cgjg7k1pZZ6pvBQ7XJPt4swbTQ2'; + }); + + it('should create proof', async () => { + const {signedFixture} = testVectors; + const unsigned = {...signedFixture}; + delete unsigned.proof; + + const signer = keyPair.signer(); + const date = new Date(signedFixture.proof.created); + + let error; + let signed; + try { + signed = await jsigs.sign(unsigned, { + suite: new DataIntegrityProof({cryptosuite, signer, date}), + purpose: new AssertionProofPurpose(), + documentLoader + }); + } catch(e) { + error = e; + } + + expect(error).to.not.exist; + expect(signed).to.deep.equal(signedFixture); + }); + + it('should verify signed fixture', async () => { + const {signedFixture} = testVectors; + + const result = await jsigs.verify(signedFixture, { + suite: new DataIntegrityProof({cryptosuite}), + purpose: new AssertionProofPurpose(), + documentLoader + }); + + expect(result.verified).to.be.true; + }); +});