Skip to content

Commit

Permalink
Support multikey and loosen key expression requirements.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlongley committed Jun 15, 2024
1 parent 45ad663 commit 3c6c228
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 45 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# @digitalbazaar/ed25519-signature-2020 Changelog

## 5.3.0 - 2024-mm-dd

### Added
- Add support for `Multikey` verification methods.

### Changed
- Loosen restrictions on verification methods that do not have
contexts, allowing processing of well-known types in those cases.
- Allow `publiKeyJwk` to be used to express key material.

## 5.2.0 - 2023-02-13

### Removed
Expand Down
38 changes: 19 additions & 19 deletions lib/Ed25519Signature2020.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
/*!
* Copyright (c) 2020-2021 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2020-2024 Digital Bazaar, Inc. All rights reserved.
*/
import jsigs from 'jsonld-signatures';
const {suites: {LinkedDataSignature}} = jsigs;
import * as base58btc from 'base58-universal';
import * as Ed25519Multikey from '@digitalbazaar/ed25519-multikey';
import {
Ed25519VerificationKey2020
} from '@digitalbazaar/ed25519-verification-key-2020';
import suiteContext2018 from 'ed25519-signature-2018-context';
import jsigs from 'jsonld-signatures';
const {suites: {LinkedDataSignature}} = jsigs;
import suiteContext2020 from 'ed25519-signature-2020-context';

// 'https://w3id.org/security/suites/ed25519-2020/v1'
const SUITE_CONTEXT_URL = suiteContext2020.constants.CONTEXT_URL;
// 'https://w3id.org/security/suites/ed25519-2018/v1'
const SUITE_CONTEXT_URL_2018 = suiteContext2018.constants.CONTEXT_URL;

// multibase base58-btc header
const MULTIBASE_BASE58BTC_HEADER = 'z';

Expand Down Expand Up @@ -56,8 +55,9 @@ export class Ed25519Signature2020 extends LinkedDataSignature {
key, signer, verifier, proof, date, useNativeCanonize,
canonizeOptions
});
// Some operations may be performed with Ed25519VerificationKey2018.
// So, Ed25519VerificationKey2020 is recommended, but not strictly required.
// some operations may be performed with `Ed25519VerificationKey2018` or
// `Multikey`; so, `Ed25519VerificationKey2020` is recommended, but not
// strictly required
this.requiredKeyType = 'Ed25519VerificationKey2020';
}

Expand Down Expand Up @@ -109,7 +109,7 @@ export class Ed25519Signature2020 extends LinkedDataSignature {

let {verifier} = this;
if(!verifier) {
const key = await this.LDKeyClass.from(verificationMethod);
const key = await Ed25519Multikey.from(verificationMethod);
verifier = key.verifier();
}
return verifier.verify({data: verifyData, signature: signatureBytes});
Expand All @@ -119,14 +119,10 @@ export class Ed25519Signature2020 extends LinkedDataSignature {
let contextUrl;
if(verificationMethod.type === 'Ed25519VerificationKey2020') {
contextUrl = SUITE_CONTEXT_URL;
} else if(verificationMethod.type === 'Ed25519VerificationKey2018') {
contextUrl = SUITE_CONTEXT_URL_2018;
} else {
throw new Error(`Unsupported key type "${verificationMethod.type}".`);
}
if(!_includesContext({
document: verificationMethod, contextUrl
})) {
if(!_includesContext({document: verificationMethod, contextUrl})) {
// For DID Documents, since keys do not have their own contexts,
// the suite context is usually provided by the documentLoader logic
throw new TypeError(
Expand Down Expand Up @@ -163,12 +159,16 @@ export class Ed25519Signature2020 extends LinkedDataSignature {
verificationMethod = typeof document === 'string' ?
JSON.parse(document) : document;

// for maximum compatibility, import using multikey library and convert to
// type `Ed25519Signature2020`
const key = await Ed25519Multikey.from(verificationMethod);
verificationMethod = {
...await key.export({publicKey: true, includeContext: true}),
'@context': SUITE_CONTEXT_URL,
type: 'Ed25519VerificationKey2020'
};
await this.assertVerificationMethod({verificationMethod});
if(verificationMethod.type === 'Ed25519VerificationKey2018') {
verificationMethod = (await Ed25519VerificationKey2020
.fromEd25519VerificationKey2018({keyPair: verificationMethod}))
.export({publicKey: true, includeContext: true});
}

return verificationMethod;
}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
"lib/**/*.js"
],
"dependencies": {
"@digitalbazaar/ed25519-multikey": "^1.1.0",
"@digitalbazaar/ed25519-verification-key-2020": "^4.1.0",
"base58-universal": "^2.0.0",
"ed25519-signature-2018-context": "^1.1.0",
"ed25519-signature-2020-context": "^1.1.0",
"jsonld-signatures": "^11.1.0"
},
Expand All @@ -26,6 +26,7 @@
"c8": "^7.12.0",
"chai": "^4.3.7",
"cross-env": "^7.0.3",
"ed25519-signature-2018-context": "^1.1.0",
"eslint": "^8.34.0",
"eslint-config-digitalbazaar": "^4.2.0",
"eslint-plugin-jsdoc": "^40.0.0",
Expand Down
28 changes: 3 additions & 25 deletions test/Ed25519Signature2020.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*!
* Copyright (c) 2021-2022 Digital Bazaar, Inc. All rights reserved.
* Copyright (c) 2021-2024 Digital Bazaar, Inc. All rights reserved.
*/
import {expect} from 'chai';

Expand Down Expand Up @@ -416,27 +416,6 @@ describe('Ed25519Signature2020', () => {
});
expect(result.verified).to.be.true;
});
it('should throw error when verification method does not have' +
'2018 context', async () => {
const mockPublicKey2018WithoutContext = {...mockPublicKey2018};
// intentionally delete the context
delete mockPublicKey2018WithoutContext['@context'];
loader.addStatic(mockKeyPair2018.controller, controllerDoc2018);
loader.addStatic(mockPublicKey2018WithoutContext.id,
mockPublicKey2018WithoutContext);
const documentLoader = loader.build();
const suite = new Ed25519Signature2020();
const result = await jsigs.verify(signedCredential, {
suite,
purpose: new AssertionProofPurpose(),
documentLoader
});
expect(result.verified).to.be.false;
expect(result.results[0].error.name).equal('TypeError');
expect(result.results[0].error.message).equal(
'The verification method (key) must contain ' +
'\"https://w3id.org/security/suites/ed25519-2018/v1\" context.');
});
it('should throw error when verification method contains 2018 key ' +
'with (not-matching) 2020 context', async () => {
const mockPublicKey2018With2020Context = {...mockPublicKey2018};
Expand All @@ -454,10 +433,9 @@ describe('Ed25519Signature2020', () => {
documentLoader
});
expect(result.verified).to.be.false;
expect(result.results[0].error.name).equal('TypeError');
expect(result.results[0].error.message).equal(
'The verification method (key) must contain ' +
'\"https://w3id.org/security/suites/ed25519-2018/v1\" context.');
'Context not supported ' +
'"https://w3id.org/security/suites/ed25519-2020/v1".');
});
});
});

0 comments on commit 3c6c228

Please sign in to comment.