From 59eba1166938ea20dabc5b59e970bbdc1027bd78 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Wed, 7 Apr 2021 08:17:53 -0400 Subject: [PATCH 01/48] Commit LD Changes --- packages/credential-w3c/package.json | 5 +- packages/credential-w3c/src/action-handler.ts | 64 +++++++++++++++++-- packages/credential-w3c/src/index.ts | 2 +- .../credential-w3c/src/message-handler.ts | 2 + .../types/jsonld-signatures/index.d.ts | 1 + .../credential-w3c/types/jsonld/index.d.ts | 1 + 6 files changed, 68 insertions(+), 7 deletions(-) create mode 100644 packages/credential-w3c/types/jsonld-signatures/index.d.ts create mode 100644 packages/credential-w3c/types/jsonld/index.d.ts diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index 13c13ca36..bf3fe6b2d 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -21,7 +21,10 @@ "blakejs": "^1.1.0", "debug": "^4.1.1", "did-jwt-vc": "2.1.7", - "did-resolver": "3.1.3" + "did-resolver": "3.1.3", + "jsonld-signatures": "^7.0.0", + "uint8arrays": "^2.1.3", + "@stablelib/ed25519": "^1.0.1" }, "devDependencies": { "@types/debug": "4.1.7", diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 1d5568b4c..4aadd4fbb 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -21,6 +21,10 @@ import { PresentationPayload, } from 'did-jwt-vc' +import * as jsigs from 'jsonld-signatures' +import * as ed25519 from '@stablelib/ed25519' +import documentLoaders from 'jsonld' + import { schema } from './' import Debug from 'debug' @@ -29,11 +33,11 @@ const debug = Debug('veramo:w3c:action-handler') /** * The type of encoding to be used for the Verifiable Credential or Presentation to be generated. * - * Only `jwt` is supported at the moment. + * Only `jwt` and `lds` is supported at the moment. * * @public */ -export type EncodingFormat = 'jwt' // | "json" | "json-ld" +export type ProofFormat = 'jwt' | 'lds' /** * Encapsulates the parameters required to create a @@ -63,7 +67,7 @@ export interface ICreateVerifiablePresentationArgs { * The desired format for the VerifiablePresentation to be created. * Currently, only JWT is supported */ - proofFormat: EncodingFormat + proofFormat: ProofFormat /** * Remove payload members during JWT-JSON transformation. Defaults to `true`. @@ -100,7 +104,7 @@ export interface ICreateVerifiableCredentialArgs { * The desired format for the VerifiablePresentation to be created. * Currently, only JWT is supported */ - proofFormat: EncodingFormat + proofFormat: ProofFormat /** * Remove payload members during JWT-JSON transformation. Defaults to `true`. @@ -162,9 +166,47 @@ export type IContext = IAgentContext< IResolver & Pick & Pick & - Pick + Pick > + +//------------------------- BEGIN JSON_LD HELPER / DELEGATING STUFF + +const createSigner = (function (key: IKey) { + if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) + + debug(key) + + const privateKeyBytes = Uint8Array.from(Buffer.from(key.privateKeyHex, 'hex')) + const getSignatureBytes = function({ data }: any) { + const signature = ed25519.sign(privateKeyBytes, Uint8Array.from(Buffer.from(data, 'utf8'))) + return signature + }; + return { sign: getSignatureBytes } +}); + +const signLdDocEd25519Sig = async (doc: W3CCredential, key: IKey): Promise => { + if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) + console.log('PrivateKey: ' + key.privateKeyHex) + + const { node: documentLoader } = documentLoaders; + + return await jsigs.sign(doc, { + documentLoader, + suite: new jsigs.suites.Ed25519Signature2018({ + verificationMethod: key.kid, // TODO: change kid. + signer: createSigner(key), + }), + purpose: new jsigs.purposes.AuthenticationProofPurpose({ + challenge: "abc", + domain: "example.com", + }), + }) +} + +//------------------------- END JSON_LD HELPER / DELEGATING STUFF + + /** * A Veramo plugin that implements the {@link ICredentialIssuer} methods. * @@ -263,6 +305,18 @@ export class CredentialIssuer implements IAgentPlugin { //FIXME: `args` should allow picking a key or key type const key = identifier.keys.find((k) => k.type === 'Secp256k1' || k.type === 'Ed25519') if (!key) throw Error('No signing key for ' + identifier.did) + + //------------------------- BEGIN JSON_LD INSERT + + if (args.proofFormat === 'lds') { + const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) + if (keyPayload.type !== 'Ed25519') throw Error('LDS signing only with Ed25519!' + identifier.did) + const resultVC = signLdDocEd25519Sig(credential, keyPayload) + return resultVC + } + + //------------------------- END JSON_LD INSERT + //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` debug('Signing VC with', identifier.did) let alg = 'ES256K' diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index 6bb4c035a..fd1725200 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -13,7 +13,7 @@ export { ICredentialIssuer, ICreateVerifiableCredentialArgs, ICreateVerifiablePresentationArgs, - EncodingFormat, + ProofFormat, } from './action-handler' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index f2ce0e184..dc56fe8bc 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -45,6 +45,8 @@ export type IContext = IAgentContext * @public */ export class W3cMessageHandler extends AbstractMessageHandler { + + async handle(message: Message, context: IContext): Promise { const meta = message.getLastMetaData() diff --git a/packages/credential-w3c/types/jsonld-signatures/index.d.ts b/packages/credential-w3c/types/jsonld-signatures/index.d.ts new file mode 100644 index 000000000..397663721 --- /dev/null +++ b/packages/credential-w3c/types/jsonld-signatures/index.d.ts @@ -0,0 +1 @@ +declare module 'jsonld-signatures' diff --git a/packages/credential-w3c/types/jsonld/index.d.ts b/packages/credential-w3c/types/jsonld/index.d.ts new file mode 100644 index 000000000..b07278854 --- /dev/null +++ b/packages/credential-w3c/types/jsonld/index.d.ts @@ -0,0 +1 @@ +declare module 'jsonld' From dfcd4210a89a2ed0832cefb0193074bfed3257bd Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Thu, 15 Apr 2021 08:23:17 -0500 Subject: [PATCH 02/48] tests: started to add integration tests for JSON-LD Integration --- __tests__/localAgent.test.ts | 6 ++++-- ...verifiableData.ts => verifiableDataJWT.ts} | 0 __tests__/shared/verifiableDataLD.ts | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) rename __tests__/shared/{verifiableData.ts => verifiableDataJWT.ts} (100%) create mode 100644 __tests__/shared/verifiableDataLD.ts diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index b1cd47902..88b09271a 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -57,7 +57,8 @@ import fs from 'fs' jest.setTimeout(30000) // Shared tests -import verifiableData from './shared/verifiableData' +import verifiableDataJWT from './shared/verifiableDataJWT' +import verifiableDataLD from './shared/verifiableDataLD' import handleSdrMessage from './shared/handleSdrMessage' import resolveDid from './shared/resolveDid' import webDidFlow from './shared/webDidFlow' @@ -228,7 +229,8 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local integration tests', () => { - verifiableData(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) handleSdrMessage(testContext) resolveDid(testContext) webDidFlow(testContext) diff --git a/__tests__/shared/verifiableData.ts b/__tests__/shared/verifiableDataJWT.ts similarity index 100% rename from __tests__/shared/verifiableData.ts rename to __tests__/shared/verifiableDataJWT.ts diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts new file mode 100644 index 000000000..99e13a3e3 --- /dev/null +++ b/__tests__/shared/verifiableDataLD.ts @@ -0,0 +1,19 @@ +import { TAgent, IDIDManager, IDataStore } from '../../packages/core/src' +import { IDataStoreORM } from '../../packages/data-store/src' +import { ICredentialIssuer } from '../../packages/credential-w3c/src' + +type ConfiguredAgent = TAgent + +export default (testContext: { + getAgent: () => ConfiguredAgent + setup: () => Promise + tearDown: () => Promise +}) => { + describe('dummyTest', () => { + + it('should create identifier', async () => { + const a = 3 + 7 + expect(a).toEqual(10) + }) + }) +} From 29f3d48ea5efa1e2c7467f5fce194e76a88feffe Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Thu, 15 Apr 2021 18:02:35 +0300 Subject: [PATCH 03/48] fix: missing deps --- __tests__/localMemoryStoreAgent.test.ts | 6 ++++-- __tests__/restAgent.test.ts | 6 ++++-- packages/credential-w3c/package.json | 7 ++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 82146d484..80939b83b 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -42,7 +42,8 @@ import fs from 'fs' jest.setTimeout(30000) // Shared tests -import verifiableData from './shared/verifiableData' +import verifiableDataJWT from './shared/verifiableDataJWT' +import verifiableDataLD from './shared/verifiableDataLD' import handleSdrMessage from './shared/handleSdrMessage' import resolveDid from './shared/resolveDid' import webDidFlow from './shared/webDidFlow' @@ -183,7 +184,8 @@ const getAgent = () => agent const testContext = { getAgent, setup, tearDown } describe('Local in-memory integration tests', () => { - verifiableData(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) handleSdrMessage(testContext) resolveDid(testContext) webDidFlow(testContext) diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 03499b8d1..9108331bb 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -62,7 +62,8 @@ import fs from 'fs' jest.setTimeout(30000) // Shared tests -import verifiableData from './shared/verifiableData' +import verifiableDataJWT from './shared/verifiableDataJWT' +import verifiableDataLD from './shared/verifiableDataLD' import handleSdrMessage from './shared/handleSdrMessage' import resolveDid from './shared/resolveDid' import webDidFlow from './shared/webDidFlow' @@ -233,7 +234,8 @@ const tearDown = async (): Promise => { const testContext = { getAgent, setup, tearDown } describe('REST integration tests', () => { - verifiableData(testContext) + verifiableDataJWT(testContext) + verifiableDataLD(testContext) handleSdrMessage(testContext) resolveDid(testContext) webDidFlow(testContext) diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index bf3fe6b2d..d1fcbac64 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -14,6 +14,7 @@ } }, "dependencies": { + "@stablelib/ed25519": "^1.0.1", "@veramo/core": "^3.1.0", "@veramo/did-jwt": "^3.1.0", "@veramo/did-resolver": "^3.1.0", @@ -22,9 +23,9 @@ "debug": "^4.1.1", "did-jwt-vc": "2.1.7", "did-resolver": "3.1.3", - "jsonld-signatures": "^7.0.0", - "uint8arrays": "^2.1.3", - "@stablelib/ed25519": "^1.0.1" + "jsonld": "^5.2.0", + "jsonld-signatures": "^9.0.2", + "uint8arrays": "^2.1.3" }, "devDependencies": { "@types/debug": "4.1.7", From d8feab7fbb8bb190e38aec4ef011c21f59fc208c Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Thu, 15 Apr 2021 10:58:48 -0500 Subject: [PATCH 04/48] fix: Fixed failing tests. --- .../src/__tests__/action-handler.test.ts | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index 9f54397b6..d3031f5e2 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -1,4 +1,10 @@ -import { W3CCredential, VerifiableCredential, IIdentifier, W3CPresentation } from '@veramo/core' +import { + W3CCredential, + VerifiableCredential, + IIdentifier, + W3CPresentation, + VerifiablePresentation, IKey, +} from '@veramo/core' const mockDidJwtVc = { createVerifiableCredentialJwt: jest.fn().mockReturnValue('mockVcJwt'), @@ -66,9 +72,15 @@ let agent = { getDIDComponentById: jest.fn(), emit: jest.fn(), keyManagerSign: jest.fn().mockImplementation(async (args): Promise => 'mockJWT'), - dataStoreSaveVerifiableCredential: jest.fn().mockImplementation(async (args): Promise => true), - dataStoreSaveVerifiablePresentation: jest.fn().mockImplementation(async (args): Promise => true), - getSchema: jest.fn(), + keyManagerGet: jest.fn().mockImplementation(async (args): Promise => ({ + kid: '', + kms: '', + type: 'Ed25519', + publicKeyHex: '', + })), + dataStoreSaveVerifiableCredential: jest.fn().mockImplementation(async (args): Promise => true), + dataStoreSaveVerifiablePresentation: jest.fn().mockImplementation(async (args): Promise => true), + getSchema: jest.fn(), didManagerGet: jest.fn(), } From c3be9de9f2bce2a4db24080b9634e4b10a6db00b Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Fri, 16 Apr 2021 08:17:47 -0500 Subject: [PATCH 05/48] feat: added Secp256k1 support. --- __tests__/shared/verifiableDataJWT.ts | 4 +- __tests__/shared/verifiableDataLD.ts | 46 ++++++++++-- packages/credential-w3c/package.json | 9 ++- packages/credential-w3c/src/action-handler.ts | 70 +++++++++++-------- .../types/jsonld-signatures/index.d.ts | 1 - 5 files changed, 93 insertions(+), 37 deletions(-) delete mode 100644 packages/credential-w3c/types/jsonld-signatures/index.d.ts diff --git a/__tests__/shared/verifiableDataJWT.ts b/__tests__/shared/verifiableDataJWT.ts index 0a2188ecc..1db3707dd 100644 --- a/__tests__/shared/verifiableDataJWT.ts +++ b/__tests__/shared/verifiableDataJWT.ts @@ -10,7 +10,7 @@ export default (testContext: { setup: () => Promise tearDown: () => Promise }) => { - describe('creating Verifiable Credentials', () => { + describe('creating Verifiable Credentials in JWT', () => { let agent: ConfiguredAgent let identifier: IIdentifier @@ -25,7 +25,7 @@ export default (testContext: { expect(identifier).toHaveProperty('did') }) - it('should create verifiable credential', async () => { + it('should create verifiable credential in JWT', async () => { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index 99e13a3e3..b4bcc1176 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -1,4 +1,4 @@ -import { TAgent, IDIDManager, IDataStore } from '../../packages/core/src' +import { TAgent, IDIDManager, IDataStore, IIdentifier } from '../../packages/core/src' import { IDataStoreORM } from '../../packages/data-store/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' @@ -9,11 +9,49 @@ export default (testContext: { setup: () => Promise tearDown: () => Promise }) => { - describe('dummyTest', () => { + describe('creating Verifiable Credentials in LD', () => { + let agent: ConfiguredAgent + let identifier: IIdentifier + + beforeAll(() => { + testContext.setup() + agent = testContext.getAgent() + }) + afterAll(testContext.tearDown) it('should create identifier', async () => { - const a = 3 + 7 - expect(a).toEqual(10) + identifier = await agent.didManagerCreate({ kms: 'local' }) + expect(identifier).toHaveProperty('did') + }) + + it('should create verifiable credential in LD', async () => { + const verifiableCredential = await agent.createVerifiableCredential({ + credential: { + issuer: { id: identifier.did }, + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://example.com/1/2/3'], + type: ['VerifiableCredential', 'Custom'], + issuanceDate: new Date().toISOString(), + credentialSubject: { + id: 'did:web:example.com', + you: 'Rock', + }, + }, + proofFormat: 'lds', + }) + + expect(verifiableCredential).toHaveProperty('proof.jwt') + expect(verifiableCredential['@context']).toEqual([ + 'https://www.w3.org/2018/credentials/v1', + 'https://example.com/1/2/3', + ]) + expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Custom']) + + const hash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) + expect(typeof hash).toEqual('string') + + const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash }) + expect(verifiableCredential).toEqual(verifiableCredential2) }) + }) } diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index d1fcbac64..f53662c51 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -19,18 +19,25 @@ "@veramo/did-jwt": "^3.1.0", "@veramo/did-resolver": "^3.1.0", "@veramo/message-handler": "^3.1.0", + "base-58": "^0.0.1", "blakejs": "^1.1.0", "debug": "^4.1.1", "did-jwt-vc": "2.1.7", "did-resolver": "3.1.3", + "uint8arrays": "^2.1.3", + "ecdsa-secp256k1-signature-2019": "^1.0.1", "jsonld": "^5.2.0", "jsonld-signatures": "^9.0.2", - "uint8arrays": "^2.1.3" + "secp256k1-key-pair": "git+https://github.com/digitalbazaar/ecdsa-secp256k1-verification-key-2019.git", + "vc-js": "^0.6.4" }, "devDependencies": { "@types/debug": "4.1.7", "typescript": "4.5.2" }, + "resolutions": { + "secp256k1-key-pair": "git+https://github.com/digitalbazaar/ecdsa-secp256k1-verification-key-2019.git" + }, "files": [ "build/**/*", "src/**/*", diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 4aadd4fbb..294a03a40 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -21,9 +21,13 @@ import { PresentationPayload, } from 'did-jwt-vc' -import * as jsigs from 'jsonld-signatures' -import * as ed25519 from '@stablelib/ed25519' import documentLoaders from 'jsonld' +const vc = require('vc-js'); +const {Ed25519KeyPair, suites: {Ed25519Signature2018}} = + require('jsonld-signatures'); +const EcdsaSepc256k1Signature2019 = require('ecdsa-secp256k1-signature-2019'); +const Secp256k1KeyPair = require('secp256k1-key-pair'); +const Base58 = require('base-58') import { schema } from './' @@ -172,36 +176,46 @@ export type IContext = IAgentContext< //------------------------- BEGIN JSON_LD HELPER / DELEGATING STUFF -const createSigner = (function (key: IKey) { - if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) - - debug(key) - - const privateKeyBytes = Uint8Array.from(Buffer.from(key.privateKeyHex, 'hex')) - const getSignatureBytes = function({ data }: any) { - const signature = ed25519.sign(privateKeyBytes, Uint8Array.from(Buffer.from(data, 'utf8'))) - return signature - }; - return { sign: getSignatureBytes } -}); - -const signLdDocEd25519Sig = async (doc: W3CCredential, key: IKey): Promise => { +// const createSigner = (function (key: IKey) { +// if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) +// +// debug(key) +// +// const privateKeyBytes = Uint8Array.from(Buffer.from(key.privateKeyHex, 'hex')) +// const getSignatureBytes = function({ data }: any) { +// const signature = ed25519.sign(privateKeyBytes, Uint8Array.from(Buffer.from(data, 'utf8'))) +// return signature +// }; +// return { sign: getSignatureBytes } +// }); + +const signLdDoc = async (credential: W3CCredential, key: IKey): Promise => { if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) console.log('PrivateKey: ' + key.privateKeyHex) const { node: documentLoader } = documentLoaders; + let suite + const keyDocOptions = { + publicKeyBase58: Base58.encode(Buffer.from(key.privateKeyHex, 'hex')), + privateKeyBase58: Base58.encode(Buffer.from(key.privateKeyHex, 'hex')) + } + switch(key.type) { + case 'Secp256k1': + suite = new EcdsaSepc256k1Signature2019( + {key: new Secp256k1KeyPair(keyDocOptions)}); + break; + case 'Ed25519': + suite = new Ed25519Signature2018({key: new Ed25519KeyPair(keyDocOptions)}); + break; + default: + throw new Error(`Unknown key type ${key.type}.`); + } - return await jsigs.sign(doc, { + return await vc.issue({ + credential, + suite, documentLoader, - suite: new jsigs.suites.Ed25519Signature2018({ - verificationMethod: key.kid, // TODO: change kid. - signer: createSigner(key), - }), - purpose: new jsigs.purposes.AuthenticationProofPurpose({ - challenge: "abc", - domain: "example.com", - }), - }) + }); } //------------------------- END JSON_LD HELPER / DELEGATING STUFF @@ -310,9 +324,7 @@ export class CredentialIssuer implements IAgentPlugin { if (args.proofFormat === 'lds') { const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - if (keyPayload.type !== 'Ed25519') throw Error('LDS signing only with Ed25519!' + identifier.did) - const resultVC = signLdDocEd25519Sig(credential, keyPayload) - return resultVC + return signLdDoc(credential, keyPayload) } //------------------------- END JSON_LD INSERT diff --git a/packages/credential-w3c/types/jsonld-signatures/index.d.ts b/packages/credential-w3c/types/jsonld-signatures/index.d.ts deleted file mode 100644 index 397663721..000000000 --- a/packages/credential-w3c/types/jsonld-signatures/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'jsonld-signatures' From 1f61cb62951d0d245a8db4aca2f5da323a4731e7 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Fri, 16 Apr 2021 16:12:52 -0500 Subject: [PATCH 06/48] started implementation of verifyVerifiablePresesntation. --- __tests__/shared/verifiableDataLD.ts | 36 +++--- packages/credential-w3c/src/action-handler.ts | 107 ++++++++++++++++-- packages/credential-w3c/src/contexts.ts | 16 +++ .../src/contexts/socialmedia-v1.jsonld | 7 ++ 4 files changed, 143 insertions(+), 23 deletions(-) create mode 100644 packages/credential-w3c/src/contexts.ts create mode 100644 packages/credential-w3c/src/contexts/socialmedia-v1.jsonld diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index b4bcc1176..ecb7c7a0e 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -28,29 +28,33 @@ export default (testContext: { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://example.com/1/2/3'], - type: ['VerifiableCredential', 'Custom'], + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://veramo.io/contexts/socialmedia/v1' + ], + type: ['VerifiableCredential', 'VerifableSocialMediaPosting'], issuanceDate: new Date().toISOString(), credentialSubject: { - id: 'did:web:example.com', - you: 'Rock', + // id: 'did:web:example.com', + // you: 'Rock', }, }, proofFormat: 'lds', }) - expect(verifiableCredential).toHaveProperty('proof.jwt') - expect(verifiableCredential['@context']).toEqual([ - 'https://www.w3.org/2018/credentials/v1', - 'https://example.com/1/2/3', - ]) - expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Custom']) - - const hash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) - expect(typeof hash).toEqual('string') - - const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash }) - expect(verifiableCredential).toEqual(verifiableCredential2) + console.log(verifiableCredential) + // expect(verifiableCredential).toHaveProperty('proof.jwt') + // expect(verifiableCredential['@context']).toEqual([ + // 'https://www.w3.org/2018/credentials/v1', + // 'https://example.com/1/2/3', + // ]) + // expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Custom']) + // + // const hash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) + // expect(typeof hash).toEqual('string') + // + // const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash }) + // expect(verifiableCredential).toEqual(verifiableCredential2) }) }) diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 294a03a40..1c5483a0b 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -21,16 +21,19 @@ import { PresentationPayload, } from 'did-jwt-vc' -import documentLoaders from 'jsonld' +// Start LOAD LD Libraries const vc = require('vc-js'); -const {Ed25519KeyPair, suites: {Ed25519Signature2018}} = +const { defaultDocumentLoader } = vc; +const {Ed25519KeyPair, extendContextLoader, suites: {Ed25519Signature2018}} = require('jsonld-signatures'); const EcdsaSepc256k1Signature2019 = require('ecdsa-secp256k1-signature-2019'); const Secp256k1KeyPair = require('secp256k1-key-pair'); const Base58 = require('base-58') +// Start END LD Libraries -import { schema } from './' +import { schema } from './' +import localContexts from './contexts' import Debug from 'debug' const debug = Debug('veramo:w3c:action-handler') @@ -117,6 +120,45 @@ export interface ICreateVerifiableCredentialArgs { removeOriginalFields?: boolean } +/** + * Encapsulates the parameters required to verify a + * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} + * + * @public + */ +export interface IVerifyVerifiableCredentialArgs { + /** + * The json payload of the Credential according to the + * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} + * + * The signer of the Credential is chosen based on the `issuer.id` property + * of the `credential` + * + */ + credential: { + '@context'?: string[] + id?: string + type?: string[] + issuer: { id: string; [x: string]: any } + issuanceDate?: string + expirationDate?: string + credentialSubject: { + id?: string + [x: string]: any + } + credentialStatus?: { + id: string + type: string + } + proof: { + type: string + jwt?: string + [x: string]: any + } + [x: string]: any + } +} + /** * The interface definition for a plugin that can generate Verifiable Credentials and Presentations * @@ -158,6 +200,24 @@ export interface ICredentialIssuer extends IPluginMethodMap { args: ICreateVerifiableCredentialArgs, context: IContext, ): Promise + + + /** + * Verifies a Verifiable Credential JWT or LDS Format. + * The payload, signer and format are chosen based on the `args` parameter. + * + * @param args - Arguments necessary to verify a VerifiableCredential + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the {@link @veramo/core#???} that was requested or rejects with an error + * if there was a problem with the input or while getting the key to signs + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} + */ + verifyVerifiableCredential( + args: IVerifyVerifiableCredentialArgs, + context: IContext, + ): Promise } /** @@ -193,16 +253,17 @@ const signLdDoc = async (credential: W3CCredential, key: IKey): Promise { + console.log(`resolving context for: ${url}`) + + if (localContexts.has(url)) { + console.log(`Returning local context for: ${url}`) + return { + contextUrl: null, + documentUrl: url, + document: localContexts.get(url) + }; + } + + return defaultDocumentLoader(url); + }); + return await vc.issue({ credential, suite, @@ -234,6 +310,7 @@ export class CredentialIssuer implements IAgentPlugin { this.methods = { createVerifiablePresentation: this.createVerifiablePresentation, createVerifiableCredential: this.createVerifiableCredential, + verifyVerifiableCredential: this.verifyVerifiableCredential, } } @@ -354,6 +431,22 @@ export class CredentialIssuer implements IAgentPlugin { return Promise.reject(error) } } + + /** {@inheritdoc ICredentialIssuer.createVerifiablePresentation} */ + async verifyVerifiableCredential( + args: IVerifyVerifiableCredentialArgs, + context: IContext, + ): Promise { + const credential = args.credential + // JWT + if (credential.proof.jwt) { + + } + + // EcdsaSecp256k1Signature2019 + // + return false + } } function wrapSigner( diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/contexts.ts new file mode 100644 index 000000000..98e0f45f3 --- /dev/null +++ b/packages/credential-w3c/src/contexts.ts @@ -0,0 +1,16 @@ + +import * as fs from 'fs'; +import * as path from 'path'; + +function _read(_path: string) { + return JSON.parse( + fs.readFileSync( + path.join(__dirname, _path), + {encoding: 'utf8'})); +} + +const contexts = new Map([ + ['https://veramo.io/contexts/socialmedia/v1', _read('./contexts/socialmedia-v1.jsonld')], +]); + +export default contexts; diff --git a/packages/credential-w3c/src/contexts/socialmedia-v1.jsonld b/packages/credential-w3c/src/contexts/socialmedia-v1.jsonld new file mode 100644 index 000000000..50e0ed8c4 --- /dev/null +++ b/packages/credential-w3c/src/contexts/socialmedia-v1.jsonld @@ -0,0 +1,7 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "VerifableSocialMediaPosting": "https://veramo.io/contexts/socialmedia#VerifableSocialMediaPosting" + } +} From 8be5943a17d9234fe8e267b3ac93cd1a38737221 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Sun, 18 Apr 2021 23:27:17 -0500 Subject: [PATCH 07/48] feat: finished EcdsaSecp256k1RecoverySignature2020 and Ed25519Signature2020 credential signature and verification. --- __tests__/shared/verifiableDataLD.ts | 18 ++- packages/credential-w3c/package.json | 8 +- packages/credential-w3c/src/action-handler.ts | 144 +++++++++++++----- packages/credential-w3c/src/contexts.ts | 2 + 4 files changed, 129 insertions(+), 43 deletions(-) diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index ecb7c7a0e..fd887f691 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -24,6 +24,13 @@ export default (testContext: { expect(identifier).toHaveProperty('did') }) + it('should resolve identifier', async () => { + const didDoc = (await agent.resolveDid({ didUrl: identifier.did })).didDocument + console.log(JSON.stringify(didDoc, null, 2)); + expect(didDoc).toHaveProperty('verificationMethod') + }) + + it('should create verifiable credential in LD', async () => { const verifiableCredential = await agent.createVerifiableCredential({ credential: { @@ -42,7 +49,16 @@ export default (testContext: { proofFormat: 'lds', }) - console.log(verifiableCredential) + console.log(JSON.stringify(verifiableCredential, null, 2)) + console.log(`JWS: ${verifiableCredential.proof.jws}`) + // check that verification works + + const result = await agent.verifyVerifiableCredential({ + credential: verifiableCredential + }) + + expect(result).toEqual(true) + // expect(verifiableCredential).toHaveProperty('proof.jwt') // expect(verifiableCredential['@context']).toEqual([ // 'https://www.w3.org/2018/credentials/v1', diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index f53662c51..591e7dd08 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -14,11 +14,13 @@ } }, "dependencies": { - "@stablelib/ed25519": "^1.0.1", + "@transmute/did-key-ed25519": "^0.2.1-unstable.41", + "@transmute/ed25519-signature-2020": "^0.2.1-unstable.10", "@veramo/core": "^3.1.0", "@veramo/did-jwt": "^3.1.0", "@veramo/did-resolver": "^3.1.0", "@veramo/message-handler": "^3.1.0", + "EcdsaSecp256k1RecoverySignature2020": "git+https://github.com/decentralized-identity/EcdsaSecp256k1RecoverySignature2020.git", "base-58": "^0.0.1", "blakejs": "^1.1.0", "debug": "^4.1.1", @@ -28,16 +30,12 @@ "ecdsa-secp256k1-signature-2019": "^1.0.1", "jsonld": "^5.2.0", "jsonld-signatures": "^9.0.2", - "secp256k1-key-pair": "git+https://github.com/digitalbazaar/ecdsa-secp256k1-verification-key-2019.git", "vc-js": "^0.6.4" }, "devDependencies": { "@types/debug": "4.1.7", "typescript": "4.5.2" }, - "resolutions": { - "secp256k1-key-pair": "git+https://github.com/digitalbazaar/ecdsa-secp256k1-verification-key-2019.git" - }, "files": [ "build/**/*", "src/**/*", diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 1c5483a0b..08c57f2a9 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -22,13 +22,13 @@ import { } from 'did-jwt-vc' // Start LOAD LD Libraries +const { purposes: { AssertionProofPurpose }} = require("jsonld-signatures"); const vc = require('vc-js'); const { defaultDocumentLoader } = vc; -const {Ed25519KeyPair, extendContextLoader, suites: {Ed25519Signature2018}} = - require('jsonld-signatures'); -const EcdsaSepc256k1Signature2019 = require('ecdsa-secp256k1-signature-2019'); -const Secp256k1KeyPair = require('secp256k1-key-pair'); -const Base58 = require('base-58') +const {extendContextLoader} = require('jsonld-signatures'); +const {EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020} = require('EcdsaSecp256k1RecoverySignature2020') +import {Ed25519Signature2020, Ed25519KeyPair2020} from '@transmute/ed25519-signature-2020' +const Base58 = require('base-58'); // Start END LD Libraries @@ -151,8 +151,7 @@ export interface IVerifyVerifiableCredentialArgs { type: string } proof: { - type: string - jwt?: string + type?: string [x: string]: any } [x: string]: any @@ -235,6 +234,14 @@ export type IContext = IAgentContext< //------------------------- BEGIN JSON_LD HELPER / DELEGATING STUFF +/** + * TODO: General Implementation Notes + * - EcdsaSecp256k1Signature2019 (Signature) and EcdsaSecp256k1VerificationKey2019 (Key) + * are not useable right now, since they are not able to work with blockChainId and ECRecover. + * - DID Fragement Resolution. + * - Key Manager and Verification Methods: Veramo currently implements no link between those. + */ + // const createSigner = (function (key: IKey) { // if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) @@ -249,48 +256,91 @@ export type IContext = IAgentContext< // return { sign: getSignatureBytes } // }); -const signLdDoc = async (credential: W3CCredential, key: IKey): Promise => { +const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: string) => { + console.log(`resolving context for: ${url}`) + + // did resolution + if (url.startsWith('did:')) { + const didDoc = await context.agent.resolveDid({ didUrl: url }) + let returnDocument = didDoc.didDocument + + if (!returnDocument) return + + + returnDocument.assertionMethod = [] + // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId + // blockchainAccountId to ethereumAddress + returnDocument.verificationMethod?.forEach(x => { + if (x.blockchainAccountId) { + x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf("@")) + } + + // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." + // @ts-ignore + returnDocument.assertionMethod.push(x.id) + }) + + console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) + return { + contextUrl: null, + documentUrl: url, + document: returnDocument + }; + } + + if (localContexts.has(url)) { + console.log(`Returning local context for: ${url}`) + return { + contextUrl: null, + documentUrl: url, + document: localContexts.get(url) + }; + } + + return defaultDocumentLoader(url); +}); + +const signLdDoc = async ( + credential: W3CCredential, + key: IKey, + controller: string, + context: any): Promise => { if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) console.log('PrivateKey: ' + key.privateKeyHex) let suite - const keyDocOptions = { - publicKeyBase58: Base58.encode(Buffer.from(key.publicKeyHex, 'hex')), - privateKeyBase58: Base58.encode(Buffer.from(key.privateKeyHex, 'hex')) - } + switch(key.type) { case 'Secp256k1': - suite = new EcdsaSepc256k1Signature2019({ - key: new Secp256k1KeyPair(keyDocOptions), - verificationMethod: key.kid + suite = new EcdsaSecp256k1RecoverySignature2020({ + key: new EcdsaSecp256k1RecoveryMethod2020({ + publicKeyHex: key.publicKeyHex, + privateKeyHex: key.privateKeyHex, + type: 'EcdsaSecp256k1RecoveryMethod2020', // A little verbose? + controller, + id: `${controller}#controller` // TODO: Only default controller verificationMethod supported + }), }); break; case 'Ed25519': - suite = new Ed25519Signature2018({key: new Ed25519KeyPair(keyDocOptions)}); + suite = new Ed25519Signature2020({ + key: new Ed25519KeyPair2020({ + id: `${controller}#controller`, + controller, + publicKeyBase58: Base58.encode(Buffer.from(key.publicKeyHex, 'hex')), + privateKeyBase58: Base58.encode(Buffer.from(key.privateKeyHex, 'hex')), + }) + }); break; default: throw new Error(`Unknown key type ${key.type}.`); } - const documentLoader = extendContextLoader(async (url: string) => { - console.log(`resolving context for: ${url}`) - - if (localContexts.has(url)) { - console.log(`Returning local context for: ${url}`) - return { - contextUrl: null, - documentUrl: url, - document: localContexts.get(url) - }; - } - - return defaultDocumentLoader(url); - }); - return await vc.issue({ credential, suite, - documentLoader, + documentLoader: getDocumentLoader(context), + compactProof: false }); } @@ -400,8 +450,13 @@ export class CredentialIssuer implements IAgentPlugin { //------------------------- BEGIN JSON_LD INSERT if (args.proofFormat === 'lds') { + // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod + if (key.kid != identifier.controllerKeyId) { + throw new Error('Trying to use a non-controller key for an LD-Proof is not supported') + } + const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - return signLdDoc(credential, keyPayload) + return signLdDoc(credential, keyPayload, identifier.did, context) } //------------------------- END JSON_LD INSERT @@ -440,12 +495,27 @@ export class CredentialIssuer implements IAgentPlugin { const credential = args.credential // JWT if (credential.proof.jwt) { - + // Not implemented yet. + throw Error('verifyVerifiableCredential currently does not the verification of VC-JWT credentials.') } - // EcdsaSecp256k1Signature2019 - // - return false + const result = await vc.verifyCredential({ + credential, + suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2020()], + documentLoader: getDocumentLoader(context), + purpose: new AssertionProofPurpose(), + compactProof: false + }); + + if (result.verified) + return true + + // NOT verified. + + // result can include raw Error + console.log(`Error verifying LD Credential`) + console.log(JSON.stringify(result, null, 2)); + throw Error('Error verifying credential') } } diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/contexts.ts index 98e0f45f3..eeffcb0b2 100644 --- a/packages/credential-w3c/src/contexts.ts +++ b/packages/credential-w3c/src/contexts.ts @@ -11,6 +11,8 @@ function _read(_path: string) { const contexts = new Map([ ['https://veramo.io/contexts/socialmedia/v1', _read('./contexts/socialmedia-v1.jsonld')], + ['https://www.w3.org/ns/did/v1', _read('./contexts/security_context_v1.jsonld')], + ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('./contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')] ]); export default contexts; From fae2d8b5a3112c9d5f64e1414c3b0a8e0c99a931 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Sun, 18 Apr 2021 23:43:29 -0500 Subject: [PATCH 08/48] fixed merge conflicts and added local jsonld contexts. --- ...ds-ecdsa-secp256k1-recovery2020-0.0.jsonld | 21 +++++++ .../src/contexts/security_context_v1.jsonld | 58 +++++++++++++++++++ packages/credential-w3c/tsconfig.json | 4 +- 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 packages/credential-w3c/src/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld create mode 100644 packages/credential-w3c/src/contexts/security_context_v1.jsonld diff --git a/packages/credential-w3c/src/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld b/packages/credential-w3c/src/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld new file mode 100644 index 000000000..2da92bda4 --- /dev/null +++ b/packages/credential-w3c/src/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld @@ -0,0 +1,21 @@ +{ + "@context": { + "@version": 1.1, + "id": "@id", + "type": "@type", + "esrs2020": "https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#", + "EcdsaSecp256k1RecoverySignature2020": "esrs2020:EcdsaSecp256k1RecoverySignature2020", + "EcdsaSecp256k1RecoveryMethod2020": "esrs2020:EcdsaSecp256k1RecoveryMethod2020", + "publicKeyJwk": { + "@id": "esrs2020:publicKeyJwk", + "@type": "@json" + }, + "privateKeyJwk": { + "@id": "esrs2020:privateKeyJwk", + "@type": "@json" + }, + "publicKeyHex": "esrs2020:publicKeyHex", + "privateKeyHex": "esrs2020:privateKeyHex", + "ethereumAddress": "esrs2020:ethereumAddress" + } +} diff --git a/packages/credential-w3c/src/contexts/security_context_v1.jsonld b/packages/credential-w3c/src/contexts/security_context_v1.jsonld new file mode 100644 index 000000000..b447d0108 --- /dev/null +++ b/packages/credential-w3c/src/contexts/security_context_v1.jsonld @@ -0,0 +1,58 @@ +{ + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + + "alsoKnownAs": { + "@id": "https://www.w3.org/ns/activitystreams#alsoKnownAs", + "@type": "@id" + }, + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "controller": { + "@id": "https://w3id.org/security#controller", + "@type": "@id" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + }, + "service": { + "@id": "https://www.w3.org/ns/did#service", + "@type": "@id", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "serviceEndpoint": { + "@id": "https://www.w3.org/ns/did#serviceEndpoint", + "@type": "@id" + } + } + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } +} diff --git a/packages/credential-w3c/tsconfig.json b/packages/credential-w3c/tsconfig.json index 2d766723b..4abc07c24 100644 --- a/packages/credential-w3c/tsconfig.json +++ b/packages/credential-w3c/tsconfig.json @@ -3,7 +3,9 @@ "compilerOptions": { "rootDir": "src", "outDir": "build", - "declarationDir": "build" + "declarationDir": "build", + // https://github.com/transmute-industries/vc.js/issues/60 + "skipLibCheck": true }, "references": [ { "path": "../core" }, From 1cc995f22ab2d5a8350bac20533749213534a659 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 19 Apr 2021 08:26:38 -0500 Subject: [PATCH 09/48] feat: added presentation creation and verification. --- packages/credential-w3c/src/action-handler.ts | 186 +++++++++++++----- 1 file changed, 132 insertions(+), 54 deletions(-) diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 08c57f2a9..a53b92ad9 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -7,9 +7,7 @@ import { IPluginMethodMap, VerifiableCredential, VerifiablePresentation, - IDataStore, - IKey, - IIdentifier, + IDataStore, IKey, IIdentifier, } from '@veramo/core' import { @@ -31,7 +29,6 @@ import {Ed25519Signature2020, Ed25519KeyPair2020} from '@transmute/ed25519-signa const Base58 = require('base-58'); // Start END LD Libraries - import { schema } from './' import localContexts from './contexts' import Debug from 'debug' @@ -70,6 +67,11 @@ export interface ICreateVerifiablePresentationArgs { */ save?: boolean + /** + * Optional string challenge parameter to add to the verifiable presentation. + */ + challenge?: string + /** * The desired format for the VerifiablePresentation to be created. * Currently, only JWT is supported @@ -135,27 +137,30 @@ export interface IVerifyVerifiableCredentialArgs { * of the `credential` * */ - credential: { - '@context'?: string[] - id?: string - type?: string[] - issuer: { id: string; [x: string]: any } - issuanceDate?: string - expirationDate?: string - credentialSubject: { - id?: string - [x: string]: any - } - credentialStatus?: { - id: string - type: string - } - proof: { - type?: string - [x: string]: any - } - [x: string]: any - } + credential: VerifiableCredential +} + +/** + * Encapsulates the parameters required to verify a + * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} + * + * @public + */ +export interface IVerifyVerifiablePresentationArgs { + /** + * The json payload of the Credential according to the + * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} + * + * The signer of the Credential is chosen based on the `issuer.id` property + * of the `credential` + * + */ + presentation: VerifiablePresentation + + /** + * Optional string challenge parameter to verify the verifiable presentation against + */ + challenge?: string } /** @@ -203,7 +208,6 @@ export interface ICredentialIssuer extends IPluginMethodMap { /** * Verifies a Verifiable Credential JWT or LDS Format. - * The payload, signer and format are chosen based on the `args` parameter. * * @param args - Arguments necessary to verify a VerifiableCredential * @param context - This reserved param is automatically added and handled by the framework, *do not override* @@ -217,6 +221,23 @@ export interface ICredentialIssuer extends IPluginMethodMap { args: IVerifyVerifiableCredentialArgs, context: IContext, ): Promise + + + /** + * Verifies a Verifiable Presentation JWT or LDS Format. + * + * @param args - Arguments necessary to verify a VerifiableCredential + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the {@link @veramo/core#???} that was requested or rejects with an error + * if there was a problem with the input or while getting the key to signs + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} + */ + verifyVerifiablePresentation( + args: IVerifyVerifiablePresentationArgs, + context: IContext, + ): Promise } /** @@ -242,20 +263,6 @@ export type IContext = IAgentContext< * - Key Manager and Verification Methods: Veramo currently implements no link between those. */ - -// const createSigner = (function (key: IKey) { -// if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) -// -// debug(key) -// -// const privateKeyBytes = Uint8Array.from(Buffer.from(key.privateKeyHex, 'hex')) -// const getSignatureBytes = function({ data }: any) { -// const signature = ed25519.sign(privateKeyBytes, Uint8Array.from(Buffer.from(data, 'utf8'))) -// return signature -// }; -// return { sign: getSignatureBytes } -// }); - const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: string) => { console.log(`resolving context for: ${url}`) @@ -300,15 +307,13 @@ const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: return defaultDocumentLoader(url); }); -const signLdDoc = async ( - credential: W3CCredential, - key: IKey, - controller: string, - context: any): Promise => { - if (!key.privateKeyHex) throw Error('Key does not expose private Key: ' + key.kid) - console.log('PrivateKey: ' + key.privateKeyHex) - +const getLDSigningSuite = (key: IKey, identifier: IIdentifier) => { let suite + const controller = identifier.did + + if (!key.privateKeyHex) { + throw Error('No private Key for LD Signing available.') + } switch(key.type) { case 'Secp256k1': @@ -336,14 +341,44 @@ const signLdDoc = async ( throw new Error(`Unknown key type ${key.type}.`); } + return suite +} + +const issueLDVerifiableCredential = async ( + credential: W3CCredential, + key: IKey, + identifier: IIdentifier, + context: IContext): Promise => { + + const suite = getLDSigningSuite(key, identifier) + const documentLoader = getDocumentLoader(context) + return await vc.issue({ credential, suite, - documentLoader: getDocumentLoader(context), + documentLoader, compactProof: false }); } +const signLDVerifiablePresentation = async ( + presentation: W3CPresentation, + key: IKey, + challenge: string | undefined, + identifier: IIdentifier, + context: IContext +): Promise => { + + const suite = getLDSigningSuite(key, identifier) + const documentLoader = getDocumentLoader(context) + return await vc.signPresentation({ + presentation, + suite, + challenge, + documentLoader + }) +} + //------------------------- END JSON_LD HELPER / DELEGATING STUFF @@ -361,6 +396,7 @@ export class CredentialIssuer implements IAgentPlugin { createVerifiablePresentation: this.createVerifiablePresentation, createVerifiableCredential: this.createVerifiableCredential, verifyVerifiableCredential: this.verifyVerifiableCredential, + verifyVerifiablePresentation: this.verifyVerifiablePresentation, } } @@ -391,6 +427,19 @@ export class CredentialIssuer implements IAgentPlugin { //FIXME: `args` should allow picking a key or key type const key = identifier.keys.find((k) => k.type === 'Secp256k1' || k.type === 'Ed25519') if (!key) throw Error('No signing key for ' + identifier.did) + + //------------------------- BEGIN JSON_LD INSERT + if (args.proofFormat === 'lds') { + // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod + if (key.kid != identifier.controllerKeyId) { + throw new Error('Trying to use a non-controller key for an LD-Proof is not supported') + } + + const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) + return signLDVerifiablePresentation(presentation, keyPayload, args.challenge, identifier, context) + } + //------------------------- END JSON_LD INSERT + //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` debug('Signing VP with', identifier.did) let alg = 'ES256K' @@ -448,7 +497,6 @@ export class CredentialIssuer implements IAgentPlugin { if (!key) throw Error('No signing key for ' + identifier.did) //------------------------- BEGIN JSON_LD INSERT - if (args.proofFormat === 'lds') { // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod if (key.kid != identifier.controllerKeyId) { @@ -456,9 +504,8 @@ export class CredentialIssuer implements IAgentPlugin { } const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - return signLdDoc(credential, keyPayload, identifier.did, context) + return issueLDVerifiableCredential(credential, keyPayload, identifier, context) } - //------------------------- END JSON_LD INSERT //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` @@ -513,9 +560,40 @@ export class CredentialIssuer implements IAgentPlugin { // NOT verified. // result can include raw Error - console.log(`Error verifying LD Credential`) + console.log(`Error verifying LD Verifiable Credential`) + console.log(JSON.stringify(result, null, 2)); + throw Error('Error verifying LD Verifiable Credential') + } + + async verifyVerifiablePresentation( + args: IVerifyVerifiablePresentationArgs, + context: IContext, + ): Promise { + const presentation = args.presentation + // JWT + if (presentation.proof.jwt) { + // Not implemented yet. + throw Error('verifyVerifiablePresentation currently does not the verification of VC-JWT credentials.') + } + + const result = await vc.verify({ + presentation, + suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2020()], + documentLoader: getDocumentLoader(context), + challenge: args.challenge, + purpose: new AssertionProofPurpose(), + compactProof: false + }); + + if (result.verified) + return true + + // NOT verified. + + // result can include raw Error + console.log(`Error verifying LD Verifiable Presentation`) console.log(JSON.stringify(result, null, 2)); - throw Error('Error verifying credential') + throw Error('Error verifying LD Verifiable Presentation') } } From 07c037d119dd076a74d1148fe5e4f9fc2db85427 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 19 Apr 2021 09:06:14 -0500 Subject: [PATCH 10/48] feat: Demo update and integration test workaround. --- __tests__/shared/verifiableDataLD.ts | 5 +++-- packages/credential-w3c/src/contexts.ts | 1 + scripts/prepare-integration-tests.ts | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index fd887f691..f929173ac 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -37,11 +37,12 @@ export default (testContext: { issuer: { id: identifier.did }, '@context': [ 'https://www.w3.org/2018/credentials/v1', - 'https://veramo.io/contexts/socialmedia/v1' + 'https://veramo.io/contexts/kyc/v1' ], - type: ['VerifiableCredential', 'VerifableSocialMediaPosting'], + type: ['VerifiableCredential', 'VerifiableKyc'], issuanceDate: new Date().toISOString(), credentialSubject: { + name: "Martin, the great" // id: 'did:web:example.com', // you: 'Rock', }, diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/contexts.ts index eeffcb0b2..571f24c41 100644 --- a/packages/credential-w3c/src/contexts.ts +++ b/packages/credential-w3c/src/contexts.ts @@ -11,6 +11,7 @@ function _read(_path: string) { const contexts = new Map([ ['https://veramo.io/contexts/socialmedia/v1', _read('./contexts/socialmedia-v1.jsonld')], + ['https://veramo.io/contexts/kyc/v1', _read('./contexts/kyc-v1.jsonld')], ['https://www.w3.org/ns/did/v1', _read('./contexts/security_context_v1.jsonld')], ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('./contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')] ]); diff --git a/scripts/prepare-integration-tests.ts b/scripts/prepare-integration-tests.ts index 636cfb3e4..5f5972097 100644 --- a/scripts/prepare-integration-tests.ts +++ b/scripts/prepare-integration-tests.ts @@ -64,6 +64,8 @@ for (const packageName of Object.keys(agentPlugins)) { const generator = TJS.createGenerator({ path: resolve('packages/' + packageName + '/src/index.ts'), encodeRefs: false, + // TODO: https://github.com/transmute-industries/vc.js/issues/60 + skipTypeCheck: true }) const apiModel: ApiModel = new ApiModel() From 53265ae73e2470af9c20b2fd08457e5d1ac74751 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 19 Apr 2021 09:15:37 -0500 Subject: [PATCH 11/48] fix: added missing KYC context. --- packages/credential-w3c/src/contexts/kyc-v1.jsonld | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 packages/credential-w3c/src/contexts/kyc-v1.jsonld diff --git a/packages/credential-w3c/src/contexts/kyc-v1.jsonld b/packages/credential-w3c/src/contexts/kyc-v1.jsonld new file mode 100644 index 000000000..306d0174e --- /dev/null +++ b/packages/credential-w3c/src/contexts/kyc-v1.jsonld @@ -0,0 +1,8 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "VerifiableKyc": "https://veramo.io/contexts/kyc#VerifiableKyc", + "name": "https://schema.org/name" + } +} From b206074c6d48aab275ffbd8182b725f22a89e2e3 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 19 Apr 2021 09:39:40 -0500 Subject: [PATCH 12/48] fix: fixed loading of context files from build. --- .../credential-w3c/{src => }/contexts/kyc-v1.jsonld | 0 .../lds-ecdsa-secp256k1-recovery2020-0.0.jsonld | 0 .../{src => }/contexts/security_context_v1.jsonld | 0 .../{src => }/contexts/socialmedia-v1.jsonld | 0 packages/credential-w3c/src/contexts.ts | 10 +++++----- 5 files changed, 5 insertions(+), 5 deletions(-) rename packages/credential-w3c/{src => }/contexts/kyc-v1.jsonld (100%) rename packages/credential-w3c/{src => }/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld (100%) rename packages/credential-w3c/{src => }/contexts/security_context_v1.jsonld (100%) rename packages/credential-w3c/{src => }/contexts/socialmedia-v1.jsonld (100%) diff --git a/packages/credential-w3c/src/contexts/kyc-v1.jsonld b/packages/credential-w3c/contexts/kyc-v1.jsonld similarity index 100% rename from packages/credential-w3c/src/contexts/kyc-v1.jsonld rename to packages/credential-w3c/contexts/kyc-v1.jsonld diff --git a/packages/credential-w3c/src/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld b/packages/credential-w3c/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld similarity index 100% rename from packages/credential-w3c/src/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld rename to packages/credential-w3c/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld diff --git a/packages/credential-w3c/src/contexts/security_context_v1.jsonld b/packages/credential-w3c/contexts/security_context_v1.jsonld similarity index 100% rename from packages/credential-w3c/src/contexts/security_context_v1.jsonld rename to packages/credential-w3c/contexts/security_context_v1.jsonld diff --git a/packages/credential-w3c/src/contexts/socialmedia-v1.jsonld b/packages/credential-w3c/contexts/socialmedia-v1.jsonld similarity index 100% rename from packages/credential-w3c/src/contexts/socialmedia-v1.jsonld rename to packages/credential-w3c/contexts/socialmedia-v1.jsonld diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/contexts.ts index 571f24c41..63406b2e2 100644 --- a/packages/credential-w3c/src/contexts.ts +++ b/packages/credential-w3c/src/contexts.ts @@ -5,15 +5,15 @@ import * as path from 'path'; function _read(_path: string) { return JSON.parse( fs.readFileSync( - path.join(__dirname, _path), + path.join(__dirname, '../contexts', _path), {encoding: 'utf8'})); } const contexts = new Map([ - ['https://veramo.io/contexts/socialmedia/v1', _read('./contexts/socialmedia-v1.jsonld')], - ['https://veramo.io/contexts/kyc/v1', _read('./contexts/kyc-v1.jsonld')], - ['https://www.w3.org/ns/did/v1', _read('./contexts/security_context_v1.jsonld')], - ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('./contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')] + ['https://veramo.io/contexts/socialmedia/v1', _read('socialmedia-v1.jsonld')], + ['https://veramo.io/contexts/kyc/v1', _read('kyc-v1.jsonld')], + ['https://www.w3.org/ns/did/v1', _read('security_context_v1.jsonld')], + ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')] ]); export default contexts; From 36aa8a40029cf748727b1f76b2c5344a5386a830 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 19 Apr 2021 10:41:47 -0500 Subject: [PATCH 13/48] feat: Added profile credential --- packages/credential-w3c/contexts/profile-v1.jsonld | 8 ++++++++ packages/credential-w3c/src/contexts.ts | 1 + 2 files changed, 9 insertions(+) create mode 100644 packages/credential-w3c/contexts/profile-v1.jsonld diff --git a/packages/credential-w3c/contexts/profile-v1.jsonld b/packages/credential-w3c/contexts/profile-v1.jsonld new file mode 100644 index 000000000..925515f8a --- /dev/null +++ b/packages/credential-w3c/contexts/profile-v1.jsonld @@ -0,0 +1,8 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "Profile": "https://veramo.io/contexts/profile#Profile", + "name": "https://schema.org/name" + } +} diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/contexts.ts index 63406b2e2..710620b33 100644 --- a/packages/credential-w3c/src/contexts.ts +++ b/packages/credential-w3c/src/contexts.ts @@ -12,6 +12,7 @@ function _read(_path: string) { const contexts = new Map([ ['https://veramo.io/contexts/socialmedia/v1', _read('socialmedia-v1.jsonld')], ['https://veramo.io/contexts/kyc/v1', _read('kyc-v1.jsonld')], + ['https://veramo.io/contexts/profile/v1', _read('profile-v1.jsonld')], ['https://www.w3.org/ns/did/v1', _read('security_context_v1.jsonld')], ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')] ]); From 2bb983c791e3d84c300d9b6a095f272e3065b260 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 19 Apr 2021 12:02:36 -0500 Subject: [PATCH 14/48] feat: sync Demo --- __tests__/localAgent.test.ts | 3 + __tests__/localMemoryStoreAgent.test.ts | 4 + __tests__/restAgent.test.ts | 4 + __tests__/shared/verifiableDataLD.ts | 108 ++++++++++++++---- packages/cli/default/default.yml | 2 + packages/credential-w3c/src/action-handler.ts | 41 ++++--- 6 files changed, 118 insertions(+), 44 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 88b09271a..14b5d268c 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -166,6 +166,9 @@ const setup = async (options?: IAgentOptions): Promise => { 'did:key': new KeyDIDProvider({ defaultKms: 'local', }), + 'did:key': new KeyDIDProvider({ + defaultKms: 'local', + }), 'did:fake': new FakeDidProvider(), }, }), diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 80939b83b..5d307c03d 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -53,6 +53,7 @@ import keyManager from './shared/keyManager' import didManager from './shared/didManager' import didCommPacking from './shared/didCommPacking' import messageHandler from './shared/messageHandler' +import { KeyDIDProvider } from '@veramo/did-provider-key' const databaseFile = `./tmp/local-database2-${Math.random().toPrecision(5)}.sqlite` const infuraProjectId = '3586660d179141e3801c3895de1c2eba' @@ -134,6 +135,9 @@ const setup = async (options?: IAgentOptions): Promise => { 'did:key': new KeyDIDProvider({ defaultKms: 'local', }), + 'did:key': new KeyDIDProvider({ + defaultKms: 'local', + }), 'did:fake': new FakeDidProvider(), }, }), diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 9108331bb..0ad569382 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -50,6 +50,7 @@ import { AgentRestClient } from '../packages/remote-client/src' import { AgentRouter, RequestWithAgentRouter, MessagingRouter } from '../packages/remote-server/src' import { getDidKeyResolver } from '../packages/did-provider-key/src' import { IDIDDiscovery, DIDDiscovery } from '../packages/did-discovery/src' +import { KeyDIDProvider } from '../packages/did-provider-key/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' import { Resolver } from 'did-resolver' @@ -159,6 +160,9 @@ const setup = async (options?: IAgentOptions): Promise => { 'did:key': new KeyDIDProvider({ defaultKms: 'local', }), + 'did:key': new KeyDIDProvider({ + defaultKms: 'local', + }), 'did:fake': new FakeDidProvider(), }, }), diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index f929173ac..6418e92df 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -11,7 +11,9 @@ export default (testContext: { }) => { describe('creating Verifiable Credentials in LD', () => { let agent: ConfiguredAgent - let identifier: IIdentifier + let ethrIdentifier: IIdentifier + let keyE256KIdentifier: IIdentifier + let storedCredentialHash: string beforeAll(() => { testContext.setup() @@ -19,13 +21,13 @@ export default (testContext: { }) afterAll(testContext.tearDown) - it('should create identifier', async () => { - identifier = await agent.didManagerCreate({ kms: 'local' }) - expect(identifier).toHaveProperty('did') + it('should create ethr identifier', async () => { + ethrIdentifier = await agent.didManagerCreate({ kms: 'local' }) + expect(ethrIdentifier).toHaveProperty('did') }) it('should resolve identifier', async () => { - const didDoc = (await agent.resolveDid({ didUrl: identifier.did })).didDocument + const didDoc = (await agent.resolveDid({ didUrl: ethrIdentifier.did })).didDocument console.log(JSON.stringify(didDoc, null, 2)); expect(didDoc).toHaveProperty('verificationMethod') }) @@ -34,45 +36,101 @@ export default (testContext: { it('should create verifiable credential in LD', async () => { const verifiableCredential = await agent.createVerifiableCredential({ credential: { - issuer: { id: identifier.did }, + issuer: { id: ethrIdentifier.did }, '@context': [ 'https://www.w3.org/2018/credentials/v1', - 'https://veramo.io/contexts/kyc/v1' + 'https://veramo.io/contexts/profile/v1' ], - type: ['VerifiableCredential', 'VerifiableKyc'], + type: ['VerifiableCredential', 'Profile'], issuanceDate: new Date().toISOString(), credentialSubject: { name: "Martin, the great" - // id: 'did:web:example.com', - // you: 'Rock', }, }, proofFormat: 'lds', }) - console.log(JSON.stringify(verifiableCredential, null, 2)) - console.log(`JWS: ${verifiableCredential.proof.jws}`) - // check that verification works + // Check credential: + expect(verifiableCredential).toHaveProperty('proof') + expect(verifiableCredential).toHaveProperty('proof.jws') + expect(verifiableCredential.proof.verificationMethod).toEqual(`${ethrIdentifier.did}#controller`) + + expect(verifiableCredential['@context']).toEqual([ + 'https://www.w3.org/2018/credentials/v1', + 'https://veramo.io/contexts/profile/v1', + ]) + expect(verifiableCredential['type']).toEqual( + ['VerifiableCredential', 'Profile']) + storedCredentialHash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) + expect(typeof storedCredentialHash).toEqual('string') + + const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + expect(verifiableCredential).toEqual(verifiableCredential2) + }) + + it('should verify a verifiable credential in LD', async () => { + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + + // check that verification works const result = await agent.verifyVerifiableCredential({ credential: verifiableCredential }) expect(result).toEqual(true) + }) - // expect(verifiableCredential).toHaveProperty('proof.jwt') - // expect(verifiableCredential['@context']).toEqual([ - // 'https://www.w3.org/2018/credentials/v1', - // 'https://example.com/1/2/3', - // ]) - // expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Custom']) - // - // const hash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) - // expect(typeof hash).toEqual('string') - // - // const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash }) - // expect(verifiableCredential).toEqual(verifiableCredential2) + it('should create did:key identifier', async () => { + keyE256KIdentifier = await agent.didManagerCreate({ kms: 'local', provider: 'did:key' }) + expect(keyE256KIdentifier).toHaveProperty('did') }) + it('should create verifiable credential in LD with did:key', async () => { + const verifiableCredential = await agent.createVerifiableCredential({ + credential: { + issuer: { id: keyE256KIdentifier.did }, + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://veramo.io/contexts/profile/v1' + ], + type: ['VerifiableCredential', 'Profile'], + issuanceDate: new Date().toISOString(), + credentialSubject: { + name: "Martin, the great" + }, + }, + proofFormat: 'lds', + }) + + // Check credential: + expect(verifiableCredential).toHaveProperty('proof') + expect(verifiableCredential).toHaveProperty('proof.proofValue') + expect(verifiableCredential.proof.verificationMethod).toEqual(`${keyE256KIdentifier.did}#controller`) + + expect(verifiableCredential['@context']).toEqual([ + 'https://www.w3.org/2018/credentials/v1', + 'https://veramo.io/contexts/profile/v1', + ]) + expect(verifiableCredential['type']).toEqual( + ['VerifiableCredential', 'Profile']) + + storedCredentialHash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) + expect(typeof storedCredentialHash).toEqual('string') + + const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + expect(verifiableCredential).toEqual(verifiableCredential2) + }) + + // it('should verify a verifiable credential in LD with did:key', async () => { + // const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + // + // // check that verification works + // const result = await agent.verifyVerifiableCredential({ + // credential: verifiableCredential + // }) + // + // expect(result).toEqual(true) + // }) + }) } diff --git a/packages/cli/default/default.yml b/packages/cli/default/default.yml index abdf6bc3e..be5cf5859 100644 --- a/packages/cli/default/default.yml +++ b/packages/cli/default/default.yml @@ -59,6 +59,8 @@ constants: - createSelectiveDisclosureRequest - getVerifiableCredentialsForSdr - validatePresentationAgainstSdr + - verifyVerifiableCredential + - verifyVerifiablePresentation # Data base dbConnection: diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index a53b92ad9..67a4c0319 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -212,8 +212,7 @@ export interface ICredentialIssuer extends IPluginMethodMap { * @param args - Arguments necessary to verify a VerifiableCredential * @param context - This reserved param is automatically added and handled by the framework, *do not override* * - * @returns - a promise that resolves to the {@link @veramo/core#???} that was requested or rejects with an error - * if there was a problem with the input or while getting the key to signs + * @returns - a promise that resolves to the boolean true on successful verification or rejects on error * * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} */ @@ -229,10 +228,9 @@ export interface ICredentialIssuer extends IPluginMethodMap { * @param args - Arguments necessary to verify a VerifiableCredential * @param context - This reserved param is automatically added and handled by the framework, *do not override* * - * @returns - a promise that resolves to the {@link @veramo/core#???} that was requested or rejects with an error - * if there was a problem with the input or while getting the key to signs + * @returns - a promise that resolves to the boolean true on successful verification or rejects on error * - * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Credential data model} */ verifyVerifiablePresentation( args: IVerifyVerifiablePresentationArgs, @@ -257,7 +255,7 @@ export type IContext = IAgentContext< //------------------------- BEGIN JSON_LD HELPER / DELEGATING STUFF /** * TODO: General Implementation Notes - * - EcdsaSecp256k1Signature2019 (Signature) and EcdsaSecp256k1VerificationKey2019 (Key) + * - (SOLVED) EcdsaSecp256k1Signature2019 (Signature) and EcdsaSecp256k1VerificationKey2019 (Key) * are not useable right now, since they are not able to work with blockChainId and ECRecover. * - DID Fragement Resolution. * - Key Manager and Verification Methods: Veramo currently implements no link between those. @@ -267,25 +265,29 @@ const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: console.log(`resolving context for: ${url}`) // did resolution - if (url.startsWith('did:')) { + if (url.toLowerCase().startsWith('did:')) { const didDoc = await context.agent.resolveDid({ didUrl: url }) let returnDocument = didDoc.didDocument if (!returnDocument) return + // specific resolution modifications + // did:ethr + if (url.toLowerCase().startsWith('did:ethr')) { + returnDocument.assertionMethod = [] + // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId + // blockchainAccountId to ethereumAddress + returnDocument.verificationMethod?.forEach(x => { + if (x.blockchainAccountId) { + x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf("@")) + } - returnDocument.assertionMethod = [] - // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId - // blockchainAccountId to ethereumAddress - returnDocument.verificationMethod?.forEach(x => { - if (x.blockchainAccountId) { - x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf("@")) - } + // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." + // @ts-ignore + returnDocument.assertionMethod.push(x.id) + }) + } - // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." - // @ts-ignore - returnDocument.assertionMethod.push(x.id) - }) console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) return { @@ -534,7 +536,7 @@ export class CredentialIssuer implements IAgentPlugin { } } - /** {@inheritdoc ICredentialIssuer.createVerifiablePresentation} */ + /** {@inheritdoc ICredentialIssuer.verifyVerifiableCredential} */ async verifyVerifiableCredential( args: IVerifyVerifiableCredentialArgs, context: IContext, @@ -565,6 +567,7 @@ export class CredentialIssuer implements IAgentPlugin { throw Error('Error verifying LD Verifiable Credential') } + /** {@inheritdoc ICredentialIssuer.verifyVerifiablePresentation} */ async verifyVerifiablePresentation( args: IVerifyVerifiablePresentationArgs, context: IContext, From 81fdaeb790cae38c76bc006b864db79606e6ee3f Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 19 Apr 2021 12:41:25 -0500 Subject: [PATCH 15/48] fix: manually updated plugin.schema.json, to contain newly exposed methods. --- packages/credential-w3c/plugin.schema.json | 299 ++++++++++++--------- 1 file changed, 177 insertions(+), 122 deletions(-) diff --git a/packages/credential-w3c/plugin.schema.json b/packages/credential-w3c/plugin.schema.json index 1d683007d..0d7b9e8a8 100644 --- a/packages/credential-w3c/plugin.schema.json +++ b/packages/credential-w3c/plugin.schema.json @@ -6,88 +6,92 @@ "type": "object", "properties": { "credential": { + "$ref": "#/components/schemas/W3CCredential", + "description": "The json payload of the Credential according to the {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model}\n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" + }, + "save": { + "type": "boolean", + "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the {@link @veramo/core#IDataStore | storage plugin} to be saved" + }, + "proofFormat": { + "$ref": "#/components/schemas/EncodingFormat", + "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" + } + }, + "required": [ + "credential", + "proofFormat" + ], + "description": "Encapsulates the parameters required to create a {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential}" + }, + "W3CCredential": { + "type": "object", + "properties": { + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "issuer": { "type": "object", "properties": { - "@context": { - "type": "array", - "items": { - "type": "string" - } - }, "id": { "type": "string" - }, - "type": { - "type": "array", - "items": { - "type": "string" - } - }, - "issuer": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - }, - "required": [ - "id" - ] - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, - "credentialStatus": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "required": [ - "id", - "type" - ] } }, "required": [ - "issuer", - "credentialSubject" - ], - "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" + "id" + ] }, - "save": { - "type": "boolean", - "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved" + "issuanceDate": { + "type": "string" }, - "proofFormat": { - "$ref": "#/components/schemas/EncodingFormat", - "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" + "expirationDate": { + "type": "string" }, - "removeOriginalFields": { - "type": "boolean", - "description": "Remove payload members during JWT-JSON transformation. Defaults to `true`. See https://www.w3.org/TR/vc-data-model/#jwt-encoding" + "credentialSubject": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "additionalProperties": true + }, + "credentialStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ] } }, "required": [ - "credential", - "proofFormat" + "@context", + "type", + "issuer", + "issuanceDate", + "credentialSubject" ], - "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" + "description": "W3CCredential {@link https://github.com/decentralized-identifier/did-jwt-vc}" }, "EncodingFormat": { "type": "string", @@ -169,76 +173,108 @@ "credentialSubject", "proof" ], - "description": "Verifiable Credential {@link https://github.com/decentralized-identifier/did-jwt-vc }" + "description": "Verifiable Credential {@link https://github.com/decentralized-identifier/did-jwt-vc}" }, "ICreateVerifiablePresentationArgs": { "type": "object", "properties": { "presentation": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "holder": { - "type": "string" - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "@context": { - "type": "array", - "items": { - "type": "string" - } - }, - "type": { - "type": "array", - "items": { - "type": "string" - } - }, - "verifier": { - "type": "array", - "items": { - "type": "string" - } - }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } - } - }, - "required": [ - "holder", - "verifier", - "verifiableCredential" - ], - "description": "The json payload of the Presentation according to the\n {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model } .\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" + "$ref": "#/components/schemas/W3CPresentation", + "description": "The json payload of the Presentation according to the {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model}.\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`" }, "save": { "type": "boolean", - "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved" + "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the {@link @veramo/core#IDataStore | storage plugin} to be saved" }, "proofFormat": { "$ref": "#/components/schemas/EncodingFormat", "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" - }, - "removeOriginalFields": { - "type": "boolean", - "description": "Remove payload members during JWT-JSON transformation. Defaults to `true`. See https://www.w3.org/TR/vc-data-model/#jwt-encoding" } }, "required": [ "presentation", "proofFormat" ], - "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" + "description": "Encapsulates the parameters required to create a {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation}" + }, + "IVerifyVerifiablePresentationArgs": { + "type": "object", + "properties": { + "presentation": { + "$ref": "#/components/schemas/VerifiablePresentation", + "description": "The json payload of the Presentation according to the {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model}.\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`" + }, + "challenge": { + "type": "string", + "description": "Optional challenge to be past to verification" + } + }, + "required": [ + "presentation" + ], + "description": "Encapsulates the parameters required to verify a {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation}" + }, + "IVerifyVerifiableCredentialArgs": { + "type": "object", + "properties": { + "credential": { + "$ref": "#/components/schemas/VerifiableCredential", + "description": "The json payload of the Credential according to the {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model}.\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`" + } + }, + "required": [ + "credential" + ], + "description": "Encapsulates the parameters required to verify a {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential}" + }, + "W3CPresentation": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "holder": { + "type": "string" + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "verifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "verifiableCredential": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VerifiableCredential" + } + } + }, + "required": [ + "holder", + "@context", + "type", + "verifier", + "verifiableCredential" + ], + "description": "W3CPresentation {@link https://github.com/decentralized-identifier/did-jwt-vc}" }, "VerifiablePresentation": { "type": "object", @@ -293,9 +329,10 @@ "@context", "type", "verifier", + "verifiableCredential", "proof" ], - "description": "Verifiable Presentation {@link https://github.com/decentralized-identifier/did-jwt-vc }" + "description": "Verifiable Presentation {@link https://github.com/decentralized-identifier/did-jwt-vc}" } }, "methods": { @@ -316,8 +353,26 @@ "returnType": { "$ref": "#/components/schemas/VerifiablePresentation" } + }, + "verifyVerifiableCredential": { + "description": "Verifies a Verifiable Credential.", + "arguments": { + "$ref": "#/components/schemas/IVerifyVerifiableCredentialArgs" + }, + "returnType": { + "type": "boolean" + } + }, + "verifyVerifiablePresentation": { + "description": "Verifies a Verifiable Presentation.", + "arguments": { + "$ref": "#/components/schemas/IVerifyVerifiablePresentationArgs" + }, + "returnType": { + "type": "boolean" + } } } } } -} \ No newline at end of file +} From 29e73f348eeb4b500b7408680d8fbea923f2e73d Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Thu, 22 Apr 2021 11:42:57 -0500 Subject: [PATCH 16/48] feat: allow so send LD Credentials --- packages/cli/src/credential.ts | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/credential.ts b/packages/cli/src/credential.ts index 591824775..af003b6c6 100644 --- a/packages/cli/src/credential.ts +++ b/packages/cli/src/credential.ts @@ -19,6 +19,15 @@ credential process.exit() } const answers = await inquirer.prompt([ + { + type: 'list', + name: 'proofFormat', + choices: [ + 'jwt', + 'lds' + ], + message: 'Credential proofFormat', + }, { type: 'list', name: 'iss', @@ -70,7 +79,10 @@ credential const credential: W3CCredential = { issuer: { id: answers.iss }, - '@context': ['https://www.w3.org/2018/credentials/v1'], + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://veramo.io/contexts/profile/v1' + ], type: answers.type.split(','), issuanceDate: new Date().toISOString(), credentialSubject, @@ -101,18 +113,27 @@ credential const verifiableCredential = await agent.createVerifiableCredential({ save: true, credential, - proofFormat: 'jwt', + proofFormat: answers.proofFormat, }) if (cmd.send) { + let body; + let type; + if (answers.proofFormat == 'jwt') { + body = verifiableCredential.proof.jwt + type = 'jwt' + } else { + body = verifiableCredential + type = 'w3c.vc' + } try { const message = await agent.sendMessageDIDCommAlpha1({ save: true, data: { from: answers.iss, to: answers.sub, - type: 'jwt', - body: verifiableCredential.proof.jwt, + type, + body, }, }) console.dir(message, { depth: 10 }) From 3b12f328bfc82d74545509f0331750ef54659938 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Tue, 20 Apr 2021 00:12:08 -0400 Subject: [PATCH 17/48] feat: enabled presentation creation and verification with EcdsaSecp256k1RecoverySignature2020 --- __tests__/shared/verifiableDataLD.ts | 68 +++++++++++++++++++ ...ds-ecdsa-secp256k1-recovery2020-0.0.jsonld | 41 ++++++++++- packages/credential-w3c/src/action-handler.ts | 64 +++++++++++++++-- 3 files changed, 166 insertions(+), 7 deletions(-) diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index 6418e92df..f8f7daa04 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -14,10 +14,12 @@ export default (testContext: { let ethrIdentifier: IIdentifier let keyE256KIdentifier: IIdentifier let storedCredentialHash: string + let challenge: string beforeAll(() => { testContext.setup() agent = testContext.getAgent() + challenge = 'TEST_CHALLENGE_STRING' }) afterAll(testContext.tearDown) @@ -58,6 +60,7 @@ export default (testContext: { expect(verifiableCredential['@context']).toEqual([ 'https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1', + 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld' ]) expect(verifiableCredential['type']).toEqual( ['VerifiableCredential', 'Profile']) @@ -80,6 +83,53 @@ export default (testContext: { expect(result).toEqual(true) }) + it('should sign a verifiable presentation in LD', async () => { + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + + const verifiablePresentation = await agent.createVerifiablePresentation({ + presentation: { + holder: ethrIdentifier.did, + verifier: [], + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + verifiableCredential: [verifiableCredential], + }, + challenge, + proofFormat: 'lds', + }) + + expect(verifiablePresentation).toHaveProperty('proof.jws') + }) + + + it('should sign and verify a verifiable presentation in LD', async () => { + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + + const domain = 'TEST_DOMAIN' + const verifiablePresentation = await agent.createVerifiablePresentation({ + presentation: { + holder: ethrIdentifier.did, + verifier: [], + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + verifiableCredential: [verifiableCredential], + }, + challenge, + domain, + proofFormat: 'lds', + }) + + console.log(JSON.stringify(verifiablePresentation, null, 2)) + + const result = await agent.verifyVerifiablePresentation({ + presentation: verifiablePresentation, + challenge, + domain + }) + + expect(result).toBeTruthy() + }) + it('should create did:key identifier', async () => { keyE256KIdentifier = await agent.didManagerCreate({ kms: 'local', provider: 'did:key' }) expect(keyE256KIdentifier).toHaveProperty('did') @@ -96,11 +146,29 @@ export default (testContext: { type: ['VerifiableCredential', 'Profile'], issuanceDate: new Date().toISOString(), credentialSubject: { + id: keyE256KIdentifier.did, name: "Martin, the great" }, }, proofFormat: 'lds', }) + // console.log('JSON_CREDENTIAL:') + // console.log(JSON.stringify({ + // credential: { + // issuer: { id: keyE256KIdentifier.did }, + // '@context': [ + // 'https://www.w3.org/2018/credentials/v1', + // 'https://veramo.io/contexts/profile/v1' + // ], + // type: ['VerifiableCredential', 'Profile'], + // issuanceDate: new Date().toISOString(), + // credentialSubject: { + // id: keyE256KIdentifier.did, + // name: "Martin, the great" + // }, + // }, + // proofFormat: 'lds', + // }, null, 2)) // Check credential: expect(verifiableCredential).toHaveProperty('proof') diff --git a/packages/credential-w3c/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld b/packages/credential-w3c/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld index 2da92bda4..c79835e3c 100644 --- a/packages/credential-w3c/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld +++ b/packages/credential-w3c/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld @@ -4,7 +4,46 @@ "id": "@id", "type": "@type", "esrs2020": "https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#", - "EcdsaSecp256k1RecoverySignature2020": "esrs2020:EcdsaSecp256k1RecoverySignature2020", + + "EcdsaSecp256k1RecoverySignature2020": { + "@id": "https://w3id.org/security#EcdsaSecp256k1RecoverySignature2020", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + "EcdsaSecp256k1RecoveryMethod2020": "esrs2020:EcdsaSecp256k1RecoveryMethod2020", "publicKeyJwk": { "@id": "esrs2020:publicKeyJwk", diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 67a4c0319..ef1486519 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -68,10 +68,15 @@ export interface ICreateVerifiablePresentationArgs { save?: boolean /** - * Optional string challenge parameter to add to the verifiable presentation. + * Optional (only JWT) string challenge parameter to add to the verifiable presentation. */ challenge?: string + /** + * Optional string domain parameter to add to the verifiable presentation. + */ + domain?: string + /** * The desired format for the VerifiablePresentation to be created. * Currently, only JWT is supported @@ -155,12 +160,31 @@ export interface IVerifyVerifiablePresentationArgs { * of the `credential` * */ - presentation: VerifiablePresentation + presentation: { + id?: string + holder: string + // issuanceDate?: string + // expirationDate?: string + '@context': string[] + type: string[] + // verifier: string[] + verifiableCredential: VerifiableCredential[] + proof: { + type?: string + [x: string]: any + } + [x: string]: any + } /** - * Optional string challenge parameter to verify the verifiable presentation against + * Optional (only for JWT) string challenge parameter to verify the verifiable presentation against */ challenge?: string + + /** + * Optional (only for JWT) string domain parameter to verify the verifiable presentation against + */ + domain?: string } /** @@ -355,6 +379,15 @@ const issueLDVerifiableCredential = async ( const suite = getLDSigningSuite(key, identifier) const documentLoader = getDocumentLoader(context) + // some suites are misssisng the right contexts + switch (suite.type) { + case "EcdsaSecp256k1RecoverySignature2020": + console.log(`Adding context to credential ${suite.type}`) + credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') + break + default: + } + return await vc.issue({ credential, suite, @@ -367,17 +400,30 @@ const signLDVerifiablePresentation = async ( presentation: W3CPresentation, key: IKey, challenge: string | undefined, + domain: string | undefined, identifier: IIdentifier, context: IContext ): Promise => { const suite = getLDSigningSuite(key, identifier) const documentLoader = getDocumentLoader(context) + + // TODO: Remove invalid field 'verifiers' from Presentation. Needs to be adapted for LD credentials + // Only remove empty array (vc.signPresentation will throw then) + const sanitizedPresentation = presentation as any + if (sanitizedPresentation.verifier.length == 0) { + delete sanitizedPresentation.verifier + } + + return await vc.signPresentation({ - presentation, + presentation: sanitizedPresentation, suite, challenge, - documentLoader + domain, + documentLoader, + purpose: new AssertionProofPurpose(), + compactProof: false }) } @@ -438,9 +484,14 @@ export class CredentialIssuer implements IAgentPlugin { } const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - return signLDVerifiablePresentation(presentation, keyPayload, args.challenge, identifier, context) + return signLDVerifiablePresentation(presentation, + keyPayload, args.challenge, args.domain, identifier, context) } //------------------------- END JSON_LD INSERT + else { + // only add issuanceDate for JWT + presentation.issuanceDate = args.presentation.issuanceDate || new Date().toISOString() + } //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` debug('Signing VP with', identifier.did) @@ -584,6 +635,7 @@ export class CredentialIssuer implements IAgentPlugin { suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2020()], documentLoader: getDocumentLoader(context), challenge: args.challenge, + domain: args.domain, purpose: new AssertionProofPurpose(), compactProof: false }); From 4b2123b4a5a1f190d7d75deb607d4781ca2754ed Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Wed, 21 Apr 2021 21:47:49 -0500 Subject: [PATCH 18/48] fix: fixed save operation for credentials --- __tests__/shared/verifiableDataLD.ts | 9 +++- packages/credential-w3c/src/action-handler.ts | 48 ++++++++++--------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index f8f7daa04..ed93b9d7f 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -49,6 +49,7 @@ export default (testContext: { name: "Martin, the great" }, }, + save: true, proofFormat: 'lds', }) @@ -65,8 +66,9 @@ export default (testContext: { expect(verifiableCredential['type']).toEqual( ['VerifiableCredential', 'Profile']) - storedCredentialHash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) - expect(typeof storedCredentialHash).toEqual('string') + expect(await agent.dataStoreORMGetVerifiableCredentialsCount()).toEqual(1) + + storedCredentialHash = (await agent.dataStoreORMGetVerifiableCredentials())[0].hash const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) expect(verifiableCredential).toEqual(verifiableCredential2) @@ -95,6 +97,9 @@ export default (testContext: { verifiableCredential: [verifiableCredential], }, challenge, + // TODO: QueryFailedError: SQLITE_CONSTRAINT: NOT NULL constraint failed: presentation.issuanceDate + // Currently LD Presentations are NEVER saved. (they have no issuanceDate) + save: true, proofFormat: 'lds', }) diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index ef1486519..92818948f 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -286,7 +286,7 @@ export type IContext = IAgentContext< */ const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: string) => { - console.log(`resolving context for: ${url}`) + // console.log(`resolving context for: ${url}`) // did resolution if (url.toLowerCase().startsWith('did:')) { @@ -313,7 +313,7 @@ const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: } - console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) + // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) return { contextUrl: null, documentUrl: url, @@ -322,7 +322,7 @@ const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: } if (localContexts.has(url)) { - console.log(`Returning local context for: ${url}`) + // console.log(`Returning local context for: ${url}`) return { contextUrl: null, documentUrl: url, @@ -382,7 +382,7 @@ const issueLDVerifiableCredential = async ( // some suites are misssisng the right contexts switch (suite.type) { case "EcdsaSecp256k1RecoverySignature2020": - console.log(`Adding context to credential ${suite.type}`) + // console.log(`Adding context to credential ${suite.type}`) credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') break default: @@ -484,7 +484,7 @@ export class CredentialIssuer implements IAgentPlugin { } const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - return signLDVerifiablePresentation(presentation, + return await signLDVerifiablePresentation(presentation, keyPayload, args.challenge, args.domain, identifier, context) } //------------------------- END JSON_LD INSERT @@ -550,6 +550,7 @@ export class CredentialIssuer implements IAgentPlugin { if (!key) throw Error('No signing key for ' + identifier.did) //------------------------- BEGIN JSON_LD INSERT + let verifiableCredential; if (args.proofFormat === 'lds') { // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod if (key.kid != identifier.controllerKeyId) { @@ -557,25 +558,26 @@ export class CredentialIssuer implements IAgentPlugin { } const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - return issueLDVerifiableCredential(credential, keyPayload, identifier, context) + verifiableCredential = await issueLDVerifiableCredential(credential, keyPayload, identifier, context) + } else { + //------------------------- END JSON_LD INSERT + //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` + + + debug('Signing VC with', identifier.did) + let alg = 'ES256K' + if (key.type === 'Ed25519') { + alg = 'EdDSA' + }const signer = wrapSigner(context, key, alg) + const jwt = await createVerifiableCredentialJwt( + credentialas CredentialPayload, + { did: identifier.did, signer, alg }, + { removeOriginalFields: args.removeOriginalFields }, + ) + //FIXME: flagging this as a potential privacy leak. + debug(jwt) + verifiableCredential = normalizeCredential(jwt) } - //------------------------- END JSON_LD INSERT - - //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` - debug('Signing VC with', identifier.did) - let alg = 'ES256K' - if (key.type === 'Ed25519') { - alg = 'EdDSA' - } - const signer = wrapSigner(context, key, alg) - const jwt = await createVerifiableCredentialJwt( - credential as CredentialPayload, - { did: identifier.did, signer, alg }, - { removeOriginalFields: args.removeOriginalFields }, - ) - //FIXME: flagging this as a potential privacy leak. - debug(jwt) - const verifiableCredential = normalizeCredential(jwt) if (args.save) { await context.agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) } From 51faa1abc5b3d560aae62b431063ed0e4b11d022 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Wed, 21 Apr 2021 23:09:57 -0500 Subject: [PATCH 19/48] feat: Finished message-handler for VCs (LD-Based) --- __tests__/shared/verifiableDataLD.ts | 70 ++++++++++++++++++- .../credential-w3c/src/message-handler.ts | 59 +++++++++++++++- 2 files changed, 124 insertions(+), 5 deletions(-) diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index ed93b9d7f..89efc7d2d 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -1,8 +1,9 @@ import { TAgent, IDIDManager, IDataStore, IIdentifier } from '../../packages/core/src' import { IDataStoreORM } from '../../packages/data-store/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' +import { IDIDComm } from '@veramo/did-comm' -type ConfiguredAgent = TAgent +type ConfiguredAgent = TAgent export default (testContext: { getAgent: () => ConfiguredAgent @@ -30,7 +31,7 @@ export default (testContext: { it('should resolve identifier', async () => { const didDoc = (await agent.resolveDid({ didUrl: ethrIdentifier.did })).didDocument - console.log(JSON.stringify(didDoc, null, 2)); + // console.log(JSON.stringify(didDoc, null, 2)); expect(didDoc).toHaveProperty('verificationMethod') }) @@ -85,6 +86,41 @@ export default (testContext: { expect(result).toEqual(true) }) + it('should handleMessage with VC (non-JWT)', async () => { + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + + const parsedMessage = await agent.handleMessage({ + raw: JSON.stringify({ + body: verifiableCredential, + type: 'w3c.vc' + }), + save: false, + metaData: [{ type: 'LDS' }], + }) + expect(typeof parsedMessage.id).toEqual('string') + }) + + it('should fail handleMessage with wrong VC (non-JWT)', async () => { + expect.assertions(1); + + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + + verifiableCredential.credentialSubject.name = "Martin, the not so greats" + + try { + await agent.handleMessage({ + raw: JSON.stringify({ + body: verifiableCredential, + type: 'w3c.vc' + }), + save: false, + metaData: [{ type: 'LDS' }], + }); + } catch (e) { + expect(e).toEqual(Error('Error verifying LD Verifiable Credential')); + } + }) + it('should sign a verifiable presentation in LD', async () => { const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) @@ -124,7 +160,7 @@ export default (testContext: { proofFormat: 'lds', }) - console.log(JSON.stringify(verifiablePresentation, null, 2)) + // console.log(JSON.stringify(verifiablePresentation, null, 2)) const result = await agent.verifyVerifiablePresentation({ presentation: verifiablePresentation, @@ -135,6 +171,33 @@ export default (testContext: { expect(result).toBeTruthy() }) + it('should handleMessage with VPs (non-JWT)', async () => { + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + + const verifiablePresentation = await agent.createVerifiablePresentation({ + presentation: { + holder: ethrIdentifier.did, + verifier: [], + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + verifiableCredential: [verifiableCredential], + }, + challenge: 'VERAMO', + domain: 'VERAMO', + proofFormat: 'lds', + }) + + const parsedMessage = await agent.handleMessage({ + raw: JSON.stringify({ + body: verifiablePresentation, + type: 'w3c.vp' + }), + save: false, + metaData: [{ type: 'LDS' }], + }) + expect(typeof parsedMessage.id).toEqual('string') + }) + it('should create did:key identifier', async () => { keyE256KIdentifier = await agent.didManagerCreate({ kms: 'local', provider: 'did:key' }) expect(keyE256KIdentifier).toHaveProperty('did') @@ -205,5 +268,6 @@ export default (testContext: { // expect(result).toEqual(true) // }) + }) } diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index dc56fe8bc..366da3d6a 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -1,4 +1,4 @@ -import { IAgentContext, IResolver } from '@veramo/core' +import { IAgentContext, IResolver, VerifiableCredential, VerifiablePresentation } from '@veramo/core' import { Message, AbstractMessageHandler } from '@veramo/message-handler' import { blake2bHex } from 'blakejs' @@ -10,6 +10,8 @@ import { } from 'did-jwt-vc' import Debug from 'debug' + +import { ICredentialIssuer } from './action-handler' const debug = Debug('veramo:w3c:message-handler') /** @@ -30,7 +32,7 @@ export const MessageTypes = { * * This interface can be used for static type checks, to make sure your application is properly initialized. */ -export type IContext = IAgentContext +export type IContext = IAgentContext /** * An implementation of the {@link @veramo/message-handler#AbstractMessageHandler}. @@ -50,6 +52,8 @@ export class W3cMessageHandler extends AbstractMessageHandler { async handle(message: Message, context: IContext): Promise { const meta = message.getLastMetaData() + console.log(JSON.stringify(message, null, 2)) + //FIXME: messages should not be expected to be only JWT if (meta?.type === 'JWT' && message.raw) { const { data } = message @@ -99,6 +103,57 @@ export class W3cMessageHandler extends AbstractMessageHandler { } catch (e) {} } + // LDS Verification and Handling + if (message.type === MessageTypes.vc && message.data) { + // verify credential + const credential = message.data as VerifiableCredential + + // throws on error. + await context.agent.verifyVerifiableCredential({ credential }) + message.id = blake2bHex(message.raw) + message.type = MessageTypes.vc + message.from = credential.issuer.id + message.to = credential.credentialSubject.id + + if (credential.tag) { + message.threadId = credential.tag + } + + message.createdAt = credential.issuanceDate + message.credentials = [credential] + return message + } + + if (message.type === MessageTypes.vp && message.data) { + // verify credential + const presentation = message.data as VerifiablePresentation + + // throws on error. + await context.agent.verifyVerifiablePresentation({ + presentation, + // TODO: HARDCODED CHALLENGE VERIFICATION FOR NOW + challenge: 'VERAMO', + domain: 'VERAMO' + }) + + const credentials = presentation.verifiableCredential + + message.id = blake2bHex(message.raw) + message.type = MessageTypes.vp + message.from = presentation.holder + // message.to = presentation.verifier?.[0] + + if (presentation.tag) { + message.threadId = presentation.tag + } + + // message.createdAt = presentation.issuanceDate + message.presentations = [presentation] + message.credentials = credentials + return message + } + + return super.handle(message, context) } } From 2babf88a575e348671925f8fcc21e8724b2161ec Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Thu, 22 Apr 2021 08:52:05 -0400 Subject: [PATCH 20/48] feat: fixed did:key ed25519 signature handling. --- __tests__/shared/verifiableDataLD.ts | 42 +++++--------- .../credential-w3c/contexts/did_v0.11.jsonld | 58 +++++++++++++++++++ .../contexts/transmute_v1.jsonld | 21 +++++++ .../src/__tests__/message-handler.test.ts | 4 ++ packages/credential-w3c/src/contexts.ts | 2 + .../credential-w3c/src/message-handler.ts | 2 +- 6 files changed, 99 insertions(+), 30 deletions(-) create mode 100644 packages/credential-w3c/contexts/did_v0.11.jsonld create mode 100644 packages/credential-w3c/contexts/transmute_v1.jsonld diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index 89efc7d2d..898059ec8 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -220,28 +220,11 @@ export default (testContext: { }, proofFormat: 'lds', }) - // console.log('JSON_CREDENTIAL:') - // console.log(JSON.stringify({ - // credential: { - // issuer: { id: keyE256KIdentifier.did }, - // '@context': [ - // 'https://www.w3.org/2018/credentials/v1', - // 'https://veramo.io/contexts/profile/v1' - // ], - // type: ['VerifiableCredential', 'Profile'], - // issuanceDate: new Date().toISOString(), - // credentialSubject: { - // id: keyE256KIdentifier.did, - // name: "Martin, the great" - // }, - // }, - // proofFormat: 'lds', - // }, null, 2)) // Check credential: expect(verifiableCredential).toHaveProperty('proof') - expect(verifiableCredential).toHaveProperty('proof.proofValue') - expect(verifiableCredential.proof.verificationMethod).toEqual(`${keyE256KIdentifier.did}#controller`) + expect(verifiableCredential).toHaveProperty('proof.jws') + expect(verifiableCredential.proof.verificationMethod).toEqual(`${keyE256KIdentifier.did}#${keyE256KIdentifier.did.substring(keyE256KIdentifier.did.lastIndexOf(':') + 1)}`) expect(verifiableCredential['@context']).toEqual([ 'https://www.w3.org/2018/credentials/v1', @@ -257,16 +240,17 @@ export default (testContext: { expect(verifiableCredential).toEqual(verifiableCredential2) }) - // it('should verify a verifiable credential in LD with did:key', async () => { - // const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) - // - // // check that verification works - // const result = await agent.verifyVerifiableCredential({ - // credential: verifiableCredential - // }) - // - // expect(result).toEqual(true) - // }) + it('should verify a verifiable credential in LD with did:key', async () => { + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + + console.log(JSON.stringify(verifiableCredential, null, 2)) + // check that verification works + const result = await agent.verifyVerifiableCredential({ + credential: verifiableCredential + }) + + expect(result).toEqual(true) + }) }) diff --git a/packages/credential-w3c/contexts/did_v0.11.jsonld b/packages/credential-w3c/contexts/did_v0.11.jsonld new file mode 100644 index 000000000..646c00be2 --- /dev/null +++ b/packages/credential-w3c/contexts/did_v0.11.jsonld @@ -0,0 +1,58 @@ +{ + "@context": { + "@version": 1.1, + "id": "@id", + "type": "@type", + + "dc": "http://purl.org/dc/terms/", + "schema": "http://schema.org/", + "sec": "https://w3id.org/security#", + "didv": "https://w3id.org/did#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "EcdsaSecp256k1Signature2019": "sec:EcdsaSecp256k1Signature2019", + "EcdsaSecp256k1VerificationKey2019": "sec:EcdsaSecp256k1VerificationKey2019", + "Ed25519Signature2018": "sec:Ed25519Signature2018", + "Ed25519VerificationKey2018": "sec:Ed25519VerificationKey2018", + "RsaSignature2018": "sec:RsaSignature2018", + "RsaVerificationKey2018": "sec:RsaVerificationKey2018", + "SchnorrSecp256k1Signature2019": "sec:SchnorrSecp256k1Signature2019", + "SchnorrSecp256k1VerificationKey2019": "sec:SchnorrSecp256k1VerificationKey2019", + "ServiceEndpointProxyService": "didv:ServiceEndpointProxyService", + + "allowedAction": "sec:allowedAction", + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"}, + "capability": {"@id": "sec:capability", "@type": "@id"}, + "capabilityAction": "sec:capabilityAction", + "capabilityChain": {"@id": "sec:capabilityChain", "@type": "@id", "@container": "@list"}, + "capabilityDelegation": {"@id": "sec:capabilityDelegationMethod", "@type": "@id", "@container": "@set"}, + "capabilityInvocation": {"@id": "sec:capabilityInvocationMethod", "@type": "@id", "@container": "@set"}, + "capabilityStatusList": {"@id": "sec:capabilityStatusList", "@type": "@id"}, + "canonicalizationAlgorithm": "sec:canonicalizationAlgorithm", + "caveat": {"@id": "sec:caveat", "@type": "@id", "@container": "@set"}, + "challenge": "sec:challenge", + "controller": {"@id": "sec:controller", "@type": "@id"}, + "created": {"@id": "dc:created", "@type": "xsd:dateTime"}, + "creator": {"@id": "dc:creator", "@type": "@id"}, + "delegator": {"@id": "sec:delegator", "@type": "@id"}, + "domain": "sec:domain", + "expirationDate": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "invocationTarget": {"@id": "sec:invocationTarget", "@type": "@id"}, + "invoker": {"@id": "sec:invoker", "@type": "@id"}, + "jws": "sec:jws", + "keyAgreement": {"@id": "sec:keyAgreementMethod", "@type": "@id", "@container": "@set"}, + "nonce": "sec:nonce", + "owner": {"@id": "sec:owner", "@type": "@id"}, + "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, + "proofPurpose": {"@id": "sec:proofPurpose", "@type": "@vocab"}, + "proofValue": "sec:proofValue", + "publicKey": {"@id": "sec:publicKey", "@type": "@id", "@container": "@set"}, + "publicKeyBase58": "sec:publicKeyBase58", + "publicKeyPem": "sec:publicKeyPem", + "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"}, + "service": {"@id": "didv:service", "@type": "@id", "@container": "@set"}, + "serviceEndpoint": {"@id": "didv:serviceEndpoint", "@type": "@id"}, + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } +} diff --git a/packages/credential-w3c/contexts/transmute_v1.jsonld b/packages/credential-w3c/contexts/transmute_v1.jsonld new file mode 100644 index 000000000..e51c050ad --- /dev/null +++ b/packages/credential-w3c/contexts/transmute_v1.jsonld @@ -0,0 +1,21 @@ +{ + "@context": [ + { + "@version": 1.1 + }, + "https://www.w3.org/ns/did/v1", + { + "JsonWebKey2020": "https://w3id.org/security#JsonWebKey2020", + "Ed25519VerificationKey2018": "https://w3id.org/security#Ed25519VerificationKey2018", + "X25519KeyAgreementKey2019": "https://w3id.org/security#X25519KeyAgreementKey2019", + + "publicKeyJwk": { + "@id": "https://w3id.org/security#publicKeyJwk", + "@type": "@json" + }, + "publicKeyBase58": { + "@id": "https://w3id.org/security#publicKeyBase58" + } + } + ] +} diff --git a/packages/credential-w3c/src/__tests__/message-handler.test.ts b/packages/credential-w3c/src/__tests__/message-handler.test.ts index 7f910d082..81456126d 100644 --- a/packages/credential-w3c/src/__tests__/message-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/message-handler.test.ts @@ -63,6 +63,10 @@ describe('@veramo/credential-w3c', () => { } } }, + createVerifiableCredential: jest.fn(), + createVerifiablePresentation: jest.fn(), + verifyVerifiableCredential: jest.fn(), + verifyVerifiablePresentation: jest.fn(), getDIDComponentById: jest.fn(), }, } diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/contexts.ts index 710620b33..e234667db 100644 --- a/packages/credential-w3c/src/contexts.ts +++ b/packages/credential-w3c/src/contexts.ts @@ -14,6 +14,8 @@ const contexts = new Map([ ['https://veramo.io/contexts/kyc/v1', _read('kyc-v1.jsonld')], ['https://veramo.io/contexts/profile/v1', _read('profile-v1.jsonld')], ['https://www.w3.org/ns/did/v1', _read('security_context_v1.jsonld')], + ['https://w3id.org/did/v0.11', _read('did_v0.11.jsonld')], + ['https://ns.did.ai/transmute/v1', _read('transmute_v1.jsonld')], ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')] ]); diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index 366da3d6a..58e7f0098 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -52,7 +52,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { async handle(message: Message, context: IContext): Promise { const meta = message.getLastMetaData() - console.log(JSON.stringify(message, null, 2)) + // console.log(JSON.stringify(message, null, 2)) //FIXME: messages should not be expected to be only JWT if (meta?.type === 'JWT' && message.raw) { From 9e85f0a9ce6ab3822f55ef49ea064e88d3317414 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Tue, 18 May 2021 09:39:20 -0400 Subject: [PATCH 21/48] fix: Update after cherry-picking commits. --- __tests__/localMemoryStoreAgent.test.ts | 1 - __tests__/restAgent.test.ts | 1 + packages/credential-w3c/package.json | 1 + packages/credential-w3c/src/action-handler.ts | 34 +++++++++++++++---- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 5d307c03d..b2ea43855 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -53,7 +53,6 @@ import keyManager from './shared/keyManager' import didManager from './shared/didManager' import didCommPacking from './shared/didCommPacking' import messageHandler from './shared/messageHandler' -import { KeyDIDProvider } from '@veramo/did-provider-key' const databaseFile = `./tmp/local-database2-${Math.random().toPrecision(5)}.sqlite` const infuraProjectId = '3586660d179141e3801c3895de1c2eba' diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 0ad569382..796dde6af 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -51,6 +51,7 @@ import { AgentRouter, RequestWithAgentRouter, MessagingRouter } from '../package import { getDidKeyResolver } from '../packages/did-provider-key/src' import { IDIDDiscovery, DIDDiscovery } from '../packages/did-discovery/src' import { KeyDIDProvider } from '../packages/did-provider-key/src' +import { getUniversalResolver } from '../packages/did-resolver/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' import { Resolver } from 'did-resolver' diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index 591e7dd08..e1f364d94 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -16,6 +16,7 @@ "dependencies": { "@transmute/did-key-ed25519": "^0.2.1-unstable.41", "@transmute/ed25519-signature-2020": "^0.2.1-unstable.10", + "@transmute/ed25519-signature-2018": "^0.6.1-unstable.28", "@veramo/core": "^3.1.0", "@veramo/did-jwt": "^3.1.0", "@veramo/did-resolver": "^3.1.0", diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 92818948f..52b38129c 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -25,7 +25,7 @@ const vc = require('vc-js'); const { defaultDocumentLoader } = vc; const {extendContextLoader} = require('jsonld-signatures'); const {EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020} = require('EcdsaSecp256k1RecoverySignature2020') -import {Ed25519Signature2020, Ed25519KeyPair2020} from '@transmute/ed25519-signature-2020' +import {Ed25519Signature2018, Ed25519KeyPair} from '@transmute/ed25519-signature-2018' const Base58 = require('base-58'); // Start END LD Libraries @@ -312,6 +312,20 @@ const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: }) } + // did:key + if (url.toLowerCase().startsWith('did:key')) { + // TODO: Fix the strange id naming in did:key. make sure its ${}#controller + // let newId = ''; + // returnDocument.publicKey?.forEach(x => { + // newId = `${x.id.substring(0, x.id.lastIndexOf("#"))}#controller` + // x.id = newId + // }) + // + // returnDocument.assertionMethod = [ newId ] + // returnDocument.verificationMethod = returnDocument.publicKey + // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) + } + // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) return { @@ -354,9 +368,17 @@ const getLDSigningSuite = (key: IKey, identifier: IIdentifier) => { }); break; case 'Ed25519': - suite = new Ed25519Signature2020({ - key: new Ed25519KeyPair2020({ - id: `${controller}#controller`, + // DID Key ID + let id = `${controller}#controller` + // TODO: Hacky id adjustment + if (controller.startsWith('did:key')) { + id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` + } + + + suite = new Ed25519Signature2018({ + key: new Ed25519KeyPair({ + id, controller, publicKeyBase58: Base58.encode(Buffer.from(key.publicKeyHex, 'hex')), privateKeyBase58: Base58.encode(Buffer.from(key.privateKeyHex, 'hex')), @@ -603,7 +625,7 @@ export class CredentialIssuer implements IAgentPlugin { const result = await vc.verifyCredential({ credential, - suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2020()], + suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2018()], documentLoader: getDocumentLoader(context), purpose: new AssertionProofPurpose(), compactProof: false @@ -634,7 +656,7 @@ export class CredentialIssuer implements IAgentPlugin { const result = await vc.verify({ presentation, - suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2020()], + suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2018({})], documentLoader: getDocumentLoader(context), challenge: args.challenge, domain: args.domain, From 70840029253f6ba6f7cc0c6f1dc61f23365f7047 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Thu, 8 Jul 2021 07:48:56 -0400 Subject: [PATCH 22/48] chore: resolve technical merge conflicts (with tests still failing) --- packages/credential-w3c/package.json | 2 +- packages/credential-w3c/src/action-handler.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index e1f364d94..c0a1815b6 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -16,7 +16,7 @@ "dependencies": { "@transmute/did-key-ed25519": "^0.2.1-unstable.41", "@transmute/ed25519-signature-2020": "^0.2.1-unstable.10", - "@transmute/ed25519-signature-2018": "^0.6.1-unstable.28", + "@transmute/ed25519-signature-2018": "^0.7.0-unstable.2", "@veramo/core": "^3.1.0", "@veramo/did-jwt": "^3.1.0", "@veramo/did-resolver": "^3.1.0", diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 52b38129c..03ad605d8 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -25,7 +25,7 @@ const vc = require('vc-js'); const { defaultDocumentLoader } = vc; const {extendContextLoader} = require('jsonld-signatures'); const {EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020} = require('EcdsaSecp256k1RecoverySignature2020') -import {Ed25519Signature2018, Ed25519KeyPair} from '@transmute/ed25519-signature-2018' +import {Ed25519Signature2018, Ed25519VerificationKey2018} from '@transmute/ed25519-signature-2018' const Base58 = require('base-58'); // Start END LD Libraries @@ -377,7 +377,7 @@ const getLDSigningSuite = (key: IKey, identifier: IIdentifier) => { suite = new Ed25519Signature2018({ - key: new Ed25519KeyPair({ + key: new Ed25519VerificationKey2018({ id, controller, publicKeyBase58: Base58.encode(Buffer.from(key.publicKeyHex, 'hex')), From e37b4c53e209fb4f4822039351f53a3d2c84bbe3 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Tue, 27 Jul 2021 11:03:07 -0400 Subject: [PATCH 23/48] fix: failing test for ed25519 signature. (Transmute Library Update) --- .../contexts/ed25519-signature-2018-v1.jsonld | 91 +++++ packages/credential-w3c/plugin.schema.json | 376 ++++++++++-------- packages/credential-w3c/src/action-handler.ts | 4 +- packages/credential-w3c/src/contexts.ts | 3 +- 4 files changed, 308 insertions(+), 166 deletions(-) create mode 100644 packages/credential-w3c/contexts/ed25519-signature-2018-v1.jsonld diff --git a/packages/credential-w3c/contexts/ed25519-signature-2018-v1.jsonld b/packages/credential-w3c/contexts/ed25519-signature-2018-v1.jsonld new file mode 100644 index 000000000..6533c287e --- /dev/null +++ b/packages/credential-w3c/contexts/ed25519-signature-2018-v1.jsonld @@ -0,0 +1,91 @@ +{ + "@context": { + "id": "@id", + "type": "@type", + "@protected": true, + "proof": { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph" + }, + "Ed25519VerificationKey2018": { + "@id": "https://w3id.org/security#Ed25519VerificationKey2018", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "controller": { + "@id": "https://w3id.org/security#controller", + "@type": "@id" + }, + "revoked": { + "@id": "https://w3id.org/security#revoked", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "publicKeyBase58": { + "@id": "https://w3id.org/security#publicKeyBase58" + } + } + }, + "Ed25519Signature2018": { + "@id": "https://w3id.org/security#Ed25519Signature2018", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "challenge": "https://w3id.org/security#challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "domain": "https://w3id.org/security#domain", + "expires": { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "nonce": "https://w3id.org/security#nonce", + "proofPurpose": { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "jws": { + "@id": "https://w3id.org/security#jws" + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } + } + } +} diff --git a/packages/credential-w3c/plugin.schema.json b/packages/credential-w3c/plugin.schema.json index 0d7b9e8a8..617c5bacf 100644 --- a/packages/credential-w3c/plugin.schema.json +++ b/packages/credential-w3c/plugin.schema.json @@ -6,97 +6,96 @@ "type": "object", "properties": { "credential": { - "$ref": "#/components/schemas/W3CCredential", - "description": "The json payload of the Credential according to the {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model}\n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" - }, - "save": { - "type": "boolean", - "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the {@link @veramo/core#IDataStore | storage plugin} to be saved" - }, - "proofFormat": { - "$ref": "#/components/schemas/EncodingFormat", - "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" - } - }, - "required": [ - "credential", - "proofFormat" - ], - "description": "Encapsulates the parameters required to create a {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential}" - }, - "W3CCredential": { - "type": "object", - "properties": { - "@context": { - "type": "array", - "items": { - "type": "string" - } - }, - "id": { - "type": "string" - }, - "type": { - "type": "array", - "items": { - "type": "string" - } - }, - "issuer": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - }, - "required": [ - "id" - ] - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - }, - "additionalProperties": true - }, - "credentialStatus": { "type": "object", "properties": { + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, "id": { "type": "string" }, "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "issuer": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { "type": "string" + }, + "credentialSubject": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "credentialStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ] } }, "required": [ - "id", - "type" - ] + "issuer", + "credentialSubject" + ], + "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" + }, + "save": { + "type": "boolean", + "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved" + }, + "proofFormat": { + "$ref": "#/components/schemas/ProofFormat", + "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" + }, + "removeOriginalFields": { + "type": "boolean", + "description": "Remove payload members during JWT-JSON transformation. Defaults to `true`. See https://www.w3.org/TR/vc-data-model/#jwt-encoding" } }, "required": [ - "@context", - "type", - "issuer", - "issuanceDate", - "credentialSubject" + "credential", + "proofFormat" ], - "description": "W3CCredential {@link https://github.com/decentralized-identifier/did-jwt-vc}" + "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" }, - "EncodingFormat": { + "ProofFormat": { "type": "string", - "const": "jwt", - "description": "The type of encoding to be used for the Verifiable Credential or Presentation to be generated.\n\nOnly `jwt` is supported at the moment." + "enum": [ + "jwt", + "lds" + ], + "description": "The type of encoding to be used for the Verifiable Credential or Presentation to be generated.\n\nOnly `jwt` and `lds` is supported at the moment." }, "VerifiableCredential": { "type": "object", @@ -173,108 +172,84 @@ "credentialSubject", "proof" ], - "description": "Verifiable Credential {@link https://github.com/decentralized-identifier/did-jwt-vc}" + "description": "Verifiable Credential {@link https://github.com/decentralized-identifier/did-jwt-vc }" }, "ICreateVerifiablePresentationArgs": { "type": "object", "properties": { "presentation": { - "$ref": "#/components/schemas/W3CPresentation", - "description": "The json payload of the Presentation according to the {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model}.\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`" + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "holder": { + "type": "string" + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "verifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "verifiableCredential": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VerifiableCredential" + } + } + }, + "required": [ + "holder", + "verifier", + "verifiableCredential" + ], + "description": "The json payload of the Presentation according to the\n {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model } .\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, "save": { "type": "boolean", - "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the {@link @veramo/core#IDataStore | storage plugin} to be saved" - }, - "proofFormat": { - "$ref": "#/components/schemas/EncodingFormat", - "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" - } - }, - "required": [ - "presentation", - "proofFormat" - ], - "description": "Encapsulates the parameters required to create a {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation}" - }, - "IVerifyVerifiablePresentationArgs": { - "type": "object", - "properties": { - "presentation": { - "$ref": "#/components/schemas/VerifiablePresentation", - "description": "The json payload of the Presentation according to the {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model}.\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`" + "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved" }, "challenge": { "type": "string", - "description": "Optional challenge to be past to verification" - } - }, - "required": [ - "presentation" - ], - "description": "Encapsulates the parameters required to verify a {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation}" - }, - "IVerifyVerifiableCredentialArgs": { - "type": "object", - "properties": { - "credential": { - "$ref": "#/components/schemas/VerifiableCredential", - "description": "The json payload of the Credential according to the {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model}.\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`" - } - }, - "required": [ - "credential" - ], - "description": "Encapsulates the parameters required to verify a {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential}" - }, - "W3CPresentation": { - "type": "object", - "properties": { - "id": { - "type": "string" + "description": "Optional (only JWT) string challenge parameter to add to the verifiable presentation." }, - "holder": { - "type": "string" - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "@context": { - "type": "array", - "items": { - "type": "string" - } - }, - "type": { - "type": "array", - "items": { - "type": "string" - } + "domain": { + "type": "string", + "description": "Optional string domain parameter to add to the verifiable presentation." }, - "verifier": { - "type": "array", - "items": { - "type": "string" - } + "proofFormat": { + "$ref": "#/components/schemas/ProofFormat", + "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } + "removeOriginalFields": { + "type": "boolean", + "description": "Remove payload members during JWT-JSON transformation. Defaults to `true`. See https://www.w3.org/TR/vc-data-model/#jwt-encoding" } }, "required": [ - "holder", - "@context", - "type", - "verifier", - "verifiableCredential" + "presentation", + "proofFormat" ], - "description": "W3CPresentation {@link https://github.com/decentralized-identifier/did-jwt-vc}" + "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" }, "VerifiablePresentation": { "type": "object", @@ -332,7 +307,82 @@ "verifiableCredential", "proof" ], - "description": "Verifiable Presentation {@link https://github.com/decentralized-identifier/did-jwt-vc}" + "description": "Verifiable Presentation {@link https://github.com/decentralized-identifier/did-jwt-vc }" + }, + "IVerifyVerifiableCredentialArgs": { + "type": "object", + "properties": { + "credential": { + "$ref": "#/components/schemas/VerifiableCredential", + "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" + } + }, + "required": [ + "credential" + ], + "description": "Encapsulates the parameters required to verify a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" + }, + "IVerifyVerifiablePresentationArgs": { + "type": "object", + "properties": { + "presentation": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "holder": { + "type": "string" + }, + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "verifiableCredential": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VerifiableCredential" + } + }, + "proof": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } + } + }, + "required": [ + "holder", + "@context", + "type", + "verifiableCredential", + "proof" + ], + "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" + }, + "challenge": { + "type": "string", + "description": "Optional (only for JWT) string challenge parameter to verify the verifiable presentation against" + }, + "domain": { + "type": "string", + "description": "Optional (only for JWT) string domain parameter to verify the verifiable presentation against" + } + }, + "required": [ + "presentation" + ], + "description": "Encapsulates the parameters required to verify a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" } }, "methods": { @@ -355,7 +405,7 @@ } }, "verifyVerifiableCredential": { - "description": "Verifies a Verifiable Credential.", + "description": "Verifies a Verifiable Credential JWT or LDS Format.", "arguments": { "$ref": "#/components/schemas/IVerifyVerifiableCredentialArgs" }, @@ -364,7 +414,7 @@ } }, "verifyVerifiablePresentation": { - "description": "Verifies a Verifiable Presentation.", + "description": "Verifies a Verifiable Presentation JWT or LDS Format.", "arguments": { "$ref": "#/components/schemas/IVerifyVerifiablePresentationArgs" }, @@ -375,4 +425,4 @@ } } } -} +} \ No newline at end of file diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 03ad605d8..3388b2723 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -380,8 +380,8 @@ const getLDSigningSuite = (key: IKey, identifier: IIdentifier) => { key: new Ed25519VerificationKey2018({ id, controller, - publicKeyBase58: Base58.encode(Buffer.from(key.publicKeyHex, 'hex')), - privateKeyBase58: Base58.encode(Buffer.from(key.privateKeyHex, 'hex')), + publicKey: Buffer.from(key.publicKeyHex, 'hex'), + privateKey: Buffer.from(key.privateKeyHex, 'hex'), }) }); break; diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/contexts.ts index e234667db..ec7e4d660 100644 --- a/packages/credential-w3c/src/contexts.ts +++ b/packages/credential-w3c/src/contexts.ts @@ -16,7 +16,8 @@ const contexts = new Map([ ['https://www.w3.org/ns/did/v1', _read('security_context_v1.jsonld')], ['https://w3id.org/did/v0.11', _read('did_v0.11.jsonld')], ['https://ns.did.ai/transmute/v1', _read('transmute_v1.jsonld')], - ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')] + ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')], + ['https://w3id.org/security/suites/ed25519-2018/v1', _read('ed25519-signature-2018-v1.jsonld')] ]); export default contexts; From e4feb35aa56fa6b6f7953687c9b4f39339a23332 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 2 Aug 2021 08:14:20 -0400 Subject: [PATCH 24/48] feat: moved LD logic into dedicated module --- __tests__/localAgent.test.ts | 7 +- __tests__/localMemoryStoreAgent.test.ts | 7 +- __tests__/restAgent.test.ts | 7 +- packages/cli/default/default.yml | 14 +- .../src/__tests__/action-handler.test.ts | 5 +- packages/credential-w3c/src/action-handler.ts | 241 +---------------- packages/credential-w3c/src/index.ts | 1 + .../src/ld-credential-module.ts | 255 ++++++++++++++++++ 8 files changed, 300 insertions(+), 237 deletions(-) create mode 100644 packages/credential-w3c/src/ld-credential-module.ts diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 14b5d268c..50095dcb5 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -20,7 +20,7 @@ import { KeyManager } from '../packages/key-manager/src' import { DIDManager, AliasDiscoveryProvider } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler } from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, LdCredentialModule } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider } from '../packages/did-provider-key/src' @@ -201,7 +201,10 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm([new DIDCommHttpTransport()]), - new CredentialIssuer(), + new CredentialIssuer({ + ldCredentialModule: new LdCredentialModule() + } + ), new SelectiveDisclosure(), new DIDDiscovery({ providers: [new AliasDiscoveryProvider(), new ProfileDiscoveryProvider()], diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index b2ea43855..df6f15d6d 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -19,7 +19,7 @@ import { DIDManager, MemoryDIDStore } from '../packages/did-manager/src' import { createConnection, Connection } from 'typeorm' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler } from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, LdCredentialModule } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider } from '../packages/did-provider-key/src' @@ -159,7 +159,10 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm(), - new CredentialIssuer(), + new CredentialIssuer({ + ldCredentialModule: new LdCredentialModule() + } + ), new SelectiveDisclosure(), ...(options?.plugins || []), ], diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 796dde6af..e53bca2fc 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -23,7 +23,7 @@ import { KeyManager } from '../packages/key-manager/src' import { DIDManager, AliasDiscoveryProvider } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler } from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, LdCredentialModule } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider } from '../packages/did-provider-key/src' @@ -187,7 +187,10 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm([new DIDCommHttpTransport()]), - new CredentialIssuer(), + new CredentialIssuer({ + ldCredentialModule: new LdCredentialModule() + } + ), new SelectiveDisclosure(), new DIDDiscovery({ providers: [new AliasDiscoveryProvider(), new ProfileDiscoveryProvider()], diff --git a/packages/cli/default/default.yml b/packages/cli/default/default.yml index be5cf5859..5d5f749c9 100644 --- a/packages/cli/default/default.yml +++ b/packages/cli/default/default.yml @@ -277,6 +277,18 @@ didDiscovery: - $require: '@veramo/did-manager#AliasDiscoveryProvider' - $require: '@veramo/data-store#ProfileDiscoveryProvider' +# LD-Handler +ldCredentialModule: + - $require: '@veramo/credential-w3c#LdCredentialModule' + + +# W3C credentialPlugin +credentialPlugin: + - $require: '@veramo/credential-w3c#CredentialIssuer' + $args: + - ldCredentialModule: + $ref: /ldCredentialModule + # Agent agent: $require: '@veramo/core#Agent' @@ -289,7 +301,7 @@ agent: - $ref: /didDiscovery - $ref: /messageHandler - $require: '@veramo/did-comm#DIDComm' - - $require: '@veramo/credential-w3c#CredentialIssuer' + - $ref: /credentialPlugin - $require: '@veramo/selective-disclosure#SelectiveDisclosure' - $require: '@veramo/data-store#DataStore' $args: diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index d3031f5e2..d6ad53921 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -17,6 +17,7 @@ const mockDidJwtVc = { jest.mock('did-jwt-vc', () => mockDidJwtVc) import { CredentialIssuer, IContext } from '../action-handler' +import { LdCredentialModule } from '../ld-credential-module' const mockIdentifiers: IIdentifier[] = [ { @@ -63,7 +64,9 @@ const mockIdentifiers: IIdentifier[] = [ }, ] -const w3c = new CredentialIssuer() +const w3c = new CredentialIssuer({ + ldCredentialModule: new LdCredentialModule() +}) let agent = { execute: jest.fn(), diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 3388b2723..b4baa3672 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -7,7 +7,7 @@ import { IPluginMethodMap, VerifiableCredential, VerifiablePresentation, - IDataStore, IKey, IIdentifier, + IDataStore, IKey, } from '@veramo/core' import { @@ -19,18 +19,7 @@ import { PresentationPayload, } from 'did-jwt-vc' -// Start LOAD LD Libraries -const { purposes: { AssertionProofPurpose }} = require("jsonld-signatures"); -const vc = require('vc-js'); -const { defaultDocumentLoader } = vc; -const {extendContextLoader} = require('jsonld-signatures'); -const {EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020} = require('EcdsaSecp256k1RecoverySignature2020') -import {Ed25519Signature2018, Ed25519VerificationKey2018} from '@transmute/ed25519-signature-2018' -const Base58 = require('base-58'); -// Start END LD Libraries - -import { schema } from './' -import localContexts from './contexts' +import { LdCredentialModule, schema } from './' import Debug from 'debug' const debug = Debug('veramo:w3c:action-handler') @@ -275,183 +264,6 @@ export type IContext = IAgentContext< Pick > - -//------------------------- BEGIN JSON_LD HELPER / DELEGATING STUFF -/** - * TODO: General Implementation Notes - * - (SOLVED) EcdsaSecp256k1Signature2019 (Signature) and EcdsaSecp256k1VerificationKey2019 (Key) - * are not useable right now, since they are not able to work with blockChainId and ECRecover. - * - DID Fragement Resolution. - * - Key Manager and Verification Methods: Veramo currently implements no link between those. - */ - -const getDocumentLoader = (context: IContext) => extendContextLoader(async (url: string) => { - // console.log(`resolving context for: ${url}`) - - // did resolution - if (url.toLowerCase().startsWith('did:')) { - const didDoc = await context.agent.resolveDid({ didUrl: url }) - let returnDocument = didDoc.didDocument - - if (!returnDocument) return - - // specific resolution modifications - // did:ethr - if (url.toLowerCase().startsWith('did:ethr')) { - returnDocument.assertionMethod = [] - // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId - // blockchainAccountId to ethereumAddress - returnDocument.verificationMethod?.forEach(x => { - if (x.blockchainAccountId) { - x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf("@")) - } - - // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." - // @ts-ignore - returnDocument.assertionMethod.push(x.id) - }) - } - - // did:key - if (url.toLowerCase().startsWith('did:key')) { - // TODO: Fix the strange id naming in did:key. make sure its ${}#controller - // let newId = ''; - // returnDocument.publicKey?.forEach(x => { - // newId = `${x.id.substring(0, x.id.lastIndexOf("#"))}#controller` - // x.id = newId - // }) - // - // returnDocument.assertionMethod = [ newId ] - // returnDocument.verificationMethod = returnDocument.publicKey - // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) - } - - - // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) - return { - contextUrl: null, - documentUrl: url, - document: returnDocument - }; - } - - if (localContexts.has(url)) { - // console.log(`Returning local context for: ${url}`) - return { - contextUrl: null, - documentUrl: url, - document: localContexts.get(url) - }; - } - - return defaultDocumentLoader(url); -}); - -const getLDSigningSuite = (key: IKey, identifier: IIdentifier) => { - let suite - const controller = identifier.did - - if (!key.privateKeyHex) { - throw Error('No private Key for LD Signing available.') - } - - switch(key.type) { - case 'Secp256k1': - suite = new EcdsaSecp256k1RecoverySignature2020({ - key: new EcdsaSecp256k1RecoveryMethod2020({ - publicKeyHex: key.publicKeyHex, - privateKeyHex: key.privateKeyHex, - type: 'EcdsaSecp256k1RecoveryMethod2020', // A little verbose? - controller, - id: `${controller}#controller` // TODO: Only default controller verificationMethod supported - }), - }); - break; - case 'Ed25519': - // DID Key ID - let id = `${controller}#controller` - // TODO: Hacky id adjustment - if (controller.startsWith('did:key')) { - id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` - } - - - suite = new Ed25519Signature2018({ - key: new Ed25519VerificationKey2018({ - id, - controller, - publicKey: Buffer.from(key.publicKeyHex, 'hex'), - privateKey: Buffer.from(key.privateKeyHex, 'hex'), - }) - }); - break; - default: - throw new Error(`Unknown key type ${key.type}.`); - } - - return suite -} - -const issueLDVerifiableCredential = async ( - credential: W3CCredential, - key: IKey, - identifier: IIdentifier, - context: IContext): Promise => { - - const suite = getLDSigningSuite(key, identifier) - const documentLoader = getDocumentLoader(context) - - // some suites are misssisng the right contexts - switch (suite.type) { - case "EcdsaSecp256k1RecoverySignature2020": - // console.log(`Adding context to credential ${suite.type}`) - credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') - break - default: - } - - return await vc.issue({ - credential, - suite, - documentLoader, - compactProof: false - }); -} - -const signLDVerifiablePresentation = async ( - presentation: W3CPresentation, - key: IKey, - challenge: string | undefined, - domain: string | undefined, - identifier: IIdentifier, - context: IContext -): Promise => { - - const suite = getLDSigningSuite(key, identifier) - const documentLoader = getDocumentLoader(context) - - // TODO: Remove invalid field 'verifiers' from Presentation. Needs to be adapted for LD credentials - // Only remove empty array (vc.signPresentation will throw then) - const sanitizedPresentation = presentation as any - if (sanitizedPresentation.verifier.length == 0) { - delete sanitizedPresentation.verifier - } - - - return await vc.signPresentation({ - presentation: sanitizedPresentation, - suite, - challenge, - domain, - documentLoader, - purpose: new AssertionProofPurpose(), - compactProof: false - }) -} - -//------------------------- END JSON_LD HELPER / DELEGATING STUFF - - /** * A Veramo plugin that implements the {@link ICredentialIssuer} methods. * @@ -461,7 +273,12 @@ export class CredentialIssuer implements IAgentPlugin { readonly methods: ICredentialIssuer readonly schema = schema.ICredentialIssuer - constructor() { + private ldCredentialModule: LdCredentialModule + constructor(options: { + ldCredentialModule: LdCredentialModule + }) { + this.ldCredentialModule = options.ldCredentialModule + this.methods = { createVerifiablePresentation: this.createVerifiablePresentation, createVerifiableCredential: this.createVerifiableCredential, @@ -506,7 +323,7 @@ export class CredentialIssuer implements IAgentPlugin { } const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - return await signLDVerifiablePresentation(presentation, + return await this.ldCredentialModule.signLDVerifiablePresentation(presentation, keyPayload, args.challenge, args.domain, identifier, context) } //------------------------- END JSON_LD INSERT @@ -580,7 +397,7 @@ export class CredentialIssuer implements IAgentPlugin { } const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - verifiableCredential = await issueLDVerifiableCredential(credential, keyPayload, identifier, context) + verifiableCredential = await this.ldCredentialModule.issueLDVerifiableCredential(credential, keyPayload, identifier, context) } else { //------------------------- END JSON_LD INSERT //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` @@ -623,23 +440,7 @@ export class CredentialIssuer implements IAgentPlugin { throw Error('verifyVerifiableCredential currently does not the verification of VC-JWT credentials.') } - const result = await vc.verifyCredential({ - credential, - suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2018()], - documentLoader: getDocumentLoader(context), - purpose: new AssertionProofPurpose(), - compactProof: false - }); - - if (result.verified) - return true - - // NOT verified. - - // result can include raw Error - console.log(`Error verifying LD Verifiable Credential`) - console.log(JSON.stringify(result, null, 2)); - throw Error('Error verifying LD Verifiable Credential') + return this.ldCredentialModule.verifyVerifiableCredential(credential, context) } /** {@inheritdoc ICredentialIssuer.verifyVerifiablePresentation} */ @@ -654,25 +455,7 @@ export class CredentialIssuer implements IAgentPlugin { throw Error('verifyVerifiablePresentation currently does not the verification of VC-JWT credentials.') } - const result = await vc.verify({ - presentation, - suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2018({})], - documentLoader: getDocumentLoader(context), - challenge: args.challenge, - domain: args.domain, - purpose: new AssertionProofPurpose(), - compactProof: false - }); - - if (result.verified) - return true - - // NOT verified. - - // result can include raw Error - console.log(`Error verifying LD Verifiable Presentation`) - console.log(JSON.stringify(result, null, 2)); - throw Error('Error verifying LD Verifiable Presentation') + return this.ldCredentialModule.verifyVerifiablePresentation(presentation, args.challenge, args.domain, context) } } diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index fd1725200..808a52cf7 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -15,5 +15,6 @@ export { ICreateVerifiablePresentationArgs, ProofFormat, } from './action-handler' +export { LdCredentialModule } from './ld-credential-module' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/credential-w3c/src/ld-credential-module.ts b/packages/credential-w3c/src/ld-credential-module.ts new file mode 100644 index 000000000..5bd4372e4 --- /dev/null +++ b/packages/credential-w3c/src/ld-credential-module.ts @@ -0,0 +1,255 @@ +import localContexts from './contexts' +import { + IAgentContext, + IIdentifier, + IKey, IResolver, + VerifiableCredential, + VerifiablePresentation, + W3CCredential, + W3CPresentation, +} from '@veramo/core' + +import Debug from 'debug' + +const { purposes: { AssertionProofPurpose }} = require("jsonld-signatures"); +const vc = require('vc-js'); +const { defaultDocumentLoader } = vc; +const {extendContextLoader} = require('jsonld-signatures'); +const {EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020} = require('EcdsaSecp256k1RecoverySignature2020') +import {Ed25519Signature2018, Ed25519VerificationKey2018} from '@transmute/ed25519-signature-2018' +import { IContext, IVerifyVerifiablePresentationArgs } from './action-handler' + +const debug = Debug('veramo:w3c:ld-credential-module') + +export class LdCredentialModule { + + /** + * TODO: General Implementation Notes + * - (SOLVED) EcdsaSecp256k1Signature2019 (Signature) and EcdsaSecp256k1VerificationKey2019 (Key) + * are not useable right now, since they are not able to work with blockChainId and ECRecover. + * - DID Fragement Resolution. + * - Key Manager and Verification Methods: Veramo currently implements no link between those. + */ + + getDocumentLoader(context: IAgentContext) { + return extendContextLoader(async (url: string) => { + // console.log(`resolving context for: ${url}`) + + // did resolution + if (url.toLowerCase().startsWith('did:')) { + const didDoc = await context.agent.resolveDid({ didUrl: url }) + let returnDocument = didDoc.didDocument + + if (!returnDocument) return + + // specific resolution modifications + // did:ethr + if (url.toLowerCase().startsWith('did:ethr')) { + returnDocument.assertionMethod = [] + // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId + // blockchainAccountId to ethereumAddress + returnDocument.verificationMethod?.forEach(x => { + if (x.blockchainAccountId) { + x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf("@")) + } + + // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." + // @ts-ignore + returnDocument.assertionMethod.push(x.id) + }) + } + + // did:key + if (url.toLowerCase().startsWith('did:key')) { + // TODO: Fix the strange id naming in did:key. make sure its ${}#controller + // let newId = ''; + // returnDocument.publicKey?.forEach(x => { + // newId = `${x.id.substring(0, x.id.lastIndexOf("#"))}#controller` + // x.id = newId + // }) + // + // returnDocument.assertionMethod = [ newId ] + // returnDocument.verificationMethod = returnDocument.publicKey + // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) + } + + + // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) + return { + contextUrl: null, + documentUrl: url, + document: returnDocument + }; + } + + if (localContexts.has(url)) { + // console.log(`Returning local context for: ${url}`) + return { + contextUrl: null, + documentUrl: url, + document: localContexts.get(url) + }; + } + + debug('WARNING: Possible unknown context/identifier for', url) + console.log(`WARNING: Possible unknown context/identifier for: ${url}`) + + return defaultDocumentLoader(url); + }); + } + + getLDSigningSuite(key: IKey, identifier: IIdentifier) { + let suite + const controller = identifier.did + + if (!key.privateKeyHex) { + throw Error('No private Key for LD Signing available.') + } + + switch(key.type) { + case 'Secp256k1': + suite = new EcdsaSecp256k1RecoverySignature2020({ + key: new EcdsaSecp256k1RecoveryMethod2020({ + publicKeyHex: key.publicKeyHex, + privateKeyHex: key.privateKeyHex, + type: 'EcdsaSecp256k1RecoveryMethod2020', // A little verbose? + controller, + id: `${controller}#controller` // TODO: Only default controller verificationMethod supported + }), + }); + break; + case 'Ed25519': + // DID Key ID + let id = `${controller}#controller` + // TODO: Hacky id adjustment + if (controller.startsWith('did:key')) { + id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` + } + + + suite = new Ed25519Signature2018({ + key: new Ed25519VerificationKey2018({ + id, + controller, + publicKey: Buffer.from(key.publicKeyHex, 'hex'), + privateKey: Buffer.from(key.privateKeyHex, 'hex'), + }) + }); + break; + default: + throw new Error(`Unknown key type ${key.type}.`); + } + + return suite + } + + async issueLDVerifiableCredential( + credential: W3CCredential, + key: IKey, + identifier: IIdentifier, + context: IAgentContext): Promise { + + const suite = this.getLDSigningSuite(key, identifier) + const documentLoader = this.getDocumentLoader(context) + + // some suites are misssisng the right contexts + switch (suite.type) { + case "EcdsaSecp256k1RecoverySignature2020": + // console.log(`Adding context to credential ${suite.type}`) + credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') + break + default: + } + + return await vc.issue({ + credential, + suite, + documentLoader, + compactProof: false + }); + } + + async signLDVerifiablePresentation( + presentation: W3CPresentation, + key: IKey, + challenge: string | undefined, + domain: string | undefined, + identifier: IIdentifier, + context: IAgentContext, + ): Promise { + + const suite = this.getLDSigningSuite(key, identifier) + const documentLoader = this.getDocumentLoader(context) + + // TODO: Remove invalid field 'verifiers' from Presentation. Needs to be adapted for LD credentials + // Only remove empty array (vc.signPresentation will throw then) + const sanitizedPresentation = presentation as any + if (sanitizedPresentation.verifier.length == 0) { + delete sanitizedPresentation.verifier + } + + + return await vc.signPresentation({ + presentation: sanitizedPresentation, + suite, + challenge, + domain, + documentLoader, + purpose: new AssertionProofPurpose(), + compactProof: false + }) + } + + async verifyVerifiableCredential( + credential: VerifiableCredential, + context: IAgentContext + ): Promise { + + const result = await vc.verifyCredential({ + credential, + suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2018()], + documentLoader: this.getDocumentLoader(context), + purpose: new AssertionProofPurpose(), + compactProof: false + }); + + if (result.verified) + return true + + // NOT verified. + + // result can include raw Error + console.log(`Error verifying LD Verifiable Credential`) + console.log(JSON.stringify(result, null, 2)); + throw Error('Error verifying LD Verifiable Credential') + } + + async verifyVerifiablePresentation( + presentation: any, // TODO: set Type for LD Presentation + challenge: string | undefined, + domain: string | undefined, + context: IAgentContext + ): Promise { + + const result = await vc.verify({ + presentation, + suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2018({})], + documentLoader: this.getDocumentLoader(context), + challenge, + domain, + purpose: new AssertionProofPurpose(), + compactProof: false + }); + + if (result.verified) + return true + + // NOT verified. + + // result can include raw Error + console.log(`Error verifying LD Verifiable Presentation`) + console.log(JSON.stringify(result, null, 2)); + throw Error('Error verifying LD Verifiable Presentation') + } + +} From d84edf27b0afc5b1b09db0dfab4f43571d5fcdbe Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 2 Aug 2021 08:48:01 -0400 Subject: [PATCH 25/48] fix: fixed tests by binding 'this' correctly to the class function execution. --- packages/credential-w3c/src/action-handler.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index b4baa3672..d2e4cda61 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -280,10 +280,10 @@ export class CredentialIssuer implements IAgentPlugin { this.ldCredentialModule = options.ldCredentialModule this.methods = { - createVerifiablePresentation: this.createVerifiablePresentation, - createVerifiableCredential: this.createVerifiableCredential, - verifyVerifiableCredential: this.verifyVerifiableCredential, - verifyVerifiablePresentation: this.verifyVerifiablePresentation, + createVerifiablePresentation: this.createVerifiablePresentation.bind(this), + createVerifiableCredential: this.createVerifiableCredential.bind(this), + verifyVerifiableCredential: this.verifyVerifiableCredential.bind(this), + verifyVerifiablePresentation: this.verifyVerifiablePresentation.bind(this), } } From 9af22d8a5d53267dab10852c51f8b00154fc8659 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Mon, 2 Aug 2021 14:00:12 -0400 Subject: [PATCH 26/48] feat: Post-Merge fixes. --- .../contexts/X25519KeyAgreementKey2019.jsonld | 26 +++++++++++++++++++ packages/credential-w3c/package.json | 3 ++- packages/credential-w3c/src/action-handler.ts | 22 +++------------- packages/credential-w3c/src/contexts.ts | 3 ++- .../src/ld-credential-module.ts | 17 +++++++----- .../credential-w3c/src/message-handler.ts | 2 +- 6 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 packages/credential-w3c/contexts/X25519KeyAgreementKey2019.jsonld diff --git a/packages/credential-w3c/contexts/X25519KeyAgreementKey2019.jsonld b/packages/credential-w3c/contexts/X25519KeyAgreementKey2019.jsonld new file mode 100644 index 000000000..d01bac010 --- /dev/null +++ b/packages/credential-w3c/contexts/X25519KeyAgreementKey2019.jsonld @@ -0,0 +1,26 @@ +{ + "@context": { + "id": "@id", + "type": "@type", + "@protected": true, + "X25519KeyAgreementKey2019": { + "@id": "https://w3id.org/security#X25519KeyAgreementKey2019", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "controller": { + "@id": "https://w3id.org/security#controller", + "@type": "@id" + }, + "revoked": { + "@id": "https://w3id.org/security#revoked", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "publicKeyBase58": { + "@id": "https://w3id.org/security#publicKeyBase58" + } + } + } + } +} diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index c0a1815b6..f5ad2da14 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -14,9 +14,10 @@ } }, "dependencies": { + "@transmute/credentials-context": "^0.7.0-unstable.6", + "@transmute/ed25519-signature-2018": "^0.7.0-unstable.6", "@transmute/did-key-ed25519": "^0.2.1-unstable.41", "@transmute/ed25519-signature-2020": "^0.2.1-unstable.10", - "@transmute/ed25519-signature-2018": "^0.7.0-unstable.2", "@veramo/core": "^3.1.0", "@veramo/did-jwt": "^3.1.0", "@veramo/did-resolver": "^3.1.0", diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index d2e4cda61..f8ebf6cb3 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -131,7 +131,7 @@ export interface IVerifyVerifiableCredentialArgs { * of the `credential` * */ - credential: VerifiableCredential + credential: Partial } /** @@ -149,21 +149,7 @@ export interface IVerifyVerifiablePresentationArgs { * of the `credential` * */ - presentation: { - id?: string - holder: string - // issuanceDate?: string - // expirationDate?: string - '@context': string[] - type: string[] - // verifier: string[] - verifiableCredential: VerifiableCredential[] - proof: { - type?: string - [x: string]: any - } - [x: string]: any - } + presentation: Partial /** * Optional (only for JWT) string challenge parameter to verify the verifiable presentation against @@ -400,9 +386,7 @@ export class CredentialIssuer implements IAgentPlugin { verifiableCredential = await this.ldCredentialModule.issueLDVerifiableCredential(credential, keyPayload, identifier, context) } else { //------------------------- END JSON_LD INSERT - //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` - - + //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` or `lds` debug('Signing VC with', identifier.did) let alg = 'ES256K' if (key.type === 'Ed25519') { diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/contexts.ts index ec7e4d660..cf3c7eec1 100644 --- a/packages/credential-w3c/src/contexts.ts +++ b/packages/credential-w3c/src/contexts.ts @@ -17,7 +17,8 @@ const contexts = new Map([ ['https://w3id.org/did/v0.11', _read('did_v0.11.jsonld')], ['https://ns.did.ai/transmute/v1', _read('transmute_v1.jsonld')], ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')], - ['https://w3id.org/security/suites/ed25519-2018/v1', _read('ed25519-signature-2018-v1.jsonld')] + ['https://w3id.org/security/suites/ed25519-2018/v1', _read('ed25519-signature-2018-v1.jsonld')], + ['https://w3id.org/security/suites/x25519-2019/v1', _read('X25519KeyAgreementKey2019.jsonld')] ]); export default contexts; diff --git a/packages/credential-w3c/src/ld-credential-module.ts b/packages/credential-w3c/src/ld-credential-module.ts index 5bd4372e4..d5e7f3f62 100644 --- a/packages/credential-w3c/src/ld-credential-module.ts +++ b/packages/credential-w3c/src/ld-credential-module.ts @@ -5,8 +5,6 @@ import { IKey, IResolver, VerifiableCredential, VerifiablePresentation, - W3CCredential, - W3CPresentation, } from '@veramo/core' import Debug from 'debug' @@ -18,6 +16,7 @@ const {extendContextLoader} = require('jsonld-signatures'); const {EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020} = require('EcdsaSecp256k1RecoverySignature2020') import {Ed25519Signature2018, Ed25519VerificationKey2018} from '@transmute/ed25519-signature-2018' import { IContext, IVerifyVerifiablePresentationArgs } from './action-handler' +import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' const debug = Debug('veramo:w3c:ld-credential-module') @@ -144,7 +143,7 @@ export class LdCredentialModule { } async issueLDVerifiableCredential( - credential: W3CCredential, + credential: Partial, key: IKey, identifier: IIdentifier, context: IAgentContext): Promise { @@ -152,10 +151,14 @@ export class LdCredentialModule { const suite = this.getLDSigningSuite(key, identifier) const documentLoader = this.getDocumentLoader(context) - // some suites are misssisng the right contexts + // some suites are missing the right contexts + // TODO: How to generalize this? switch (suite.type) { case "EcdsaSecp256k1RecoverySignature2020": // console.log(`Adding context to credential ${suite.type}`) + if (!Array.isArray(credential['@context'])) { + credential['@context'] = [] + } credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') break default: @@ -170,7 +173,7 @@ export class LdCredentialModule { } async signLDVerifiablePresentation( - presentation: W3CPresentation, + presentation: Partial, key: IKey, challenge: string | undefined, domain: string | undefined, @@ -201,7 +204,7 @@ export class LdCredentialModule { } async verifyVerifiableCredential( - credential: VerifiableCredential, + credential: Partial, context: IAgentContext ): Promise { @@ -225,7 +228,7 @@ export class LdCredentialModule { } async verifyVerifiablePresentation( - presentation: any, // TODO: set Type for LD Presentation + presentation: Partial, challenge: string | undefined, domain: string | undefined, context: IAgentContext diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index 58e7f0098..229d97074 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -130,7 +130,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { // throws on error. await context.agent.verifyVerifiablePresentation({ - presentation, + presentation , // TODO: HARDCODED CHALLENGE VERIFICATION FOR NOW challenge: 'VERAMO', domain: 'VERAMO' From 69143db4839c94e6cc87b6509b855df9fbd37321 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Tue, 3 Aug 2021 08:57:38 -0400 Subject: [PATCH 27/48] feat: Allow to dynamically load ld-contexts --- __tests__/localAgent.test.ts | 15 ++++++++++-- __tests__/localMemoryStoreAgent.test.ts | 14 +++++++++-- __tests__/restAgent.test.ts | 14 +++++++++-- packages/cli/default/default.yml | 15 +++++++++++- .../src/__tests__/action-handler.test.ts | 22 ++++++++--------- packages/credential-w3c/src/index.ts | 2 ++ .../credential-w3c/src/ld-context-loader.ts | 24 +++++++++++++++++++ .../src/ld-credential-module.ts | 16 +++++++++---- .../{contexts.ts => ld-default-contexts.ts} | 5 +--- 9 files changed, 101 insertions(+), 26 deletions(-) create mode 100644 packages/credential-w3c/src/ld-context-loader.ts rename packages/credential-w3c/src/{contexts.ts => ld-default-contexts.ts} (94%) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 50095dcb5..408af5f65 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -20,7 +20,14 @@ import { KeyManager } from '../packages/key-manager/src' import { DIDManager, AliasDiscoveryProvider } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, LdCredentialModule } from '../packages/credential-w3c/src' +import { + CredentialIssuer, + ICredentialIssuer, + W3cMessageHandler, + LdCredentialModule, + LdContextLoader, + LdDefaultContexts, +} from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider } from '../packages/did-provider-key/src' @@ -202,7 +209,11 @@ const setup = async (options?: IAgentOptions): Promise => { }), new DIDComm([new DIDCommHttpTransport()]), new CredentialIssuer({ - ldCredentialModule: new LdCredentialModule() + ldCredentialModule: new LdCredentialModule({ + ldContextLoader: new LdContextLoader({ + contextsPaths: [ LdDefaultContexts ] + }) + }) } ), new SelectiveDisclosure(), diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index df6f15d6d..65701ba0d 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -19,7 +19,13 @@ import { DIDManager, MemoryDIDStore } from '../packages/did-manager/src' import { createConnection, Connection } from 'typeorm' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, LdCredentialModule } from '../packages/credential-w3c/src' +import { + CredentialIssuer, + ICredentialIssuer, + W3cMessageHandler, + LdCredentialModule, + LdContextLoader, LdDefaultContexts, +} from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider } from '../packages/did-provider-key/src' @@ -160,7 +166,11 @@ const setup = async (options?: IAgentOptions): Promise => { }), new DIDComm(), new CredentialIssuer({ - ldCredentialModule: new LdCredentialModule() + ldCredentialModule: new LdCredentialModule({ + ldContextLoader: new LdContextLoader({ + contextsPaths: [ LdDefaultContexts ] + }) + }) } ), new SelectiveDisclosure(), diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index e53bca2fc..6f968adea 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -23,7 +23,13 @@ import { KeyManager } from '../packages/key-manager/src' import { DIDManager, AliasDiscoveryProvider } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, LdCredentialModule } from '../packages/credential-w3c/src' +import { + CredentialIssuer, + ICredentialIssuer, + W3cMessageHandler, + LdCredentialModule, + LdContextLoader, LdDefaultContexts, +} from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider } from '../packages/did-provider-key/src' @@ -188,7 +194,11 @@ const setup = async (options?: IAgentOptions): Promise => { }), new DIDComm([new DIDCommHttpTransport()]), new CredentialIssuer({ - ldCredentialModule: new LdCredentialModule() + ldCredentialModule: new LdCredentialModule({ + ldContextLoader: new LdContextLoader({ + contextsPaths: [ LdDefaultContexts ] + }) + }) } ), new SelectiveDisclosure(), diff --git a/packages/cli/default/default.yml b/packages/cli/default/default.yml index 5d5f749c9..bf8bf0708 100644 --- a/packages/cli/default/default.yml +++ b/packages/cli/default/default.yml @@ -277,9 +277,22 @@ didDiscovery: - $require: '@veramo/did-manager#AliasDiscoveryProvider' - $require: '@veramo/data-store#ProfileDiscoveryProvider' -# LD-Handler +# LD-Context Loader +ldContextLoader: + - $require: '@veramo/credential-w3c#LdContextLoader' + $args: + - contextsPaths: + - $require: '@veramo/credential-w3c?t=object#LdDefaultContexts' + # others could be included here, e.g. + #- $require: '@transmute/credentials-context?t=object#contexts' + + +# LD-Module ldCredentialModule: - $require: '@veramo/credential-w3c#LdCredentialModule' + $args: + - ldContextLoader: + - $ref: /ldContextLoader # W3C credentialPlugin diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index d6ad53921..345c38f21 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -1,10 +1,9 @@ -import { - W3CCredential, - VerifiableCredential, - IIdentifier, - W3CPresentation, - VerifiablePresentation, IKey, -} from '@veramo/core' +import { IIdentifier, VerifiableCredential, W3CCredential, W3CPresentation, + VerifiablePresentation, IKey,} from '@veramo/core' +import { CredentialIssuer, IContext } from '../action-handler' +import { LdCredentialModule } from '../ld-credential-module' +import { LdContextLoader } from '../ld-context-loader' +import { LdDefaultContexts } from '../ld-default-contexts' const mockDidJwtVc = { createVerifiableCredentialJwt: jest.fn().mockReturnValue('mockVcJwt'), @@ -16,9 +15,6 @@ const mockDidJwtVc = { jest.mock('did-jwt-vc', () => mockDidJwtVc) -import { CredentialIssuer, IContext } from '../action-handler' -import { LdCredentialModule } from '../ld-credential-module' - const mockIdentifiers: IIdentifier[] = [ { did: 'did:example:111', @@ -65,7 +61,11 @@ const mockIdentifiers: IIdentifier[] = [ ] const w3c = new CredentialIssuer({ - ldCredentialModule: new LdCredentialModule() + ldCredentialModule: new LdCredentialModule({ + ldContextLoader: new LdContextLoader({ + contextsPaths: [ LdDefaultContexts ] + }) + }) }) let agent = { diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index 808a52cf7..fcffa4d32 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -16,5 +16,7 @@ export { ProofFormat, } from './action-handler' export { LdCredentialModule } from './ld-credential-module' +export { LdContextLoader } from './ld-context-loader' +export { LdDefaultContexts } from './ld-default-contexts' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/credential-w3c/src/ld-context-loader.ts b/packages/credential-w3c/src/ld-context-loader.ts new file mode 100644 index 000000000..c225ba062 --- /dev/null +++ b/packages/credential-w3c/src/ld-context-loader.ts @@ -0,0 +1,24 @@ +function concatMaps(mapList: Map[]) { + const map = new Map() + for (const mapItem of mapList) { + for (const item of mapItem) { + map.set(...item); + } + } + return map +} + +/** + * The LdContextLoader is initialized with a List of Map + * that it unifies into a single Map to provide to the documentLoader within + * the w3c credential module. + */ +export class LdContextLoader extends Map { + + constructor(options: { + contextsPaths: Map[] + }) { + + super(concatMaps(options.contextsPaths)) + } +} \ No newline at end of file diff --git a/packages/credential-w3c/src/ld-credential-module.ts b/packages/credential-w3c/src/ld-credential-module.ts index d5e7f3f62..e61dc5c0e 100644 --- a/packages/credential-w3c/src/ld-credential-module.ts +++ b/packages/credential-w3c/src/ld-credential-module.ts @@ -1,4 +1,3 @@ -import localContexts from './contexts' import { IAgentContext, IIdentifier, @@ -15,8 +14,8 @@ const { defaultDocumentLoader } = vc; const {extendContextLoader} = require('jsonld-signatures'); const {EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020} = require('EcdsaSecp256k1RecoverySignature2020') import {Ed25519Signature2018, Ed25519VerificationKey2018} from '@transmute/ed25519-signature-2018' -import { IContext, IVerifyVerifiablePresentationArgs } from './action-handler' import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' +import { LdContextLoader } from './ld-context-loader' const debug = Debug('veramo:w3c:ld-credential-module') @@ -30,6 +29,15 @@ export class LdCredentialModule { * - Key Manager and Verification Methods: Veramo currently implements no link between those. */ + private ldContextLoader: LdContextLoader + constructor(options: { + ldContextLoader: LdContextLoader + }) { + this.ldContextLoader = options.ldContextLoader + } + + + getDocumentLoader(context: IAgentContext) { return extendContextLoader(async (url: string) => { // console.log(`resolving context for: ${url}`) @@ -81,12 +89,12 @@ export class LdCredentialModule { }; } - if (localContexts.has(url)) { + if (this.ldContextLoader.has(url)) { // console.log(`Returning local context for: ${url}`) return { contextUrl: null, documentUrl: url, - document: localContexts.get(url) + document: this.ldContextLoader.get(url) }; } diff --git a/packages/credential-w3c/src/contexts.ts b/packages/credential-w3c/src/ld-default-contexts.ts similarity index 94% rename from packages/credential-w3c/src/contexts.ts rename to packages/credential-w3c/src/ld-default-contexts.ts index cf3c7eec1..1a040ab67 100644 --- a/packages/credential-w3c/src/contexts.ts +++ b/packages/credential-w3c/src/ld-default-contexts.ts @@ -1,4 +1,3 @@ - import * as fs from 'fs'; import * as path from 'path'; @@ -9,7 +8,7 @@ function _read(_path: string) { {encoding: 'utf8'})); } -const contexts = new Map([ +export const LdDefaultContexts = new Map([ ['https://veramo.io/contexts/socialmedia/v1', _read('socialmedia-v1.jsonld')], ['https://veramo.io/contexts/kyc/v1', _read('kyc-v1.jsonld')], ['https://veramo.io/contexts/profile/v1', _read('profile-v1.jsonld')], @@ -20,5 +19,3 @@ const contexts = new Map([ ['https://w3id.org/security/suites/ed25519-2018/v1', _read('ed25519-signature-2018-v1.jsonld')], ['https://w3id.org/security/suites/x25519-2019/v1', _read('X25519KeyAgreementKey2019.jsonld')] ]); - -export default contexts; From 5b2313c44234a5829e757dc68d9ab0fcef092038 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Tue, 3 Aug 2021 09:26:04 -0400 Subject: [PATCH 28/48] feat: included credential context from transmute library --- __tests__/localAgent.test.ts | 11 +++++++++-- __tests__/localMemoryStoreAgent.test.ts | 7 ++++++- __tests__/restAgent.test.ts | 6 +++++- __tests__/shared/verifiableDataLD.ts | 2 +- packages/cli/default/default.yml | 5 +++-- .../src/__tests__/action-handler.test.ts | 6 +++++- packages/credential-w3c/src/ld-credential-module.ts | 2 +- 7 files changed, 30 insertions(+), 9 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 408af5f65..7af299457 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -1,7 +1,7 @@ /** * This runs a suite of ./shared tests using an agent configured for local operations, * using a SQLite db for storage of credentials, presentations, messages as well as keys and DIDs. - * + * * This suite also runs a ganache local blockchain to run through some examples of DIDComm using did:ethr identifiers. */ @@ -59,6 +59,9 @@ import { createGanacheProvider } from './utils/ganache-provider' import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' +import { getDidKeyResolver } from '../packages/did-provider-key' +import { IDIDDiscovery, DIDDiscovery } from '../packages/did-discovery' +import {contexts as credential_contexts} from '@transmute/credentials-context' import fs from 'fs' jest.setTimeout(30000) @@ -78,6 +81,7 @@ import messageHandler from './shared/messageHandler' import didDiscovery from './shared/didDiscovery' import dbInitOptions from './shared/dbInitOptions' import didCommWithEthrDidFlow from './shared/didCommWithEthrDidFlow' +import contexts from '@veramo/credential-w3c/build/contexts' const infuraProjectId = '3586660d179141e3801c3895de1c2eba' const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c' @@ -211,7 +215,10 @@ const setup = async (options?: IAgentOptions): Promise => { new CredentialIssuer({ ldCredentialModule: new LdCredentialModule({ ldContextLoader: new LdContextLoader({ - contextsPaths: [ LdDefaultContexts ] + contextsPaths: [ + LdDefaultContexts, + credential_contexts as Map + ] }) }) } diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 65701ba0d..eda04afc3 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -43,6 +43,8 @@ import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' +import { getDidKeyResolver } from '../packages/did-provider-key' +import {contexts as credential_contexts} from '@transmute/credentials-context' import fs from 'fs' jest.setTimeout(30000) @@ -168,7 +170,10 @@ const setup = async (options?: IAgentOptions): Promise => { new CredentialIssuer({ ldCredentialModule: new LdCredentialModule({ ldContextLoader: new LdContextLoader({ - contextsPaths: [ LdDefaultContexts ] + contextsPaths: [ + LdDefaultContexts, + credential_contexts as Map + ] }) }) } diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 6f968adea..e542a1885 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -65,6 +65,7 @@ import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' import express from 'express' import { Server } from 'http' +import {contexts as credential_contexts} from '@transmute/credentials-context' import fs from 'fs' jest.setTimeout(30000) @@ -196,7 +197,10 @@ const setup = async (options?: IAgentOptions): Promise => { new CredentialIssuer({ ldCredentialModule: new LdCredentialModule({ ldContextLoader: new LdContextLoader({ - contextsPaths: [ LdDefaultContexts ] + contextsPaths: [ + LdDefaultContexts, + credential_contexts as Map + ] }) }) } diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index 898059ec8..931dd7062 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -243,7 +243,7 @@ export default (testContext: { it('should verify a verifiable credential in LD with did:key', async () => { const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) - console.log(JSON.stringify(verifiableCredential, null, 2)) + // console.log(JSON.stringify(verifiableCredential, null, 2)) // check that verification works const result = await agent.verifyVerifiableCredential({ credential: verifiableCredential diff --git a/packages/cli/default/default.yml b/packages/cli/default/default.yml index bf8bf0708..c8503d6a8 100644 --- a/packages/cli/default/default.yml +++ b/packages/cli/default/default.yml @@ -282,9 +282,10 @@ ldContextLoader: - $require: '@veramo/credential-w3c#LdContextLoader' $args: - contextsPaths: + # The LdDefaultContext is a "catch-all" for now. - $require: '@veramo/credential-w3c?t=object#LdDefaultContexts' - # others could be included here, e.g. - #- $require: '@transmute/credentials-context?t=object#contexts' + - $require: '@transmute/credentials-context?t=object#contexts' + # others should be included here # LD-Module diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index 345c38f21..5a2e713da 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -4,6 +4,7 @@ import { CredentialIssuer, IContext } from '../action-handler' import { LdCredentialModule } from '../ld-credential-module' import { LdContextLoader } from '../ld-context-loader' import { LdDefaultContexts } from '../ld-default-contexts' +import {contexts as credential_contexts} from '@transmute/credentials-context' const mockDidJwtVc = { createVerifiableCredentialJwt: jest.fn().mockReturnValue('mockVcJwt'), @@ -63,7 +64,10 @@ const mockIdentifiers: IIdentifier[] = [ const w3c = new CredentialIssuer({ ldCredentialModule: new LdCredentialModule({ ldContextLoader: new LdContextLoader({ - contextsPaths: [ LdDefaultContexts ] + contextsPaths: [ + LdDefaultContexts, + credential_contexts as Map + ] }) }) }) diff --git a/packages/credential-w3c/src/ld-credential-module.ts b/packages/credential-w3c/src/ld-credential-module.ts index e61dc5c0e..dc2335003 100644 --- a/packages/credential-w3c/src/ld-credential-module.ts +++ b/packages/credential-w3c/src/ld-credential-module.ts @@ -231,7 +231,7 @@ export class LdCredentialModule { // result can include raw Error console.log(`Error verifying LD Verifiable Credential`) - console.log(JSON.stringify(result, null, 2)); + // console.log(JSON.stringify(result, null, 2)); throw Error('Error verifying LD Verifiable Credential') } From 5bd4c04a48146e29c35dd3a2b296e33b79cf45a2 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Tue, 3 Aug 2021 22:43:52 -0400 Subject: [PATCH 29/48] feat: added outline for ld signature loader implementation --- .../credential-w3c/src/ld-suite-loader.ts | 28 +++++ packages/credential-w3c/src/ld-suites.ts | 106 ++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 packages/credential-w3c/src/ld-suite-loader.ts create mode 100644 packages/credential-w3c/src/ld-suites.ts diff --git a/packages/credential-w3c/src/ld-suite-loader.ts b/packages/credential-w3c/src/ld-suite-loader.ts new file mode 100644 index 000000000..8a3b6abc0 --- /dev/null +++ b/packages/credential-w3c/src/ld-suite-loader.ts @@ -0,0 +1,28 @@ +import { VeramoLdSignature } from './ld-suites' +import { TKeyType } from '@veramo/core' + +/** + * Initializes a list of Veramo-wrapped LD Signature suites and exposes those to the Agent Module + */ +export class LdSuiteLoader { + + constructor(options: { + veramoLdSignatures: VeramoLdSignature[] + }) { + this.signatureMap = options.veramoLdSignatures.reduce((map, obj) => { + map.set(obj.getSupportedVeramoKeyType(), obj); + return map + }, new Map()) + } + + private signatureMap: Map; + + getSignatureForKeyType(type: TKeyType) { + if (this.signatureMap.has(type)) { + return this.signatureMap.get(type) + } + + throw new Error('No Veramo LD Signature Suite for ' + type) + } + +} \ No newline at end of file diff --git a/packages/credential-w3c/src/ld-suites.ts b/packages/credential-w3c/src/ld-suites.ts new file mode 100644 index 000000000..b5b4bfbc6 --- /dev/null +++ b/packages/credential-w3c/src/ld-suites.ts @@ -0,0 +1,106 @@ +import { IIdentifier, IKey, TKeyType } from '@veramo/core' +import { Ed25519Signature2018, Ed25519VerificationKey2018 } from '@transmute/ed25519-signature-2018' +import { CredentialPayload } from 'did-jwt-vc' +const { EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020 } = require('EcdsaSecp256k1RecoverySignature2020') + + +export abstract class VeramoLdSignature { + + // LinkedDataSignature Suites according to + // https://github.com/digitalbazaar/jsonld-signatures/blob/main/lib/suites/LinkedDataSignature.js + // Add type definition as soon as https://github.com/digitalbazaar/jsonld-signatures + // supports those. + + abstract getSupportedVerificationType(): string; + + abstract getSupportedVeramoKeyType(): TKeyType; + + abstract getSuiteForSigning(key: IKey, identifier: IIdentifier): any; + + abstract getSuiteForVerification(): any; + + abstract preSigningCredModification(credential: Partial): void; +} + + +export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature { + + getSupportedVerificationType(): string { + return 'EcdsaSecp256k1RecoveryMethod2020' + } + + getSupportedVeramoKeyType(): TKeyType { + return 'Secp256k1' + } + + getSuiteForSigning(key: IKey, identifier: IIdentifier): any { + const controller = identifier.did + + return new EcdsaSecp256k1RecoverySignature2020({ + key: new EcdsaSecp256k1RecoveryMethod2020({ + publicKeyHex: key.publicKeyHex, + privateKeyHex: key.privateKeyHex, + type: this.getSupportedVerificationType(), + controller, + id: `${controller}#controller` // TODO: Only default controller verificationMethod supported + }), + }); + } + + getSuiteForVerification(): any { + return new EcdsaSecp256k1RecoverySignature2020() + } + + preSigningCredModification(credential: Partial): void { + if (!Array.isArray(credential['@context'])) { + credential['@context'] = [] + } + credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') + } + +} + +export class VeramoEd25519Signature2018 extends VeramoLdSignature { + + getSupportedVerificationType(): string { + return 'EcdsaSecp256k1RecoveryMethod2020' + } + + getSupportedVeramoKeyType(): TKeyType { + return 'Ed25519' + } + + getSuiteForSigning(key: IKey, identifier: IIdentifier): any { + if (!key.privateKeyHex) { + throw Error('No private Key for LD Signing available.') + } + + const controller = identifier.did + + // DID Key ID + let id = `${controller}#controller` + // TODO: Hacky id adjustment + if (controller.startsWith('did:key')) { + id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` + } + + + return new Ed25519Signature2018({ + key: new Ed25519VerificationKey2018({ + id, + controller, + publicKey: Buffer.from(key.publicKeyHex, 'hex'), + privateKey: Buffer.from(key.privateKeyHex, 'hex'), + }) + }); + } + + getSuiteForVerification(): any { + return new Ed25519Signature2018(); + } + + preSigningCredModification(credential: Partial): void { + // nothing to do here + } + +} \ No newline at end of file From 0bea9a8ace430a5f6c441486f0be487cbe582740 Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Wed, 4 Aug 2021 00:09:36 -0400 Subject: [PATCH 30/48] feat: moved all suite-related code into VeramoLDSignature implementations --- __tests__/localAgent.test.ts | 9 ++ __tests__/localMemoryStoreAgent.test.ts | 12 +- __tests__/restAgent.test.ts | 12 +- .../src/__tests__/action-handler.test.ts | 16 +++ packages/credential-w3c/src/index.ts | 6 + .../src/ld-credential-module.ts | 125 +++--------------- .../credential-w3c/src/ld-suite-loader.ts | 12 +- packages/credential-w3c/src/ld-suites.ts | 37 +++++- 8 files changed, 117 insertions(+), 112 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 7af299457..851fdeafe 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -27,6 +27,9 @@ import { LdCredentialModule, LdContextLoader, LdDefaultContexts, + LdSuiteLoader, + VeramoEd25519Signature2018, + VeramoEcdsaSecp256k1RecoverySignature2020 } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' @@ -219,6 +222,12 @@ const setup = async (options?: IAgentOptions): Promise => { LdDefaultContexts, credential_contexts as Map ] + }), + ldSuiteLoader: new LdSuiteLoader({ + veramoLdSignatures: [ + new VeramoEd25519Signature2018(), + new VeramoEcdsaSecp256k1RecoverySignature2020() + ] }) }) } diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index eda04afc3..62bf515e8 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -24,7 +24,11 @@ import { ICredentialIssuer, W3cMessageHandler, LdCredentialModule, - LdContextLoader, LdDefaultContexts, + LdContextLoader, + LdDefaultContexts, + LdSuiteLoader, + VeramoEd25519Signature2018, + VeramoEcdsaSecp256k1RecoverySignature2020, } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' @@ -174,6 +178,12 @@ const setup = async (options?: IAgentOptions): Promise => { LdDefaultContexts, credential_contexts as Map ] + }), + ldSuiteLoader: new LdSuiteLoader({ + veramoLdSignatures: [ + new VeramoEd25519Signature2018(), + new VeramoEcdsaSecp256k1RecoverySignature2020() + ] }) }) } diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index e542a1885..040bdddd0 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -28,7 +28,11 @@ import { ICredentialIssuer, W3cMessageHandler, LdCredentialModule, - LdContextLoader, LdDefaultContexts, + LdContextLoader, + LdDefaultContexts, + LdSuiteLoader, + VeramoEd25519Signature2018, + VeramoEcdsaSecp256k1RecoverySignature2020, } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' @@ -201,6 +205,12 @@ const setup = async (options?: IAgentOptions): Promise => { LdDefaultContexts, credential_contexts as Map ] + }), + ldSuiteLoader: new LdSuiteLoader({ + veramoLdSignatures: [ + new VeramoEd25519Signature2018(), + new VeramoEcdsaSecp256k1RecoverySignature2020() + ] }) }) } diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index 5a2e713da..465bd2582 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -6,6 +6,15 @@ import { LdContextLoader } from '../ld-context-loader' import { LdDefaultContexts } from '../ld-default-contexts' import {contexts as credential_contexts} from '@transmute/credentials-context' +import { IIdentifier, VerifiableCredential, W3CCredential, W3CPresentation } from '@veramo/core' +import { CredentialIssuer, IContext } from '../action-handler' +import { LdCredentialModule } from '../ld-credential-module' +import { LdContextLoader } from '../ld-context-loader' +import { LdDefaultContexts } from '../ld-default-contexts' +import { contexts as credential_contexts } from '@transmute/credentials-context' +import { LdSuiteLoader } from '../ld-suite-loader' +import { VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018 } from '../ld-suites' + const mockDidJwtVc = { createVerifiableCredentialJwt: jest.fn().mockReturnValue('mockVcJwt'), createVerifiablePresentationJwt: jest.fn().mockReturnValue('mockVcJwt'), @@ -14,6 +23,7 @@ const mockDidJwtVc = { normalizePresentation: jest.fn().mockReturnValue('mockPresentation'), } +// Mock must come before imports with transitive dependency. jest.mock('did-jwt-vc', () => mockDidJwtVc) const mockIdentifiers: IIdentifier[] = [ @@ -68,6 +78,12 @@ const w3c = new CredentialIssuer({ LdDefaultContexts, credential_contexts as Map ] + }), + ldSuiteLoader: new LdSuiteLoader({ + veramoLdSignatures: [ + new VeramoEd25519Signature2018(), + new VeramoEcdsaSecp256k1RecoverySignature2020() + ] }) }) }) diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index fcffa4d32..acbecb1e3 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -18,5 +18,11 @@ export { export { LdCredentialModule } from './ld-credential-module' export { LdContextLoader } from './ld-context-loader' export { LdDefaultContexts } from './ld-default-contexts' +export { LdSuiteLoader } from './ld-suite-loader' +export { + VeramoLdSignature, + VeramoEcdsaSecp256k1RecoverySignature2020, + VeramoEd25519Signature2018 +} from './ld-suites' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/credential-w3c/src/ld-credential-module.ts b/packages/credential-w3c/src/ld-credential-module.ts index dc2335003..e4e8581cd 100644 --- a/packages/credential-w3c/src/ld-credential-module.ts +++ b/packages/credential-w3c/src/ld-credential-module.ts @@ -11,11 +11,10 @@ import Debug from 'debug' const { purposes: { AssertionProofPurpose }} = require("jsonld-signatures"); const vc = require('vc-js'); const { defaultDocumentLoader } = vc; -const {extendContextLoader} = require('jsonld-signatures'); -const {EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020} = require('EcdsaSecp256k1RecoverySignature2020') -import {Ed25519Signature2018, Ed25519VerificationKey2018} from '@transmute/ed25519-signature-2018' +const { extendContextLoader } = require('jsonld-signatures'); import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' import { LdContextLoader } from './ld-context-loader' +import { LdSuiteLoader } from './ld-suite-loader' const debug = Debug('veramo:w3c:ld-credential-module') @@ -30,10 +29,13 @@ export class LdCredentialModule { */ private ldContextLoader: LdContextLoader + private ldSuiteLoader: LdSuiteLoader constructor(options: { ldContextLoader: LdContextLoader + ldSuiteLoader: LdSuiteLoader }) { this.ldContextLoader = options.ldContextLoader + this.ldSuiteLoader = options.ldSuiteLoader } @@ -45,40 +47,15 @@ export class LdCredentialModule { // did resolution if (url.toLowerCase().startsWith('did:')) { const didDoc = await context.agent.resolveDid({ didUrl: url }) - let returnDocument = didDoc.didDocument + const returnDocument = didDoc.didDocument if (!returnDocument) return - // specific resolution modifications - // did:ethr - if (url.toLowerCase().startsWith('did:ethr')) { - returnDocument.assertionMethod = [] - // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId - // blockchainAccountId to ethereumAddress - returnDocument.verificationMethod?.forEach(x => { - if (x.blockchainAccountId) { - x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf("@")) - } - - // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." - // @ts-ignore - returnDocument.assertionMethod.push(x.id) - }) - } - - // did:key - if (url.toLowerCase().startsWith('did:key')) { - // TODO: Fix the strange id naming in did:key. make sure its ${}#controller - // let newId = ''; - // returnDocument.publicKey?.forEach(x => { - // newId = `${x.id.substring(0, x.id.lastIndexOf("#"))}#controller` - // x.id = newId - // }) - // - // returnDocument.assertionMethod = [ newId ] - // returnDocument.verificationMethod = returnDocument.publicKey - // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) - } + // currently Veramo LD suites can modify the resolution response for DIDs from + // the document Loader. This allows to fix incompatibilities between DID Documents + // and LD suites to be fixed specifically within the Veramo LD Suites definition + this.ldSuiteLoader.getAllSignatureSuites().forEach( + x => x.preDidResolutionModification(url, returnDocument)) // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) @@ -105,76 +82,21 @@ export class LdCredentialModule { }); } - getLDSigningSuite(key: IKey, identifier: IIdentifier) { - let suite - const controller = identifier.did - - if (!key.privateKeyHex) { - throw Error('No private Key for LD Signing available.') - } - - switch(key.type) { - case 'Secp256k1': - suite = new EcdsaSecp256k1RecoverySignature2020({ - key: new EcdsaSecp256k1RecoveryMethod2020({ - publicKeyHex: key.publicKeyHex, - privateKeyHex: key.privateKeyHex, - type: 'EcdsaSecp256k1RecoveryMethod2020', // A little verbose? - controller, - id: `${controller}#controller` // TODO: Only default controller verificationMethod supported - }), - }); - break; - case 'Ed25519': - // DID Key ID - let id = `${controller}#controller` - // TODO: Hacky id adjustment - if (controller.startsWith('did:key')) { - id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` - } - - - suite = new Ed25519Signature2018({ - key: new Ed25519VerificationKey2018({ - id, - controller, - publicKey: Buffer.from(key.publicKeyHex, 'hex'), - privateKey: Buffer.from(key.privateKeyHex, 'hex'), - }) - }); - break; - default: - throw new Error(`Unknown key type ${key.type}.`); - } - - return suite - } - async issueLDVerifiableCredential( credential: Partial, key: IKey, identifier: IIdentifier, context: IAgentContext): Promise { - const suite = this.getLDSigningSuite(key, identifier) + const suite = this.ldSuiteLoader.getSignatureSuiteForKeyType(key.type) const documentLoader = this.getDocumentLoader(context) - // some suites are missing the right contexts - // TODO: How to generalize this? - switch (suite.type) { - case "EcdsaSecp256k1RecoverySignature2020": - // console.log(`Adding context to credential ${suite.type}`) - if (!Array.isArray(credential['@context'])) { - credential['@context'] = [] - } - credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') - break - default: - } + // some suites can modify the incoming credential (e.g. add required contexts)W + suite.preSigningCredModification(credential) return await vc.issue({ credential, - suite, + suite: suite.getSuiteForSigning(key, identifier), documentLoader, compactProof: false }); @@ -189,20 +111,15 @@ export class LdCredentialModule { context: IAgentContext, ): Promise { - const suite = this.getLDSigningSuite(key, identifier) + const suite = this.ldSuiteLoader.getSignatureSuiteForKeyType(key.type) const documentLoader = this.getDocumentLoader(context) - // TODO: Remove invalid field 'verifiers' from Presentation. Needs to be adapted for LD credentials - // Only remove empty array (vc.signPresentation will throw then) - const sanitizedPresentation = presentation as any - if (sanitizedPresentation.verifier.length == 0) { - delete sanitizedPresentation.verifier - } + suite.preSigningPresModification(presentation) return await vc.signPresentation({ - presentation: sanitizedPresentation, - suite, + presentation, + suite: suite.getSuiteForSigning(key, identifier), challenge, domain, documentLoader, @@ -218,7 +135,7 @@ export class LdCredentialModule { const result = await vc.verifyCredential({ credential, - suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2018()], + suite: this.ldSuiteLoader.getAllSignatureSuites().map(x => x.getSuiteForVerification()), documentLoader: this.getDocumentLoader(context), purpose: new AssertionProofPurpose(), compactProof: false @@ -244,7 +161,7 @@ export class LdCredentialModule { const result = await vc.verify({ presentation, - suite: [new EcdsaSecp256k1RecoverySignature2020(), new Ed25519Signature2018({})], + suite: this.ldSuiteLoader.getAllSignatureSuites().map(x => x.getSuiteForVerification()), documentLoader: this.getDocumentLoader(context), challenge, domain, diff --git a/packages/credential-w3c/src/ld-suite-loader.ts b/packages/credential-w3c/src/ld-suite-loader.ts index 8a3b6abc0..3f82ce9fd 100644 --- a/packages/credential-w3c/src/ld-suite-loader.ts +++ b/packages/credential-w3c/src/ld-suite-loader.ts @@ -14,15 +14,17 @@ export class LdSuiteLoader { return map }, new Map()) } - private signatureMap: Map; - getSignatureForKeyType(type: TKeyType) { - if (this.signatureMap.has(type)) { - return this.signatureMap.get(type) - } + getSignatureSuiteForKeyType(type: TKeyType) { + const suite = this.signatureMap.get(type) + if (suite) return suite throw new Error('No Veramo LD Signature Suite for ' + type) } + getAllSignatureSuites() { + return Array.from(this.signatureMap.values()) + } + } \ No newline at end of file diff --git a/packages/credential-w3c/src/ld-suites.ts b/packages/credential-w3c/src/ld-suites.ts index b5b4bfbc6..4af1a2c44 100644 --- a/packages/credential-w3c/src/ld-suites.ts +++ b/packages/credential-w3c/src/ld-suites.ts @@ -1,6 +1,7 @@ import { IIdentifier, IKey, TKeyType } from '@veramo/core' import { Ed25519Signature2018, Ed25519VerificationKey2018 } from '@transmute/ed25519-signature-2018' -import { CredentialPayload } from 'did-jwt-vc' +import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' +import { DIDDocument } from 'did-resolver/src/resolver' const { EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020 } = require('EcdsaSecp256k1RecoverySignature2020') @@ -19,7 +20,18 @@ export abstract class VeramoLdSignature { abstract getSuiteForVerification(): any; + abstract preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void; + abstract preSigningCredModification(credential: Partial): void; + + preSigningPresModification(presentation: Partial): void { + // TODO: Remove invalid field 'verifiers' from Presentation. Needs to be adapted for LD credentials + // Only remove empty array (vc.signPresentation will throw then) + const sanitizedPresentation = presentation as any + if (sanitizedPresentation.verifier.length == 0) { + delete sanitizedPresentation.verifier + } + } } @@ -58,6 +70,25 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') } + preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { + // specific resolution modifications + // did:ethr + if (didUrl.toLowerCase().startsWith('did:ethr')) { + didDoc.assertionMethod = [] + // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId + // blockchainAccountId to ethereumAddress + didDoc.verificationMethod?.forEach(x => { + if (x.blockchainAccountId) { + x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf("@")) + } + + // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." + // @ts-ignore + didDoc.assertionMethod.push(x.id) + }) + } + } + } export class VeramoEd25519Signature2018 extends VeramoLdSignature { @@ -103,4 +134,8 @@ export class VeramoEd25519Signature2018 extends VeramoLdSignature { // nothing to do here } + preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { + // nothing to do here + } + } \ No newline at end of file From 9d6f3978d55dd2292721691f6fc7f6533086dd8b Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Wed, 4 Aug 2021 00:16:47 -0400 Subject: [PATCH 31/48] feat: updated default config to new initialization --- packages/cli/default/default.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/cli/default/default.yml b/packages/cli/default/default.yml index c8503d6a8..95e826298 100644 --- a/packages/cli/default/default.yml +++ b/packages/cli/default/default.yml @@ -277,6 +277,14 @@ didDiscovery: - $require: '@veramo/did-manager#AliasDiscoveryProvider' - $require: '@veramo/data-store#ProfileDiscoveryProvider' +# LD-Suite Loader +ldSuiteLoader: + - $require: '@veramo/credential-w3c#LdSuiteLoader' + $args: + - veramoLdSignatures: + - $require: '@veramo/credential-w3c#VeramoEd25519Signature2018' + - $require: '@veramo/credential-w3c#VeramoEcdsaSecp256k1RecoverySignature2020' + # LD-Context Loader ldContextLoader: - $require: '@veramo/credential-w3c#LdContextLoader' @@ -294,7 +302,8 @@ ldCredentialModule: $args: - ldContextLoader: - $ref: /ldContextLoader - + - ldSuiteLoader: + - $ref: /ldSuiteLoader # W3C credentialPlugin credentialPlugin: From bda39f028b5d435258219cbb42766ca06bba91fc Mon Sep 17 00:00:00 2001 From: Martin Riedel Date: Wed, 4 Aug 2021 00:26:03 -0400 Subject: [PATCH 32/48] fix: removed broken import --- __tests__/localAgent.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 851fdeafe..d01cd05cc 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -84,7 +84,6 @@ import messageHandler from './shared/messageHandler' import didDiscovery from './shared/didDiscovery' import dbInitOptions from './shared/dbInitOptions' import didCommWithEthrDidFlow from './shared/didCommWithEthrDidFlow' -import contexts from '@veramo/credential-w3c/build/contexts' const infuraProjectId = '3586660d179141e3801c3895de1c2eba' const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c' From 501dcfff9c508a14a72ab136c061e35b22c4b104 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Wed, 8 Sep 2021 19:15:52 +0200 Subject: [PATCH 33/48] feat: add @veramo/utils package --- packages/did-comm/package.json | 1 + packages/did-comm/src/didcomm.ts | 20 +- packages/did-comm/src/types/utility-types.ts | 32 --- packages/did-comm/src/utils.ts | 210 +------------------ packages/did-comm/tsconfig.json | 3 +- packages/did-provider-key/tsconfig.json | 3 +- packages/did-resolver/src/index.ts | 2 +- packages/tsconfig.settings.json | 1 + packages/utils/LICENSE | 201 ++++++++++++++++++ packages/utils/README.md | 3 + packages/utils/package.json | 38 ++++ packages/utils/src/index.ts | 9 + packages/utils/src/types/utility-types.ts | 32 +++ packages/utils/src/utils.ts | 205 ++++++++++++++++++ packages/utils/tsconfig.json | 12 ++ 15 files changed, 528 insertions(+), 244 deletions(-) create mode 100644 packages/utils/LICENSE create mode 100644 packages/utils/README.md create mode 100644 packages/utils/package.json create mode 100644 packages/utils/src/index.ts create mode 100644 packages/utils/src/types/utility-types.ts create mode 100644 packages/utils/src/utils.ts create mode 100644 packages/utils/tsconfig.json diff --git a/packages/did-comm/package.json b/packages/did-comm/package.json index 08f3bf596..3e826ac24 100644 --- a/packages/did-comm/package.json +++ b/packages/did-comm/package.json @@ -18,6 +18,7 @@ "@stablelib/ed25519": "^1.0.2", "@veramo/core": "^3.1.0", "@veramo/message-handler": "^3.1.0", + "@veramo/utils": "^2.1.0", "cross-fetch": "^3.1.4", "debug": "^4.1.1", "did-jwt": "5.12.0", diff --git a/packages/did-comm/src/didcomm.ts b/packages/did-comm/src/didcomm.ts index 2332ca0fa..9326dda44 100644 --- a/packages/did-comm/src/didcomm.ts +++ b/packages/did-comm/src/didcomm.ts @@ -26,20 +26,24 @@ import { DIDDocument, parse as parseDidUrl, VerificationMethod } from 'did-resol import { schema } from '.' import { v4 as uuidv4 } from 'uuid' import * as u8a from 'uint8arrays' -import { convertPublicKeyToX25519 } from '@stablelib/ed25519' import { - isDefined, - mapIdentifierKeysToDoc, - encodeJoseBlob, createEcdhWrapper, - resolveDidOrThrow, - dereferenceDidKeys, - decodeJoseBlob, extractSenderEncryptionKey, extractManagedRecipients, mapRecipientsToLocalKeys, } from './utils' +import { + decodeJoseBlob, + dereferenceDidKeys, + encodeJoseBlob, + isDefined, + mapIdentifierKeysToDoc, + resolveDidOrThrow, + _ExtendedIKey, + _NormalizedVerificationMethod, +} from '@veramo/utils' + import Debug from 'debug' import { IDIDComm } from './types/IDIDComm' import { DIDCommHttpTransport, IDIDCommTransport } from './transports/transports' @@ -54,10 +58,8 @@ import { _DIDCommEncryptedMessage, _DIDCommPlainMessage, _DIDCommSignedMessage, - _ExtendedIKey, _FlattenedJWS, _GenericJWS, - _NormalizedVerificationMethod, } from './types/utility-types' const debug = Debug('veramo:did-comm:action-handler') diff --git a/packages/did-comm/src/types/utility-types.ts b/packages/did-comm/src/types/utility-types.ts index 2f9003b7a..04fbc20a1 100644 --- a/packages/did-comm/src/types/utility-types.ts +++ b/packages/did-comm/src/types/utility-types.ts @@ -1,6 +1,4 @@ -import { IKey, KeyMetadata } from '@veramo/core' import { JWE } from 'did-jwt' -import { VerificationMethod } from 'did-resolver' import { DIDCommMessageMediaType, IDIDCommMessage } from './message-types' /** @@ -40,33 +38,3 @@ export type _GenericJWS = { * @internal */ export type _DIDCommSignedMessage = _FlattenedJWS | _GenericJWS - -/** - * Broader definition of Verification method that includes the legacy publicKeyBase64 - * @internal - */ -export type _ExtendedVerificationMethod = VerificationMethod & { publicKeyBase64?: string } - -/** - * Represents an {@link @veramo/core#IKey} that has been augmented with its corresponding - * entry from a DID document. - * `key.meta.verificationMethod` will contain the {@link did-resolver#VerificationMethod} - * object from the {@link did-resolver#DIDDocument} - * @internal - */ -export interface _ExtendedIKey extends IKey { - meta: KeyMetadata & { - verificationMethod: _NormalizedVerificationMethod - } -} - -/** - * Represents a {@link did-resolver#VerificationMethod} whose public key material - * has been converted to `publicKeyHex` - * - * @internal - */ -export type _NormalizedVerificationMethod = Omit< - VerificationMethod, - 'publicKeyBase58' | 'publicKeyBase64' | 'publicKeyJwk' -> diff --git a/packages/did-comm/src/utils.ts b/packages/did-comm/src/utils.ts index e8be49eee..73be16297 100644 --- a/packages/did-comm/src/utils.ts +++ b/packages/did-comm/src/utils.ts @@ -1,17 +1,6 @@ -import { convertPublicKeyToX25519, convertSecretKeyToX25519 } from '@stablelib/ed25519' -import { computePublicKey } from '@ethersproject/signing-key' -import { - DIDDocumentSection, - IAgentContext, - IDIDManager, - IIdentifier, - IKey, - IKeyManager, - IResolver, - TKeyType, -} from '@veramo/core' +import { IAgentContext, IDIDManager, IIdentifier, IKeyManager, IResolver, TKeyType } from '@veramo/core' import { ECDH, JWE } from 'did-jwt' -import { VerificationMethod, parse as parseDidUrl, DIDDocument } from 'did-resolver' +import { parse as parseDidUrl } from 'did-resolver' import * as u8a from 'uint8arrays' import Debug from 'debug' @@ -19,41 +8,14 @@ import { _ExtendedIKey, _ExtendedVerificationMethod, _NormalizedVerificationMethod, -} from './types/utility-types' -const debug = Debug('veramo:did-comm:action-handler') - -export function bytesToBase64url(b: Uint8Array): string { - return u8a.toString(b, 'base64url') -} - -export function base64ToBytes(s: string): Uint8Array { - const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') - return u8a.fromString(inputBase64Url, 'base64url') -} - -export function bytesToBase64(b: Uint8Array): string { - return u8a.toString(b, 'base64pad') -} - -export function encodeBase64url(s: string): string { - return bytesToBase64url(u8a.fromString(s)) -} - -export function decodeBase64url(s: string): string { - return u8a.toString(base64ToBytes(s)) -} - -export function encodeJoseBlob(payload: {}) { - return u8a.toString(u8a.fromString(JSON.stringify(payload), 'utf-8'), 'base64url') -} - -export function decodeJoseBlob(blob: string) { - return JSON.parse(u8a.toString(u8a.fromString(blob, 'base64url'), 'utf-8')) -} + decodeJoseBlob, + isDefined, + mapIdentifierKeysToDoc, + resolveDidOrThrow, + extractPublicKeyHex, +} from '@veramo/utils' -export function isDefined(arg: T): arg is Exclude { - return arg && typeof arg !== 'undefined' -} +const debug = Debug('veramo:did-comm:action-handler') export function createEcdhWrapper(secretKeyRef: string, context: IAgentContext): ECDH { return async (theirPublicKey: Uint8Array): Promise => { @@ -82,7 +44,7 @@ export async function extractSenderEncryptionKey( if (!['Ed25519VerificationKey2018', 'X25519KeyAgreementKey2019'].includes(sKey.type)) { throw new Error(`not_supported: sender key of type ${sKey.type} is not supported`) } - let publicKeyHex = convertToPublicKeyHex(sKey, true) + let publicKeyHex = extractPublicKeyHex(sKey, true) senderKey = u8a.fromString(publicKeyHex, 'base16') } return senderKey @@ -120,39 +82,6 @@ export async function extractManagedRecipients( return managedRecipients } -export function convertIdentifierEncryptionKeys(identifier: IIdentifier): IKey[] { - return identifier.keys - .map((key) => { - if (key.type === 'Ed25519') { - const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') - key.publicKeyHex = u8a.toString(convertPublicKeyToX25519(publicBytes), 'base16') - if (key.privateKeyHex) { - const privateBytes = u8a.fromString(key.privateKeyHex) - key.privateKeyHex = u8a.toString(convertSecretKeyToX25519(privateBytes), 'base16') - } - key.type = 'X25519' - } else if (key.type !== 'X25519') { - debug(`key of type ${key.type} is not supported for [de]encryption`) - return null - } - return key - }) - .filter(isDefined) -} - -export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[] { - return identifier.keys - .map((key) => { - if (key.type === 'Secp256k1') { - const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') - const compressedKey = computePublicKey(publicBytes, true).substring(2) - key.publicKeyHex = compressedKey - } - return key - }) - .filter(isDefined) -} - export async function mapRecipientsToLocalKeys( managedKeys: { recipient: any; kid: string; identifier: IIdentifier }[], context: IAgentContext, @@ -172,122 +101,3 @@ export async function mapRecipientsToLocalKeys( const localKeys = potentialKeys.filter(isDefined) return localKeys } - -export async function mapIdentifierKeysToDoc( - identifier: IIdentifier, - section: DIDDocumentSection = 'keyAgreement', - context: IAgentContext, -): Promise<_ExtendedIKey[]> { - const didDocument = await resolveDidOrThrow(identifier.did, context) - - // dereference all key agreement keys from DID document and normalize - const keyAgreementKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( - didDocument, - section, - context, - ) - - let localKeys = identifier.keys.filter(isDefined) - if (section === 'keyAgreement') { - localKeys = convertIdentifierEncryptionKeys(identifier) - } else { - localKeys = compressIdentifierSecp256k1Keys(identifier) - } - // finally map the didDocument keys to the identifier keys by comparing `publicKeyHex` - const extendedKeys: _ExtendedIKey[] = keyAgreementKeys - .map((verificationMethod) => { - const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex) - if (localKey) { - const { meta, ...localProps } = localKey - return { ...localProps, meta: { ...meta, verificationMethod } } - } else { - return null - } - }) - .filter(isDefined) - - return extendedKeys -} - -export async function resolveDidOrThrow( - didUrl: string, - context: IAgentContext, -): Promise { - // TODO: add caching - const docResult = await context.agent.resolveDid({ didUrl: didUrl }) - const err = docResult?.didResolutionMetadata?.error - const msg = docResult?.didResolutionMetadata?.message - const didDocument = docResult.didDocument - if (!isDefined(didDocument) || err) { - throw new Error(`not_found: could not resolve DID document for '${didUrl}': ${err} ${msg}`) - } - return didDocument -} - -/** - * Dereferences key agreement keys from DID document and normalizes them for easy comparison. - * - * When dereferencing keyAgreement keys, only Ed25519 and X25519 curves are supported. - * Other key types are omitted from the result and Ed25519 keys are converted to X25519 - * - * @returns Promise - */ -export async function dereferenceDidKeys( - didDocument: DIDDocument, - section: DIDDocumentSection = 'keyAgreement', - context: IAgentContext, -): Promise<_NormalizedVerificationMethod[]> { - const convert = section === 'keyAgreement' - if (section === 'service') { - return [] - } - return ( - await Promise.all( - (didDocument[section] || []).map(async (key: string | VerificationMethod) => { - if (typeof key === 'string') { - try { - return (await context.agent.getDIDComponentById({ - didDocument, - didUrl: key, - section, - })) as _ExtendedVerificationMethod - } catch (e) { - return null - } - } else { - return key as _ExtendedVerificationMethod - } - }), - ) - ) - .filter(isDefined) - .map((key) => { - const hexKey = convertToPublicKeyHex(key, convert) - const { publicKeyHex, publicKeyBase58, publicKeyBase64, publicKeyJwk, ...keyProps } = key - const newKey = { ...keyProps, publicKeyHex: hexKey } - if (convert && 'Ed25519VerificationKey2018' === newKey.type) { - newKey.type = 'X25519KeyAgreementKey2019' - } - return newKey - }) - .filter((key) => key.publicKeyHex.length > 0) -} - -function convertToPublicKeyHex(pk: _ExtendedVerificationMethod, convert: boolean): string { - let keyBytes: Uint8Array - if (pk.publicKeyHex) { - keyBytes = u8a.fromString(pk.publicKeyHex, 'base16') - } else if (pk.publicKeyBase58) { - keyBytes = u8a.fromString(pk.publicKeyBase58, 'base58btc') - } else if (pk.publicKeyBase64) { - keyBytes = u8a.fromString(pk.publicKeyBase64, 'base64pad') - } else return '' - if (convert) { - if (['Ed25519', 'Ed25519VerificationKey2018'].includes(pk.type)) { - keyBytes = convertPublicKeyToX25519(keyBytes) - } else if (!['X25519', 'X25519KeyAgreementKey2019'].includes(pk.type)) { - return '' - } - } - return u8a.toString(keyBytes, 'base16') -} diff --git a/packages/did-comm/tsconfig.json b/packages/did-comm/tsconfig.json index 52381682d..9f4ec81c4 100644 --- a/packages/did-comm/tsconfig.json +++ b/packages/did-comm/tsconfig.json @@ -8,6 +8,7 @@ "references": [ { "path": "../core" }, { "path": "../did-resolver" }, - { "path": "../message-handler" } + { "path": "../message-handler" }, + { "path": "../utils" } ] } diff --git a/packages/did-provider-key/tsconfig.json b/packages/did-provider-key/tsconfig.json index 3b2ed1492..21035981a 100644 --- a/packages/did-provider-key/tsconfig.json +++ b/packages/did-provider-key/tsconfig.json @@ -3,7 +3,8 @@ "compilerOptions": { "rootDir": "src", "outDir": "build", - "declarationDir": "build" + "declarationDir": "build", + "skipLibCheck": true }, "references": [{ "path": "../core" }, { "path": "../did-manager" }] } diff --git a/packages/did-resolver/src/index.ts b/packages/did-resolver/src/index.ts index 876553960..2d8b8c352 100644 --- a/packages/did-resolver/src/index.ts +++ b/packages/did-resolver/src/index.ts @@ -5,4 +5,4 @@ * @packageDocumentation */ export { DIDResolverPlugin } from './resolver' -export { UniversalResolver } from './universal-resolver' +export { UniversalResolver, getUniversalResolver, getUniversalResolverFor } from './universal-resolver' diff --git a/packages/tsconfig.settings.json b/packages/tsconfig.settings.json index f91a2f090..0d3a45d48 100644 --- a/packages/tsconfig.settings.json +++ b/packages/tsconfig.settings.json @@ -12,6 +12,7 @@ "declaration": true, "composite": true, "emitDecoratorMetadata": true, + "useUnknownInCatchVariables": false, "experimentalDecorators": true }, "exclude": ["**/__tests__/**/*", "**/build/**/*"] diff --git a/packages/utils/LICENSE b/packages/utils/LICENSE new file mode 100644 index 000000000..ab3a54e4e --- /dev/null +++ b/packages/utils/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Consensys AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/utils/README.md b/packages/utils/README.md new file mode 100644 index 000000000..ef0eb69ba --- /dev/null +++ b/packages/utils/README.md @@ -0,0 +1,3 @@ +# Veramo utils + +A package with helper methods. diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 000000000..073cb6431 --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,38 @@ +{ + "name": "@veramo/utils", + "description": "Helper methods for Veramo plugins", + "version": "2.1.2", + "main": "build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "tsc" + }, + "dependencies": { + "@stablelib/ed25519": "^1.0.2", + "@veramo/core": "^2.1.0", + "cross-fetch": "^3.1.4", + "debug": "^4.1.1", + "did-jwt": "5.7.0", + "did-resolver": "^3.1.0", + "uint8arrays": "^3.0.0", + "uuid": "^8.3.0" + }, + "devDependencies": { + "@types/debug": "4.1.7", + "@types/uuid": "8.3.1", + "typescript": "4.4.2" + }, + "files": [ + "build/**/*", + "src/**/*", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "repository": "git@github.com:uport-project/veramo.git", + "author": "Mircea Nistor ", + "license": "Apache-2.0", + "keywords": [] +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 000000000..74906acfa --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1,9 @@ +/** + * Contains helper methods for Veramo plugins. + * + * @packageDocumentation + */ + + +export * from './types/utility-types' +export * from './utils' diff --git a/packages/utils/src/types/utility-types.ts b/packages/utils/src/types/utility-types.ts new file mode 100644 index 000000000..60d890eee --- /dev/null +++ b/packages/utils/src/types/utility-types.ts @@ -0,0 +1,32 @@ +import { IKey, KeyMetadata } from '@veramo/core' +import { VerificationMethod } from 'did-resolver' + +/** + * Broader definition of Verification method that includes the legacy publicKeyBase64 + * @internal + */ +export type _ExtendedVerificationMethod = VerificationMethod & { publicKeyBase64?: string } + +/** + * Represents an {@link @veramo/core#IKey} that has been augmented with its corresponding + * entry from a DID document. + * `key.meta.verificationMethod` will contain the {@link did-resolver#VerificationMethod} + * object from the {@link did-resolver#DIDDocument} + * @internal + */ +export interface _ExtendedIKey extends IKey { + meta: KeyMetadata & { + verificationMethod: _NormalizedVerificationMethod + } +} + +/** + * Represents a {@link did-resolver#VerificationMethod} whose public key material + * has been converted to `publicKeyHex` + * + * @internal + */ +export type _NormalizedVerificationMethod = Omit< + VerificationMethod, + 'publicKeyBase58' | 'publicKeyBase64' | 'publicKeyJwk' +> diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts new file mode 100644 index 000000000..330b58c52 --- /dev/null +++ b/packages/utils/src/utils.ts @@ -0,0 +1,205 @@ +import { convertPublicKeyToX25519, convertSecretKeyToX25519 } from '@stablelib/ed25519' +import { computePublicKey } from '@ethersproject/signing-key' +import { DIDDocumentSection, IAgentContext, IIdentifier, IKey, IResolver } from '@veramo/core' +import { VerificationMethod, DIDDocument } from 'did-resolver' +import * as u8a from 'uint8arrays' + +import Debug from 'debug' +import { + _ExtendedIKey, + _ExtendedVerificationMethod, + _NormalizedVerificationMethod, +} from './types/utility-types' +const debug = Debug('veramo:utils') + +export function bytesToBase64url(b: Uint8Array): string { + return u8a.toString(b, 'base64url') +} + +export function base64ToBytes(s: string): Uint8Array { + const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') + return u8a.fromString(inputBase64Url, 'base64url') +} + +export function bytesToBase64(b: Uint8Array): string { + return u8a.toString(b, 'base64pad') +} + +export function encodeBase64url(s: string): string { + return bytesToBase64url(u8a.fromString(s)) +} + +export function decodeBase64url(s: string): string { + return u8a.toString(base64ToBytes(s)) +} + +export function encodeJoseBlob(payload: {}) { + return u8a.toString(u8a.fromString(JSON.stringify(payload), 'utf-8'), 'base64url') +} + +export function decodeJoseBlob(blob: string) { + return JSON.parse(u8a.toString(u8a.fromString(blob, 'base64url'), 'utf-8')) +} + +export function isDefined(arg: T): arg is Exclude { + return arg !== null && typeof arg !== 'undefined' +} + +export function convertIdentifierEncryptionKeys(identifier: IIdentifier): IKey[] { + return identifier.keys + .map((key) => { + if (key.type === 'Ed25519') { + const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') + key.publicKeyHex = u8a.toString(convertPublicKeyToX25519(publicBytes), 'base16') + if (key.privateKeyHex) { + const privateBytes = u8a.fromString(key.privateKeyHex) + key.privateKeyHex = u8a.toString(convertSecretKeyToX25519(privateBytes), 'base16') + } + key.type = 'X25519' + } else if (key.type !== 'X25519') { + debug(`key of type ${key.type} is not supported for [de]encryption`) + return null + } + return key + }) + .filter(isDefined) +} + +export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[] { + return identifier.keys + .map((key) => { + if (key.type === 'Secp256k1') { + const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') + const compressedKey = computePublicKey(publicBytes, true).substring(2) + key.publicKeyHex = compressedKey + } + return key + }) + .filter(isDefined) +} + +export async function mapIdentifierKeysToDoc( + identifier: IIdentifier, + section: DIDDocumentSection = 'keyAgreement', + context: IAgentContext, +): Promise<_ExtendedIKey[]> { + const didDocument = await resolveDidOrThrow(identifier.did, context) + + // dereference all key agreement keys from DID document and normalize + const keyAgreementKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( + didDocument, + section, + context, + ) + + let localKeys = identifier.keys.filter(isDefined) + if (section === 'keyAgreement') { + localKeys = convertIdentifierEncryptionKeys(identifier) + } else { + localKeys = compressIdentifierSecp256k1Keys(identifier) + } + // finally map the didDocument keys to the identifier keys by comparing `publicKeyHex` + const extendedKeys: _ExtendedIKey[] = keyAgreementKeys + .map((verificationMethod) => { + const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex) + if (localKey) { + const { meta, ...localProps } = localKey + return { ...localProps, meta: { ...meta, verificationMethod } } + } else { + return null + } + }) + .filter(isDefined) + + return extendedKeys +} + +export async function resolveDidOrThrow( + didUrl: string, + context: IAgentContext, +): Promise { + // TODO: add caching + const docResult = await context.agent.resolveDid({ didUrl: didUrl }) + const err = docResult?.didResolutionMetadata?.error + const msg = docResult?.didResolutionMetadata?.message + const didDocument = docResult.didDocument + if (!isDefined(didDocument) || err) { + throw new Error(`not_found: could not resolve DID document for '${didUrl}': ${err} ${msg}`) + } + return didDocument +} + +/** + * Dereferences keys from DID document and normalizes them for easy comparison. + * + * When dereferencing keyAgreement keys, only Ed25519 and X25519 curves are supported. + * Other key types are omitted from the result and Ed25519 keys are converted to X25519 + * + * @returns Promise + */ +export async function dereferenceDidKeys( + didDocument: DIDDocument, + section: DIDDocumentSection = 'keyAgreement', + context: IAgentContext, +): Promise<_NormalizedVerificationMethod[]> { + const convert = section === 'keyAgreement' + if (section === 'service') { + return [] + } + return ( + await Promise.all( + (didDocument[section] || []).map(async (key: string | VerificationMethod) => { + if (typeof key === 'string') { + try { + return (await context.agent.getDIDComponentById({ + didDocument, + didUrl: key, + section, + })) as _ExtendedVerificationMethod + } catch (e) { + return null + } + } else { + return key as _ExtendedVerificationMethod + } + }), + ) + ) + .filter(isDefined) + .map((key) => { + const hexKey = extractPublicKeyHex(key, convert) + const { publicKeyHex, publicKeyBase58, publicKeyBase64, publicKeyJwk, ...keyProps } = key + const newKey = { ...keyProps, publicKeyHex: hexKey } + if (convert && 'Ed25519VerificationKey2018' === newKey.type) { + newKey.type = 'X25519KeyAgreementKey2019' + } + return newKey + }) + .filter((key) => key.publicKeyHex.length > 0) +} + +/** + * Converts the publicKey of a VerificationMethod to hex encoding (publicKeyHex) + * + * @param pk the VerificationMethod to be converted + * @param convert when this flag is set to true, Ed25519 keys are converted to their X25519 pairs + * @returns + */ +export function extractPublicKeyHex(pk: _ExtendedVerificationMethod, convert: boolean = false): string { + let keyBytes: Uint8Array + if (pk.publicKeyHex) { + keyBytes = u8a.fromString(pk.publicKeyHex, 'base16') + } else if (pk.publicKeyBase58) { + keyBytes = u8a.fromString(pk.publicKeyBase58, 'base58btc') + } else if (pk.publicKeyBase64) { + keyBytes = u8a.fromString(pk.publicKeyBase64, 'base64pad') + } else return '' + if (convert) { + if (['Ed25519', 'Ed25519VerificationKey2018'].includes(pk.type)) { + keyBytes = convertPublicKeyToX25519(keyBytes) + } else if (!['X25519', 'X25519KeyAgreementKey2019'].includes(pk.type)) { + return '' + } + } + return u8a.toString(keyBytes, 'base16') +} diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json new file mode 100644 index 000000000..9399ace08 --- /dev/null +++ b/packages/utils/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../tsconfig.settings.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build", + "declarationDir": "build" + }, + "references": [ + { "path": "../core" }, + { "path": "../did-resolver" }, + ] +} From 21bb1fd0e346368e05d15664d7f4af16254b2266 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Wed, 8 Sep 2021 19:16:58 +0200 Subject: [PATCH 34/48] fix: remove support for Ed25519 LD crypto suites and fix EcdsaSecp256k1RecoveryMethod2020 --- __tests__/localAgent.test.ts | 39 ++--- __tests__/localMemoryStoreAgent.test.ts | 41 ++--- __tests__/restAgent.test.ts | 38 ++--- __tests__/shared/verifiableDataLD.ts | 143 +++++------------- __tests__/utils/fake-did.ts | 2 +- packages/cli/default/default.yml | 4 +- .../src/__tests__/action-handler.test.ts | 59 +++----- .../src/__tests__/message-handler.test.ts | 6 +- packages/credential-w3c/src/action-handler.ts | 87 ++++++----- packages/credential-w3c/src/index.ts | 1 - .../src/ld-credential-module.ts | 91 +++++------ packages/credential-w3c/src/ld-suites.ts | 118 ++++++--------- .../credential-w3c/src/message-handler.ts | 18 +-- packages/credential-w3c/tsconfig.json | 3 +- .../credential-w3c/types/jsonld/index.d.ts | 3 + .../data-store/src/entities/credential.ts | 2 +- 16 files changed, 263 insertions(+), 392 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index d01cd05cc..fe0dce530 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -28,8 +28,7 @@ import { LdContextLoader, LdDefaultContexts, LdSuiteLoader, - VeramoEd25519Signature2018, - VeramoEcdsaSecp256k1RecoverySignature2020 + VeramoEcdsaSecp256k1RecoverySignature2020, } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' @@ -55,16 +54,14 @@ import { PrivateKeyStore, migrations, } from '../packages/data-store/src' -import { createConnection, Connection } from 'typeorm' - import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' + +import { createConnection, Connection } from 'typeorm' import { createGanacheProvider } from './utils/ganache-provider' import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' -import { getDidKeyResolver } from '../packages/did-provider-key' -import { IDIDDiscovery, DIDDiscovery } from '../packages/did-discovery' -import {contexts as credential_contexts} from '@transmute/credentials-context' +import { contexts as credential_contexts } from '@transmute/credentials-context' import fs from 'fs' jest.setTimeout(30000) @@ -179,9 +176,6 @@ const setup = async (options?: IAgentOptions): Promise => { 'did:key': new KeyDIDProvider({ defaultKms: 'local', }), - 'did:key': new KeyDIDProvider({ - defaultKms: 'local', - }), 'did:fake': new FakeDidProvider(), }, }), @@ -216,21 +210,16 @@ const setup = async (options?: IAgentOptions): Promise => { new DIDComm([new DIDCommHttpTransport()]), new CredentialIssuer({ ldCredentialModule: new LdCredentialModule({ - ldContextLoader: new LdContextLoader({ - contextsPaths: [ - LdDefaultContexts, - credential_contexts as Map - ] - }), - ldSuiteLoader: new LdSuiteLoader({ - veramoLdSignatures: [ - new VeramoEd25519Signature2018(), - new VeramoEcdsaSecp256k1RecoverySignature2020() - ] - }) - }) - } - ), + ldContextLoader: new LdContextLoader({ + contextsPaths: [LdDefaultContexts, credential_contexts as Map], + }), + ldSuiteLoader: new LdSuiteLoader({ + veramoLdSignatures: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + ], + }), + }), + }), new SelectiveDisclosure(), new DIDDiscovery({ providers: [new AliasDiscoveryProvider(), new ProfileDiscoveryProvider()], diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 62bf515e8..be3e4af3c 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -27,12 +27,11 @@ import { LdContextLoader, LdDefaultContexts, LdSuiteLoader, - VeramoEd25519Signature2018, VeramoEcdsaSecp256k1RecoverySignature2020, } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' -import { KeyDIDProvider } from '../packages/did-provider-key/src' +import { KeyDIDProvider, getDidKeyResolver } from '../packages/did-provider-key/src' import { DIDComm, DIDCommMessageHandler, IDIDComm } from '../packages/did-comm/src' import { SelectiveDisclosure, @@ -41,14 +40,12 @@ import { } from '../packages/selective-disclosure/src' import { KeyManagementSystem } from '../packages/kms-local/src' import { Entities, IDataStoreORM, DataStore, DataStoreORM, migrations } from '../packages/data-store/src' -import { getDidKeyResolver } from '../packages/did-provider-key/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' -import { getDidKeyResolver } from '../packages/did-provider-key' -import {contexts as credential_contexts} from '@transmute/credentials-context' +import { contexts as credential_contexts } from '@transmute/credentials-context' import fs from 'fs' jest.setTimeout(30000) @@ -89,7 +86,7 @@ const setup = async (options?: IAgentOptions): Promise => { database: databaseFile, synchronize: false, migrations: migrations, - migrationsRun:true, + migrationsRun: true, logging: false, entities: Entities, }) @@ -146,9 +143,6 @@ const setup = async (options?: IAgentOptions): Promise => { 'did:key': new KeyDIDProvider({ defaultKms: 'local', }), - 'did:key': new KeyDIDProvider({ - defaultKms: 'local', - }), 'did:fake': new FakeDidProvider(), }, }), @@ -172,22 +166,17 @@ const setup = async (options?: IAgentOptions): Promise => { }), new DIDComm(), new CredentialIssuer({ - ldCredentialModule: new LdCredentialModule({ - ldContextLoader: new LdContextLoader({ - contextsPaths: [ - LdDefaultContexts, - credential_contexts as Map - ] - }), - ldSuiteLoader: new LdSuiteLoader({ - veramoLdSignatures: [ - new VeramoEd25519Signature2018(), - new VeramoEcdsaSecp256k1RecoverySignature2020() - ] - }) - }) - } - ), + ldCredentialModule: new LdCredentialModule({ + ldContextLoader: new LdContextLoader({ + contextsPaths: [LdDefaultContexts, credential_contexts as Map], + }), + ldSuiteLoader: new LdSuiteLoader({ + veramoLdSignatures: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + ], + }), + }), + }), new SelectiveDisclosure(), ...(options?.plugins || []), ], @@ -202,7 +191,7 @@ const tearDown = async (): Promise => { } catch (e) { // nop } - try{ + try { fs.unlinkSync(databaseFile) } catch (e) { //nop diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 040bdddd0..9298b874c 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -31,12 +31,11 @@ import { LdContextLoader, LdDefaultContexts, LdSuiteLoader, - VeramoEd25519Signature2018, VeramoEcdsaSecp256k1RecoverySignature2020, } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' -import { KeyDIDProvider } from '../packages/did-provider-key/src' +import { KeyDIDProvider, getDidKeyResolver } from '../packages/did-provider-key/src' import { DIDComm, DIDCommMessageHandler, IDIDComm, DIDCommHttpTransport } from '../packages/did-comm/src' import { SelectiveDisclosure, @@ -58,10 +57,7 @@ import { import { createConnection, Connection } from 'typeorm' import { AgentRestClient } from '../packages/remote-client/src' import { AgentRouter, RequestWithAgentRouter, MessagingRouter } from '../packages/remote-server/src' -import { getDidKeyResolver } from '../packages/did-provider-key/src' import { IDIDDiscovery, DIDDiscovery } from '../packages/did-discovery/src' -import { KeyDIDProvider } from '../packages/did-provider-key/src' -import { getUniversalResolver } from '../packages/did-resolver/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' import { Resolver } from 'did-resolver' @@ -69,7 +65,7 @@ import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' import express from 'express' import { Server } from 'http' -import {contexts as credential_contexts} from '@transmute/credentials-context' +import { contexts as credential_contexts } from '@transmute/credentials-context' import fs from 'fs' jest.setTimeout(30000) @@ -172,9 +168,6 @@ const setup = async (options?: IAgentOptions): Promise => { 'did:key': new KeyDIDProvider({ defaultKms: 'local', }), - 'did:key': new KeyDIDProvider({ - defaultKms: 'local', - }), 'did:fake': new FakeDidProvider(), }, }), @@ -199,22 +192,17 @@ const setup = async (options?: IAgentOptions): Promise => { }), new DIDComm([new DIDCommHttpTransport()]), new CredentialIssuer({ - ldCredentialModule: new LdCredentialModule({ - ldContextLoader: new LdContextLoader({ - contextsPaths: [ - LdDefaultContexts, - credential_contexts as Map - ] - }), - ldSuiteLoader: new LdSuiteLoader({ - veramoLdSignatures: [ - new VeramoEd25519Signature2018(), - new VeramoEcdsaSecp256k1RecoverySignature2020() - ] - }) - }) - } - ), + ldCredentialModule: new LdCredentialModule({ + ldContextLoader: new LdContextLoader({ + contextsPaths: [LdDefaultContexts, credential_contexts as Map], + }), + ldSuiteLoader: new LdSuiteLoader({ + veramoLdSignatures: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + ], + }), + }), + }), new SelectiveDisclosure(), new DIDDiscovery({ providers: [new AliasDiscoveryProvider(), new ProfileDiscoveryProvider()], diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index 931dd7062..9128817eb 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -13,44 +13,29 @@ export default (testContext: { describe('creating Verifiable Credentials in LD', () => { let agent: ConfiguredAgent let ethrIdentifier: IIdentifier - let keyE256KIdentifier: IIdentifier let storedCredentialHash: string let challenge: string - beforeAll(() => { - testContext.setup() + beforeAll(async () => { + await testContext.setup() agent = testContext.getAgent() challenge = 'TEST_CHALLENGE_STRING' + ethrIdentifier = await agent.didManagerCreate({ kms: 'local', provider: 'did:ethr' }) }) afterAll(testContext.tearDown) - it('should create ethr identifier', async () => { - ethrIdentifier = await agent.didManagerCreate({ kms: 'local' }) - expect(ethrIdentifier).toHaveProperty('did') - }) - - it('should resolve identifier', async () => { - const didDoc = (await agent.resolveDid({ didUrl: ethrIdentifier.did })).didDocument - // console.log(JSON.stringify(didDoc, null, 2)); - expect(didDoc).toHaveProperty('verificationMethod') - }) - - it('should create verifiable credential in LD', async () => { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: ethrIdentifier.did }, - '@context': [ - 'https://www.w3.org/2018/credentials/v1', - 'https://veramo.io/contexts/profile/v1' - ], + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1'], type: ['VerifiableCredential', 'Profile'], issuanceDate: new Date().toISOString(), credentialSubject: { - name: "Martin, the great" + name: 'Martin, the great', }, }, - save: true, + save: false, proofFormat: 'lds', }) @@ -62,37 +47,40 @@ export default (testContext: { expect(verifiableCredential['@context']).toEqual([ 'https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1', - 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld' + 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', ]) - expect(verifiableCredential['type']).toEqual( - ['VerifiableCredential', 'Profile']) + expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Profile']) - expect(await agent.dataStoreORMGetVerifiableCredentialsCount()).toEqual(1) - - storedCredentialHash = (await agent.dataStoreORMGetVerifiableCredentials())[0].hash + storedCredentialHash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) - const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) expect(verifiableCredential).toEqual(verifiableCredential2) }) it('should verify a verifiable credential in LD', async () => { - const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) // check that verification works - const result = await agent.verifyVerifiableCredential({ - credential: verifiableCredential + const result = await agent.verifyCredential({ + credential: verifiableCredential, }) expect(result).toEqual(true) }) it('should handleMessage with VC (non-JWT)', async () => { - const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) const parsedMessage = await agent.handleMessage({ raw: JSON.stringify({ body: verifiableCredential, - type: 'w3c.vc' + type: 'w3c.vc', }), save: false, metaData: [{ type: 'LDS' }], @@ -101,28 +89,32 @@ export default (testContext: { }) it('should fail handleMessage with wrong VC (non-JWT)', async () => { - expect.assertions(1); + expect.assertions(1) - const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) - verifiableCredential.credentialSubject.name = "Martin, the not so greats" + verifiableCredential.credentialSubject.name = 'Martin, the not so greats' try { await agent.handleMessage({ raw: JSON.stringify({ body: verifiableCredential, - type: 'w3c.vc' + type: 'w3c.vc', }), save: false, metaData: [{ type: 'LDS' }], - }); + }) } catch (e) { - expect(e).toEqual(Error('Error verifying LD Verifiable Credential')); + expect(e).toEqual(Error('Error verifying LD Verifiable Credential')) } }) it('should sign a verifiable presentation in LD', async () => { - const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) const verifiablePresentation = await agent.createVerifiablePresentation({ presentation: { @@ -142,9 +134,10 @@ export default (testContext: { expect(verifiablePresentation).toHaveProperty('proof.jws') }) - it('should sign and verify a verifiable presentation in LD', async () => { - const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) const domain = 'TEST_DOMAIN' const verifiablePresentation = await agent.createVerifiablePresentation({ @@ -162,17 +155,19 @@ export default (testContext: { // console.log(JSON.stringify(verifiablePresentation, null, 2)) - const result = await agent.verifyVerifiablePresentation({ + const result = await agent.verifyPresentation({ presentation: verifiablePresentation, challenge, - domain + domain, }) expect(result).toBeTruthy() }) it('should handleMessage with VPs (non-JWT)', async () => { - const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) const verifiablePresentation = await agent.createVerifiablePresentation({ presentation: { @@ -190,68 +185,12 @@ export default (testContext: { const parsedMessage = await agent.handleMessage({ raw: JSON.stringify({ body: verifiablePresentation, - type: 'w3c.vp' + type: 'w3c.vp', }), save: false, metaData: [{ type: 'LDS' }], }) expect(typeof parsedMessage.id).toEqual('string') }) - - it('should create did:key identifier', async () => { - keyE256KIdentifier = await agent.didManagerCreate({ kms: 'local', provider: 'did:key' }) - expect(keyE256KIdentifier).toHaveProperty('did') - }) - - it('should create verifiable credential in LD with did:key', async () => { - const verifiableCredential = await agent.createVerifiableCredential({ - credential: { - issuer: { id: keyE256KIdentifier.did }, - '@context': [ - 'https://www.w3.org/2018/credentials/v1', - 'https://veramo.io/contexts/profile/v1' - ], - type: ['VerifiableCredential', 'Profile'], - issuanceDate: new Date().toISOString(), - credentialSubject: { - id: keyE256KIdentifier.did, - name: "Martin, the great" - }, - }, - proofFormat: 'lds', - }) - - // Check credential: - expect(verifiableCredential).toHaveProperty('proof') - expect(verifiableCredential).toHaveProperty('proof.jws') - expect(verifiableCredential.proof.verificationMethod).toEqual(`${keyE256KIdentifier.did}#${keyE256KIdentifier.did.substring(keyE256KIdentifier.did.lastIndexOf(':') + 1)}`) - - expect(verifiableCredential['@context']).toEqual([ - 'https://www.w3.org/2018/credentials/v1', - 'https://veramo.io/contexts/profile/v1', - ]) - expect(verifiableCredential['type']).toEqual( - ['VerifiableCredential', 'Profile']) - - storedCredentialHash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) - expect(typeof storedCredentialHash).toEqual('string') - - const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) - expect(verifiableCredential).toEqual(verifiableCredential2) - }) - - it('should verify a verifiable credential in LD with did:key', async () => { - const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) - - // console.log(JSON.stringify(verifiableCredential, null, 2)) - // check that verification works - const result = await agent.verifyVerifiableCredential({ - credential: verifiableCredential - }) - - expect(result).toEqual(true) - }) - - }) } diff --git a/__tests__/utils/fake-did.ts b/__tests__/utils/fake-did.ts index e3effc866..7c125f468 100644 --- a/__tests__/utils/fake-did.ts +++ b/__tests__/utils/fake-did.ts @@ -9,7 +9,7 @@ import { VerificationMethod, } from 'did-resolver' import { TAgent } from '@veramo/core/src' -import { _NormalizedVerificationMethod } from '../../packages/did-comm/src' +import { _NormalizedVerificationMethod } from '../../packages/utils/src' /** * A DID method that uses the information stored by the DID manager to resolve diff --git a/packages/cli/default/default.yml b/packages/cli/default/default.yml index 95e826298..17ed1eb9f 100644 --- a/packages/cli/default/default.yml +++ b/packages/cli/default/default.yml @@ -59,8 +59,8 @@ constants: - createSelectiveDisclosureRequest - getVerifiableCredentialsForSdr - validatePresentationAgainstSdr - - verifyVerifiableCredential - - verifyVerifiablePresentation + - verifyCredential + - verifyPresentation # Data base dbConnection: diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index 465bd2582..073eb4afb 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -1,30 +1,23 @@ -import { IIdentifier, VerifiableCredential, W3CCredential, W3CPresentation, - VerifiablePresentation, IKey,} from '@veramo/core' -import { CredentialIssuer, IContext } from '../action-handler' -import { LdCredentialModule } from '../ld-credential-module' -import { LdContextLoader } from '../ld-context-loader' -import { LdDefaultContexts } from '../ld-default-contexts' -import {contexts as credential_contexts} from '@transmute/credentials-context' +// Mock must come before imports with transitive dependency. +jest.mock('did-jwt-vc', () => { + const mockDidJwtVc = { + createVerifiableCredentialJwt: jest.fn().mockReturnValue('mockVcJwt'), + createVerifiablePresentationJwt: jest.fn().mockReturnValue('mockVcJwt'), + verifyCredential: jest.fn().mockReturnValue({ payload: {} }), + normalizeCredential: jest.fn().mockReturnValue('mockCredential'), + normalizePresentation: jest.fn().mockReturnValue('mockPresentation'), + } + return mockDidJwtVc +}) -import { IIdentifier, VerifiableCredential, W3CCredential, W3CPresentation } from '@veramo/core' +import { IIdentifier, IKey, VerifiableCredential, W3CCredential, W3CPresentation } from '@veramo/core' import { CredentialIssuer, IContext } from '../action-handler' import { LdCredentialModule } from '../ld-credential-module' import { LdContextLoader } from '../ld-context-loader' import { LdDefaultContexts } from '../ld-default-contexts' import { contexts as credential_contexts } from '@transmute/credentials-context' import { LdSuiteLoader } from '../ld-suite-loader' -import { VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018 } from '../ld-suites' - -const mockDidJwtVc = { - createVerifiableCredentialJwt: jest.fn().mockReturnValue('mockVcJwt'), - createVerifiablePresentationJwt: jest.fn().mockReturnValue('mockVcJwt'), - verifyCredential: jest.fn().mockReturnValue({ payload: {} }), - normalizeCredential: jest.fn().mockReturnValue('mockCredential'), - normalizePresentation: jest.fn().mockReturnValue('mockPresentation'), -} - -// Mock must come before imports with transitive dependency. -jest.mock('did-jwt-vc', () => mockDidJwtVc) +import { VeramoEcdsaSecp256k1RecoverySignature2020 } from '../ld-suites' const mockIdentifiers: IIdentifier[] = [ { @@ -74,18 +67,12 @@ const mockIdentifiers: IIdentifier[] = [ const w3c = new CredentialIssuer({ ldCredentialModule: new LdCredentialModule({ ldContextLoader: new LdContextLoader({ - contextsPaths: [ - LdDefaultContexts, - credential_contexts as Map - ] + contextsPaths: [LdDefaultContexts, credential_contexts as Map], }), ldSuiteLoader: new LdSuiteLoader({ - veramoLdSignatures: [ - new VeramoEd25519Signature2018(), - new VeramoEcdsaSecp256k1RecoverySignature2020() - ] - }) - }) + veramoLdSignatures: [new VeramoEcdsaSecp256k1RecoverySignature2020()], + }), + }), }) let agent = { @@ -95,15 +82,17 @@ let agent = { getDIDComponentById: jest.fn(), emit: jest.fn(), keyManagerSign: jest.fn().mockImplementation(async (args): Promise => 'mockJWT'), - keyManagerGet: jest.fn().mockImplementation(async (args): Promise => ({ + keyManagerGet: jest.fn().mockImplementation( + async (args): Promise => ({ kid: '', kms: '', type: 'Ed25519', publicKeyHex: '', - })), - dataStoreSaveVerifiableCredential: jest.fn().mockImplementation(async (args): Promise => true), - dataStoreSaveVerifiablePresentation: jest.fn().mockImplementation(async (args): Promise => true), - getSchema: jest.fn(), + }), + ), + dataStoreSaveVerifiableCredential: jest.fn().mockImplementation(async (args): Promise => true), + dataStoreSaveVerifiablePresentation: jest.fn().mockImplementation(async (args): Promise => true), + getSchema: jest.fn(), didManagerGet: jest.fn(), } diff --git a/packages/credential-w3c/src/__tests__/message-handler.test.ts b/packages/credential-w3c/src/__tests__/message-handler.test.ts index 81456126d..f3a14111b 100644 --- a/packages/credential-w3c/src/__tests__/message-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/message-handler.test.ts @@ -1,4 +1,4 @@ -import { DIDDocument, DIDResolutionResult } from '@veramo/core' +import { DIDResolutionResult } from '@veramo/core' import { Message } from '@veramo/message-handler' import { W3cMessageHandler, MessageTypes } from '../index' import { IContext } from '../message-handler' @@ -65,8 +65,8 @@ describe('@veramo/credential-w3c', () => { }, createVerifiableCredential: jest.fn(), createVerifiablePresentation: jest.fn(), - verifyVerifiableCredential: jest.fn(), - verifyVerifiablePresentation: jest.fn(), + verifyCredential: jest.fn(), + verifyPresentation: jest.fn(), getDIDComponentById: jest.fn(), }, } diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index f8ebf6cb3..4962a9f58 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -7,7 +7,9 @@ import { IPluginMethodMap, VerifiableCredential, VerifiablePresentation, - IDataStore, IKey, + IDataStore, + IKey, + IIdentifier, } from '@veramo/core' import { @@ -105,7 +107,6 @@ export interface ICreateVerifiableCredentialArgs { /** * The desired format for the VerifiablePresentation to be created. - * Currently, only JWT is supported */ proofFormat: ProofFormat @@ -122,7 +123,7 @@ export interface ICreateVerifiableCredentialArgs { * * @public */ -export interface IVerifyVerifiableCredentialArgs { +export interface IVerifyCredentialArgs { /** * The json payload of the Credential according to the * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} @@ -131,7 +132,7 @@ export interface IVerifyVerifiableCredentialArgs { * of the `credential` * */ - credential: Partial + credential: VerifiableCredential } /** @@ -140,7 +141,7 @@ export interface IVerifyVerifiableCredentialArgs { * * @public */ -export interface IVerifyVerifiablePresentationArgs { +export interface IVerifyPresentationArgs { /** * The json payload of the Credential according to the * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} @@ -149,7 +150,7 @@ export interface IVerifyVerifiablePresentationArgs { * of the `credential` * */ - presentation: Partial + presentation: VerifiablePresentation /** * Optional (only for JWT) string challenge parameter to verify the verifiable presentation against @@ -204,7 +205,6 @@ export interface ICredentialIssuer extends IPluginMethodMap { context: IContext, ): Promise - /** * Verifies a Verifiable Credential JWT or LDS Format. * @@ -215,11 +215,7 @@ export interface ICredentialIssuer extends IPluginMethodMap { * * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} */ - verifyVerifiableCredential( - args: IVerifyVerifiableCredentialArgs, - context: IContext, - ): Promise - + verifyCredential(args: IVerifyCredentialArgs, context: IContext): Promise /** * Verifies a Verifiable Presentation JWT or LDS Format. @@ -231,10 +227,7 @@ export interface ICredentialIssuer extends IPluginMethodMap { * * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Credential data model} */ - verifyVerifiablePresentation( - args: IVerifyVerifiablePresentationArgs, - context: IContext, - ): Promise + verifyPresentation(args: IVerifyPresentationArgs, context: IContext): Promise } /** @@ -247,7 +240,7 @@ export type IContext = IAgentContext< IResolver & Pick & Pick & - Pick + Pick > /** @@ -260,16 +253,14 @@ export class CredentialIssuer implements IAgentPlugin { readonly schema = schema.ICredentialIssuer private ldCredentialModule: LdCredentialModule - constructor(options: { - ldCredentialModule: LdCredentialModule - }) { + constructor(options: { ldCredentialModule: LdCredentialModule }) { this.ldCredentialModule = options.ldCredentialModule this.methods = { createVerifiablePresentation: this.createVerifiablePresentation.bind(this), createVerifiableCredential: this.createVerifiableCredential.bind(this), - verifyVerifiableCredential: this.verifyVerifiableCredential.bind(this), - verifyVerifiablePresentation: this.verifyVerifiablePresentation.bind(this), + verifyCredential: this.verifyCredential.bind(this), + verifyPresentation: this.verifyPresentation.bind(this), } } @@ -309,8 +300,17 @@ export class CredentialIssuer implements IAgentPlugin { } const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - return await this.ldCredentialModule.signLDVerifiablePresentation(presentation, - keyPayload, args.challenge, args.domain, identifier, context) + //issuanceDate must not be present for presentations + delete presentation.issuanceDate + + return await this.ldCredentialModule.signLDVerifiablePresentation( + presentation, + keyPayload, + args.challenge, + args.domain, + identifier, + context, + ) } //------------------------- END JSON_LD INSERT else { @@ -375,7 +375,7 @@ export class CredentialIssuer implements IAgentPlugin { if (!key) throw Error('No signing key for ' + identifier.did) //------------------------- BEGIN JSON_LD INSERT - let verifiableCredential; + let verifiableCredential if (args.proofFormat === 'lds') { // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod if (key.kid != identifier.controllerKeyId) { @@ -383,7 +383,12 @@ export class CredentialIssuer implements IAgentPlugin { } const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - verifiableCredential = await this.ldCredentialModule.issueLDVerifiableCredential(credential, keyPayload, identifier, context) + verifiableCredential = await this.ldCredentialModule.issueLDVerifiableCredential( + credential, + keyPayload, + identifier, + context, + ) } else { //------------------------- END JSON_LD INSERT //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` or `lds` @@ -391,9 +396,10 @@ export class CredentialIssuer implements IAgentPlugin { let alg = 'ES256K' if (key.type === 'Ed25519') { alg = 'EdDSA' - }const signer = wrapSigner(context, key, alg) + } + const signer = wrapSigner(context, key, alg) const jwt = await createVerifiableCredentialJwt( - credentialas CredentialPayload, + credential as CredentialPayload, { did: identifier.did, signer, alg }, { removeOriginalFields: args.removeOriginalFields }, ) @@ -412,34 +418,39 @@ export class CredentialIssuer implements IAgentPlugin { } } - /** {@inheritdoc ICredentialIssuer.verifyVerifiableCredential} */ - async verifyVerifiableCredential( - args: IVerifyVerifiableCredentialArgs, + /** {@inheritdoc ICredentialIssuer.verifyCredential} */ + async verifyCredential( + args: IVerifyCredentialArgs, context: IContext, ): Promise { const credential = args.credential // JWT if (credential.proof.jwt) { // Not implemented yet. - throw Error('verifyVerifiableCredential currently does not the verification of VC-JWT credentials.') + throw Error('verifyCredential currently does not the verification of VC-JWT credentials.') } - return this.ldCredentialModule.verifyVerifiableCredential(credential, context) + return this.ldCredentialModule.verifyCredential(credential, context) } - /** {@inheritdoc ICredentialIssuer.verifyVerifiablePresentation} */ - async verifyVerifiablePresentation( - args: IVerifyVerifiablePresentationArgs, + /** {@inheritdoc ICredentialIssuer.verifyPresentation} */ + async verifyPresentation( + args: IVerifyPresentationArgs, context: IContext, ): Promise { const presentation = args.presentation // JWT if (presentation.proof.jwt) { // Not implemented yet. - throw Error('verifyVerifiablePresentation currently does not the verification of VC-JWT credentials.') + throw Error('verifyPresentation currently does not the verification of VC-JWT credentials.') } - return this.ldCredentialModule.verifyVerifiablePresentation(presentation, args.challenge, args.domain, context) + return this.ldCredentialModule.verifyPresentation( + presentation, + args.challenge, + args.domain, + context, + ) } } diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index acbecb1e3..47b92b763 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -22,7 +22,6 @@ export { LdSuiteLoader } from './ld-suite-loader' export { VeramoLdSignature, VeramoEcdsaSecp256k1RecoverySignature2020, - VeramoEd25519Signature2018 } from './ld-suites' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/credential-w3c/src/ld-credential-module.ts b/packages/credential-w3c/src/ld-credential-module.ts index e4e8581cd..6122bc7a7 100644 --- a/packages/credential-w3c/src/ld-credential-module.ts +++ b/packages/credential-w3c/src/ld-credential-module.ts @@ -1,45 +1,39 @@ import { IAgentContext, IIdentifier, - IKey, IResolver, + IKey, + IResolver, VerifiableCredential, VerifiablePresentation, } from '@veramo/core' - import Debug from 'debug' - -const { purposes: { AssertionProofPurpose }} = require("jsonld-signatures"); -const vc = require('vc-js'); -const { defaultDocumentLoader } = vc; -const { extendContextLoader } = require('jsonld-signatures'); import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' +import { extendContextLoader, purposes } from 'jsonld-signatures' +import * as vc from 'vc-js' import { LdContextLoader } from './ld-context-loader' import { LdSuiteLoader } from './ld-suite-loader' +import { RequiredAgentMethods } from './ld-suites' + +const AssertionProofPurpose = purposes.AssertionProofPurpose const debug = Debug('veramo:w3c:ld-credential-module') export class LdCredentialModule { - /** * TODO: General Implementation Notes * - (SOLVED) EcdsaSecp256k1Signature2019 (Signature) and EcdsaSecp256k1VerificationKey2019 (Key) * are not useable right now, since they are not able to work with blockChainId and ECRecover. - * - DID Fragement Resolution. + * - DID Fragment Resolution. * - Key Manager and Verification Methods: Veramo currently implements no link between those. */ private ldContextLoader: LdContextLoader private ldSuiteLoader: LdSuiteLoader - constructor(options: { - ldContextLoader: LdContextLoader - ldSuiteLoader: LdSuiteLoader - }) { + constructor(options: { ldContextLoader: LdContextLoader; ldSuiteLoader: LdSuiteLoader }) { this.ldContextLoader = options.ldContextLoader this.ldSuiteLoader = options.ldSuiteLoader } - - getDocumentLoader(context: IAgentContext) { return extendContextLoader(async (url: string) => { // console.log(`resolving context for: ${url}`) @@ -54,16 +48,16 @@ export class LdCredentialModule { // currently Veramo LD suites can modify the resolution response for DIDs from // the document Loader. This allows to fix incompatibilities between DID Documents // and LD suites to be fixed specifically within the Veramo LD Suites definition - this.ldSuiteLoader.getAllSignatureSuites().forEach( - x => x.preDidResolutionModification(url, returnDocument)) - + this.ldSuiteLoader + .getAllSignatureSuites() + .forEach((x) => x.preDidResolutionModification(url, returnDocument)) // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) return { contextUrl: null, documentUrl: url, - document: returnDocument - }; + document: returnDocument, + } } if (this.ldContextLoader.has(url)) { @@ -71,23 +65,23 @@ export class LdCredentialModule { return { contextUrl: null, documentUrl: url, - document: this.ldContextLoader.get(url) - }; + document: this.ldContextLoader.get(url), + } } debug('WARNING: Possible unknown context/identifier for', url) console.log(`WARNING: Possible unknown context/identifier for: ${url}`) - return defaultDocumentLoader(url); - }); + return vc.defaultDocumentLoader(url) + }) } async issueLDVerifiableCredential( credential: Partial, key: IKey, identifier: IIdentifier, - context: IAgentContext): Promise { - + context: IAgentContext, + ): Promise { const suite = this.ldSuiteLoader.getSignatureSuiteForKeyType(key.type) const documentLoader = this.getDocumentLoader(context) @@ -96,10 +90,10 @@ export class LdCredentialModule { return await vc.issue({ credential, - suite: suite.getSuiteForSigning(key, identifier), + suite: suite.getSuiteForSigning(key, identifier, context), documentLoader, - compactProof: false - }); + compactProof: false, + }) } async signLDVerifiablePresentation( @@ -108,41 +102,37 @@ export class LdCredentialModule { challenge: string | undefined, domain: string | undefined, identifier: IIdentifier, - context: IAgentContext, + context: IAgentContext, ): Promise { - const suite = this.ldSuiteLoader.getSignatureSuiteForKeyType(key.type) const documentLoader = this.getDocumentLoader(context) suite.preSigningPresModification(presentation) - return await vc.signPresentation({ presentation, - suite: suite.getSuiteForSigning(key, identifier), + suite: suite.getSuiteForSigning(key, identifier, context), challenge, domain, documentLoader, purpose: new AssertionProofPurpose(), - compactProof: false + compactProof: false, }) } - async verifyVerifiableCredential( + async verifyCredential( credential: Partial, - context: IAgentContext + context: IAgentContext, ): Promise { - const result = await vc.verifyCredential({ credential, - suite: this.ldSuiteLoader.getAllSignatureSuites().map(x => x.getSuiteForVerification()), + suite: this.ldSuiteLoader.getAllSignatureSuites().map((x) => x.getSuiteForVerification()), documentLoader: this.getDocumentLoader(context), purpose: new AssertionProofPurpose(), - compactProof: false - }); + compactProof: false, + }) - if (result.verified) - return true + if (result.verified) return true // NOT verified. @@ -152,32 +142,29 @@ export class LdCredentialModule { throw Error('Error verifying LD Verifiable Credential') } - async verifyVerifiablePresentation( + async verifyPresentation( presentation: Partial, challenge: string | undefined, domain: string | undefined, - context: IAgentContext + context: IAgentContext, ): Promise { - const result = await vc.verify({ presentation, - suite: this.ldSuiteLoader.getAllSignatureSuites().map(x => x.getSuiteForVerification()), + suite: this.ldSuiteLoader.getAllSignatureSuites().map((x) => x.getSuiteForVerification()), documentLoader: this.getDocumentLoader(context), challenge, domain, purpose: new AssertionProofPurpose(), - compactProof: false - }); + compactProof: false, + }) - if (result.verified) - return true + if (result.verified) return true // NOT verified. // result can include raw Error console.log(`Error verifying LD Verifiable Presentation`) - console.log(JSON.stringify(result, null, 2)); + console.log(JSON.stringify(result, null, 2)) throw Error('Error verifying LD Verifiable Presentation') } - } diff --git a/packages/credential-w3c/src/ld-suites.ts b/packages/credential-w3c/src/ld-suites.ts index 4af1a2c44..75f964565 100644 --- a/packages/credential-w3c/src/ld-suites.ts +++ b/packages/credential-w3c/src/ld-suites.ts @@ -1,28 +1,36 @@ -import { IIdentifier, IKey, TKeyType } from '@veramo/core' -import { Ed25519Signature2018, Ed25519VerificationKey2018 } from '@transmute/ed25519-signature-2018' +import { IAgentContext, IIdentifier, IKey, IKeyManager, IResolver, TKeyType } from '@veramo/core' import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' import { DIDDocument } from 'did-resolver/src/resolver' -const { EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020 } = require('EcdsaSecp256k1RecoverySignature2020') +import { + EcdsaSecp256k1RecoveryMethod2020, + EcdsaSecp256k1RecoverySignature2020, +} from '@transmute/lds-ecdsa-secp256k1-recovery2020' +import * as u8a from 'uint8arrays' +import { encodeJoseBlob } from '@veramo/utils' +export type RequiredAgentMethods = IResolver & Pick export abstract class VeramoLdSignature { - // LinkedDataSignature Suites according to // https://github.com/digitalbazaar/jsonld-signatures/blob/main/lib/suites/LinkedDataSignature.js // Add type definition as soon as https://github.com/digitalbazaar/jsonld-signatures // supports those. - abstract getSupportedVerificationType(): string; + abstract getSupportedVerificationType(): string - abstract getSupportedVeramoKeyType(): TKeyType; + abstract getSupportedVeramoKeyType(): TKeyType - abstract getSuiteForSigning(key: IKey, identifier: IIdentifier): any; + abstract getSuiteForSigning( + key: IKey, + identifier: IIdentifier, + context: IAgentContext, + ): any - abstract getSuiteForVerification(): any; + abstract getSuiteForVerification(): any - abstract preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void; + abstract preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void - abstract preSigningCredModification(credential: Partial): void; + abstract preSigningCredModification(credential: Partial): void preSigningPresModification(presentation: Partial): void { // TODO: Remove invalid field 'verifiers' from Presentation. Needs to be adapted for LD credentials @@ -34,9 +42,7 @@ export abstract class VeramoLdSignature { } } - export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature { - getSupportedVerificationType(): string { return 'EcdsaSecp256k1RecoveryMethod2020' } @@ -45,18 +51,40 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature return 'Secp256k1' } - getSuiteForSigning(key: IKey, identifier: IIdentifier): any { + getSuiteForSigning(key: IKey, identifier: IIdentifier, context: IAgentContext): any { const controller = identifier.did + const signer = { + //returns a JWS detached + sign: async (args: { data: Uint8Array }): Promise => { + const header = { + alg: 'ES256K-R', + b64: false, + crit: ['b64'], + } + const headerString = encodeJoseBlob(header) + const messageBuffer = u8a.concat([u8a.fromString(`${headerString}.`, 'utf-8'), args.data]) + const messageString = u8a.toString(messageBuffer, 'base64') + const signature = await context.agent.keyManagerSign({ + keyRef: key.kid, + algorithm: 'ES256K-R', + data: messageString, + encoding: 'base64', + }) + return `${headerString}..${signature}` + }, + } + return new EcdsaSecp256k1RecoverySignature2020({ + // signer, key: new EcdsaSecp256k1RecoveryMethod2020({ publicKeyHex: key.publicKeyHex, - privateKeyHex: key.privateKeyHex, + signer: () => signer, type: this.getSupportedVerificationType(), controller, - id: `${controller}#controller` // TODO: Only default controller verificationMethod supported + id: `${controller}#controller`, // FIXME: Only default controller verificationMethod supported }), - }); + }) } getSuiteForVerification(): any { @@ -67,7 +95,9 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature if (!Array.isArray(credential['@context'])) { credential['@context'] = [] } - credential['@context'].push('https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld') + credential['@context'].push( + 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', + ) } preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { @@ -77,9 +107,9 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature didDoc.assertionMethod = [] // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId // blockchainAccountId to ethereumAddress - didDoc.verificationMethod?.forEach(x => { + didDoc.verificationMethod?.forEach((x) => { if (x.blockchainAccountId) { - x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf("@")) + x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf('@')) } // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." @@ -88,54 +118,4 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature }) } } - } - -export class VeramoEd25519Signature2018 extends VeramoLdSignature { - - getSupportedVerificationType(): string { - return 'EcdsaSecp256k1RecoveryMethod2020' - } - - getSupportedVeramoKeyType(): TKeyType { - return 'Ed25519' - } - - getSuiteForSigning(key: IKey, identifier: IIdentifier): any { - if (!key.privateKeyHex) { - throw Error('No private Key for LD Signing available.') - } - - const controller = identifier.did - - // DID Key ID - let id = `${controller}#controller` - // TODO: Hacky id adjustment - if (controller.startsWith('did:key')) { - id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` - } - - - return new Ed25519Signature2018({ - key: new Ed25519VerificationKey2018({ - id, - controller, - publicKey: Buffer.from(key.publicKeyHex, 'hex'), - privateKey: Buffer.from(key.privateKeyHex, 'hex'), - }) - }); - } - - getSuiteForVerification(): any { - return new Ed25519Signature2018(); - } - - preSigningCredModification(credential: Partial): void { - // nothing to do here - } - - preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { - // nothing to do here - } - -} \ No newline at end of file diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index 229d97074..8d7218fe3 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -1,17 +1,13 @@ import { IAgentContext, IResolver, VerifiableCredential, VerifiablePresentation } from '@veramo/core' -import { Message, AbstractMessageHandler } from '@veramo/message-handler' +import { AbstractMessageHandler, Message } from '@veramo/message-handler' import { blake2bHex } from 'blakejs' - +import Debug from 'debug' import { - validateJwtCredentialPayload, - validateJwtPresentationPayload, - normalizePresentation, - normalizeCredential, + normalizeCredential, normalizePresentation, validateJwtCredentialPayload, + validateJwtPresentationPayload } from 'did-jwt-vc' - -import Debug from 'debug' - import { ICredentialIssuer } from './action-handler' + const debug = Debug('veramo:w3c:message-handler') /** @@ -109,7 +105,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { const credential = message.data as VerifiableCredential // throws on error. - await context.agent.verifyVerifiableCredential({ credential }) + await context.agent.verifyCredential({ credential }) message.id = blake2bHex(message.raw) message.type = MessageTypes.vc message.from = credential.issuer.id @@ -129,7 +125,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { const presentation = message.data as VerifiablePresentation // throws on error. - await context.agent.verifyVerifiablePresentation({ + await context.agent.verifyPresentation({ presentation , // TODO: HARDCODED CHALLENGE VERIFICATION FOR NOW challenge: 'VERAMO', diff --git a/packages/credential-w3c/tsconfig.json b/packages/credential-w3c/tsconfig.json index 4abc07c24..7bf251435 100644 --- a/packages/credential-w3c/tsconfig.json +++ b/packages/credential-w3c/tsconfig.json @@ -10,6 +10,7 @@ "references": [ { "path": "../core" }, { "path": "../did-jwt" }, - { "path": "../message-handler" } + { "path": "../message-handler" }, + { "path": "../utils" } ] } diff --git a/packages/credential-w3c/types/jsonld/index.d.ts b/packages/credential-w3c/types/jsonld/index.d.ts index b07278854..480a99c5d 100644 --- a/packages/credential-w3c/types/jsonld/index.d.ts +++ b/packages/credential-w3c/types/jsonld/index.d.ts @@ -1 +1,4 @@ declare module 'jsonld' +declare module 'jsonld-signatures' +declare module 'vc-js' +declare module '@transmute/lds-ecdsa-secp256k1-recovery2020' diff --git a/packages/data-store/src/entities/credential.ts b/packages/data-store/src/entities/credential.ts index 4c07ae2c1..04d83b49c 100644 --- a/packages/data-store/src/entities/credential.ts +++ b/packages/data-store/src/entities/credential.ts @@ -28,7 +28,7 @@ export class Credential extends BaseEntity { @ManyToOne((type) => Identifier, (identifier) => identifier.issuedCredentials, { cascade: ['insert'], eager: true, - onDelete: "CASCADE" + onDelete: 'CASCADE', }) //@ts-ignore issuer: Identifier From 6c0317ae14450631e18df8b75490583651df0fab Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Wed, 17 Nov 2021 14:42:59 +0100 Subject: [PATCH 35/48] fix: refactor after rebase --- __tests__/initial.migration.test.ts | 2 +- __tests__/localAgent.test.ts | 2 +- __tests__/localMemoryStoreAgent.test.ts | 2 +- packages/cli/package.json | 2 +- packages/credential-w3c/package.json | 11 +- packages/credential-w3c/plugin.schema.json | 233 ++++++++++-------- packages/credential-w3c/src/action-handler.ts | 34 ++- .../credential-w3c/src/ld-context-loader.ts | 30 ++- packages/did-comm/package.json | 2 +- packages/did-provider-key/package.json | 4 +- packages/did-provider-key/src/resolver.ts | 27 +- packages/key-manager/package.json | 2 +- packages/remote-client/package.json | 2 +- packages/utils/package.json | 10 +- 14 files changed, 206 insertions(+), 157 deletions(-) diff --git a/__tests__/initial.migration.test.ts b/__tests__/initial.migration.test.ts index a3d4e2816..7f9bde92f 100644 --- a/__tests__/initial.migration.test.ts +++ b/__tests__/initial.migration.test.ts @@ -29,7 +29,7 @@ import { createConnection, Connection, ConnectionOptions } from 'typeorm' import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' -import fs from 'fs' +import * as fs from 'fs' jest.setTimeout(30000) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index fe0dce530..9d051a3c2 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -62,7 +62,7 @@ import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' import { contexts as credential_contexts } from '@transmute/credentials-context' -import fs from 'fs' +import * as fs from 'fs' jest.setTimeout(30000) diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index be3e4af3c..8a1c8ff0b 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -46,7 +46,7 @@ import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' import { contexts as credential_contexts } from '@transmute/credentials-context' -import fs from 'fs' +import * as fs from 'fs' jest.setTimeout(30000) diff --git a/packages/cli/package.json b/packages/cli/package.json index 66e249d3f..3bbc55d60 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -55,7 +55,7 @@ "json-schema": "^0.4.0", "jsonpointer": "^5.0.0", "oas-resolver": "^2.5.3", - "openapi-types": "^9.0.0", + "openapi-types": "9.3.1", "passport": "^0.5.0", "passport-http-bearer": "^1.0.1", "pg": "^8.6.0", diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index f5ad2da14..2c0c9b563 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -14,24 +14,21 @@ } }, "dependencies": { - "@transmute/credentials-context": "^0.7.0-unstable.6", - "@transmute/ed25519-signature-2018": "^0.7.0-unstable.6", - "@transmute/did-key-ed25519": "^0.2.1-unstable.41", - "@transmute/ed25519-signature-2020": "^0.2.1-unstable.10", + "@transmute/credentials-context": "^0.7.0-unstable.26", + "@transmute/lds-ecdsa-secp256k1-recovery2020": "^0.0.7", "@veramo/core": "^3.1.0", "@veramo/did-jwt": "^3.1.0", "@veramo/did-resolver": "^3.1.0", "@veramo/message-handler": "^3.1.0", - "EcdsaSecp256k1RecoverySignature2020": "git+https://github.com/decentralized-identity/EcdsaSecp256k1RecoverySignature2020.git", - "base-58": "^0.0.1", + "@veramo/utils": "^3.1.0", "blakejs": "^1.1.0", "debug": "^4.1.1", "did-jwt-vc": "2.1.7", "did-resolver": "3.1.3", - "uint8arrays": "^2.1.3", "ecdsa-secp256k1-signature-2019": "^1.0.1", "jsonld": "^5.2.0", "jsonld-signatures": "^9.0.2", + "uint8arrays": "^2.1.3", "vc-js": "^0.6.4" }, "devDependencies": { diff --git a/packages/credential-w3c/plugin.schema.json b/packages/credential-w3c/plugin.schema.json index 617c5bacf..1bab18334 100644 --- a/packages/credential-w3c/plugin.schema.json +++ b/packages/credential-w3c/plugin.schema.json @@ -9,36 +9,69 @@ "type": "object", "properties": { "@context": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": {} + } + ] }, "id": { "type": "string" }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": {} + } + ] }, "issuer": { - "type": "object", - "properties": { - "id": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + { "type": "string" } - }, - "required": [ - "id" ] }, "issuanceDate": { - "type": "string" + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": {} + } + ] }, "expirationDate": { - "type": "string" + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": {} + } + ] }, "credentialSubject": { "type": "object", @@ -62,12 +95,10 @@ "id", "type" ] - } + }, + "evidence": {}, + "termsOfUse": {} }, - "required": [ - "issuer", - "credentialSubject" - ], "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, "save": { @@ -76,7 +107,7 @@ }, "proofFormat": { "$ref": "#/components/schemas/ProofFormat", - "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" + "description": "The desired format for the VerifiablePresentation to be created." }, "removeOriginalFields": { "type": "boolean", @@ -166,13 +197,13 @@ }, "required": [ "@context", - "type", - "issuer", - "issuanceDate", "credentialSubject", - "proof" + "issuanceDate", + "issuer", + "proof", + "type" ], - "description": "Verifiable Credential {@link https://github.com/decentralized-identifier/did-jwt-vc }" + "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" }, "ICreateVerifiablePresentationArgs": { "type": "object", @@ -180,48 +211,71 @@ "presentation": { "type": "object", "properties": { - "id": { - "type": "string" - }, - "holder": { - "type": "string" + "@context": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] }, - "issuanceDate": { - "type": "string" + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] }, - "expirationDate": { + "id": { "type": "string" }, - "@context": { + "verifiableCredential": { "type": "array", "items": { - "type": "string" + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/JWT" + } + ] } }, - "type": { - "type": "array", - "items": { - "type": "string" - } + "holder": { + "type": "string" }, "verifier": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" } }, - "required": [ - "holder", - "verifier", - "verifiableCredential" - ], "description": "The json payload of the Presentation according to the\n {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model } .\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, "save": { @@ -251,6 +305,9 @@ ], "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" }, + "JWT": { + "type": "string" + }, "VerifiablePresentation": { "type": "object", "properties": { @@ -300,16 +357,15 @@ } }, "required": [ - "holder", "@context", + "holder", + "proof", "type", - "verifier", - "verifiableCredential", - "proof" + "verifier" ], - "description": "Verifiable Presentation {@link https://github.com/decentralized-identifier/did-jwt-vc }" + "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" }, - "IVerifyVerifiableCredentialArgs": { + "IVerifyCredentialArgs": { "type": "object", "properties": { "credential": { @@ -322,52 +378,11 @@ ], "description": "Encapsulates the parameters required to verify a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" }, - "IVerifyVerifiablePresentationArgs": { + "IVerifyPresentationArgs": { "type": "object", "properties": { "presentation": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "holder": { - "type": "string" - }, - "@context": { - "type": "array", - "items": { - "type": "string" - } - }, - "type": { - "type": "array", - "items": { - "type": "string" - } - }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } - }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } - } - }, - "required": [ - "holder", - "@context", - "type", - "verifiableCredential", - "proof" - ], + "$ref": "#/components/schemas/VerifiablePresentation", "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" }, "challenge": { @@ -404,19 +419,19 @@ "$ref": "#/components/schemas/VerifiablePresentation" } }, - "verifyVerifiableCredential": { + "verifyCredential": { "description": "Verifies a Verifiable Credential JWT or LDS Format.", "arguments": { - "$ref": "#/components/schemas/IVerifyVerifiableCredentialArgs" + "$ref": "#/components/schemas/IVerifyCredentialArgs" }, "returnType": { "type": "boolean" } }, - "verifyVerifiablePresentation": { + "verifyPresentation": { "description": "Verifies a Verifiable Presentation JWT or LDS Format.", "arguments": { - "$ref": "#/components/schemas/IVerifyVerifiablePresentationArgs" + "$ref": "#/components/schemas/IVerifyPresentationArgs" }, "returnType": { "type": "boolean" diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 4962a9f58..0a1416f0f 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -5,11 +5,9 @@ import { IDIDManager, IKeyManager, IPluginMethodMap, - VerifiableCredential, - VerifiablePresentation, IDataStore, IKey, - IIdentifier, + IIdentifier, VerifiableCredential, VerifiablePresentation, W3CPresentation, } from '@veramo/core' import { @@ -18,13 +16,27 @@ import { CredentialPayload, normalizeCredential, normalizePresentation, - PresentationPayload, } from 'did-jwt-vc' import { LdCredentialModule, schema } from './' import Debug from 'debug' +import { JWT } from 'did-jwt-vc/lib/types' + const debug = Debug('veramo:w3c:action-handler') +export interface PresentationPayload { + '@context': string | string[] + type: string | string[] + id?: string + verifiableCredential?: (VerifiableCredential | JWT)[] + holder: string + verifier?: string | string[] + issuanceDate?: string + expirationDate?: string, + + [x: string]: any +} + /** * The type of encoding to be used for the Verifiable Credential or Presentation to be generated. * @@ -236,12 +248,10 @@ export interface ICredentialIssuer extends IPluginMethodMap { * * This interface can be used for static type checks, to make sure your application is properly initialized. */ -export type IContext = IAgentContext< - IResolver & - Pick & - Pick & - Pick -> +export type IContext = IAgentContext & + Pick & + Pick> /** * A Veramo plugin that implements the {@link ICredentialIssuer} methods. @@ -425,7 +435,7 @@ export class CredentialIssuer implements IAgentPlugin { ): Promise { const credential = args.credential // JWT - if (credential.proof.jwt) { + if (typeof credential === 'string' || credential.proof.jwt) { // Not implemented yet. throw Error('verifyCredential currently does not the verification of VC-JWT credentials.') } @@ -440,7 +450,7 @@ export class CredentialIssuer implements IAgentPlugin { ): Promise { const presentation = args.presentation // JWT - if (presentation.proof.jwt) { + if (typeof presentation === 'string' || (presentation).proof.jwt) { // Not implemented yet. throw Error('verifyPresentation currently does not the verification of VC-JWT credentials.') } diff --git a/packages/credential-w3c/src/ld-context-loader.ts b/packages/credential-w3c/src/ld-context-loader.ts index c225ba062..ffe09cc66 100644 --- a/packages/credential-w3c/src/ld-context-loader.ts +++ b/packages/credential-w3c/src/ld-context-loader.ts @@ -1,24 +1,28 @@ -function concatMaps(mapList: Map[]) { - const map = new Map() - for (const mapItem of mapList) { - for (const item of mapItem) { - map.set(...item); - } - } - return map -} - /** * The LdContextLoader is initialized with a List of Map * that it unifies into a single Map to provide to the documentLoader within * the w3c credential module. */ -export class LdContextLoader extends Map { +export class LdContextLoader { + private contexts: Record constructor(options: { - contextsPaths: Map[] + contextsPaths: (Map | Record)[] }) { + this.contexts = {}; + // generate-plugin-schema is failing unless we use the cast to `any[]` + Array.from(options.contextsPaths as any[], (mapItem) => { + for (const [key, value] of mapItem) { + this.contexts[key] = value + } + }) + } + + has(url: string): boolean { + return this.contexts[url] !== null && typeof this.contexts[url] !== 'undefined' + } - super(concatMaps(options.contextsPaths)) + get(url: string): object | undefined { + return this.contexts[url] } } \ No newline at end of file diff --git a/packages/did-comm/package.json b/packages/did-comm/package.json index 3e826ac24..40d2c575d 100644 --- a/packages/did-comm/package.json +++ b/packages/did-comm/package.json @@ -18,7 +18,7 @@ "@stablelib/ed25519": "^1.0.2", "@veramo/core": "^3.1.0", "@veramo/message-handler": "^3.1.0", - "@veramo/utils": "^2.1.0", + "@veramo/utils": "^3.1.0", "cross-fetch": "^3.1.4", "debug": "^4.1.1", "did-jwt": "5.12.0", diff --git a/packages/did-provider-key/package.json b/packages/did-provider-key/package.json index 3135cbd53..c452dce5f 100644 --- a/packages/did-provider-key/package.json +++ b/packages/did-provider-key/package.json @@ -9,7 +9,9 @@ "extract-api": "yarn veramo dev extract-api" }, "dependencies": { - "@transmute/did-key.js": "^0.3.0-unstable", + "@transmute/did-key-ed25519": "^0.3.0-unstable.4", + "@transmute/did-key-secp256k1": "^0.3.0-unstable.4", + "@transmute/did-key-x25519": "^0.3.0-unstable.4", "@veramo/core": "^3.1.0", "@veramo/did-manager": "^3.1.0", "debug": "^4.1.1", diff --git a/packages/did-provider-key/src/resolver.ts b/packages/did-provider-key/src/resolver.ts index 1c5187e8b..e4b16dd06 100644 --- a/packages/did-provider-key/src/resolver.ts +++ b/packages/did-provider-key/src/resolver.ts @@ -1,6 +1,14 @@ -import { resolve } from '@transmute/did-key.js' +import { resolve as resolveED25519 } from '@transmute/did-key-ed25519' +import { resolve as resolveX25519 } from '@transmute/did-key-x25519' +import { resolve as resolveSecp256k1 } from '@transmute/did-key-secp256k1' import { DIDResolutionOptions, DIDResolutionResult, DIDResolver, ParsedDID, Resolvable } from 'did-resolver' +export const startsWithMap: Record = { + 'did:key:z6Mk': resolveED25519, + 'did:key:z6LS': resolveX25519, + 'did:key:zQ3s': resolveSecp256k1, +}; + const resolveDidKey: DIDResolver = async ( didUrl: string, _parsed: ParsedDID, @@ -8,8 +16,21 @@ const resolveDidKey: DIDResolver = async ( options: DIDResolutionOptions, ): Promise => { try { - const didResolution = (await resolve(didUrl, options as any)) as DIDResolutionResult - return didResolution + const startsWith = _parsed.did.substring(0, 12); + if (startsWithMap[startsWith] !== undefined) { + const didResolution = (await startsWithMap[startsWith](didUrl, options as any)) + return { + didDocumentMetadata: {}, + didResolutionMetadata: {}, + ...didResolution + } + } else { + return { + didDocumentMetadata: {}, + didResolutionMetadata: { error: 'invalidDid', message: 'unsupported key type for did:key' }, + didDocument: null, + } + } } catch (err: any) { return { didDocumentMetadata: {}, diff --git a/packages/key-manager/package.json b/packages/key-manager/package.json index 31d73db60..87710efa8 100644 --- a/packages/key-manager/package.json +++ b/packages/key-manager/package.json @@ -6,7 +6,7 @@ "types": "build/index.d.ts", "scripts": { "build": "tsc", - "extract-api": "yarn veramo dev generate-plugin-schema" + "extract-api": "yarn veramo dev extract-api" }, "dependencies": { "@ethersproject/bytes": "5.5.0", diff --git a/packages/remote-client/package.json b/packages/remote-client/package.json index 34551978a..08491ce72 100644 --- a/packages/remote-client/package.json +++ b/packages/remote-client/package.json @@ -12,7 +12,7 @@ "@veramo/core": "^3.1.0", "cross-fetch": "^3.1.4", "debug": "^4.1.1", - "openapi-types": "^9.0.0" + "openapi-types": "9.3.1" }, "devDependencies": { "@types/debug": "4.1.7", diff --git a/packages/utils/package.json b/packages/utils/package.json index 073cb6431..f9686fa04 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,7 +1,7 @@ { "name": "@veramo/utils", "description": "Helper methods for Veramo plugins", - "version": "2.1.2", + "version": "3.1.0", "main": "build/index.js", "types": "build/index.d.ts", "scripts": { @@ -9,18 +9,18 @@ }, "dependencies": { "@stablelib/ed25519": "^1.0.2", - "@veramo/core": "^2.1.0", + "@veramo/core": "^3.1.0", "cross-fetch": "^3.1.4", "debug": "^4.1.1", - "did-jwt": "5.7.0", - "did-resolver": "^3.1.0", + "did-jwt": "^5.11.1", + "did-resolver": "^3.1.3", "uint8arrays": "^3.0.0", "uuid": "^8.3.0" }, "devDependencies": { "@types/debug": "4.1.7", "@types/uuid": "8.3.1", - "typescript": "4.4.2" + "typescript": "4.4.4" }, "files": [ "build/**/*", From 6cf3b93c029aa425621e6374acbfbbf02def9b87 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Wed, 17 Nov 2021 18:11:25 +0100 Subject: [PATCH 36/48] feat(credential-w3c): re-add support for Ed25519Signature2018 for JSON-LD creds --- __tests__/localAgent.test.ts | 2 + __tests__/localMemoryStoreAgent.test.ts | 2 + __tests__/restAgent.test.ts | 5 +- __tests__/shared/verifiableDataLD.ts | 65 ++++++++++++++++++--- packages/credential-w3c/package.json | 1 + packages/credential-w3c/src/index.ts | 1 + packages/credential-w3c/src/ld-suites.ts | 72 ++++++++++++++++++++++++ 7 files changed, 139 insertions(+), 9 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 9d051a3c2..8089c4a24 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -29,6 +29,7 @@ import { LdDefaultContexts, LdSuiteLoader, VeramoEcdsaSecp256k1RecoverySignature2020, + VeramoEd25519Signature2018, } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' @@ -216,6 +217,7 @@ const setup = async (options?: IAgentOptions): Promise => { ldSuiteLoader: new LdSuiteLoader({ veramoLdSignatures: [ new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018() ], }), }), diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 8a1c8ff0b..170882761 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -28,6 +28,7 @@ import { LdDefaultContexts, LdSuiteLoader, VeramoEcdsaSecp256k1RecoverySignature2020, + VeramoEd25519Signature2018 } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' @@ -173,6 +174,7 @@ const setup = async (options?: IAgentOptions): Promise => { ldSuiteLoader: new LdSuiteLoader({ veramoLdSignatures: [ new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018(), ], }), }), diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 9298b874c..489a040a5 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -31,7 +31,7 @@ import { LdContextLoader, LdDefaultContexts, LdSuiteLoader, - VeramoEcdsaSecp256k1RecoverySignature2020, + VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018, } from '../packages/credential-w3c/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' @@ -66,7 +66,7 @@ import { getResolver as webDidResolver } from 'web-did-resolver' import express from 'express' import { Server } from 'http' import { contexts as credential_contexts } from '@transmute/credentials-context' -import fs from 'fs' +import * as fs from 'fs' jest.setTimeout(30000) @@ -199,6 +199,7 @@ const setup = async (options?: IAgentOptions): Promise => { ldSuiteLoader: new LdSuiteLoader({ veramoLdSignatures: [ new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018() ], }), }), diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index 9128817eb..5ee6b4f84 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -12,7 +12,8 @@ export default (testContext: { }) => { describe('creating Verifiable Credentials in LD', () => { let agent: ConfiguredAgent - let ethrIdentifier: IIdentifier + let didEthrIdentifier: IIdentifier + let didKeyIdentifier: IIdentifier let storedCredentialHash: string let challenge: string @@ -20,14 +21,15 @@ export default (testContext: { await testContext.setup() agent = testContext.getAgent() challenge = 'TEST_CHALLENGE_STRING' - ethrIdentifier = await agent.didManagerCreate({ kms: 'local', provider: 'did:ethr' }) + didEthrIdentifier = await agent.didManagerCreate({ kms: 'local', provider: 'did:ethr' }) + didKeyIdentifier = await agent.didManagerCreate({ kms: 'local', provider: 'did:key' }) }) afterAll(testContext.tearDown) it('should create verifiable credential in LD', async () => { const verifiableCredential = await agent.createVerifiableCredential({ credential: { - issuer: { id: ethrIdentifier.did }, + issuer: { id: didEthrIdentifier.did }, '@context': ['https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1'], type: ['VerifiableCredential', 'Profile'], issuanceDate: new Date().toISOString(), @@ -42,7 +44,7 @@ export default (testContext: { // Check credential: expect(verifiableCredential).toHaveProperty('proof') expect(verifiableCredential).toHaveProperty('proof.jws') - expect(verifiableCredential.proof.verificationMethod).toEqual(`${ethrIdentifier.did}#controller`) + expect(verifiableCredential.proof.verificationMethod).toEqual(`${didEthrIdentifier.did}#controller`) expect(verifiableCredential['@context']).toEqual([ 'https://www.w3.org/2018/credentials/v1', @@ -118,7 +120,7 @@ export default (testContext: { const verifiablePresentation = await agent.createVerifiablePresentation({ presentation: { - holder: ethrIdentifier.did, + holder: didEthrIdentifier.did, verifier: [], '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation'], @@ -142,7 +144,7 @@ export default (testContext: { const domain = 'TEST_DOMAIN' const verifiablePresentation = await agent.createVerifiablePresentation({ presentation: { - holder: ethrIdentifier.did, + holder: didEthrIdentifier.did, verifier: [], '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation'], @@ -171,7 +173,7 @@ export default (testContext: { const verifiablePresentation = await agent.createVerifiablePresentation({ presentation: { - holder: ethrIdentifier.did, + holder: didEthrIdentifier.did, verifier: [], '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation'], @@ -192,5 +194,54 @@ export default (testContext: { }) expect(typeof parsedMessage.id).toEqual('string') }) + + it('should create verifiable credential in LD with did:key', async () => { + const verifiableCredential = await agent.createVerifiableCredential({ + credential: { + issuer: { id: didKeyIdentifier.did }, + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://veramo.io/contexts/profile/v1' + ], + type: ['VerifiableCredential', 'Profile'], + issuanceDate: new Date().toISOString(), + credentialSubject: { + id: didKeyIdentifier.did, + name: "Martin, the great" + }, + }, + proofFormat: 'lds', + }) + + // Check credential: + expect(verifiableCredential).toHaveProperty('proof') + expect(verifiableCredential).toHaveProperty('proof.jws') + expect(verifiableCredential.proof.verificationMethod).toEqual(`${didKeyIdentifier.did}#${didKeyIdentifier.did.substring(didKeyIdentifier.did.lastIndexOf(':') + 1)}`) + + expect(verifiableCredential['@context']).toEqual([ + 'https://www.w3.org/2018/credentials/v1', + 'https://veramo.io/contexts/profile/v1', + ]) + expect(verifiableCredential['type']).toEqual( + ['VerifiableCredential', 'Profile']) + + storedCredentialHash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) + expect(typeof storedCredentialHash).toEqual('string') + + const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + expect(verifiableCredential).toEqual(verifiableCredential2) + }) + + it('should verify a verifiable credential in LD with did:key', async () => { + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + + const result = await agent.verifyCredential({ + credential: verifiableCredential + }) + + expect(result).toEqual(true) + }) + + }) } diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index 2c0c9b563..491012710 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -15,6 +15,7 @@ }, "dependencies": { "@transmute/credentials-context": "^0.7.0-unstable.26", + "@transmute/ed25519-signature-2018": "^0.7.0-unstable.27", "@transmute/lds-ecdsa-secp256k1-recovery2020": "^0.0.7", "@veramo/core": "^3.1.0", "@veramo/did-jwt": "^3.1.0", diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index 47b92b763..acbecb1e3 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -22,6 +22,7 @@ export { LdSuiteLoader } from './ld-suite-loader' export { VeramoLdSignature, VeramoEcdsaSecp256k1RecoverySignature2020, + VeramoEd25519Signature2018 } from './ld-suites' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/credential-w3c/src/ld-suites.ts b/packages/credential-w3c/src/ld-suites.ts index 75f964565..36c70671e 100644 --- a/packages/credential-w3c/src/ld-suites.ts +++ b/packages/credential-w3c/src/ld-suites.ts @@ -5,6 +5,7 @@ import { EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020, } from '@transmute/lds-ecdsa-secp256k1-recovery2020' +import { Ed25519Signature2018, Ed25519VerificationKey2018 } from '@transmute/ed25519-signature-2018' import * as u8a from 'uint8arrays' import { encodeJoseBlob } from '@veramo/utils' @@ -119,3 +120,74 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature } } } + +export class VeramoEd25519Signature2018 extends VeramoLdSignature { + + getSupportedVerificationType(): string { + return 'Ed25519VerificationKey2018' + } + + getSupportedVeramoKeyType(): TKeyType { + return 'Ed25519' + } + + getSuiteForSigning(key: IKey, identifier: IIdentifier, context: IAgentContext): any { + const controller = identifier.did + + // DID Key ID + let id = `${controller}#controller` + // TODO: Hacky id adjustment + if (controller.startsWith('did:key')) { + id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` + } + + const signer = { + // returns a JWS detached + sign: async (args: { data: Uint8Array }): Promise => { + const header = { + alg: 'EdDSA', + b64: false, + crit: ['b64'], + } + const headerString = encodeJoseBlob(header) + const messageBuffer = u8a.concat([u8a.fromString(`${headerString}.`, 'utf-8'), args.data]) + const messageString = u8a.toString(messageBuffer, 'base64') + const signature = await context.agent.keyManagerSign({ + keyRef: key.kid, + algorithm: 'EdDSA', + data: messageString, + encoding: 'base64', + }) + return `${headerString}..${signature}` + }, + } + + const verificationKey = new Ed25519VerificationKey2018({ + id, + controller, + publicKey: u8a.fromString(key.publicKeyHex, 'base16'), + signer: () => signer, + type: this.getSupportedVerificationType(), + }) + // overwrite the signer since we're not passing the private key and transmute doesn't support that behavior + verificationKey.signer = () => signer as any + + return new Ed25519Signature2018({ + key: verificationKey, + signer: signer + }); + } + + getSuiteForVerification(): any { + return new Ed25519Signature2018(); + } + + preSigningCredModification(credential: Partial): void { + // nothing to do here + } + + preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { + // nothing to do here + } + +} \ No newline at end of file From 7145d2b312b6ee399e367097ca898f3a56d28fa2 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Wed, 17 Nov 2021 19:05:11 +0100 Subject: [PATCH 37/48] refactor: isolate ld-suites to individual files --- packages/credential-w3c/package.json | 1 - .../src/__tests__/action-handler.test.ts | 2 +- packages/credential-w3c/src/index.ts | 6 +- .../credential-w3c/src/ld-default-contexts.ts | 2 +- .../credential-w3c/src/ld-suite-loader.ts | 2 +- packages/credential-w3c/src/ld-suites.ts | 154 ------------------ .../credential-w3c/src/message-handler.ts | 6 +- .../EcdsaSecp256k1RecoverySignature2020.ts | 88 ++++++++++ .../src/suites/Ed25519Signature2018.ts | 77 +++++++++ 9 files changed, 174 insertions(+), 164 deletions(-) create mode 100644 packages/credential-w3c/src/suites/EcdsaSecp256k1RecoverySignature2020.ts create mode 100644 packages/credential-w3c/src/suites/Ed25519Signature2018.ts diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index 491012710..9b6616551 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -26,7 +26,6 @@ "debug": "^4.1.1", "did-jwt-vc": "2.1.7", "did-resolver": "3.1.3", - "ecdsa-secp256k1-signature-2019": "^1.0.1", "jsonld": "^5.2.0", "jsonld-signatures": "^9.0.2", "uint8arrays": "^2.1.3", diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index 073eb4afb..a4a5b9282 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -17,7 +17,7 @@ import { LdContextLoader } from '../ld-context-loader' import { LdDefaultContexts } from '../ld-default-contexts' import { contexts as credential_contexts } from '@transmute/credentials-context' import { LdSuiteLoader } from '../ld-suite-loader' -import { VeramoEcdsaSecp256k1RecoverySignature2020 } from '../ld-suites' +import { VeramoEcdsaSecp256k1RecoverySignature2020 } from '../suites/EcdsaSecp256k1RecoverySignature2020' const mockIdentifiers: IIdentifier[] = [ { diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index acbecb1e3..ba85371c7 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -15,14 +15,14 @@ export { ICreateVerifiablePresentationArgs, ProofFormat, } from './action-handler' -export { LdCredentialModule } from './ld-credential-module' +export { LdCredentialModule } from './ld-credential-module' export { LdContextLoader } from './ld-context-loader' export { LdDefaultContexts } from './ld-default-contexts' export { LdSuiteLoader } from './ld-suite-loader' export { VeramoLdSignature, - VeramoEcdsaSecp256k1RecoverySignature2020, - VeramoEd25519Signature2018 } from './ld-suites' +export * from './suites/EcdsaSecp256k1RecoverySignature2020' +export * from './suites/Ed25519Signature2018' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/credential-w3c/src/ld-default-contexts.ts b/packages/credential-w3c/src/ld-default-contexts.ts index 1a040ab67..731b3661f 100644 --- a/packages/credential-w3c/src/ld-default-contexts.ts +++ b/packages/credential-w3c/src/ld-default-contexts.ts @@ -5,7 +5,7 @@ function _read(_path: string) { return JSON.parse( fs.readFileSync( path.join(__dirname, '../contexts', _path), - {encoding: 'utf8'})); + { encoding: 'utf8' })); } export const LdDefaultContexts = new Map([ diff --git a/packages/credential-w3c/src/ld-suite-loader.ts b/packages/credential-w3c/src/ld-suite-loader.ts index 3f82ce9fd..59c17649a 100644 --- a/packages/credential-w3c/src/ld-suite-loader.ts +++ b/packages/credential-w3c/src/ld-suite-loader.ts @@ -10,7 +10,7 @@ export class LdSuiteLoader { veramoLdSignatures: VeramoLdSignature[] }) { this.signatureMap = options.veramoLdSignatures.reduce((map, obj) => { - map.set(obj.getSupportedVeramoKeyType(), obj); + map.set(obj.getSupportedVeramoKeyType(), obj); return map }, new Map()) } diff --git a/packages/credential-w3c/src/ld-suites.ts b/packages/credential-w3c/src/ld-suites.ts index 36c70671e..efad46330 100644 --- a/packages/credential-w3c/src/ld-suites.ts +++ b/packages/credential-w3c/src/ld-suites.ts @@ -1,13 +1,6 @@ import { IAgentContext, IIdentifier, IKey, IKeyManager, IResolver, TKeyType } from '@veramo/core' import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' import { DIDDocument } from 'did-resolver/src/resolver' -import { - EcdsaSecp256k1RecoveryMethod2020, - EcdsaSecp256k1RecoverySignature2020, -} from '@transmute/lds-ecdsa-secp256k1-recovery2020' -import { Ed25519Signature2018, Ed25519VerificationKey2018 } from '@transmute/ed25519-signature-2018' -import * as u8a from 'uint8arrays' -import { encodeJoseBlob } from '@veramo/utils' export type RequiredAgentMethods = IResolver & Pick @@ -43,151 +36,4 @@ export abstract class VeramoLdSignature { } } -export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature { - getSupportedVerificationType(): string { - return 'EcdsaSecp256k1RecoveryMethod2020' - } - - getSupportedVeramoKeyType(): TKeyType { - return 'Secp256k1' - } - - getSuiteForSigning(key: IKey, identifier: IIdentifier, context: IAgentContext): any { - const controller = identifier.did - - const signer = { - //returns a JWS detached - sign: async (args: { data: Uint8Array }): Promise => { - const header = { - alg: 'ES256K-R', - b64: false, - crit: ['b64'], - } - const headerString = encodeJoseBlob(header) - const messageBuffer = u8a.concat([u8a.fromString(`${headerString}.`, 'utf-8'), args.data]) - const messageString = u8a.toString(messageBuffer, 'base64') - const signature = await context.agent.keyManagerSign({ - keyRef: key.kid, - algorithm: 'ES256K-R', - data: messageString, - encoding: 'base64', - }) - return `${headerString}..${signature}` - }, - } - - return new EcdsaSecp256k1RecoverySignature2020({ - // signer, - key: new EcdsaSecp256k1RecoveryMethod2020({ - publicKeyHex: key.publicKeyHex, - signer: () => signer, - type: this.getSupportedVerificationType(), - controller, - id: `${controller}#controller`, // FIXME: Only default controller verificationMethod supported - }), - }) - } - - getSuiteForVerification(): any { - return new EcdsaSecp256k1RecoverySignature2020() - } - - preSigningCredModification(credential: Partial): void { - if (!Array.isArray(credential['@context'])) { - credential['@context'] = [] - } - credential['@context'].push( - 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', - ) - } - - preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { - // specific resolution modifications - // did:ethr - if (didUrl.toLowerCase().startsWith('did:ethr')) { - didDoc.assertionMethod = [] - // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId - // blockchainAccountId to ethereumAddress - didDoc.verificationMethod?.forEach((x) => { - if (x.blockchainAccountId) { - x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf('@')) - } - - // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." - // @ts-ignore - didDoc.assertionMethod.push(x.id) - }) - } - } -} - -export class VeramoEd25519Signature2018 extends VeramoLdSignature { - - getSupportedVerificationType(): string { - return 'Ed25519VerificationKey2018' - } - - getSupportedVeramoKeyType(): TKeyType { - return 'Ed25519' - } - - getSuiteForSigning(key: IKey, identifier: IIdentifier, context: IAgentContext): any { - const controller = identifier.did - - // DID Key ID - let id = `${controller}#controller` - // TODO: Hacky id adjustment - if (controller.startsWith('did:key')) { - id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` - } - - const signer = { - // returns a JWS detached - sign: async (args: { data: Uint8Array }): Promise => { - const header = { - alg: 'EdDSA', - b64: false, - crit: ['b64'], - } - const headerString = encodeJoseBlob(header) - const messageBuffer = u8a.concat([u8a.fromString(`${headerString}.`, 'utf-8'), args.data]) - const messageString = u8a.toString(messageBuffer, 'base64') - const signature = await context.agent.keyManagerSign({ - keyRef: key.kid, - algorithm: 'EdDSA', - data: messageString, - encoding: 'base64', - }) - return `${headerString}..${signature}` - }, - } - - const verificationKey = new Ed25519VerificationKey2018({ - id, - controller, - publicKey: u8a.fromString(key.publicKeyHex, 'base16'), - signer: () => signer, - type: this.getSupportedVerificationType(), - }) - // overwrite the signer since we're not passing the private key and transmute doesn't support that behavior - verificationKey.signer = () => signer as any - - return new Ed25519Signature2018({ - key: verificationKey, - signer: signer - }); - } - - getSuiteForVerification(): any { - return new Ed25519Signature2018(); - } - - preSigningCredModification(credential: Partial): void { - // nothing to do here - } - - preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { - // nothing to do here - } -} \ No newline at end of file diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index 8d7218fe3..3801f6e88 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -76,7 +76,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { message.credentials = credentials return message - } catch (e) {} + } catch (e) { } try { validateJwtCredentialPayload(data) @@ -96,7 +96,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { message.createdAt = credential.issuanceDate message.credentials = [credential] return message - } catch (e) {} + } catch (e) { } } // LDS Verification and Handling @@ -126,7 +126,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { // throws on error. await context.agent.verifyPresentation({ - presentation , + presentation, // TODO: HARDCODED CHALLENGE VERIFICATION FOR NOW challenge: 'VERAMO', domain: 'VERAMO' diff --git a/packages/credential-w3c/src/suites/EcdsaSecp256k1RecoverySignature2020.ts b/packages/credential-w3c/src/suites/EcdsaSecp256k1RecoverySignature2020.ts new file mode 100644 index 000000000..69a6fb034 --- /dev/null +++ b/packages/credential-w3c/src/suites/EcdsaSecp256k1RecoverySignature2020.ts @@ -0,0 +1,88 @@ +import { RequiredAgentMethods, VeramoLdSignature } from "../ld-suites"; +import { DIDDocument, IAgentContext, IIdentifier, IKey, TKeyType } from "@veramo/core"; +import { + EcdsaSecp256k1RecoveryMethod2020, + EcdsaSecp256k1RecoverySignature2020, +} from '@transmute/lds-ecdsa-secp256k1-recovery2020' +import { CredentialPayload } from 'did-jwt-vc' + +import * as u8a from 'uint8arrays' +import { encodeJoseBlob } from '@veramo/utils' + +export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature { + getSupportedVerificationType(): string { + return 'EcdsaSecp256k1RecoveryMethod2020' + } + + getSupportedVeramoKeyType(): TKeyType { + return 'Secp256k1' + } + + getSuiteForSigning(key: IKey, identifier: IIdentifier, context: IAgentContext): any { + const controller = identifier.did + + const signer = { + //returns a JWS detached + sign: async (args: { data: Uint8Array }): Promise => { + const header = { + alg: 'ES256K-R', + b64: false, + crit: ['b64'], + } + const headerString = encodeJoseBlob(header) + const messageBuffer = u8a.concat([u8a.fromString(`${headerString}.`, 'utf-8'), args.data]) + const messageString = u8a.toString(messageBuffer, 'base64') + const signature = await context.agent.keyManagerSign({ + keyRef: key.kid, + algorithm: 'ES256K-R', + data: messageString, + encoding: 'base64', + }) + return `${headerString}..${signature}` + }, + } + + return new EcdsaSecp256k1RecoverySignature2020({ + // signer, + key: new EcdsaSecp256k1RecoveryMethod2020({ + publicKeyHex: key.publicKeyHex, + signer: () => signer, + type: this.getSupportedVerificationType(), + controller, + id: `${controller}#controller`, // FIXME: Only default controller verificationMethod supported + }), + }) + } + + getSuiteForVerification(): any { + return new EcdsaSecp256k1RecoverySignature2020() + } + + preSigningCredModification(credential: Partial): void { + if (!Array.isArray(credential['@context'])) { + credential['@context'] = [] + } + credential['@context'].push( + 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', + ) + } + + preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { + // specific resolution modifications + // did:ethr + if (didUrl.toLowerCase().startsWith('did:ethr')) { + didDoc.assertionMethod = [] + // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId + // blockchainAccountId to ethereumAddress + didDoc.verificationMethod?.forEach((x) => { + if (x.blockchainAccountId) { + x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf('@')) + } + + // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." + // @ts-ignore + didDoc.assertionMethod.push(x.id) + }) + } + } +} \ No newline at end of file diff --git a/packages/credential-w3c/src/suites/Ed25519Signature2018.ts b/packages/credential-w3c/src/suites/Ed25519Signature2018.ts new file mode 100644 index 000000000..885234ef4 --- /dev/null +++ b/packages/credential-w3c/src/suites/Ed25519Signature2018.ts @@ -0,0 +1,77 @@ +import { encodeJoseBlob } from "@veramo/utils"; +import { RequiredAgentMethods, VeramoLdSignature } from "../ld-suites"; +import { DIDDocument, IAgentContext, IIdentifier, IKey, TKeyType } from "@veramo/core"; +import * as u8a from 'uint8arrays' +import { Ed25519Signature2018, Ed25519VerificationKey2018 } from '@transmute/ed25519-signature-2018' +import { CredentialPayload } from 'did-jwt-vc' + +export class VeramoEd25519Signature2018 extends VeramoLdSignature { + + getSupportedVerificationType(): string { + return 'Ed25519VerificationKey2018' + } + + getSupportedVeramoKeyType(): TKeyType { + return 'Ed25519' + } + + getSuiteForSigning(key: IKey, identifier: IIdentifier, context: IAgentContext): any { + const controller = identifier.did + + // DID Key ID + let id = `${controller}#controller` + // TODO: Hacky id adjustment + if (controller.startsWith('did:key')) { + id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` + } + + const signer = { + // returns a JWS detached + sign: async (args: { data: Uint8Array }): Promise => { + const header = { + alg: 'EdDSA', + b64: false, + crit: ['b64'], + } + const headerString = encodeJoseBlob(header) + const messageBuffer = u8a.concat([u8a.fromString(`${headerString}.`, 'utf-8'), args.data]) + const messageString = u8a.toString(messageBuffer, 'base64') + const signature = await context.agent.keyManagerSign({ + keyRef: key.kid, + algorithm: 'EdDSA', + data: messageString, + encoding: 'base64', + }) + return `${headerString}..${signature}` + }, + } + + const verificationKey = new Ed25519VerificationKey2018({ + id, + controller, + publicKey: u8a.fromString(key.publicKeyHex, 'base16'), + signer: () => signer, + type: this.getSupportedVerificationType(), + }) + // overwrite the signer since we're not passing the private key and transmute doesn't support that behavior + verificationKey.signer = () => signer as any + + return new Ed25519Signature2018({ + key: verificationKey, + signer: signer + }); + } + + getSuiteForVerification(): any { + return new Ed25519Signature2018(); + } + + preSigningCredModification(credential: Partial): void { + // nothing to do here + } + + preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { + // nothing to do here + } + +} \ No newline at end of file From 4f55926608b080f2665b595ef6e0d9434a7b6119 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Thu, 18 Nov 2021 14:20:41 +0100 Subject: [PATCH 38/48] refactor(credential-ld): isolate credential-ld as individual package --- __tests__/localAgent.test.ts | 57 +-- __tests__/localMemoryStoreAgent.test.ts | 53 ++- __tests__/restAgent.test.ts | 37 +- packages/credential-ld/CHANGELOG.md | 81 ++++ packages/credential-ld/LICENSE | 201 ++++++++ packages/credential-ld/README.md | 6 + packages/credential-ld/api-extractor.json | 18 + .../contexts/X25519KeyAgreementKey2019.jsonld | 26 ++ .../credential-ld/contexts/did_v0.11.jsonld | 58 +++ .../contexts/ed25519-signature-2018-v1.jsonld | 91 ++++ packages/credential-ld/contexts/kyc-v1.jsonld | 8 + ...ds-ecdsa-secp256k1-recovery2020-0.0.jsonld | 60 +++ .../credential-ld/contexts/profile-v1.jsonld | 8 + .../contexts/security_context_v1.jsonld | 58 +++ .../contexts/socialmedia-v1.jsonld | 7 + .../contexts/transmute_v1.jsonld | 21 + packages/credential-ld/package.json | 50 ++ packages/credential-ld/plugin.schema.json | 441 ++++++++++++++++++ packages/credential-ld/src/action-handler.ts | 438 +++++++++++++++++ packages/credential-ld/src/index.ts | 24 + .../src/ld-context-loader.ts | 0 .../src/ld-credential-module.ts | 0 .../src/ld-default-contexts.ts | 0 .../src/ld-suite-loader.ts | 0 .../src/ld-suites.ts | 0 .../EcdsaSecp256k1RecoverySignature2020.ts | 0 .../src/suites/Ed25519Signature2018.ts | 0 packages/credential-ld/tsconfig.json | 15 + .../credential-ld/types/blakejs/index.d.ts | 1 + .../types/jsonld/index.d.ts | 0 packages/credential-w3c/package.json | 11 +- .../src/__tests__/action-handler.test.ts | 21 +- packages/credential-w3c/src/action-handler.ts | 158 ++++--- packages/credential-w3c/src/index.ts | 9 - .../credential-w3c/src/message-handler.ts | 3 +- ...torage.ts => 3.createPrivateKeyStorage.ts} | 2 +- .../migrations/4.allowNullVPIssuanceDate.ts | 81 ++++ packages/data-store/src/migrations/index.ts | 5 +- packages/tsconfig.json | 1 + 39 files changed, 1873 insertions(+), 177 deletions(-) create mode 100644 packages/credential-ld/CHANGELOG.md create mode 100644 packages/credential-ld/LICENSE create mode 100644 packages/credential-ld/README.md create mode 100644 packages/credential-ld/api-extractor.json create mode 100644 packages/credential-ld/contexts/X25519KeyAgreementKey2019.jsonld create mode 100644 packages/credential-ld/contexts/did_v0.11.jsonld create mode 100644 packages/credential-ld/contexts/ed25519-signature-2018-v1.jsonld create mode 100644 packages/credential-ld/contexts/kyc-v1.jsonld create mode 100644 packages/credential-ld/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld create mode 100644 packages/credential-ld/contexts/profile-v1.jsonld create mode 100644 packages/credential-ld/contexts/security_context_v1.jsonld create mode 100644 packages/credential-ld/contexts/socialmedia-v1.jsonld create mode 100644 packages/credential-ld/contexts/transmute_v1.jsonld create mode 100644 packages/credential-ld/package.json create mode 100644 packages/credential-ld/plugin.schema.json create mode 100644 packages/credential-ld/src/action-handler.ts create mode 100644 packages/credential-ld/src/index.ts rename packages/{credential-w3c => credential-ld}/src/ld-context-loader.ts (100%) rename packages/{credential-w3c => credential-ld}/src/ld-credential-module.ts (100%) rename packages/{credential-w3c => credential-ld}/src/ld-default-contexts.ts (100%) rename packages/{credential-w3c => credential-ld}/src/ld-suite-loader.ts (100%) rename packages/{credential-w3c => credential-ld}/src/ld-suites.ts (100%) rename packages/{credential-w3c => credential-ld}/src/suites/EcdsaSecp256k1RecoverySignature2020.ts (100%) rename packages/{credential-w3c => credential-ld}/src/suites/Ed25519Signature2018.ts (100%) create mode 100644 packages/credential-ld/tsconfig.json create mode 100644 packages/credential-ld/types/blakejs/index.d.ts rename packages/{credential-w3c => credential-ld}/types/jsonld/index.d.ts (100%) rename packages/data-store/src/migrations/{createPrivateKeyStorage.ts => 3.createPrivateKeyStorage.ts} (98%) create mode 100644 packages/data-store/src/migrations/4.allowNullVPIssuanceDate.ts diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 8089c4a24..6b86d1126 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -24,13 +24,17 @@ import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, +} from '../packages/credential-w3c/src' +import { + CredentialIssuerLD, + ICredentialIssuerLD, LdCredentialModule, LdContextLoader, LdDefaultContexts, LdSuiteLoader, VeramoEcdsaSecp256k1RecoverySignature2020, - VeramoEd25519Signature2018, -} from '../packages/credential-w3c/src' + VeramoEd25519Signature2018 +} from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider } from '../packages/did-provider-key/src' @@ -86,18 +90,17 @@ import didCommWithEthrDidFlow from './shared/didCommWithEthrDidFlow' const infuraProjectId = '3586660d179141e3801c3895de1c2eba' const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c' -let agent: TAgent< - IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialIssuer & - ISelectiveDisclosure & - IDIDDiscovery -> +let agent: TAgent let dbConnection: Promise let databaseFile: string @@ -118,18 +121,17 @@ const setup = async (options?: IAgentOptions): Promise => { const { provider, registry } = await createGanacheProvider() - agent = createAgent< - IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialIssuer & - ISelectiveDisclosure & - IDIDDiscovery - >({ + agent = createAgent({ ...options, context: { // authenticatedDid: 'did:example:3456' @@ -209,7 +211,8 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm([new DIDCommHttpTransport()]), - new CredentialIssuer({ + new CredentialIssuer(), + new CredentialIssuerLD({ ldCredentialModule: new LdCredentialModule({ ldContextLoader: new LdContextLoader({ contextsPaths: [LdDefaultContexts, credential_contexts as Map], diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 170882761..ea187ca3d 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -1,7 +1,7 @@ /** * This runs a suite of ./shared tests using an agent configured for local operations, * using a SQLite db for storage of credentials and an in-memory store for keys and DIDs. - * + * */ import { createAgent, @@ -23,13 +23,17 @@ import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, +} from '../packages/credential-w3c/src' +import { + CredentialIssuerLD, + ICredentialIssuerLD, LdCredentialModule, LdContextLoader, LdDefaultContexts, LdSuiteLoader, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018 -} from '../packages/credential-w3c/src' +} from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider, getDidKeyResolver } from '../packages/did-provider-key/src' @@ -67,17 +71,16 @@ import messageHandler from './shared/messageHandler' const databaseFile = `./tmp/local-database2-${Math.random().toPrecision(5)}.sqlite` const infuraProjectId = '3586660d179141e3801c3895de1c2eba' -let agent: TAgent< - IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialIssuer & - ISelectiveDisclosure -> +let agent: TAgent let dbConnection: Promise const setup = async (options?: IAgentOptions): Promise => { @@ -92,17 +95,16 @@ const setup = async (options?: IAgentOptions): Promise => { entities: Entities, }) - agent = createAgent< - IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialIssuer & - ISelectiveDisclosure - >({ + agent = createAgent({ ...options, context: { // authenticatedDid: 'did:example:3456' @@ -166,7 +168,8 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm(), - new CredentialIssuer({ + new CredentialIssuer(), + new CredentialIssuerLD({ ldCredentialModule: new LdCredentialModule({ ldContextLoader: new LdContextLoader({ contextsPaths: [LdDefaultContexts, credential_contexts as Map], diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 489a040a5..632f1a9e4 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -1,7 +1,7 @@ /** * This runs a suite of ./shared tests using an agent configured for remote operations. * There is a local agent that only uses @veramo/remove-client and a remote agent that provides the actual functionality. - * + * * This suite also runs a messaging server to run through some examples of DIDComm using did:fake identifiers. * See didWithFakeDidFlow() for more details. */ @@ -27,12 +27,17 @@ import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, +} from '../packages/credential-w3c/src' +import { + CredentialIssuerLD, + ICredentialIssuerLD, LdCredentialModule, LdContextLoader, LdDefaultContexts, LdSuiteLoader, - VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018, -} from '../packages/credential-w3c/src' + VeramoEcdsaSecp256k1RecoverySignature2020, + VeramoEd25519Signature2018 +} from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { KeyDIDProvider, getDidKeyResolver } from '../packages/did-provider-key/src' @@ -95,18 +100,17 @@ let serverAgent: IAgent let restServer: Server const getAgent = (options?: IAgentOptions) => - createAgent< - IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialIssuer & - ISelectiveDisclosure & - IDIDDiscovery - >({ + createAgent({ ...options, plugins: [ new AgentRestClient({ @@ -191,7 +195,8 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm([new DIDCommHttpTransport()]), - new CredentialIssuer({ + new CredentialIssuer(), + new CredentialIssuerLD({ ldCredentialModule: new LdCredentialModule({ ldContextLoader: new LdContextLoader({ contextsPaths: [LdDefaultContexts, credential_contexts as Map], diff --git a/packages/credential-ld/CHANGELOG.md b/packages/credential-ld/CHANGELOG.md new file mode 100644 index 000000000..637962f7a --- /dev/null +++ b/packages/credential-ld/CHANGELOG.md @@ -0,0 +1,81 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +# [3.1.0](https://github.com/uport-project/veramo/compare/v3.0.0...v3.1.0) (2021-11-12) + + +### Bug Fixes + +* **deps:** update did-libraries ([0ea73fc](https://github.com/uport-project/veramo/commit/0ea73fc1dba02c3d4c4df5befef265f7f573b2d1)) +* **deps:** update did-libraries ([417dc5d](https://github.com/uport-project/veramo/commit/417dc5dd157ee259b6f89f4987f1ecca444fb1d4)) + + + + + +# [3.0.0](https://github.com/uport-project/veramo/compare/v2.1.3...v3.0.0) (2021-09-20) + + +### Bug Fixes + +* **deps:** update all non-major dependencies ([8fc5312](https://github.com/uport-project/veramo/commit/8fc53120498ce2982e8ec640e00bbb03f6f4204e)) + + + + + +# [2.1.0](https://github.com/uport-project/veramo/compare/v2.0.1...v2.1.0) (2021-08-11) + + +### Bug Fixes + +* **credentials-w3c:** accept Presentations without Credentials ([#616](https://github.com/uport-project/veramo/issues/616)) ([2389cd0](https://github.com/uport-project/veramo/commit/2389cd0df080e968ee320d66fabf2e8a7b51ba47)) + + + + + +# [2.0.0](https://github.com/uport-project/veramo/compare/v1.2.2...v2.0.0) (2021-07-14) + + +### Bug Fixes + +* **credential-w3c:** fixed handling of Ed25519 keys when creating VPs ([#534](https://github.com/uport-project/veramo/issues/534))([#516](https://github.com/uport-project/veramo/issues/516)) ([988c76c](https://github.com/uport-project/veramo/commit/988c76c46d391f3b76499ff141bdefe21e729c4a)) +* **deps:** bump did-jwt to 5.4.0 ([#528](https://github.com/uport-project/veramo/issues/528)) ([65f22cf](https://github.com/uport-project/veramo/commit/65f22cf6dcca48b5bb35331894536a2a567a1189)) + + +### Features + +* implement didcomm v2 packing/unpacking ([#575](https://github.com/uport-project/veramo/issues/575)) ([249b07e](https://github.com/uport-project/veramo/commit/249b07eca8d2de9eb5252d71683d5f1fba319d60)), closes [#559](https://github.com/uport-project/veramo/issues/559) [#558](https://github.com/uport-project/veramo/issues/558) +* **key-manager:** add generic signing capabilities ([#529](https://github.com/uport-project/veramo/issues/529)) ([5f10a1b](https://github.com/uport-project/veramo/commit/5f10a1bcea214cb593de12fa6ec3a91b3cb712bb)), closes [#522](https://github.com/uport-project/veramo/issues/522) + + + + + +# [1.2.0](https://github.com/uport-project/veramo/compare/v1.1.2...v1.2.0) (2021-04-27) + + +### Features + +* adapt to did core spec ([#430](https://github.com/uport-project/veramo/issues/430)) ([9712db0](https://github.com/uport-project/veramo/commit/9712db0eea1a3f48cf0665d66ae715ea0c23cd4a)), closes [#418](https://github.com/uport-project/veramo/issues/418) [#428](https://github.com/uport-project/veramo/issues/428) [#417](https://github.com/uport-project/veramo/issues/417) [#416](https://github.com/uport-project/veramo/issues/416) [#412](https://github.com/uport-project/veramo/issues/412) [#397](https://github.com/uport-project/veramo/issues/397) [#384](https://github.com/uport-project/veramo/issues/384) [#394](https://github.com/uport-project/veramo/issues/394) +* add option to keep payload fields when creating JWT VC/VP ([#431](https://github.com/uport-project/veramo/issues/431)) ([43923e1](https://github.com/uport-project/veramo/commit/43923e18b8e0b68c4552489d568ab16748156970)), closes [#394](https://github.com/uport-project/veramo/issues/394) +* **did-provider-key:** add did:key provider; fixes [#335](https://github.com/uport-project/veramo/issues/335) ([#351](https://github.com/uport-project/veramo/issues/351)) ([42cd2b0](https://github.com/uport-project/veramo/commit/42cd2b08a2fd21b5b5d7bdfa57dd00ccc7184dc7)), closes [decentralized-identity/did-jwt#78](https://github.com/decentralized-identity/did-jwt/issues/78) + + + + + +# [1.1.0](https://github.com/uport-project/veramo/compare/v1.0.1...v1.1.0) (2021-01-26) + +**Note:** Version bump only for package @veramo/credential-w3c + + + + + +## 1.0.1 (2020-12-18) + +**Note:** Version bump only for package @veramo/credential-w3c diff --git a/packages/credential-ld/LICENSE b/packages/credential-ld/LICENSE new file mode 100644 index 000000000..fd815d7f8 --- /dev/null +++ b/packages/credential-ld/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2019 Consensys AG + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/credential-ld/README.md b/packages/credential-ld/README.md new file mode 100644 index 000000000..d03061eba --- /dev/null +++ b/packages/credential-ld/README.md @@ -0,0 +1,6 @@ +# Veramo JSON-LD credentials plugin + +Veramo package for working with W3C JSON-LD Verifiable Credentials & Presentations. + +This package contains a plugin and a message handler for issuing and verifying Credentials +and Presentations that adhere to W3C standards. diff --git a/packages/credential-ld/api-extractor.json b/packages/credential-ld/api-extractor.json new file mode 100644 index 000000000..409d7f16c --- /dev/null +++ b/packages/credential-ld/api-extractor.json @@ -0,0 +1,18 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "apiReport": { + "enabled": true, + "reportFolder": "./api", + "reportTempFolder": "./api" + }, + + "docModel": { + "enabled": true, + "apiJsonFilePath": "./api/.api.json" + }, + + "dtsRollup": { + "enabled": false + }, + "mainEntryPointFilePath": "/build/index.d.ts" +} diff --git a/packages/credential-ld/contexts/X25519KeyAgreementKey2019.jsonld b/packages/credential-ld/contexts/X25519KeyAgreementKey2019.jsonld new file mode 100644 index 000000000..d01bac010 --- /dev/null +++ b/packages/credential-ld/contexts/X25519KeyAgreementKey2019.jsonld @@ -0,0 +1,26 @@ +{ + "@context": { + "id": "@id", + "type": "@type", + "@protected": true, + "X25519KeyAgreementKey2019": { + "@id": "https://w3id.org/security#X25519KeyAgreementKey2019", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "controller": { + "@id": "https://w3id.org/security#controller", + "@type": "@id" + }, + "revoked": { + "@id": "https://w3id.org/security#revoked", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "publicKeyBase58": { + "@id": "https://w3id.org/security#publicKeyBase58" + } + } + } + } +} diff --git a/packages/credential-ld/contexts/did_v0.11.jsonld b/packages/credential-ld/contexts/did_v0.11.jsonld new file mode 100644 index 000000000..646c00be2 --- /dev/null +++ b/packages/credential-ld/contexts/did_v0.11.jsonld @@ -0,0 +1,58 @@ +{ + "@context": { + "@version": 1.1, + "id": "@id", + "type": "@type", + + "dc": "http://purl.org/dc/terms/", + "schema": "http://schema.org/", + "sec": "https://w3id.org/security#", + "didv": "https://w3id.org/did#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "EcdsaSecp256k1Signature2019": "sec:EcdsaSecp256k1Signature2019", + "EcdsaSecp256k1VerificationKey2019": "sec:EcdsaSecp256k1VerificationKey2019", + "Ed25519Signature2018": "sec:Ed25519Signature2018", + "Ed25519VerificationKey2018": "sec:Ed25519VerificationKey2018", + "RsaSignature2018": "sec:RsaSignature2018", + "RsaVerificationKey2018": "sec:RsaVerificationKey2018", + "SchnorrSecp256k1Signature2019": "sec:SchnorrSecp256k1Signature2019", + "SchnorrSecp256k1VerificationKey2019": "sec:SchnorrSecp256k1VerificationKey2019", + "ServiceEndpointProxyService": "didv:ServiceEndpointProxyService", + + "allowedAction": "sec:allowedAction", + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"}, + "capability": {"@id": "sec:capability", "@type": "@id"}, + "capabilityAction": "sec:capabilityAction", + "capabilityChain": {"@id": "sec:capabilityChain", "@type": "@id", "@container": "@list"}, + "capabilityDelegation": {"@id": "sec:capabilityDelegationMethod", "@type": "@id", "@container": "@set"}, + "capabilityInvocation": {"@id": "sec:capabilityInvocationMethod", "@type": "@id", "@container": "@set"}, + "capabilityStatusList": {"@id": "sec:capabilityStatusList", "@type": "@id"}, + "canonicalizationAlgorithm": "sec:canonicalizationAlgorithm", + "caveat": {"@id": "sec:caveat", "@type": "@id", "@container": "@set"}, + "challenge": "sec:challenge", + "controller": {"@id": "sec:controller", "@type": "@id"}, + "created": {"@id": "dc:created", "@type": "xsd:dateTime"}, + "creator": {"@id": "dc:creator", "@type": "@id"}, + "delegator": {"@id": "sec:delegator", "@type": "@id"}, + "domain": "sec:domain", + "expirationDate": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "invocationTarget": {"@id": "sec:invocationTarget", "@type": "@id"}, + "invoker": {"@id": "sec:invoker", "@type": "@id"}, + "jws": "sec:jws", + "keyAgreement": {"@id": "sec:keyAgreementMethod", "@type": "@id", "@container": "@set"}, + "nonce": "sec:nonce", + "owner": {"@id": "sec:owner", "@type": "@id"}, + "proof": {"@id": "sec:proof", "@type": "@id", "@container": "@graph"}, + "proofPurpose": {"@id": "sec:proofPurpose", "@type": "@vocab"}, + "proofValue": "sec:proofValue", + "publicKey": {"@id": "sec:publicKey", "@type": "@id", "@container": "@set"}, + "publicKeyBase58": "sec:publicKeyBase58", + "publicKeyPem": "sec:publicKeyPem", + "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"}, + "service": {"@id": "didv:service", "@type": "@id", "@container": "@set"}, + "serviceEndpoint": {"@id": "didv:serviceEndpoint", "@type": "@id"}, + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } +} diff --git a/packages/credential-ld/contexts/ed25519-signature-2018-v1.jsonld b/packages/credential-ld/contexts/ed25519-signature-2018-v1.jsonld new file mode 100644 index 000000000..6533c287e --- /dev/null +++ b/packages/credential-ld/contexts/ed25519-signature-2018-v1.jsonld @@ -0,0 +1,91 @@ +{ + "@context": { + "id": "@id", + "type": "@type", + "@protected": true, + "proof": { + "@id": "https://w3id.org/security#proof", + "@type": "@id", + "@container": "@graph" + }, + "Ed25519VerificationKey2018": { + "@id": "https://w3id.org/security#Ed25519VerificationKey2018", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "controller": { + "@id": "https://w3id.org/security#controller", + "@type": "@id" + }, + "revoked": { + "@id": "https://w3id.org/security#revoked", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "publicKeyBase58": { + "@id": "https://w3id.org/security#publicKeyBase58" + } + } + }, + "Ed25519Signature2018": { + "@id": "https://w3id.org/security#Ed25519Signature2018", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "challenge": "https://w3id.org/security#challenge", + "created": { + "@id": "http://purl.org/dc/terms/created", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "domain": "https://w3id.org/security#domain", + "expires": { + "@id": "https://w3id.org/security#expiration", + "@type": "http://www.w3.org/2001/XMLSchema#dateTime" + }, + "nonce": "https://w3id.org/security#nonce", + "proofPurpose": { + "@id": "https://w3id.org/security#proofPurpose", + "@type": "@vocab", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + } + } + }, + "jws": { + "@id": "https://w3id.org/security#jws" + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } + } + } +} diff --git a/packages/credential-ld/contexts/kyc-v1.jsonld b/packages/credential-ld/contexts/kyc-v1.jsonld new file mode 100644 index 000000000..306d0174e --- /dev/null +++ b/packages/credential-ld/contexts/kyc-v1.jsonld @@ -0,0 +1,8 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "VerifiableKyc": "https://veramo.io/contexts/kyc#VerifiableKyc", + "name": "https://schema.org/name" + } +} diff --git a/packages/credential-ld/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld b/packages/credential-ld/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld new file mode 100644 index 000000000..c79835e3c --- /dev/null +++ b/packages/credential-ld/contexts/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld @@ -0,0 +1,60 @@ +{ + "@context": { + "@version": 1.1, + "id": "@id", + "type": "@type", + "esrs2020": "https://identity.foundation/EcdsaSecp256k1RecoverySignature2020#", + + "EcdsaSecp256k1RecoverySignature2020": { + "@id": "https://w3id.org/security#EcdsaSecp256k1RecoverySignature2020", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + + "challenge": "sec:challenge", + "created": {"@id": "http://purl.org/dc/terms/created", "@type": "xsd:dateTime"}, + "domain": "sec:domain", + "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"}, + "jws": "sec:jws", + "nonce": "sec:nonce", + "proofPurpose": { + "@id": "sec:proofPurpose", + "@type": "@vocab", + "@context": { + "@version": 1.1, + "@protected": true, + + "id": "@id", + "type": "@type", + + "sec": "https://w3id.org/security#", + + "assertionMethod": {"@id": "sec:assertionMethod", "@type": "@id", "@container": "@set"}, + "authentication": {"@id": "sec:authenticationMethod", "@type": "@id", "@container": "@set"} + } + }, + "proofValue": "sec:proofValue", + "verificationMethod": {"@id": "sec:verificationMethod", "@type": "@id"} + } + }, + + "EcdsaSecp256k1RecoveryMethod2020": "esrs2020:EcdsaSecp256k1RecoveryMethod2020", + "publicKeyJwk": { + "@id": "esrs2020:publicKeyJwk", + "@type": "@json" + }, + "privateKeyJwk": { + "@id": "esrs2020:privateKeyJwk", + "@type": "@json" + }, + "publicKeyHex": "esrs2020:publicKeyHex", + "privateKeyHex": "esrs2020:privateKeyHex", + "ethereumAddress": "esrs2020:ethereumAddress" + } +} diff --git a/packages/credential-ld/contexts/profile-v1.jsonld b/packages/credential-ld/contexts/profile-v1.jsonld new file mode 100644 index 000000000..925515f8a --- /dev/null +++ b/packages/credential-ld/contexts/profile-v1.jsonld @@ -0,0 +1,8 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "Profile": "https://veramo.io/contexts/profile#Profile", + "name": "https://schema.org/name" + } +} diff --git a/packages/credential-ld/contexts/security_context_v1.jsonld b/packages/credential-ld/contexts/security_context_v1.jsonld new file mode 100644 index 000000000..b447d0108 --- /dev/null +++ b/packages/credential-ld/contexts/security_context_v1.jsonld @@ -0,0 +1,58 @@ +{ + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + + "alsoKnownAs": { + "@id": "https://www.w3.org/ns/activitystreams#alsoKnownAs", + "@type": "@id" + }, + "assertionMethod": { + "@id": "https://w3id.org/security#assertionMethod", + "@type": "@id", + "@container": "@set" + }, + "authentication": { + "@id": "https://w3id.org/security#authenticationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityDelegation": { + "@id": "https://w3id.org/security#capabilityDelegationMethod", + "@type": "@id", + "@container": "@set" + }, + "capabilityInvocation": { + "@id": "https://w3id.org/security#capabilityInvocationMethod", + "@type": "@id", + "@container": "@set" + }, + "controller": { + "@id": "https://w3id.org/security#controller", + "@type": "@id" + }, + "keyAgreement": { + "@id": "https://w3id.org/security#keyAgreementMethod", + "@type": "@id", + "@container": "@set" + }, + "service": { + "@id": "https://www.w3.org/ns/did#service", + "@type": "@id", + "@context": { + "@protected": true, + "id": "@id", + "type": "@type", + "serviceEndpoint": { + "@id": "https://www.w3.org/ns/did#serviceEndpoint", + "@type": "@id" + } + } + }, + "verificationMethod": { + "@id": "https://w3id.org/security#verificationMethod", + "@type": "@id" + } + } +} diff --git a/packages/credential-ld/contexts/socialmedia-v1.jsonld b/packages/credential-ld/contexts/socialmedia-v1.jsonld new file mode 100644 index 000000000..50e0ed8c4 --- /dev/null +++ b/packages/credential-ld/contexts/socialmedia-v1.jsonld @@ -0,0 +1,7 @@ +{ + "@context": { + "@version": 1.1, + "@protected": true, + "VerifableSocialMediaPosting": "https://veramo.io/contexts/socialmedia#VerifableSocialMediaPosting" + } +} diff --git a/packages/credential-ld/contexts/transmute_v1.jsonld b/packages/credential-ld/contexts/transmute_v1.jsonld new file mode 100644 index 000000000..e51c050ad --- /dev/null +++ b/packages/credential-ld/contexts/transmute_v1.jsonld @@ -0,0 +1,21 @@ +{ + "@context": [ + { + "@version": 1.1 + }, + "https://www.w3.org/ns/did/v1", + { + "JsonWebKey2020": "https://w3id.org/security#JsonWebKey2020", + "Ed25519VerificationKey2018": "https://w3id.org/security#Ed25519VerificationKey2018", + "X25519KeyAgreementKey2019": "https://w3id.org/security#X25519KeyAgreementKey2019", + + "publicKeyJwk": { + "@id": "https://w3id.org/security#publicKeyJwk", + "@type": "@json" + }, + "publicKeyBase58": { + "@id": "https://w3id.org/security#publicKeyBase58" + } + } + ] +} diff --git a/packages/credential-ld/package.json b/packages/credential-ld/package.json new file mode 100644 index 000000000..09a2657f5 --- /dev/null +++ b/packages/credential-ld/package.json @@ -0,0 +1,50 @@ +{ + "name": "@veramo/credential-ld", + "description": "Veramo plugin for working with W3C JSON-LD Verifiable Credentials & Presentations.", + "version": "3.1.0", + "main": "build/index.js", + "types": "build/index.d.ts", + "scripts": { + "build": "tsc", + "generate-plugin-schema": "yarn veramo dev generate-plugin-schema" + }, + "veramo": { + "pluginInterfaces": { + "ICredentialIssuerLD": "./src/action-handler.ts" + } + }, + "dependencies": { + "@transmute/credentials-context": "^0.7.0-unstable.26", + "@transmute/ed25519-signature-2018": "^0.7.0-unstable.27", + "@transmute/lds-ecdsa-secp256k1-recovery2020": "^0.0.7", + "@veramo/core": "^3.1.0", + "@veramo/did-resolver": "^3.1.0", + "@veramo/utils": "^3.1.0", + "blakejs": "^1.1.0", + "debug": "^4.1.1", + "did-resolver": "3.1.3", + "jsonld": "^5.2.0", + "jsonld-signatures": "^9.0.2", + "uint8arrays": "^2.1.3", + "vc-js": "^0.6.4" + }, + "devDependencies": { + "@types/debug": "4.1.7", + "borc": "^3.0.0", + "typescript": "4.4.4" + }, + "files": [ + "build/**/*", + "src/**/*", + "plugin.schema.json", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "repository": "git@github.com:uport-project/veramo.git", + "author": "Mircea Nistor ", + "license": "Apache-2.0", + "keywords": [] +} diff --git a/packages/credential-ld/plugin.schema.json b/packages/credential-ld/plugin.schema.json new file mode 100644 index 000000000..e87b19d84 --- /dev/null +++ b/packages/credential-ld/plugin.schema.json @@ -0,0 +1,441 @@ +{ + "ICredentialIssuerLD": { + "components": { + "schemas": { + "ICreateVerifiableCredentialArgs": { + "type": "object", + "properties": { + "credential": { + "type": "object", + "properties": { + "@context": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "id": { + "type": "string" + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "issuanceDate": { + "$ref": "#/components/schemas/DateType" + }, + "expirationDate": { + "$ref": "#/components/schemas/DateType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" + }, + "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + } + }, + "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" + }, + "save": { + "type": "boolean", + "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved" + }, + "proofFormat": { + "$ref": "#/components/schemas/ProofFormat", + "description": "The desired format for the VerifiablePresentation to be created." + }, + "removeOriginalFields": { + "type": "boolean", + "description": "Remove payload members during JWT-JSON transformation. Defaults to `true`. See https://www.w3.org/TR/vc-data-model/#jwt-encoding" + } + }, + "required": [ + "credential", + "proofFormat" + ], + "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" + }, + "IssuerType": { + "anyOf": [ + { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + { + "type": "string" + } + ] + }, + "DateType": { + "type": "string" + }, + "CredentialSubject": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "CredentialStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ] + }, + "ProofFormat": { + "type": "string", + "enum": [ + "jwt", + "lds" + ], + "description": "The type of encoding to be used for the Verifiable Credential or Presentation to be generated.\n\nOnly `jwt` and `lds` is supported at the moment." + }, + "VerifiableCredential": { + "type": "object", + "properties": { + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "issuer": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "required": [ + "id" + ] + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "credentialSubject": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "credentialStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "required": [ + "id", + "type" + ] + }, + "proof": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } + } + }, + "required": [ + "@context", + "credentialSubject", + "issuanceDate", + "issuer", + "proof", + "type" + ], + "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + }, + "ICreateVerifiablePresentationArgs": { + "type": "object", + "properties": { + "presentation": { + "type": "object", + "properties": { + "@context": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "type": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "id": { + "type": "string" + }, + "verifiableCredential": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/JWT" + } + ] + } + }, + "holder": { + "type": "string" + }, + "verifier": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + } + }, + "description": "The json payload of the Presentation according to the\n {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model } .\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" + }, + "save": { + "type": "boolean", + "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved" + }, + "challenge": { + "type": "string", + "description": "Optional (only JWT) string challenge parameter to add to the verifiable presentation." + }, + "domain": { + "type": "string", + "description": "Optional string domain parameter to add to the verifiable presentation." + }, + "proofFormat": { + "$ref": "#/components/schemas/ProofFormat", + "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" + }, + "removeOriginalFields": { + "type": "boolean", + "description": "Remove payload members during JWT-JSON transformation. Defaults to `true`. See https://www.w3.org/TR/vc-data-model/#jwt-encoding" + } + }, + "required": [ + "presentation", + "proofFormat" + ], + "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" + }, + "JWT": { + "type": "string" + }, + "VerifiablePresentation": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "holder": { + "type": "string" + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "verifier": { + "type": "array", + "items": { + "type": "string" + } + }, + "verifiableCredential": { + "type": "array", + "items": { + "$ref": "#/components/schemas/VerifiableCredential" + } + }, + "proof": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + } + } + }, + "required": [ + "@context", + "holder", + "proof", + "type", + "verifier" + ], + "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + }, + "IVerifyCredentialArgs": { + "type": "object", + "properties": { + "credential": { + "$ref": "#/components/schemas/VerifiableCredential", + "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" + } + }, + "required": [ + "credential" + ], + "description": "Encapsulates the parameters required to verify a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" + }, + "IVerifyPresentationArgs": { + "type": "object", + "properties": { + "presentation": { + "$ref": "#/components/schemas/VerifiablePresentation", + "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" + }, + "challenge": { + "type": "string", + "description": "Optional (only for JWT) string challenge parameter to verify the verifiable presentation against" + }, + "domain": { + "type": "string", + "description": "Optional (only for JWT) string domain parameter to verify the verifiable presentation against" + } + }, + "required": [ + "presentation" + ], + "description": "Encapsulates the parameters required to verify a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" + } + }, + "methods": { + "createVerifiableCredentialLD": { + "description": "Creates a Verifiable Credential. The payload, signer and format are chosen based on the ", + "arguments": { + "$ref": "#/components/schemas/ICreateVerifiableCredentialArgs" + }, + "returnType": { + "$ref": "#/components/schemas/VerifiableCredential" + } + }, + "createVerifiablePresentationLD": { + "description": "Creates a Verifiable Presentation. The payload, signer and format are chosen based on the ", + "arguments": { + "$ref": "#/components/schemas/ICreateVerifiablePresentationArgs" + }, + "returnType": { + "$ref": "#/components/schemas/VerifiablePresentation" + } + }, + "verifyCredentialLD": { + "description": "Verifies a Verifiable Credential JWT or LDS Format.", + "arguments": { + "$ref": "#/components/schemas/IVerifyCredentialArgs" + }, + "returnType": { + "type": "boolean" + } + }, + "verifyPresentationLD": { + "description": "Verifies a Verifiable Presentation JWT or LDS Format.", + "arguments": { + "$ref": "#/components/schemas/IVerifyPresentationArgs" + }, + "returnType": { + "type": "boolean" + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/credential-ld/src/action-handler.ts b/packages/credential-ld/src/action-handler.ts new file mode 100644 index 000000000..e05d2d40b --- /dev/null +++ b/packages/credential-ld/src/action-handler.ts @@ -0,0 +1,438 @@ +import { + IAgentContext, + IAgentPlugin, + IResolver, + IDIDManager, + IKeyManager, + IPluginMethodMap, + IDataStore, + IKey, + IIdentifier, VerifiableCredential, VerifiablePresentation, W3CPresentation, +} from '@veramo/core' + +import { LdCredentialModule, schema } from './' +import Debug from 'debug' + +const debug = Debug('veramo:w3c:action-handler') + +export type JWT = string +export type IssuerType = { id: string, [x: string]: any } | string +export type CredentialSubject = { id?: string, [x: string]: any } + +export interface CredentialStatus { + id: string + type: string + + [x: string]: any +} + +export type DateType = string | Date + +/** + * used as input when creating Verifiable Credentials + */ +export interface CredentialPayload { + '@context': string | string[] + id?: string + type: string | string[] + issuer: IssuerType + issuanceDate: DateType + expirationDate?: DateType + credentialSubject: CredentialSubject + credentialStatus?: CredentialStatus + + [x: string]: any +} + +/** + * used as input when creating Verifiable Presentations + */ +export interface PresentationPayload { + '@context': string | string[] + type: string | string[] + id?: string + verifiableCredential?: (VerifiableCredential | JWT)[] + holder: string + verifier?: string | string[] + issuanceDate?: string + expirationDate?: string, + + [x: string]: any +} + +/** + * The type of encoding to be used for the Verifiable Credential or Presentation to be generated. + * + * Only `jwt` and `lds` is supported at the moment. + * + * @public + */ +export type ProofFormat = 'jwt' | 'lds' + +/** + * Encapsulates the parameters required to create a + * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} + * + * @public + */ +export interface ICreateVerifiablePresentationArgs { + /** + * The json payload of the Presentation according to the + * {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model}. + * + * The signer of the Presentation is chosen based on the `holder` property + * of the `presentation` + * + * '@context', 'type' and 'issuanceDate' will be added automatically if omitted + */ + presentation: Partial + + /** + * If this parameter is true, the resulting VerifiablePresentation is sent to the + * {@link @veramo/core#IDataStore | storage plugin} to be saved + */ + save?: boolean + + /** + * Optional (only JWT) string challenge parameter to add to the verifiable presentation. + */ + challenge?: string + + /** + * Optional string domain parameter to add to the verifiable presentation. + */ + domain?: string + + /** + * The desired format for the VerifiablePresentation to be created. + * Currently, only JWT is supported + */ + proofFormat: ProofFormat + + /** + * Remove payload members during JWT-JSON transformation. Defaults to `true`. + * See https://www.w3.org/TR/vc-data-model/#jwt-encoding + */ + removeOriginalFields?: boolean +} + +/** + * Encapsulates the parameters required to create a + * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} + * + * @public + */ +export interface ICreateVerifiableCredentialArgs { + /** + * The json payload of the Credential according to the + * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} + * + * The signer of the Credential is chosen based on the `issuer.id` property + * of the `credential` + * + * '@context', 'type' and 'issuanceDate' will be added automatically if omitted + */ + credential: Partial + + /** + * If this parameter is true, the resulting VerifiablePresentation is sent to the + * {@link @veramo/core#IDataStore | storage plugin} to be saved + */ + save?: boolean + + /** + * The desired format for the VerifiablePresentation to be created. + */ + proofFormat: ProofFormat + + /** + * Remove payload members during JWT-JSON transformation. Defaults to `true`. + * See https://www.w3.org/TR/vc-data-model/#jwt-encoding + */ + removeOriginalFields?: boolean +} + +/** + * Encapsulates the parameters required to verify a + * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} + * + * @public + */ +export interface IVerifyCredentialArgs { + /** + * The json payload of the Credential according to the + * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} + * + * The signer of the Credential is chosen based on the `issuer.id` property + * of the `credential` + * + */ + credential: VerifiableCredential +} + +/** + * Encapsulates the parameters required to verify a + * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} + * + * @public + */ +export interface IVerifyPresentationArgs { + /** + * The json payload of the Credential according to the + * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} + * + * The signer of the Credential is chosen based on the `issuer.id` property + * of the `credential` + * + */ + presentation: VerifiablePresentation + + /** + * Optional (only for JWT) string challenge parameter to verify the verifiable presentation against + */ + challenge?: string + + /** + * Optional (only for JWT) string domain parameter to verify the verifiable presentation against + */ + domain?: string +} + +/** + * The interface definition for a plugin that can issue and verify Verifiable Credentials and Presentations + * that use JSON-LD format. + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model | W3C Verifiable Credentials data model} + * + * @public + */ +export interface ICredentialIssuerLD extends IPluginMethodMap { + /** + * Creates a Verifiable Presentation. + * The payload, signer and format are chosen based on the `args` parameter. + * + * @param args - Arguments necessary to create the Presentation. + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the {@link @veramo/core#VerifiablePresentation} that was requested or rejects with an error + * if there was a problem with the input or while getting the key to sign + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Presentation data model } + */ + createVerifiablePresentationLD( + args: ICreateVerifiablePresentationArgs, + context: IContext, + ): Promise + + /** + * Creates a Verifiable Credential. + * The payload, signer and format are chosen based on the `args` parameter. + * + * @param args - Arguments necessary to create the Presentation. + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the {@link @veramo/core#VerifiableCredential} that was requested or rejects with an error + * if there was a problem with the input or while getting the key to sign + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} + */ + createVerifiableCredentialLD( + args: ICreateVerifiableCredentialArgs, + context: IContext, + ): Promise + + /** + * Verifies a Verifiable Credential JWT or LDS Format. + * + * @param args - Arguments necessary to verify a VerifiableCredential + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the boolean true on successful verification or rejects on error + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} + */ + verifyCredentialLD(args: IVerifyCredentialArgs, context: IContext): Promise + + /** + * Verifies a Verifiable Presentation JWT or LDS Format. + * + * @param args - Arguments necessary to verify a VerifiableCredential + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the boolean true on successful verification or rejects on error + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Credential data model} + */ + verifyPresentationLD(args: IVerifyPresentationArgs, context: IContext): Promise +} + +/** + * Represents the requirements that this plugin has. + * The agent that is using this plugin is expected to provide these methods. + * + * This interface can be used for static type checks, to make sure your application is properly initialized. + */ +export type IContext = IAgentContext & + Pick & + Pick> + +/** + * A Veramo plugin that implements the {@link ICredentialIssuer} methods. + * + * @public + */ +export class CredentialIssuerLD implements IAgentPlugin { + readonly methods: ICredentialIssuerLD + readonly schema = schema.ICredentialIssuer + + private ldCredentialModule: LdCredentialModule + + constructor(options: { ldCredentialModule: LdCredentialModule }) { + this.ldCredentialModule = options.ldCredentialModule + + this.methods = { + createVerifiablePresentationLD: this.createVerifiablePresentationLD.bind(this), + createVerifiableCredentialLD: this.createVerifiableCredentialLD.bind(this), + verifyCredentialLD: this.verifyCredentialLD.bind(this), + verifyPresentationLD: this.verifyPresentationLD.bind(this), + } + } + + /** {@inheritdoc ICredentialIssuerLD.createVerifiablePresentationLD} */ + async createVerifiablePresentationLD( + args: ICreateVerifiablePresentationArgs, + context: IContext, + ): Promise { + const presentation: Partial = { + ...args?.presentation, + '@context': args?.presentation['@context'] || ['https://www.w3.org/2018/credentials/v1'], + //FIXME: make sure 'VerifiablePresentation' is the first element in this array: + type: args?.presentation?.type || ['VerifiablePresentation'], + issuanceDate: args?.presentation?.issuanceDate || new Date().toISOString(), + } + + if (!presentation.holder || typeof presentation.holder === 'undefined') { + throw new Error('invalid_argument: args.presentation.holder must not be empty') + } + + let identifier: IIdentifier + try { + identifier = await context.agent.didManagerGet({ did: presentation.holder }) + } catch (e) { + throw new Error('invalid_argument: args.presentation.holder must be a DID managed by this agent') + } + try { + //FIXME: `args` should allow picking a key or key type + const key = identifier.keys.find((k) => k.type === 'Secp256k1' || k.type === 'Ed25519') + if (!key) throw Error('No signing key for ' + identifier.did) + + // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod + if (key.kid != identifier.controllerKeyId) { + throw new Error('Trying to use a non-controller key for an LD-Proof is not supported') + } + + const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) + //issuanceDate must not be present for presentations because it is not defined in a @context + delete presentation.issuanceDate + + return await this.ldCredentialModule.signLDVerifiablePresentation( + presentation, + keyPayload, + args.challenge, + args.domain, + identifier, + context, + ) + } catch (error) { + debug(error) + return Promise.reject(error) + } + } + + /** {@inheritdoc ICredentialIssuerLD.createVerifiableCredentialLD} */ + async createVerifiableCredentialLD( + args: ICreateVerifiableCredentialArgs, + context: IContext, + ): Promise { + const credential: Partial = { + ...args?.credential, + '@context': args?.credential?.['@context'] || ['https://www.w3.org/2018/credentials/v1'], + //FIXME: make sure 'VerifiableCredential' is the first element in this array: + type: args?.credential?.type || ['VerifiableCredential'], + issuanceDate: args?.credential?.issuanceDate || new Date().toISOString(), + } + + //FIXME: if the identifier is not found, the error message should reflect that. + const issuer = typeof credential.issuer === 'string' ? credential.issuer : credential?.issuer?.id + if (!issuer || typeof issuer === 'undefined') { + throw new Error('invalid_argument: args.credential.issuer must not be empty') + } + + let identifier: IIdentifier + try { + identifier = await context.agent.didManagerGet({ did: issuer }) + } catch (e) { + throw new Error(`invalid_argument: args.credential.issuer must be a DID managed by this agent. ${e}`) + } + try { + //FIXME: `args` should allow picking a key or key type + const key = identifier.keys.find((k) => k.type === 'Secp256k1' || k.type === 'Ed25519') + if (!key) throw Error('No signing key for ' + identifier.did) + + //------------------------- BEGIN JSON_LD INSERT + let verifiableCredential + // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod + if (key.kid != identifier.controllerKeyId) { + throw new Error('Trying to use a non-controller key for an LD-Proof is not supported') + } + + const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) + verifiableCredential = await this.ldCredentialModule.issueLDVerifiableCredential( + credential, + keyPayload, + identifier, + context, + ) + + return verifiableCredential + } catch (error) { + debug(error) + return Promise.reject(error) + } + } + + /** {@inheritdoc ICredentialIssuerLD.verifyCredentialLD} */ + async verifyCredentialLD( + args: IVerifyCredentialArgs, + context: IContext, + ): Promise { + const credential = args.credential + return this.ldCredentialModule.verifyCredential(credential, context) + } + + /** {@inheritdoc ICredentialIssuerLD.verifyPresentationLD} */ + async verifyPresentationLD( + args: IVerifyPresentationArgs, + context: IContext, + ): Promise { + const presentation = args.presentation + return this.ldCredentialModule.verifyPresentation( + presentation, + args.challenge, + args.domain, + context, + ) + } +} + +function wrapSigner( + context: IAgentContext>, + key: IKey, + algorithm?: string, +) { + return async (data: string | Uint8Array) => { + const result = await context.agent.keyManagerSign({ keyRef: key.kid, data: data, algorithm }) + return result + } +} diff --git a/packages/credential-ld/src/index.ts b/packages/credential-ld/src/index.ts new file mode 100644 index 000000000..37b76f793 --- /dev/null +++ b/packages/credential-ld/src/index.ts @@ -0,0 +1,24 @@ +/** + * Provides a {@link @veramo/credential-ld#CredentialIssuerLD | plugin} for the {@link @veramo/core#Agent} that implements + * {@link @veramo/credential-ld#ICredentialIssuerLD} interface. + * + * @packageDocumentation + */ +export { + CredentialIssuerLD, + ICredentialIssuerLD, + ICreateVerifiableCredentialArgs, + ICreateVerifiablePresentationArgs, + ProofFormat, +} from './action-handler' +export { LdCredentialModule } from './ld-credential-module' +export { LdContextLoader } from './ld-context-loader' +export { LdDefaultContexts } from './ld-default-contexts' +export { LdSuiteLoader } from './ld-suite-loader' +export { + VeramoLdSignature, +} from './ld-suites' +export * from './suites/EcdsaSecp256k1RecoverySignature2020' +export * from './suites/Ed25519Signature2018' +const schema = require('../plugin.schema.json') +export { schema } diff --git a/packages/credential-w3c/src/ld-context-loader.ts b/packages/credential-ld/src/ld-context-loader.ts similarity index 100% rename from packages/credential-w3c/src/ld-context-loader.ts rename to packages/credential-ld/src/ld-context-loader.ts diff --git a/packages/credential-w3c/src/ld-credential-module.ts b/packages/credential-ld/src/ld-credential-module.ts similarity index 100% rename from packages/credential-w3c/src/ld-credential-module.ts rename to packages/credential-ld/src/ld-credential-module.ts diff --git a/packages/credential-w3c/src/ld-default-contexts.ts b/packages/credential-ld/src/ld-default-contexts.ts similarity index 100% rename from packages/credential-w3c/src/ld-default-contexts.ts rename to packages/credential-ld/src/ld-default-contexts.ts diff --git a/packages/credential-w3c/src/ld-suite-loader.ts b/packages/credential-ld/src/ld-suite-loader.ts similarity index 100% rename from packages/credential-w3c/src/ld-suite-loader.ts rename to packages/credential-ld/src/ld-suite-loader.ts diff --git a/packages/credential-w3c/src/ld-suites.ts b/packages/credential-ld/src/ld-suites.ts similarity index 100% rename from packages/credential-w3c/src/ld-suites.ts rename to packages/credential-ld/src/ld-suites.ts diff --git a/packages/credential-w3c/src/suites/EcdsaSecp256k1RecoverySignature2020.ts b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts similarity index 100% rename from packages/credential-w3c/src/suites/EcdsaSecp256k1RecoverySignature2020.ts rename to packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts diff --git a/packages/credential-w3c/src/suites/Ed25519Signature2018.ts b/packages/credential-ld/src/suites/Ed25519Signature2018.ts similarity index 100% rename from packages/credential-w3c/src/suites/Ed25519Signature2018.ts rename to packages/credential-ld/src/suites/Ed25519Signature2018.ts diff --git a/packages/credential-ld/tsconfig.json b/packages/credential-ld/tsconfig.json new file mode 100644 index 000000000..15a16d261 --- /dev/null +++ b/packages/credential-ld/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../tsconfig.settings.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build", + "declarationDir": "build", + // https://github.com/transmute-industries/vc.js/issues/60 + "skipLibCheck": true + }, + "references": [ + { "path": "../core" }, + { "path": "../did-resolver" }, + { "path": "../utils" } + ] +} diff --git a/packages/credential-ld/types/blakejs/index.d.ts b/packages/credential-ld/types/blakejs/index.d.ts new file mode 100644 index 000000000..d3f1b7fe3 --- /dev/null +++ b/packages/credential-ld/types/blakejs/index.d.ts @@ -0,0 +1 @@ +declare module 'blakejs' diff --git a/packages/credential-w3c/types/jsonld/index.d.ts b/packages/credential-ld/types/jsonld/index.d.ts similarity index 100% rename from packages/credential-w3c/types/jsonld/index.d.ts rename to packages/credential-ld/types/jsonld/index.d.ts diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index 9b6616551..3aec488ea 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -14,9 +14,6 @@ } }, "dependencies": { - "@transmute/credentials-context": "^0.7.0-unstable.26", - "@transmute/ed25519-signature-2018": "^0.7.0-unstable.27", - "@transmute/lds-ecdsa-secp256k1-recovery2020": "^0.0.7", "@veramo/core": "^3.1.0", "@veramo/did-jwt": "^3.1.0", "@veramo/did-resolver": "^3.1.0", @@ -26,10 +23,10 @@ "debug": "^4.1.1", "did-jwt-vc": "2.1.7", "did-resolver": "3.1.3", - "jsonld": "^5.2.0", - "jsonld-signatures": "^9.0.2", - "uint8arrays": "^2.1.3", - "vc-js": "^0.6.4" + "uint8arrays": "^2.1.3" + }, + "optionalDependencies": { + "@veramo/credential-ld": "^3.1.0" }, "devDependencies": { "@types/debug": "4.1.7", diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index a4a5b9282..1fd641e60 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -12,12 +12,6 @@ jest.mock('did-jwt-vc', () => { import { IIdentifier, IKey, VerifiableCredential, W3CCredential, W3CPresentation } from '@veramo/core' import { CredentialIssuer, IContext } from '../action-handler' -import { LdCredentialModule } from '../ld-credential-module' -import { LdContextLoader } from '../ld-context-loader' -import { LdDefaultContexts } from '../ld-default-contexts' -import { contexts as credential_contexts } from '@transmute/credentials-context' -import { LdSuiteLoader } from '../ld-suite-loader' -import { VeramoEcdsaSecp256k1RecoverySignature2020 } from '../suites/EcdsaSecp256k1RecoverySignature2020' const mockIdentifiers: IIdentifier[] = [ { @@ -64,16 +58,7 @@ const mockIdentifiers: IIdentifier[] = [ }, ] -const w3c = new CredentialIssuer({ - ldCredentialModule: new LdCredentialModule({ - ldContextLoader: new LdContextLoader({ - contextsPaths: [LdDefaultContexts, credential_contexts as Map], - }), - ldSuiteLoader: new LdSuiteLoader({ - veramoLdSignatures: [new VeramoEcdsaSecp256k1RecoverySignature2020()], - }), - }), -}) +const w3c = new CredentialIssuer() let agent = { execute: jest.fn(), @@ -94,6 +79,10 @@ let agent = { dataStoreSaveVerifiablePresentation: jest.fn().mockImplementation(async (args): Promise => true), getSchema: jest.fn(), didManagerGet: jest.fn(), + createVerifiableCredentialLD: jest.fn(), + createVerifiablePresentationLD: jest.fn(), + verifyCredentialLD: jest.fn(), + verifyPresentationLD: jest.fn(), } describe('@veramo/credential-w3c', () => { diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 0a1416f0f..252c3f39f 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -10,17 +10,22 @@ import { IIdentifier, VerifiableCredential, VerifiablePresentation, W3CPresentation, } from '@veramo/core' +import { ICredentialIssuerLD } from "@veramo/credential-ld" + import { createVerifiableCredentialJwt, createVerifiablePresentationJwt, + verifyCredential as verifyCredentialJWT, + verifyPresentation as verifyPresentationJWT, CredentialPayload, normalizeCredential, normalizePresentation, } from 'did-jwt-vc' -import { LdCredentialModule, schema } from './' +import { schema } from './' import Debug from 'debug' import { JWT } from 'did-jwt-vc/lib/types' +import { Resolvable } from 'did-resolver' const debug = Debug('veramo:w3c:action-handler') @@ -251,7 +256,8 @@ export interface ICredentialIssuer extends IPluginMethodMap { export type IContext = IAgentContext & Pick & - Pick> + Pick & + ICredentialIssuerLD> /** * A Veramo plugin that implements the {@link ICredentialIssuer} methods. @@ -262,10 +268,7 @@ export class CredentialIssuer implements IAgentPlugin { readonly methods: ICredentialIssuer readonly schema = schema.ICredentialIssuer - private ldCredentialModule: LdCredentialModule - constructor(options: { ldCredentialModule: LdCredentialModule }) { - this.ldCredentialModule = options.ldCredentialModule - + constructor() { this.methods = { createVerifiablePresentation: this.createVerifiablePresentation.bind(this), createVerifiableCredential: this.createVerifiableCredential.bind(this), @@ -302,48 +305,34 @@ export class CredentialIssuer implements IAgentPlugin { const key = identifier.keys.find((k) => k.type === 'Secp256k1' || k.type === 'Ed25519') if (!key) throw Error('No signing key for ' + identifier.did) - //------------------------- BEGIN JSON_LD INSERT + let verifiablePresentation: VerifiablePresentation + if (args.proofFormat === 'lds') { - // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod - if (key.kid != identifier.controllerKeyId) { - throw new Error('Trying to use a non-controller key for an LD-Proof is not supported') + if (typeof context.agent.createVerifiablePresentationLD === 'function') { + verifiablePresentation = await context.agent.createVerifiablePresentationLD(args) + } else { + throw new Error('invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed') } - - const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - //issuanceDate must not be present for presentations - delete presentation.issuanceDate - - return await this.ldCredentialModule.signLDVerifiablePresentation( - presentation, - keyPayload, - args.challenge, - args.domain, - identifier, - context, - ) - } - //------------------------- END JSON_LD INSERT - else { + } else { // only add issuanceDate for JWT presentation.issuanceDate = args.presentation.issuanceDate || new Date().toISOString() - } + //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` + debug('Signing VP with', identifier.did) + let alg = 'ES256K' + if (key.type === 'Ed25519') { + alg = 'EdDSA' + } + const signer = wrapSigner(context, key, alg) - //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` - debug('Signing VP with', identifier.did) - let alg = 'ES256K' - if (key.type === 'Ed25519') { - alg = 'EdDSA' + const jwt = await createVerifiablePresentationJwt( + presentation as PresentationPayload, + { did: identifier.did, signer, alg }, + { removeOriginalFields: args.removeOriginalFields }, + ) + //FIXME: flagging this as a potential privacy leak. + debug(jwt) + verifiablePresentation = normalizePresentation(jwt) } - const signer = wrapSigner(context, key, alg) - - const jwt = await createVerifiablePresentationJwt( - presentation as PresentationPayload, - { did: identifier.did, signer, alg }, - { removeOriginalFields: args.removeOriginalFields }, - ) - //FIXME: flagging this as a potential privacy leak. - debug(jwt) - const verifiablePresentation = normalizePresentation(jwt) if (args.save) { await context.agent.dataStoreSaveVerifiablePresentation({ verifiablePresentation }) } @@ -384,24 +373,14 @@ export class CredentialIssuer implements IAgentPlugin { const key = identifier.keys.find((k) => k.type === 'Secp256k1' || k.type === 'Ed25519') if (!key) throw Error('No signing key for ' + identifier.did) - //------------------------- BEGIN JSON_LD INSERT - let verifiableCredential + let verifiableCredential: VerifiableCredential if (args.proofFormat === 'lds') { - // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod - if (key.kid != identifier.controllerKeyId) { - throw new Error('Trying to use a non-controller key for an LD-Proof is not supported') + if (typeof context.agent.createVerifiableCredentialLD === 'function') { + verifiableCredential = await context.agent.createVerifiableCredentialLD(args as any) + } else { + throw new Error('invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed') } - - const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - verifiableCredential = await this.ldCredentialModule.issueLDVerifiableCredential( - credential, - keyPayload, - identifier, - context, - ) } else { - //------------------------- END JSON_LD INSERT - //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` or `lds` debug('Signing VC with', identifier.did) let alg = 'ES256K' if (key.type === 'Ed25519') { @@ -434,13 +413,31 @@ export class CredentialIssuer implements IAgentPlugin { context: IContext, ): Promise { const credential = args.credential - // JWT if (typeof credential === 'string' || credential.proof.jwt) { - // Not implemented yet. - throw Error('verifyCredential currently does not the verification of VC-JWT credentials.') + // JWT + let jwt: string + if (typeof credential === 'string') { + jwt = credential + } else { + jwt = credential.proof.jwt + } + const resolver = { resolve: (didUrl: string) => context.agent.resolveDid({ didUrl }) } as Resolvable + try { + const verification = await verifyCredentialJWT(jwt, resolver) + return true + } catch (e: any) { + //TODO: return a more detailed reason for failure + return false + } + } else { + // JSON-LD + if (typeof context.agent.verifyCredentialLD === 'function') { + const result = await context.agent.verifyCredentialLD(args) + return result + } else { + throw new Error('invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed') + } } - - return this.ldCredentialModule.verifyCredential(credential, context) } /** {@inheritdoc ICredentialIssuer.verifyPresentation} */ @@ -449,18 +446,35 @@ export class CredentialIssuer implements IAgentPlugin { context: IContext, ): Promise { const presentation = args.presentation - // JWT if (typeof presentation === 'string' || (presentation).proof.jwt) { - // Not implemented yet. - throw Error('verifyPresentation currently does not the verification of VC-JWT credentials.') + // JWT + let jwt: string + if (typeof presentation === 'string') { + jwt = presentation + } else { + jwt = presentation.proof.jwt + } + const resolver = { resolve: (didUrl: string) => context.agent.resolveDid({ didUrl }) } as Resolvable + + try { + const verification = await verifyPresentationJWT(jwt, resolver, { + challenge: args.challenge, + domain: args.domain + }) + return true + } catch (e: any) { + //TODO: return a more detailed reason for failure + return false + } + } else { + // JSON-LD + if (typeof context.agent.verifyPresentationLD === 'function') { + const result = await context.agent.verifyPresentationLD(args) + return result + } else { + throw new Error('invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed') + } } - - return this.ldCredentialModule.verifyPresentation( - presentation, - args.challenge, - args.domain, - context, - ) } } diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index ba85371c7..fd1725200 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -15,14 +15,5 @@ export { ICreateVerifiablePresentationArgs, ProofFormat, } from './action-handler' -export { LdCredentialModule } from './ld-credential-module' -export { LdContextLoader } from './ld-context-loader' -export { LdDefaultContexts } from './ld-default-contexts' -export { LdSuiteLoader } from './ld-suite-loader' -export { - VeramoLdSignature, -} from './ld-suites' -export * from './suites/EcdsaSecp256k1RecoverySignature2020' -export * from './suites/Ed25519Signature2018' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index 3801f6e88..d98b11b91 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -44,7 +44,6 @@ export type IContext = IAgentContext */ export class W3cMessageHandler extends AbstractMessageHandler { - async handle(message: Message, context: IContext): Promise { const meta = message.getLastMetaData() @@ -121,7 +120,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { } if (message.type === MessageTypes.vp && message.data) { - // verify credential + // verify presentation const presentation = message.data as VerifiablePresentation // throws on error. diff --git a/packages/data-store/src/migrations/createPrivateKeyStorage.ts b/packages/data-store/src/migrations/3.createPrivateKeyStorage.ts similarity index 98% rename from packages/data-store/src/migrations/createPrivateKeyStorage.ts rename to packages/data-store/src/migrations/3.createPrivateKeyStorage.ts index 26a24f048..0186a64c2 100644 --- a/packages/data-store/src/migrations/createPrivateKeyStorage.ts +++ b/packages/data-store/src/migrations/3.createPrivateKeyStorage.ts @@ -2,7 +2,7 @@ import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' import { PrivateKey } from '..' import { PreMigrationKey } from '../entities/PreMigrationEntities' import Debug from 'debug' -const debug = Debug('veramo:data-store:key-migration') +const debug = Debug('veramo:data-store:migrate-private-keys') /** * Migration of existing private keys from Veramo 2.x to Veramo 3.x diff --git a/packages/data-store/src/migrations/4.allowNullVPIssuanceDate.ts b/packages/data-store/src/migrations/4.allowNullVPIssuanceDate.ts new file mode 100644 index 000000000..d16f36a7e --- /dev/null +++ b/packages/data-store/src/migrations/4.allowNullVPIssuanceDate.ts @@ -0,0 +1,81 @@ +import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm' +import { Presentation } from '..' +import Debug from 'debug' + +const debug = Debug('veramo:data-store:migrate-presentation-issuance-date') + +/** + * Reduce issuanceDate constraint of Presentations + */ +export class AllowNullIssuanceDateForPresentations1637237492913 implements MigrationInterface { + private getTableName(givenName: string, queryRunner: QueryRunner): string { + return ( + queryRunner.connection.entityMetadatas.find((meta) => meta.givenTableName === givenName)?.tableName || + givenName + ) + } + + async up(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.driver.options.type === 'sqlite') { + debug(`splitting migration into multiple transactions to allow sqlite table updates`) + await queryRunner.commitTransaction(); + debug(`turning off foreign keys`) + await queryRunner.query('PRAGMA foreign_keys=off'); + await queryRunner.startTransaction(); + } + + const tableName = this.getTableName('presentation', queryRunner) + // update issuanceDate column + let table = await queryRunner.getTable(tableName); + const oldColumn = table?.findColumnByName("issuanceDate")!; + const newColumn = oldColumn.clone(); + newColumn.isNullable = true; + debug(`updating issuanceDate for presentations to allow null`) + await queryRunner.changeColumn(table!, oldColumn, newColumn); + debug(`updated issuanceDate for presentations to allow null`) + + if (queryRunner.connection.driver.options.type === 'sqlite') { + debug(`splitting migration into multiple transactions to allow sqlite table updates`) + await queryRunner.commitTransaction(); + debug(`turning on foreign keys`) + await queryRunner.query('PRAGMA foreign_keys=on'); + await queryRunner.startTransaction(); + } + } + + async down(queryRunner: QueryRunner): Promise { + if (queryRunner.connection.driver.options.type === 'sqlite') { + debug(`splitting migration into multiple transactions to allow sqlite table updates`) + await queryRunner.commitTransaction(); + debug(`turning off foreign keys`) + await queryRunner.query('PRAGMA foreign_keys=off'); + await queryRunner.startTransaction(); + } + const tableName = this.getTableName('presentation', queryRunner) + debug(`DOWN update NULL 'issuanceDate' with FAKE data for '${tableName}' table`) + await queryRunner.manager + .createQueryBuilder() + .update(Presentation) + .set({ issuanceDate: new Date(0) }) + .where('issuanceDate is NULL') + .execute() + // update issuanceDate column + let table = await queryRunner.getTable(tableName); + const oldColumn = table?.findColumnByName("issuanceDate")!; + const newColumn = oldColumn.clone(); + newColumn.isNullable = false; + debug(`updating issuanceDate for presentations to NOT allow null`) + await queryRunner.changeColumn(table!, oldColumn, newColumn); + debug(`updated issuanceDate for presentations to NOT allow null`) + + if (queryRunner.connection.driver.options.type === 'sqlite') { + debug(`splitting migration into multiple transactions to allow sqlite table updates`) + await queryRunner.commitTransaction(); + debug(`turning on foreign keys`) + await queryRunner.query('PRAGMA foreign_keys=on'); + await queryRunner.startTransaction(); + } + + debug(`DOWN updated issuanceDate for presentations to NOT allow null`) + } +} diff --git a/packages/data-store/src/migrations/index.ts b/packages/data-store/src/migrations/index.ts index ed3ec01dd..63287dfe1 100644 --- a/packages/data-store/src/migrations/index.ts +++ b/packages/data-store/src/migrations/index.ts @@ -1,5 +1,6 @@ import { CreateDatabase1447159020001 } from './1.createDatabase' import { SimplifyRelations1447159020002 } from './2.simplifyRelations' -import { CreatePrivateKeyStorage1629293428674 } from './createPrivateKeyStorage' +import { CreatePrivateKeyStorage1629293428674 } from './3.createPrivateKeyStorage' +import { AllowNullIssuanceDateForPresentations1637237492913 } from "./4.allowNullVPIssuanceDate"; -export const migrations = [CreateDatabase1447159020001, SimplifyRelations1447159020002, CreatePrivateKeyStorage1629293428674] +export const migrations = [CreateDatabase1447159020001, SimplifyRelations1447159020002, CreatePrivateKeyStorage1629293428674, AllowNullIssuanceDateForPresentations1637237492913] diff --git a/packages/tsconfig.json b/packages/tsconfig.json index 1c63653d6..2b74ad93a 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -3,6 +3,7 @@ "references": [ { "path": "core" }, { "path": "credential-w3c" }, + { "path": "credential-ld" }, { "path": "data-store" }, { "path": "did-comm" }, { "path": "did-discovery" }, From 223c2073f6876dd29599660f0167d0c073d672e2 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Thu, 18 Nov 2021 16:27:09 +0100 Subject: [PATCH 39/48] refactor(credential-ld): simplify credential-ld initialization API --- __tests__/localAgent.test.ts | 67 +++++++----------- __tests__/localMemoryStoreAgent.test.ts | 50 +++++--------- __tests__/restAgent.test.ts | 68 +++++++------------ packages/credential-ld/src/action-handler.ts | 15 ++-- .../credential-ld/src/ld-context-loader.ts | 16 +++-- .../credential-ld/src/ld-credential-module.ts | 4 +- 6 files changed, 90 insertions(+), 130 deletions(-) diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 6b86d1126..82d1de734 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -7,70 +7,55 @@ import { createAgent, - TAgent, + IAgentOptions, + IDataStore, IDIDManager, - IResolver, IKeyManager, - IDataStore, IMessageHandler, - IAgentOptions, + IResolver, + TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' import { KeyManager } from '../packages/key-manager/src' -import { DIDManager, AliasDiscoveryProvider } from '../packages/did-manager/src' +import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { - CredentialIssuer, - ICredentialIssuer, - W3cMessageHandler, -} from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, } from '../packages/credential-w3c/src' import { CredentialIssuerLD, ICredentialIssuerLD, - LdCredentialModule, - LdContextLoader, LdDefaultContexts, - LdSuiteLoader, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018 } from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' -import { KeyDIDProvider } from '../packages/did-provider-key/src' -import { DIDComm, DIDCommMessageHandler, IDIDComm, DIDCommHttpTransport } from '../packages/did-comm/src' -import { - SelectiveDisclosure, - ISelectiveDisclosure, - SdrMessageHandler, -} from '../packages/selective-disclosure/src' +import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' +import { DIDComm, DIDCommHttpTransport, DIDCommMessageHandler, IDIDComm } from '../packages/did-comm/src' +import { ISelectiveDisclosure, SdrMessageHandler, SelectiveDisclosure, } from '../packages/selective-disclosure/src' import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src' -import { IDIDDiscovery, DIDDiscovery } from '../packages/did-discovery/src' -import { getDidKeyResolver } from '../packages/did-provider-key/src' +import { DIDDiscovery, IDIDDiscovery } from '../packages/did-discovery/src' import { - Entities, - KeyStore, - DIDStore, - IDataStoreORM, DataStore, DataStoreORM, - ProfileDiscoveryProvider, - PrivateKeyStore, + DIDStore, + Entities, + IDataStoreORM, + KeyStore, migrations, + PrivateKeyStore, + ProfileDiscoveryProvider, } from '../packages/data-store/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' -import { createConnection, Connection } from 'typeorm' +import { Connection, createConnection } from 'typeorm' import { createGanacheProvider } from './utils/ganache-provider' import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' import { contexts as credential_contexts } from '@transmute/credentials-context' import * as fs from 'fs' - -jest.setTimeout(30000) - // Shared tests import verifiableDataJWT from './shared/verifiableDataJWT' import verifiableDataLD from './shared/verifiableDataLD' @@ -87,6 +72,8 @@ import didDiscovery from './shared/didDiscovery' import dbInitOptions from './shared/dbInitOptions' import didCommWithEthrDidFlow from './shared/didCommWithEthrDidFlow' +jest.setTimeout(30000) + const infuraProjectId = '3586660d179141e3801c3895de1c2eba' const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c' @@ -213,17 +200,11 @@ const setup = async (options?: IAgentOptions): Promise => { new DIDComm([new DIDCommHttpTransport()]), new CredentialIssuer(), new CredentialIssuerLD({ - ldCredentialModule: new LdCredentialModule({ - ldContextLoader: new LdContextLoader({ - contextsPaths: [LdDefaultContexts, credential_contexts as Map], - }), - ldSuiteLoader: new LdSuiteLoader({ - veramoLdSignatures: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018() - ], - }), - }), + contextMaps: [LdDefaultContexts, credential_contexts as any], + suites: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018() + ], }), new SelectiveDisclosure(), new DIDDiscovery({ diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index ea187ca3d..1fac864e5 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -5,46 +5,35 @@ */ import { createAgent, - TAgent, + IAgentOptions, + IDataStore, IDIDManager, - IResolver, IKeyManager, - IDataStore, IMessageHandler, - IAgentOptions, + IResolver, + TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' import { KeyManager, MemoryKeyStore, MemoryPrivateKeyStore } from '../packages/key-manager/src' import { DIDManager, MemoryDIDStore } from '../packages/did-manager/src' -import { createConnection, Connection } from 'typeorm' +import { Connection, createConnection } from 'typeorm' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { - CredentialIssuer, - ICredentialIssuer, - W3cMessageHandler, -} from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, } from '../packages/credential-w3c/src' import { CredentialIssuerLD, ICredentialIssuerLD, - LdCredentialModule, - LdContextLoader, LdDefaultContexts, - LdSuiteLoader, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018 } from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' -import { KeyDIDProvider, getDidKeyResolver } from '../packages/did-provider-key/src' +import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' import { DIDComm, DIDCommMessageHandler, IDIDComm } from '../packages/did-comm/src' -import { - SelectiveDisclosure, - ISelectiveDisclosure, - SdrMessageHandler, -} from '../packages/selective-disclosure/src' +import { ISelectiveDisclosure, SdrMessageHandler, SelectiveDisclosure, } from '../packages/selective-disclosure/src' import { KeyManagementSystem } from '../packages/kms-local/src' -import { Entities, IDataStoreORM, DataStore, DataStoreORM, migrations } from '../packages/data-store/src' +import { DataStore, DataStoreORM, Entities, IDataStoreORM, migrations } from '../packages/data-store/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' import { Resolver } from 'did-resolver' @@ -52,9 +41,6 @@ import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' import { contexts as credential_contexts } from '@transmute/credentials-context' import * as fs from 'fs' - -jest.setTimeout(30000) - // Shared tests import verifiableDataJWT from './shared/verifiableDataJWT' import verifiableDataLD from './shared/verifiableDataLD' @@ -68,6 +54,8 @@ import didManager from './shared/didManager' import didCommPacking from './shared/didCommPacking' import messageHandler from './shared/messageHandler' +jest.setTimeout(30000) + const databaseFile = `./tmp/local-database2-${Math.random().toPrecision(5)}.sqlite` const infuraProjectId = '3586660d179141e3801c3895de1c2eba' @@ -170,17 +158,11 @@ const setup = async (options?: IAgentOptions): Promise => { new DIDComm(), new CredentialIssuer(), new CredentialIssuerLD({ - ldCredentialModule: new LdCredentialModule({ - ldContextLoader: new LdContextLoader({ - contextsPaths: [LdDefaultContexts, credential_contexts as Map], - }), - ldSuiteLoader: new LdSuiteLoader({ - veramoLdSignatures: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018(), - ], - }), - }), + contextMaps: [LdDefaultContexts, credential_contexts as any], + suites: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018() + ], }), new SelectiveDisclosure(), ...(options?.plugins || []), diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 632f1a9e4..2bfa0a611 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -8,61 +8,50 @@ import 'cross-fetch/polyfill' import { Agent, - IAgent, createAgent, + IAgent, + IAgentOptions, + IDataStore, IDIDManager, - IResolver, IKeyManager, - IDataStore, IMessageHandler, - IAgentOptions, + IResolver, TAgent, } from '../packages/core/src' import { MessageHandler } from '../packages/message-handler/src' import { KeyManager } from '../packages/key-manager/src' -import { DIDManager, AliasDiscoveryProvider } from '../packages/did-manager/src' +import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { - CredentialIssuer, - ICredentialIssuer, - W3cMessageHandler, -} from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, } from '../packages/credential-w3c/src' import { CredentialIssuerLD, ICredentialIssuerLD, - LdCredentialModule, - LdContextLoader, LdDefaultContexts, - LdSuiteLoader, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018 } from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' -import { KeyDIDProvider, getDidKeyResolver } from '../packages/did-provider-key/src' -import { DIDComm, DIDCommMessageHandler, IDIDComm, DIDCommHttpTransport } from '../packages/did-comm/src' -import { - SelectiveDisclosure, - ISelectiveDisclosure, - SdrMessageHandler, -} from '../packages/selective-disclosure/src' +import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' +import { DIDComm, DIDCommHttpTransport, DIDCommMessageHandler, IDIDComm } from '../packages/did-comm/src' +import { ISelectiveDisclosure, SdrMessageHandler, SelectiveDisclosure, } from '../packages/selective-disclosure/src' import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src' import { - Entities, - KeyStore, - DIDStore, - IDataStoreORM, DataStore, DataStoreORM, - ProfileDiscoveryProvider, - PrivateKeyStore, + DIDStore, + Entities, + IDataStoreORM, + KeyStore, migrations, + PrivateKeyStore, + ProfileDiscoveryProvider, } from '../packages/data-store/src' -import { createConnection, Connection } from 'typeorm' +import { Connection, createConnection } from 'typeorm' import { AgentRestClient } from '../packages/remote-client/src' -import { AgentRouter, RequestWithAgentRouter, MessagingRouter } from '../packages/remote-server/src' -import { IDIDDiscovery, DIDDiscovery } from '../packages/did-discovery/src' +import { AgentRouter, MessagingRouter, RequestWithAgentRouter } from '../packages/remote-server/src' +import { DIDDiscovery, IDIDDiscovery } from '../packages/did-discovery/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' import { Resolver } from 'did-resolver' @@ -72,9 +61,6 @@ import express from 'express' import { Server } from 'http' import { contexts as credential_contexts } from '@transmute/credentials-context' import * as fs from 'fs' - -jest.setTimeout(30000) - // Shared tests import verifiableDataJWT from './shared/verifiableDataJWT' import verifiableDataLD from './shared/verifiableDataLD' @@ -89,6 +75,8 @@ import didWithFakeDidFlow from './shared/didCommWithFakeDidFlow' import messageHandler from './shared/messageHandler' import didDiscovery from './shared/didDiscovery' +jest.setTimeout(30000) + const databaseFile = `./tmp/rest-database-${Math.random().toPrecision(5)}.sqlite` const infuraProjectId = '3586660d179141e3801c3895de1c2eba' const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c' @@ -197,17 +185,11 @@ const setup = async (options?: IAgentOptions): Promise => { new DIDComm([new DIDCommHttpTransport()]), new CredentialIssuer(), new CredentialIssuerLD({ - ldCredentialModule: new LdCredentialModule({ - ldContextLoader: new LdContextLoader({ - contextsPaths: [LdDefaultContexts, credential_contexts as Map], - }), - ldSuiteLoader: new LdSuiteLoader({ - veramoLdSignatures: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018() - ], - }), - }), + contextMaps: [LdDefaultContexts, credential_contexts as any], + suites: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018() + ], }), new SelectiveDisclosure(), new DIDDiscovery({ diff --git a/packages/credential-ld/src/action-handler.ts b/packages/credential-ld/src/action-handler.ts index e05d2d40b..ea59d0b7c 100644 --- a/packages/credential-ld/src/action-handler.ts +++ b/packages/credential-ld/src/action-handler.ts @@ -10,8 +10,9 @@ import { IIdentifier, VerifiableCredential, VerifiablePresentation, W3CPresentation, } from '@veramo/core' -import { LdCredentialModule, schema } from './' +import { LdContextLoader, LdCredentialModule, LdSuiteLoader, schema, VeramoLdSignature } from './' import Debug from 'debug' +import { ContextDoc, OrPromise, RecordLike } from "./ld-context-loader"; const debug = Debug('veramo:w3c:action-handler') @@ -278,7 +279,7 @@ export type IContext = IAgentContext> /** - * A Veramo plugin that implements the {@link ICredentialIssuer} methods. + * A Veramo plugin that implements the {@link ICredentialIssuerLD} methods. * * @public */ @@ -288,8 +289,14 @@ export class CredentialIssuerLD implements IAgentPlugin { private ldCredentialModule: LdCredentialModule - constructor(options: { ldCredentialModule: LdCredentialModule }) { - this.ldCredentialModule = options.ldCredentialModule + constructor(options: { + contextMaps: RecordLike>[], + suites: VeramoLdSignature[] + }) { + this.ldCredentialModule = new LdCredentialModule({ + ldContextLoader: new LdContextLoader({ contextsPaths: options.contextMaps }), + ldSuiteLoader: new LdSuiteLoader({ veramoLdSignatures: options.suites }) + }) this.methods = { createVerifiablePresentationLD: this.createVerifiablePresentationLD.bind(this), diff --git a/packages/credential-ld/src/ld-context-loader.ts b/packages/credential-ld/src/ld-context-loader.ts index ffe09cc66..2159c0662 100644 --- a/packages/credential-ld/src/ld-context-loader.ts +++ b/packages/credential-ld/src/ld-context-loader.ts @@ -1,13 +1,21 @@ +export type OrPromise = T | Promise + +export type ContextDoc = { + "@context": Record +} + +export type RecordLike = Map | Record + /** - * The LdContextLoader is initialized with a List of Map + * The LdContextLoader is initialized with a List of Map * that it unifies into a single Map to provide to the documentLoader within * the w3c credential module. */ export class LdContextLoader { - private contexts: Record + private contexts: Record> constructor(options: { - contextsPaths: (Map | Record)[] + contextsPaths: RecordLike>[] }) { this.contexts = {}; // generate-plugin-schema is failing unless we use the cast to `any[]` @@ -22,7 +30,7 @@ export class LdContextLoader { return this.contexts[url] !== null && typeof this.contexts[url] !== 'undefined' } - get(url: string): object | undefined { + async get(url: string): Promise { return this.contexts[url] } } \ No newline at end of file diff --git a/packages/credential-ld/src/ld-credential-module.ts b/packages/credential-ld/src/ld-credential-module.ts index 6122bc7a7..972a79a1c 100644 --- a/packages/credential-ld/src/ld-credential-module.ts +++ b/packages/credential-ld/src/ld-credential-module.ts @@ -61,11 +61,11 @@ export class LdCredentialModule { } if (this.ldContextLoader.has(url)) { - // console.log(`Returning local context for: ${url}`) + const contextDoc = await this.ldContextLoader.get(url) return { contextUrl: null, documentUrl: url, - document: this.ldContextLoader.get(url), + document: contextDoc, } } From 8c1081bac6e595b5351e7a94c9c3fef8fb6dc4c8 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Fri, 19 Nov 2021 20:06:46 +0100 Subject: [PATCH 40/48] feat(credential-ld): proper mapping from localKeys to verificationMethods --- __tests__/shared/verifiableDataLD.ts | 2 +- packages/credential-ld/plugin.schema.json | 58 +-- packages/credential-ld/src/action-handler.ts | 427 ++++-------------- packages/credential-ld/src/index.ts | 8 +- .../credential-ld/src/ld-context-loader.ts | 11 +- .../credential-ld/src/ld-credential-module.ts | 25 +- packages/credential-ld/src/ld-suite-loader.ts | 17 +- packages/credential-ld/src/ld-suites.ts | 5 +- .../EcdsaSecp256k1RecoverySignature2020.ts | 15 +- .../src/suites/Ed25519Signature2018.ts | 10 +- packages/credential-ld/src/types.ts | 254 +++++++++++ packages/credential-w3c/src/action-handler.ts | 5 +- packages/did-comm/src/didcomm.ts | 4 +- packages/utils/package.json | 1 + packages/utils/src/types/utility-types.ts | 11 + packages/utils/src/utils.ts | 44 +- 16 files changed, 450 insertions(+), 447 deletions(-) create mode 100644 packages/credential-ld/src/types.ts diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index 5ee6b4f84..432f60808 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -1,7 +1,7 @@ import { TAgent, IDIDManager, IDataStore, IIdentifier } from '../../packages/core/src' import { IDataStoreORM } from '../../packages/data-store/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' -import { IDIDComm } from '@veramo/did-comm' +import { IDIDComm } from '../../packages/did-comm/src' type ConfiguredAgent = TAgent diff --git a/packages/credential-ld/plugin.schema.json b/packages/credential-ld/plugin.schema.json index e87b19d84..329342a8f 100644 --- a/packages/credential-ld/plugin.schema.json +++ b/packages/credential-ld/plugin.schema.json @@ -2,7 +2,7 @@ "ICredentialIssuerLD": { "components": { "schemas": { - "ICreateVerifiableCredentialArgs": { + "ICreateVerifiableCredentialLDArgs": { "type": "object", "properties": { "credential": { @@ -55,22 +55,13 @@ }, "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, - "save": { - "type": "boolean", - "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved" - }, - "proofFormat": { - "$ref": "#/components/schemas/ProofFormat", - "description": "The desired format for the VerifiablePresentation to be created." - }, - "removeOriginalFields": { - "type": "boolean", - "description": "Remove payload members during JWT-JSON transformation. Defaults to `true`. See https://www.w3.org/TR/vc-data-model/#jwt-encoding" + "keyRef": { + "type": "string", + "description": "Optional. The key handle ( {@link IKey#kid } ) from the internal database." } }, "required": [ - "credential", - "proofFormat" + "credential" ], "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" }, @@ -118,14 +109,6 @@ "type" ] }, - "ProofFormat": { - "type": "string", - "enum": [ - "jwt", - "lds" - ], - "description": "The type of encoding to be used for the Verifiable Credential or Presentation to be generated.\n\nOnly `jwt` and `lds` is supported at the moment." - }, "VerifiableCredential": { "type": "object", "properties": { @@ -203,7 +186,7 @@ ], "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" }, - "ICreateVerifiablePresentationArgs": { + "ICreateVerifiablePresentationLDArgs": { "type": "object", "properties": { "presentation": { @@ -276,10 +259,6 @@ }, "description": "The json payload of the Presentation according to the\n {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model } .\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, - "save": { - "type": "boolean", - "description": "If this parameter is true, the resulting VerifiablePresentation is sent to the\n {@link @veramo/core#IDataStore | storage plugin } to be saved" - }, "challenge": { "type": "string", "description": "Optional (only JWT) string challenge parameter to add to the verifiable presentation." @@ -288,18 +267,13 @@ "type": "string", "description": "Optional string domain parameter to add to the verifiable presentation." }, - "proofFormat": { - "$ref": "#/components/schemas/ProofFormat", - "description": "The desired format for the VerifiablePresentation to be created. Currently, only JWT is supported" - }, - "removeOriginalFields": { - "type": "boolean", - "description": "Remove payload members during JWT-JSON transformation. Defaults to `true`. See https://www.w3.org/TR/vc-data-model/#jwt-encoding" + "keyRef": { + "type": "string", + "description": "Optional. The key handle ( {@link IKey#kid } ) from the internal database." } }, "required": [ - "presentation", - "proofFormat" + "presentation" ], "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" }, @@ -363,7 +337,7 @@ ], "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" }, - "IVerifyCredentialArgs": { + "IVerifyCredentialLDArgs": { "type": "object", "properties": { "credential": { @@ -376,7 +350,7 @@ ], "description": "Encapsulates the parameters required to verify a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" }, - "IVerifyPresentationArgs": { + "IVerifyPresentationLDArgs": { "type": "object", "properties": { "presentation": { @@ -402,7 +376,7 @@ "createVerifiableCredentialLD": { "description": "Creates a Verifiable Credential. The payload, signer and format are chosen based on the ", "arguments": { - "$ref": "#/components/schemas/ICreateVerifiableCredentialArgs" + "$ref": "#/components/schemas/ICreateVerifiableCredentialLDArgs" }, "returnType": { "$ref": "#/components/schemas/VerifiableCredential" @@ -411,7 +385,7 @@ "createVerifiablePresentationLD": { "description": "Creates a Verifiable Presentation. The payload, signer and format are chosen based on the ", "arguments": { - "$ref": "#/components/schemas/ICreateVerifiablePresentationArgs" + "$ref": "#/components/schemas/ICreateVerifiablePresentationLDArgs" }, "returnType": { "$ref": "#/components/schemas/VerifiablePresentation" @@ -420,7 +394,7 @@ "verifyCredentialLD": { "description": "Verifies a Verifiable Credential JWT or LDS Format.", "arguments": { - "$ref": "#/components/schemas/IVerifyCredentialArgs" + "$ref": "#/components/schemas/IVerifyCredentialLDArgs" }, "returnType": { "type": "boolean" @@ -429,7 +403,7 @@ "verifyPresentationLD": { "description": "Verifies a Verifiable Presentation JWT or LDS Format.", "arguments": { - "$ref": "#/components/schemas/IVerifyPresentationArgs" + "$ref": "#/components/schemas/IVerifyPresentationLDArgs" }, "returnType": { "type": "boolean" diff --git a/packages/credential-ld/src/action-handler.ts b/packages/credential-ld/src/action-handler.ts index ea59d0b7c..7db02d549 100644 --- a/packages/credential-ld/src/action-handler.ts +++ b/packages/credential-ld/src/action-handler.ts @@ -1,282 +1,33 @@ import { IAgentContext, IAgentPlugin, - IResolver, - IDIDManager, - IKeyManager, - IPluginMethodMap, - IDataStore, + IIdentifier, IKey, - IIdentifier, VerifiableCredential, VerifiablePresentation, W3CPresentation, + IResolver, + VerifiableCredential, + VerifiablePresentation, } from '@veramo/core' - -import { LdContextLoader, LdCredentialModule, LdSuiteLoader, schema, VeramoLdSignature } from './' +import { schema, VeramoLdSignature } from './' import Debug from 'debug' -import { ContextDoc, OrPromise, RecordLike } from "./ld-context-loader"; - -const debug = Debug('veramo:w3c:action-handler') - -export type JWT = string -export type IssuerType = { id: string, [x: string]: any } | string -export type CredentialSubject = { id?: string, [x: string]: any } - -export interface CredentialStatus { - id: string - type: string - - [x: string]: any -} - -export type DateType = string | Date - -/** - * used as input when creating Verifiable Credentials - */ -export interface CredentialPayload { - '@context': string | string[] - id?: string - type: string | string[] - issuer: IssuerType - issuanceDate: DateType - expirationDate?: DateType - credentialSubject: CredentialSubject - credentialStatus?: CredentialStatus - - [x: string]: any -} - -/** - * used as input when creating Verifiable Presentations - */ -export interface PresentationPayload { - '@context': string | string[] - type: string | string[] - id?: string - verifiableCredential?: (VerifiableCredential | JWT)[] - holder: string - verifier?: string | string[] - issuanceDate?: string - expirationDate?: string, - - [x: string]: any -} - -/** - * The type of encoding to be used for the Verifiable Credential or Presentation to be generated. - * - * Only `jwt` and `lds` is supported at the moment. - * - * @public - */ -export type ProofFormat = 'jwt' | 'lds' - -/** - * Encapsulates the parameters required to create a - * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} - * - * @public - */ -export interface ICreateVerifiablePresentationArgs { - /** - * The json payload of the Presentation according to the - * {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model}. - * - * The signer of the Presentation is chosen based on the `holder` property - * of the `presentation` - * - * '@context', 'type' and 'issuanceDate' will be added automatically if omitted - */ - presentation: Partial - - /** - * If this parameter is true, the resulting VerifiablePresentation is sent to the - * {@link @veramo/core#IDataStore | storage plugin} to be saved - */ - save?: boolean - - /** - * Optional (only JWT) string challenge parameter to add to the verifiable presentation. - */ - challenge?: string - - /** - * Optional string domain parameter to add to the verifiable presentation. - */ - domain?: string - - /** - * The desired format for the VerifiablePresentation to be created. - * Currently, only JWT is supported - */ - proofFormat: ProofFormat - - /** - * Remove payload members during JWT-JSON transformation. Defaults to `true`. - * See https://www.w3.org/TR/vc-data-model/#jwt-encoding - */ - removeOriginalFields?: boolean -} - -/** - * Encapsulates the parameters required to create a - * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} - * - * @public - */ -export interface ICreateVerifiableCredentialArgs { - /** - * The json payload of the Credential according to the - * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} - * - * The signer of the Credential is chosen based on the `issuer.id` property - * of the `credential` - * - * '@context', 'type' and 'issuanceDate' will be added automatically if omitted - */ - credential: Partial - - /** - * If this parameter is true, the resulting VerifiablePresentation is sent to the - * {@link @veramo/core#IDataStore | storage plugin} to be saved - */ - save?: boolean - - /** - * The desired format for the VerifiablePresentation to be created. - */ - proofFormat: ProofFormat - - /** - * Remove payload members during JWT-JSON transformation. Defaults to `true`. - * See https://www.w3.org/TR/vc-data-model/#jwt-encoding - */ - removeOriginalFields?: boolean -} - -/** - * Encapsulates the parameters required to verify a - * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} - * - * @public - */ -export interface IVerifyCredentialArgs { - /** - * The json payload of the Credential according to the - * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} - * - * The signer of the Credential is chosen based on the `issuer.id` property - * of the `credential` - * - */ - credential: VerifiableCredential -} - -/** - * Encapsulates the parameters required to verify a - * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} - * - * @public - */ -export interface IVerifyPresentationArgs { - /** - * The json payload of the Credential according to the - * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} - * - * The signer of the Credential is chosen based on the `issuer.id` property - * of the `credential` - * - */ - presentation: VerifiablePresentation - - /** - * Optional (only for JWT) string challenge parameter to verify the verifiable presentation against - */ - challenge?: string - - /** - * Optional (only for JWT) string domain parameter to verify the verifiable presentation against - */ - domain?: string -} - -/** - * The interface definition for a plugin that can issue and verify Verifiable Credentials and Presentations - * that use JSON-LD format. - * - * @remarks Please see {@link https://www.w3.org/TR/vc-data-model | W3C Verifiable Credentials data model} - * - * @public - */ -export interface ICredentialIssuerLD extends IPluginMethodMap { - /** - * Creates a Verifiable Presentation. - * The payload, signer and format are chosen based on the `args` parameter. - * - * @param args - Arguments necessary to create the Presentation. - * @param context - This reserved param is automatically added and handled by the framework, *do not override* - * - * @returns - a promise that resolves to the {@link @veramo/core#VerifiablePresentation} that was requested or rejects with an error - * if there was a problem with the input or while getting the key to sign - * - * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Presentation data model } - */ - createVerifiablePresentationLD( - args: ICreateVerifiablePresentationArgs, - context: IContext, - ): Promise - - /** - * Creates a Verifiable Credential. - * The payload, signer and format are chosen based on the `args` parameter. - * - * @param args - Arguments necessary to create the Presentation. - * @param context - This reserved param is automatically added and handled by the framework, *do not override* - * - * @returns - a promise that resolves to the {@link @veramo/core#VerifiableCredential} that was requested or rejects with an error - * if there was a problem with the input or while getting the key to sign - * - * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} - */ - createVerifiableCredentialLD( - args: ICreateVerifiableCredentialArgs, - context: IContext, - ): Promise - - /** - * Verifies a Verifiable Credential JWT or LDS Format. - * - * @param args - Arguments necessary to verify a VerifiableCredential - * @param context - This reserved param is automatically added and handled by the framework, *do not override* - * - * @returns - a promise that resolves to the boolean true on successful verification or rejects on error - * - * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} - */ - verifyCredentialLD(args: IVerifyCredentialArgs, context: IContext): Promise +import { LdContextLoader } from "./ld-context-loader"; +import { _ExtendedIKey, asArray, mapIdentifierKeysToDoc, RecordLike, OrPromise } from "@veramo/utils"; - /** - * Verifies a Verifiable Presentation JWT or LDS Format. - * - * @param args - Arguments necessary to verify a VerifiableCredential - * @param context - This reserved param is automatically added and handled by the framework, *do not override* - * - * @returns - a promise that resolves to the boolean true on successful verification or rejects on error - * - * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Credential data model} - */ - verifyPresentationLD(args: IVerifyPresentationArgs, context: IContext): Promise -} +import { LdCredentialModule } from "./ld-credential-module"; +import { LdSuiteLoader } from './ld-suite-loader'; +import { + ContextDoc, + CredentialPayload, + ICreateVerifiableCredentialLDArgs, + ICreateVerifiablePresentationLDArgs, + ICredentialIssuerLD, + IRequiredContext, + IVerifyCredentialLDArgs, + IVerifyPresentationLDArgs, + MANDATORY_CREDENTIAL_CONTEXT, + PresentationPayload, +} from "./types"; -/** - * Represents the requirements that this plugin has. - * The agent that is using this plugin is expected to provide these methods. - * - * This interface can be used for static type checks, to make sure your application is properly initialized. - */ -export type IContext = IAgentContext & - Pick & - Pick> +const debug = Debug('veramo:w3c:action-handler') /** * A Veramo plugin that implements the {@link ICredentialIssuerLD} methods. @@ -307,17 +58,28 @@ export class CredentialIssuerLD implements IAgentPlugin { } /** {@inheritdoc ICredentialIssuerLD.createVerifiablePresentationLD} */ - async createVerifiablePresentationLD( - args: ICreateVerifiablePresentationArgs, - context: IContext, + public async createVerifiablePresentationLD( + args: ICreateVerifiablePresentationLDArgs, + context: IRequiredContext, ): Promise { + const presentationContext: string[] = asArray(args?.presentation?.['@context'] || []) || ['https://www.w3.org/2018/credentials/v1'] + if (presentationContext[0] !== MANDATORY_CREDENTIAL_CONTEXT) { + presentationContext.unshift(MANDATORY_CREDENTIAL_CONTEXT) + } + const presentationType = asArray(args?.presentation?.type || []) || ['VerifiablePresentation'] + if (presentationType[0] !== 'VerifiablePresentation') { + presentationType.unshift('VerifiablePresentation') + } + const presentation: Partial = { ...args?.presentation, - '@context': args?.presentation['@context'] || ['https://www.w3.org/2018/credentials/v1'], - //FIXME: make sure 'VerifiablePresentation' is the first element in this array: - type: args?.presentation?.type || ['VerifiablePresentation'], - issuanceDate: args?.presentation?.issuanceDate || new Date().toISOString(), + '@context': presentationContext, + type: presentationType, } + //issuanceDate must not be present for presentations because it is not defined in a @context + delete presentation.issuanceDate + + // FIXME: if credentials use the internal JwtProof2020, map them to only use JWT before bundling the presentation if (!presentation.holder || typeof presentation.holder === 'undefined') { throw new Error('invalid_argument: args.presentation.holder must not be empty') @@ -330,25 +92,15 @@ export class CredentialIssuerLD implements IAgentPlugin { throw new Error('invalid_argument: args.presentation.holder must be a DID managed by this agent') } try { - //FIXME: `args` should allow picking a key or key type - const key = identifier.keys.find((k) => k.type === 'Secp256k1' || k.type === 'Ed25519') - if (!key) throw Error('No signing key for ' + identifier.did) - - // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod - if (key.kid != identifier.controllerKeyId) { - throw new Error('Trying to use a non-controller key for an LD-Proof is not supported') - } - - const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - //issuanceDate must not be present for presentations because it is not defined in a @context - delete presentation.issuanceDate + const { signingKey, verificationMethodId } = await this.findSigningKeyWithId(context, identifier, args.keyRef) return await this.ldCredentialModule.signLDVerifiablePresentation( presentation, - keyPayload, + identifier.did, + signingKey, + verificationMethodId, args.challenge, args.domain, - identifier, context, ) } catch (error) { @@ -358,19 +110,29 @@ export class CredentialIssuerLD implements IAgentPlugin { } /** {@inheritdoc ICredentialIssuerLD.createVerifiableCredentialLD} */ - async createVerifiableCredentialLD( - args: ICreateVerifiableCredentialArgs, - context: IContext, + public async createVerifiableCredentialLD( + args: ICreateVerifiableCredentialLDArgs, + context: IRequiredContext, ): Promise { + const credentialContext: string[] = asArray(args?.credential?.['@context'] || []) || ['https://www.w3.org/2018/credentials/v1'] + if (credentialContext[0] !== MANDATORY_CREDENTIAL_CONTEXT) { + credentialContext.unshift(MANDATORY_CREDENTIAL_CONTEXT) + } + const credentialType = asArray(args?.credential?.type || []) || ['VerifiableCredential'] + if (credentialType[0] !== 'VerifiableCredential') { + credentialType.unshift('VerifiableCredential') + } + let issuanceDate = args?.credential?.issuanceDate || new Date().toISOString() + if (issuanceDate instanceof Date) { + issuanceDate = issuanceDate.toISOString() + } const credential: Partial = { ...args?.credential, - '@context': args?.credential?.['@context'] || ['https://www.w3.org/2018/credentials/v1'], - //FIXME: make sure 'VerifiableCredential' is the first element in this array: - type: args?.credential?.type || ['VerifiableCredential'], - issuanceDate: args?.credential?.issuanceDate || new Date().toISOString(), + '@context': credentialContext, + type: credentialType, + issuanceDate, } - //FIXME: if the identifier is not found, the error message should reflect that. const issuer = typeof credential.issuer === 'string' ? credential.issuer : credential?.issuer?.id if (!issuer || typeof issuer === 'undefined') { throw new Error('invalid_argument: args.credential.issuer must not be empty') @@ -383,26 +145,15 @@ export class CredentialIssuerLD implements IAgentPlugin { throw new Error(`invalid_argument: args.credential.issuer must be a DID managed by this agent. ${e}`) } try { - //FIXME: `args` should allow picking a key or key type - const key = identifier.keys.find((k) => k.type === 'Secp256k1' || k.type === 'Ed25519') - if (!key) throw Error('No signing key for ' + identifier.did) - - //------------------------- BEGIN JSON_LD INSERT - let verifiableCredential - // LDS ONLY works on `controllerKeyId` because it's uniquely resolvable as a verificationMethod - if (key.kid != identifier.controllerKeyId) { - throw new Error('Trying to use a non-controller key for an LD-Proof is not supported') - } + const { signingKey, verificationMethodId } = await this.findSigningKeyWithId(context, identifier, args.keyRef) - const keyPayload = await context.agent.keyManagerGet({ kid: key.kid }) - verifiableCredential = await this.ldCredentialModule.issueLDVerifiableCredential( + return await this.ldCredentialModule.issueLDVerifiableCredential( credential, - keyPayload, - identifier, + identifier.did, + signingKey, + verificationMethodId, context, ) - - return verifiableCredential } catch (error) { debug(error) return Promise.reject(error) @@ -410,18 +161,18 @@ export class CredentialIssuerLD implements IAgentPlugin { } /** {@inheritdoc ICredentialIssuerLD.verifyCredentialLD} */ - async verifyCredentialLD( - args: IVerifyCredentialArgs, - context: IContext, + public async verifyCredentialLD( + args: IVerifyCredentialLDArgs, + context: IRequiredContext, ): Promise { const credential = args.credential return this.ldCredentialModule.verifyCredential(credential, context) } /** {@inheritdoc ICredentialIssuerLD.verifyPresentationLD} */ - async verifyPresentationLD( - args: IVerifyPresentationArgs, - context: IContext, + public async verifyPresentationLD( + args: IVerifyPresentationLDArgs, + context: IRequiredContext, ): Promise { const presentation = args.presentation return this.ldCredentialModule.verifyPresentation( @@ -431,15 +182,29 @@ export class CredentialIssuerLD implements IAgentPlugin { context, ) } -} -function wrapSigner( - context: IAgentContext>, - key: IKey, - algorithm?: string, -) { - return async (data: string | Uint8Array) => { - const result = await context.agent.keyManagerSign({ keyRef: key.kid, data: data, algorithm }) - return result + private async findSigningKeyWithId(context: IAgentContext, identifier: IIdentifier, keyRef?: string): Promise<{ signingKey: IKey; verificationMethodId: string }> { + const extendedKeys: _ExtendedIKey[] = await mapIdentifierKeysToDoc(identifier, 'assertionMethod', context) + let supportedTypes = this.ldCredentialModule.ldSuiteLoader.getAllSignatureSuiteTypes() + let signingKey: _ExtendedIKey | undefined + let verificationMethodId: string + if (keyRef) { + signingKey = extendedKeys.find((k) => k.kid === keyRef) + } + if (signingKey && !supportedTypes.includes(signingKey.meta.verificationMethod.type)) { + debug('WARNING: requested signing key DOES NOT correspond to a supported Signature suite type. Looking for the next best key.') + signingKey = undefined + } + if (!signingKey) { + if (keyRef) { + debug('WARNING: no signing key was found that matches the reference provided. Searching for the first available signing key.') + } + signingKey = extendedKeys.find((k) => supportedTypes.includes(k.meta.verificationMethod.type)) + } + + if (!signingKey) throw Error(`key_not_found: No suitable signing key found for ${identifier.did}`) + verificationMethodId = signingKey.meta.verificationMethod.id + return { signingKey, verificationMethodId } } } + diff --git a/packages/credential-ld/src/index.ts b/packages/credential-ld/src/index.ts index 37b76f793..d106fc10a 100644 --- a/packages/credential-ld/src/index.ts +++ b/packages/credential-ld/src/index.ts @@ -6,15 +6,9 @@ */ export { CredentialIssuerLD, - ICredentialIssuerLD, - ICreateVerifiableCredentialArgs, - ICreateVerifiablePresentationArgs, - ProofFormat, } from './action-handler' -export { LdCredentialModule } from './ld-credential-module' -export { LdContextLoader } from './ld-context-loader' +export * from './types' export { LdDefaultContexts } from './ld-default-contexts' -export { LdSuiteLoader } from './ld-suite-loader' export { VeramoLdSignature, } from './ld-suites' diff --git a/packages/credential-ld/src/ld-context-loader.ts b/packages/credential-ld/src/ld-context-loader.ts index 2159c0662..38047a9c8 100644 --- a/packages/credential-ld/src/ld-context-loader.ts +++ b/packages/credential-ld/src/ld-context-loader.ts @@ -1,16 +1,11 @@ -export type OrPromise = T | Promise - -export type ContextDoc = { - "@context": Record -} - -export type RecordLike = Map | Record - /** * The LdContextLoader is initialized with a List of Map * that it unifies into a single Map to provide to the documentLoader within * the w3c credential module. */ +import { OrPromise, RecordLike } from "@veramo/utils"; +import { ContextDoc } from "./types"; + export class LdContextLoader { private contexts: Record> diff --git a/packages/credential-ld/src/ld-credential-module.ts b/packages/credential-ld/src/ld-credential-module.ts index 972a79a1c..9ad482df4 100644 --- a/packages/credential-ld/src/ld-credential-module.ts +++ b/packages/credential-ld/src/ld-credential-module.ts @@ -1,6 +1,5 @@ import { IAgentContext, - IIdentifier, IKey, IResolver, VerifiableCredential, @@ -28,7 +27,7 @@ export class LdCredentialModule { */ private ldContextLoader: LdContextLoader - private ldSuiteLoader: LdSuiteLoader + ldSuiteLoader: LdSuiteLoader constructor(options: { ldContextLoader: LdContextLoader; ldSuiteLoader: LdSuiteLoader }) { this.ldContextLoader = options.ldContextLoader this.ldSuiteLoader = options.ldSuiteLoader @@ -40,23 +39,23 @@ export class LdCredentialModule { // did resolution if (url.toLowerCase().startsWith('did:')) { - const didDoc = await context.agent.resolveDid({ didUrl: url }) - const returnDocument = didDoc.didDocument + const resolutionResult = await context.agent.resolveDid({ didUrl: url }) + const didDoc = resolutionResult.didDocument - if (!returnDocument) return + if (!didDoc) return // currently Veramo LD suites can modify the resolution response for DIDs from // the document Loader. This allows to fix incompatibilities between DID Documents // and LD suites to be fixed specifically within the Veramo LD Suites definition this.ldSuiteLoader .getAllSignatureSuites() - .forEach((x) => x.preDidResolutionModification(url, returnDocument)) + .forEach((x) => x.preDidResolutionModification(url, didDoc)) // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) return { contextUrl: null, documentUrl: url, - document: returnDocument, + document: didDoc, } } @@ -78,8 +77,9 @@ export class LdCredentialModule { async issueLDVerifiableCredential( credential: Partial, + issuerDid: string, key: IKey, - identifier: IIdentifier, + verificationMethodId: string, context: IAgentContext, ): Promise { const suite = this.ldSuiteLoader.getSignatureSuiteForKeyType(key.type) @@ -90,7 +90,7 @@ export class LdCredentialModule { return await vc.issue({ credential, - suite: suite.getSuiteForSigning(key, identifier, context), + suite: suite.getSuiteForSigning(key, issuerDid, verificationMethodId, context), documentLoader, compactProof: false, }) @@ -98,10 +98,11 @@ export class LdCredentialModule { async signLDVerifiablePresentation( presentation: Partial, + holderDid: string, key: IKey, + verificationMethodId: string, challenge: string | undefined, domain: string | undefined, - identifier: IIdentifier, context: IAgentContext, ): Promise { const suite = this.ldSuiteLoader.getSignatureSuiteForKeyType(key.type) @@ -111,7 +112,7 @@ export class LdCredentialModule { return await vc.signPresentation({ presentation, - suite: suite.getSuiteForSigning(key, identifier, context), + suite: suite.getSuiteForSigning(key, holderDid, verificationMethodId, context), challenge, domain, documentLoader, @@ -137,7 +138,7 @@ export class LdCredentialModule { // NOT verified. // result can include raw Error - console.log(`Error verifying LD Verifiable Credential`) + debug(`Error verifying LD Verifiable Credential: ${JSON.stringify(result, null, 2)}`) // console.log(JSON.stringify(result, null, 2)); throw Error('Error verifying LD Verifiable Credential') } diff --git a/packages/credential-ld/src/ld-suite-loader.ts b/packages/credential-ld/src/ld-suite-loader.ts index 59c17649a..ff8bf3e54 100644 --- a/packages/credential-ld/src/ld-suite-loader.ts +++ b/packages/credential-ld/src/ld-suite-loader.ts @@ -9,22 +9,25 @@ export class LdSuiteLoader { constructor(options: { veramoLdSignatures: VeramoLdSignature[] }) { - this.signatureMap = options.veramoLdSignatures.reduce((map, obj) => { - map.set(obj.getSupportedVeramoKeyType(), obj); - return map - }, new Map()) + options.veramoLdSignatures.forEach((obj) => { + this.signatureMap[obj.getSupportedVeramoKeyType()] = obj + }) } - private signatureMap: Map; + private signatureMap: Record = {}; getSignatureSuiteForKeyType(type: TKeyType) { - const suite = this.signatureMap.get(type) + const suite = this.signatureMap[type] if (suite) return suite throw new Error('No Veramo LD Signature Suite for ' + type) } getAllSignatureSuites() { - return Array.from(this.signatureMap.values()) + return Object.values(this.signatureMap) + } + + getAllSignatureSuiteTypes() { + return Object.values(this.signatureMap).map(x => x.getSupportedVerificationType()) } } \ No newline at end of file diff --git a/packages/credential-ld/src/ld-suites.ts b/packages/credential-ld/src/ld-suites.ts index efad46330..829d18a05 100644 --- a/packages/credential-ld/src/ld-suites.ts +++ b/packages/credential-ld/src/ld-suites.ts @@ -1,4 +1,4 @@ -import { IAgentContext, IIdentifier, IKey, IKeyManager, IResolver, TKeyType } from '@veramo/core' +import { IAgentContext, IKey, IKeyManager, IResolver, TKeyType } from '@veramo/core' import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' import { DIDDocument } from 'did-resolver/src/resolver' @@ -16,7 +16,8 @@ export abstract class VeramoLdSignature { abstract getSuiteForSigning( key: IKey, - identifier: IIdentifier, + issuerDid: string, + verificationMethodId: string, context: IAgentContext, ): any diff --git a/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts index 69a6fb034..146a6ecc2 100644 --- a/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts +++ b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts @@ -1,5 +1,5 @@ import { RequiredAgentMethods, VeramoLdSignature } from "../ld-suites"; -import { DIDDocument, IAgentContext, IIdentifier, IKey, TKeyType } from "@veramo/core"; +import { DIDDocument, IAgentContext, IKey, TKeyType } from "@veramo/core"; import { EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020, @@ -18,9 +18,8 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature return 'Secp256k1' } - getSuiteForSigning(key: IKey, identifier: IIdentifier, context: IAgentContext): any { - const controller = identifier.did - + getSuiteForSigning(key: IKey, did: string, verifiableMethodId: string, context: IAgentContext): any { + const controller = did const signer = { //returns a JWS detached sign: async (args: { data: Uint8Array }): Promise => { @@ -49,7 +48,7 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature signer: () => signer, type: this.getSupportedVerificationType(), controller, - id: `${controller}#controller`, // FIXME: Only default controller verificationMethod supported + id: verifiableMethodId }), }) } @@ -68,20 +67,14 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature } preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { - // specific resolution modifications // did:ethr if (didUrl.toLowerCase().startsWith('did:ethr')) { - didDoc.assertionMethod = [] // TODO: EcdsaSecp256k1RecoveryMethod2020 does not support blockchainAccountId // blockchainAccountId to ethereumAddress didDoc.verificationMethod?.forEach((x) => { if (x.blockchainAccountId) { x.ethereumAddress = x.blockchainAccountId.substring(0, x.blockchainAccountId.lastIndexOf('@')) } - - // TODO: Verification method \"did:ethr:rinkeby:0x99b5bcc24ac2701d763aac0a8466ac51a189501b#controller\" not authorized by controller for proof purpose \"assertionMethod\"." - // @ts-ignore - didDoc.assertionMethod.push(x.id) }) } } diff --git a/packages/credential-ld/src/suites/Ed25519Signature2018.ts b/packages/credential-ld/src/suites/Ed25519Signature2018.ts index 885234ef4..ce2f4155d 100644 --- a/packages/credential-ld/src/suites/Ed25519Signature2018.ts +++ b/packages/credential-ld/src/suites/Ed25519Signature2018.ts @@ -15,15 +15,11 @@ export class VeramoEd25519Signature2018 extends VeramoLdSignature { return 'Ed25519' } - getSuiteForSigning(key: IKey, identifier: IIdentifier, context: IAgentContext): any { - const controller = identifier.did + getSuiteForSigning(key: IKey, issuerDid: string, verificationMethodId: string, context: IAgentContext): any { + const controller = issuerDid // DID Key ID - let id = `${controller}#controller` - // TODO: Hacky id adjustment - if (controller.startsWith('did:key')) { - id = `${controller}#${controller.substring(controller.lastIndexOf(':') + 1)}` - } + let id = verificationMethodId const signer = { // returns a JWS detached diff --git a/packages/credential-ld/src/types.ts b/packages/credential-ld/src/types.ts new file mode 100644 index 000000000..4c518a6f6 --- /dev/null +++ b/packages/credential-ld/src/types.ts @@ -0,0 +1,254 @@ +import { + IAgentContext, + IDIDManager, + IKey, + IKeyManager, + IPluginMethodMap, + IResolver, + VerifiableCredential, + VerifiablePresentation, +} from '@veramo/core' + +/** + * The interface definition for a plugin that can issue and verify Verifiable Credentials and Presentations + * that use JSON-LD format. + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model | W3C Verifiable Credentials data model} + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ +export interface ICredentialIssuerLD extends IPluginMethodMap { + /** + * Creates a Verifiable Presentation. + * The payload, signer and format are chosen based on the `args` parameter. + * + * @param args - Arguments necessary to create the Presentation. + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the {@link @veramo/core#VerifiablePresentation} that was requested or rejects with an error + * if there was a problem with the input or while getting the key to sign + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Presentation data model } + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ + createVerifiablePresentationLD( + args: ICreateVerifiablePresentationLDArgs, + context: IRequiredContext, + ): Promise + + /** + * Creates a Verifiable Credential. + * The payload, signer and format are chosen based on the `args` parameter. + * + * @param args - Arguments necessary to create the Presentation. + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the {@link @veramo/core#VerifiableCredential} that was requested or rejects with an error + * if there was a problem with the input or while getting the key to sign + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ + createVerifiableCredentialLD( + args: ICreateVerifiableCredentialLDArgs, + context: IRequiredContext, + ): Promise + + /** + * Verifies a Verifiable Credential JWT or LDS Format. + * + * @param args - Arguments necessary to verify a VerifiableCredential + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the boolean true on successful verification or rejects on error + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ + verifyCredentialLD(args: IVerifyCredentialLDArgs, context: IRequiredContext): Promise + + /** + * Verifies a Verifiable Presentation JWT or LDS Format. + * + * @param args - Arguments necessary to verify a VerifiableCredential + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to the boolean true on successful verification or rejects on error + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Credential data model} + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ + verifyPresentationLD(args: IVerifyPresentationLDArgs, context: IRequiredContext): Promise +} + +/** + * Encapsulates the parameters required to create a + * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ +export interface ICreateVerifiablePresentationLDArgs { + /** + * The json payload of the Presentation according to the + * {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model}. + * + * The signer of the Presentation is chosen based on the `holder` property + * of the `presentation` + * + * '@context', 'type' and 'issuanceDate' will be added automatically if omitted + */ + presentation: Partial + + /** + * Optional (only JWT) string challenge parameter to add to the verifiable presentation. + */ + challenge?: string + + /** + * Optional string domain parameter to add to the verifiable presentation. + */ + domain?: string + + /** + * Optional. The key handle ({@link IKey#kid}) from the internal database. + */ + keyRef?: string +} + +/** + * Encapsulates the parameters required to create a + * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ +export interface ICreateVerifiableCredentialLDArgs { + /** + * The json payload of the Credential according to the + * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} + * + * The signer of the Credential is chosen based on the `issuer.id` property + * of the `credential` + * + * '@context', 'type' and 'issuanceDate' will be added automatically if omitted + */ + credential: Partial + + /** + * Optional. The key handle ({@link IKey#kid}) from the internal database. + */ + keyRef?: string +} + +/** + * Encapsulates the parameters required to verify a + * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ +export interface IVerifyCredentialLDArgs { + /** + * The json payload of the Credential according to the + * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} + * + * The signer of the Credential is chosen based on the `issuer.id` property + * of the `credential` + * + */ + credential: VerifiableCredential +} + +/** + * Encapsulates the parameters required to verify a + * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ +export interface IVerifyPresentationLDArgs { + /** + * The json payload of the Credential according to the + * {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model} + * + * The signer of the Credential is chosen based on the `issuer.id` property + * of the `credential` + * + */ + presentation: VerifiablePresentation + + /** + * Optional (only for JWT) string challenge parameter to verify the verifiable presentation against + */ + challenge?: string + + /** + * Optional (only for JWT) string domain parameter to verify the verifiable presentation against + */ + domain?: string +} + +/** + * Represents the requirements that this plugin has. + * The agent that is using this plugin is expected to provide these methods. + * + * This interface can be used for static type checks, to make sure your application is properly initialized. + * + * @beta This API is likely to change without a BREAKING CHANGE notice + */ +export type IRequiredContext = IAgentContext & + Pick> + +export type JWT = string + +export type IssuerType = { id: string, [x: string]: any } | string +export type CredentialSubject = { id?: string, [x: string]: any } + +export interface CredentialStatus { + id: string + type: string + + [x: string]: any +} + +export type DateType = string | Date + +/** + * used as input when creating Verifiable Credentials + */ +export interface CredentialPayload { + '@context': string | string[] + id?: string + type: string | string[] + issuer: IssuerType + issuanceDate: DateType + expirationDate?: DateType + credentialSubject: CredentialSubject + credentialStatus?: CredentialStatus + + [x: string]: any +} + +/** + * used as input when creating Verifiable Presentations + */ +export interface PresentationPayload { + '@context': string | string[] + type: string | string[] + id?: string + verifiableCredential?: (VerifiableCredential | JWT)[] + holder: string + verifier?: string | string[] + issuanceDate?: string + expirationDate?: string, + + [x: string]: any +} + +export type ContextDoc = { + "@context": Record +} + +export const MANDATORY_CREDENTIAL_CONTEXT = 'https://www.w3.org/2018/credentials/v1' \ No newline at end of file diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 252c3f39f..afa618a8f 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -10,8 +10,6 @@ import { IIdentifier, VerifiableCredential, VerifiablePresentation, W3CPresentation, } from '@veramo/core' -import { ICredentialIssuerLD } from "@veramo/credential-ld" - import { createVerifiableCredentialJwt, createVerifiablePresentationJwt, @@ -256,8 +254,7 @@ export interface ICredentialIssuer extends IPluginMethodMap { export type IContext = IAgentContext & Pick & - Pick & - ICredentialIssuerLD> + Pick> /** * A Veramo plugin that implements the {@link ICredentialIssuer} methods. diff --git a/packages/did-comm/src/didcomm.ts b/packages/did-comm/src/didcomm.ts index 9326dda44..57be93597 100644 --- a/packages/did-comm/src/didcomm.ts +++ b/packages/did-comm/src/didcomm.ts @@ -274,11 +274,11 @@ export class DIDComm implements IAgentPlugin { const didDocument: DIDDocument = await resolveDidOrThrow(args?.message?.to, context) // 2.1 extract all recipient key agreement keys and normalize them - const keyAgreementKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( + const keyAgreementKeys: _NormalizedVerificationMethod[] = (await dereferenceDidKeys( didDocument, 'keyAgreement', context, - ) + )).filter(k => k.publicKeyHex?.length! > 0) if (keyAgreementKeys.length === 0) { throw new Error(`key_not_found: no key agreement keys found for recipient ${args?.message?.to}`) diff --git a/packages/utils/package.json b/packages/utils/package.json index f9686fa04..86449b407 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -8,6 +8,7 @@ "build": "tsc" }, "dependencies": { + "@ethersproject/transactions": "^5.5.0", "@stablelib/ed25519": "^1.0.2", "@veramo/core": "^3.1.0", "cross-fetch": "^3.1.4", diff --git a/packages/utils/src/types/utility-types.ts b/packages/utils/src/types/utility-types.ts index 60d890eee..6fe6a0b30 100644 --- a/packages/utils/src/types/utility-types.ts +++ b/packages/utils/src/types/utility-types.ts @@ -30,3 +30,14 @@ export type _NormalizedVerificationMethod = Omit< VerificationMethod, 'publicKeyBase58' | 'publicKeyBase64' | 'publicKeyJwk' > + +/** + * Accept a Type or a Promise of that Type + */ +export type OrPromise = T | Promise + +/** + * A mapping of string to another type. + * Both Map and Record are accepted. + */ +export type RecordLike = Map | Record diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts index 330b58c52..24fc91088 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/utils.ts @@ -1,15 +1,13 @@ import { convertPublicKeyToX25519, convertSecretKeyToX25519 } from '@stablelib/ed25519' import { computePublicKey } from '@ethersproject/signing-key' +import { computeAddress } from '@ethersproject/transactions' import { DIDDocumentSection, IAgentContext, IIdentifier, IKey, IResolver } from '@veramo/core' -import { VerificationMethod, DIDDocument } from 'did-resolver' +import { DIDDocument, VerificationMethod } from 'did-resolver' import * as u8a from 'uint8arrays' import Debug from 'debug' -import { - _ExtendedIKey, - _ExtendedVerificationMethod, - _NormalizedVerificationMethod, -} from './types/utility-types' +import { _ExtendedIKey, _ExtendedVerificationMethod, _NormalizedVerificationMethod, } from './types/utility-types' + const debug = Debug('veramo:utils') export function bytesToBase64url(b: Uint8Array): string { @@ -42,7 +40,11 @@ export function decodeJoseBlob(blob: string) { } export function isDefined(arg: T): arg is Exclude { - return arg !== null && typeof arg !== 'undefined' + return arg !== null && typeof arg !== 'undefined' +} + +export function asArray(arg: T | T[]): T[] { + return Array.isArray(arg) ? arg : [arg] } export function convertIdentifierEncryptionKeys(identifier: IIdentifier): IKey[] { @@ -70,14 +72,31 @@ export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[] .map((key) => { if (key.type === 'Secp256k1') { const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') - const compressedKey = computePublicKey(publicBytes, true).substring(2) - key.publicKeyHex = compressedKey + key.publicKeyHex = computePublicKey(publicBytes, true).substring(2) + key.meta = { ...key.meta } + key.meta.ethereumAddress = computeAddress('0x' + key.publicKeyHex) } return key }) .filter(isDefined) } +function compareBlockchainAccountId(localKey: IKey, verificationMethod: _NormalizedVerificationMethod) { + if (verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2020' || localKey.type !== 'Secp256k1') { + return false; + } + let vmEthAddr = verificationMethod.ethereumAddress?.toLowerCase() + if (!vmEthAddr) { + if (verificationMethod.blockchainAccountId?.includes('@eip155')) { + vmEthAddr = verificationMethod.blockchainAccountId?.split('@eip155')[0].toLowerCase() + } else if (verificationMethod.blockchainAccountId?.startsWith('eip155')) { + vmEthAddr = verificationMethod.blockchainAccountId.split(':')[2]?.toLowerCase() + } + } + const computedAddr = computeAddress('0x' + localKey.publicKeyHex).toLowerCase() + return (computedAddr === vmEthAddr) +} + export async function mapIdentifierKeysToDoc( identifier: IIdentifier, section: DIDDocumentSection = 'keyAgreement', @@ -86,7 +105,7 @@ export async function mapIdentifierKeysToDoc( const didDocument = await resolveDidOrThrow(identifier.did, context) // dereference all key agreement keys from DID document and normalize - const keyAgreementKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( + const documentKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( didDocument, section, context, @@ -99,9 +118,9 @@ export async function mapIdentifierKeysToDoc( localKeys = compressIdentifierSecp256k1Keys(identifier) } // finally map the didDocument keys to the identifier keys by comparing `publicKeyHex` - const extendedKeys: _ExtendedIKey[] = keyAgreementKeys + const extendedKeys: _ExtendedIKey[] = documentKeys .map((verificationMethod) => { - const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex) + const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex || compareBlockchainAccountId(localKey, verificationMethod)) if (localKey) { const { meta, ...localProps } = localKey return { ...localProps, meta: { ...meta, verificationMethod } } @@ -175,7 +194,6 @@ export async function dereferenceDidKeys( } return newKey }) - .filter((key) => key.publicKeyHex.length > 0) } /** From 9c586275c79a62fc192f1e605d5bc1454f38d996 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Mon, 22 Nov 2021 12:17:34 +0100 Subject: [PATCH 41/48] feat(cli): update default config to use CredentialIssuerLD module --- packages/cli/default/client.yml | 4 ++- packages/cli/default/default.yml | 53 +++++++++------------------- scripts/prepare-integration-tests.ts | 7 ++-- 3 files changed, 21 insertions(+), 43 deletions(-) diff --git a/packages/cli/default/client.yml b/packages/cli/default/client.yml index 02c799b4f..6150612f9 100644 --- a/packages/cli/default/client.yml +++ b/packages/cli/default/client.yml @@ -57,8 +57,10 @@ agent: - unpackDIDCommMessage - sendDIDCommMessage - sendMessageDIDCommAlpha1 - - createVerifiablePresentation - createVerifiableCredential + - createVerifiablePresentation + - verifyCredential + - verifyPresentation - createSelectiveDisclosureRequest - getVerifiableCredentialsForSdr - validatePresentationAgainstSdr diff --git a/packages/cli/default/default.yml b/packages/cli/default/default.yml index 17ed1eb9f..600d7ad34 100644 --- a/packages/cli/default/default.yml +++ b/packages/cli/default/default.yml @@ -54,13 +54,13 @@ constants: - unpackDIDCommMessage - sendDIDCommMessage - sendMessageDIDCommAlpha1 - - createVerifiablePresentation - createVerifiableCredential + - createVerifiablePresentation + - verifyCredential + - verifyPresentation - createSelectiveDisclosureRequest - getVerifiableCredentialsForSdr - validatePresentationAgainstSdr - - verifyCredential - - verifyPresentation # Data base dbConnection: @@ -277,40 +277,18 @@ didDiscovery: - $require: '@veramo/did-manager#AliasDiscoveryProvider' - $require: '@veramo/data-store#ProfileDiscoveryProvider' -# LD-Suite Loader -ldSuiteLoader: - - $require: '@veramo/credential-w3c#LdSuiteLoader' - $args: - - veramoLdSignatures: - - $require: '@veramo/credential-w3c#VeramoEd25519Signature2018' - - $require: '@veramo/credential-w3c#VeramoEcdsaSecp256k1RecoverySignature2020' - -# LD-Context Loader -ldContextLoader: - - $require: '@veramo/credential-w3c#LdContextLoader' - $args: - - contextsPaths: - # The LdDefaultContext is a "catch-all" for now. - - $require: '@veramo/credential-w3c?t=object#LdDefaultContexts' - - $require: '@transmute/credentials-context?t=object#contexts' - # others should be included here - - -# LD-Module -ldCredentialModule: - - $require: '@veramo/credential-w3c#LdCredentialModule' - $args: - - ldContextLoader: - - $ref: /ldContextLoader - - ldSuiteLoader: - - $ref: /ldSuiteLoader - # W3C credentialPlugin -credentialPlugin: - - $require: '@veramo/credential-w3c#CredentialIssuer' - $args: - - ldCredentialModule: - $ref: /ldCredentialModule +credentialIssuerLD: + $require: '@veramo/credential-ld#CredentialIssuerLD' + $args: + - suites: + - $require: '@veramo/credential-ld#VeramoEd25519Signature2018' + - $require: '@veramo/credential-ld#VeramoEcdsaSecp256k1RecoverySignature2020' + contextMaps: + # The LdDefaultContext is a "catch-all" for now. + - $require: '@veramo/credential-ld?t=object#LdDefaultContexts' + - $require: '@transmute/credentials-context?t=object#contexts' + # others should be included here # Agent agent: @@ -324,7 +302,8 @@ agent: - $ref: /didDiscovery - $ref: /messageHandler - $require: '@veramo/did-comm#DIDComm' - - $ref: /credentialPlugin + - $require: '@veramo/credential-w3c#CredentialIssuer' + - $ref: /credentialIssuerLD - $require: '@veramo/selective-disclosure#SelectiveDisclosure' - $require: '@veramo/data-store#DataStore' $args: diff --git a/scripts/prepare-integration-tests.ts b/scripts/prepare-integration-tests.ts index 5f5972097..995e695d4 100644 --- a/scripts/prepare-integration-tests.ts +++ b/scripts/prepare-integration-tests.ts @@ -1,15 +1,12 @@ import { resolve } from 'path' -import { writeFileSync, readFileSync } from 'fs' +import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'fs' import * as TJS from 'ts-json-schema-generator' -import { existsSync, readdirSync, copyFileSync, mkdirSync, unlinkSync } from 'fs' import { DocFencedCode } from '@microsoft/tsdoc' import { + ApiMethodSignature, ApiModel, - ApiPackage, ApiParameterListMixin, - ApiDocumentedItem, ApiReturnTypeMixin, - ApiMethodSignature, } from '@microsoft/api-extractor-model' const outputFolder = './temp' From d4397012cf5a80852e103dc016bd575cb0a3e6c2 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Mon, 22 Nov 2021 13:42:10 +0100 Subject: [PATCH 42/48] feat(credential-w3c): autoselect presentation audience if it's a managed DID --- packages/credential-w3c/src/action-handler.ts | 22 +++++++++++++++++-- .../credential-w3c/src/message-handler.ts | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index afa618a8f..1e535a92d 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -20,10 +20,13 @@ import { normalizePresentation, } from 'did-jwt-vc' +import { decodeJWT } from 'did-jwt' + import { schema } from './' import Debug from 'debug' import { JWT } from 'did-jwt-vc/lib/types' import { Resolvable } from 'did-resolver' +import { asArray } from "@veramo/utils"; const debug = Debug('veramo:w3c:action-handler') @@ -252,7 +255,7 @@ export interface ICredentialIssuer extends IPluginMethodMap { * This interface can be used for static type checks, to make sure your application is properly initialized. */ export type IContext = IAgentContext & + Pick & Pick & Pick> @@ -453,10 +456,25 @@ export class CredentialIssuer implements IAgentPlugin { } const resolver = { resolve: (didUrl: string) => context.agent.resolveDid({ didUrl }) } as Resolvable + let audience = args.domain + if (!audience) { + const { payload } = await decodeJWT(jwt) + if (payload.aud) { + // automatically add a managed DID as audience if one is found + const intendedAudience = asArray(payload.aud) + const managedDids = await context.agent.didManagerFind() + const filtered = managedDids.filter(identifier => intendedAudience.includes(identifier.did)) + if (filtered.length > 0) { + audience = filtered[0].did + } + } + } + try { const verification = await verifyPresentationJWT(jwt, resolver, { challenge: args.challenge, - domain: args.domain + domain: args.domain, + audience }) return true } catch (e: any) { diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index d98b11b91..b2e3af080 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -126,7 +126,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { // throws on error. await context.agent.verifyPresentation({ presentation, - // TODO: HARDCODED CHALLENGE VERIFICATION FOR NOW + // FIXME: HARDCODED CHALLENGE VERIFICATION FOR NOW challenge: 'VERAMO', domain: 'VERAMO' }) From fe4b4cf140bd4d9188fc7a27d8c9af025a3ec560 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Mon, 22 Nov 2021 13:46:35 +0100 Subject: [PATCH 43/48] feat(cli): add CLI methods to verify credentials and presentations --- packages/cli/package.json | 1 + packages/cli/src/credential.ts | 37 ++++++++++++++++++++++++++++ packages/cli/src/presentation.ts | 41 +++++++++++++++++++++++++++++++- packages/cli/src/util.ts | 24 +++++++++++++++++++ packages/core/package.json | 5 +--- packages/tsconfig.json | 5 ++-- 6 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 packages/cli/src/util.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index 3bbc55d60..1db1421a1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -53,6 +53,7 @@ "inquirer": "^8.0.0", "inquirer-autocomplete-prompt": "^1.2.0", "json-schema": "^0.4.0", + "json5": "^2.2.0", "jsonpointer": "^5.0.0", "oas-resolver": "^2.5.3", "openapi-types": "9.3.1", diff --git a/packages/cli/src/credential.ts b/packages/cli/src/credential.ts index af003b6c6..2ff7accac 100644 --- a/packages/cli/src/credential.ts +++ b/packages/cli/src/credential.ts @@ -3,6 +3,9 @@ import { getAgent } from './setup' import { program } from 'commander' import inquirer from 'inquirer' import qrcode from 'qrcode-terminal' +import * as fs from 'fs' +import * as json5 from 'json5' +import { readStdin } from './util' const credential = program.command('credential').description('W3C Verifiable Credential') @@ -148,3 +151,37 @@ credential console.dir(verifiableCredential, { depth: 10 }) } }) + +credential + .command('verify') + .description('Verify a W3C Verifiable Credential provided as raw string, file or stdin') + .option('-f, --filename ', 'Optional. Read the credential from a file instead of stdin') + .option('-r, --raw ', 'Optional. Specify the credential as a parameter instead of file or stdin') + .action(async (options) => { + const agent = getAgent(program.opts().config) + let raw: string = '' + if (options.raw) { + raw = options.raw + } else if (options.filename) { + raw = await fs.promises.readFile(options.filename, 'utf-8') + } else { + raw = await readStdin() + } + let credentialAsJSON: any + try { + credentialAsJSON = json5.parse(raw) + } catch (e: any) { + credentialAsJSON = { + proof: { + type: 'JwtProof2020', + jwt: raw + } + } as any + } + const result = await agent.verifyCredential({ credential: credentialAsJSON }) + if (result === true) { + console.log('Credential was verified successfully.') + } else { + console.error('Credential could not be verified.') + } + }) diff --git a/packages/cli/src/presentation.ts b/packages/cli/src/presentation.ts index d96cbfe50..4f0f1aa4d 100644 --- a/packages/cli/src/presentation.ts +++ b/packages/cli/src/presentation.ts @@ -1,8 +1,10 @@ -import { W3CCredential } from '@veramo/core' import { getAgent } from './setup' import { program } from 'commander' import inquirer from 'inquirer' import qrcode from 'qrcode-terminal' +import { readStdin } from "./util"; +import * as fs from 'fs' +import * as json5 from 'json5' const presentation = program.command('presentation').description('W3C Verifiable Presentation') @@ -153,3 +155,40 @@ presentation } } }) + + +presentation + .command('verify') + .description('Verify a W3C Verifiable Presentation provided as a string param or a file or from stdin') + .option('-c, --challenge ', 'Optional. Specify a challenge that the presentation should match.') + .option('-d, --domain ', 'Optional. Specify a domain that the presentation should match.') + .option('-f, --filename ', 'Optional. Read the presentation from a file instead of stdin') + .option('-r, --raw ', 'Optional. Specify the presentation as a parameter string instead of a file or stdin.') + .action(async (options) => { + const agent = getAgent(program.opts().config) + let raw: string = '' + if (options.raw) { + raw = options.raw + } else if (options.filename) { + raw = await fs.promises.readFile(options.filename, 'utf-8') + } else { + raw = await readStdin() + } + let presentationAsJSON: any + try { + presentationAsJSON = json5.parse(raw) + } catch (e: any) { + presentationAsJSON = { + proof: { + type: 'JwtProof2020', + jwt: raw + } + } as any + } + const result = await agent.verifyPresentation({ presentation: presentationAsJSON, challenge: options.challenge, domain: options.domain }) + if (result === true) { + console.log('Presentation was verified successfully.') + } else { + console.error('Presentation could not be verified.') + } + }) diff --git a/packages/cli/src/util.ts b/packages/cli/src/util.ts new file mode 100644 index 000000000..452cb26fe --- /dev/null +++ b/packages/cli/src/util.ts @@ -0,0 +1,24 @@ +export async function readStdin(): Promise { + return new Promise((resolve, reject) => { + let data = ''; + process.stdin.setEncoding('utf-8'); + + process.stdin.on('readable', () => { + let chunk: string + while (chunk = process.stdin.read()) { + data += chunk; + } + }); + + process.stdin.on('end', () => { + // There will be a trailing \n from the user hitting enter. Get rid of it. + data = data.replace(/\n$/, ''); + resolve(data) + }) + + process.stdin.on('error', (error) => { + reject(error) + }) + } + ) +} \ No newline at end of file diff --git a/packages/core/package.json b/packages/core/package.json index ae0d16468..1a07dbd7e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -41,10 +41,7 @@ "repository": "git@github.com:uport-project/veramo.git", "author": "Simonas Karuzas ", "contributors": [ - { - "name": "Mircea Nistor", - "email": "mircea.nistor@mesh.xyz" - } + "Mircea Nistor " ], "license": "Apache-2.0", "keywords": [] diff --git a/packages/tsconfig.json b/packages/tsconfig.json index 2b74ad93a..fd9100e65 100644 --- a/packages/tsconfig.json +++ b/packages/tsconfig.json @@ -2,8 +2,8 @@ "files": [], "references": [ { "path": "core" }, - { "path": "credential-w3c" }, { "path": "credential-ld" }, + { "path": "credential-w3c" }, { "path": "data-store" }, { "path": "did-comm" }, { "path": "did-discovery" }, @@ -19,6 +19,7 @@ { "path": "remote-client" }, { "path": "remote-server" }, { "path": "selective-disclosure" }, - { "path": "url-handler" } + { "path": "url-handler" }, + { "path": "utils" } ] } From 00500227c1329e7c8781a9905644a5297e1cbafd Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Tue, 23 Nov 2021 12:26:34 +0100 Subject: [PATCH 44/48] feat(core): add common type definitions for credentials and presentations --- __tests__/initial.migration.test.ts | 26 +- __tests__/restAgent.test.ts | 1 + __tests__/shared/dbInitOptions.ts | 31 +- __tests__/shared/didCommWithEthrDidFlow.ts | 11 +- __tests__/shared/didCommWithFakeDidFlow.ts | 8 +- __tests__/shared/didDiscovery.ts | 3 +- __tests__/shared/didManager.ts | 2 +- __tests__/shared/documentationExamples.ts | 2 +- __tests__/shared/handleSdrMessage.ts | 2 +- __tests__/shared/keyManager.ts | 4 +- __tests__/shared/messageHandler.ts | 2 +- __tests__/shared/resolveDid.ts | 2 +- __tests__/shared/saveClaims.ts | 5 +- __tests__/shared/verifiableDataJWT.ts | 17 +- __tests__/shared/verifiableDataLD.ts | 2 +- __tests__/shared/webDidFlow.ts | 2 +- __tests__/utils/fake-did.ts | 9 +- __tests__/utils/ganache-provider.ts | 1 + docsconfig.json | 5 +- package.json | 2 +- packages/cli/src/credential.ts | 4 +- packages/cli/src/explore/credentials.ts | 5 +- packages/cli/src/explore/presentations.ts | 5 +- packages/cli/src/presentation.ts | 3 +- packages/cli/src/sdr.ts | 8 +- packages/core/plugin.schema.json | 406 ++++++++++------ packages/core/src/index.ts | 1 + packages/core/src/types/IDataStore.ts | 5 +- packages/core/src/types/IMessage.ts | 65 +-- packages/core/src/types/vc-data-model.ts | 176 +++++++ packages/credential-ld/package.json | 1 - packages/credential-ld/plugin.schema.json | 373 +++++++-------- packages/credential-ld/src/action-handler.ts | 62 +-- .../credential-ld/src/ld-credential-module.ts | 11 +- packages/credential-ld/src/ld-suites.ts | 7 +- .../EcdsaSecp256k1RecoverySignature2020.ts | 17 +- .../src/suites/Ed25519Signature2018.ts | 5 +- packages/credential-ld/src/types.ts | 56 +-- packages/credential-w3c/package.json | 4 +- packages/credential-w3c/plugin.schema.json | 444 +++++++++--------- .../src/__tests__/action-handler.test.ts | 91 ++-- packages/credential-w3c/src/action-handler.ts | 89 ++-- .../credential-w3c/src/message-handler.ts | 27 +- .../credential-w3c/types/blakejs/index.d.ts | 1 - packages/data-store/package.json | 1 + packages/data-store/plugin.schema.json | 202 +++++--- .../src/__tests__/data-store-orm.test.ts | 16 +- packages/data-store/src/data-store-orm.ts | 2 +- .../data-store/src/entities/credential.ts | 19 +- .../data-store/src/entities/presentation.ts | 51 +- packages/data-store/tsconfig.json | 3 +- packages/did-comm/plugin.schema.json | 202 +++++--- .../message-handler/src/message-handler.ts | 4 +- packages/selective-disclosure/package.json | 4 +- .../selective-disclosure/plugin.schema.json | 208 +++++--- .../src/action-handler.ts | 98 ++-- .../src/message-handler.ts | 11 +- .../types/blakejs/index.d.ts | 1 - packages/utils/package.json | 2 + packages/utils/src/credential-utils.ts | 106 +++++ packages/utils/src/did-utils.ts | 185 ++++++++ packages/utils/src/encodings.ts | 30 ++ packages/utils/src/index.ts | 4 +- packages/utils/src/utils.ts | 216 --------- .../types/import.types.d.ts} | 0 yarn.lock | 6 +- 66 files changed, 1914 insertions(+), 1460 deletions(-) create mode 100644 packages/core/src/types/vc-data-model.ts delete mode 100644 packages/credential-w3c/types/blakejs/index.d.ts delete mode 100644 packages/selective-disclosure/types/blakejs/index.d.ts create mode 100644 packages/utils/src/credential-utils.ts create mode 100644 packages/utils/src/did-utils.ts create mode 100644 packages/utils/src/encodings.ts rename packages/{credential-ld/types/blakejs/index.d.ts => utils/types/import.types.d.ts} (100%) diff --git a/__tests__/initial.migration.test.ts b/__tests__/initial.migration.test.ts index 7f9bde92f..e1f31f199 100644 --- a/__tests__/initial.migration.test.ts +++ b/__tests__/initial.migration.test.ts @@ -3,29 +3,36 @@ * TypeORM migrations were available (before Veramo 3.0.0) */ -import { createAgent, TAgent, IDIDManager, IResolver, IKeyManager, IDataStore } from '../packages/core/src' +import { + createAgent, + IDataStore, + IDIDManager, + IKeyManager, + IResolver, + TAgent, + VerifiableCredential +} from '../packages/core/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' -import { KeyDIDProvider } from '../packages/did-provider-key/src' +import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' import { DIDComm, IDIDComm } from '../packages/did-comm/src' import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src' import { - Entities, - IDataStoreORM, DataStore, DataStoreORM, - KeyStore, DIDStore, + Entities, + IDataStoreORM, + KeyStore, migrations, PrivateKeyStore, } from '../packages/data-store/src' -import { getDidKeyResolver } from '../packages/did-provider-key/src' import { KeyManager } from '../packages/key-manager/src' import { DIDManager } from '../packages/did-manager/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' -import { createConnection, Connection, ConnectionOptions } from 'typeorm' +import { Connection, ConnectionOptions, createConnection } from 'typeorm' import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' @@ -196,10 +203,11 @@ describe('database initial migration tests', () => { }) it('reads a presentation by hash', async () => { - const cred = await agent.dataStoreGetVerifiablePresentation({ + const presentation = await agent.dataStoreGetVerifiablePresentation({ hash: '4cfe965596a0d343ff2cc02afd32068bced34caa2b1e7e3f253b23e420de106b58a613f06f55d9d9cbbdbe0b0f051a45d44404020b123c58f0ee48bdaeafdc90', }) - expect(cred?.verifiableCredential?.[0]?.credentialSubject?.name).toEqual('Alice') + const cred0: VerifiableCredential = presentation?.verifiableCredential?.[0] as VerifiableCredential + expect(cred0.credentialSubject?.name).toEqual('Alice') }) it('reads existing messages', async () => { diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 2bfa0a611..9b3a04afc 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -57,6 +57,7 @@ import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { getResolver as webDidResolver } from 'web-did-resolver' +// @ts-ignore import express from 'express' import { Server } from 'http' import { contexts as credential_contexts } from '@transmute/credentials-context' diff --git a/__tests__/shared/dbInitOptions.ts b/__tests__/shared/dbInitOptions.ts index b53fbe867..7bdb52e4f 100644 --- a/__tests__/shared/dbInitOptions.ts +++ b/__tests__/shared/dbInitOptions.ts @@ -1,28 +1,27 @@ import { - TAgent, + IAgentOptions, + IDataStore, IDIDManager, - IKeyManager, IIdentifier, - IAgentOptions, IKey, - IDataStore, + IKeyManager, IMessageHandler, IResolver, + TAgent, } from '@veramo/core/src' import { IDataStoreORM } from '@veramo/data-store/src' import { ICredentialIssuer } from '@veramo/credential-w3c/src' import { IDIDComm, IPackedDIDCommMessage } from '../../packages/did-comm/src' +import { extractIssuer } from "../../packages/utils" -type ConfiguredAgent = TAgent< - IDataStoreORM & - IDataStore & - IDIDManager & - IKeyManager & - ICredentialIssuer & - IDIDComm & - IMessageHandler & - IResolver -> +type ConfiguredAgent = TAgent export default (testContext: { getAgent: () => ConfiguredAgent @@ -145,6 +144,7 @@ export default (testContext: { proofFormat: 'jwt', credential: { credentialSubject: { id: identifier.did, pseudonym: 'FakeAlice' }, + type: ['Example'], issuer: identifier.did, }, }) @@ -155,7 +155,7 @@ export default (testContext: { hash: credentialId, }) credentialRaw = retrieved.proof.jwt - expect(retrieved.issuer.id).toEqual(identifier.did) + expect(extractIssuer(retrieved)).toEqual(identifier.did) }) let packedMessage: IPackedDIDCommMessage @@ -182,6 +182,7 @@ export default (testContext: { const incomingCredential = await agent.createVerifiableCredential({ proofFormat: 'jwt', credential: { + type: ['Example'], credentialSubject: { incoming: 'yes', }, diff --git a/__tests__/shared/didCommWithEthrDidFlow.ts b/__tests__/shared/didCommWithEthrDidFlow.ts index 083dcdd2a..e438e9f4f 100644 --- a/__tests__/shared/didCommWithEthrDidFlow.ts +++ b/__tests__/shared/didCommWithEthrDidFlow.ts @@ -1,17 +1,18 @@ import { - TAgent, + IAgentOptions, IDIDManager, - IKeyManager, + IEventListener, IIdentifier, - IResolver, IKey, - IEventListener, - IAgentOptions, + IKeyManager, IMessageHandler, + IResolver, + TAgent, } from '../../packages/core/src' import { IDIDComm } from '../../packages/did-comm/src' import { MessagingRouter, RequestWithAgentRouter } from '../../packages/remote-server/src' import * as u8a from 'uint8arrays' +// @ts-ignore import express from 'express' import { Server } from 'http' diff --git a/__tests__/shared/didCommWithFakeDidFlow.ts b/__tests__/shared/didCommWithFakeDidFlow.ts index ca7f992f7..29b0af21a 100644 --- a/__tests__/shared/didCommWithFakeDidFlow.ts +++ b/__tests__/shared/didCommWithFakeDidFlow.ts @@ -1,11 +1,11 @@ import { - TAgent, + IAgentOptions, IDIDManager, - IKeyManager, + IEventListener, IIdentifier, + IKeyManager, IResolver, - IEventListener, - IAgentOptions, + TAgent, } from '../../packages/core/src' import { IDIDComm } from '../../packages/did-comm/src' diff --git a/__tests__/shared/didDiscovery.ts b/__tests__/shared/didDiscovery.ts index a72d07d09..42301177d 100644 --- a/__tests__/shared/didDiscovery.ts +++ b/__tests__/shared/didDiscovery.ts @@ -1,9 +1,8 @@ import { IDIDDiscovery } from '../../packages/did-discovery/src' -import { TAgent, IDIDManager, IKeyManager, IIdentifier } from '../../packages/core/src' +import { IAgentOptions, IDIDManager, TAgent } from '../../packages/core/src' import { IDataStoreORM } from '../../packages/data-store/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' import { getConnection } from 'typeorm' -import { IAgentOptions } from '@veramo/core' type ConfiguredAgent = TAgent diff --git a/__tests__/shared/didManager.ts b/__tests__/shared/didManager.ts index 74f583b2a..907e96d30 100644 --- a/__tests__/shared/didManager.ts +++ b/__tests__/shared/didManager.ts @@ -1,4 +1,4 @@ -import { TAgent, IDIDManager, IKeyManager, IIdentifier } from '../../packages/core/src' +import { IDIDManager, IIdentifier, IKeyManager, TAgent } from '../../packages/core/src' type ConfiguredAgent = TAgent diff --git a/__tests__/shared/documentationExamples.ts b/__tests__/shared/documentationExamples.ts index 094097b71..2f046df06 100644 --- a/__tests__/shared/documentationExamples.ts +++ b/__tests__/shared/documentationExamples.ts @@ -5,7 +5,7 @@ * To document a new package, add it to docsconfig.json array and have it processed with `extract-api` or `generate-plugin-schema`. */ -import { TAgent, IDIDManager, IDataStore, IMessageHandler } from '../../packages/core/src' +import { IDataStore, IDIDManager, IMessageHandler, TAgent } from '../../packages/core/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' import { ISelectiveDisclosure } from '../../packages/selective-disclosure/src' import { IDataStoreORM } from '../../packages/data-store/src' diff --git a/__tests__/shared/handleSdrMessage.ts b/__tests__/shared/handleSdrMessage.ts index 6a022b596..78653b0ea 100644 --- a/__tests__/shared/handleSdrMessage.ts +++ b/__tests__/shared/handleSdrMessage.ts @@ -1,4 +1,4 @@ -import { TAgent, IDIDManager, IIdentifier, IDataStore, IMessageHandler } from '../../packages/core/src' +import { IDataStore, IDIDManager, IIdentifier, IMessageHandler, TAgent } from '../../packages/core/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' import { ISelectiveDisclosure, SelectiveDisclosure } from '../../packages/selective-disclosure/src' import { IDataStoreORM } from '../../packages/data-store/src' diff --git a/__tests__/shared/keyManager.ts b/__tests__/shared/keyManager.ts index 53de3f7cd..299f921cb 100644 --- a/__tests__/shared/keyManager.ts +++ b/__tests__/shared/keyManager.ts @@ -1,5 +1,5 @@ -import { TAgent, IDIDManager, IKeyManager, IAgentOptions, IKey, TKeyType } from '../../packages/core/src' -import { serialize, computeAddress } from '@ethersproject/transactions' +import { IAgentOptions, IDIDManager, IKeyManager, TAgent, TKeyType } from '../../packages/core/src' +import { computeAddress, serialize } from '@ethersproject/transactions' type ConfiguredAgent = TAgent diff --git a/__tests__/shared/messageHandler.ts b/__tests__/shared/messageHandler.ts index 96093e2c2..53dba7de3 100644 --- a/__tests__/shared/messageHandler.ts +++ b/__tests__/shared/messageHandler.ts @@ -1,4 +1,4 @@ -import { TAgent, IDataStore, IMessageHandler, IMessage } from '../../packages/core/src' +import { IDataStore, IMessage, IMessageHandler, TAgent } from '../../packages/core/src' import { IDataStoreORM } from '../../packages/data-store/src' type ConfiguredAgent = TAgent diff --git a/__tests__/shared/resolveDid.ts b/__tests__/shared/resolveDid.ts index c1975240d..e45c77cda 100644 --- a/__tests__/shared/resolveDid.ts +++ b/__tests__/shared/resolveDid.ts @@ -1,4 +1,4 @@ -import { TAgent, IResolver, IAgentOptions, IDIDManager } from '../../packages/core/src' +import { IAgentOptions, IDIDManager, IResolver, TAgent } from '../../packages/core/src' type ConfiguredAgent = TAgent diff --git a/__tests__/shared/saveClaims.ts b/__tests__/shared/saveClaims.ts index 3cdd5e4c6..a6350a43b 100644 --- a/__tests__/shared/saveClaims.ts +++ b/__tests__/shared/saveClaims.ts @@ -1,8 +1,7 @@ -import { TAgent, IDIDManager, IIdentifier, IDataStore, IMessageHandler } from '../../packages/core/src' +import { IDataStore, IDIDManager, IIdentifier, IMessageHandler, TAgent } from '../../packages/core/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' import { ISelectiveDisclosure } from '../../packages/selective-disclosure/src' -import { IDataStoreORM } from '../../packages/data-store/src' -import { FindCredentialsArgs } from '@veramo/data-store' +import { FindCredentialsArgs, IDataStoreORM } from '../../packages/data-store/src' type ConfiguredAgent = TAgent< IDIDManager & ICredentialIssuer & IDataStoreORM & IDataStore & IMessageHandler & ISelectiveDisclosure diff --git a/__tests__/shared/verifiableDataJWT.ts b/__tests__/shared/verifiableDataJWT.ts index 1db3707dd..4e8e1bce8 100644 --- a/__tests__/shared/verifiableDataJWT.ts +++ b/__tests__/shared/verifiableDataJWT.ts @@ -58,6 +58,7 @@ export default (testContext: { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, + type: ['Example'], credentialSubject: { id: 'did:web:example.com', you: 'Rock', @@ -69,7 +70,7 @@ export default (testContext: { expect(verifiableCredential).toHaveProperty('proof.jwt') expect(verifiableCredential).toHaveProperty('issuanceDate') expect(verifiableCredential['@context']).toEqual(['https://www.w3.org/2018/credentials/v1']) - expect(verifiableCredential['type']).toEqual(['VerifiableCredential']) + expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Example']) const token = verifiableCredential.proof.jwt const { payload } = decodeJWT(token) @@ -81,6 +82,7 @@ export default (testContext: { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, + type: ['Example'], credentialSubject: { id: 'did:web:example.com', you: 'Rock', @@ -93,7 +95,7 @@ export default (testContext: { expect(verifiableCredential).toHaveProperty('proof.jwt') expect(verifiableCredential).toHaveProperty('issuanceDate') expect(verifiableCredential['@context']).toEqual(['https://www.w3.org/2018/credentials/v1']) - expect(verifiableCredential['type']).toEqual(['VerifiableCredential']) + expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Example']) const token = verifiableCredential.proof.jwt const { payload } = decodeJWT(token) @@ -145,6 +147,7 @@ export default (testContext: { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, + type: ['Example'], credentialSubject: { id: 'did:web:example.com', you: 'Rock', @@ -156,6 +159,7 @@ export default (testContext: { const verifiablePresentation = await agent.createVerifiablePresentation({ presentation: { holder: identifier.did, + type: ['Example'], verifier: [], verifiableCredential: [verifiableCredential], }, @@ -164,7 +168,7 @@ export default (testContext: { expect(verifiablePresentation).toHaveProperty('proof.jwt') expect(verifiablePresentation['@context']).toEqual(['https://www.w3.org/2018/credentials/v1']) - expect(verifiablePresentation['type']).toEqual(['VerifiablePresentation']) + expect(verifiablePresentation['type']).toEqual(['VerifiablePresentation', 'Example']) const hash = await agent.dataStoreSaveVerifiablePresentation({ verifiablePresentation }) expect(typeof hash).toEqual('string') @@ -181,6 +185,7 @@ export default (testContext: { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, + type: ['Example'], credentialSubject: { id: 'did:web:example.com', you: 'Rock', @@ -192,6 +197,7 @@ export default (testContext: { const verifiablePresentation = await agent.createVerifiablePresentation({ presentation: { holder: identifier.did, + type: ['Example'], verifier: [], verifiableCredential: [verifiableCredential], }, @@ -201,7 +207,7 @@ export default (testContext: { expect(verifiablePresentation).toHaveProperty('proof.jwt') expect(verifiablePresentation['@context']).toEqual(['https://www.w3.org/2018/credentials/v1']) - expect(verifiablePresentation['type']).toEqual(['VerifiablePresentation']) + expect(verifiablePresentation['type']).toEqual(['VerifiablePresentation', 'Example']) const token = verifiablePresentation.proof.jwt const { payload } = decodeJWT(token) @@ -267,6 +273,7 @@ export default (testContext: { const credentialInput = { credentialSubject: { id: 'did:example:subject', name: 'Alice' }, issuer: { id: importedDID.did }, + type: ['Example'], } const { proof, issuanceDate, ...comparableOutput } = await agent.createVerifiableCredential({ credential: credentialInput, @@ -279,7 +286,7 @@ export default (testContext: { issuer: { id: 'did:ethr:rinkeby:0x03155ee0cbefeecd80de63a62b4ed8f0f97ac22a58f76a265903b9acab79bf018c', }, - type: ['VerifiableCredential'], + type: ['VerifiableCredential', 'Example'], '@context': ['https://www.w3.org/2018/credentials/v1'], }) }) diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index 432f60808..e99065dac 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -1,4 +1,4 @@ -import { TAgent, IDIDManager, IDataStore, IIdentifier } from '../../packages/core/src' +import { IDataStore, IDIDManager, IIdentifier, TAgent } from '../../packages/core/src' import { IDataStoreORM } from '../../packages/data-store/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' import { IDIDComm } from '../../packages/did-comm/src' diff --git a/__tests__/shared/webDidFlow.ts b/__tests__/shared/webDidFlow.ts index 9e5ad0cf3..2a9b2e031 100644 --- a/__tests__/shared/webDidFlow.ts +++ b/__tests__/shared/webDidFlow.ts @@ -1,4 +1,4 @@ -import { TAgent, IDIDManager, IIdentifier, IKey } from '../../packages/core/src' +import { IDIDManager, IIdentifier, IKey, TAgent } from '../../packages/core/src' import { ICredentialIssuer } from '../../packages/credential-w3c/src' type ConfiguredAgent = TAgent diff --git a/__tests__/utils/fake-did.ts b/__tests__/utils/fake-did.ts index 7c125f468..1be9edf72 100644 --- a/__tests__/utils/fake-did.ts +++ b/__tests__/utils/fake-did.ts @@ -1,15 +1,14 @@ -import { IAgentContext, IKeyManager, IIdentifier, IKey, IService, IDIDManager } from '@veramo/core' -import { AbstractIdentifierProvider } from '../../packages/did-manager/src/index' +import { IAgentContext, IDIDManager, IIdentifier, IKey, IKeyManager, IService, TAgent } from '../../packages/core/src' +import { AbstractIdentifierProvider } from '../../packages/did-manager/src' +import { _NormalizedVerificationMethod } from '../../packages/utils/src' import { DIDResolutionOptions, DIDResolutionResult, DIDResolver, - Resolvable, ParsedDID, + Resolvable, VerificationMethod, } from 'did-resolver' -import { TAgent } from '@veramo/core/src' -import { _NormalizedVerificationMethod } from '../../packages/utils/src' /** * A DID method that uses the information stored by the DID manager to resolve diff --git a/__tests__/utils/ganache-provider.ts b/__tests__/utils/ganache-provider.ts index 25f0bde50..ccd156c08 100644 --- a/__tests__/utils/ganache-provider.ts +++ b/__tests__/utils/ganache-provider.ts @@ -1,5 +1,6 @@ import { JsonRpcProvider, Web3Provider } from '@ethersproject/providers' import { Contract, ContractFactory } from '@ethersproject/contracts' +// @ts-ignore import DidRegistryContract from 'ethr-did-registry' import ganache from 'ganache-cli' diff --git a/docsconfig.json b/docsconfig.json index ae6c7f38b..722ea59f3 100644 --- a/docsconfig.json +++ b/docsconfig.json @@ -1,9 +1,11 @@ { "documentPackages": [ "core", + "credential-ld", "credential-w3c", "data-store", "did-comm", + "did-discovery", "did-jwt", "did-manager", "did-provider-ethr", @@ -16,6 +18,7 @@ "remote-client", "remote-server", "selective-disclosure", - "url-handler" + "url-handler", + "utils" ] } \ No newline at end of file diff --git a/package.json b/package.json index cd8b72ac6..436c3513d 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "@types/jest": "27.0.3", "codecov": "3.8.3", "cross-fetch": "3.1.4", - "ethr-did-registry": "0.0.3", + "ethr-did-registry": "^0.0.3", "ganache-cli": "6.12.2", "jest": "27.3.1", "jest-fetch-mock": "3.0.3", diff --git a/packages/cli/src/credential.ts b/packages/cli/src/credential.ts index 2ff7accac..3a23aafb4 100644 --- a/packages/cli/src/credential.ts +++ b/packages/cli/src/credential.ts @@ -1,4 +1,3 @@ -import { W3CCredential } from '@veramo/core' import { getAgent } from './setup' import { program } from 'commander' import inquirer from 'inquirer' @@ -6,6 +5,7 @@ import qrcode from 'qrcode-terminal' import * as fs from 'fs' import * as json5 from 'json5' import { readStdin } from './util' +import { CredentialPayload } from '@veramo/core' const credential = program.command('credential').description('W3C Verifiable Credential') @@ -80,7 +80,7 @@ credential const type: string = answers.claimType credentialSubject[type] = answers.claimValue - const credential: W3CCredential = { + const credential: CredentialPayload = { issuer: { id: answers.iss }, '@context': [ 'https://www.w3.org/2018/credentials/v1', diff --git a/packages/cli/src/explore/credentials.ts b/packages/cli/src/explore/credentials.ts index 9dade809e..4ad646afe 100644 --- a/packages/cli/src/explore/credentials.ts +++ b/packages/cli/src/explore/credentials.ts @@ -3,6 +3,7 @@ import { UniqueVerifiableCredential } from '@veramo/data-store' import { shortDate, shortDid } from './utils' import { ConfiguredAgent } from '../setup' import { styles } from './styles' +import { asArray, extractIssuer } from "@veramo/utils"; export const getCredentialsTable = async (agent: ConfiguredAgent, screen: Widgets.Screen) => { screen.title = 'Credentials' @@ -25,8 +26,8 @@ export const getCredentialsTable = async (agent: ConfiguredAgent, screen: Widget [['Created', 'Type', 'From', 'To']].concat( credentials.map(({ verifiableCredential: m }) => [ shortDate(m.issuanceDate), - m.type.join(','), - shortDid(m.issuer.id), + asArray(m.type || []).join(','), + shortDid(extractIssuer(m)), shortDid(m.credentialSubject.id), ]), ), diff --git a/packages/cli/src/explore/presentations.ts b/packages/cli/src/explore/presentations.ts index b7768ca96..e3d5ecf50 100644 --- a/packages/cli/src/explore/presentations.ts +++ b/packages/cli/src/explore/presentations.ts @@ -3,6 +3,7 @@ import { UniqueVerifiablePresentation } from '@veramo/data-store' import { shortDate, shortDid } from './utils' import { ConfiguredAgent } from '../setup' import { styles } from './styles' +import { asArray } from '@veramo/utils' export const getPresentationsTable = async (agent: ConfiguredAgent, screen: Widgets.Screen) => { screen.title = 'Presentations' @@ -25,9 +26,9 @@ export const getPresentationsTable = async (agent: ConfiguredAgent, screen: Widg [['Created', 'Type', 'Holder', 'Verifier']].concat( presentations.map(({ verifiablePresentation: m }) => [ shortDate(m.issuanceDate), - m.type.join(','), + asArray(m.type || []).join(','), shortDid(m.holder), - shortDid(m.verifier.join(',')), + shortDid(asArray(m.verifier || []).join(',')), ]), ), ) diff --git a/packages/cli/src/presentation.ts b/packages/cli/src/presentation.ts index 4f0f1aa4d..67da08c8d 100644 --- a/packages/cli/src/presentation.ts +++ b/packages/cli/src/presentation.ts @@ -5,6 +5,7 @@ import qrcode from 'qrcode-terminal' import { readStdin } from "./util"; import * as fs from 'fs' import * as json5 from 'json5' +import { extractIssuer } from '@veramo/utils'; const presentation = program.command('presentation').description('W3C Verifiable Presentation') @@ -87,7 +88,7 @@ presentation name: JSON.stringify(credential.verifiableCredential.credentialSubject) + ' | Issuer: ' + - credential.verifiableCredential.issuer.id, + extractIssuer(credential.verifiableCredential), value: credential.verifiableCredential, }) } diff --git a/packages/cli/src/sdr.ts b/packages/cli/src/sdr.ts index a3867454a..91cf7d097 100644 --- a/packages/cli/src/sdr.ts +++ b/packages/cli/src/sdr.ts @@ -5,6 +5,8 @@ import inquirer from 'inquirer' import qrcode from 'qrcode-terminal' import { shortDate, shortDid } from './explore/utils' import { VerifiableCredential } from '@veramo/core' +import { asArray, extractIssuer } from '@veramo/utils' + const fuzzy = require('fuzzy') const sdr = program.command('sdr').description('Selective Disclosure Request') @@ -173,7 +175,7 @@ sdr name: JSON.stringify(credential.verifiableCredential.credentialSubject) + ' | Issuer: ' + - credential.verifiableCredential.issuer.id, + extractIssuer(credential.verifiableCredential), value: credential.verifiableCredential.proof.jwt, }) } @@ -305,9 +307,9 @@ sdr name: c.verifiableCredential.credentialSubject[item.claimType] + ' (' + - c.verifiableCredential.type.join(',') + + asArray(c.verifiableCredential.type || []).join(',') + ') issued by: ' + - c.verifiableCredential.issuer.id + + extractIssuer(c.verifiableCredential) + ' ' + shortDate(c.verifiableCredential.issuanceDate) + ' ago', diff --git a/packages/core/plugin.schema.json b/packages/core/plugin.schema.json index 8469249bf..13c999bbc 100644 --- a/packages/core/plugin.schema.json +++ b/packages/core/plugin.schema.json @@ -1486,7 +1486,7 @@ "required": [ "hash" ], - "description": "Input arguments for {@link IDataStore.IDataStoreDeleteVerifiableCredentialArgs | IDataStoreDeleteVerifiableCredentialArgs }" + "description": "Input arguments for {@link IDataStoreDeleteVerifiableCredentialArgs | IDataStoreDeleteVerifiableCredentialArgs }" }, "IDataStoreGetMessageArgs": { "type": "object", @@ -1613,30 +1613,39 @@ "VerifiableCredential": { "type": "object", "properties": { - "@context": { - "type": "array", - "items": { - "type": "string" - } + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "id": { - "type": "string" + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, - "issuer": { - "type": "object", - "properties": { - "id": { + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } - }, - "required": [ - "id" ] }, "issuanceDate": { @@ -1645,74 +1654,111 @@ "expirationDate": { "type": "string" }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + }, + "id": { + "type": "string" + } + }, + "required": [ + "@context", + "credentialSubject", + "issuanceDate", + "issuer", + "proof" + ], + "description": "Represents a signed Verifiable Credential payload (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + }, + "ProofType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "description": "A proof property of a Verifiable Credential or Presentation" + }, + "IssuerType": { + "anyOf": [ + { "type": "object", "properties": { "id": { "type": "string" - }, - "type": { - "type": "string" } }, "required": [ - "id", - "type" + "id" ] }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + { + "type": "string" } - }, - "required": [ - "@context", - "credentialSubject", - "issuanceDate", - "issuer", - "proof", - "type" ], - "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + "description": "The issuer of a Credential or the holder of a Presentation.\n\nThe value of the issuer property MUST be either a URI or an object containing an id property. It is RECOMMENDED that the URI in the issuer or its id be one which, if de-referenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model }" }, - "VerifiablePresentation": { + "CredentialSubject": { "type": "object", "properties": { "id": { "type": "string" - }, - "holder": { + } + }, + "description": "The value of the credentialSubject property is defined as a set of objects that contain one or more properties that are each related to a subject of the verifiable credential. Each object MAY contain an id.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#credential-subject | Credential Subject }" + }, + "CredentialStatus": { + "type": "object", + "properties": { + "id": { "type": "string" }, - "issuanceDate": { + "type": { "type": "string" + } + }, + "description": "Used for the discovery of information about the current status of a verifiable credential, such as whether it is suspended or revoked. The precise contents of the credential status information is determined by the specific `credentialStatus` type definition, and varies depending on factors such as whether it is simple to implement or if it is privacy-enhancing.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status }" + }, + "VerifiablePresentation": { + "type": "object", + "properties": { + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "expirationDate": { + "holder": { "type": "string" }, - "@context": { + "verifiableCredential": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/W3CVerifiableCredential" } }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, "verifier": { "type": "array", @@ -1720,29 +1766,37 @@ "type": "string" } }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } + "issuanceDate": { + "type": "string" }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + "expirationDate": { + "type": "string" + }, + "id": { + "type": "string" } }, "required": [ "@context", "holder", - "proof", - "type", - "verifier" + "proof" + ], + "description": "Represents a signed Verifiable Presentation (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + }, + "W3CVerifiableCredential": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/CompactJWT" + } ], - "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + "description": "Represents a signed Verifiable Credential (includes proof), in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model } \nSee {@link https://www.w3.org/TR/vc-data-model/#proof-formats | proof formats }" + }, + "CompactJWT": { + "type": "string", + "description": "Represents a Json Web Token in compact form. \"header.payload.signature\"" }, "IDataStoreGetVerifiableCredentialArgs": { "type": "object", @@ -2016,30 +2070,39 @@ "VerifiableCredential": { "type": "object", "properties": { - "@context": { - "type": "array", - "items": { - "type": "string" - } + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "id": { - "type": "string" + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, - "issuer": { - "type": "object", - "properties": { - "id": { + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } - }, - "required": [ - "id" ] }, "issuanceDate": { @@ -2048,74 +2111,111 @@ "expirationDate": { "type": "string" }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + }, + "id": { + "type": "string" + } + }, + "required": [ + "@context", + "credentialSubject", + "issuanceDate", + "issuer", + "proof" + ], + "description": "Represents a signed Verifiable Credential payload (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + }, + "ProofType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "description": "A proof property of a Verifiable Credential or Presentation" + }, + "IssuerType": { + "anyOf": [ + { "type": "object", "properties": { "id": { "type": "string" - }, - "type": { - "type": "string" } }, "required": [ - "id", - "type" + "id" ] }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + { + "type": "string" } - }, - "required": [ - "@context", - "credentialSubject", - "issuanceDate", - "issuer", - "proof", - "type" ], - "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + "description": "The issuer of a Credential or the holder of a Presentation.\n\nThe value of the issuer property MUST be either a URI or an object containing an id property. It is RECOMMENDED that the URI in the issuer or its id be one which, if de-referenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model }" }, - "VerifiablePresentation": { + "CredentialSubject": { "type": "object", "properties": { "id": { "type": "string" - }, - "holder": { + } + }, + "description": "The value of the credentialSubject property is defined as a set of objects that contain one or more properties that are each related to a subject of the verifiable credential. Each object MAY contain an id.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#credential-subject | Credential Subject }" + }, + "CredentialStatus": { + "type": "object", + "properties": { + "id": { "type": "string" }, - "issuanceDate": { + "type": { "type": "string" + } + }, + "description": "Used for the discovery of information about the current status of a verifiable credential, such as whether it is suspended or revoked. The precise contents of the credential status information is determined by the specific `credentialStatus` type definition, and varies depending on factors such as whether it is simple to implement or if it is privacy-enhancing.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status }" + }, + "VerifiablePresentation": { + "type": "object", + "properties": { + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "expirationDate": { + "holder": { "type": "string" }, - "@context": { + "verifiableCredential": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/W3CVerifiableCredential" } }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, "verifier": { "type": "array", @@ -2123,29 +2223,37 @@ "type": "string" } }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } + "issuanceDate": { + "type": "string" }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + "expirationDate": { + "type": "string" + }, + "id": { + "type": "string" } }, "required": [ "@context", "holder", - "proof", - "type", - "verifier" + "proof" + ], + "description": "Represents a signed Verifiable Presentation (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + }, + "W3CVerifiableCredential": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/CompactJWT" + } ], - "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + "description": "Represents a signed Verifiable Credential (includes proof), in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model } \nSee {@link https://www.w3.org/TR/vc-data-model/#proof-formats | proof formats }" + }, + "CompactJWT": { + "type": "string", + "description": "Represents a Json Web Token in compact form. \"header.payload.signature\"" } }, "methods": { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 05f699be7..4cced983d 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -14,5 +14,6 @@ export * from './types/IKeyManager' export * from './types/IMessage' export * from './types/IMessageHandler' export * from './types/IResolver' +export * from './types/vc-data-model' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/core/src/types/IDataStore.ts b/packages/core/src/types/IDataStore.ts index 02849869c..9ef673848 100644 --- a/packages/core/src/types/IDataStore.ts +++ b/packages/core/src/types/IDataStore.ts @@ -1,5 +1,6 @@ import { IPluginMethodMap } from './IAgent' -import { IMessage, VerifiablePresentation, VerifiableCredential } from './IMessage' +import { IMessage } from './IMessage' +import { VerifiableCredential, VerifiablePresentation } from "./vc-data-model"; /** * Input arguments for {@link IDataStore.dataStoreSaveMessage | dataStoreSaveMessage} @@ -46,7 +47,7 @@ export interface IDataStoreGetVerifiableCredentialArgs { } /** - * Input arguments for {@link IDataStore.IDataStoreDeleteVerifiableCredentialArgs | IDataStoreDeleteVerifiableCredentialArgs} + * Input arguments for {@link IDataStoreDeleteVerifiableCredentialArgs | IDataStoreDeleteVerifiableCredentialArgs} * @public */ export interface IDataStoreDeleteVerifiableCredentialArgs { diff --git a/packages/core/src/types/IMessage.ts b/packages/core/src/types/IMessage.ts index e6b7f7314..f06b8bec4 100644 --- a/packages/core/src/types/IMessage.ts +++ b/packages/core/src/types/IMessage.ts @@ -1,67 +1,4 @@ -/** - * Represents a signed Verifiable Credential payload (includes proof). - * See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model} - * - * @public - */ -export interface VerifiableCredential extends W3CCredential { - proof: { - type?: string - [x: string]: any - } -} - -/** - * Represents a signed Verifiable Presentation (includes proof). - * See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model} - * @public - */ -export interface VerifiablePresentation extends W3CPresentation { - proof: { - type?: string - [x: string]: any - } -} - -/** - * Represents an unsigned W3C Credential payload. - * See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model} - * @public - */ -export interface W3CCredential { - '@context': string[] - id?: string - type: string[] - issuer: { id: string; [x: string]: any } - issuanceDate: string - expirationDate?: string - credentialSubject: { - id?: string - [x: string]: any - } - credentialStatus?: { - id: string - type: string - } - [x: string]: any -} - -/** - * Represents an unsigned W3C Presentation payload. - * See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model} - * @public - */ -export interface W3CPresentation { - id?: string - holder: string - issuanceDate?: string - expirationDate?: string - '@context': string[] - type: string[] - verifier: string[] - verifiableCredential?: VerifiableCredential[] - [x: string]: any -} +import { VerifiableCredential, VerifiablePresentation } from "./vc-data-model"; /** * Message meta data diff --git a/packages/core/src/types/vc-data-model.ts b/packages/core/src/types/vc-data-model.ts new file mode 100644 index 000000000..78cdd2cdd --- /dev/null +++ b/packages/core/src/types/vc-data-model.ts @@ -0,0 +1,176 @@ +/** + * Represents a Json Web Token in compact form. + * "header.payload.signature" + * + * @beta + */ +export type CompactJWT = string + +/** + * The issuer of a Credential or the holder of a Presentation. + * + * The value of the issuer property MUST be either a URI or an object containing an id property. + * It is RECOMMENDED that the URI in the issuer or its id be one which, if de-referenced, results in a document + * containing machine-readable information about the issuer that can be used to verify the information expressed in the + * credential. + * + * See {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model} + * + * @beta + */ +export type IssuerType = { id: string, [x: string]: any } | string + +/** + * The value of the credentialSubject property is defined as a set of objects that contain one or more properties that + * are each related to a subject of the verifiable credential. + * Each object MAY contain an id. + * + * See {@link https://www.w3.org/TR/vc-data-model/#credential-subject | Credential Subject} + * + * @beta + */ +export type CredentialSubject = { + id?: string + [x: string]: any +} + +/** + * Used for the discovery of information about the current status of a verifiable credential, such as whether it is + * suspended or revoked. + * The precise contents of the credential status information is determined by the specific `credentialStatus` type + * definition, and varies depending on factors such as whether it is simple to implement or if it is privacy-enhancing. + * + * See {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status} + * + * @beta + */ +export type CredentialStatus = { + id?: string + type?: string + [x: string]: any +} + +/** + * A proof property of a Verifiable Credential or Presentation + * + * @beta + */ +export interface ProofType { + type?: string + + [x: string]: any +} + +/** + * Represents an unsigned W3C Credential payload. + * See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model} + * + * @beta + */ +export interface UnsignedCredential { + issuer: IssuerType + credentialSubject: CredentialSubject + type?: string[] | string + '@context': string[] | string + issuanceDate: string + expirationDate?: string + credentialStatus?: CredentialStatus + id?: string + + [x: string]: any +} + +/** + * Represents a signed Verifiable Credential payload (includes proof), using a JSON representation. + * See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model} + * + * @beta + */ +export type VerifiableCredential = UnsignedCredential & { proof: ProofType } + +/** + * Represents a signed Verifiable Credential (includes proof), in either JSON or compact JWT format. + * See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model} + * See {@link https://www.w3.org/TR/vc-data-model/#proof-formats | proof formats} + * + * @beta + */ +export type W3CVerifiableCredential = VerifiableCredential | CompactJWT + +/** + * Represents an unsigned W3C Presentation payload. + * See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model} + * @public + */ +export interface UnsignedPresentation { + holder: string + verifiableCredential?: W3CVerifiableCredential[] + type?: string[] | string + '@context': string[] | string + verifier?: string[] + issuanceDate?: string + expirationDate?: string + id?: string + + [x: string]: any +} + + +/** + * Represents a signed Verifiable Presentation (includes proof), using a JSON representation. + * See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model} + * @public + */ +export type VerifiablePresentation = UnsignedPresentation & { proof: ProofType } + +/** + * Represents a signed Verifiable Presentation (includes proof) in either JSON or compact JWT format. + * See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model} + * + * @public + */ +export type W3CVerifiablePresentation = VerifiablePresentation | CompactJWT + +/** + * Represents an issuance or expiration date for Credentials / Presentations. + * This is used as input when creating them. + * + * @beta This API may change without prior notice. + */ +export type DateType = string | Date + +/** + * Used as input when creating Verifiable Credentials + * + * @beta This API may change without prior notice. + */ +export interface CredentialPayload { + issuer: IssuerType + credentialSubject?: CredentialSubject + type?: string[] + '@context'?: string[] + issuanceDate?: DateType + expirationDate?: DateType + credentialStatus?: CredentialStatus + id?: string + + [x: string]: any +} + +/** + * Used as input when creating Verifiable Presentations + * + * @beta This API may change without prior notice. + */ +export interface PresentationPayload { + holder: string + verifiableCredential?: W3CVerifiableCredential[] + type?: string[] + '@context'?: string[] + verifier?: string[] + issuanceDate?: DateType + expirationDate?: DateType, + id?: string + + [x: string]: any +} \ No newline at end of file diff --git a/packages/credential-ld/package.json b/packages/credential-ld/package.json index 09a2657f5..f8e17fd78 100644 --- a/packages/credential-ld/package.json +++ b/packages/credential-ld/package.json @@ -20,7 +20,6 @@ "@veramo/core": "^3.1.0", "@veramo/did-resolver": "^3.1.0", "@veramo/utils": "^3.1.0", - "blakejs": "^1.1.0", "debug": "^4.1.1", "did-resolver": "3.1.3", "jsonld": "^5.2.0", diff --git a/packages/credential-ld/plugin.schema.json b/packages/credential-ld/plugin.schema.json index 329342a8f..6b037133b 100644 --- a/packages/credential-ld/plugin.schema.json +++ b/packages/credential-ld/plugin.schema.json @@ -6,53 +6,7 @@ "type": "object", "properties": { "credential": { - "type": "object", - "properties": { - "@context": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "id": { - "type": "string" - }, - "type": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "issuer": { - "$ref": "#/components/schemas/IssuerType" - }, - "issuanceDate": { - "$ref": "#/components/schemas/DateType" - }, - "expirationDate": { - "$ref": "#/components/schemas/DateType" - }, - "credentialSubject": { - "$ref": "#/components/schemas/CredentialSubject" - }, - "credentialStatus": { - "$ref": "#/components/schemas/CredentialStatus" - } - }, + "$ref": "#/components/schemas/CredentialPayload", "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, "keyRef": { @@ -65,6 +19,45 @@ ], "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" }, + "CredentialPayload": { + "type": "object", + "properties": { + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" + }, + "type": { + "type": "array", + "items": { + "type": "string" + } + }, + "@context": { + "type": "array", + "items": { + "type": "string" + } + }, + "issuanceDate": { + "$ref": "#/components/schemas/DateType" + }, + "expirationDate": { + "$ref": "#/components/schemas/DateType" + }, + "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + }, + "id": { + "type": "string" + } + }, + "required": [ + "issuer" + ], + "description": "Used as input when creating Verifiable Credentials" + }, "IssuerType": { "anyOf": [ { @@ -81,10 +74,8 @@ { "type": "string" } - ] - }, - "DateType": { - "type": "string" + ], + "description": "The issuer of a Credential or the holder of a Presentation.\n\nThe value of the issuer property MUST be either a URI or an object containing an id property. It is RECOMMENDED that the URI in the issuer or its id be one which, if de-referenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model }" }, "CredentialSubject": { "type": "object", @@ -92,7 +83,12 @@ "id": { "type": "string" } - } + }, + "description": "The value of the credentialSubject property is defined as a set of objects that contain one or more properties that are each related to a subject of the verifiable credential. Each object MAY contain an id.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#credential-subject | Credential Subject }" + }, + "DateType": { + "type": "string", + "description": "Represents an issuance or expiration date for Credentials / Presentations. This is used as input when creating them." }, "CredentialStatus": { "type": "object", @@ -104,38 +100,44 @@ "type": "string" } }, - "required": [ - "id", - "type" - ] + "description": "Used for the discovery of information about the current status of a verifiable credential, such as whether it is suspended or revoked. The precise contents of the credential status information is determined by the specific `credentialStatus` type definition, and varies depending on factors such as whether it is simple to implement or if it is privacy-enhancing.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status }" }, "VerifiableCredential": { "type": "object", "properties": { - "@context": { - "type": "array", - "items": { - "type": "string" - } + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "id": { - "type": "string" + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, - "issuer": { - "type": "object", - "properties": { - "id": { + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } - }, - "required": [ - "id" ] }, "issuanceDate": { @@ -144,36 +146,11 @@ "expirationDate": { "type": "string" }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, "credentialStatus": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "required": [ - "id", - "type" - ] + "$ref": "#/components/schemas/CredentialStatus" }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + "id": { + "type": "string" } }, "required": [ @@ -181,82 +158,24 @@ "credentialSubject", "issuanceDate", "issuer", - "proof", - "type" + "proof" ], - "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + "description": "Represents a signed Verifiable Credential payload (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + }, + "ProofType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "description": "A proof property of a Verifiable Credential or Presentation" }, "ICreateVerifiablePresentationLDArgs": { "type": "object", "properties": { "presentation": { - "type": "object", - "properties": { - "@context": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "type": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "id": { - "type": "string" - }, - "verifiableCredential": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/VerifiableCredential" - }, - { - "$ref": "#/components/schemas/JWT" - } - ] - } - }, - "holder": { - "type": "string" - }, - "verifier": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - } - }, + "$ref": "#/components/schemas/PresentationPayload", "description": "The json payload of the Presentation according to the\n {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model } .\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, "challenge": { @@ -277,31 +196,25 @@ ], "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" }, - "JWT": { - "type": "string" - }, - "VerifiablePresentation": { + "PresentationPayload": { "type": "object", "properties": { - "id": { - "type": "string" - }, "holder": { "type": "string" }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" + "verifiableCredential": { + "type": "array", + "items": { + "$ref": "#/components/schemas/W3CVerifiableCredential" + } }, - "@context": { + "type": { "type": "array", "items": { "type": "string" } }, - "type": { + "@context": { "type": "array", "items": { "type": "string" @@ -313,29 +226,99 @@ "type": "string" } }, + "issuanceDate": { + "$ref": "#/components/schemas/DateType" + }, + "expirationDate": { + "$ref": "#/components/schemas/DateType" + }, + "id": { + "type": "string" + } + }, + "required": [ + "holder" + ], + "description": "Used as input when creating Verifiable Presentations" + }, + "W3CVerifiableCredential": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/CompactJWT" + } + ], + "description": "Represents a signed Verifiable Credential (includes proof), in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model } \nSee {@link https://www.w3.org/TR/vc-data-model/#proof-formats | proof formats }" + }, + "CompactJWT": { + "type": "string", + "description": "Represents a Json Web Token in compact form. \"header.payload.signature\"" + }, + "VerifiablePresentation": { + "type": "object", + "properties": { + "proof": { + "$ref": "#/components/schemas/ProofType" + }, + "holder": { + "type": "string" + }, "verifiableCredential": { "type": "array", "items": { - "$ref": "#/components/schemas/VerifiableCredential" + "$ref": "#/components/schemas/W3CVerifiableCredential" } }, - "proof": { - "type": "object", - "properties": { - "type": { + "type": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } + ] + }, + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "verifier": { + "type": "array", + "items": { + "type": "string" } + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "id": { + "type": "string" } }, "required": [ "@context", "holder", - "proof", - "type", - "verifier" + "proof" ], - "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + "description": "Represents a signed Verifiable Presentation (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" }, "IVerifyCredentialLDArgs": { "type": "object", diff --git a/packages/credential-ld/src/action-handler.ts b/packages/credential-ld/src/action-handler.ts index 7db02d549..757a5e123 100644 --- a/packages/credential-ld/src/action-handler.ts +++ b/packages/credential-ld/src/action-handler.ts @@ -1,30 +1,38 @@ import { + CredentialPayload, IAgentContext, IAgentPlugin, IIdentifier, IKey, IResolver, + PresentationPayload, VerifiableCredential, VerifiablePresentation, } from '@veramo/core' import { schema, VeramoLdSignature } from './' import Debug from 'debug' import { LdContextLoader } from "./ld-context-loader"; -import { _ExtendedIKey, asArray, mapIdentifierKeysToDoc, RecordLike, OrPromise } from "@veramo/utils"; +import { + _ExtendedIKey, + extractIssuer, + isDefined, + MANDATORY_CREDENTIAL_CONTEXT, + mapIdentifierKeysToDoc, + OrPromise, + processEntryToArray, + RecordLike +} from "@veramo/utils"; import { LdCredentialModule } from "./ld-credential-module"; import { LdSuiteLoader } from './ld-suite-loader'; import { ContextDoc, - CredentialPayload, ICreateVerifiableCredentialLDArgs, ICreateVerifiablePresentationLDArgs, ICredentialIssuerLD, IRequiredContext, IVerifyCredentialLDArgs, IVerifyPresentationLDArgs, - MANDATORY_CREDENTIAL_CONTEXT, - PresentationPayload, } from "./types"; const debug = Debug('veramo:w3c:action-handler') @@ -62,29 +70,33 @@ export class CredentialIssuerLD implements IAgentPlugin { args: ICreateVerifiablePresentationLDArgs, context: IRequiredContext, ): Promise { - const presentationContext: string[] = asArray(args?.presentation?.['@context'] || []) || ['https://www.w3.org/2018/credentials/v1'] - if (presentationContext[0] !== MANDATORY_CREDENTIAL_CONTEXT) { - presentationContext.unshift(MANDATORY_CREDENTIAL_CONTEXT) - } - const presentationType = asArray(args?.presentation?.type || []) || ['VerifiablePresentation'] - if (presentationType[0] !== 'VerifiablePresentation') { - presentationType.unshift('VerifiablePresentation') - } + const presentationContext = processEntryToArray(args?.presentation?.['@context'], MANDATORY_CREDENTIAL_CONTEXT) + const presentationType = processEntryToArray(args?.presentation?.type, 'VerifiablePresentation') - const presentation: Partial = { + const presentation: PresentationPayload = { ...args?.presentation, '@context': presentationContext, type: presentationType, } - //issuanceDate must not be present for presentations because it is not defined in a @context - delete presentation.issuanceDate - - // FIXME: if credentials use the internal JwtProof2020, map them to only use JWT before bundling the presentation - if (!presentation.holder || typeof presentation.holder === 'undefined') { + if (!isDefined(presentation.holder)) { throw new Error('invalid_argument: args.presentation.holder must not be empty') } + if (args.presentation.verifiableCredential) { + const credentials = args.presentation.verifiableCredential.map(cred => { + if (typeof cred !== 'string' && cred.proof.jwt) { + return cred.proof.jwt + } else { + return cred + } + }) + presentation.verifiableCredential = credentials + } + + //issuanceDate must not be present for presentations because it is not defined in a @context + delete presentation.issuanceDate + let identifier: IIdentifier try { identifier = await context.agent.didManagerGet({ did: presentation.holder }) @@ -114,26 +126,20 @@ export class CredentialIssuerLD implements IAgentPlugin { args: ICreateVerifiableCredentialLDArgs, context: IRequiredContext, ): Promise { - const credentialContext: string[] = asArray(args?.credential?.['@context'] || []) || ['https://www.w3.org/2018/credentials/v1'] - if (credentialContext[0] !== MANDATORY_CREDENTIAL_CONTEXT) { - credentialContext.unshift(MANDATORY_CREDENTIAL_CONTEXT) - } - const credentialType = asArray(args?.credential?.type || []) || ['VerifiableCredential'] - if (credentialType[0] !== 'VerifiableCredential') { - credentialType.unshift('VerifiableCredential') - } + const credentialContext = processEntryToArray(args?.credential?.['@context'], MANDATORY_CREDENTIAL_CONTEXT); + const credentialType = processEntryToArray(args?.credential?.type, 'VerifiableCredential') let issuanceDate = args?.credential?.issuanceDate || new Date().toISOString() if (issuanceDate instanceof Date) { issuanceDate = issuanceDate.toISOString() } - const credential: Partial = { + const credential: CredentialPayload = { ...args?.credential, '@context': credentialContext, type: credentialType, issuanceDate, } - const issuer = typeof credential.issuer === 'string' ? credential.issuer : credential?.issuer?.id + const issuer = extractIssuer(credential) if (!issuer || typeof issuer === 'undefined') { throw new Error('invalid_argument: args.credential.issuer must not be empty') } diff --git a/packages/credential-ld/src/ld-credential-module.ts b/packages/credential-ld/src/ld-credential-module.ts index 9ad482df4..3cefeb210 100644 --- a/packages/credential-ld/src/ld-credential-module.ts +++ b/packages/credential-ld/src/ld-credential-module.ts @@ -1,12 +1,13 @@ import { + CredentialPayload, IAgentContext, IKey, IResolver, + PresentationPayload, VerifiableCredential, VerifiablePresentation, } from '@veramo/core' import Debug from 'debug' -import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' import { extendContextLoader, purposes } from 'jsonld-signatures' import * as vc from 'vc-js' import { LdContextLoader } from './ld-context-loader' @@ -76,7 +77,7 @@ export class LdCredentialModule { } async issueLDVerifiableCredential( - credential: Partial, + credential: CredentialPayload, issuerDid: string, key: IKey, verificationMethodId: string, @@ -97,7 +98,7 @@ export class LdCredentialModule { } async signLDVerifiablePresentation( - presentation: Partial, + presentation: PresentationPayload, holderDid: string, key: IKey, verificationMethodId: string, @@ -122,7 +123,7 @@ export class LdCredentialModule { } async verifyCredential( - credential: Partial, + credential: VerifiableCredential, context: IAgentContext, ): Promise { const result = await vc.verifyCredential({ @@ -144,7 +145,7 @@ export class LdCredentialModule { } async verifyPresentation( - presentation: Partial, + presentation: VerifiablePresentation, challenge: string | undefined, domain: string | undefined, context: IAgentContext, diff --git a/packages/credential-ld/src/ld-suites.ts b/packages/credential-ld/src/ld-suites.ts index 829d18a05..72a495265 100644 --- a/packages/credential-ld/src/ld-suites.ts +++ b/packages/credential-ld/src/ld-suites.ts @@ -1,5 +1,4 @@ -import { IAgentContext, IKey, IKeyManager, IResolver, TKeyType } from '@veramo/core' -import { CredentialPayload, PresentationPayload } from 'did-jwt-vc' +import { CredentialPayload, IAgentContext, IKey, IKeyManager, IResolver, PresentationPayload, TKeyType } from '@veramo/core' import { DIDDocument } from 'did-resolver/src/resolver' export type RequiredAgentMethods = IResolver & Pick @@ -25,9 +24,9 @@ export abstract class VeramoLdSignature { abstract preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void - abstract preSigningCredModification(credential: Partial): void + abstract preSigningCredModification(credential: CredentialPayload): void - preSigningPresModification(presentation: Partial): void { + preSigningPresModification(presentation: PresentationPayload): void { // TODO: Remove invalid field 'verifiers' from Presentation. Needs to be adapted for LD credentials // Only remove empty array (vc.signPresentation will throw then) const sanitizedPresentation = presentation as any diff --git a/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts index 146a6ecc2..42d370a26 100644 --- a/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts +++ b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts @@ -1,13 +1,12 @@ import { RequiredAgentMethods, VeramoLdSignature } from "../ld-suites"; -import { DIDDocument, IAgentContext, IKey, TKeyType } from "@veramo/core"; +import { CredentialPayload, DIDDocument, IAgentContext, IKey, TKeyType } from "@veramo/core"; import { EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020, } from '@transmute/lds-ecdsa-secp256k1-recovery2020' -import { CredentialPayload } from 'did-jwt-vc' import * as u8a from 'uint8arrays' -import { encodeJoseBlob } from '@veramo/utils' +import { asArray, encodeJoseBlob } from '@veramo/utils' export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature { getSupportedVerificationType(): string { @@ -57,13 +56,11 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature return new EcdsaSecp256k1RecoverySignature2020() } - preSigningCredModification(credential: Partial): void { - if (!Array.isArray(credential['@context'])) { - credential['@context'] = [] - } - credential['@context'].push( - 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', - ) + preSigningCredModification(credential: CredentialPayload): void { + credential['@context'] = [ + ...asArray(credential['@context'] || []), + 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld' + ] } preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { diff --git a/packages/credential-ld/src/suites/Ed25519Signature2018.ts b/packages/credential-ld/src/suites/Ed25519Signature2018.ts index ce2f4155d..6718f282e 100644 --- a/packages/credential-ld/src/suites/Ed25519Signature2018.ts +++ b/packages/credential-ld/src/suites/Ed25519Signature2018.ts @@ -1,9 +1,8 @@ import { encodeJoseBlob } from "@veramo/utils"; import { RequiredAgentMethods, VeramoLdSignature } from "../ld-suites"; -import { DIDDocument, IAgentContext, IIdentifier, IKey, TKeyType } from "@veramo/core"; +import { CredentialPayload, DIDDocument, IAgentContext, IKey, TKeyType } from "@veramo/core"; import * as u8a from 'uint8arrays' import { Ed25519Signature2018, Ed25519VerificationKey2018 } from '@transmute/ed25519-signature-2018' -import { CredentialPayload } from 'did-jwt-vc' export class VeramoEd25519Signature2018 extends VeramoLdSignature { @@ -62,7 +61,7 @@ export class VeramoEd25519Signature2018 extends VeramoLdSignature { return new Ed25519Signature2018(); } - preSigningCredModification(credential: Partial): void { + preSigningCredModification(credential: CredentialPayload): void { // nothing to do here } diff --git a/packages/credential-ld/src/types.ts b/packages/credential-ld/src/types.ts index 4c518a6f6..4818b3f6b 100644 --- a/packages/credential-ld/src/types.ts +++ b/packages/credential-ld/src/types.ts @@ -1,10 +1,12 @@ import { + CredentialPayload, IAgentContext, IDIDManager, IKey, IKeyManager, IPluginMethodMap, IResolver, + PresentationPayload, VerifiableCredential, VerifiablePresentation, } from '@veramo/core' @@ -101,7 +103,7 @@ export interface ICreateVerifiablePresentationLDArgs { * * '@context', 'type' and 'issuanceDate' will be added automatically if omitted */ - presentation: Partial + presentation: PresentationPayload /** * Optional (only JWT) string challenge parameter to add to the verifiable presentation. @@ -135,7 +137,7 @@ export interface ICreateVerifiableCredentialLDArgs { * * '@context', 'type' and 'issuanceDate' will be added automatically if omitted */ - credential: Partial + credential: CredentialPayload /** * Optional. The key handle ({@link IKey#kid}) from the internal database. @@ -201,54 +203,6 @@ export type IRequiredContext = IAgentContext & Pick> -export type JWT = string - -export type IssuerType = { id: string, [x: string]: any } | string -export type CredentialSubject = { id?: string, [x: string]: any } - -export interface CredentialStatus { - id: string - type: string - - [x: string]: any -} - -export type DateType = string | Date - -/** - * used as input when creating Verifiable Credentials - */ -export interface CredentialPayload { - '@context': string | string[] - id?: string - type: string | string[] - issuer: IssuerType - issuanceDate: DateType - expirationDate?: DateType - credentialSubject: CredentialSubject - credentialStatus?: CredentialStatus - - [x: string]: any -} - -/** - * used as input when creating Verifiable Presentations - */ -export interface PresentationPayload { - '@context': string | string[] - type: string | string[] - id?: string - verifiableCredential?: (VerifiableCredential | JWT)[] - holder: string - verifier?: string | string[] - issuanceDate?: string - expirationDate?: string, - - [x: string]: any -} - export type ContextDoc = { "@context": Record -} - -export const MANDATORY_CREDENTIAL_CONTEXT = 'https://www.w3.org/2018/credentials/v1' \ No newline at end of file +} \ No newline at end of file diff --git a/packages/credential-w3c/package.json b/packages/credential-w3c/package.json index 3aec488ea..08042f07e 100644 --- a/packages/credential-w3c/package.json +++ b/packages/credential-w3c/package.json @@ -19,11 +19,11 @@ "@veramo/did-resolver": "^3.1.0", "@veramo/message-handler": "^3.1.0", "@veramo/utils": "^3.1.0", - "blakejs": "^1.1.0", "debug": "^4.1.1", "did-jwt-vc": "2.1.7", "did-resolver": "3.1.3", - "uint8arrays": "^2.1.3" + "uint8arrays": "^2.1.3", + "uuid": "^8.3.0" }, "optionalDependencies": { "@veramo/credential-ld": "^3.1.0" diff --git a/packages/credential-w3c/plugin.schema.json b/packages/credential-w3c/plugin.schema.json index 1bab18334..9aa1fbed1 100644 --- a/packages/credential-w3c/plugin.schema.json +++ b/packages/credential-w3c/plugin.schema.json @@ -6,99 +6,7 @@ "type": "object", "properties": { "credential": { - "type": "object", - "properties": { - "@context": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": {} - } - ] - }, - "id": { - "type": "string" - }, - "type": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": {} - } - ] - }, - "issuer": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string" - } - }, - "required": [ - "id" - ] - }, - { - "type": "string" - } - ] - }, - "issuanceDate": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": {} - } - ] - }, - "expirationDate": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "properties": {} - } - ] - }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, - "credentialStatus": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "required": [ - "id", - "type" - ] - }, - "evidence": {}, - "termsOfUse": {} - }, + "$ref": "#/components/schemas/CredentialPayload", "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, "save": { @@ -120,33 +28,48 @@ ], "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" }, - "ProofFormat": { - "type": "string", - "enum": [ - "jwt", - "lds" - ], - "description": "The type of encoding to be used for the Verifiable Credential or Presentation to be generated.\n\nOnly `jwt` and `lds` is supported at the moment." - }, - "VerifiableCredential": { + "CredentialPayload": { "type": "object", "properties": { - "@context": { + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" + }, + "type": { "type": "array", "items": { "type": "string" } }, - "id": { - "type": "string" - }, - "type": { + "@context": { "type": "array", "items": { "type": "string" } }, - "issuer": { + "issuanceDate": { + "$ref": "#/components/schemas/DateType" + }, + "expirationDate": { + "$ref": "#/components/schemas/DateType" + }, + "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + }, + "id": { + "type": "string" + } + }, + "required": [ + "issuer" + ], + "description": "Used as input when creating Verifiable Credentials" + }, + "IssuerType": { + "anyOf": [ + { "type": "object", "properties": { "id": { @@ -157,42 +80,94 @@ "id" ] }, - "issuanceDate": { + { + "type": "string" + } + ], + "description": "The issuer of a Credential or the holder of a Presentation.\n\nThe value of the issuer property MUST be either a URI or an object containing an id property. It is RECOMMENDED that the URI in the issuer or its id be one which, if de-referenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model }" + }, + "CredentialSubject": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "description": "The value of the credentialSubject property is defined as a set of objects that contain one or more properties that are each related to a subject of the verifiable credential. Each object MAY contain an id.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#credential-subject | Credential Subject }" + }, + "DateType": { + "type": "string", + "description": "Represents an issuance or expiration date for Credentials / Presentations. This is used as input when creating them." + }, + "CredentialStatus": { + "type": "object", + "properties": { + "id": { "type": "string" }, - "expirationDate": { + "type": { "type": "string" + } + }, + "description": "Used for the discovery of information about the current status of a verifiable credential, such as whether it is suspended or revoked. The precise contents of the credential status information is determined by the specific `credentialStatus` type definition, and varies depending on factors such as whether it is simple to implement or if it is privacy-enhancing.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status }" + }, + "ProofFormat": { + "type": "string", + "enum": [ + "jwt", + "lds" + ], + "description": "The type of encoding to be used for the Verifiable Credential or Presentation to be generated.\n\nOnly `jwt` and `lds` is supported at the moment." + }, + "VerifiableCredential": { + "type": "object", + "properties": { + "proof": { + "$ref": "#/components/schemas/ProofType" + }, + "issuer": { + "$ref": "#/components/schemas/IssuerType" }, "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } + "$ref": "#/components/schemas/CredentialSubject" }, - "credentialStatus": { - "type": "object", - "properties": { - "id": { - "type": "string" + "type": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } }, - "type": { + { "type": "string" } - }, - "required": [ - "id", - "type" ] }, - "proof": { - "type": "object", - "properties": { - "type": { + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } - } + ] + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + }, + "id": { + "type": "string" } }, "required": [ @@ -200,82 +175,24 @@ "credentialSubject", "issuanceDate", "issuer", - "proof", - "type" + "proof" ], - "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + "description": "Represents a signed Verifiable Credential payload (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + }, + "ProofType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "description": "A proof property of a Verifiable Credential or Presentation" }, "ICreateVerifiablePresentationArgs": { "type": "object", "properties": { "presentation": { - "type": "object", - "properties": { - "@context": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "type": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "id": { - "type": "string" - }, - "verifiableCredential": { - "type": "array", - "items": { - "anyOf": [ - { - "$ref": "#/components/schemas/VerifiableCredential" - }, - { - "$ref": "#/components/schemas/JWT" - } - ] - } - }, - "holder": { - "type": "string" - }, - "verifier": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - } - }, + "$ref": "#/components/schemas/PresentationPayload", "description": "The json payload of the Presentation according to the\n {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model } .\n\nThe signer of the Presentation is chosen based on the `holder` property of the `presentation`\n\n'@context', 'type' and 'issuanceDate' will be added automatically if omitted" }, "save": { @@ -305,31 +222,25 @@ ], "description": "Encapsulates the parameters required to create a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" }, - "JWT": { - "type": "string" - }, - "VerifiablePresentation": { + "PresentationPayload": { "type": "object", "properties": { - "id": { - "type": "string" - }, "holder": { "type": "string" }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" + "verifiableCredential": { + "type": "array", + "items": { + "$ref": "#/components/schemas/W3CVerifiableCredential" + } }, - "@context": { + "type": { "type": "array", "items": { "type": "string" } }, - "type": { + "@context": { "type": "array", "items": { "type": "string" @@ -341,35 +252,105 @@ "type": "string" } }, + "issuanceDate": { + "$ref": "#/components/schemas/DateType" + }, + "expirationDate": { + "$ref": "#/components/schemas/DateType" + }, + "id": { + "type": "string" + } + }, + "required": [ + "holder" + ], + "description": "Used as input when creating Verifiable Presentations" + }, + "W3CVerifiableCredential": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/CompactJWT" + } + ], + "description": "Represents a signed Verifiable Credential (includes proof), in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model } \nSee {@link https://www.w3.org/TR/vc-data-model/#proof-formats | proof formats }" + }, + "CompactJWT": { + "type": "string", + "description": "Represents a Json Web Token in compact form. \"header.payload.signature\"" + }, + "VerifiablePresentation": { + "type": "object", + "properties": { + "proof": { + "$ref": "#/components/schemas/ProofType" + }, + "holder": { + "type": "string" + }, "verifiableCredential": { "type": "array", "items": { - "$ref": "#/components/schemas/VerifiableCredential" + "$ref": "#/components/schemas/W3CVerifiableCredential" } }, - "proof": { - "type": "object", - "properties": { - "type": { + "type": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } + ] + }, + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "verifier": { + "type": "array", + "items": { + "type": "string" } + }, + "issuanceDate": { + "type": "string" + }, + "expirationDate": { + "type": "string" + }, + "id": { + "type": "string" } }, "required": [ "@context", "holder", - "proof", - "type", - "verifier" + "proof" ], - "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + "description": "Represents a signed Verifiable Presentation (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" }, "IVerifyCredentialArgs": { "type": "object", "properties": { "credential": { - "$ref": "#/components/schemas/VerifiableCredential", + "$ref": "#/components/schemas/W3CVerifiableCredential", "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" } }, @@ -382,7 +363,7 @@ "type": "object", "properties": { "presentation": { - "$ref": "#/components/schemas/VerifiablePresentation", + "$ref": "#/components/schemas/W3CVerifiablePresentation", "description": "The json payload of the Credential according to the\n {@link https://www.w3.org/TR/vc-data-model/#credentials | canonical model } \n\nThe signer of the Credential is chosen based on the `issuer.id` property of the `credential`" }, "challenge": { @@ -398,6 +379,17 @@ "presentation" ], "description": "Encapsulates the parameters required to verify a\n {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" + }, + "W3CVerifiablePresentation": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiablePresentation" + }, + { + "$ref": "#/components/schemas/CompactJWT" + } + ], + "description": "Represents a signed Verifiable Presentation (includes proof) in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" } }, "methods": { diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index 1fd641e60..6ad5d3d47 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -10,7 +10,7 @@ jest.mock('did-jwt-vc', () => { return mockDidJwtVc }) -import { IIdentifier, IKey, VerifiableCredential, W3CCredential, W3CPresentation } from '@veramo/core' +import { CredentialPayload, IIdentifier, IKey, PresentationPayload, VerifiableCredential } from '@veramo/core' import { CredentialIssuer, IContext } from '../action-handler' const mockIdentifiers: IIdentifier[] = [ @@ -79,6 +79,7 @@ let agent = { dataStoreSaveVerifiablePresentation: jest.fn().mockImplementation(async (args): Promise => true), getSchema: jest.fn(), didManagerGet: jest.fn(), + didManagerFind: jest.fn(), createVerifiableCredentialLD: jest.fn(), createVerifiablePresentationLD: jest.fn(), verifyCredentialLD: jest.fn(), @@ -87,20 +88,19 @@ let agent = { describe('@veramo/credential-w3c', () => { test.each(mockIdentifiers)('handles createVerifiableCredential', async (mockIdentifier) => { - expect.assertions(3 * mockIdentifiers.length) + expect.assertions(3) agent.didManagerGet = jest.fn().mockImplementation(async (args): Promise => mockIdentifier) const context: IContext = { agent: agent } - for (let otherMockIdentifier of mockIdentifiers) { - const credential: W3CCredential = { + const credential: CredentialPayload = { '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2020/demo/4342323'], type: ['VerifiableCredential', 'PublicProfile'], issuer: { id: mockIdentifier.did }, issuanceDate: new Date().toISOString(), id: 'vc1', credentialSubject: { - id: otherMockIdentifier.did, + id: 'https://example.com/user/alice', name: 'Alice', profilePicture: 'https://example.com/a.png', address: { @@ -124,59 +124,56 @@ describe('@veramo/credential-w3c', () => { verifiableCredential: 'mockCredential', }) expect(vc).toEqual('mockCredential') - } }) test.each(mockIdentifiers)('handles createVerifiablePresentation', async (mockIdentifier) => { - expect.assertions(3 * mockIdentifiers.length) + expect.assertions(3) agent.didManagerGet = jest.fn().mockImplementation(async (args): Promise => mockIdentifier) const context: IContext = { agent: agent } - mockIdentifiers.forEach(async (otherMockIdentifier) => { - const credential: VerifiableCredential = { - '@context': ['https://www.w3.org/2018/credentials/v1'], - type: ['VerifiableCredential', 'PublicProfile'], - issuer: { id: mockIdentifier.did }, - issuanceDate: new Date().toISOString(), - id: 'vc1', - credentialSubject: { - id: otherMockIdentifier.did, - name: 'Alice', - profilePicture: 'https://example.com/a.png', - address: { - street: 'Some str.', - house: 1, - }, - }, - proof: { - jwt: 'mockJWT', + + const credential: VerifiableCredential = { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiableCredential', 'PublicProfile'], + issuer: { id: mockIdentifier.did }, + issuanceDate: new Date().toISOString(), + id: 'vc1', + credentialSubject: { + id: "https://example.com/user/alice", + name: 'Alice', + profilePicture: 'https://example.com/a.png', + address: { + street: 'Some str.', + house: 1, }, - } + }, + proof: { + jwt: 'mockJWT', + }, + } - const presentation: W3CPresentation = { - '@context': ['https://www.w3.org/2018/credentials/v1'], - type: ['VerifiablePresentation'], - holder: mockIdentifier.did, - verifier: [otherMockIdentifier.did], - issuanceDate: new Date().toISOString(), - verifiableCredential: [credential], - } + const presentation: PresentationPayload = { + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + holder: mockIdentifier.did, + issuanceDate: new Date().toISOString(), + verifiableCredential: [credential], + } - const vp = await w3c.createVerifiablePresentation( - { - presentation, - save: true, - proofFormat: 'jwt', - }, - context, - ) + const vp = await w3c.createVerifiablePresentation( + { + presentation, + save: true, + proofFormat: 'jwt', + }, + context, + ) - expect(context.agent.didManagerGet).toBeCalledWith({ did: mockIdentifier.did }) - expect(context.agent.dataStoreSaveVerifiablePresentation).toBeCalledWith({ - verifiablePresentation: 'mockPresentation', - }) - expect(vp).toEqual('mockPresentation') + expect(context.agent.didManagerGet).toBeCalledWith({ did: mockIdentifier.did }) + expect(context.agent.dataStoreSaveVerifiablePresentation).toBeCalledWith({ + verifiablePresentation: 'mockPresentation', }) + expect(vp).toEqual('mockPresentation') }) }) diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 1e535a92d..5d8695dd8 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -1,48 +1,39 @@ import { + CredentialPayload, IAgentContext, IAgentPlugin, - IResolver, + IDataStore, IDIDManager, + IIdentifier, + IKey, IKeyManager, IPluginMethodMap, - IDataStore, - IKey, - IIdentifier, VerifiableCredential, VerifiablePresentation, W3CPresentation, + IResolver, + PresentationPayload, + VerifiableCredential, + VerifiablePresentation, + W3CVerifiableCredential, + W3CVerifiablePresentation, } from '@veramo/core' import { createVerifiableCredentialJwt, createVerifiablePresentationJwt, - verifyCredential as verifyCredentialJWT, - verifyPresentation as verifyPresentationJWT, - CredentialPayload, normalizeCredential, normalizePresentation, + verifyCredential as verifyCredentialJWT, + verifyPresentation as verifyPresentationJWT, } from 'did-jwt-vc' import { decodeJWT } from 'did-jwt' import { schema } from './' import Debug from 'debug' -import { JWT } from 'did-jwt-vc/lib/types' import { Resolvable } from 'did-resolver' -import { asArray } from "@veramo/utils"; +import { asArray, extractIssuer, isDefined, MANDATORY_CREDENTIAL_CONTEXT, processEntryToArray } from "@veramo/utils"; const debug = Debug('veramo:w3c:action-handler') -export interface PresentationPayload { - '@context': string | string[] - type: string | string[] - id?: string - verifiableCredential?: (VerifiableCredential | JWT)[] - holder: string - verifier?: string | string[] - issuanceDate?: string - expirationDate?: string, - - [x: string]: any -} - /** * The type of encoding to be used for the Verifiable Credential or Presentation to be generated. * @@ -68,7 +59,7 @@ export interface ICreateVerifiablePresentationArgs { * * '@context', 'type' and 'issuanceDate' will be added automatically if omitted */ - presentation: Partial + presentation: PresentationPayload /** * If this parameter is true, the resulting VerifiablePresentation is sent to the @@ -115,7 +106,7 @@ export interface ICreateVerifiableCredentialArgs { * * '@context', 'type' and 'issuanceDate' will be added automatically if omitted */ - credential: Partial + credential: CredentialPayload /** * If this parameter is true, the resulting VerifiablePresentation is sent to the @@ -150,7 +141,7 @@ export interface IVerifyCredentialArgs { * of the `credential` * */ - credential: VerifiableCredential + credential: W3CVerifiableCredential } /** @@ -168,7 +159,7 @@ export interface IVerifyPresentationArgs { * of the `credential` * */ - presentation: VerifiablePresentation + presentation: W3CVerifiablePresentation /** * Optional (only for JWT) string challenge parameter to verify the verifiable presentation against @@ -282,18 +273,31 @@ export class CredentialIssuer implements IAgentPlugin { args: ICreateVerifiablePresentationArgs, context: IContext, ): Promise { - const presentation: Partial = { + const presentationContext = processEntryToArray(args?.presentation?.['@context'], MANDATORY_CREDENTIAL_CONTEXT) + const presentationType = processEntryToArray(args?.presentation?.type, 'VerifiablePresentation') + const presentation: PresentationPayload = { ...args?.presentation, - '@context': args?.presentation['@context'] || ['https://www.w3.org/2018/credentials/v1'], - //FIXME: make sure 'VerifiablePresentation' is the first element in this array: - type: args?.presentation?.type || ['VerifiablePresentation'], - issuanceDate: args?.presentation?.issuanceDate || new Date().toISOString(), + '@context': presentationContext, + type: presentationType, + issuanceDate: args?.presentation?.issuanceDate || new Date(), } - if (!presentation.holder || typeof presentation.holder === 'undefined') { + if (!isDefined(presentation.holder)) { throw new Error('invalid_argument: args.presentation.holder must not be empty') } + if (args.presentation.verifiableCredential) { + const credentials = args.presentation.verifiableCredential.map(cred => { + // map JWT credentials to their canonical form + if (typeof cred !== 'string' && cred.proof.jwt) { + return cred.proof.jwt + } else { + return cred + } + }) + presentation.verifiableCredential = credentials + } + let identifier: IIdentifier try { identifier = await context.agent.didManagerGet({ did: presentation.holder }) @@ -316,7 +320,6 @@ export class CredentialIssuer implements IAgentPlugin { } else { // only add issuanceDate for JWT presentation.issuanceDate = args.presentation.issuanceDate || new Date().toISOString() - //FIXME: Throw an `unsupported_format` error if the `args.proofFormat` is not `jwt` debug('Signing VP with', identifier.did) let alg = 'ES256K' if (key.type === 'Ed25519') { @@ -325,7 +328,7 @@ export class CredentialIssuer implements IAgentPlugin { const signer = wrapSigner(context, key, alg) const jwt = await createVerifiablePresentationJwt( - presentation as PresentationPayload, + presentation as any, { did: identifier.did, signer, alg }, { removeOriginalFields: args.removeOriginalFields }, ) @@ -348,16 +351,18 @@ export class CredentialIssuer implements IAgentPlugin { args: ICreateVerifiableCredentialArgs, context: IContext, ): Promise { - const credential: Partial = { + const credentialContext = processEntryToArray(args?.credential?.['@context'], MANDATORY_CREDENTIAL_CONTEXT) + const credentialType = processEntryToArray(args?.credential?.type, 'VerifiableCredential') + const credential: CredentialPayload = { ...args?.credential, - '@context': args?.credential?.['@context'] || ['https://www.w3.org/2018/credentials/v1'], - //FIXME: make sure 'VerifiableCredential' is the first element in this array: - type: args?.credential?.type || ['VerifiableCredential'], + '@context': credentialContext, + type: credentialType, issuanceDate: args?.credential?.issuanceDate || new Date().toISOString(), } + //FIXME: if the identifier is not found, the error message should reflect that. - const issuer = typeof credential.issuer === 'string' ? credential.issuer : credential?.issuer?.id + const issuer = extractIssuer(credential) if (!issuer || typeof issuer === 'undefined') { throw new Error('invalid_argument: args.credential.issuer must not be empty') } @@ -388,7 +393,7 @@ export class CredentialIssuer implements IAgentPlugin { } const signer = wrapSigner(context, key, alg) const jwt = await createVerifiableCredentialJwt( - credential as CredentialPayload, + credential as any, { did: identifier.did, signer, alg }, { removeOriginalFields: args.removeOriginalFields }, ) @@ -413,7 +418,7 @@ export class CredentialIssuer implements IAgentPlugin { context: IContext, ): Promise { const credential = args.credential - if (typeof credential === 'string' || credential.proof.jwt) { + if (typeof credential === 'string' || (credential)?.proof?.jwt) { // JWT let jwt: string if (typeof credential === 'string') { @@ -446,7 +451,7 @@ export class CredentialIssuer implements IAgentPlugin { context: IContext, ): Promise { const presentation = args.presentation - if (typeof presentation === 'string' || (presentation).proof.jwt) { + if (typeof presentation === 'string' || (presentation)?.proof?.jwt) { // JWT let jwt: string if (typeof presentation === 'string') { diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index b2e3af080..2fd21be4c 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -1,12 +1,15 @@ import { IAgentContext, IResolver, VerifiableCredential, VerifiablePresentation } from '@veramo/core' import { AbstractMessageHandler, Message } from '@veramo/message-handler' -import { blake2bHex } from 'blakejs' -import Debug from 'debug' +import { computeEntryHash, decodeCredentialToObject, extractIssuer } from '@veramo/utils' import { - normalizeCredential, normalizePresentation, validateJwtCredentialPayload, + normalizeCredential, + normalizePresentation, + validateJwtCredentialPayload, validateJwtPresentationPayload } from 'did-jwt-vc' import { ICredentialIssuer } from './action-handler' +import { v4 as uuidv4 } from 'uuid' +import Debug from 'debug' const debug = Debug('veramo:w3c:message-handler') @@ -61,7 +64,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { const presentation = normalizePresentation(message.raw) const credentials = presentation.verifiableCredential - message.id = blake2bHex(message.raw) + message.id = computeEntryHash(message.raw) message.type = MessageTypes.vp message.from = presentation.holder message.to = presentation.verifier?.[0] @@ -75,7 +78,8 @@ export class W3cMessageHandler extends AbstractMessageHandler { message.credentials = credentials return message - } catch (e) { } + } catch (e) { + } try { validateJwtCredentialPayload(data) @@ -83,7 +87,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { debug('JWT is', MessageTypes.vc) const credential = normalizeCredential(message.raw) - message.id = blake2bHex(message.raw) + message.id = computeEntryHash(message.raw) message.type = MessageTypes.vc message.from = credential.issuer.id message.to = credential.credentialSubject.id @@ -95,7 +99,8 @@ export class W3cMessageHandler extends AbstractMessageHandler { message.createdAt = credential.issuanceDate message.credentials = [credential] return message - } catch (e) { } + } catch (e) { + } } // LDS Verification and Handling @@ -105,9 +110,9 @@ export class W3cMessageHandler extends AbstractMessageHandler { // throws on error. await context.agent.verifyCredential({ credential }) - message.id = blake2bHex(message.raw) + message.id = computeEntryHash(message.raw || message.id || uuidv4()) message.type = MessageTypes.vc - message.from = credential.issuer.id + message.from = extractIssuer(credential) message.to = credential.credentialSubject.id if (credential.tag) { @@ -133,7 +138,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { const credentials = presentation.verifiableCredential - message.id = blake2bHex(message.raw) + message.id = computeEntryHash(message.raw || message.id || uuidv4()) message.type = MessageTypes.vp message.from = presentation.holder // message.to = presentation.verifier?.[0] @@ -144,7 +149,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { // message.createdAt = presentation.issuanceDate message.presentations = [presentation] - message.credentials = credentials + message.credentials = (credentials || []).map(decodeCredentialToObject) return message } diff --git a/packages/credential-w3c/types/blakejs/index.d.ts b/packages/credential-w3c/types/blakejs/index.d.ts deleted file mode 100644 index d3f1b7fe3..000000000 --- a/packages/credential-w3c/types/blakejs/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'blakejs' diff --git a/packages/data-store/package.json b/packages/data-store/package.json index d7ac4acce..f594a4a35 100644 --- a/packages/data-store/package.json +++ b/packages/data-store/package.json @@ -18,6 +18,7 @@ "@veramo/did-discovery": "^3.1.0", "@veramo/did-manager": "^3.1.0", "@veramo/key-manager": "^3.1.0", + "@veramo/utils": "^3.1.0", "debug": "^4.1.1", "typeorm": "0.2.41" }, diff --git a/packages/data-store/plugin.schema.json b/packages/data-store/plugin.schema.json index 240a636ed..fb0da69a6 100644 --- a/packages/data-store/plugin.schema.json +++ b/packages/data-store/plugin.schema.json @@ -422,30 +422,39 @@ "VerifiableCredential": { "type": "object", "properties": { - "@context": { - "type": "array", - "items": { - "type": "string" - } + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "id": { - "type": "string" + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, - "issuer": { - "type": "object", - "properties": { - "id": { + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } - }, - "required": [ - "id" ] }, "issuanceDate": { @@ -454,74 +463,111 @@ "expirationDate": { "type": "string" }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + }, + "id": { + "type": "string" + } + }, + "required": [ + "@context", + "credentialSubject", + "issuanceDate", + "issuer", + "proof" + ], + "description": "Represents a signed Verifiable Credential payload (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + }, + "ProofType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "description": "A proof property of a Verifiable Credential or Presentation" + }, + "IssuerType": { + "anyOf": [ + { "type": "object", "properties": { "id": { "type": "string" - }, - "type": { - "type": "string" } }, "required": [ - "id", - "type" + "id" ] }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + { + "type": "string" } - }, - "required": [ - "@context", - "credentialSubject", - "issuanceDate", - "issuer", - "proof", - "type" ], - "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + "description": "The issuer of a Credential or the holder of a Presentation.\n\nThe value of the issuer property MUST be either a URI or an object containing an id property. It is RECOMMENDED that the URI in the issuer or its id be one which, if de-referenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model }" }, - "VerifiablePresentation": { + "CredentialSubject": { "type": "object", "properties": { "id": { "type": "string" - }, - "holder": { + } + }, + "description": "The value of the credentialSubject property is defined as a set of objects that contain one or more properties that are each related to a subject of the verifiable credential. Each object MAY contain an id.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#credential-subject | Credential Subject }" + }, + "CredentialStatus": { + "type": "object", + "properties": { + "id": { "type": "string" }, - "issuanceDate": { + "type": { "type": "string" + } + }, + "description": "Used for the discovery of information about the current status of a verifiable credential, such as whether it is suspended or revoked. The precise contents of the credential status information is determined by the specific `credentialStatus` type definition, and varies depending on factors such as whether it is simple to implement or if it is privacy-enhancing.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status }" + }, + "VerifiablePresentation": { + "type": "object", + "properties": { + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "expirationDate": { + "holder": { "type": "string" }, - "@context": { + "verifiableCredential": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/W3CVerifiableCredential" } }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, "verifier": { "type": "array", @@ -529,29 +575,37 @@ "type": "string" } }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } + "issuanceDate": { + "type": "string" }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + "expirationDate": { + "type": "string" + }, + "id": { + "type": "string" } }, "required": [ "@context", "holder", - "proof", - "type", - "verifier" + "proof" ], - "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + "description": "Represents a signed Verifiable Presentation (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + }, + "W3CVerifiableCredential": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/CompactJWT" + } + ], + "description": "Represents a signed Verifiable Credential (includes proof), in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model } \nSee {@link https://www.w3.org/TR/vc-data-model/#proof-formats | proof formats }" + }, + "CompactJWT": { + "type": "string", + "description": "Represents a Json Web Token in compact form. \"header.payload.signature\"" }, "FindCredentialsArgs": { "$ref": "#/components/schemas/FindArgs-TCredentialColumns" diff --git a/packages/data-store/src/__tests__/data-store-orm.test.ts b/packages/data-store/src/__tests__/data-store-orm.test.ts index b8dc844c4..91a6dbf3e 100644 --- a/packages/data-store/src/__tests__/data-store-orm.test.ts +++ b/packages/data-store/src/__tests__/data-store-orm.test.ts @@ -1,17 +1,10 @@ -import { - Agent, - VerifiableCredential, - VerifiablePresentation, - IMessage, - IDataStore, - TAgent, -} from '@veramo/core' -import { createConnection, Connection } from 'typeorm' +import { Agent, IDataStore, IMessage, TAgent, VerifiableCredential, VerifiablePresentation, } from '@veramo/core' +import { Connection, createConnection } from 'typeorm' import { DataStoreORM, IDataStoreORM } from '../data-store-orm' import { FindArgs, TCredentialColumns, TMessageColumns, TPresentationColumns } from '../types' import { DataStore } from '../data-store' import { Entities } from '../index' -import fs from 'fs' +import * as fs from 'fs' const did1 = 'did:test:111' const did2 = 'did:test:222' @@ -386,7 +379,8 @@ describe('@veramo/data-store queries', () => { } const presentations = await agent.dataStoreORMGetVerifiablePresentations(args) - expect(presentations[0].verifiablePresentation.verifiableCredential?.[0]?.id).toEqual('vc6') + const cred0 = presentations[0].verifiablePresentation.verifiableCredential?.[0] as VerifiableCredential + expect(cred0.id).toEqual('vc6') }) it('should query identifiers', async () => { diff --git a/packages/data-store/src/data-store-orm.ts b/packages/data-store/src/data-store-orm.ts index e71eb15a7..b3044705c 100644 --- a/packages/data-store/src/data-store-orm.ts +++ b/packages/data-store/src/data-store-orm.ts @@ -37,7 +37,7 @@ import { FindArgs, } from './types' -import { Key, schema } from './' +import { schema } from './' interface IContext { authenticatedDid?: string diff --git a/packages/data-store/src/entities/credential.ts b/packages/data-store/src/entities/credential.ts index 04d83b49c..acc43f441 100644 --- a/packages/data-store/src/entities/credential.ts +++ b/packages/data-store/src/entities/credential.ts @@ -1,10 +1,10 @@ import { VerifiableCredential } from '@veramo/core' -import { blake2bHex } from 'blakejs' -import { Entity, Column, BaseEntity, ManyToOne, PrimaryColumn, OneToMany, ManyToMany } from 'typeorm' +import { BaseEntity, Column, Entity, ManyToMany, ManyToOne, OneToMany, PrimaryColumn } from 'typeorm' import { Identifier } from './identifier' import { Message } from './message' import { Presentation } from './presentation' import { Claim } from './claim' +import { asArray, computeEntryHash, extractIssuer } from "@veramo/utils"; @Entity('credential') export class Credential extends BaseEntity { @@ -17,7 +17,7 @@ export class Credential extends BaseEntity { set raw(raw: VerifiableCredential) { this._raw = raw - this.hash = blake2bHex(JSON.stringify(raw)) + this.hash = computeEntryHash(raw) } @Column('simple-json') @@ -74,10 +74,11 @@ export class Credential extends BaseEntity { messages: Message[] } -export const createCredentialEntity = (vc: VerifiableCredential): Credential => { +export const createCredentialEntity = (vci: VerifiableCredential): Credential => { + const vc = vci const credential = new Credential() - credential.context = vc['@context'] - credential.type = vc.type + credential.context = asArray(vc['@context']) + credential.type = asArray(vc.type || []) credential.id = vc.id if (vc.issuanceDate) { @@ -89,7 +90,7 @@ export const createCredentialEntity = (vc: VerifiableCredential): Credential => } const issuer = new Identifier() - issuer.did = vc.issuer.id + issuer.did = extractIssuer(vc) credential.issuer = issuer if (vc.credentialSubject.id) { @@ -105,7 +106,7 @@ export const createCredentialEntity = (vc: VerifiableCredential): Credential => if (type !== 'id') { const isObj = typeof value === 'function' || (typeof value === 'object' && !!value) const claim = new Claim() - claim.hash = blake2bHex(JSON.stringify(vc) + type) + claim.hash = computeEntryHash(JSON.stringify(vc) + type) claim.type = type claim.value = isObj ? JSON.stringify(value) : value claim.isObj = isObj @@ -120,6 +121,6 @@ export const createCredentialEntity = (vc: VerifiableCredential): Credential => } } - credential.raw = vc + credential.raw = vci return credential } diff --git a/packages/data-store/src/entities/presentation.ts b/packages/data-store/src/entities/presentation.ts index 119378df2..815e5d9c2 100644 --- a/packages/data-store/src/entities/presentation.ts +++ b/packages/data-store/src/entities/presentation.ts @@ -1,14 +1,16 @@ -import { VerifiablePresentation } from '@veramo/core' -import { blake2bHex } from 'blakejs' -import { Entity, Column, BaseEntity, ManyToOne, JoinTable, PrimaryColumn, ManyToMany } from 'typeorm' +import { VerifiableCredential, VerifiablePresentation } from '@veramo/core' + +import { BaseEntity, Column, Entity, JoinTable, ManyToMany, ManyToOne, PrimaryColumn } from 'typeorm' import { Identifier } from './identifier' import { Message } from './message' -import { Credential, createCredentialEntity } from './credential' +import { createCredentialEntity, Credential } from './credential' +import { asArray, computeEntryHash } from '@veramo/utils' +import { normalizeCredential } from 'did-jwt-vc' @Entity('presentation') export class Presentation extends BaseEntity { @PrimaryColumn() - //@ts-ignore + //@ts-ignore hash: string //@ts-ignore @@ -16,7 +18,7 @@ export class Presentation extends BaseEntity { set raw(raw: VerifiablePresentation) { this._raw = raw - this.hash = blake2bHex(JSON.stringify(raw)) + this.hash = computeEntryHash(raw) } @Column({ type: 'simple-json' }) @@ -29,7 +31,7 @@ export class Presentation extends BaseEntity { eager: true, onDelete: 'CASCADE', }) - //@ts-ignore + //@ts-ignore holder: Identifier @ManyToMany((type) => Identifier, (identifier) => identifier?.receivedPresentations, { @@ -38,43 +40,44 @@ export class Presentation extends BaseEntity { nullable: true, }) @JoinTable() - //@ts-ignore + //@ts-ignore verifier?: Identifier[] @Column({ nullable: true }) id?: String @Column() - //@ts-ignore + //@ts-ignore issuanceDate: Date @Column({ nullable: true }) expirationDate?: Date @Column('simple-array') - //@ts-ignore + //@ts-ignore context: string[] @Column('simple-array') - //@ts-ignore + //@ts-ignore type: string[] @ManyToMany((type) => Credential, (credential) => credential.presentations, { cascade: true, }) @JoinTable() - //@ts-ignore + //@ts-ignore credentials: Credential[] @ManyToMany((type) => Message, (message) => message.presentations) - //@ts-ignore + //@ts-ignore messages: Message[] } -export const createPresentationEntity = (vp: VerifiablePresentation): Presentation => { +export const createPresentationEntity = (vpi: VerifiablePresentation): Presentation => { + const vp = vpi const presentation = new Presentation() - presentation.context = vp['@context'] - presentation.type = vp.type + presentation.context = asArray(vp['@context']) + presentation.type = asArray(vp.type || []) presentation.id = vp.id if (vp.issuanceDate) { @@ -89,14 +92,22 @@ export const createPresentationEntity = (vp: VerifiablePresentation): Presentati holder.did = vp.holder presentation.holder = holder - presentation.verifier = vp.verifier?.map((verifierDid) => { + presentation.verifier = asArray(vp.verifier || []).map((verifierDid) => { const id = new Identifier() id.did = verifierDid return id }) - presentation.raw = vp - - presentation.credentials = (vp.verifiableCredential || []).map(createCredentialEntity) + presentation.raw = vpi + + presentation.credentials = (vp.verifiableCredential || []) + .map(cred => { + if (typeof cred === 'string') { + return normalizeCredential(cred) + } else { + return cred + } + }) + .map(createCredentialEntity) return presentation } diff --git a/packages/data-store/tsconfig.json b/packages/data-store/tsconfig.json index a752bc8ad..dd5d64e59 100644 --- a/packages/data-store/tsconfig.json +++ b/packages/data-store/tsconfig.json @@ -7,6 +7,7 @@ "references": [ { "path": "../core" }, { "path": "../did-manager" }, - { "path": "../key-manager" } + { "path": "../key-manager" }, + { "path": "../utils" } ] } diff --git a/packages/did-comm/plugin.schema.json b/packages/did-comm/plugin.schema.json index 416fbb25d..a4416308c 100644 --- a/packages/did-comm/plugin.schema.json +++ b/packages/did-comm/plugin.schema.json @@ -286,30 +286,39 @@ "VerifiableCredential": { "type": "object", "properties": { - "@context": { - "type": "array", - "items": { - "type": "string" - } + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "id": { - "type": "string" + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, - "issuer": { - "type": "object", - "properties": { - "id": { + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } - }, - "required": [ - "id" ] }, "issuanceDate": { @@ -318,74 +327,111 @@ "expirationDate": { "type": "string" }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + }, + "id": { + "type": "string" + } + }, + "required": [ + "@context", + "credentialSubject", + "issuanceDate", + "issuer", + "proof" + ], + "description": "Represents a signed Verifiable Credential payload (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + }, + "ProofType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "description": "A proof property of a Verifiable Credential or Presentation" + }, + "IssuerType": { + "anyOf": [ + { "type": "object", "properties": { "id": { "type": "string" - }, - "type": { - "type": "string" } }, "required": [ - "id", - "type" + "id" ] }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + { + "type": "string" } - }, - "required": [ - "@context", - "credentialSubject", - "issuanceDate", - "issuer", - "proof", - "type" ], - "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + "description": "The issuer of a Credential or the holder of a Presentation.\n\nThe value of the issuer property MUST be either a URI or an object containing an id property. It is RECOMMENDED that the URI in the issuer or its id be one which, if de-referenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model }" }, - "VerifiablePresentation": { + "CredentialSubject": { "type": "object", "properties": { "id": { "type": "string" - }, - "holder": { + } + }, + "description": "The value of the credentialSubject property is defined as a set of objects that contain one or more properties that are each related to a subject of the verifiable credential. Each object MAY contain an id.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#credential-subject | Credential Subject }" + }, + "CredentialStatus": { + "type": "object", + "properties": { + "id": { "type": "string" }, - "issuanceDate": { + "type": { "type": "string" + } + }, + "description": "Used for the discovery of information about the current status of a verifiable credential, such as whether it is suspended or revoked. The precise contents of the credential status information is determined by the specific `credentialStatus` type definition, and varies depending on factors such as whether it is simple to implement or if it is privacy-enhancing.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status }" + }, + "VerifiablePresentation": { + "type": "object", + "properties": { + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "expirationDate": { + "holder": { "type": "string" }, - "@context": { + "verifiableCredential": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/W3CVerifiableCredential" } }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, "verifier": { "type": "array", @@ -393,29 +439,37 @@ "type": "string" } }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } + "issuanceDate": { + "type": "string" }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + "expirationDate": { + "type": "string" + }, + "id": { + "type": "string" } }, "required": [ "@context", "holder", - "proof", - "type", - "verifier" + "proof" + ], + "description": "Represents a signed Verifiable Presentation (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + }, + "W3CVerifiableCredential": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/CompactJWT" + } ], - "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + "description": "Represents a signed Verifiable Credential (includes proof), in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model } \nSee {@link https://www.w3.org/TR/vc-data-model/#proof-formats | proof formats }" + }, + "CompactJWT": { + "type": "string", + "description": "Represents a Json Web Token in compact form. \"header.payload.signature\"" }, "IUnpackDIDCommMessageArgs": { "$ref": "#/components/schemas/IPackedDIDCommMessage", diff --git a/packages/message-handler/src/message-handler.ts b/packages/message-handler/src/message-handler.ts index b14de9793..f610471dd 100644 --- a/packages/message-handler/src/message-handler.ts +++ b/packages/message-handler/src/message-handler.ts @@ -5,7 +5,7 @@ import { IMessageHandler, IHandleMessageArgs, schema, - CoreEvents, + CoreEvents, IMessage, } from '@veramo/core' import { Message } from './message' import { AbstractMessageHandler } from './abstract-message-handler' @@ -51,7 +51,7 @@ export class MessageHandler implements IAgentPlugin { } /** {@inheritDoc @veramo/core#IMessageHandler.handleMessage} */ - public async handleMessage(args: IHandleMessageArgs, context: IAgentContext): Promise { + public async handleMessage(args: IHandleMessageArgs, context: IAgentContext): Promise { const { raw, metaData, save } = args debug('%o', { raw, metaData, save }) if (!this.messageHandler) { diff --git a/packages/selective-disclosure/package.json b/packages/selective-disclosure/package.json index 3d7dfeb2a..6b63fe303 100644 --- a/packages/selective-disclosure/package.json +++ b/packages/selective-disclosure/package.json @@ -19,9 +19,9 @@ "@veramo/data-store": "^3.1.0", "@veramo/did-jwt": "^3.1.0", "@veramo/message-handler": "^3.1.0", - "blakejs": "^1.1.0", "debug": "^4.1.1", - "did-jwt": "5.12.0" + "did-jwt": "5.12.0", + "uuid": "^8.3.0" }, "devDependencies": { "@types/debug": "4.1.7", diff --git a/packages/selective-disclosure/plugin.schema.json b/packages/selective-disclosure/plugin.schema.json index afc995a22..cada582d3 100644 --- a/packages/selective-disclosure/plugin.schema.json +++ b/packages/selective-disclosure/plugin.schema.json @@ -44,29 +44,43 @@ "VerifiablePresentation": { "type": "object", "properties": { - "id": { - "type": "string" + "proof": { + "$ref": "#/components/schemas/ProofType" }, "holder": { "type": "string" }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "@context": { + "verifiableCredential": { "type": "array", "items": { - "type": "string" + "$ref": "#/components/schemas/W3CVerifiableCredential" } }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] + }, + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, "verifier": { "type": "array", @@ -74,57 +88,79 @@ "type": "string" } }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/VerifiableCredential" - } + "issuanceDate": { + "type": "string" }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + "expirationDate": { + "type": "string" + }, + "id": { + "type": "string" } }, "required": [ "@context", "holder", - "proof", - "type", - "verifier" + "proof" ], - "description": "Represents a signed Verifiable Presentation (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + "description": "Represents a signed Verifiable Presentation (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model }" + }, + "ProofType": { + "type": "object", + "properties": { + "type": { + "type": "string" + } + }, + "description": "A proof property of a Verifiable Credential or Presentation" + }, + "W3CVerifiableCredential": { + "anyOf": [ + { + "$ref": "#/components/schemas/VerifiableCredential" + }, + { + "$ref": "#/components/schemas/CompactJWT" + } + ], + "description": "Represents a signed Verifiable Credential (includes proof), in either JSON or compact JWT format. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model } \nSee {@link https://www.w3.org/TR/vc-data-model/#proof-formats | proof formats }" }, "VerifiableCredential": { "type": "object", "properties": { - "@context": { - "type": "array", - "items": { - "type": "string" - } + "proof": { + "$ref": "#/components/schemas/ProofType" }, - "id": { - "type": "string" + "issuer": { + "$ref": "#/components/schemas/IssuerType" + }, + "credentialSubject": { + "$ref": "#/components/schemas/CredentialSubject" }, "type": { - "type": "array", - "items": { - "type": "string" - } + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { + "type": "string" + } + ] }, - "issuer": { - "type": "object", - "properties": { - "id": { + "@context": { + "anyOf": [ + { + "type": "array", + "items": { + "type": "string" + } + }, + { "type": "string" } - }, - "required": [ - "id" ] }, "issuanceDate": { @@ -133,47 +169,65 @@ "expirationDate": { "type": "string" }, - "credentialSubject": { - "type": "object", - "properties": { - "id": { - "type": "string" - } - } - }, "credentialStatus": { + "$ref": "#/components/schemas/CredentialStatus" + }, + "id": { + "type": "string" + } + }, + "required": [ + "@context", + "credentialSubject", + "issuanceDate", + "issuer", + "proof" + ], + "description": "Represents a signed Verifiable Credential payload (includes proof), using a JSON representation. See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + }, + "IssuerType": { + "anyOf": [ + { "type": "object", "properties": { "id": { "type": "string" - }, - "type": { - "type": "string" } }, "required": [ - "id", - "type" + "id" ] }, - "proof": { - "type": "object", - "properties": { - "type": { - "type": "string" - } - } + { + "type": "string" } - }, - "required": [ - "@context", - "credentialSubject", - "issuanceDate", - "issuer", - "proof", - "type" ], - "description": "Represents a signed Verifiable Credential payload (includes proof). See {@link https://www.w3.org/TR/vc-data-model/#credentials | VC data model }" + "description": "The issuer of a Credential or the holder of a Presentation.\n\nThe value of the issuer property MUST be either a URI or an object containing an id property. It is RECOMMENDED that the URI in the issuer or its id be one which, if de-referenced, results in a document containing machine-readable information about the issuer that can be used to verify the information expressed in the credential.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#issuer | Issuer data model }" + }, + "CredentialSubject": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + }, + "description": "The value of the credentialSubject property is defined as a set of objects that contain one or more properties that are each related to a subject of the verifiable credential. Each object MAY contain an id.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#credential-subject | Credential Subject }" + }, + "CredentialStatus": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + }, + "description": "Used for the discovery of information about the current status of a verifiable credential, such as whether it is suspended or revoked. The precise contents of the credential status information is determined by the specific `credentialStatus` type definition, and varies depending on factors such as whether it is simple to implement or if it is privacy-enhancing.\n\nSee {@link https://www.w3.org/TR/vc-data-model/#status | Credential Status }" + }, + "CompactJWT": { + "type": "string", + "description": "Represents a Json Web Token in compact form. \"header.payload.signature\"" }, "ICreateSelectiveDisclosureRequestArgs": { "type": "object", diff --git a/packages/selective-disclosure/src/action-handler.ts b/packages/selective-disclosure/src/action-handler.ts index 790293493..3b6b5a509 100644 --- a/packages/selective-disclosure/src/action-handler.ts +++ b/packages/selective-disclosure/src/action-handler.ts @@ -1,20 +1,20 @@ -import { IAgentContext, IDIDManager, IKeyManager, IAgentPlugin, VerifiablePresentation } from '@veramo/core' -import { IDataStoreORM, TClaimsColumns, FindArgs } from '@veramo/data-store' +import { IAgentContext, IAgentPlugin, IDIDManager, IKeyManager, VerifiablePresentation } from '@veramo/core' +import { FindArgs, IDataStoreORM, TClaimsColumns } from '@veramo/data-store' import { ICredentialIssuer } from '@veramo/credential-w3c' import { + ICreateProfileCredentialsArgs, + ICreateSelectiveDisclosureRequestArgs, ICredentialsForSdr, + IGetVerifiableCredentialsForSdrArgs, IPresentationValidationResult, ISelectiveDisclosure, - ICreateSelectiveDisclosureRequestArgs, - IGetVerifiableCredentialsForSdrArgs, - IValidatePresentationAgainstSdrArgs, ISelectiveDisclosureRequest, - ICreateProfileCredentialsArgs, + IValidatePresentationAgainstSdrArgs, } from './types' import { schema } from './' import { createJWT } from 'did-jwt' -import { blake2bHex } from 'blakejs' import Debug from 'debug' +import { asArray, bytesToBase64, computeEntryHash, decodeCredentialToObject, extractIssuer } from '@veramo/utils' /** * This class adds support for creating @@ -63,12 +63,12 @@ export class SelectiveDisclosure implements IAgentPlugin { const key = identifier.keys.find((k) => k.type === 'Secp256k1') if (!key) throw Error('Signing key not found') const signer = (data: string | Uint8Array) => { - let dataString, encoding: 'base16' | undefined + let dataString, encoding: 'base64' | undefined if (typeof data === 'string') { dataString = data encoding = undefined } else { - ;(dataString = Buffer.from(data).toString('hex')), (encoding = 'base16') + ;(dataString = bytesToBase64(data)), (encoding = 'base64') } return context.agent.keyManagerSign({ keyRef: key.kid, data: dataString, encoding }) } @@ -165,42 +165,44 @@ export class SelectiveDisclosure implements IAgentPlugin { let valid = true let claims = [] for (const credentialRequest of args.sdr.claims) { - let credentials = (args.presentation?.verifiableCredential || []).filter((credential) => { - if ( - credentialRequest.claimType && - credentialRequest.claimValue && - credential.credentialSubject[credentialRequest.claimType] !== credentialRequest.claimValue - ) { - return false - } + let credentials = (args.presentation?.verifiableCredential || []) + .map(decodeCredentialToObject) + .filter((credential) => { + if ( + credentialRequest.claimType && + credentialRequest.claimValue && + credential.credentialSubject[credentialRequest.claimType] !== credentialRequest.claimValue + ) { + return false + } - if ( - credentialRequest.claimType && - !credentialRequest.claimValue && - credential.credentialSubject[credentialRequest.claimType] === undefined - ) { - return false - } + if ( + credentialRequest.claimType && + !credentialRequest.claimValue && + credential.credentialSubject[credentialRequest.claimType] === undefined + ) { + return false + } - if ( - credentialRequest.issuers && - !credentialRequest.issuers.map((i) => i.did).includes(credential.issuer.id) - ) { - return false - } - if ( - credentialRequest.credentialContext && - !credential['@context'].includes(credentialRequest.credentialContext) - ) { - return false - } + if ( + credentialRequest.issuers && + !credentialRequest.issuers.map((i) => i.did).includes(extractIssuer(credential)) + ) { + return false + } + if ( + credentialRequest.credentialContext && + !asArray(credential['@context'] || []).includes(credentialRequest.credentialContext) + ) { + return false + } - if (credentialRequest.credentialType && !credential.type.includes(credentialRequest.credentialType)) { - return false - } + if (credentialRequest.credentialType && !asArray(credential.type || []).includes(credentialRequest.credentialType)) { + return false + } - return true - }) + return true + }) if (credentialRequest.essential === true && credentials.length == 0) { valid = false @@ -209,7 +211,7 @@ export class SelectiveDisclosure implements IAgentPlugin { claims.push({ ...credentialRequest, credentials: credentials.map((vc) => ({ - hash: blake2bHex(JSON.stringify(vc)), + hash: computeEntryHash(vc), verifiableCredential: vc, })), }) @@ -252,9 +254,7 @@ export class SelectiveDisclosure implements IAgentPlugin { const credential = await context.agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, - '@context': ['https://www.w3.org/2018/credentials/v1'], - type: ['VerifiableCredential', 'Profile'], - issuanceDate: new Date().toISOString(), + type: ['Profile'], credentialSubject: { id: identifier.did, picture: args.picture, @@ -270,9 +270,7 @@ export class SelectiveDisclosure implements IAgentPlugin { const credential = await context.agent.createVerifiableCredential({ credential: { issuer: { id: identifier.did }, - '@context': ['https://www.w3.org/2018/credentials/v1'], - type: ['VerifiableCredential', 'Profile'], - issuanceDate: new Date().toISOString(), + type: ['Profile'], credentialSubject: { id: identifier.did, url: args.url, @@ -288,9 +286,7 @@ export class SelectiveDisclosure implements IAgentPlugin { presentation: { verifier: args.holder ? [args.holder] : [], holder: identifier.did, - '@context': ['https://www.w3.org/2018/credentials/v1'], - type: ['VerifiablePresentation', 'Profile'], - issuanceDate: new Date().toISOString(), + type: ['Profile'], verifiableCredential: credentials, }, proofFormat: 'jwt', diff --git a/packages/selective-disclosure/src/message-handler.ts b/packages/selective-disclosure/src/message-handler.ts index cc1fbcebf..698ca40fd 100644 --- a/packages/selective-disclosure/src/message-handler.ts +++ b/packages/selective-disclosure/src/message-handler.ts @@ -1,8 +1,9 @@ import { IAgentContext, IMessageHandler } from '@veramo/core' import { Message, AbstractMessageHandler } from '@veramo/message-handler' -import { blake2bHex } from 'blakejs' +import { v4 as uuidv4 } from 'uuid' import Debug from 'debug' +import { asArray, computeEntryHash } from "@veramo/utils"; const debug = Debug('veramo:selective-disclosure:message-handler') /** @@ -28,16 +29,12 @@ export class SdrMessageHandler extends AbstractMessageHandler { if (message?.data?.type == MessageTypes.sdr && message?.data?.claims) { debug('Message type is', MessageTypes.sdr) - message.id = blake2bHex(message.raw) + message.id = computeEntryHash(message.raw || message.id || uuidv4()) message.type = MessageTypes.sdr message.from = message.data.iss if (message.data.replyTo) { - message.replyTo = Array.isArray(message.data.replyTo) - ? message.data.replyTo - : message.data.replyTo - ? [message.data.replyTo] - : undefined + message.replyTo = asArray(message.data.replyTo) } if (message.data.replyUrl) { diff --git a/packages/selective-disclosure/types/blakejs/index.d.ts b/packages/selective-disclosure/types/blakejs/index.d.ts deleted file mode 100644 index d3f1b7fe3..000000000 --- a/packages/selective-disclosure/types/blakejs/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'blakejs' diff --git a/packages/utils/package.json b/packages/utils/package.json index 86449b407..8b9c3b1c7 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -11,9 +11,11 @@ "@ethersproject/transactions": "^5.5.0", "@stablelib/ed25519": "^1.0.2", "@veramo/core": "^3.1.0", + "blakejs": "^1.1.1", "cross-fetch": "^3.1.4", "debug": "^4.1.1", "did-jwt": "^5.11.1", + "did-jwt-vc": "^2.1.7", "did-resolver": "^3.1.3", "uint8arrays": "^3.0.0", "uuid": "^8.3.0" diff --git a/packages/utils/src/credential-utils.ts b/packages/utils/src/credential-utils.ts new file mode 100644 index 000000000..419f92483 --- /dev/null +++ b/packages/utils/src/credential-utils.ts @@ -0,0 +1,106 @@ +import { asArray } from "./utils"; +import { + CredentialPayload, IMessage, + IssuerType, + PresentationPayload, VerifiableCredential, + W3CVerifiableCredential, + W3CVerifiablePresentation +} from "@veramo/core"; +import { blake2bHex } from 'blakejs' +import { decodeJWT } from "did-jwt"; +import { normalizeCredential } from "did-jwt-vc"; + +export const MANDATORY_CREDENTIAL_CONTEXT = 'https://www.w3.org/2018/credentials/v1' + +/** + * Processes a n entry or an array of entries into an array of entries. If a `startWithEntry` param is provided, it is + * set as the first item in the result array. + * @param inputEntryOrArray + * @param startWithEntry + * + * @beta This API may change without prior notice. + */ +export function processEntryToArray(inputEntryOrArray?: string | string[], startWithEntry?: string): string[] { + const result: string[] = asArray(inputEntryOrArray || []) || [startWithEntry] + if (startWithEntry && result[0] !== startWithEntry) { + result.unshift(startWithEntry) + } + return result; +} + +/** + * Parses a {@link W3CVerifiableCredential} and converts it to a {@link VerifiableCredential} so it is easier to use + * programmatically. + * + * @param input + + * @beta This API may change without prior notice. + */ +export function decodeCredentialToObject(input: W3CVerifiableCredential): VerifiableCredential { + return (typeof input === 'string') ? normalizeCredential(input) : input +} + +// /** +// * Parses a {@link VerifiablePresentation} and converts it to a {@link SignedPresentation} so it is easier to use +// * programmatically. +// * +// * @param input +// +// * @beta This API may change without prior notice. +// */ +// export function convertToSignedPresentation(input: VerifiablePresentation): SignedPresentation { +// let result: SignedPresentation +// if (typeof input === 'string') { +// result = normalizePresentation(input) +// } else { +// result = input as SignedPresentation +// result.verifiableCredential = result.verifiableCredential?.map(convertToSignedCredential) +// } +// return result +// } + +/** + * Computes a hash for a given credential or presentation. + * This hash is usable as an internal ID for database indexing + * + * @param input + * + * @beta This API may change without prior notice. + */ +export function computeEntryHash(input: W3CVerifiableCredential | W3CVerifiablePresentation | IMessage): string { + if (typeof input === 'string') { + // TODO: try to parse as JSON before assuming it's a JWT? + return blake2bHex(input) + } else if ((input)?.proof?.jwt) { + return blake2bHex((input).proof.jwt) + } else { + return blake2bHex(JSON.stringify(input)) + } +} + +/** + * Decodes a credential or presentation and returns the issuer ID + * `iss` from a JWT or `issuer`/`issuer.id` from a VC or `holder` from a VP + * + * @param input + * + * @beta This API may change without prior notice. + */ +export function extractIssuer(input: W3CVerifiableCredential | W3CVerifiablePresentation | CredentialPayload | PresentationPayload): string { + if (typeof input === 'string') { + // JWT + const { payload } = decodeJWT(input) + return payload.iss || '' + } else { + // JSON + let iss: IssuerType + if (input.issuer) { + iss = input.issuer + } else if (input.holder) { + iss = input.holder + } else { + iss = '' + } + return (typeof iss === 'string') ? iss : (iss?.id || '') + } +} \ No newline at end of file diff --git a/packages/utils/src/did-utils.ts b/packages/utils/src/did-utils.ts new file mode 100644 index 000000000..d52964461 --- /dev/null +++ b/packages/utils/src/did-utils.ts @@ -0,0 +1,185 @@ +import { convertPublicKeyToX25519, convertSecretKeyToX25519 } from '@stablelib/ed25519' +import { computePublicKey } from '@ethersproject/signing-key' +import { computeAddress } from '@ethersproject/transactions' +import { DIDDocumentSection, IAgentContext, IIdentifier, IKey, IResolver } from '@veramo/core' +import { DIDDocument, VerificationMethod } from 'did-resolver' +import { _ExtendedIKey, _ExtendedVerificationMethod, _NormalizedVerificationMethod } from "./types/utility-types"; +import { isDefined } from "./utils"; +import * as u8a from 'uint8arrays' +import Debug from 'debug' +const debug = Debug('veramo:utils') + +export function convertIdentifierEncryptionKeys(identifier: IIdentifier): IKey[] { + return identifier.keys + .map((key) => { + if (key.type === 'Ed25519') { + const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') + key.publicKeyHex = u8a.toString(convertPublicKeyToX25519(publicBytes), 'base16') + if (key.privateKeyHex) { + const privateBytes = u8a.fromString(key.privateKeyHex) + key.privateKeyHex = u8a.toString(convertSecretKeyToX25519(privateBytes), 'base16') + } + key.type = 'X25519' + } else if (key.type !== 'X25519') { + debug(`key of type ${key.type} is not supported for [de]encryption`) + return null + } + return key + }) + .filter(isDefined) +} + +export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[] { + return identifier.keys + .map((key) => { + if (key.type === 'Secp256k1') { + const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') + key.publicKeyHex = computePublicKey(publicBytes, true).substring(2) + key.meta = { ...key.meta } + key.meta.ethereumAddress = computeAddress('0x' + key.publicKeyHex) + } + return key + }) + .filter(isDefined) +} + +function compareBlockchainAccountId(localKey: IKey, verificationMethod: _NormalizedVerificationMethod) { + if (verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2020' || localKey.type !== 'Secp256k1') { + return false; + } + let vmEthAddr = verificationMethod.ethereumAddress?.toLowerCase() + if (!vmEthAddr) { + if (verificationMethod.blockchainAccountId?.includes('@eip155')) { + vmEthAddr = verificationMethod.blockchainAccountId?.split('@eip155')[0].toLowerCase() + } else if (verificationMethod.blockchainAccountId?.startsWith('eip155')) { + vmEthAddr = verificationMethod.blockchainAccountId.split(':')[2]?.toLowerCase() + } + } + const computedAddr = computeAddress('0x' + localKey.publicKeyHex).toLowerCase() + return (computedAddr === vmEthAddr) +} + +export async function mapIdentifierKeysToDoc( + identifier: IIdentifier, + section: DIDDocumentSection = 'keyAgreement', + context: IAgentContext, +): Promise<_ExtendedIKey[]> { + const didDocument = await resolveDidOrThrow(identifier.did, context) + + // dereference all key agreement keys from DID document and normalize + const documentKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( + didDocument, + section, + context, + ) + + let localKeys = identifier.keys.filter(isDefined) + if (section === 'keyAgreement') { + localKeys = convertIdentifierEncryptionKeys(identifier) + } else { + localKeys = compressIdentifierSecp256k1Keys(identifier) + } + // finally map the didDocument keys to the identifier keys by comparing `publicKeyHex` + const extendedKeys: _ExtendedIKey[] = documentKeys + .map((verificationMethod) => { + const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex || compareBlockchainAccountId(localKey, verificationMethod)) + if (localKey) { + const { meta, ...localProps } = localKey + return { ...localProps, meta: { ...meta, verificationMethod } } + } else { + return null + } + }) + .filter(isDefined) + + return extendedKeys +} + +export async function resolveDidOrThrow( + didUrl: string, + context: IAgentContext, +): Promise { + // TODO: add caching + const docResult = await context.agent.resolveDid({ didUrl: didUrl }) + const err = docResult?.didResolutionMetadata?.error + const msg = docResult?.didResolutionMetadata?.message + const didDocument = docResult.didDocument + if (!isDefined(didDocument) || err) { + throw new Error(`not_found: could not resolve DID document for '${didUrl}': ${err} ${msg}`) + } + return didDocument +} + +/** + * Dereferences keys from DID document and normalizes them for easy comparison. + * + * When dereferencing keyAgreement keys, only Ed25519 and X25519 curves are supported. + * Other key types are omitted from the result and Ed25519 keys are converted to X25519 + * + * @returns Promise + */ +export async function dereferenceDidKeys( + didDocument: DIDDocument, + section: DIDDocumentSection = 'keyAgreement', + context: IAgentContext, +): Promise<_NormalizedVerificationMethod[]> { + const convert = section === 'keyAgreement' + if (section === 'service') { + return [] + } + return ( + await Promise.all( + (didDocument[section] || []).map(async (key: string | VerificationMethod) => { + if (typeof key === 'string') { + try { + return (await context.agent.getDIDComponentById({ + didDocument, + didUrl: key, + section, + })) as _ExtendedVerificationMethod + } catch (e) { + return null + } + } else { + return key as _ExtendedVerificationMethod + } + }), + ) + ) + .filter(isDefined) + .map((key) => { + const hexKey = extractPublicKeyHex(key, convert) + const { publicKeyHex, publicKeyBase58, publicKeyBase64, publicKeyJwk, ...keyProps } = key + const newKey = { ...keyProps, publicKeyHex: hexKey } + if (convert && 'Ed25519VerificationKey2018' === newKey.type) { + newKey.type = 'X25519KeyAgreementKey2019' + } + return newKey + }) +} + +/** + * Converts the publicKey of a VerificationMethod to hex encoding (publicKeyHex) + * + * @param pk the VerificationMethod to be converted + * @param convert when this flag is set to true, Ed25519 keys are converted to their X25519 pairs + * @returns + */ +export function extractPublicKeyHex(pk: _ExtendedVerificationMethod, convert: boolean = false): string { + let keyBytes: Uint8Array + if (pk.publicKeyHex) { + keyBytes = u8a.fromString(pk.publicKeyHex, 'base16') + } else if (pk.publicKeyBase58) { + keyBytes = u8a.fromString(pk.publicKeyBase58, 'base58btc') + } else if (pk.publicKeyBase64) { + keyBytes = u8a.fromString(pk.publicKeyBase64, 'base64pad') + } else return '' + if (convert) { + if (['Ed25519', 'Ed25519VerificationKey2018'].includes(pk.type)) { + keyBytes = convertPublicKeyToX25519(keyBytes) + } else if (!['X25519', 'X25519KeyAgreementKey2019'].includes(pk.type)) { + return '' + } + } + return u8a.toString(keyBytes, 'base16') +} diff --git a/packages/utils/src/encodings.ts b/packages/utils/src/encodings.ts new file mode 100644 index 000000000..4a87366e4 --- /dev/null +++ b/packages/utils/src/encodings.ts @@ -0,0 +1,30 @@ +import * as u8a from 'uint8arrays' + +export function bytesToBase64url(b: Uint8Array): string { + return u8a.toString(b, 'base64url') +} + +export function base64ToBytes(s: string): Uint8Array { + const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') + return u8a.fromString(inputBase64Url, 'base64url') +} + +export function bytesToBase64(b: Uint8Array): string { + return u8a.toString(b, 'base64pad') +} + +export function encodeBase64url(s: string): string { + return bytesToBase64url(u8a.fromString(s)) +} + +export function decodeBase64url(s: string): string { + return u8a.toString(base64ToBytes(s)) +} + +export function encodeJoseBlob(payload: {}) { + return u8a.toString(u8a.fromString(JSON.stringify(payload), 'utf-8'), 'base64url') +} + +export function decodeJoseBlob(blob: string) { + return JSON.parse(u8a.toString(u8a.fromString(blob, 'base64url'), 'utf-8')) +} \ No newline at end of file diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 74906acfa..bc253baa3 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -4,6 +4,8 @@ * @packageDocumentation */ - export * from './types/utility-types' +export * from './credential-utils' +export * from './did-utils' +export * from './encodings' export * from './utils' diff --git a/packages/utils/src/utils.ts b/packages/utils/src/utils.ts index 24fc91088..f3081fc39 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/utils.ts @@ -1,44 +1,3 @@ -import { convertPublicKeyToX25519, convertSecretKeyToX25519 } from '@stablelib/ed25519' -import { computePublicKey } from '@ethersproject/signing-key' -import { computeAddress } from '@ethersproject/transactions' -import { DIDDocumentSection, IAgentContext, IIdentifier, IKey, IResolver } from '@veramo/core' -import { DIDDocument, VerificationMethod } from 'did-resolver' -import * as u8a from 'uint8arrays' - -import Debug from 'debug' -import { _ExtendedIKey, _ExtendedVerificationMethod, _NormalizedVerificationMethod, } from './types/utility-types' - -const debug = Debug('veramo:utils') - -export function bytesToBase64url(b: Uint8Array): string { - return u8a.toString(b, 'base64url') -} - -export function base64ToBytes(s: string): Uint8Array { - const inputBase64Url = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') - return u8a.fromString(inputBase64Url, 'base64url') -} - -export function bytesToBase64(b: Uint8Array): string { - return u8a.toString(b, 'base64pad') -} - -export function encodeBase64url(s: string): string { - return bytesToBase64url(u8a.fromString(s)) -} - -export function decodeBase64url(s: string): string { - return u8a.toString(base64ToBytes(s)) -} - -export function encodeJoseBlob(payload: {}) { - return u8a.toString(u8a.fromString(JSON.stringify(payload), 'utf-8'), 'base64url') -} - -export function decodeJoseBlob(blob: string) { - return JSON.parse(u8a.toString(u8a.fromString(blob, 'base64url'), 'utf-8')) -} - export function isDefined(arg: T): arg is Exclude { return arg !== null && typeof arg !== 'undefined' } @@ -46,178 +5,3 @@ export function isDefined(arg: T): arg is Exclude { export function asArray(arg: T | T[]): T[] { return Array.isArray(arg) ? arg : [arg] } - -export function convertIdentifierEncryptionKeys(identifier: IIdentifier): IKey[] { - return identifier.keys - .map((key) => { - if (key.type === 'Ed25519') { - const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') - key.publicKeyHex = u8a.toString(convertPublicKeyToX25519(publicBytes), 'base16') - if (key.privateKeyHex) { - const privateBytes = u8a.fromString(key.privateKeyHex) - key.privateKeyHex = u8a.toString(convertSecretKeyToX25519(privateBytes), 'base16') - } - key.type = 'X25519' - } else if (key.type !== 'X25519') { - debug(`key of type ${key.type} is not supported for [de]encryption`) - return null - } - return key - }) - .filter(isDefined) -} - -export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[] { - return identifier.keys - .map((key) => { - if (key.type === 'Secp256k1') { - const publicBytes = u8a.fromString(key.publicKeyHex, 'base16') - key.publicKeyHex = computePublicKey(publicBytes, true).substring(2) - key.meta = { ...key.meta } - key.meta.ethereumAddress = computeAddress('0x' + key.publicKeyHex) - } - return key - }) - .filter(isDefined) -} - -function compareBlockchainAccountId(localKey: IKey, verificationMethod: _NormalizedVerificationMethod) { - if (verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2020' || localKey.type !== 'Secp256k1') { - return false; - } - let vmEthAddr = verificationMethod.ethereumAddress?.toLowerCase() - if (!vmEthAddr) { - if (verificationMethod.blockchainAccountId?.includes('@eip155')) { - vmEthAddr = verificationMethod.blockchainAccountId?.split('@eip155')[0].toLowerCase() - } else if (verificationMethod.blockchainAccountId?.startsWith('eip155')) { - vmEthAddr = verificationMethod.blockchainAccountId.split(':')[2]?.toLowerCase() - } - } - const computedAddr = computeAddress('0x' + localKey.publicKeyHex).toLowerCase() - return (computedAddr === vmEthAddr) -} - -export async function mapIdentifierKeysToDoc( - identifier: IIdentifier, - section: DIDDocumentSection = 'keyAgreement', - context: IAgentContext, -): Promise<_ExtendedIKey[]> { - const didDocument = await resolveDidOrThrow(identifier.did, context) - - // dereference all key agreement keys from DID document and normalize - const documentKeys: _NormalizedVerificationMethod[] = await dereferenceDidKeys( - didDocument, - section, - context, - ) - - let localKeys = identifier.keys.filter(isDefined) - if (section === 'keyAgreement') { - localKeys = convertIdentifierEncryptionKeys(identifier) - } else { - localKeys = compressIdentifierSecp256k1Keys(identifier) - } - // finally map the didDocument keys to the identifier keys by comparing `publicKeyHex` - const extendedKeys: _ExtendedIKey[] = documentKeys - .map((verificationMethod) => { - const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex || compareBlockchainAccountId(localKey, verificationMethod)) - if (localKey) { - const { meta, ...localProps } = localKey - return { ...localProps, meta: { ...meta, verificationMethod } } - } else { - return null - } - }) - .filter(isDefined) - - return extendedKeys -} - -export async function resolveDidOrThrow( - didUrl: string, - context: IAgentContext, -): Promise { - // TODO: add caching - const docResult = await context.agent.resolveDid({ didUrl: didUrl }) - const err = docResult?.didResolutionMetadata?.error - const msg = docResult?.didResolutionMetadata?.message - const didDocument = docResult.didDocument - if (!isDefined(didDocument) || err) { - throw new Error(`not_found: could not resolve DID document for '${didUrl}': ${err} ${msg}`) - } - return didDocument -} - -/** - * Dereferences keys from DID document and normalizes them for easy comparison. - * - * When dereferencing keyAgreement keys, only Ed25519 and X25519 curves are supported. - * Other key types are omitted from the result and Ed25519 keys are converted to X25519 - * - * @returns Promise - */ -export async function dereferenceDidKeys( - didDocument: DIDDocument, - section: DIDDocumentSection = 'keyAgreement', - context: IAgentContext, -): Promise<_NormalizedVerificationMethod[]> { - const convert = section === 'keyAgreement' - if (section === 'service') { - return [] - } - return ( - await Promise.all( - (didDocument[section] || []).map(async (key: string | VerificationMethod) => { - if (typeof key === 'string') { - try { - return (await context.agent.getDIDComponentById({ - didDocument, - didUrl: key, - section, - })) as _ExtendedVerificationMethod - } catch (e) { - return null - } - } else { - return key as _ExtendedVerificationMethod - } - }), - ) - ) - .filter(isDefined) - .map((key) => { - const hexKey = extractPublicKeyHex(key, convert) - const { publicKeyHex, publicKeyBase58, publicKeyBase64, publicKeyJwk, ...keyProps } = key - const newKey = { ...keyProps, publicKeyHex: hexKey } - if (convert && 'Ed25519VerificationKey2018' === newKey.type) { - newKey.type = 'X25519KeyAgreementKey2019' - } - return newKey - }) -} - -/** - * Converts the publicKey of a VerificationMethod to hex encoding (publicKeyHex) - * - * @param pk the VerificationMethod to be converted - * @param convert when this flag is set to true, Ed25519 keys are converted to their X25519 pairs - * @returns - */ -export function extractPublicKeyHex(pk: _ExtendedVerificationMethod, convert: boolean = false): string { - let keyBytes: Uint8Array - if (pk.publicKeyHex) { - keyBytes = u8a.fromString(pk.publicKeyHex, 'base16') - } else if (pk.publicKeyBase58) { - keyBytes = u8a.fromString(pk.publicKeyBase58, 'base58btc') - } else if (pk.publicKeyBase64) { - keyBytes = u8a.fromString(pk.publicKeyBase64, 'base64pad') - } else return '' - if (convert) { - if (['Ed25519', 'Ed25519VerificationKey2018'].includes(pk.type)) { - keyBytes = convertPublicKeyToX25519(keyBytes) - } else if (!['X25519', 'X25519KeyAgreementKey2019'].includes(pk.type)) { - return '' - } - } - return u8a.toString(keyBytes, 'base16') -} diff --git a/packages/credential-ld/types/blakejs/index.d.ts b/packages/utils/types/import.types.d.ts similarity index 100% rename from packages/credential-ld/types/blakejs/index.d.ts rename to packages/utils/types/import.types.d.ts diff --git a/yarn.lock b/yarn.lock index 6202543ef..e7e84835d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3197,7 +3197,7 @@ bl@^4.1.0: inherits "^2.0.4" readable-stream "^3.4.0" -blakejs@^1.1.0: +blakejs@^1.1.0, blakejs@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.1.1.tgz#bf313053978b2cd4c444a48795710be05c785702" integrity sha512-bLG6PHOCZJKNshTjGRBvET0vTciwQE6zFKOKKXPDJfwFBd4Ac0yBfPZqcGvGJap50l7ktvlpFqc2jGVaUgbJgg== @@ -4224,7 +4224,7 @@ dezalgo@^1.0.0: asap "^2.0.0" wrappy "1" -did-jwt-vc@2.1.7: +did-jwt-vc@2.1.7, did-jwt-vc@^2.1.7: version "2.1.7" resolved "https://registry.yarnpkg.com/did-jwt-vc/-/did-jwt-vc-2.1.7.tgz#c9bf69cdf86f3e46db30344eb5d1e6cbada9164b" integrity sha512-MSuoy2yVRqlF4viGjtUzDmFBfxercwgUwIY+rs3swnyU7RmeZ5Iwp5TBhStIy3ro+BIQEPtXtp1xlcbATJW1iA== @@ -4579,7 +4579,7 @@ ethjs-util@0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" -ethr-did-registry@0.0.3, ethr-did-registry@^0.0.3: +ethr-did-registry@^0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/ethr-did-registry/-/ethr-did-registry-0.0.3.tgz#f363d2c73cb9572b57bd7a5c9c90c88485feceb5" integrity sha512-4BPvMGkxAK9vTduCq6D5b8ZqjteD2cvDIPPriXP6nnmPhWKFSxypo+AFvyQ0omJGa0cGTR+dkdI/8jiF7U/qaw== From aa48b7d6a9e819cdf4a1a322c3cd5ba8c758076b Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Tue, 23 Nov 2021 16:23:07 +0100 Subject: [PATCH 45/48] style: reformat code --- __tests__/initial.migration.test.ts | 2 +- __tests__/localAgent.test.ts | 66 +- __tests__/localMemoryStoreAgent.test.ts | 59 +- __tests__/restAgent.test.ts | 39 +- __tests__/shared/dbInitOptions.ts | 20 +- __tests__/shared/didDiscovery.ts | 4 +- __tests__/shared/verifiableDataLD.ts | 28 +- __tests__/shared/webDidFlow.ts | 8 +- __tests__/utils/fake-did.ts | 10 +- __tests__/utils/ganache-provider.ts | 2 +- packages/cli/src/credential.ts | 18 +- packages/cli/src/explore/credentials.ts | 2 +- packages/cli/src/presentation.ts | 20 +- packages/cli/src/util.ts | 37 +- packages/core/src/types/IAgent.ts | 3 +- packages/core/src/types/IDIDManager.ts | 5 +- packages/core/src/types/IDataStore.ts | 3 +- packages/core/src/types/IMessage.ts | 2 +- packages/core/src/types/vc-data-model.ts | 7 +- packages/credential-ld/src/action-handler.ts | 65 +- packages/credential-ld/src/index.ts | 8 +- .../credential-ld/src/ld-context-loader.ts | 12 +- .../credential-ld/src/ld-credential-module.ts | 4 +- .../credential-ld/src/ld-default-contexts.ts | 18 +- packages/credential-ld/src/ld-suite-loader.ts | 12 +- packages/credential-ld/src/ld-suites.ts | 12 +- .../EcdsaSecp256k1RecoverySignature2020.ts | 17 +- .../src/suites/Ed25519Signature2018.ts | 23 +- packages/credential-ld/src/types.ts | 10 +- .../src/__tests__/action-handler.test.ts | 61 +- packages/credential-w3c/src/action-handler.ts | 61 +- .../credential-w3c/src/message-handler.ts | 18 +- .../src/__tests__/data-store-orm.test.ts | 9 +- .../data-store/src/__tests__/entities.test.ts | 2 +- packages/data-store/src/entities/claim.ts | 4 +- .../data-store/src/entities/credential.ts | 2 +- packages/data-store/src/entities/message.ts | 21 +- .../data-store/src/entities/presentation.ts | 18 +- .../data-store/src/identifier/did-store.ts | 27 +- .../src/identifier/private-key-store.ts | 6 +- packages/data-store/src/index.ts | 26 +- .../migrations/4.allowNullVPIssuanceDate.ts | 44 +- packages/data-store/src/migrations/index.ts | 9 +- packages/data-store/types/blakejs/index.d.ts | 1 - packages/did-comm/src/didcomm.ts | 32 +- packages/did-comm/src/types/message-types.ts | 8 +- packages/did-provider-key/src/resolver.ts | 8 +- .../src/__tests__/integration.test.ts | 1 - packages/did-resolver/src/resolver.ts | 4 +- .../src/abstract-private-key-store.ts | 2 +- packages/key-manager/src/memory-key-store.ts | 2 +- .../kms-local/src/key-management-system.ts | 3 +- .../message-handler/src/message-handler.ts | 8 +- .../remote-server/src/messaging-router.ts | 2 +- .../remote-server/src/web-did-doc-router.ts | 7 +- .../src/action-handler.ts | 13 +- .../src/message-handler.ts | 2 +- packages/third.party.types.d.ts | 2 +- packages/utils/src/credential-utils.ts | 80 +- packages/utils/src/did-utils.ts | 18 +- packages/utils/src/encodings.ts | 2 +- packages/utils/src/index.ts | 2 +- .../utils/src/{utils.ts => type-utils.ts} | 4 +- yarn.lock | 964 +++++++++++------- 64 files changed, 1144 insertions(+), 845 deletions(-) delete mode 100644 packages/data-store/types/blakejs/index.d.ts rename packages/utils/src/{utils.ts => type-utils.ts} (52%) diff --git a/__tests__/initial.migration.test.ts b/__tests__/initial.migration.test.ts index e1f31f199..7f6710248 100644 --- a/__tests__/initial.migration.test.ts +++ b/__tests__/initial.migration.test.ts @@ -10,7 +10,7 @@ import { IKeyManager, IResolver, TAgent, - VerifiableCredential + VerifiableCredential, } from '../packages/core/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 82d1de734..05ef4da1b 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -20,19 +20,23 @@ import { KeyManager } from '../packages/key-manager/src' import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, } from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler } from '../packages/credential-w3c/src' import { CredentialIssuerLD, ICredentialIssuerLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, - VeramoEd25519Signature2018 + VeramoEd25519Signature2018, } from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' import { DIDComm, DIDCommHttpTransport, DIDCommMessageHandler, IDIDComm } from '../packages/did-comm/src' -import { ISelectiveDisclosure, SdrMessageHandler, SelectiveDisclosure, } from '../packages/selective-disclosure/src' +import { + ISelectiveDisclosure, + SdrMessageHandler, + SelectiveDisclosure, +} from '../packages/selective-disclosure/src' import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src' import { DIDDiscovery, IDIDDiscovery } from '../packages/did-discovery/src' @@ -77,22 +81,25 @@ jest.setTimeout(30000) const infuraProjectId = '3586660d179141e3801c3895de1c2eba' const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa830c' -let agent: TAgent +let agent: TAgent< + IDIDManager & + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialIssuer & + ICredentialIssuerLD & + ISelectiveDisclosure & + IDIDDiscovery +> let dbConnection: Promise let databaseFile: string const setup = async (options?: IAgentOptions): Promise => { - databaseFile = options?.context?.databaseFile || `./tmp/local-database-${Math.random().toPrecision(5)}.sqlite` + databaseFile = + options?.context?.databaseFile || `./tmp/local-database-${Math.random().toPrecision(5)}.sqlite` dbConnection = createConnection({ name: options?.context?.['dbName'] || 'test', type: 'sqlite', @@ -108,17 +115,19 @@ const setup = async (options?: IAgentOptions): Promise => { const { provider, registry } = await createGanacheProvider() - agent = createAgent({ + agent = createAgent< + IDIDManager & + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialIssuer & + ICredentialIssuerLD & + ISelectiveDisclosure & + IDIDDiscovery + >({ ...options, context: { // authenticatedDid: 'did:example:3456' @@ -201,10 +210,7 @@ const setup = async (options?: IAgentOptions): Promise => { new CredentialIssuer(), new CredentialIssuerLD({ contextMaps: [LdDefaultContexts, credential_contexts as any], - suites: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018() - ], + suites: [new VeramoEcdsaSecp256k1RecoverySignature2020(), new VeramoEd25519Signature2018()], }), new SelectiveDisclosure(), new DIDDiscovery({ diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index 1fac864e5..0c4684da4 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -19,19 +19,23 @@ import { DIDManager, MemoryDIDStore } from '../packages/did-manager/src' import { Connection, createConnection } from 'typeorm' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, } from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler } from '../packages/credential-w3c/src' import { CredentialIssuerLD, ICredentialIssuerLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, - VeramoEd25519Signature2018 + VeramoEd25519Signature2018, } from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' import { DIDComm, DIDCommMessageHandler, IDIDComm } from '../packages/did-comm/src' -import { ISelectiveDisclosure, SdrMessageHandler, SelectiveDisclosure, } from '../packages/selective-disclosure/src' +import { + ISelectiveDisclosure, + SdrMessageHandler, + SelectiveDisclosure, +} from '../packages/selective-disclosure/src' import { KeyManagementSystem } from '../packages/kms-local/src' import { DataStore, DataStoreORM, Entities, IDataStoreORM, migrations } from '../packages/data-store/src' import { FakeDidProvider, FakeDidResolver } from './utils/fake-did' @@ -59,16 +63,18 @@ jest.setTimeout(30000) const databaseFile = `./tmp/local-database2-${Math.random().toPrecision(5)}.sqlite` const infuraProjectId = '3586660d179141e3801c3895de1c2eba' -let agent: TAgent +let agent: TAgent< + IDIDManager & + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialIssuer & + ICredentialIssuerLD & + ISelectiveDisclosure +> let dbConnection: Promise const setup = async (options?: IAgentOptions): Promise => { @@ -83,16 +89,18 @@ const setup = async (options?: IAgentOptions): Promise => { entities: Entities, }) - agent = createAgent({ + agent = createAgent< + IDIDManager & + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialIssuer & + ICredentialIssuerLD & + ISelectiveDisclosure + >({ ...options, context: { // authenticatedDid: 'did:example:3456' @@ -159,10 +167,7 @@ const setup = async (options?: IAgentOptions): Promise => { new CredentialIssuer(), new CredentialIssuerLD({ contextMaps: [LdDefaultContexts, credential_contexts as any], - suites: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018() - ], + suites: [new VeramoEcdsaSecp256k1RecoverySignature2020(), new VeramoEd25519Signature2018()], }), new SelectiveDisclosure(), ...(options?.plugins || []), diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 9b3a04afc..93eaefbc8 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -23,19 +23,23 @@ import { KeyManager } from '../packages/key-manager/src' import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' -import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler, } from '../packages/credential-w3c/src' +import { CredentialIssuer, ICredentialIssuer, W3cMessageHandler } from '../packages/credential-w3c/src' import { CredentialIssuerLD, ICredentialIssuerLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, - VeramoEd25519Signature2018 + VeramoEd25519Signature2018, } from '../packages/credential-ld/src' import { EthrDIDProvider } from '../packages/did-provider-ethr/src' import { WebDIDProvider } from '../packages/did-provider-web/src' import { getDidKeyResolver, KeyDIDProvider } from '../packages/did-provider-key/src' import { DIDComm, DIDCommHttpTransport, DIDCommMessageHandler, IDIDComm } from '../packages/did-comm/src' -import { ISelectiveDisclosure, SdrMessageHandler, SelectiveDisclosure, } from '../packages/selective-disclosure/src' +import { + ISelectiveDisclosure, + SdrMessageHandler, + SelectiveDisclosure, +} from '../packages/selective-disclosure/src' import { KeyManagementSystem, SecretBox } from '../packages/kms-local/src' import { DataStore, @@ -89,17 +93,19 @@ let serverAgent: IAgent let restServer: Server const getAgent = (options?: IAgentOptions) => - createAgent({ + createAgent< + IDIDManager & + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialIssuer & + ICredentialIssuerLD & + ISelectiveDisclosure & + IDIDDiscovery + >({ ...options, plugins: [ new AgentRestClient({ @@ -187,10 +193,7 @@ const setup = async (options?: IAgentOptions): Promise => { new CredentialIssuer(), new CredentialIssuerLD({ contextMaps: [LdDefaultContexts, credential_contexts as any], - suites: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018() - ], + suites: [new VeramoEcdsaSecp256k1RecoverySignature2020(), new VeramoEd25519Signature2018()], }), new SelectiveDisclosure(), new DIDDiscovery({ diff --git a/__tests__/shared/dbInitOptions.ts b/__tests__/shared/dbInitOptions.ts index 7bdb52e4f..b289f4f60 100644 --- a/__tests__/shared/dbInitOptions.ts +++ b/__tests__/shared/dbInitOptions.ts @@ -12,16 +12,18 @@ import { import { IDataStoreORM } from '@veramo/data-store/src' import { ICredentialIssuer } from '@veramo/credential-w3c/src' import { IDIDComm, IPackedDIDCommMessage } from '../../packages/did-comm/src' -import { extractIssuer } from "../../packages/utils" +import { extractIssuer } from '../../packages/utils' -type ConfiguredAgent = TAgent +type ConfiguredAgent = TAgent< + IDataStoreORM & + IDataStore & + IDIDManager & + IKeyManager & + ICredentialIssuer & + IDIDComm & + IMessageHandler & + IResolver +> export default (testContext: { getAgent: () => ConfiguredAgent diff --git a/__tests__/shared/didDiscovery.ts b/__tests__/shared/didDiscovery.ts index 42301177d..3728d7b3f 100644 --- a/__tests__/shared/didDiscovery.ts +++ b/__tests__/shared/didDiscovery.ts @@ -106,7 +106,9 @@ export default (testContext: { const connection = getConnection('did-discovery-test') await connection.close() const result = await agent.discoverDid({ query: 'bob' }) - expect(result?.errors?.profile).toMatch(/(Connection with sqlite database is not established)|(Cannot read property 'connect' of undefined)/) + expect(result?.errors?.profile).toMatch( + /(Connection with sqlite database is not established)|(Cannot read property 'connect' of undefined)/, + ) }) }) } diff --git a/__tests__/shared/verifiableDataLD.ts b/__tests__/shared/verifiableDataLD.ts index e99065dac..ce3e42b15 100644 --- a/__tests__/shared/verifiableDataLD.ts +++ b/__tests__/shared/verifiableDataLD.ts @@ -199,15 +199,12 @@ export default (testContext: { const verifiableCredential = await agent.createVerifiableCredential({ credential: { issuer: { id: didKeyIdentifier.did }, - '@context': [ - 'https://www.w3.org/2018/credentials/v1', - 'https://veramo.io/contexts/profile/v1' - ], + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1'], type: ['VerifiableCredential', 'Profile'], issuanceDate: new Date().toISOString(), credentialSubject: { id: didKeyIdentifier.did, - name: "Martin, the great" + name: 'Martin, the great', }, }, proofFormat: 'lds', @@ -216,32 +213,37 @@ export default (testContext: { // Check credential: expect(verifiableCredential).toHaveProperty('proof') expect(verifiableCredential).toHaveProperty('proof.jws') - expect(verifiableCredential.proof.verificationMethod).toEqual(`${didKeyIdentifier.did}#${didKeyIdentifier.did.substring(didKeyIdentifier.did.lastIndexOf(':') + 1)}`) + expect(verifiableCredential.proof.verificationMethod).toEqual( + `${didKeyIdentifier.did}#${didKeyIdentifier.did.substring( + didKeyIdentifier.did.lastIndexOf(':') + 1, + )}`, + ) expect(verifiableCredential['@context']).toEqual([ 'https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1', ]) - expect(verifiableCredential['type']).toEqual( - ['VerifiableCredential', 'Profile']) + expect(verifiableCredential['type']).toEqual(['VerifiableCredential', 'Profile']) storedCredentialHash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) expect(typeof storedCredentialHash).toEqual('string') - const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential2 = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) expect(verifiableCredential).toEqual(verifiableCredential2) }) it('should verify a verifiable credential in LD with did:key', async () => { - const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ hash: storedCredentialHash }) + const verifiableCredential = await agent.dataStoreGetVerifiableCredential({ + hash: storedCredentialHash, + }) const result = await agent.verifyCredential({ - credential: verifiableCredential + credential: verifiableCredential, }) expect(result).toEqual(true) }) - - }) } diff --git a/__tests__/shared/webDidFlow.ts b/__tests__/shared/webDidFlow.ts index 2a9b2e031..47afba924 100644 --- a/__tests__/shared/webDidFlow.ts +++ b/__tests__/shared/webDidFlow.ts @@ -65,18 +65,18 @@ export default (testContext: { it('should create identifier with alias: alice', async () => { alice = await agent.didManagerGetOrCreate({ alias: 'alice', - provider: 'did:ethr:rinkeby' + provider: 'did:ethr:rinkeby', }) - + expect(alice.provider).toEqual('did:ethr:rinkeby') expect(alice.alias).toEqual('alice') expect(alice.did).toBeDefined() }) - + it('should create identifier with alias: bob', async () => { bob = await agent.didManagerGetOrCreate({ alias: 'bob', - provider: 'did:ethr:rinkeby' + provider: 'did:ethr:rinkeby', }) expect(bob.provider).toEqual('did:ethr:rinkeby') diff --git a/__tests__/utils/fake-did.ts b/__tests__/utils/fake-did.ts index 1be9edf72..257f7422c 100644 --- a/__tests__/utils/fake-did.ts +++ b/__tests__/utils/fake-did.ts @@ -1,4 +1,12 @@ -import { IAgentContext, IDIDManager, IIdentifier, IKey, IKeyManager, IService, TAgent } from '../../packages/core/src' +import { + IAgentContext, + IDIDManager, + IIdentifier, + IKey, + IKeyManager, + IService, + TAgent, +} from '../../packages/core/src' import { AbstractIdentifierProvider } from '../../packages/did-manager/src' import { _NormalizedVerificationMethod } from '../../packages/utils/src' import { diff --git a/__tests__/utils/ganache-provider.ts b/__tests__/utils/ganache-provider.ts index ccd156c08..0356d3cdf 100644 --- a/__tests__/utils/ganache-provider.ts +++ b/__tests__/utils/ganache-provider.ts @@ -6,7 +6,7 @@ import ganache from 'ganache-cli' /** * Creates a Web3Provider that connects to a local ganache instance with a bunch of known keys and an ERC1056 contract. - * + * * This provider can only be used in a single test suite, because of some concurrency issues with ganache. */ export async function createGanacheProvider(): Promise<{ provider: JsonRpcProvider; registry: string }> { diff --git a/packages/cli/src/credential.ts b/packages/cli/src/credential.ts index 3a23aafb4..23bc61500 100644 --- a/packages/cli/src/credential.ts +++ b/packages/cli/src/credential.ts @@ -25,10 +25,7 @@ credential { type: 'list', name: 'proofFormat', - choices: [ - 'jwt', - 'lds' - ], + choices: ['jwt', 'lds'], message: 'Credential proofFormat', }, { @@ -82,10 +79,7 @@ credential const credential: CredentialPayload = { issuer: { id: answers.iss }, - '@context': [ - 'https://www.w3.org/2018/credentials/v1', - 'https://veramo.io/contexts/profile/v1' - ], + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1'], type: answers.type.split(','), issuanceDate: new Date().toISOString(), credentialSubject, @@ -120,8 +114,8 @@ credential }) if (cmd.send) { - let body; - let type; + let body + let type if (answers.proofFormat == 'jwt') { body = verifiableCredential.proof.jwt type = 'jwt' @@ -174,8 +168,8 @@ credential credentialAsJSON = { proof: { type: 'JwtProof2020', - jwt: raw - } + jwt: raw, + }, } as any } const result = await agent.verifyCredential({ credential: credentialAsJSON }) diff --git a/packages/cli/src/explore/credentials.ts b/packages/cli/src/explore/credentials.ts index 4ad646afe..43ae2eb8a 100644 --- a/packages/cli/src/explore/credentials.ts +++ b/packages/cli/src/explore/credentials.ts @@ -3,7 +3,7 @@ import { UniqueVerifiableCredential } from '@veramo/data-store' import { shortDate, shortDid } from './utils' import { ConfiguredAgent } from '../setup' import { styles } from './styles' -import { asArray, extractIssuer } from "@veramo/utils"; +import { asArray, extractIssuer } from '@veramo/utils' export const getCredentialsTable = async (agent: ConfiguredAgent, screen: Widgets.Screen) => { screen.title = 'Credentials' diff --git a/packages/cli/src/presentation.ts b/packages/cli/src/presentation.ts index 67da08c8d..3c8431169 100644 --- a/packages/cli/src/presentation.ts +++ b/packages/cli/src/presentation.ts @@ -2,10 +2,10 @@ import { getAgent } from './setup' import { program } from 'commander' import inquirer from 'inquirer' import qrcode from 'qrcode-terminal' -import { readStdin } from "./util"; +import { readStdin } from './util' import * as fs from 'fs' import * as json5 from 'json5' -import { extractIssuer } from '@veramo/utils'; +import { extractIssuer } from '@veramo/utils' const presentation = program.command('presentation').description('W3C Verifiable Presentation') @@ -157,14 +157,16 @@ presentation } }) - presentation .command('verify') .description('Verify a W3C Verifiable Presentation provided as a string param or a file or from stdin') .option('-c, --challenge ', 'Optional. Specify a challenge that the presentation should match.') .option('-d, --domain ', 'Optional. Specify a domain that the presentation should match.') .option('-f, --filename ', 'Optional. Read the presentation from a file instead of stdin') - .option('-r, --raw ', 'Optional. Specify the presentation as a parameter string instead of a file or stdin.') + .option( + '-r, --raw ', + 'Optional. Presentation as a parameter string instead of a file or stdin.', + ) .action(async (options) => { const agent = getAgent(program.opts().config) let raw: string = '' @@ -182,11 +184,15 @@ presentation presentationAsJSON = { proof: { type: 'JwtProof2020', - jwt: raw - } + jwt: raw, + }, } as any } - const result = await agent.verifyPresentation({ presentation: presentationAsJSON, challenge: options.challenge, domain: options.domain }) + const result = await agent.verifyPresentation({ + presentation: presentationAsJSON, + challenge: options.challenge, + domain: options.domain, + }) if (result === true) { console.log('Presentation was verified successfully.') } else { diff --git a/packages/cli/src/util.ts b/packages/cli/src/util.ts index 452cb26fe..73822a717 100644 --- a/packages/cli/src/util.ts +++ b/packages/cli/src/util.ts @@ -1,24 +1,23 @@ export async function readStdin(): Promise { return new Promise((resolve, reject) => { - let data = ''; - process.stdin.setEncoding('utf-8'); + let data = '' + process.stdin.setEncoding('utf-8') - process.stdin.on('readable', () => { - let chunk: string - while (chunk = process.stdin.read()) { - data += chunk; - } - }); + process.stdin.on('readable', () => { + let chunk: string + while ((chunk = process.stdin.read())) { + data += chunk + } + }) - process.stdin.on('end', () => { - // There will be a trailing \n from the user hitting enter. Get rid of it. - data = data.replace(/\n$/, ''); - resolve(data) - }) + process.stdin.on('end', () => { + // There will be a trailing \n from the user hitting enter. Get rid of it. + data = data.replace(/\n$/, '') + resolve(data) + }) - process.stdin.on('error', (error) => { - reject(error) - }) - } - ) -} \ No newline at end of file + process.stdin.on('error', (error) => { + reject(error) + }) + }) +} diff --git a/packages/core/src/types/IAgent.ts b/packages/core/src/types/IAgent.ts index 3b89f56b7..4c967bff5 100644 --- a/packages/core/src/types/IAgent.ts +++ b/packages/core/src/types/IAgent.ts @@ -84,8 +84,7 @@ export interface RemoveContext { */ export type TAgent = { [P in keyof T]: RemoveContext -} & - IAgent +} & IAgent /** * Standard plugin method context interface diff --git a/packages/core/src/types/IDIDManager.ts b/packages/core/src/types/IDIDManager.ts index 8130f22a2..8905417bf 100644 --- a/packages/core/src/types/IDIDManager.ts +++ b/packages/core/src/types/IDIDManager.ts @@ -303,7 +303,10 @@ export interface IDIDManager extends IPluginMethodMap { /** * Imports identifier */ - didManagerImport(args: MinimalImportableIdentifier, context: IAgentContext): Promise + didManagerImport( + args: MinimalImportableIdentifier, + context: IAgentContext, + ): Promise /** * Deletes identifier diff --git a/packages/core/src/types/IDataStore.ts b/packages/core/src/types/IDataStore.ts index 9ef673848..e1fe98d58 100644 --- a/packages/core/src/types/IDataStore.ts +++ b/packages/core/src/types/IDataStore.ts @@ -1,6 +1,6 @@ import { IPluginMethodMap } from './IAgent' import { IMessage } from './IMessage' -import { VerifiableCredential, VerifiablePresentation } from "./vc-data-model"; +import { VerifiableCredential, VerifiablePresentation } from './vc-data-model' /** * Input arguments for {@link IDataStore.dataStoreSaveMessage | dataStoreSaveMessage} @@ -105,7 +105,6 @@ export interface IDataStore extends IPluginMethodMap { */ dataStoreSaveVerifiableCredential(args: IDataStoreSaveVerifiableCredentialArgs): Promise - /** * Deletes verifiable credential from the data store * @param args - verifiable credential diff --git a/packages/core/src/types/IMessage.ts b/packages/core/src/types/IMessage.ts index f06b8bec4..c01bacb4d 100644 --- a/packages/core/src/types/IMessage.ts +++ b/packages/core/src/types/IMessage.ts @@ -1,4 +1,4 @@ -import { VerifiableCredential, VerifiablePresentation } from "./vc-data-model"; +import { VerifiableCredential, VerifiablePresentation } from './vc-data-model' /** * Message meta data diff --git a/packages/core/src/types/vc-data-model.ts b/packages/core/src/types/vc-data-model.ts index 78cdd2cdd..85c4661f7 100644 --- a/packages/core/src/types/vc-data-model.ts +++ b/packages/core/src/types/vc-data-model.ts @@ -18,7 +18,7 @@ export type CompactJWT = string * * @beta */ -export type IssuerType = { id: string, [x: string]: any } | string +export type IssuerType = { id: string; [x: string]: any } | string /** * The value of the credentialSubject property is defined as a set of objects that contain one or more properties that @@ -115,7 +115,6 @@ export interface UnsignedPresentation { [x: string]: any } - /** * Represents a signed Verifiable Presentation (includes proof), using a JSON representation. * See {@link https://www.w3.org/TR/vc-data-model/#presentations | VP data model} @@ -169,8 +168,8 @@ export interface PresentationPayload { '@context'?: string[] verifier?: string[] issuanceDate?: DateType - expirationDate?: DateType, + expirationDate?: DateType id?: string [x: string]: any -} \ No newline at end of file +} diff --git a/packages/credential-ld/src/action-handler.ts b/packages/credential-ld/src/action-handler.ts index 757a5e123..30e440d6b 100644 --- a/packages/credential-ld/src/action-handler.ts +++ b/packages/credential-ld/src/action-handler.ts @@ -11,7 +11,7 @@ import { } from '@veramo/core' import { schema, VeramoLdSignature } from './' import Debug from 'debug' -import { LdContextLoader } from "./ld-context-loader"; +import { LdContextLoader } from './ld-context-loader' import { _ExtendedIKey, extractIssuer, @@ -20,11 +20,11 @@ import { mapIdentifierKeysToDoc, OrPromise, processEntryToArray, - RecordLike -} from "@veramo/utils"; + RecordLike, +} from '@veramo/utils' -import { LdCredentialModule } from "./ld-credential-module"; -import { LdSuiteLoader } from './ld-suite-loader'; +import { LdCredentialModule } from './ld-credential-module' +import { LdSuiteLoader } from './ld-suite-loader' import { ContextDoc, ICreateVerifiableCredentialLDArgs, @@ -33,7 +33,7 @@ import { IRequiredContext, IVerifyCredentialLDArgs, IVerifyPresentationLDArgs, -} from "./types"; +} from './types' const debug = Debug('veramo:w3c:action-handler') @@ -48,13 +48,10 @@ export class CredentialIssuerLD implements IAgentPlugin { private ldCredentialModule: LdCredentialModule - constructor(options: { - contextMaps: RecordLike>[], - suites: VeramoLdSignature[] - }) { + constructor(options: { contextMaps: RecordLike>[]; suites: VeramoLdSignature[] }) { this.ldCredentialModule = new LdCredentialModule({ ldContextLoader: new LdContextLoader({ contextsPaths: options.contextMaps }), - ldSuiteLoader: new LdSuiteLoader({ veramoLdSignatures: options.suites }) + ldSuiteLoader: new LdSuiteLoader({ veramoLdSignatures: options.suites }), }) this.methods = { @@ -70,7 +67,10 @@ export class CredentialIssuerLD implements IAgentPlugin { args: ICreateVerifiablePresentationLDArgs, context: IRequiredContext, ): Promise { - const presentationContext = processEntryToArray(args?.presentation?.['@context'], MANDATORY_CREDENTIAL_CONTEXT) + const presentationContext = processEntryToArray( + args?.presentation?.['@context'], + MANDATORY_CREDENTIAL_CONTEXT, + ) const presentationType = processEntryToArray(args?.presentation?.type, 'VerifiablePresentation') const presentation: PresentationPayload = { @@ -84,7 +84,7 @@ export class CredentialIssuerLD implements IAgentPlugin { } if (args.presentation.verifiableCredential) { - const credentials = args.presentation.verifiableCredential.map(cred => { + const credentials = args.presentation.verifiableCredential.map((cred) => { if (typeof cred !== 'string' && cred.proof.jwt) { return cred.proof.jwt } else { @@ -104,7 +104,11 @@ export class CredentialIssuerLD implements IAgentPlugin { throw new Error('invalid_argument: args.presentation.holder must be a DID managed by this agent') } try { - const { signingKey, verificationMethodId } = await this.findSigningKeyWithId(context, identifier, args.keyRef) + const { signingKey, verificationMethodId } = await this.findSigningKeyWithId( + context, + identifier, + args.keyRef, + ) return await this.ldCredentialModule.signLDVerifiablePresentation( presentation, @@ -126,7 +130,10 @@ export class CredentialIssuerLD implements IAgentPlugin { args: ICreateVerifiableCredentialLDArgs, context: IRequiredContext, ): Promise { - const credentialContext = processEntryToArray(args?.credential?.['@context'], MANDATORY_CREDENTIAL_CONTEXT); + const credentialContext = processEntryToArray( + args?.credential?.['@context'], + MANDATORY_CREDENTIAL_CONTEXT, + ) const credentialType = processEntryToArray(args?.credential?.type, 'VerifiableCredential') let issuanceDate = args?.credential?.issuanceDate || new Date().toISOString() if (issuanceDate instanceof Date) { @@ -151,7 +158,11 @@ export class CredentialIssuerLD implements IAgentPlugin { throw new Error(`invalid_argument: args.credential.issuer must be a DID managed by this agent. ${e}`) } try { - const { signingKey, verificationMethodId } = await this.findSigningKeyWithId(context, identifier, args.keyRef) + const { signingKey, verificationMethodId } = await this.findSigningKeyWithId( + context, + identifier, + args.keyRef, + ) return await this.ldCredentialModule.issueLDVerifiableCredential( credential, @@ -181,15 +192,14 @@ export class CredentialIssuerLD implements IAgentPlugin { context: IRequiredContext, ): Promise { const presentation = args.presentation - return this.ldCredentialModule.verifyPresentation( - presentation, - args.challenge, - args.domain, - context, - ) + return this.ldCredentialModule.verifyPresentation(presentation, args.challenge, args.domain, context) } - private async findSigningKeyWithId(context: IAgentContext, identifier: IIdentifier, keyRef?: string): Promise<{ signingKey: IKey; verificationMethodId: string }> { + private async findSigningKeyWithId( + context: IAgentContext, + identifier: IIdentifier, + keyRef?: string, + ): Promise<{ signingKey: IKey; verificationMethodId: string }> { const extendedKeys: _ExtendedIKey[] = await mapIdentifierKeysToDoc(identifier, 'assertionMethod', context) let supportedTypes = this.ldCredentialModule.ldSuiteLoader.getAllSignatureSuiteTypes() let signingKey: _ExtendedIKey | undefined @@ -198,12 +208,16 @@ export class CredentialIssuerLD implements IAgentPlugin { signingKey = extendedKeys.find((k) => k.kid === keyRef) } if (signingKey && !supportedTypes.includes(signingKey.meta.verificationMethod.type)) { - debug('WARNING: requested signing key DOES NOT correspond to a supported Signature suite type. Looking for the next best key.') + debug( + 'WARNING: requested signing key DOES NOT correspond to a supported Signature suite type. Looking for the next best key.', + ) signingKey = undefined } if (!signingKey) { if (keyRef) { - debug('WARNING: no signing key was found that matches the reference provided. Searching for the first available signing key.') + debug( + 'WARNING: no signing key was found that matches the reference provided. Searching for the first available signing key.', + ) } signingKey = extendedKeys.find((k) => supportedTypes.includes(k.meta.verificationMethod.type)) } @@ -213,4 +227,3 @@ export class CredentialIssuerLD implements IAgentPlugin { return { signingKey, verificationMethodId } } } - diff --git a/packages/credential-ld/src/index.ts b/packages/credential-ld/src/index.ts index d106fc10a..ddb2e29b8 100644 --- a/packages/credential-ld/src/index.ts +++ b/packages/credential-ld/src/index.ts @@ -4,14 +4,10 @@ * * @packageDocumentation */ -export { - CredentialIssuerLD, -} from './action-handler' +export { CredentialIssuerLD } from './action-handler' export * from './types' export { LdDefaultContexts } from './ld-default-contexts' -export { - VeramoLdSignature, -} from './ld-suites' +export { VeramoLdSignature } from './ld-suites' export * from './suites/EcdsaSecp256k1RecoverySignature2020' export * from './suites/Ed25519Signature2018' const schema = require('../plugin.schema.json') diff --git a/packages/credential-ld/src/ld-context-loader.ts b/packages/credential-ld/src/ld-context-loader.ts index 38047a9c8..09090fa2b 100644 --- a/packages/credential-ld/src/ld-context-loader.ts +++ b/packages/credential-ld/src/ld-context-loader.ts @@ -3,16 +3,14 @@ * that it unifies into a single Map to provide to the documentLoader within * the w3c credential module. */ -import { OrPromise, RecordLike } from "@veramo/utils"; -import { ContextDoc } from "./types"; +import { OrPromise, RecordLike } from '@veramo/utils' +import { ContextDoc } from './types' export class LdContextLoader { private contexts: Record> - constructor(options: { - contextsPaths: RecordLike>[] - }) { - this.contexts = {}; + constructor(options: { contextsPaths: RecordLike>[] }) { + this.contexts = {} // generate-plugin-schema is failing unless we use the cast to `any[]` Array.from(options.contextsPaths as any[], (mapItem) => { for (const [key, value] of mapItem) { @@ -28,4 +26,4 @@ export class LdContextLoader { async get(url: string): Promise { return this.contexts[url] } -} \ No newline at end of file +} diff --git a/packages/credential-ld/src/ld-credential-module.ts b/packages/credential-ld/src/ld-credential-module.ts index 3cefeb210..adede4549 100644 --- a/packages/credential-ld/src/ld-credential-module.ts +++ b/packages/credential-ld/src/ld-credential-module.ts @@ -48,9 +48,7 @@ export class LdCredentialModule { // currently Veramo LD suites can modify the resolution response for DIDs from // the document Loader. This allows to fix incompatibilities between DID Documents // and LD suites to be fixed specifically within the Veramo LD Suites definition - this.ldSuiteLoader - .getAllSignatureSuites() - .forEach((x) => x.preDidResolutionModification(url, didDoc)) + this.ldSuiteLoader.getAllSignatureSuites().forEach((x) => x.preDidResolutionModification(url, didDoc)) // console.log(`Returning from Documentloader: ${JSON.stringify(returnDocument)}`) return { diff --git a/packages/credential-ld/src/ld-default-contexts.ts b/packages/credential-ld/src/ld-default-contexts.ts index 731b3661f..972669e3d 100644 --- a/packages/credential-ld/src/ld-default-contexts.ts +++ b/packages/credential-ld/src/ld-default-contexts.ts @@ -1,11 +1,8 @@ -import * as fs from 'fs'; -import * as path from 'path'; +import * as fs from 'fs' +import * as path from 'path' function _read(_path: string) { - return JSON.parse( - fs.readFileSync( - path.join(__dirname, '../contexts', _path), - { encoding: 'utf8' })); + return JSON.parse(fs.readFileSync(path.join(__dirname, '../contexts', _path), { encoding: 'utf8' })) } export const LdDefaultContexts = new Map([ @@ -15,7 +12,10 @@ export const LdDefaultContexts = new Map([ ['https://www.w3.org/ns/did/v1', _read('security_context_v1.jsonld')], ['https://w3id.org/did/v0.11', _read('did_v0.11.jsonld')], ['https://ns.did.ai/transmute/v1', _read('transmute_v1.jsonld')], - ['https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', _read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld')], + [ + 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', + _read('lds-ecdsa-secp256k1-recovery2020-0.0.jsonld'), + ], ['https://w3id.org/security/suites/ed25519-2018/v1', _read('ed25519-signature-2018-v1.jsonld')], - ['https://w3id.org/security/suites/x25519-2019/v1', _read('X25519KeyAgreementKey2019.jsonld')] -]); + ['https://w3id.org/security/suites/x25519-2019/v1', _read('X25519KeyAgreementKey2019.jsonld')], +]) diff --git a/packages/credential-ld/src/ld-suite-loader.ts b/packages/credential-ld/src/ld-suite-loader.ts index ff8bf3e54..09fff8e3d 100644 --- a/packages/credential-ld/src/ld-suite-loader.ts +++ b/packages/credential-ld/src/ld-suite-loader.ts @@ -5,15 +5,12 @@ import { TKeyType } from '@veramo/core' * Initializes a list of Veramo-wrapped LD Signature suites and exposes those to the Agent Module */ export class LdSuiteLoader { - - constructor(options: { - veramoLdSignatures: VeramoLdSignature[] - }) { + constructor(options: { veramoLdSignatures: VeramoLdSignature[] }) { options.veramoLdSignatures.forEach((obj) => { this.signatureMap[obj.getSupportedVeramoKeyType()] = obj }) } - private signatureMap: Record = {}; + private signatureMap: Record = {} getSignatureSuiteForKeyType(type: TKeyType) { const suite = this.signatureMap[type] @@ -27,7 +24,6 @@ export class LdSuiteLoader { } getAllSignatureSuiteTypes() { - return Object.values(this.signatureMap).map(x => x.getSupportedVerificationType()) + return Object.values(this.signatureMap).map((x) => x.getSupportedVerificationType()) } - -} \ No newline at end of file +} diff --git a/packages/credential-ld/src/ld-suites.ts b/packages/credential-ld/src/ld-suites.ts index 72a495265..0236b4bf1 100644 --- a/packages/credential-ld/src/ld-suites.ts +++ b/packages/credential-ld/src/ld-suites.ts @@ -1,4 +1,12 @@ -import { CredentialPayload, IAgentContext, IKey, IKeyManager, IResolver, PresentationPayload, TKeyType } from '@veramo/core' +import { + CredentialPayload, + IAgentContext, + IKey, + IKeyManager, + IResolver, + PresentationPayload, + TKeyType, +} from '@veramo/core' import { DIDDocument } from 'did-resolver/src/resolver' export type RequiredAgentMethods = IResolver & Pick @@ -35,5 +43,3 @@ export abstract class VeramoLdSignature { } } } - - diff --git a/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts index 42d370a26..dc5f1d67a 100644 --- a/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts +++ b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts @@ -1,5 +1,5 @@ -import { RequiredAgentMethods, VeramoLdSignature } from "../ld-suites"; -import { CredentialPayload, DIDDocument, IAgentContext, IKey, TKeyType } from "@veramo/core"; +import { RequiredAgentMethods, VeramoLdSignature } from '../ld-suites' +import { CredentialPayload, DIDDocument, IAgentContext, IKey, TKeyType } from '@veramo/core' import { EcdsaSecp256k1RecoveryMethod2020, EcdsaSecp256k1RecoverySignature2020, @@ -17,7 +17,12 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature return 'Secp256k1' } - getSuiteForSigning(key: IKey, did: string, verifiableMethodId: string, context: IAgentContext): any { + getSuiteForSigning( + key: IKey, + did: string, + verifiableMethodId: string, + context: IAgentContext, + ): any { const controller = did const signer = { //returns a JWS detached @@ -47,7 +52,7 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature signer: () => signer, type: this.getSupportedVerificationType(), controller, - id: verifiableMethodId + id: verifiableMethodId, }), }) } @@ -59,7 +64,7 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature preSigningCredModification(credential: CredentialPayload): void { credential['@context'] = [ ...asArray(credential['@context'] || []), - 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld' + 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', ] } @@ -75,4 +80,4 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature }) } } -} \ No newline at end of file +} diff --git a/packages/credential-ld/src/suites/Ed25519Signature2018.ts b/packages/credential-ld/src/suites/Ed25519Signature2018.ts index 6718f282e..c1beb6595 100644 --- a/packages/credential-ld/src/suites/Ed25519Signature2018.ts +++ b/packages/credential-ld/src/suites/Ed25519Signature2018.ts @@ -1,11 +1,10 @@ -import { encodeJoseBlob } from "@veramo/utils"; -import { RequiredAgentMethods, VeramoLdSignature } from "../ld-suites"; -import { CredentialPayload, DIDDocument, IAgentContext, IKey, TKeyType } from "@veramo/core"; +import { encodeJoseBlob } from '@veramo/utils' +import { RequiredAgentMethods, VeramoLdSignature } from '../ld-suites' +import { CredentialPayload, DIDDocument, IAgentContext, IKey, TKeyType } from '@veramo/core' import * as u8a from 'uint8arrays' import { Ed25519Signature2018, Ed25519VerificationKey2018 } from '@transmute/ed25519-signature-2018' export class VeramoEd25519Signature2018 extends VeramoLdSignature { - getSupportedVerificationType(): string { return 'Ed25519VerificationKey2018' } @@ -14,7 +13,12 @@ export class VeramoEd25519Signature2018 extends VeramoLdSignature { return 'Ed25519' } - getSuiteForSigning(key: IKey, issuerDid: string, verificationMethodId: string, context: IAgentContext): any { + getSuiteForSigning( + key: IKey, + issuerDid: string, + verificationMethodId: string, + context: IAgentContext, + ): any { const controller = issuerDid // DID Key ID @@ -53,12 +57,12 @@ export class VeramoEd25519Signature2018 extends VeramoLdSignature { return new Ed25519Signature2018({ key: verificationKey, - signer: signer - }); + signer: signer, + }) } getSuiteForVerification(): any { - return new Ed25519Signature2018(); + return new Ed25519Signature2018() } preSigningCredModification(credential: CredentialPayload): void { @@ -68,5 +72,4 @@ export class VeramoEd25519Signature2018 extends VeramoLdSignature { preDidResolutionModification(didUrl: string, didDoc: DIDDocument): void { // nothing to do here } - -} \ No newline at end of file +} diff --git a/packages/credential-ld/src/types.ts b/packages/credential-ld/src/types.ts index 4818b3f6b..ceb4a4f01 100644 --- a/packages/credential-ld/src/types.ts +++ b/packages/credential-ld/src/types.ts @@ -199,10 +199,10 @@ export interface IVerifyPresentationLDArgs { * * @beta This API is likely to change without a BREAKING CHANGE notice */ -export type IRequiredContext = IAgentContext & - Pick> +export type IRequiredContext = IAgentContext< + IResolver & Pick & Pick +> export type ContextDoc = { - "@context": Record -} \ No newline at end of file + '@context': Record +} diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index 6ad5d3d47..ddec04dda 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -93,37 +93,37 @@ describe('@veramo/credential-w3c', () => { agent.didManagerGet = jest.fn().mockImplementation(async (args): Promise => mockIdentifier) const context: IContext = { agent: agent } - const credential: CredentialPayload = { - '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2020/demo/4342323'], - type: ['VerifiableCredential', 'PublicProfile'], - issuer: { id: mockIdentifier.did }, - issuanceDate: new Date().toISOString(), - id: 'vc1', - credentialSubject: { - id: 'https://example.com/user/alice', - name: 'Alice', - profilePicture: 'https://example.com/a.png', - address: { - street: 'Some str.', - house: 1, - }, + const credential: CredentialPayload = { + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2020/demo/4342323'], + type: ['VerifiableCredential', 'PublicProfile'], + issuer: { id: mockIdentifier.did }, + issuanceDate: new Date().toISOString(), + id: 'vc1', + credentialSubject: { + id: 'https://example.com/user/alice', + name: 'Alice', + profilePicture: 'https://example.com/a.png', + address: { + street: 'Some str.', + house: 1, }, - } + }, + } - const vc = await w3c.createVerifiableCredential( - { - credential, - save: true, - proofFormat: 'jwt', - }, - context, - ) - // TODO Update these after refactoring did-jwt-vc - expect(context.agent.didManagerGet).toBeCalledWith({ did: mockIdentifier.did }) - expect(context.agent.dataStoreSaveVerifiableCredential).toBeCalledWith({ - verifiableCredential: 'mockCredential', - }) - expect(vc).toEqual('mockCredential') + const vc = await w3c.createVerifiableCredential( + { + credential, + save: true, + proofFormat: 'jwt', + }, + context, + ) + // TODO Update these after refactoring did-jwt-vc + expect(context.agent.didManagerGet).toBeCalledWith({ did: mockIdentifier.did }) + expect(context.agent.dataStoreSaveVerifiableCredential).toBeCalledWith({ + verifiableCredential: 'mockCredential', + }) + expect(vc).toEqual('mockCredential') }) test.each(mockIdentifiers)('handles createVerifiablePresentation', async (mockIdentifier) => { @@ -132,7 +132,6 @@ describe('@veramo/credential-w3c', () => { agent.didManagerGet = jest.fn().mockImplementation(async (args): Promise => mockIdentifier) const context: IContext = { agent: agent } - const credential: VerifiableCredential = { '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiableCredential', 'PublicProfile'], @@ -140,7 +139,7 @@ describe('@veramo/credential-w3c', () => { issuanceDate: new Date().toISOString(), id: 'vc1', credentialSubject: { - id: "https://example.com/user/alice", + id: 'https://example.com/user/alice', name: 'Alice', profilePicture: 'https://example.com/a.png', address: { diff --git a/packages/credential-w3c/src/action-handler.ts b/packages/credential-w3c/src/action-handler.ts index 5d8695dd8..7ee3fb6f8 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -30,7 +30,13 @@ import { decodeJWT } from 'did-jwt' import { schema } from './' import Debug from 'debug' import { Resolvable } from 'did-resolver' -import { asArray, extractIssuer, isDefined, MANDATORY_CREDENTIAL_CONTEXT, processEntryToArray } from "@veramo/utils"; +import { + asArray, + extractIssuer, + isDefined, + MANDATORY_CREDENTIAL_CONTEXT, + processEntryToArray, +} from '@veramo/utils' const debug = Debug('veramo:w3c:action-handler') @@ -245,10 +251,12 @@ export interface ICredentialIssuer extends IPluginMethodMap { * * This interface can be used for static type checks, to make sure your application is properly initialized. */ -export type IContext = IAgentContext & - Pick & - Pick> +export type IContext = IAgentContext< + IResolver & + Pick & + Pick & + Pick +> /** * A Veramo plugin that implements the {@link ICredentialIssuer} methods. @@ -273,7 +281,10 @@ export class CredentialIssuer implements IAgentPlugin { args: ICreateVerifiablePresentationArgs, context: IContext, ): Promise { - const presentationContext = processEntryToArray(args?.presentation?.['@context'], MANDATORY_CREDENTIAL_CONTEXT) + const presentationContext = processEntryToArray( + args?.presentation?.['@context'], + MANDATORY_CREDENTIAL_CONTEXT, + ) const presentationType = processEntryToArray(args?.presentation?.type, 'VerifiablePresentation') const presentation: PresentationPayload = { ...args?.presentation, @@ -287,7 +298,7 @@ export class CredentialIssuer implements IAgentPlugin { } if (args.presentation.verifiableCredential) { - const credentials = args.presentation.verifiableCredential.map(cred => { + const credentials = args.presentation.verifiableCredential.map((cred) => { // map JWT credentials to their canonical form if (typeof cred !== 'string' && cred.proof.jwt) { return cred.proof.jwt @@ -315,7 +326,9 @@ export class CredentialIssuer implements IAgentPlugin { if (typeof context.agent.createVerifiablePresentationLD === 'function') { verifiablePresentation = await context.agent.createVerifiablePresentationLD(args) } else { - throw new Error('invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed') + throw new Error( + 'invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed', + ) } } else { // only add issuanceDate for JWT @@ -351,7 +364,10 @@ export class CredentialIssuer implements IAgentPlugin { args: ICreateVerifiableCredentialArgs, context: IContext, ): Promise { - const credentialContext = processEntryToArray(args?.credential?.['@context'], MANDATORY_CREDENTIAL_CONTEXT) + const credentialContext = processEntryToArray( + args?.credential?.['@context'], + MANDATORY_CREDENTIAL_CONTEXT, + ) const credentialType = processEntryToArray(args?.credential?.type, 'VerifiableCredential') const credential: CredentialPayload = { ...args?.credential, @@ -360,7 +376,6 @@ export class CredentialIssuer implements IAgentPlugin { issuanceDate: args?.credential?.issuanceDate || new Date().toISOString(), } - //FIXME: if the identifier is not found, the error message should reflect that. const issuer = extractIssuer(credential) if (!issuer || typeof issuer === 'undefined') { @@ -383,7 +398,9 @@ export class CredentialIssuer implements IAgentPlugin { if (typeof context.agent.createVerifiableCredentialLD === 'function') { verifiableCredential = await context.agent.createVerifiableCredentialLD(args as any) } else { - throw new Error('invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed') + throw new Error( + 'invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed', + ) } } else { debug('Signing VC with', identifier.did) @@ -413,10 +430,7 @@ export class CredentialIssuer implements IAgentPlugin { } /** {@inheritdoc ICredentialIssuer.verifyCredential} */ - async verifyCredential( - args: IVerifyCredentialArgs, - context: IContext, - ): Promise { + async verifyCredential(args: IVerifyCredentialArgs, context: IContext): Promise { const credential = args.credential if (typeof credential === 'string' || (credential)?.proof?.jwt) { // JWT @@ -440,16 +454,15 @@ export class CredentialIssuer implements IAgentPlugin { const result = await context.agent.verifyCredentialLD(args) return result } else { - throw new Error('invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed') + throw new Error( + 'invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed', + ) } } } /** {@inheritdoc ICredentialIssuer.verifyPresentation} */ - async verifyPresentation( - args: IVerifyPresentationArgs, - context: IContext, - ): Promise { + async verifyPresentation(args: IVerifyPresentationArgs, context: IContext): Promise { const presentation = args.presentation if (typeof presentation === 'string' || (presentation)?.proof?.jwt) { // JWT @@ -468,7 +481,7 @@ export class CredentialIssuer implements IAgentPlugin { // automatically add a managed DID as audience if one is found const intendedAudience = asArray(payload.aud) const managedDids = await context.agent.didManagerFind() - const filtered = managedDids.filter(identifier => intendedAudience.includes(identifier.did)) + const filtered = managedDids.filter((identifier) => intendedAudience.includes(identifier.did)) if (filtered.length > 0) { audience = filtered[0].did } @@ -479,7 +492,7 @@ export class CredentialIssuer implements IAgentPlugin { const verification = await verifyPresentationJWT(jwt, resolver, { challenge: args.challenge, domain: args.domain, - audience + audience, }) return true } catch (e: any) { @@ -492,7 +505,9 @@ export class CredentialIssuer implements IAgentPlugin { const result = await context.agent.verifyPresentationLD(args) return result } else { - throw new Error('invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed') + throw new Error( + 'invalid_configuration: your agent does not seem to have ICredentialIssuerLD plugin installed', + ) } } } diff --git a/packages/credential-w3c/src/message-handler.ts b/packages/credential-w3c/src/message-handler.ts index 2fd21be4c..c55335918 100644 --- a/packages/credential-w3c/src/message-handler.ts +++ b/packages/credential-w3c/src/message-handler.ts @@ -1,11 +1,11 @@ import { IAgentContext, IResolver, VerifiableCredential, VerifiablePresentation } from '@veramo/core' import { AbstractMessageHandler, Message } from '@veramo/message-handler' -import { computeEntryHash, decodeCredentialToObject, extractIssuer } from '@veramo/utils' +import { asArray, computeEntryHash, decodeCredentialToObject, extractIssuer } from '@veramo/utils' import { normalizeCredential, normalizePresentation, validateJwtCredentialPayload, - validateJwtPresentationPayload + validateJwtPresentationPayload, } from 'did-jwt-vc' import { ICredentialIssuer } from './action-handler' import { v4 as uuidv4 } from 'uuid' @@ -46,7 +46,6 @@ export type IContext = IAgentContext * @public */ export class W3cMessageHandler extends AbstractMessageHandler { - async handle(message: Message, context: IContext): Promise { const meta = message.getLastMetaData() @@ -78,8 +77,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { message.credentials = credentials return message - } catch (e) { - } + } catch (e) {} try { validateJwtCredentialPayload(data) @@ -99,8 +97,7 @@ export class W3cMessageHandler extends AbstractMessageHandler { message.createdAt = credential.issuanceDate message.credentials = [credential] return message - } catch (e) { - } + } catch (e) {} } // LDS Verification and Handling @@ -133,11 +130,9 @@ export class W3cMessageHandler extends AbstractMessageHandler { presentation, // FIXME: HARDCODED CHALLENGE VERIFICATION FOR NOW challenge: 'VERAMO', - domain: 'VERAMO' + domain: 'VERAMO', }) - const credentials = presentation.verifiableCredential - message.id = computeEntryHash(message.raw || message.id || uuidv4()) message.type = MessageTypes.vp message.from = presentation.holder @@ -149,11 +144,10 @@ export class W3cMessageHandler extends AbstractMessageHandler { // message.createdAt = presentation.issuanceDate message.presentations = [presentation] - message.credentials = (credentials || []).map(decodeCredentialToObject) + message.credentials = asArray(presentation.verifiableCredential).map(decodeCredentialToObject) return message } - return super.handle(message, context) } } diff --git a/packages/data-store/src/__tests__/data-store-orm.test.ts b/packages/data-store/src/__tests__/data-store-orm.test.ts index 91a6dbf3e..20cb9db00 100644 --- a/packages/data-store/src/__tests__/data-store-orm.test.ts +++ b/packages/data-store/src/__tests__/data-store-orm.test.ts @@ -1,4 +1,11 @@ -import { Agent, IDataStore, IMessage, TAgent, VerifiableCredential, VerifiablePresentation, } from '@veramo/core' +import { + Agent, + IDataStore, + IMessage, + TAgent, + VerifiableCredential, + VerifiablePresentation, +} from '@veramo/core' import { Connection, createConnection } from 'typeorm' import { DataStoreORM, IDataStoreORM } from '../data-store-orm' import { FindArgs, TCredentialColumns, TMessageColumns, TPresentationColumns } from '../types' diff --git a/packages/data-store/src/__tests__/entities.test.ts b/packages/data-store/src/__tests__/entities.test.ts index 5d957e027..d798c91f7 100644 --- a/packages/data-store/src/__tests__/entities.test.ts +++ b/packages/data-store/src/__tests__/entities.test.ts @@ -4,7 +4,7 @@ import { createConnection, Connection, In } from 'typeorm' import { Identifier, Message, Claim } from '../index' import { Entities } from '../index' import { blake2bHex } from 'blakejs' -import fs from 'fs' +import * as fs from 'fs' describe('DB entities test', () => { let connection: Connection diff --git a/packages/data-store/src/entities/claim.ts b/packages/data-store/src/entities/claim.ts index a24e5bab3..46eb0d989 100644 --- a/packages/data-store/src/entities/claim.ts +++ b/packages/data-store/src/entities/claim.ts @@ -10,7 +10,7 @@ export class Claim extends BaseEntity { @ManyToOne((type) => Identifier, (identifier) => identifier.issuedClaims, { eager: true, - onDelete: "CASCADE" + onDelete: 'CASCADE', }) //@ts-ignore issuer: Identifier @@ -22,7 +22,7 @@ export class Claim extends BaseEntity { subject?: Identifier @ManyToOne((type) => Credential, (credential) => credential.claims, { - onDelete: "CASCADE" + onDelete: 'CASCADE', }) //@ts-ignore credential: Credential diff --git a/packages/data-store/src/entities/credential.ts b/packages/data-store/src/entities/credential.ts index acc43f441..12c7eb5e0 100644 --- a/packages/data-store/src/entities/credential.ts +++ b/packages/data-store/src/entities/credential.ts @@ -4,7 +4,7 @@ import { Identifier } from './identifier' import { Message } from './message' import { Presentation } from './presentation' import { Claim } from './claim' -import { asArray, computeEntryHash, extractIssuer } from "@veramo/utils"; +import { asArray, computeEntryHash, extractIssuer } from '@veramo/utils' @Entity('credential') export class Credential extends BaseEntity { diff --git a/packages/data-store/src/entities/message.ts b/packages/data-store/src/entities/message.ts index 847ca19ce..9ba8bb4fd 100644 --- a/packages/data-store/src/entities/message.ts +++ b/packages/data-store/src/entities/message.ts @@ -1,19 +1,20 @@ import { - Entity, - Column, BaseEntity, - ManyToOne, - ManyToMany, - PrimaryColumn, - JoinTable, BeforeInsert, BeforeUpdate, + Column, + Entity, + JoinTable, + ManyToMany, + ManyToOne, + PrimaryColumn, } from 'typeorm' -import { blake2bHex } from 'blakejs' import { IMessage } from '@veramo/core' import { Identifier } from './identifier' -import { Presentation, createPresentationEntity } from './presentation' -import { Credential, createCredentialEntity } from './credential' +import { createPresentationEntity, Presentation } from './presentation' +import { createCredentialEntity, Credential } from './credential' +import { computeEntryHash } from '@veramo/utils' +import { v4 as uuidv4 } from 'uuid' export interface MetaData { type: string @@ -25,7 +26,7 @@ export class Message extends BaseEntity { @BeforeInsert() setId() { if (!this.id) { - this.id = blake2bHex(this.raw) + this.id = computeEntryHash(this.raw || uuidv4()) } } diff --git a/packages/data-store/src/entities/presentation.ts b/packages/data-store/src/entities/presentation.ts index 815e5d9c2..090716ef3 100644 --- a/packages/data-store/src/entities/presentation.ts +++ b/packages/data-store/src/entities/presentation.ts @@ -10,7 +10,7 @@ import { normalizeCredential } from 'did-jwt-vc' @Entity('presentation') export class Presentation extends BaseEntity { @PrimaryColumn() - //@ts-ignore + //@ts-ignore hash: string //@ts-ignore @@ -31,7 +31,7 @@ export class Presentation extends BaseEntity { eager: true, onDelete: 'CASCADE', }) - //@ts-ignore + //@ts-ignore holder: Identifier @ManyToMany((type) => Identifier, (identifier) => identifier?.receivedPresentations, { @@ -40,36 +40,36 @@ export class Presentation extends BaseEntity { nullable: true, }) @JoinTable() - //@ts-ignore + //@ts-ignore verifier?: Identifier[] @Column({ nullable: true }) id?: String @Column() - //@ts-ignore + //@ts-ignore issuanceDate: Date @Column({ nullable: true }) expirationDate?: Date @Column('simple-array') - //@ts-ignore + //@ts-ignore context: string[] @Column('simple-array') - //@ts-ignore + //@ts-ignore type: string[] @ManyToMany((type) => Credential, (credential) => credential.presentations, { cascade: true, }) @JoinTable() - //@ts-ignore + //@ts-ignore credentials: Credential[] @ManyToMany((type) => Message, (message) => message.presentations) - //@ts-ignore + //@ts-ignore messages: Message[] } @@ -101,7 +101,7 @@ export const createPresentationEntity = (vpi: VerifiablePresentation): Presentat presentation.raw = vpi presentation.credentials = (vp.verifiableCredential || []) - .map(cred => { + .map((cred) => { if (typeof cred === 'string') { return normalizeCredential(cred) } else { diff --git a/packages/data-store/src/identifier/did-store.ts b/packages/data-store/src/identifier/did-store.ts index dbb2da5bc..a1ca7d503 100644 --- a/packages/data-store/src/identifier/did-store.ts +++ b/packages/data-store/src/identifier/did-store.ts @@ -44,13 +44,16 @@ export class DIDStore extends AbstractDIDStore { controllerKeyId: identifier.controllerKeyId, provider: identifier.provider!!, services: identifier.services, - keys: identifier.keys.map((k) => ({ - kid: k.kid, - type: k.type, - kms: k.kms, - publicKeyHex: k.publicKeyHex, - meta: k.meta, - } as IKey)), + keys: identifier.keys.map( + (k) => + ({ + kid: k.kid, + type: k.type, + kms: k.kms, + publicKeyHex: k.publicKeyHex, + meta: k.meta, + } as IKey), + ), } if (identifier.alias) { result.alias = identifier.alias @@ -59,12 +62,10 @@ export class DIDStore extends AbstractDIDStore { } async delete({ did }: { did: string }) { - const identifier = await (await this.dbConnection) - .getRepository(Identifier) - .findOne({ - where: { did }, - relations: ['keys', 'services', 'issuedCredentials', 'issuedPresentations'], - }) + const identifier = await (await this.dbConnection).getRepository(Identifier).findOne({ + where: { did }, + relations: ['keys', 'services', 'issuedCredentials', 'issuedPresentations'], + }) if (!identifier || typeof identifier === 'undefined') { return true } diff --git a/packages/data-store/src/identifier/private-key-store.ts b/packages/data-store/src/identifier/private-key-store.ts index d986c83ce..f42dd4b90 100644 --- a/packages/data-store/src/identifier/private-key-store.ts +++ b/packages/data-store/src/identifier/private-key-store.ts @@ -2,7 +2,7 @@ import { AbstractSecretBox, AbstractPrivateKeyStore } from '@veramo/key-manager' import { Connection } from 'typeorm' import { ImportablePrivateKey, ManagedPrivateKey } from '@veramo/key-manager/src/abstract-private-key-store' import { PrivateKey } from '../entities/private-key' -import { v4 as uuid4} from 'uuid' +import { v4 as uuid4 } from 'uuid' import Debug from 'debug' const debug = Debug('veramo:typeorm:key-store') @@ -43,7 +43,9 @@ export class PrivateKeyStore extends AbstractPrivateKeyStore { const keyRepo = await (await this.dbConnection).getRepository(PrivateKey) const existingKey = await keyRepo.findOne(key.alias) if (existingKey && existingKey.privateKeyHex !== key.privateKeyHex) { - throw new Error(`key_already_exists: A key with this alias exists but with different data. Please use a different alias.`) + throw new Error( + `key_already_exists: A key with this alias exists but with different data. Please use a different alias.`, + ) } await keyRepo.save(key) return key diff --git a/packages/data-store/src/index.ts b/packages/data-store/src/index.ts index 4f249fbdf..c6614297c 100644 --- a/packages/data-store/src/index.ts +++ b/packages/data-store/src/index.ts @@ -35,8 +35,30 @@ import { Service } from './entities/service' import { Message, MetaData } from './entities/message' import { PrivateKey } from './entities/private-key' import { PreMigrationKey } from './entities/PreMigrationEntities' -export const Entities = [Key, Identifier, Message, Claim, Credential, Presentation, Service, PrivateKey, PreMigrationKey] -export { KeyType, Key, Identifier, Message, Claim, Credential, Presentation, MetaData, Service, PrivateKey, PreMigrationKey } +export const Entities = [ + Key, + Identifier, + Message, + Claim, + Credential, + Presentation, + Service, + PrivateKey, + PreMigrationKey, +] +export { + KeyType, + Key, + Identifier, + Message, + Claim, + Credential, + Presentation, + MetaData, + Service, + PrivateKey, + PreMigrationKey, +} export { migrations } from './migrations' const schema = require('../plugin.schema.json') export { schema } diff --git a/packages/data-store/src/migrations/4.allowNullVPIssuanceDate.ts b/packages/data-store/src/migrations/4.allowNullVPIssuanceDate.ts index d16f36a7e..2ca1c4fad 100644 --- a/packages/data-store/src/migrations/4.allowNullVPIssuanceDate.ts +++ b/packages/data-store/src/migrations/4.allowNullVPIssuanceDate.ts @@ -18,38 +18,38 @@ export class AllowNullIssuanceDateForPresentations1637237492913 implements Migra async up(queryRunner: QueryRunner): Promise { if (queryRunner.connection.driver.options.type === 'sqlite') { debug(`splitting migration into multiple transactions to allow sqlite table updates`) - await queryRunner.commitTransaction(); + await queryRunner.commitTransaction() debug(`turning off foreign keys`) - await queryRunner.query('PRAGMA foreign_keys=off'); - await queryRunner.startTransaction(); + await queryRunner.query('PRAGMA foreign_keys=off') + await queryRunner.startTransaction() } const tableName = this.getTableName('presentation', queryRunner) // update issuanceDate column - let table = await queryRunner.getTable(tableName); - const oldColumn = table?.findColumnByName("issuanceDate")!; - const newColumn = oldColumn.clone(); - newColumn.isNullable = true; + let table = await queryRunner.getTable(tableName) + const oldColumn = table?.findColumnByName('issuanceDate')! + const newColumn = oldColumn.clone() + newColumn.isNullable = true debug(`updating issuanceDate for presentations to allow null`) - await queryRunner.changeColumn(table!, oldColumn, newColumn); + await queryRunner.changeColumn(table!, oldColumn, newColumn) debug(`updated issuanceDate for presentations to allow null`) if (queryRunner.connection.driver.options.type === 'sqlite') { debug(`splitting migration into multiple transactions to allow sqlite table updates`) - await queryRunner.commitTransaction(); + await queryRunner.commitTransaction() debug(`turning on foreign keys`) - await queryRunner.query('PRAGMA foreign_keys=on'); - await queryRunner.startTransaction(); + await queryRunner.query('PRAGMA foreign_keys=on') + await queryRunner.startTransaction() } } async down(queryRunner: QueryRunner): Promise { if (queryRunner.connection.driver.options.type === 'sqlite') { debug(`splitting migration into multiple transactions to allow sqlite table updates`) - await queryRunner.commitTransaction(); + await queryRunner.commitTransaction() debug(`turning off foreign keys`) - await queryRunner.query('PRAGMA foreign_keys=off'); - await queryRunner.startTransaction(); + await queryRunner.query('PRAGMA foreign_keys=off') + await queryRunner.startTransaction() } const tableName = this.getTableName('presentation', queryRunner) debug(`DOWN update NULL 'issuanceDate' with FAKE data for '${tableName}' table`) @@ -60,20 +60,20 @@ export class AllowNullIssuanceDateForPresentations1637237492913 implements Migra .where('issuanceDate is NULL') .execute() // update issuanceDate column - let table = await queryRunner.getTable(tableName); - const oldColumn = table?.findColumnByName("issuanceDate")!; - const newColumn = oldColumn.clone(); - newColumn.isNullable = false; + let table = await queryRunner.getTable(tableName) + const oldColumn = table?.findColumnByName('issuanceDate')! + const newColumn = oldColumn.clone() + newColumn.isNullable = false debug(`updating issuanceDate for presentations to NOT allow null`) - await queryRunner.changeColumn(table!, oldColumn, newColumn); + await queryRunner.changeColumn(table!, oldColumn, newColumn) debug(`updated issuanceDate for presentations to NOT allow null`) if (queryRunner.connection.driver.options.type === 'sqlite') { debug(`splitting migration into multiple transactions to allow sqlite table updates`) - await queryRunner.commitTransaction(); + await queryRunner.commitTransaction() debug(`turning on foreign keys`) - await queryRunner.query('PRAGMA foreign_keys=on'); - await queryRunner.startTransaction(); + await queryRunner.query('PRAGMA foreign_keys=on') + await queryRunner.startTransaction() } debug(`DOWN updated issuanceDate for presentations to NOT allow null`) diff --git a/packages/data-store/src/migrations/index.ts b/packages/data-store/src/migrations/index.ts index 63287dfe1..ea7bdc84c 100644 --- a/packages/data-store/src/migrations/index.ts +++ b/packages/data-store/src/migrations/index.ts @@ -1,6 +1,11 @@ import { CreateDatabase1447159020001 } from './1.createDatabase' import { SimplifyRelations1447159020002 } from './2.simplifyRelations' import { CreatePrivateKeyStorage1629293428674 } from './3.createPrivateKeyStorage' -import { AllowNullIssuanceDateForPresentations1637237492913 } from "./4.allowNullVPIssuanceDate"; +import { AllowNullIssuanceDateForPresentations1637237492913 } from './4.allowNullVPIssuanceDate' -export const migrations = [CreateDatabase1447159020001, SimplifyRelations1447159020002, CreatePrivateKeyStorage1629293428674, AllowNullIssuanceDateForPresentations1637237492913] +export const migrations = [ + CreateDatabase1447159020001, + SimplifyRelations1447159020002, + CreatePrivateKeyStorage1629293428674, + AllowNullIssuanceDateForPresentations1637237492913, +] diff --git a/packages/data-store/types/blakejs/index.d.ts b/packages/data-store/types/blakejs/index.d.ts deleted file mode 100644 index d3f1b7fe3..000000000 --- a/packages/data-store/types/blakejs/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -declare module 'blakejs' diff --git a/packages/did-comm/src/didcomm.ts b/packages/did-comm/src/didcomm.ts index 57be93597..8b7cbc110 100644 --- a/packages/did-comm/src/didcomm.ts +++ b/packages/did-comm/src/didcomm.ts @@ -274,11 +274,9 @@ export class DIDComm implements IAgentPlugin { const didDocument: DIDDocument = await resolveDidOrThrow(args?.message?.to, context) // 2.1 extract all recipient key agreement keys and normalize them - const keyAgreementKeys: _NormalizedVerificationMethod[] = (await dereferenceDidKeys( - didDocument, - 'keyAgreement', - context, - )).filter(k => k.publicKeyHex?.length! > 0) + const keyAgreementKeys: _NormalizedVerificationMethod[] = ( + await dereferenceDidKeys(didDocument, 'keyAgreement', context) + ).filter((k) => k.publicKeyHex?.length! > 0) if (keyAgreementKeys.length === 0) { throw new Error(`key_not_found: no key agreement keys found for recipient ${args?.message?.to}`) @@ -290,20 +288,26 @@ export class DIDComm implements IAgentPlugin { .filter(isDefined) if (recipients.length === 0) { - throw new Error(`not_supported: no compatible key agreement keys found for recipient ${args?.message?.to}`) + throw new Error( + `not_supported: no compatible key agreement keys found for recipient ${args?.message?.to}`, + ) } // 3. create Encrypter for each recipient - const encrypters: Encrypter[] = recipients.map((recipient) => { - if (args.packing === 'authcrypt') { - return createAuthEncrypter(recipient.publicKeyBytes, senderECDH, { kid: recipient.kid }) - } else { - return createAnonEncrypter(recipient.publicKeyBytes, { kid: recipient.kid }) - } - }).filter(isDefined) + const encrypters: Encrypter[] = recipients + .map((recipient) => { + if (args.packing === 'authcrypt') { + return createAuthEncrypter(recipient.publicKeyBytes, senderECDH, { kid: recipient.kid }) + } else { + return createAnonEncrypter(recipient.publicKeyBytes, { kid: recipient.kid }) + } + }) + .filter(isDefined) if (encrypters.length === 0) { - throw new Error(`not_supported: could not create suitable encryption for recipient ${args?.message?.to}`) + throw new Error( + `not_supported: could not create suitable encryption for recipient ${args?.message?.to}`, + ) } // 4. createJWE diff --git a/packages/did-comm/src/types/message-types.ts b/packages/did-comm/src/types/message-types.ts index 6f3bf3625..04cd0cc83 100644 --- a/packages/did-comm/src/types/message-types.ts +++ b/packages/did-comm/src/types/message-types.ts @@ -28,7 +28,7 @@ export enum DIDCommMessageMediaType { * A plain JSON DIDComm message */ PLAIN = 'application/didcomm-plain+json', - + /** * A JWS signed DIDComm message */ @@ -44,12 +44,12 @@ export enum DIDCommMessageMediaType { * The possible types of message packing. * * `authcrypt`, `anoncrypt`, `anoncrypt+authcrypt`, and `anoncrypt+jws` will produce `DIDCommMessageMediaType.ENCRYPTED` messages. - * + * * `jws` will produce `DIDCommMessageMediaType.SIGNED` messages. - * + * * `none` will produce `DIDCommMessageMediaType.PLAIN` messages. * - * + * * @beta */ export type DIDCommMessagePacking = diff --git a/packages/did-provider-key/src/resolver.ts b/packages/did-provider-key/src/resolver.ts index e4b16dd06..f76846729 100644 --- a/packages/did-provider-key/src/resolver.ts +++ b/packages/did-provider-key/src/resolver.ts @@ -7,7 +7,7 @@ export const startsWithMap: Record = { 'did:key:z6Mk': resolveED25519, 'did:key:z6LS': resolveX25519, 'did:key:zQ3s': resolveSecp256k1, -}; +} const resolveDidKey: DIDResolver = async ( didUrl: string, @@ -16,13 +16,13 @@ const resolveDidKey: DIDResolver = async ( options: DIDResolutionOptions, ): Promise => { try { - const startsWith = _parsed.did.substring(0, 12); + const startsWith = _parsed.did.substring(0, 12) if (startsWithMap[startsWith] !== undefined) { - const didResolution = (await startsWithMap[startsWith](didUrl, options as any)) + const didResolution = await startsWithMap[startsWith](didUrl, options as any) return { didDocumentMetadata: {}, didResolutionMetadata: {}, - ...didResolution + ...didResolution, } } else { return { diff --git a/packages/did-resolver/src/__tests__/integration.test.ts b/packages/did-resolver/src/__tests__/integration.test.ts index 06085a68d..54c4c45a8 100644 --- a/packages/did-resolver/src/__tests__/integration.test.ts +++ b/packages/did-resolver/src/__tests__/integration.test.ts @@ -135,5 +135,4 @@ describe('@veramo/did-resolver', () => { didResolutionMetadata: { error: 'unsupportedDidMethod' }, }) }) - }) diff --git a/packages/did-resolver/src/resolver.ts b/packages/did-resolver/src/resolver.ts index 5fe443e9f..3aacef6d2 100644 --- a/packages/did-resolver/src/resolver.ts +++ b/packages/did-resolver/src/resolver.ts @@ -44,7 +44,7 @@ export class DIDResolverPlugin implements IAgentPlugin { accept: 'application/did+ld+json', ...options, } - + // ensure the required fields are present, even if the resolver is not compliant const cannedResponse: DIDResolutionResult = { didDocumentMetadata: {}, @@ -53,7 +53,7 @@ export class DIDResolverPlugin implements IAgentPlugin { } const resolution = await this.didResolver.resolve(didUrl, resolverOptions) - + return { ...cannedResponse, ...resolution, diff --git a/packages/key-manager/src/abstract-private-key-store.ts b/packages/key-manager/src/abstract-private-key-store.ts index f036e188f..76603f0a6 100644 --- a/packages/key-manager/src/abstract-private-key-store.ts +++ b/packages/key-manager/src/abstract-private-key-store.ts @@ -1,4 +1,4 @@ -import { ManagedKeyInfo, RequireOnly, TKeyType } from '@veramo/core' +import { ManagedKeyInfo, RequireOnly, TKeyType } from '@veramo/core' export interface ManagedPrivateKey { alias: string diff --git a/packages/key-manager/src/memory-key-store.ts b/packages/key-manager/src/memory-key-store.ts index c3f139fb6..219e3c504 100644 --- a/packages/key-manager/src/memory-key-store.ts +++ b/packages/key-manager/src/memory-key-store.ts @@ -37,7 +37,7 @@ export class MemoryKeyStore extends AbstractKeyStore { /** * An implementation of {@link AbstractPrivateKeyStore} that holds everything in memory. - * + * * This is usable by {@link @veramo/kms-local} to hold the private key data. */ export class MemoryPrivateKeyStore extends AbstractPrivateKeyStore { diff --git a/packages/kms-local/src/key-management-system.ts b/packages/kms-local/src/key-management-system.ts index 70be169da..dc69a4600 100644 --- a/packages/kms-local/src/key-management-system.ts +++ b/packages/kms-local/src/key-management-system.ts @@ -214,7 +214,8 @@ export class KeyManagementSystem extends AbstractKeyManagementSystem { if (from) { debug('WARNING: executing a transaction signing request with a `from` field.') if (wallet.address.toLowerCase() !== from.toLowerCase()) { - const msg = "invalid_arguments: eth_signTransaction `from` field does not match the chosen key. `from` field should be omitted." + const msg = + 'invalid_arguments: eth_signTransaction `from` field does not match the chosen key. `from` field should be omitted.' debug(msg) throw new Error(msg) } diff --git a/packages/message-handler/src/message-handler.ts b/packages/message-handler/src/message-handler.ts index f610471dd..3c45436ac 100644 --- a/packages/message-handler/src/message-handler.ts +++ b/packages/message-handler/src/message-handler.ts @@ -5,7 +5,8 @@ import { IMessageHandler, IHandleMessageArgs, schema, - CoreEvents, IMessage, + CoreEvents, + IMessage, } from '@veramo/core' import { Message } from './message' import { AbstractMessageHandler } from './abstract-message-handler' @@ -51,7 +52,10 @@ export class MessageHandler implements IAgentPlugin { } /** {@inheritDoc @veramo/core#IMessageHandler.handleMessage} */ - public async handleMessage(args: IHandleMessageArgs, context: IAgentContext): Promise { + public async handleMessage( + args: IHandleMessageArgs, + context: IAgentContext, + ): Promise { const { raw, metaData, save } = args debug('%o', { raw, metaData, save }) if (!this.messageHandler) { diff --git a/packages/remote-server/src/messaging-router.ts b/packages/remote-server/src/messaging-router.ts index a8cef3601..3cab3628f 100644 --- a/packages/remote-server/src/messaging-router.ts +++ b/packages/remote-server/src/messaging-router.ts @@ -37,7 +37,7 @@ export const MessagingRouter = (options: MessagingRouterOptions): Router => { const message = await req.agent?.handleMessage({ raw: req.body as any as string, metaData: [options.metaData], - save: typeof options.save === 'undefined' ? true : options.save, + save: typeof options.save === 'undefined' ? true : options.save, }) if (message) { diff --git a/packages/remote-server/src/web-did-doc-router.ts b/packages/remote-server/src/web-did-doc-router.ts index 02afb1bae..b9bebe010 100644 --- a/packages/remote-server/src/web-did-doc-router.ts +++ b/packages/remote-server/src/web-did-doc-router.ts @@ -17,7 +17,7 @@ const keyMapping: Record = { /** * @public */ - export interface WebDidDocRouterOptions { +export interface WebDidDocRouterOptions { services?: ServiceEndpoint[] } @@ -52,7 +52,10 @@ export const WebDidDocRouter = (options: WebDidDocRouterOptions): Router => { authentication: signingKeyIds, assertionMethod: signingKeyIds, keyAgreement: keyAgreementKeyIds, - service: typeof options.services === 'undefined' ? identifier.services : [...options.services, ...identifier.services], + service: + typeof options.services === 'undefined' + ? identifier.services + : [...options.services, ...identifier.services], } return didDoc diff --git a/packages/selective-disclosure/src/action-handler.ts b/packages/selective-disclosure/src/action-handler.ts index 3b6b5a509..0488f41c4 100644 --- a/packages/selective-disclosure/src/action-handler.ts +++ b/packages/selective-disclosure/src/action-handler.ts @@ -14,7 +14,13 @@ import { import { schema } from './' import { createJWT } from 'did-jwt' import Debug from 'debug' -import { asArray, bytesToBase64, computeEntryHash, decodeCredentialToObject, extractIssuer } from '@veramo/utils' +import { + asArray, + bytesToBase64, + computeEntryHash, + decodeCredentialToObject, + extractIssuer, +} from '@veramo/utils' /** * This class adds support for creating @@ -197,7 +203,10 @@ export class SelectiveDisclosure implements IAgentPlugin { return false } - if (credentialRequest.credentialType && !asArray(credential.type || []).includes(credentialRequest.credentialType)) { + if ( + credentialRequest.credentialType && + !asArray(credential.type || []).includes(credentialRequest.credentialType) + ) { return false } diff --git a/packages/selective-disclosure/src/message-handler.ts b/packages/selective-disclosure/src/message-handler.ts index 698ca40fd..359e8a475 100644 --- a/packages/selective-disclosure/src/message-handler.ts +++ b/packages/selective-disclosure/src/message-handler.ts @@ -3,7 +3,7 @@ import { Message, AbstractMessageHandler } from '@veramo/message-handler' import { v4 as uuidv4 } from 'uuid' import Debug from 'debug' -import { asArray, computeEntryHash } from "@veramo/utils"; +import { asArray, computeEntryHash } from '@veramo/utils' const debug = Debug('veramo:selective-disclosure:message-handler') /** diff --git a/packages/third.party.types.d.ts b/packages/third.party.types.d.ts index 155544cfe..ca93060d1 100644 --- a/packages/third.party.types.d.ts +++ b/packages/third.party.types.d.ts @@ -1,2 +1,2 @@ declare module 'ganache-cli' -declare module 'ethr-did-registry' \ No newline at end of file +declare module 'ethr-did-registry' diff --git a/packages/utils/src/credential-utils.ts b/packages/utils/src/credential-utils.ts index 419f92483..4366fc6a8 100644 --- a/packages/utils/src/credential-utils.ts +++ b/packages/utils/src/credential-utils.ts @@ -1,31 +1,37 @@ -import { asArray } from "./utils"; +import { asArray, isDefined } from './type-utils' import { - CredentialPayload, IMessage, + CredentialPayload, + IMessage, IssuerType, - PresentationPayload, VerifiableCredential, + PresentationPayload, + VerifiableCredential, + VerifiablePresentation, W3CVerifiableCredential, - W3CVerifiablePresentation -} from "@veramo/core"; + W3CVerifiablePresentation, +} from '@veramo/core' import { blake2bHex } from 'blakejs' -import { decodeJWT } from "did-jwt"; -import { normalizeCredential } from "did-jwt-vc"; +import { decodeJWT } from 'did-jwt' +import { normalizeCredential, normalizePresentation } from 'did-jwt-vc' export const MANDATORY_CREDENTIAL_CONTEXT = 'https://www.w3.org/2018/credentials/v1' /** - * Processes a n entry or an array of entries into an array of entries. If a `startWithEntry` param is provided, it is + * Processes an entry or an array of entries into an array of entries. If a `startWithEntry` param is provided, it is * set as the first item in the result array. * @param inputEntryOrArray * @param startWithEntry * * @beta This API may change without prior notice. */ -export function processEntryToArray(inputEntryOrArray?: string | string[], startWithEntry?: string): string[] { - const result: string[] = asArray(inputEntryOrArray || []) || [startWithEntry] +export function processEntryToArray( + inputEntryOrArray?: string | string[] | null, + startWithEntry?: string, +): string[] { + const result: string[] = asArray(inputEntryOrArray) || [startWithEntry] if (startWithEntry && result[0] !== startWithEntry) { result.unshift(startWithEntry) } - return result; + return result.filter(isDefined).filter((item, index, arr) => arr.indexOf(item) === index) } /** @@ -37,27 +43,29 @@ export function processEntryToArray(inputEntryOrArray?: string | string[], start * @beta This API may change without prior notice. */ export function decodeCredentialToObject(input: W3CVerifiableCredential): VerifiableCredential { - return (typeof input === 'string') ? normalizeCredential(input) : input + return typeof input === 'string' ? normalizeCredential(input) : input } -// /** -// * Parses a {@link VerifiablePresentation} and converts it to a {@link SignedPresentation} so it is easier to use -// * programmatically. -// * -// * @param input -// -// * @beta This API may change without prior notice. -// */ -// export function convertToSignedPresentation(input: VerifiablePresentation): SignedPresentation { -// let result: SignedPresentation -// if (typeof input === 'string') { -// result = normalizePresentation(input) -// } else { -// result = input as SignedPresentation -// result.verifiableCredential = result.verifiableCredential?.map(convertToSignedCredential) -// } -// return result -// } +/** + * Parses a {@link W3CVerifiableCredential} and converts it to a {@link VerifiablePresentation} so it is easier to use + * programmatically. + * + * @param input + + * @beta This API may change without prior notice. + */ +export function decodePresentationToObject(input: W3CVerifiablePresentation): VerifiablePresentation { + let result: VerifiablePresentation + if (typeof input === 'string') { + result = normalizePresentation(input) + } else { + result = input as VerifiablePresentation + result.verifiableCredential = asArray(result.verifiableCredential).map( + decodeCredentialToObject, + ) + } + return result +} /** * Computes a hash for a given credential or presentation. @@ -67,7 +75,9 @@ export function decodeCredentialToObject(input: W3CVerifiableCredential): Verifi * * @beta This API may change without prior notice. */ -export function computeEntryHash(input: W3CVerifiableCredential | W3CVerifiablePresentation | IMessage): string { +export function computeEntryHash( + input: W3CVerifiableCredential | W3CVerifiablePresentation | IMessage, +): string { if (typeof input === 'string') { // TODO: try to parse as JSON before assuming it's a JWT? return blake2bHex(input) @@ -86,7 +96,9 @@ export function computeEntryHash(input: W3CVerifiableCredential | W3CVerifiableP * * @beta This API may change without prior notice. */ -export function extractIssuer(input: W3CVerifiableCredential | W3CVerifiablePresentation | CredentialPayload | PresentationPayload): string { +export function extractIssuer( + input: W3CVerifiableCredential | W3CVerifiablePresentation | CredentialPayload | PresentationPayload, +): string { if (typeof input === 'string') { // JWT const { payload } = decodeJWT(input) @@ -101,6 +113,6 @@ export function extractIssuer(input: W3CVerifiableCredential | W3CVerifiablePres } else { iss = '' } - return (typeof iss === 'string') ? iss : (iss?.id || '') + return typeof iss === 'string' ? iss : iss?.id || '' } -} \ No newline at end of file +} diff --git a/packages/utils/src/did-utils.ts b/packages/utils/src/did-utils.ts index d52964461..0211a9122 100644 --- a/packages/utils/src/did-utils.ts +++ b/packages/utils/src/did-utils.ts @@ -3,8 +3,12 @@ import { computePublicKey } from '@ethersproject/signing-key' import { computeAddress } from '@ethersproject/transactions' import { DIDDocumentSection, IAgentContext, IIdentifier, IKey, IResolver } from '@veramo/core' import { DIDDocument, VerificationMethod } from 'did-resolver' -import { _ExtendedIKey, _ExtendedVerificationMethod, _NormalizedVerificationMethod } from "./types/utility-types"; -import { isDefined } from "./utils"; +import { + _ExtendedIKey, + _ExtendedVerificationMethod, + _NormalizedVerificationMethod, +} from './types/utility-types' +import { isDefined } from './type-utils' import * as u8a from 'uint8arrays' import Debug from 'debug' const debug = Debug('veramo:utils') @@ -45,7 +49,7 @@ export function compressIdentifierSecp256k1Keys(identifier: IIdentifier): IKey[] function compareBlockchainAccountId(localKey: IKey, verificationMethod: _NormalizedVerificationMethod) { if (verificationMethod.type !== 'EcdsaSecp256k1RecoveryMethod2020' || localKey.type !== 'Secp256k1') { - return false; + return false } let vmEthAddr = verificationMethod.ethereumAddress?.toLowerCase() if (!vmEthAddr) { @@ -56,7 +60,7 @@ function compareBlockchainAccountId(localKey: IKey, verificationMethod: _Normali } } const computedAddr = computeAddress('0x' + localKey.publicKeyHex).toLowerCase() - return (computedAddr === vmEthAddr) + return computedAddr === vmEthAddr } export async function mapIdentifierKeysToDoc( @@ -82,7 +86,11 @@ export async function mapIdentifierKeysToDoc( // finally map the didDocument keys to the identifier keys by comparing `publicKeyHex` const extendedKeys: _ExtendedIKey[] = documentKeys .map((verificationMethod) => { - const localKey = localKeys.find((localKey) => localKey.publicKeyHex === verificationMethod.publicKeyHex || compareBlockchainAccountId(localKey, verificationMethod)) + const localKey = localKeys.find( + (localKey) => + localKey.publicKeyHex === verificationMethod.publicKeyHex || + compareBlockchainAccountId(localKey, verificationMethod), + ) if (localKey) { const { meta, ...localProps } = localKey return { ...localProps, meta: { ...meta, verificationMethod } } diff --git a/packages/utils/src/encodings.ts b/packages/utils/src/encodings.ts index 4a87366e4..1a4ae23d2 100644 --- a/packages/utils/src/encodings.ts +++ b/packages/utils/src/encodings.ts @@ -27,4 +27,4 @@ export function encodeJoseBlob(payload: {}) { export function decodeJoseBlob(blob: string) { return JSON.parse(u8a.toString(u8a.fromString(blob, 'base64url'), 'utf-8')) -} \ No newline at end of file +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index bc253baa3..c534db6b0 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -8,4 +8,4 @@ export * from './types/utility-types' export * from './credential-utils' export * from './did-utils' export * from './encodings' -export * from './utils' +export * from './type-utils' diff --git a/packages/utils/src/utils.ts b/packages/utils/src/type-utils.ts similarity index 52% rename from packages/utils/src/utils.ts rename to packages/utils/src/type-utils.ts index f3081fc39..8abc9eab5 100644 --- a/packages/utils/src/utils.ts +++ b/packages/utils/src/type-utils.ts @@ -2,6 +2,6 @@ export function isDefined(arg: T): arg is Exclude { return arg !== null && typeof arg !== 'undefined' } -export function asArray(arg: T | T[]): T[] { - return Array.isArray(arg) ? arg : [arg] +export function asArray(arg?: T | T[] | null): T[] { + return isDefined(arg) ? (Array.isArray(arg) ? arg : [arg]) : [] } diff --git a/yarn.lock b/yarn.lock index e7e84835d..a679c98e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -334,6 +334,15 @@ "@transmute/did-context" "^0.6.1-unstable.25" jsonld-checker "^0.1.6" +"@digitalbazaar/http-client@^1.1.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@digitalbazaar/http-client/-/http-client-1.2.0.tgz#1ea3661e77000a15bd892a294f20dc6cc5d1c93b" + integrity sha512-W9KQQ5pUJcaR0I4c2HPJC0a7kRbZApIorZgPnEDwMBgj16iQzutGLrCXYaZOmxqVLVNqqlQ4aUJh+HBQZy4W6Q== + dependencies: + esm "^3.2.22" + ky "^0.25.1" + ky-universal "^0.8.2" + "@ethersproject/abi@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" @@ -1525,30 +1534,6 @@ npmlog "^4.1.2" write-file-atomic "^3.0.3" -"@mattrglobal/bbs-signatures@0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bbs-signatures/-/bbs-signatures-0.5.0.tgz#c8e3842a657cabbf7f1e16db06f77c9f84a42f3f" - integrity sha512-4te4TpacAmeCM8aa/kHkU0i1IJwsO1x/Tez6/YLUWg6rK6bfGA1NNO7IBc12u9ETkoTsiU32UmsiYWXcw9QwKQ== - optionalDependencies: - "@mattrglobal/node-bbs-signatures" "0.11.0" - -"@mattrglobal/bls12381-key-pair@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/bls12381-key-pair/-/bls12381-key-pair-0.5.0.tgz#dd6014b5b14903d4a280af4286c74d8a97b38410" - integrity sha512-eXAtke0HOEr9RcT+NEI1MERE50gUnnLm1mYBJkUugk9REP3MfKXtX2Mo4FXyCH/IR4Oxj2jCcfNYW/h0Q3x5sg== - dependencies: - "@mattrglobal/bbs-signatures" "0.5.0" - bs58 "4.0.1" - rfc4648 "1.4.0" - -"@mattrglobal/node-bbs-signatures@0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@mattrglobal/node-bbs-signatures/-/node-bbs-signatures-0.11.0.tgz#c63ab8648a529cfe1dd855cc78a93f78ee27a9f4" - integrity sha512-V0wcY0ZewrPOiMOrL3wam0oYL1SLbF2ihgAM6JQvLrAKw1MckYiJ8T4vL+nOBs2hf1PA1TZI+USe5mqMWuVKTw== - dependencies: - neon-cli "0.4.0" - node-pre-gyp "0.14.0" - "@microsoft/api-documenter@7.13.68": version "7.13.68" resolved "https://registry.yarnpkg.com/@microsoft/api-documenter/-/api-documenter-7.13.68.tgz#c1e144764cac0684adefe78fd848d78c3f374681" @@ -2033,6 +2018,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@sovpro/delimited-stream@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@sovpro/delimited-stream/-/delimited-stream-1.1.0.tgz#4334bba7ee241036e580fdd99c019377630d26b4" + integrity sha512-kQpk267uxB19X3X2T1mvNMjyvIEonpNSHrMlK5ZaBU6aZxw7wPbpgKJOjHN3+/GPVpXgAV9soVT2oyHpLkLtyw== + "@sqltools/formatter@^1.2.2": version "1.2.3" resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.3.tgz#1185726610acc37317ddab11c3c7f9066966bd20" @@ -2043,6 +2033,26 @@ resolved "https://registry.yarnpkg.com/@stablelib/aead/-/aead-1.0.1.tgz#c4b1106df9c23d1b867eb9b276d8f42d5fc4c0c3" integrity sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg== +"@stablelib/aes-kw@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/aes-kw/-/aes-kw-1.0.1.tgz#43f25517f719d69bb995909a5b69a2d9172c2a93" + integrity sha512-KrOkiRex1tQTbWk+hFB5fFw4vqKhNnTUtlCRf1bhUEOFp7hadWe49/sLa/P4X4FBQVoh3Z9Lj0zS1OWu/AHA1w== + dependencies: + "@stablelib/aes" "^1.0.1" + "@stablelib/binary" "^1.0.1" + "@stablelib/blockcipher" "^1.0.1" + "@stablelib/constant-time" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + +"@stablelib/aes@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/aes/-/aes-1.0.1.tgz#f2a8aec2cebaf0e69be2b49c7c57b4267867ffa5" + integrity sha512-bMiezJDeFONDHbMEa+Kic26962+bwkZfsHPAmcqTjLaHCAhEQuK3i1H0POPOkcHCdj75oVRIqFCraCA0cyHPvw== + dependencies: + "@stablelib/binary" "^1.0.1" + "@stablelib/blockcipher" "^1.0.1" + "@stablelib/wipe" "^1.0.1" + "@stablelib/binary@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" @@ -2050,6 +2060,11 @@ dependencies: "@stablelib/int" "^1.0.1" +"@stablelib/blockcipher@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@stablelib/blockcipher/-/blockcipher-1.0.1.tgz#535f067d147ecdc9625ccd2b0d129f6d53d563d2" + integrity sha512-4bkpV8HUAv0CgI1fUqkPUEEvv3RXQ3qBkuZaSWhshXGAz1JCpriesgiO9Qs4f0KzBJkCtvcho5n7d/RKvnHbew== + "@stablelib/bytes@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/bytes/-/bytes-1.0.1.tgz#0f4aa7b03df3080b878c7dea927d01f42d6a20d8" @@ -2183,7 +2198,7 @@ "@stablelib/chacha" "^1.0.1" "@stablelib/wipe" "^1.0.1" -"@stablelib/xchacha20poly1305@^1.0.1": +"@stablelib/xchacha20poly1305@^1.0.0", "@stablelib/xchacha20poly1305@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@stablelib/xchacha20poly1305/-/xchacha20poly1305-1.0.1.tgz#addcaf30b92dd956f76b3357888e2f91b92e7a61" integrity sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg== @@ -2208,27 +2223,16 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@transmute/bls12381-key-pair@^0.7.0-unstable.2": - version "0.7.0-unstable.3" - resolved "https://registry.yarnpkg.com/@transmute/bls12381-key-pair/-/bls12381-key-pair-0.7.0-unstable.3.tgz#eb3eee008bec30a8894ef2d31e9d481ccc155872" - integrity sha512-v/niSi6WrZe/ONDo/7E5fjj127IKrHA7E4W3lEnovxE32iAmplyW6UyW/3Fo0vffPnpv7MPLwnkavNPhbv4GWA== - dependencies: - "@mattrglobal/bls12381-key-pair" "^0.5.0" - "@transmute/ld-key-pair" "^0.7.0-unstable.3" +"@transmute/credentials-context@^0.7.0-unstable.26": + version "0.7.0-unstable.28" + resolved "https://registry.yarnpkg.com/@transmute/credentials-context/-/credentials-context-0.7.0-unstable.28.tgz#713cfdce5fe7c84b79ab0a7e8eebcf7eeac404b9" + integrity sha512-yW5TCBDNp1JzWiMZD3Av04EKVcf+zBTZ6pZe8Z+zILpsDineDYd4BBUP3Q7Z6/oyL8hcL/zyoH3Zoi1nuED6xA== "@transmute/did-context@^0.6.1-unstable.25", "@transmute/did-context@^0.6.1-unstable.36": version "0.6.1-unstable.37" resolved "https://registry.yarnpkg.com/@transmute/did-context/-/did-context-0.6.1-unstable.37.tgz#12ad065e142bc688460090d0ce338948e513c262" integrity sha512-p/QnG3QKS4218hjIDgdvJOFATCXsAnZKgy4egqRrJLlo3Y6OaDBg7cA73dixOwUPoEKob0K6rLIGcsCI/L1acw== -"@transmute/did-key-bls12381@^0.3.0-unstable.4": - version "0.3.0-unstable.4" - resolved "https://registry.yarnpkg.com/@transmute/did-key-bls12381/-/did-key-bls12381-0.3.0-unstable.4.tgz#0524e7a05eed2efcb62cea581923523202afa097" - integrity sha512-1EHDBRYVsj0x6uUJ/6a9GkTD6czW24mW4l2qqhEtGrYXM5VC3xIBkBqoqIYg1M8R14jI27MqvxlKLGuY2DJM7g== - dependencies: - "@transmute/bls12381-key-pair" "^0.7.0-unstable.2" - "@transmute/did-key-common" "^0.3.0-unstable.4" - "@transmute/did-key-common@^0.3.0-unstable.4": version "0.3.0-unstable.4" resolved "https://registry.yarnpkg.com/@transmute/did-key-common/-/did-key-common-0.3.0-unstable.4.tgz#62042d62795e466c39705e2905912afa8158ab58" @@ -2256,19 +2260,6 @@ "@transmute/did-key-common" "^0.3.0-unstable.4" "@transmute/secp256k1-key-pair" "^0.7.0-unstable.2" -"@transmute/did-key-test-vectors@^0.3.0-unstable.4": - version "0.3.0-unstable.4" - resolved "https://registry.yarnpkg.com/@transmute/did-key-test-vectors/-/did-key-test-vectors-0.3.0-unstable.4.tgz#b26a2b8703e2144ea77f94e4ab194fe422c3349b" - integrity sha512-C0J0l7R2RbsQ0T3kr83q6Qa4SGqkHjq8dKCVNzhdpbwcUqiROoIUrU+dRvwUAO52PySNc/1h7xXXvIG3VmywfQ== - -"@transmute/did-key-web-crypto@^0.3.0-unstable.4": - version "0.3.0-unstable.4" - resolved "https://registry.yarnpkg.com/@transmute/did-key-web-crypto/-/did-key-web-crypto-0.3.0-unstable.4.tgz#8c22bfa3443905552bf3344b223eef132d0fff3c" - integrity sha512-vnWneWZ0GNtk7f7AYaTp6TyNoHd4kMOvVGbFF8CLWZonXZFcYg0PZvLxGy+BCQyj6n9CCJYb5myhki1dL4TDdw== - dependencies: - "@transmute/did-key-common" "^0.3.0-unstable.4" - "@transmute/web-crypto-key-pair" "^0.7.0-unstable.2" - "@transmute/did-key-x25519@^0.3.0-unstable.4": version "0.3.0-unstable.4" resolved "https://registry.yarnpkg.com/@transmute/did-key-x25519/-/did-key-x25519-0.3.0-unstable.4.tgz#7068313f0cb4c1473b4fbef636cd8bb3ad8cfc05" @@ -2277,17 +2268,14 @@ "@transmute/did-key-common" "^0.3.0-unstable.4" "@transmute/x25519-key-pair" "^0.7.0-unstable.1" -"@transmute/did-key.js@^0.3.0-unstable": - version "0.3.0-unstable.4" - resolved "https://registry.yarnpkg.com/@transmute/did-key.js/-/did-key.js-0.3.0-unstable.4.tgz#2ee3cec339d5826a03612c09dd7c77936df34106" - integrity sha512-7jhj65NqNaH/GShqweLCxkVwZKYbUyDap/AwugmoD2PRlrUUQvzeOqjRDUCHvnMrpf06vxBLlpVyJA6fKYYpWA== +"@transmute/ed25519-key-pair@0.7.0-unstable.2": + version "0.7.0-unstable.2" + resolved "https://registry.yarnpkg.com/@transmute/ed25519-key-pair/-/ed25519-key-pair-0.7.0-unstable.2.tgz#b36c076fe8b8fead312cf27b05d9973513fe3239" + integrity sha512-B0jg348Z8F0+lGWQic28xVxBZiXOJYbisWp6EfP4fQdMV3G4sES9YubpdiuoZHjesDZrf6xZ7cEB81mjGJMUkA== dependencies: - "@transmute/did-key-bls12381" "^0.3.0-unstable.4" - "@transmute/did-key-ed25519" "^0.3.0-unstable.4" - "@transmute/did-key-secp256k1" "^0.3.0-unstable.4" - "@transmute/did-key-test-vectors" "^0.3.0-unstable.4" - "@transmute/did-key-web-crypto" "^0.3.0-unstable.4" - "@transmute/did-key-x25519" "^0.3.0-unstable.4" + "@stablelib/ed25519" "^1.0.1" + "@transmute/ld-key-pair" "^0.7.0-unstable.2" + "@transmute/x25519-key-pair" "^0.7.0-unstable.2" "@transmute/ed25519-key-pair@^0.6.1-unstable.37": version "0.6.1-unstable.37" @@ -2298,16 +2286,56 @@ "@transmute/ld-key-pair" "^0.6.1-unstable.37" "@transmute/x25519-key-pair" "^0.6.1-unstable.37" +"@transmute/ed25519-signature-2018@^0.7.0-unstable.27": + version "0.7.0-unstable.28" + resolved "https://registry.yarnpkg.com/@transmute/ed25519-signature-2018/-/ed25519-signature-2018-0.7.0-unstable.28.tgz#451118729f77745b6de71f5132d81be31c296c31" + integrity sha512-J08bED6buAQYBniM1ItrwwyFMSb4w5HLq65wOU/N3yeuJfm9/AwIHe3cCS5S8oBvMIz8fMwRKngaEJb5aRr+iw== + dependencies: + "@transmute/ed25519-key-pair" "0.7.0-unstable.2" + "@transmute/jose-ld" "^0.7.0-unstable.28" + "@transmute/security-context" "^0.7.0-unstable.28" + jsonld "5.2.0" + +"@transmute/jose-ld@^0.7.0-unstable.28": + version "0.7.0-unstable.28" + resolved "https://registry.yarnpkg.com/@transmute/jose-ld/-/jose-ld-0.7.0-unstable.28.tgz#91404a9073e3d93d994986d742b67f2814126c9a" + integrity sha512-IhAInINNY/KUf3tdlyjjtl/dVBB6DzY8DoB49udFDFVmQiYPuHYQc74blO3KyryqQZPNvTSv1k0EvvQO2i3m1w== + dependencies: + "@peculiar/webcrypto" "^1.1.6" + "@stablelib/aes-kw" "^1.0.0" + "@stablelib/xchacha20poly1305" "^1.0.0" + base64url "^3.0.1" + web-streams-polyfill "^3.0.3" + "@transmute/ld-key-pair@^0.6.1-unstable.36", "@transmute/ld-key-pair@^0.6.1-unstable.37": version "0.6.1-unstable.37" resolved "https://registry.yarnpkg.com/@transmute/ld-key-pair/-/ld-key-pair-0.6.1-unstable.37.tgz#ffe8af071b4ea991a49c795724b93999f4e6c8af" integrity sha512-DcTpEruAQBfOd2laZkg3uCQ+67Y7dw2hsvo42NAQ5tItCIx5AClP7zccri7T2JUcfDUFaE32z/BLTMEKYt3XZQ== +"@transmute/ld-key-pair@^0.7.0-unstable.2", "@transmute/ld-key-pair@^0.7.0-unstable.28": + version "0.7.0-unstable.28" + resolved "https://registry.yarnpkg.com/@transmute/ld-key-pair/-/ld-key-pair-0.7.0-unstable.28.tgz#ac5d03b483c5515de3d036d590533bfa7ceb94cf" + integrity sha512-apNOgjpHRHq7BRPoXM1BDaiVCzTjnQWPiZoSUbav5mecOjSjmWEkk3lY30EVXBVzfddVIRsgVb2MIYqFchE4aA== + "@transmute/ld-key-pair@^0.7.0-unstable.3": version "0.7.0-unstable.3" resolved "https://registry.yarnpkg.com/@transmute/ld-key-pair/-/ld-key-pair-0.7.0-unstable.3.tgz#b758293b843b0937508ce3ed93b698eb6c5cfbb1" integrity sha512-iJ4dwhL+owbPkx4Vugl1ekSde5GSGnknqpANNSgBHEXNemorPiWOZD0p6EbQArO5WtaSTfpd5AOrukH7Bo3VDA== +"@transmute/lds-ecdsa-secp256k1-recovery2020@^0.0.7": + version "0.0.7" + resolved "https://registry.yarnpkg.com/@transmute/lds-ecdsa-secp256k1-recovery2020/-/lds-ecdsa-secp256k1-recovery2020-0.0.7.tgz#f48995aa42703cd545d8082d89c955f5d34c4297" + integrity sha512-OjVYDdfdDJXoCkPGWb2JsQ3a319jz6JTrdf1+j2E6WMf/83Zx2+BN7ahwgYdsstCWlWaysnsVp4F41ALvZk8/A== + dependencies: + "@trust/keyto" "^0.3.7" + base64url "^3.0.1" + bitcoin-ts "^1.14.2" + crypto-ld "^3.7.0" + ethereum-public-key-to-address "0.0.2" + json-stringify-deterministic "^1.0.1" + jsonld "^3.1.0" + jsonld-signatures "^5.0.1" + "@transmute/secp256k1-key-pair@^0.7.0-unstable.2": version "0.7.0-unstable.3" resolved "https://registry.yarnpkg.com/@transmute/secp256k1-key-pair/-/secp256k1-key-pair-0.7.0-unstable.3.tgz#14b08e8e6d656ebc997acc5e7920ed8db0fa2866" @@ -2322,14 +2350,10 @@ resolved "https://registry.yarnpkg.com/@transmute/security-context/-/security-context-0.6.1-unstable.37.tgz#532b9238efd80dbaaa3e7dd663107cd925afadcc" integrity sha512-GtLmG65qlORrz/2S4I74DT+vA4+qXsFxrMr0cNOXjUqZBd/AW1PTrFnryLF9907BfoiD58HC9qb1WVGWjSlBYw== -"@transmute/web-crypto-key-pair@^0.7.0-unstable.2": - version "0.7.0-unstable.3" - resolved "https://registry.yarnpkg.com/@transmute/web-crypto-key-pair/-/web-crypto-key-pair-0.7.0-unstable.3.tgz#107a4eb432e2d3a744fb16cc8fe67c44c1936d97" - integrity sha512-cGmGYovgKklyECs6w7ha5OPsS5GU/vrDtREv+p5q/IckP8yLcH+c9vWMucJAQHASHKtlBuJH8btlgzVOCLUTFA== - dependencies: - "@peculiar/webcrypto" "^1.1.6" - "@transmute/ld-key-pair" "^0.7.0-unstable.3" - big-integer "^1.6.48" +"@transmute/security-context@^0.7.0-unstable.28": + version "0.7.0-unstable.28" + resolved "https://registry.yarnpkg.com/@transmute/security-context/-/security-context-0.7.0-unstable.28.tgz#09a061a396a939f1e31cab32293c4488165fb998" + integrity sha512-lzBiVooNm3OZM68tCII+EmscAc8GOCmLp77jrsimQTpOOnMmjVJhFPlQNYhBWu6tzhxmgOXQDEJW/JTOIoRYOg== "@transmute/x25519-key-pair@^0.6.1-unstable.37": version "0.6.1-unstable.37" @@ -2347,6 +2371,23 @@ "@stablelib/x25519" "^1.0.0" "@transmute/ld-key-pair" "^0.7.0-unstable.3" +"@transmute/x25519-key-pair@^0.7.0-unstable.2": + version "0.7.0-unstable.28" + resolved "https://registry.yarnpkg.com/@transmute/x25519-key-pair/-/x25519-key-pair-0.7.0-unstable.28.tgz#1a7905fff15905f351e76227c037b35ae9af35bb" + integrity sha512-feaEOENCU+TIvGS5c9swpA5x8OgYwzhsEMETTJsV4UZ/sM3Q/ccihJ7z5sJz/YDJUQJgK4Kl1c+/iSNJNnYjIQ== + dependencies: + "@stablelib/x25519" "^1.0.0" + "@transmute/ld-key-pair" "^0.7.0-unstable.28" + +"@trust/keyto@^0.3.7": + version "0.3.7" + resolved "https://registry.yarnpkg.com/@trust/keyto/-/keyto-0.3.7.tgz#e251264e302a7a6be64a3e208dacb2ef6268c946" + integrity sha512-t5kWWCTkPgg24JWVuCTPMx7l13F7YHdxBeJkT1vmoHjROgiOIEAN8eeY+iRmP1Hwsx+S7U55HyuqSsECr08a8A== + dependencies: + asn1.js "^5.0.1" + base64url "^3.0.1" + elliptic "^6.4.1" + "@tsconfig/node10@^1.0.7": version "1.0.8" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" @@ -2700,6 +2741,11 @@ resolved "https://registry.yarnpkg.com/@types/url-parse/-/url-parse-1.4.5.tgz#bda0b813cc8f79d767dc8ca645fdabc238c6ae77" integrity sha512-8Wje3itJpk/FX+QItca9vjNLjGx5jlEYBw/CpMi03Fphk2DSVeZDUqWTE81BeCI5Bl6Z+zmA1O9L/8e3ZUSeLg== +"@types/uuid@8.3.1": + version "8.3.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.1.tgz#1a32969cf8f0364b3d8c8af9cc3555b7805df14f" + integrity sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg== + "@types/uuid@8.3.3": version "8.3.3" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.3.tgz#c6a60686d953dbd1b1d45e66f4ecdbd5d471b4d0" @@ -2747,6 +2793,13 @@ abbrev@1, abbrev@~1.1.1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -2827,18 +2880,6 @@ ajv@^6.12.3, ajv@~6.12.6: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-escape-sequences@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz#2483c8773f50dd9174dd9557e92b1718f1816097" - integrity sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw== - dependencies: - array-back "^3.0.1" - -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -2871,7 +2912,7 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -2968,30 +3009,16 @@ argv@0.0.2: resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab" integrity sha1-7L0W+JSbFXGDcRsb2jNPN4QBhas= -array-back@^1.0.3, array-back@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-1.0.4.tgz#644ba7f095f7ffcf7c43b5f0dc39d3c1f03c063b" - integrity sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs= - dependencies: - typical "^2.6.0" - -array-back@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-2.0.0.tgz#6877471d51ecc9c9bfa6136fb6c7d5fe69748022" - integrity sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw== - dependencies: - typical "^2.6.1" - -array-back@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/array-back/-/array-back-3.1.0.tgz#b8859d7a508871c9a7b2cf42f99428f65e96bfb0" - integrity sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q== - array-differ@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -3022,6 +3049,16 @@ asap@^2.0.0: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= +asn1.js@^5.0.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" + integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + safer-buffer "^2.1.0" + asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" @@ -3144,6 +3181,18 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64url-universal@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/base64url-universal/-/base64url-universal-1.1.0.tgz#94da6356c1d43ead55b1d91c045c0a5b09ec8181" + integrity sha512-WyftvZqye29YQ10ZnuiBeEj0lk8SN8xHU9hOznkLc85wS1cLTp6RpzlMrHxMPD9nH7S55gsBqMqgGyz93rqmkA== + dependencies: + base64url "^3.0.0" + +base64url@^3.0.0, base64url@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" + integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -3166,10 +3215,10 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== -big-integer@^1.6.48: - version "1.6.48" - resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" - integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== +bignumber.js@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" + integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== bin-links@^2.2.1: version "2.2.1" @@ -3188,6 +3237,25 @@ binary-extensions@^2.2.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bindings@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" + integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== + dependencies: + file-uri-to-path "1.0.0" + +bip66@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" + integrity sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI= + dependencies: + safe-buffer "^5.0.1" + +bitcoin-ts@^1.14.2: + version "1.15.2" + resolved "https://registry.yarnpkg.com/bitcoin-ts/-/bitcoin-ts-1.15.2.tgz#51ff4c8134f43f3f9e38515a18348795c991ed8d" + integrity sha512-N5cjC+PjAuTvU3mMcO9aZI5w6lseHickKh6tX6n5p89i2rrUbhgq0KHeOOCYNIbnFcemjGea8uuSXMFBRDl7NQ== + bl@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -3214,7 +3282,7 @@ block-stream@*: dependencies: inherits "~2.0.0" -bn.js@^4.11.0, bn.js@^4.11.9: +bn.js@^4.0.0, bn.js@^4.11.0, bn.js@^4.11.8, bn.js@^4.11.9: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== @@ -3240,6 +3308,19 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" +borc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/borc/-/borc-3.0.0.tgz#49ada1be84de86f57bb1bb89789f34c186dfa4fe" + integrity sha512-ec4JmVC46kE0+layfnwM3l15O70MlFiEbmQHY/vpqIKiUtPVntv4BY4NVnz3N4vb21edV3mY97XVckFvYHWF9g== + dependencies: + bignumber.js "^9.0.0" + buffer "^6.0.3" + commander "^2.15.0" + ieee754 "^1.1.13" + iso-url "^1.1.5" + json-text-sequence "~0.3.0" + readable-stream "^3.6.0" + bottleneck@^2.18.1: version "2.19.5" resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" @@ -3270,7 +3351,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserify-aes@^1.2.0: +browserify-aes@^1.0.6, browserify-aes@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== @@ -3300,7 +3381,7 @@ bs-logger@0.x: dependencies: fast-json-stable-stringify "2.x" -bs58@4.0.1, bs58@^4.0.0: +bs58@^4.0.0, bs58@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= @@ -3410,6 +3491,15 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== +camelcase-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" + integrity sha1-oqpfsa9oh1glnDLBQUJteJI7m3c= + dependencies: + camelcase "^4.1.0" + map-obj "^2.0.0" + quick-lru "^1.0.0" + camelcase-keys@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz#5e755d6ba51aa223ec7d3d52f25778210f9dc3c0" @@ -3419,6 +3509,11 @@ camelcase-keys@^6.2.2: map-obj "^4.0.0" quick-lru "^4.0.1" +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= + camelcase@^5.0.0, camelcase@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" @@ -3477,25 +3572,11 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e" - integrity sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ== - dependencies: - ansi-styles "^3.1.0" - escape-string-regexp "^1.0.5" - supports-color "^4.0.0" - char-regex@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -3554,13 +3635,6 @@ cli-columns@^3.1.2: string-width "^2.0.0" strip-ansi "^3.0.1" -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - dependencies: - restore-cursor "^2.0.0" - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -3602,11 +3676,6 @@ cli-table@^0.3.1: dependencies: colors "1.0.3" -cli-width@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" - integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== - cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -3736,33 +3805,7 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -command-line-args@^4.0.2: - version "4.0.7" - resolved "https://registry.yarnpkg.com/command-line-args/-/command-line-args-4.0.7.tgz#f8d1916ecb90e9e121eda6428e41300bfb64cc46" - integrity sha512-aUdPvQRAyBvQd2n7jXcsMDz68ckBJELXNzBybCHOibUWEg0mWTnaYCSRU8h9R+aNRSvDihJtssSRCiDRpLaezA== - dependencies: - array-back "^2.0.0" - find-replace "^1.0.3" - typical "^2.6.1" - -command-line-commands@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/command-line-commands/-/command-line-commands-2.0.1.tgz#c58aa13dc78c06038ed67077e57ad09a6f858f46" - integrity sha512-m8c2p1DrNd2ruIAggxd/y6DgygQayf6r8RHwchhXryaLF8I6koYjoYroVP+emeROE9DXN5b9sP1Gh+WtvTTdtQ== - dependencies: - array-back "^2.0.0" - -command-line-usage@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/command-line-usage/-/command-line-usage-4.1.0.tgz#a6b3b2e2703b4dcf8bd46ae19e118a9a52972882" - integrity sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g== - dependencies: - ansi-escape-sequences "^4.0.0" - array-back "^2.0.0" - table-layout "^0.4.2" - typical "^2.6.1" - -commander@^2.7.1: +commander@^2.15.0, commander@^2.20.3, commander@^2.7.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -3989,6 +4032,11 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== +credentials-context@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/credentials-context/-/credentials-context-1.0.0.tgz#a63cb4b7e0a4ca4460d247b7c9370a58b10ebac9" + integrity sha512-rF3GPhTUGY58xlpuVRif/1i0BxVpynpmFdGNS81S2ezdKPSKoFke5ZOZWB8ZUvGi8bV8CuDM+ZcM/uf4z0PQVQ== + cross-fetch@3.1.4, cross-fetch@^3.0.4, cross-fetch@^3.1.2, cross-fetch@^3.1.4: version "3.1.4" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" @@ -4016,6 +4064,18 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-ld@^3.7.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/crypto-ld/-/crypto-ld-3.9.0.tgz#4f42849a143b5d65f28b018d80bc0fee6cd37ba8" + integrity sha512-PFE7V6A2QNnUp6iiPVEZI4p8wsztkEWLbY1BAXVnclm/aw4KGwpJ+1Ds4vQUCJ5BsWxj15fwE5rHQ8AWaWB2nw== + dependencies: + base64url-universal "^1.0.1" + bs58 "^4.0.1" + node-forge "~0.10.0" + semver "^6.2.0" + optionalDependencies: + sodium-native "^3.2.0" + crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -4038,6 +4098,13 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + dargs@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" @@ -4100,7 +4167,7 @@ debuglog@^1.0.1: resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= -decamelize-keys@^1.1.0: +decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" integrity sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk= @@ -4128,7 +4195,7 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-extend@^0.6.0, deep-extend@~0.6.0: +deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== @@ -4347,6 +4414,15 @@ dotenv@^8.2.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== +drbg.js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/drbg.js/-/drbg.js-1.0.1.tgz#3e36b6c42b37043823cdbc332d58f31e2445480b" + integrity sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs= + dependencies: + browserify-aes "^1.0.6" + create-hash "^1.1.2" + create-hmac "^1.1.4" + duplexer2@~0.1.0: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -4377,7 +4453,7 @@ electron-to-chromium@^1.3.723: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.774.tgz#4d6661a23119e35151646c9543b346bb3beca423" integrity sha512-Fggh17Q1yyv1uMzq8Qn1Ci58P50qcRXMXd2MBcB9sxo6rJxjUutWcNw8uCm3gFWMdcblBO6mDT5HzX/RVRRECA== -elliptic@6.5.4, elliptic@^6.5.2, elliptic@^6.5.4: +elliptic@6.5.4, elliptic@^6.4.1, elliptic@^6.5.2, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -4517,6 +4593,11 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" +esm@^3.2.22: + version "3.2.25" + resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" + integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== + esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -4537,6 +4618,14 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +ethereum-checksum-address@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ethereum-checksum-address/-/ethereum-checksum-address-0.0.2.tgz#46fcb2d962dacd1ed49d7b464408ec26fd183209" + integrity sha512-GAb7mPvGgcfi1j+Bsnwm9af9Z7dLUKp+5cFm88+kMrKACfh9gLatGLVVK5pSGEG2pOGfrmqCRcuh3RtMjIg8GQ== + dependencies: + keccak256 "^1.0.0" + meow "^5.0.0" + ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" @@ -4558,6 +4647,16 @@ ethereum-cryptography@^0.1.3: secp256k1 "^4.0.1" setimmediate "^1.0.5" +ethereum-public-key-to-address@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ethereum-public-key-to-address/-/ethereum-public-key-to-address-0.0.2.tgz#c1f12ecfe8dc27e8949f1ec47ee855b32ffe14f3" + integrity sha512-KRd0yrlbgESK3A62L4sHiJRk+b/UPX92Ehd0cCXWa5L7bQaq7z5q4BSRhuUuSZj++LQHQfJQQnJkskuHjDnbCQ== + dependencies: + ethereum-checksum-address "0.0.2" + keccak256 "^1.0.0" + meow "^5.0.0" + secp256k1 "^3.7.1" + ethereumjs-util@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" @@ -4619,6 +4718,11 @@ ethr-did@2.1.5: did-resolver "^3.1.3" ethr-did-resolver "^5.0.2" +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -4747,15 +4851,6 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" - integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - external-editor@^3.0.3: version "3.1.0" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" @@ -4835,6 +4930,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fetch-blob@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" + integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== + fetch-blob@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.1.2.tgz#6bc438675f3851ecea51758ac91f6a1cd1bacabd" @@ -4856,6 +4956,11 @@ figures@^3.0.0, figures@^3.2.0: dependencies: escape-string-regexp "^1.0.5" +file-uri-to-path@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" + integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -4881,14 +4986,6 @@ finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-replace@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/find-replace/-/find-replace-1.0.3.tgz#b88e7364d2d9c959559f388c66670d6130441fa0" - integrity sha1-uI5zZNLZyVlVnziMZmcNYTBEH6A= - dependencies: - array-back "^1.0.4" - test-value "^2.1.0" - find-up@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -4968,6 +5065,15 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -5093,6 +5199,11 @@ get-port@^5.1.1: resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== +get-stdin@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" + integrity sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -5119,13 +5230,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -git-config@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/git-config/-/git-config-0.0.7.tgz#a9c8a3ef07a776c3d72261356d8b727b62202b28" - integrity sha1-qcij7wendsPXImE1bYtye2IgKyg= - dependencies: - iniparser "~1.0.5" - git-log-parser@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/git-log-parser/-/git-log-parser-1.2.0.tgz#2e6a4c1b13fc00028207ba795a7ac31667b9fd4a" @@ -5245,7 +5349,7 @@ graceful-fs@^4.2.8: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== -handlebars@^4.1.0, handlebars@^4.7.6, handlebars@^4.7.7: +handlebars@^4.7.6, handlebars@^4.7.7: version "4.7.7" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.7.tgz#9ce33416aad02dbd6c8fafa8240d5d98004945a1" integrity sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA== @@ -5280,11 +5384,6 @@ has-bigints@^1.0.1: resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - integrity sha1-6CB68cx7MNRGzHC3NLXovhj4jVE= - has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -5447,7 +5546,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.24, iconv-lite@^0.4.4: +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5509,6 +5608,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok= + indent-string@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" @@ -5547,11 +5651,6 @@ ini@^2.0.0: resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5" integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== -iniparser@~1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/iniparser/-/iniparser-1.0.5.tgz#836d6befe6dfbfcee0bccf1cf9f2acc7027f783d" - integrity sha1-g21r7+bfv87gvM8c+fKsxwJ/eD0= - init-package-json@^2.0.2, init-package-json@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-2.0.3.tgz#c8ae4f2a4ad353bcbc089e5ffe98a8f1a314e8fd" @@ -5577,26 +5676,6 @@ inquirer-autocomplete-prompt@^1.2.0: run-async "^2.4.0" rxjs "^6.6.2" -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - inquirer@^7.3.3: version "7.3.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" @@ -5889,6 +5968,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +iso-url@^1.1.5: + version "1.2.1" + resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-1.2.1.tgz#db96a49d8d9a64a1c889fc07cc525d093afb1811" + integrity sha512-9JPDgCN4B7QPkLtYAAOrEuAWvP9rWvR5offAr0/SeF046wIkglqH3VXgYYP6NcsKslH80UIVgmPqNe3j7tG2ng== + isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" @@ -6507,6 +6591,11 @@ json-schema@0.4.0, json-schema@^0.4.0: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== +json-stringify-deterministic@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-deterministic/-/json-stringify-deterministic-1.0.1.tgz#3334798c374d723d46f7ba0e47d6e5e5ac8511f9" + integrity sha512-9Fg0OY3uyzozpvJ8TVbUk09PjzhT7O2Q5kEe30g6OrKhbA/Is92igcx0XDDX7E3yAwnIlUcYLRl+ZkVrBYVP7A== + json-stringify-nice@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" @@ -6517,6 +6606,13 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json-text-sequence@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.3.0.tgz#6603e0ee45da41f949669fd18744b97fb209e6ce" + integrity sha512-7khKIYPKwXQem4lWXfpIN/FEnhztCeRPSxH4qm3fVlqulwujrRDD54xAwDDn/qVKpFtV550+QAkcWJcufzqQuA== + dependencies: + "@sovpro/delimited-stream" "^1.1.0" + json5@2.x, json5@^2.1.2, json5@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" @@ -6548,7 +6644,50 @@ jsonld-checker@^0.1.6: jsonld "^3.1.1" node-fetch "^2.6.1" -jsonld@^3.1.1: +jsonld-signatures@^5.0.0, jsonld-signatures@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/jsonld-signatures/-/jsonld-signatures-5.2.0.tgz#2f6210c3aad0a50f925991ce3e91c78856c0752d" + integrity sha512-/dGgMElXc3oBS+/OUwMc3DTK4riHKLE9Lk7NF1Upz2ZlBTNfnOw5uLRkFQOJFBDqDEm5hK6hIfkoC/rCWFh9tQ== + dependencies: + base64url "^3.0.1" + crypto-ld "^3.7.0" + jsonld "^2.0.2" + node-forge "^0.10.0" + security-context "^4.0.0" + serialize-error "^5.0.0" + +jsonld-signatures@^9.0.2: + version "9.3.0" + resolved "https://registry.yarnpkg.com/jsonld-signatures/-/jsonld-signatures-9.3.0.tgz#4da0ac56c14ece9e0bf1dc02bdf419aabd5f5ce1" + integrity sha512-I52f8AmCoaUuM7gbFUGu9Ir3odGWOmQPlNPE/VYbiRrC3UfIRig0hYhNFkcVhe0ZXsE5L0if6Fo5SutKFqs3uQ== + dependencies: + jsonld "^5.0.0" + security-context "^4.0.0" + serialize-error "^8.0.1" + +jsonld@5.2.0, jsonld@^5.0.0, jsonld@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-5.2.0.tgz#d1e8af38a334cb95edf0f2ae4e2b58baf8d2b5a9" + integrity sha512-JymgT6Xzk5CHEmHuEyvoTNviEPxv6ihLWSPu1gFdtjSAyM6cFqNrv02yS/SIur3BBIkCf0HjizRc24d8/FfQKw== + dependencies: + "@digitalbazaar/http-client" "^1.1.0" + canonicalize "^1.0.1" + lru-cache "^6.0.0" + rdf-canonize "^3.0.0" + +jsonld@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-2.0.2.tgz#f5d0631de828ce6dee3a14f08cbdc6a53174cd15" + integrity sha512-/TQzRe75/3h2khu57IUojha5oat+M82bm8RYw0jLhlmmPrW/kTWAZ9nGzKPfZWnPYnVVJJMQVc/pU8HCmpv9xg== + dependencies: + canonicalize "^1.0.1" + lru-cache "^5.1.1" + rdf-canonize "^1.0.2" + request "^2.88.0" + semver "^6.3.0" + xmldom "0.1.19" + +jsonld@^3.1.0, jsonld@^3.1.1: version "3.3.2" resolved "https://registry.yarnpkg.com/jsonld/-/jsonld-3.3.2.tgz#f8688b349385ff5c755e83c7f60a9cf834078fc0" integrity sha512-DXqG/fdiG7eJ8FzvSd58bW8DQsulQR/gjLYUz9PxBP/WTTpB2HzjjdxSAx5aBHewJ0RiFAV/QcqGCJjxHvuIzw== @@ -6591,7 +6730,15 @@ just-diff@^3.0.1: resolved "https://registry.yarnpkg.com/just-diff/-/just-diff-3.1.1.tgz#d50c597c6fd4776495308c63bdee1b6839082647" integrity sha512-sdMWKjRq8qWZEjDcVA6llnUT8RDEBIfOiGpYFPYa9u+2c39JCsejktSP7mj5eRid5EIvTzIpQ2kDOCw1Nq9BjQ== -keccak@^3.0.0: +keccak256@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.3.tgz#0a9c0383a9cda753a7351811cf69eaa607043366" + integrity sha512-EkF/4twuPm1V/gn75nejOUrKfDUJn87RMLzDWosXF3pXuOvesiSgX35GcmbqzdImCASEkE/WaklWGWSa+Ha5bQ== + dependencies: + bn.js "^4.11.8" + keccak "^3.0.1" + +keccak@^3.0.0, keccak@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.2.tgz#4c2c6e8c54e04f2670ee49fa734eb9da152206e0" integrity sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ== @@ -6610,6 +6757,19 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +ky-universal@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/ky-universal/-/ky-universal-0.8.2.tgz#edc398d54cf495d7d6830aa1ab69559a3cc7f824" + integrity sha512-xe0JaOH9QeYxdyGLnzUOVGK4Z6FGvDVzcXFTdrYA1f33MZdEa45sUDaMBy98xQMcsd2XIBrTXRrRYnegcSdgVQ== + dependencies: + abort-controller "^3.0.0" + node-fetch "3.0.0-beta.9" + +ky@^0.25.1: + version "0.25.1" + resolved "https://registry.yarnpkg.com/ky/-/ky-0.25.1.tgz#0df0bd872a9cc57e31acd5dbc1443547c881bfbc" + integrity sha512-PjpCEWlIU7VpiMVrTwssahkYXX1by6NCT0fhTUX34F3DTinARlgMpriuroolugFPcMgpPWrOW4mTb984Qm1RXA== + lcid@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-2.0.0.tgz#6ef5d2df60e52f82eb228a4c373e8d1f397253cf" @@ -6871,11 +7031,6 @@ lodash.memoize@4.x: resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= -lodash.padend@^4.6.1: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" - integrity sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4= - lodash.template@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" @@ -6901,7 +7056,7 @@ lodash.uniqby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= -lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.7.0, lodash@~4.17.15: +lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0, lodash@~4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -6914,6 +7069,14 @@ log-symbols@^4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -7032,6 +7195,11 @@ map-obj@^1.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= +map-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" + integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= + map-obj@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-4.2.1.tgz#e4ea399dbc979ae735c83c863dd31bdf364277b7" @@ -7077,6 +7245,21 @@ mem@^4.0.0: mimic-fn "^2.0.0" p-is-promise "^2.0.0" +meow@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" + integrity sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig== + dependencies: + camelcase-keys "^4.0.0" + decamelize-keys "^1.0.0" + loud-rejection "^1.0.0" + minimist-options "^3.0.1" + normalize-package-data "^2.3.4" + read-pkg-up "^3.0.0" + redent "^2.0.0" + trim-newlines "^2.0.0" + yargs-parser "^10.0.0" + meow@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/meow/-/meow-7.1.1.tgz#7c01595e3d337fcb0ec4e8eed1666ea95903d306" @@ -7161,11 +7344,6 @@ mime@^2.4.3: resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -7202,6 +7380,14 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" +minimist-options@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" + integrity sha512-FyBrT/d0d4+uiZRbqznPXqw3IpZZG3gl3wKWiX784FycUKVwBt0uLBFkQrtE4tZOrgo78nZp2jnKz3L65T5LdQ== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -7293,7 +7479,7 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: +"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -7382,11 +7568,6 @@ multimatch@^5.0.0: arrify "^2.0.1" minimatch "^3.0.4" -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= - mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" @@ -7401,6 +7582,11 @@ mz@^2.4.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nan@^2.14.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee" + integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -7425,28 +7611,6 @@ neo-async@^2.6.0: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -neon-cli@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/neon-cli/-/neon-cli-0.4.0.tgz#d89e0a55b8db577324af70470e2b4e67157205f6" - integrity sha512-66HhHb8rk+zHSG64CI6jhyOQqpibBAald8ObdQPCjXcCjzSEVnkQHutUE8dyNlHRNT7xLfrZGkDbtwrYh2p+6w== - dependencies: - chalk "~2.1.0" - command-line-args "^4.0.2" - command-line-commands "^2.0.0" - command-line-usage "^4.0.0" - git-config "0.0.7" - handlebars "^4.1.0" - inquirer "^3.0.6" - mkdirp "^0.5.1" - quickly-copy-file "^1.0.0" - rimraf "^2.6.1" - rsvp "^4.6.1" - semver "^5.1.0" - toml "^2.3.0" - ts-typed-json "^0.2.2" - validate-npm-package-license "^3.0.1" - validate-npm-package-name "^3.0.0" - nerf-dart@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/nerf-dart/-/nerf-dart-1.0.0.tgz#e6dab7febf5ad816ea81cf5c629c5a0ebde72c1a" @@ -7494,11 +7658,29 @@ node-fetch@2.6.1, node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-fetch@3.0.0-beta.9: + version "3.0.0-beta.9" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.0.0-beta.9.tgz#0a7554cfb824380dd6812864389923c783c80d9b" + integrity sha512-RdbZCEynH2tH46+tj0ua9caUHVWrd/RHnRfvly2EVdqGmI3ndS1Vn/xjm5KuGejDt2RNDQsVRLPNd2QPwcewVg== + dependencies: + data-uri-to-buffer "^3.0.1" + fetch-blob "^2.1.1" + +node-forge@^0.10.0, node-forge@~0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" + integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== + node-gyp-build@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== +node-gyp-build@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" + integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + node-gyp@3.x: version "3.8.0" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" @@ -7560,22 +7742,6 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-pre-gyp@0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz#9a0596533b877289bcad4e143982ca3d904ddc83" - integrity sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA== - dependencies: - detect-libc "^1.0.2" - mkdirp "^0.5.1" - needle "^2.2.1" - nopt "^4.0.1" - npm-packlist "^1.1.6" - npmlog "^4.0.2" - rc "^1.2.7" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^4.4.2" - node-pre-gyp@^0.11.0: version "0.11.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054" @@ -7619,7 +7785,7 @@ nopt@^5.0.0: dependencies: abbrev "1" -normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -7954,13 +8120,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" @@ -7973,11 +8132,6 @@ openapi-types@9.3.1: resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-9.3.1.tgz#617ae6db3efd3e3f68e849f65ced58801d01d3cf" integrity sha512-/Yvsd2D7miYB4HLJ3hOOS0+vnowQpaT75FsHzr/y5M9P4q9bwa7RcbW2YdH6KZBn8ceLbKGnHxMZ1CHliGHUFw== -openapi-types@^9.0.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-9.1.0.tgz#a2ab0acc5198c1725f83d7cbe063efd1bcd0479e" - integrity sha512-mhXh8QN8sbErlxfxBeZ/pzgvmDn443p8CXlxwGSi2bWANZAFvjLPI0PoGjqHW+JdBbXg6uvmvM81WXaweh/SVA== - opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" @@ -8697,18 +8851,16 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-lru@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" + integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= + quick-lru@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== -quickly-copy-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/quickly-copy-file/-/quickly-copy-file-1.0.0.tgz#9f8ff066230510ee7422b0121472b093a8690859" - integrity sha1-n4/wZiMFEO50IrASFHKwk6hpCFk= - dependencies: - mkdirp "~0.5.0" - randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8741,6 +8893,14 @@ rc@^1.2.7, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" +rdf-canonize@^1.0.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-1.2.0.tgz#9872b2cc6ed92a9969e834f9f042addaf0d4f7f9" + integrity sha512-MQdcRDz4+82nUrEb3hNQangBDpmep15uMmnWclGi/1KS0bNVc8oHpoNI0PFLHZsvwgwRzH31bO1JAScqUAstvw== + dependencies: + node-forge "^0.10.0" + semver "^6.3.0" + rdf-canonize@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-2.0.1.tgz#12c9b8b41570cd669d9b152c9e679063478ba194" @@ -8749,6 +8909,13 @@ rdf-canonize@^2.0.1: semver "^6.3.0" setimmediate "^1.0.5" +rdf-canonize@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/rdf-canonize/-/rdf-canonize-3.0.0.tgz#f5bade563e5e58f5cc5881afcba3c43839e8c747" + integrity sha512-LXRkhab1QaPJnhUIt1gtXXKswQCZ9zpflsSZFczG7mCLAkMvVjdqCGk9VXCUss0aOUeEyV2jtFxGcdX8DSkj9w== + dependencies: + setimmediate "^1.0.5" + react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" @@ -8871,6 +9038,14 @@ readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0: graceful-fs "^4.1.2" once "^1.3.0" +redent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" + integrity sha1-wbIAe0LVfrE4kHmzyDM2OdXhzKo= + dependencies: + indent-string "^3.0.0" + strip-indent "^2.0.0" + redent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" @@ -8886,11 +9061,6 @@ redeyed@~2.1.0: dependencies: esprima "~4.0.0" -reduce-flatten@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/reduce-flatten/-/reduce-flatten-1.0.1.tgz#258c78efd153ddf93cb561237f61184f3696e327" - integrity sha1-JYx479FT3fk8tWEjf2EYTzaW4yc= - reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -8994,14 +9164,6 @@ resolve@~1.19.0: is-core-module "^2.1.0" path-parse "^1.0.6" -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" @@ -9025,11 +9187,6 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rfc4648@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.4.0.tgz#c75b2856ad2e2d588b6ddb985d556f1f7f2a2abd" - integrity sha512-3qIzGhHlMHA6PoT6+cdPKZ+ZqtxkIvg8DZGKA5z6PQ33/uuhoJ+Ws/D/J9rXW6gXodgH8QYlz2UCl+sdUDmNIg== - rimraf@2, rimraf@^2.6.1, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -9059,17 +9216,7 @@ rlp@^2.2.3: dependencies: bn.js "^5.2.0" -rsvp@^3.5.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" - integrity sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw== - -rsvp@^4.6.1: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - -run-async@^2.2.0, run-async@^2.4.0: +run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== @@ -9081,18 +9228,6 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= - rxjs@^6.6.0, rxjs@^6.6.2, rxjs@^6.6.6: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" @@ -9144,6 +9279,20 @@ scrypt-js@3.0.1, scrypt-js@^3.0.0: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== +secp256k1@^3.7.1: + version "3.8.0" + resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.8.0.tgz#28f59f4b01dbee9575f56a47034b7d2e3b3b352d" + integrity sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw== + dependencies: + bindings "^1.5.0" + bip66 "^1.1.5" + bn.js "^4.11.8" + create-hash "^1.2.0" + drbg.js "^1.0.1" + elliptic "^6.5.2" + nan "^2.14.0" + safe-buffer "^5.1.2" + secp256k1@^4.0.1, secp256k1@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" @@ -9153,6 +9302,11 @@ secp256k1@^4.0.1, secp256k1@^4.0.2: node-addon-api "^2.0.0" node-gyp-build "^4.2.0" +security-context@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/security-context/-/security-context-4.0.0.tgz#e73f5d22bee9c7699a02eaaced359d001dc948e9" + integrity sha512-yiDCS7tpKQl6p4NG57BdKLTSNLFfj5HosBIzXBl4jZf/qorJzSzbEUIdLhN+vVYgyLlvjixY8DPPTgqI8zvNCA== + semantic-release@18.0.0: version "18.0.0" resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-18.0.0.tgz#b44b7101ed0525c041b984f74854852be67341cc" @@ -9199,7 +9353,7 @@ semver-regex@^3.1.2: resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-3.1.2.tgz#34b4c0d361eef262e07199dbef316d0f2ab11807" integrity sha512-bXWyL6EAKOJa81XG1OZ/Yyuq+oT0b2YLlxx7c+mrdYPaPbnj6WgVULXhinMIeZGufuUBu/eVRqXEhiv4imfwxA== -"semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -9211,7 +9365,7 @@ semver@7.x, semver@^7.1.1, semver@^7.1.2, semver@^7.1.3, semver@^7.3.2, semver@^ dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -9240,6 +9394,20 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" +serialize-error@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-5.0.0.tgz#a7ebbcdb03a5d71a6ed8461ffe0fc1a1afed62ac" + integrity sha512-/VtpuyzYf82mHYTtI4QKtwHa79vAdU5OQpNPAmE/0UDdlGT0ZxHwC+J6gXkw29wwoVI8fMPsfcVHOwXtUQYYQA== + dependencies: + type-fest "^0.8.0" + +serialize-error@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-8.1.0.tgz#3a069970c712f78634942ddd50fbbc0eaebe2f67" + integrity sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ== + dependencies: + type-fest "^0.20.2" + serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" @@ -9378,6 +9546,13 @@ socks@^2.3.3, socks@^2.6.1: ip "^1.1.5" smart-buffer "^4.1.0" +sodium-native@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sodium-native/-/sodium-native-3.3.0.tgz#50ee52ac843315866cce3d0c08ab03eb78f22361" + integrity sha512-rg6lCDM/qa3p07YGqaVD+ciAbUqm6SoO4xmlcfkbU5r1zIGrguXztLiEtaLYTV5U6k8KSIUFmnU3yQUSKmf6DA== + dependencies: + node-gyp-build "^4.3.0" + sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" @@ -9571,7 +9746,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0: +"string-width@^1.0.2 || 2", string-width@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -9687,6 +9862,11 @@ strip-hex-prefix@1.0.0: dependencies: is-hex-prefixed "1.0.0" +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g= + strip-indent@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" @@ -9718,13 +9898,6 @@ stubs@^3.0.0: resolved "https://registry.yarnpkg.com/stubs/-/stubs-3.0.0.tgz#e8d2ba1fa9c90570303c030b6900f7d5f89abe5b" integrity sha1-6NK6H6nJBXAwPAMLaQD31fiavls= -supports-color@^4.0.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" - integrity sha1-vnoN5ITexcXN34s9WRJQRJEvY1s= - dependencies: - has-flag "^2.0.0" - supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -9771,17 +9944,6 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table-layout@^0.4.2: - version "0.4.5" - resolved "https://registry.yarnpkg.com/table-layout/-/table-layout-0.4.5.tgz#d906de6a25fa09c0c90d1d08ecd833ecedcb7378" - integrity sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw== - dependencies: - array-back "^2.0.0" - deep-extend "~0.6.0" - lodash.padend "^4.6.1" - typical "^2.6.1" - wordwrapjs "^3.0.0" - tar@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" @@ -9791,7 +9953,7 @@ tar@^2.0.0: fstream "^1.0.12" inherits "2" -tar@^4, tar@^4.4.12, tar@^4.4.2: +tar@^4, tar@^4.4.12: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== @@ -9876,14 +10038,6 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -test-value@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/test-value/-/test-value-2.1.0.tgz#11da6ff670f3471a73b625ca4f3fdcf7bb748291" - integrity sha1-Edpv9nDzRxpztiXKTz/c97t0gpE= - dependencies: - array-back "^1.0.3" - typical "^2.6.0" - text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -9972,11 +10126,6 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -toml@^2.3.0: - version "2.3.6" - resolved "https://registry.yarnpkg.com/toml/-/toml-2.3.6.tgz#25b0866483a9722474895559088b436fd11f861b" - integrity sha512-gVweAectJU3ebq//Ferr2JUY4WKSDe5N+z0FvjDncLGyHmIDoxgY/2Ie4qfEIDm4IS7OA6Rmdm7pdEEdMcV/xQ== - tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -10011,6 +10160,11 @@ treeverse@^1.0.4: resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-1.0.4.tgz#a6b0ebf98a1bca6846ddc7ecbc900df08cb9cd5f" integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g== +trim-newlines@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" + integrity sha1-tAPQuRvlDDMd/EuC7s6yLD3hbSA= + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" @@ -10065,13 +10219,6 @@ ts-node@10.4.0: make-error "^1.1.1" yn "3.1.1" -ts-typed-json@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/ts-typed-json/-/ts-typed-json-0.2.2.tgz#53184bee893e45991b73c8c463a38b59e27cd47e" - integrity sha1-UxhL7ok+RZkbc8jEY6OLWeJ81H4= - dependencies: - rsvp "^3.5.0" - tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -10126,6 +10273,11 @@ type-fest@^0.18.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -10141,7 +10293,7 @@ type-fest@^0.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== -type-fest@^0.8.1: +type-fest@^0.8.0, type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== @@ -10188,6 +10340,11 @@ typeorm@0.2.41: yargs "^17.0.1" zen-observable-ts "^1.0.0" +typescript@4.4.4: + version "4.4.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" + integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== + typescript@4.5.2: version "4.5.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" @@ -10198,11 +10355,6 @@ typescript@~4.4.2, typescript@~4.4.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324" integrity sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA== -typical@^2.6.0, typical@^2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/typical/-/typical-2.6.1.tgz#5c080e5d661cbbe38259d2e70a3c7253e873881d" - integrity sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0= - uglify-js@^3.1.4: version "3.13.10" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d" @@ -10220,6 +10372,13 @@ uint8arrays@3.0.0, uint8arrays@^3.0.0: dependencies: multiformats "^9.4.2" +uint8arrays@^2.1.3: + version "2.1.10" + resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.10.tgz#34d023c843a327c676e48576295ca373c56e286a" + integrity sha512-Q9/hhJa2836nQfEJSZTmr+pg9+cDJS9XEAp7N2Vg5MzL3bK/mkMVfjscRGYruP9jNda6MAdf4QD/y78gSzkp6A== + dependencies: + multiformats "^9.4.2" + uint8arrays@^2.1.5: version "2.1.5" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-2.1.5.tgz#9e6e6377a9463d5eba4620a3f0450f7eb389a351" @@ -10386,6 +10545,20 @@ vary@^1, vary@~1.1.2: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= +vc-js@^0.6.4: + version "0.6.4" + resolved "https://registry.yarnpkg.com/vc-js/-/vc-js-0.6.4.tgz#49f408c590260d28c25a3ca4e30ece0de61c3ae1" + integrity sha512-ogwYys94k8mFyFZEn1Ru3nIA5Z/9C4iCU4grNWUYOYVWldcsn6UDp6erYFbaSkilzv7RK3cQu5N8prkxfHpZwA== + dependencies: + commander "^2.20.3" + credentials-context "^1.0.0" + debug "^4.1.1" + fs-extra "^8.1.0" + get-stdin "^7.0.0" + jsonld "^2.0.2" + jsonld-signatures "^5.0.0" + supports-color "^7.1.0" + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -10530,14 +10703,6 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -wordwrapjs@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/wordwrapjs/-/wordwrapjs-3.0.0.tgz#c94c372894cadc6feb1a66bff64e1d9af92c5d1e" - integrity sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw== - dependencies: - reduce-flatten "^1.0.1" - typical "^2.6.1" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -10696,6 +10861,13 @@ yargs-parser@20.x, yargs-parser@^20.2.2, yargs-parser@^20.2.3: resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^10.0.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== + dependencies: + camelcase "^4.1.0" + yargs-parser@^13.1.0: version "13.1.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" From e449f03cdcf678163469817129a31ff39c367108 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Tue, 23 Nov 2021 17:01:09 +0100 Subject: [PATCH 46/48] test(utils): add some tests for the utils package --- .../src/__tests__/credential-utils.test.ts | 238 ++++++++++++++++++ packages/utils/src/__tests__/utils.test.ts | 23 ++ packages/utils/src/credential-utils.ts | 28 ++- 3 files changed, 281 insertions(+), 8 deletions(-) create mode 100644 packages/utils/src/__tests__/credential-utils.test.ts create mode 100644 packages/utils/src/__tests__/utils.test.ts diff --git a/packages/utils/src/__tests__/credential-utils.test.ts b/packages/utils/src/__tests__/credential-utils.test.ts new file mode 100644 index 000000000..c5bfaec03 --- /dev/null +++ b/packages/utils/src/__tests__/credential-utils.test.ts @@ -0,0 +1,238 @@ +import { + computeEntryHash, + decodeCredentialToObject, + decodePresentationToObject, extractIssuer, + processEntryToArray, +} from '../credential-utils' + +describe('@veramo/utils credential utils', () => { + it('processEntryToArray', () => { + expect(processEntryToArray('a')).toEqual(['a']) + expect(processEntryToArray(['a'])).toEqual(['a']) + expect(processEntryToArray(['a', 'b'])).toEqual(['a', 'b']) + expect(processEntryToArray(['a', 'b'])).toEqual(['a', 'b']) + expect(processEntryToArray(undefined)).toEqual([]) + expect(processEntryToArray(null)).toEqual([]) + expect(processEntryToArray(undefined, 'start')).toEqual(['start']) + expect(processEntryToArray(null, 'start')).toEqual(['start']) + expect(processEntryToArray(['a', 'start'], 'start')).toEqual(['start', 'a']) + expect(processEntryToArray(['a', 'start', null, undefined, 'b', 'a'] as any, 'start')).toEqual([ + 'start', + 'a', + 'b', + ]) + }) + + it('decodeCredentialToObject', () => { + const jwt = + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NkstUiJ9.eyJpYXQiOjE1ODI2MTk2NzYsInN1YiI6ImRpZDpldGhyOnJpbmtlYnk6MHgzYzM1N2JhNDU4OTMzYTE5YzFkZjFjN2Y2YjQ3M2IzMzAyYmJiZTYxIiwidmMiOnsiQGNvbnRleHQiOlsiaHR0cHM6Ly93d3cudzMub3JnLzIwMTgvY3JlZGVudGlhbHMvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJuYW1lIjoiQWxpY2UifX0sImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgzYzM1N2JhNDU4OTMzYTE5YzFkZjFjN2Y2YjQ3M2IzMzAyYmJiZTYxIn0.IGF1LFOc4_PcGVeq7Yw7OGz4Gj7xXZK6p8bP9CSEIXz7mNFPM0v0nuevTZ47a0I8XgLfCFNkUrIIscjH8MFx_wE' + const normalizedCred = { + credentialSubject: { + name: 'Alice', + id: 'did:ethr:rinkeby:0x3c357ba458933a19c1df1c7f6b473b3302bbbe61', + }, + issuer: { id: 'did:ethr:rinkeby:0x3c357ba458933a19c1df1c7f6b473b3302bbbe61' }, + type: ['VerifiableCredential'], + '@context': ['https://www.w3.org/2018/credentials/v1'], + issuanceDate: '2020-02-25T08:34:36.000Z', + proof: { + type: 'JwtProof2020', + jwt, + }, + } + const ldCred = { + issuer: { id: 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn' }, + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1'], + type: ['VerifiableCredential', 'Profile'], + issuanceDate: '2021-11-23T15:06:12.820Z', + credentialSubject: { + id: 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn', + name: 'Martin, the great', + }, + proof: { + type: 'Ed25519Signature2018', + created: '2021-11-23T15:06:12Z', + verificationMethod: + 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn#z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wKMmMZNdIgL_19HYJgpRL9SeKVzYT85S-ZyVdF3IMiaiL8nhX8i48D82TQtuQlTT960h_TOQ18fQFula6QxADA', + }, + } + expect(decodeCredentialToObject(jwt)).toEqual(normalizedCred) + expect(decodeCredentialToObject(normalizedCred)).toEqual(normalizedCred) + expect(decodeCredentialToObject(ldCred)).toEqual(ldCred) + }) + + it('decodePresentationToObject', () => { + const cred1 = { + issuer: { id: 'did:ethr:0x0301240a4851f45b568809baf35921f8d78ca966738a1a0a69e693fbbd232ff080' }, + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://veramo.io/contexts/profile/v1', + 'https://identity.foundation/EcdsaSecp256k1RecoverySignature2020/lds-ecdsa-secp256k1-recovery2020-0.0.jsonld', + ], + type: ['VerifiableCredential', 'Profile'], + issuanceDate: '2021-11-23T15:09:43.891Z', + credentialSubject: { name: 'Martin, the great' }, + proof: { + type: 'EcdsaSecp256k1RecoverySignature2020', + created: '2021-11-23T15:09:44Z', + verificationMethod: + 'did:ethr:0x0301240a4851f45b568809baf35921f8d78ca966738a1a0a69e693fbbd232ff080#controller', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFUzI1NkstUiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..peNXS6DaNsCNfz8rnr_BJ49sF4yUugKqNZrcOfdX0qlPSw4iFM9Iw1e6bhmav7N2OCfauI6uCFQEpKfTc5Jm-wA', + }, + } + const cred2 = + 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ' + const cred2Expanded = { + credentialSubject: { you: 'Rock', id: 'did:web:example.com' }, + issuer: { id: 'did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7' }, + type: ['VerifiableCredential'], + '@context': ['https://www.w3.org/2018/credentials/v1'], + issuanceDate: '2021-11-23T15:11:57.000Z', + proof: { + type: 'JwtProof2020', + jwt: 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ', + }, + } + + const presentation1 = { + holder: 'did:ethr:0x0301240a4851f45b568809baf35921f8d78ca966738a1a0a69e693fbbd232ff080', + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + verifiableCredential: [cred1, cred2], + proof: { + type: 'EcdsaSecp256k1RecoverySignature2020', + created: '2021-11-23T15:09:55Z', + verificationMethod: + 'did:ethr:0x0301240a4851f45b568809baf35921f8d78ca966738a1a0a69e693fbbd232ff080#controller', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFUzI1NkstUiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..t34HTHSqdK0YLx_BW8VTKR6ON7QXdJbyhlPZmKH2HiOnmk0W05SuslwSPOU9NjjbtGU-IMvUU2pJVyzN6Pm2dQE', + }, + } + + const presentation1Expanded = { + holder: 'did:ethr:0x0301240a4851f45b568809baf35921f8d78ca966738a1a0a69e693fbbd232ff080', + '@context': ['https://www.w3.org/2018/credentials/v1'], + type: ['VerifiablePresentation'], + verifiableCredential: [cred1, cred2Expanded], + proof: { + type: 'EcdsaSecp256k1RecoverySignature2020', + created: '2021-11-23T15:09:55Z', + verificationMethod: + 'did:ethr:0x0301240a4851f45b568809baf35921f8d78ca966738a1a0a69e693fbbd232ff080#controller', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFUzI1NkstUiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..t34HTHSqdK0YLx_BW8VTKR6ON7QXdJbyhlPZmKH2HiOnmk0W05SuslwSPOU9NjjbtGU-IMvUU2pJVyzN6Pm2dQE', + }, + } + + const presentation2 = + 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vZXhhbXBsZS5jb20vMS8yLzMiXSwidHlwZSI6WyJWZXJpZmlhYmxlUHJlc2VudGF0aW9uIiwiQ3VzdG9tIl0sInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlV6STFOa3NpTENKMGVYQWlPaUpLVjFRaWZRLmV5SjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0pkTENKMGVYQmxJanBiSWxabGNtbG1hV0ZpYkdWRGNtVmtaVzUwYVdGc0lsMHNJbU55WldSbGJuUnBZV3hUZFdKcVpXTjBJanA3SW5sdmRTSTZJbEp2WTJzaWZYMHNJbk4xWWlJNkltUnBaRHAzWldJNlpYaGhiWEJzWlM1amIyMGlMQ0p1WW1ZaU9qRTJNemMyT0RBek1UY3NJbWx6Y3lJNkltUnBaRHBsZEdoeU9uSnBibXRsWW5rNk1IZ3dNbVkyTkRrMU5XUmtNRFZrWkdVMU5Ua3hNR1l6TnpsbFl6VTNPRFZsWldVd09HSmlaVFpsTlRCa1pHRmxObUpoWTJVd05UazJabVE0WkRRMk1ERTRaamNpZlEudWpNNHpObThoNS1DZzAxdmg0a2FfN05tZHdZbDhIQWpPOTBYanhZWXdTYTAtNHJxek01bmR0LU9FNnZTNnkwZ1B3aHdsUVdIbUR4ZzRYN09UekNVb1EiXX0sIm5iZiI6MTYzNzY4MDMxNywiaXNzIjoiZGlkOmV0aHI6cmlua2VieToweDAyZjY0OTU1ZGQwNWRkZTU1OTEwZjM3OWVjNTc4NWVlZTA4YmJlNmU1MGRkYWU2YmFjZTA1OTZmZDhkNDYwMThmNyIsImF1ZCI6W119.hlVtPgzHZhLXQSIU3_RTpabN30t-QJL2-KqELI6G0Dkk8_gTyQbJVIZX3OsaJUV4K99-MPL_oyMXzBXWba_iBQ' + + const presentation2Expanded = { + verifiableCredential: [ + { + credentialSubject: { you: 'Rock', id: 'did:web:example.com' }, + issuer: { + id: 'did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7', + }, + type: ['VerifiableCredential'], + '@context': ['https://www.w3.org/2018/credentials/v1'], + issuanceDate: '2021-11-23T15:11:57.000Z', + proof: { + type: 'JwtProof2020', + jwt: 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ', + }, + }, + ], + holder: 'did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7', + verifier: [], + type: ['VerifiablePresentation', 'Custom'], + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://example.com/1/2/3'], + issuanceDate: '2021-11-23T15:11:57.000Z', + proof: { + type: 'JwtProof2020', + jwt: 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2cCI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vZXhhbXBsZS5jb20vMS8yLzMiXSwidHlwZSI6WyJWZXJpZmlhYmxlUHJlc2VudGF0aW9uIiwiQ3VzdG9tIl0sInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlV6STFOa3NpTENKMGVYQWlPaUpLVjFRaWZRLmV5SjJZeUk2ZXlKQVkyOXVkR1Y0ZENJNld5Sm9kSFJ3Y3pvdkwzZDNkeTUzTXk1dmNtY3ZNakF4T0M5amNtVmtaVzUwYVdGc2N5OTJNU0pkTENKMGVYQmxJanBiSWxabGNtbG1hV0ZpYkdWRGNtVmtaVzUwYVdGc0lsMHNJbU55WldSbGJuUnBZV3hUZFdKcVpXTjBJanA3SW5sdmRTSTZJbEp2WTJzaWZYMHNJbk4xWWlJNkltUnBaRHAzWldJNlpYaGhiWEJzWlM1amIyMGlMQ0p1WW1ZaU9qRTJNemMyT0RBek1UY3NJbWx6Y3lJNkltUnBaRHBsZEdoeU9uSnBibXRsWW5rNk1IZ3dNbVkyTkRrMU5XUmtNRFZrWkdVMU5Ua3hNR1l6TnpsbFl6VTNPRFZsWldVd09HSmlaVFpsTlRCa1pHRmxObUpoWTJVd05UazJabVE0WkRRMk1ERTRaamNpZlEudWpNNHpObThoNS1DZzAxdmg0a2FfN05tZHdZbDhIQWpPOTBYanhZWXdTYTAtNHJxek01bmR0LU9FNnZTNnkwZ1B3aHdsUVdIbUR4ZzRYN09UekNVb1EiXX0sIm5iZiI6MTYzNzY4MDMxNywiaXNzIjoiZGlkOmV0aHI6cmlua2VieToweDAyZjY0OTU1ZGQwNWRkZTU1OTEwZjM3OWVjNTc4NWVlZTA4YmJlNmU1MGRkYWU2YmFjZTA1OTZmZDhkNDYwMThmNyIsImF1ZCI6W119.hlVtPgzHZhLXQSIU3_RTpabN30t-QJL2-KqELI6G0Dkk8_gTyQbJVIZX3OsaJUV4K99-MPL_oyMXzBXWba_iBQ', + }, + } + expect(decodePresentationToObject(presentation1)).toEqual(presentation1Expanded) + expect(decodePresentationToObject(presentation1Expanded)).toEqual(presentation1Expanded) + expect(decodePresentationToObject(presentation2)).toEqual(presentation2Expanded) + expect(decodePresentationToObject(presentation2Expanded)).toEqual(presentation2Expanded) + }) + + it('computeEntryHash for JWT', () => { + const jwt = 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ' + const expandedCred = { + credentialSubject: { you: 'Rock', id: 'did:web:example.com' }, + issuer: { id: 'did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7' }, + type: ['VerifiableCredential'], + '@context': ['https://www.w3.org/2018/credentials/v1'], + issuanceDate: '2021-11-23T15:11:57.000Z', + proof: { + type: 'JwtProof2020', + jwt + }, + } + const serializedCred = `{"credentialSubject":{"you":"Rock","id":"did:web:example.com"},"issuer":{"id":"did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7"},"type":["VerifiableCredential"],"@context":["https://www.w3.org/2018/credentials/v1"],"issuanceDate":"2021-11-23T15:11:57.000Z","proof":{"type":"JwtProof2020","jwt":"${jwt}"}}` + expect(computeEntryHash(expandedCred)).toEqual(computeEntryHash(serializedCred)) + expect(computeEntryHash(jwt)).toEqual(computeEntryHash(serializedCred)) + expect(computeEntryHash(serializedCred)).toEqual('452f0fb4b876e22867585ee15a6aabb7a6f9ccccf6a2ee664e9f7618737792d64b219fef0792b9d73f3ff756a265083526ecb7313ae4972ef6290b600cacbe88') + }) + + it('computeEntryHash for LD', () => { + const expandedCred = { + issuer: { id: 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn' }, + '@context': ['https://www.w3.org/2018/credentials/v1', 'https://veramo.io/contexts/profile/v1'], + type: ['VerifiableCredential', 'Profile'], + issuanceDate: '2021-11-23T15:06:12.820Z', + credentialSubject: { + id: 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn', + name: 'Martin, the great', + }, + proof: { + type: 'Ed25519Signature2018', + created: '2021-11-23T15:06:12Z', + verificationMethod: + 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn#z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn', + proofPurpose: 'assertionMethod', + jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wKMmMZNdIgL_19HYJgpRL9SeKVzYT85S-ZyVdF3IMiaiL8nhX8i48D82TQtuQlTT960h_TOQ18fQFula6QxADA', + }, + } + const serializedCred = '{"issuer":{"id":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn"},"@context":["https://www.w3.org/2018/credentials/v1","https://veramo.io/contexts/profile/v1"],"type":["VerifiableCredential","Profile"],"issuanceDate":"2021-11-23T15:06:12.820Z","credentialSubject":{"id":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn","name":"Martin, the great"},"proof":{"type":"Ed25519Signature2018","created":"2021-11-23T15:06:12Z","verificationMethod":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn#z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn","proofPurpose":"assertionMethod","jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wKMmMZNdIgL_19HYJgpRL9SeKVzYT85S-ZyVdF3IMiaiL8nhX8i48D82TQtuQlTT960h_TOQ18fQFula6QxADA"}}' + expect(computeEntryHash(expandedCred)).toEqual(computeEntryHash(serializedCred)) + expect(computeEntryHash(serializedCred)).toEqual('357436ca94682f2872b26c35a64d52c8e12dfbf86561a8f219cb395482f5978758fb577c927874cdb01189853054433a07eca81a4b3a999be12290021eb9bcbb') + }) + + it('extractIssuer', () => { + const ldCred1 = { + issuer: { id: 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn' }, + proof: { + type: 'fake', + }, + } + const ldCred2 = { + issuer: 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn', + proof: { + type: 'fake', + }, + } + const ldPres = { + holder: 'did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn', + proof: { + type: 'fake', + }, + } + const jwt = 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ' + expect(extractIssuer(ldCred1)).toEqual('did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn') + expect(extractIssuer(ldCred2)).toEqual('did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn') + expect(extractIssuer(ldPres)).toEqual('did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn') + expect(extractIssuer(jwt)).toEqual('did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7') + expect(extractIssuer('')).toEqual('') + expect(extractIssuer('header.payload.signature')).toEqual('') + expect(extractIssuer({} as any)).toEqual('') + expect(extractIssuer(null)).toEqual('') + expect(extractIssuer(undefined)).toEqual('') + }) +}) diff --git a/packages/utils/src/__tests__/utils.test.ts b/packages/utils/src/__tests__/utils.test.ts new file mode 100644 index 000000000..28eeb57f0 --- /dev/null +++ b/packages/utils/src/__tests__/utils.test.ts @@ -0,0 +1,23 @@ +import { asArray, isDefined } from '../type-utils' + +describe('@veramo/utils type utils', () => { + it('isDefined should return correct results', () => { + const a = { + b: 'defined', + } + const arr = [null, undefined, 'a', 'b'] + expect(isDefined(undefined)).toBe(false) + expect(isDefined(null)).toBe(false) + expect(isDefined(false)).toBe(true) + expect(isDefined(a.b)).toBe(true) + expect(isDefined((a as any).c)).toBe(false) + expect(arr.filter(isDefined)).toEqual(['a', 'b']) + }) + + it('asArray should return an array', () => { + expect(asArray('a')).toEqual(['a']) + expect(asArray(['a', 'b'])).toEqual(['a', 'b']) + expect(asArray(undefined)).toEqual([]) + expect(asArray(null)).toEqual([]) + }) +}) diff --git a/packages/utils/src/credential-utils.ts b/packages/utils/src/credential-utils.ts index 4366fc6a8..759edc724 100644 --- a/packages/utils/src/credential-utils.ts +++ b/packages/utils/src/credential-utils.ts @@ -78,14 +78,20 @@ export function decodePresentationToObject(input: W3CVerifiablePresentation): Ve export function computeEntryHash( input: W3CVerifiableCredential | W3CVerifiablePresentation | IMessage, ): string { + let hashable: string if (typeof input === 'string') { - // TODO: try to parse as JSON before assuming it's a JWT? - return blake2bHex(input) + try { + const cred = JSON.parse(input) + hashable = cred?.proof?.jwt || input + } catch (e) { + hashable = input + } } else if ((input)?.proof?.jwt) { - return blake2bHex((input).proof.jwt) + hashable = (input).proof.jwt } else { - return blake2bHex(JSON.stringify(input)) + hashable = JSON.stringify(input) } + return blake2bHex(hashable) } /** @@ -97,12 +103,18 @@ export function computeEntryHash( * @beta This API may change without prior notice. */ export function extractIssuer( - input: W3CVerifiableCredential | W3CVerifiablePresentation | CredentialPayload | PresentationPayload, + input?: W3CVerifiableCredential | W3CVerifiablePresentation | CredentialPayload | PresentationPayload | null, ): string { - if (typeof input === 'string') { + if (!isDefined(input)) { + return '' + } else if (typeof input === 'string') { // JWT - const { payload } = decodeJWT(input) - return payload.iss || '' + try { + const { payload } = decodeJWT(input) + return payload.iss || '' + } catch (e: any) { + return '' + } } else { // JSON let iss: IssuerType From 1071b02f1fba06a71a19c6fdd57655864b6abe98 Mon Sep 17 00:00:00 2001 From: Mircea Nistor Date: Tue, 23 Nov 2021 17:02:21 +0100 Subject: [PATCH 47/48] style(utils): reformat code --- .prettierignore | 2 +- package.json | 2 +- packages/cli/src/presentation.ts | 5 +--- .../src/__tests__/credential-utils.test.ts | 26 +++++++++++++------ packages/utils/src/credential-utils.ts | 7 ++++- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/.prettierignore b/.prettierignore index d54e58679..500f9d12a 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,6 @@ coverage examples -build/**/* +**/build docs report *.json diff --git a/package.json b/package.json index 436c3513d..36ae7ee2f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "test": "jest --config=jest.json --coverage=false", "test:watch": "yarn test --watch --verbose", "veramo": "./packages/cli/bin/veramo.js", - "prettier": "prettier --write '{packages,__tests__, !build}/**/*.{ts,js,json,md,yml}'", + "prettier": "prettier --write '{packages,__tests__}/**/*.{ts,js,json,md,yml}'", "build-clean": "rimraf ./packages/*/build ./packages/*/api ./packages/*/node_modules ./packages/*/tsconfig.tsbuildinfo && jest --clearCache", "publish:latest": "lerna publish --conventional-commits --include-merged-tags --create-release github --yes --dist-tag latest --registry https://registry.npmjs.org/:_authToken=${NPM_TOKEN}", "publish:next": "lerna publish --conventional-prerelease --force-publish --canary --no-git-tag-version --include-merged-tags --preid next --pre-dist-tag next --yes --registry https://registry.npmjs.org/:_authToken=${NPM_TOKEN}", diff --git a/packages/cli/src/presentation.ts b/packages/cli/src/presentation.ts index 3c8431169..f7bc82ec1 100644 --- a/packages/cli/src/presentation.ts +++ b/packages/cli/src/presentation.ts @@ -163,10 +163,7 @@ presentation .option('-c, --challenge ', 'Optional. Specify a challenge that the presentation should match.') .option('-d, --domain ', 'Optional. Specify a domain that the presentation should match.') .option('-f, --filename ', 'Optional. Read the presentation from a file instead of stdin') - .option( - '-r, --raw ', - 'Optional. Presentation as a parameter string instead of a file or stdin.', - ) + .option('-r, --raw ', 'Optional. Presentation as a parameter string instead of a file or stdin.') .action(async (options) => { const agent = getAgent(program.opts().config) let raw: string = '' diff --git a/packages/utils/src/__tests__/credential-utils.test.ts b/packages/utils/src/__tests__/credential-utils.test.ts index c5bfaec03..b6909a1f5 100644 --- a/packages/utils/src/__tests__/credential-utils.test.ts +++ b/packages/utils/src/__tests__/credential-utils.test.ts @@ -1,7 +1,8 @@ import { computeEntryHash, decodeCredentialToObject, - decodePresentationToObject, extractIssuer, + decodePresentationToObject, + extractIssuer, processEntryToArray, } from '../credential-utils' @@ -163,7 +164,8 @@ describe('@veramo/utils credential utils', () => { }) it('computeEntryHash for JWT', () => { - const jwt = 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ' + const jwt = + 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ' const expandedCred = { credentialSubject: { you: 'Rock', id: 'did:web:example.com' }, issuer: { id: 'did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7' }, @@ -172,13 +174,15 @@ describe('@veramo/utils credential utils', () => { issuanceDate: '2021-11-23T15:11:57.000Z', proof: { type: 'JwtProof2020', - jwt + jwt, }, } const serializedCred = `{"credentialSubject":{"you":"Rock","id":"did:web:example.com"},"issuer":{"id":"did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7"},"type":["VerifiableCredential"],"@context":["https://www.w3.org/2018/credentials/v1"],"issuanceDate":"2021-11-23T15:11:57.000Z","proof":{"type":"JwtProof2020","jwt":"${jwt}"}}` expect(computeEntryHash(expandedCred)).toEqual(computeEntryHash(serializedCred)) expect(computeEntryHash(jwt)).toEqual(computeEntryHash(serializedCred)) - expect(computeEntryHash(serializedCred)).toEqual('452f0fb4b876e22867585ee15a6aabb7a6f9ccccf6a2ee664e9f7618737792d64b219fef0792b9d73f3ff756a265083526ecb7313ae4972ef6290b600cacbe88') + expect(computeEntryHash(serializedCred)).toEqual( + '452f0fb4b876e22867585ee15a6aabb7a6f9ccccf6a2ee664e9f7618737792d64b219fef0792b9d73f3ff756a265083526ecb7313ae4972ef6290b600cacbe88', + ) }) it('computeEntryHash for LD', () => { @@ -200,9 +204,12 @@ describe('@veramo/utils credential utils', () => { jws: 'eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wKMmMZNdIgL_19HYJgpRL9SeKVzYT85S-ZyVdF3IMiaiL8nhX8i48D82TQtuQlTT960h_TOQ18fQFula6QxADA', }, } - const serializedCred = '{"issuer":{"id":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn"},"@context":["https://www.w3.org/2018/credentials/v1","https://veramo.io/contexts/profile/v1"],"type":["VerifiableCredential","Profile"],"issuanceDate":"2021-11-23T15:06:12.820Z","credentialSubject":{"id":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn","name":"Martin, the great"},"proof":{"type":"Ed25519Signature2018","created":"2021-11-23T15:06:12Z","verificationMethod":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn#z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn","proofPurpose":"assertionMethod","jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wKMmMZNdIgL_19HYJgpRL9SeKVzYT85S-ZyVdF3IMiaiL8nhX8i48D82TQtuQlTT960h_TOQ18fQFula6QxADA"}}' + const serializedCred = + '{"issuer":{"id":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn"},"@context":["https://www.w3.org/2018/credentials/v1","https://veramo.io/contexts/profile/v1"],"type":["VerifiableCredential","Profile"],"issuanceDate":"2021-11-23T15:06:12.820Z","credentialSubject":{"id":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn","name":"Martin, the great"},"proof":{"type":"Ed25519Signature2018","created":"2021-11-23T15:06:12Z","verificationMethod":"did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn#z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn","proofPurpose":"assertionMethod","jws":"eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..wKMmMZNdIgL_19HYJgpRL9SeKVzYT85S-ZyVdF3IMiaiL8nhX8i48D82TQtuQlTT960h_TOQ18fQFula6QxADA"}}' expect(computeEntryHash(expandedCred)).toEqual(computeEntryHash(serializedCred)) - expect(computeEntryHash(serializedCred)).toEqual('357436ca94682f2872b26c35a64d52c8e12dfbf86561a8f219cb395482f5978758fb577c927874cdb01189853054433a07eca81a4b3a999be12290021eb9bcbb') + expect(computeEntryHash(serializedCred)).toEqual( + '357436ca94682f2872b26c35a64d52c8e12dfbf86561a8f219cb395482f5978758fb577c927874cdb01189853054433a07eca81a4b3a999be12290021eb9bcbb', + ) }) it('extractIssuer', () => { @@ -224,11 +231,14 @@ describe('@veramo/utils credential utils', () => { type: 'fake', }, } - const jwt = 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ' + const jwt = + 'eyJhbGciOiJFUzI1NksiLCJ0eXAiOiJKV1QifQ.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSJdLCJ0eXBlIjpbIlZlcmlmaWFibGVDcmVkZW50aWFsIl0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InlvdSI6IlJvY2sifX0sInN1YiI6ImRpZDp3ZWI6ZXhhbXBsZS5jb20iLCJuYmYiOjE2Mzc2ODAzMTcsImlzcyI6ImRpZDpldGhyOnJpbmtlYnk6MHgwMmY2NDk1NWRkMDVkZGU1NTkxMGYzNzllYzU3ODVlZWUwOGJiZTZlNTBkZGFlNmJhY2UwNTk2ZmQ4ZDQ2MDE4ZjcifQ.ujM4zNm8h5-Cg01vh4ka_7NmdwYl8HAjO90XjxYYwSa0-4rqzM5ndt-OE6vS6y0gPwhwlQWHmDxg4X7OTzCUoQ' expect(extractIssuer(ldCred1)).toEqual('did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn') expect(extractIssuer(ldCred2)).toEqual('did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn') expect(extractIssuer(ldPres)).toEqual('did:key:z6MkvGFkoFarw7pXRBkKqZKwDcc2L3U4AZC1RtBiceicUHqn') - expect(extractIssuer(jwt)).toEqual('did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7') + expect(extractIssuer(jwt)).toEqual( + 'did:ethr:rinkeby:0x02f64955dd05dde55910f379ec5785eee08bbe6e50ddae6bace0596fd8d46018f7', + ) expect(extractIssuer('')).toEqual('') expect(extractIssuer('header.payload.signature')).toEqual('') expect(extractIssuer({} as any)).toEqual('') diff --git a/packages/utils/src/credential-utils.ts b/packages/utils/src/credential-utils.ts index 759edc724..a06cc769a 100644 --- a/packages/utils/src/credential-utils.ts +++ b/packages/utils/src/credential-utils.ts @@ -103,7 +103,12 @@ export function computeEntryHash( * @beta This API may change without prior notice. */ export function extractIssuer( - input?: W3CVerifiableCredential | W3CVerifiablePresentation | CredentialPayload | PresentationPayload | null, + input?: + | W3CVerifiableCredential + | W3CVerifiablePresentation + | CredentialPayload + | PresentationPayload + | null, ): string { if (!isDefined(input)) { return '' From 790fb8c22395ff452d3a2da7cd74a0af495e1f7d Mon Sep 17 00:00:00 2001 From: Simonas Karuzas Date: Wed, 24 Nov 2021 11:05:17 +0200 Subject: [PATCH 48/48] feat(cli): credential UX improvements --- packages/cli/src/credential.ts | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/credential.ts b/packages/cli/src/credential.ts index 23bc61500..c6dcfefa7 100644 --- a/packages/cli/src/credential.ts +++ b/packages/cli/src/credential.ts @@ -13,6 +13,7 @@ credential .command('create', { isDefault: true }) .description('Create W3C Verifiable Credential') .option('-s, --send', 'Send') + .option('-j, --json', 'Output in JSON') .option('-q, --qrcode', 'Show qrcode') .action(async (cmd) => { const agent = getAgent(program.opts().config) @@ -142,7 +143,11 @@ credential if (cmd.qrcode) { qrcode.generate(verifiableCredential.proof.jwt) } else { - console.dir(verifiableCredential, { depth: 10 }) + if (cmd.json) { + console.log(JSON.stringify(verifiableCredential, null, 2)) + } else { + console.dir(verifiableCredential, { depth: 10 }) + } } }) @@ -172,10 +177,14 @@ credential }, } as any } - const result = await agent.verifyCredential({ credential: credentialAsJSON }) - if (result === true) { - console.log('Credential was verified successfully.') - } else { - console.error('Credential could not be verified.') + try { + const result = await agent.verifyCredential({ credential: credentialAsJSON }) + if (result === true) { + console.log('Credential was verified successfully.') + } else { + console.error('Credential could not be verified.') + } + } catch (e) { + console.error(e.message) } })