diff --git a/CHANGELOG.md b/CHANGELOG.md index 017c36a..69217d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/Ed25519Signature2020.js b/lib/Ed25519Signature2020.js index 4d3338b..e4e6a81 100644 --- a/lib/Ed25519Signature2020.js +++ b/lib/Ed25519Signature2020.js @@ -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'; @@ -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'; } @@ -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}); @@ -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( @@ -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; } diff --git a/package.json b/package.json index 29c42c3..3103a7c 100644 --- a/package.json +++ b/package.json @@ -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" }, @@ -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", diff --git a/test/Ed25519Signature2020.spec.js b/test/Ed25519Signature2020.spec.js index 57a4aa1..527fe6f 100644 --- a/test/Ed25519Signature2020.spec.js +++ b/test/Ed25519Signature2020.spec.js @@ -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'; @@ -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}; @@ -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".'); }); }); });