From 5b7d3fad7d2ada4954f8020a6474df13d9fe51dc Mon Sep 17 00:00:00 2001 From: nickreynolds Date: Thu, 11 Jul 2024 07:31:16 -0400 Subject: [PATCH] feat(credential-w3c): remove hardcoded proof formats (#1395) BREAKING CHANGE: The credential plugins specializing in JSON-LD / EIP712 are no longer top level veramo plugins but are now managed by the `@veramo/credential-w3c` plugin which will be able to use multiple proof formats and multiplex accordingly. The constructor for the `CredentialPlugin` has changed to accept different implementations of proof formats. --- __tests__/localAgent.test.ts | 69 +- __tests__/localJsonStoreAgent.test.ts | 70 ++- __tests__/localMemoryStoreAgent.test.ts | 66 +- __tests__/restAgent.test.ts | 58 +- __tests__/shared/credentialPluginTests.ts | 2 +- __tests__/shared/verifiableDataEIP712.ts | 11 +- __tests__/utils/json-file-store.ts | 6 +- packages/cli/default/default.yml | 122 ++-- packages/cli/package.json | 3 +- packages/core-types/src/plugin.schema.ts | 41 +- .../core-types/src/types/ICredentialIssuer.ts | 37 +- .../src/types/ICredentialVerifier.ts | 13 +- .../core-types/src/types/vc-data-model.ts | 4 +- packages/credential-eip712/package.json | 10 +- ...lEIP712.ts => CredentialProviderEIP712.ts} | 125 ++-- packages/credential-eip712/src/index.ts | 4 +- .../credential-eip712/src/plugin.schema.ts | 432 ------------- .../src/types/ICredentialEIP712.ts | 196 ------ packages/credential-jwt/CHANGELOG.md | 4 + packages/credential-jwt/LICENSE | 201 ++++++ packages/credential-jwt/api-extractor.json | 18 + packages/credential-jwt/package.json | 54 ++ .../__tests__/issue-verify-flow-jwt.test.ts | 127 ++++ .../src/agent/CredentialProviderJWT.ts | 364 +++++++++++ packages/credential-jwt/src/index.ts | 1 + packages/credential-jwt/tsconfig.json | 15 + packages/credential-ld/package.json | 10 +- ...ion-handler.ts => CredentialProviderLD.ts} | 101 +-- .../issue-verify-ed25519-2020.test.ts | 244 ++++---- ...w.test.ts => issue-verify-flow-ld.test.ts} | 13 +- packages/credential-ld/src/index.ts | 7 +- .../credential-ld/src/ld-credential-module.ts | 7 +- .../credential-ld/src/ld-default-contexts.ts | 2 +- packages/credential-ld/src/ld-suites.ts | 2 + packages/credential-ld/src/plugin.schema.ts | 476 -------------- .../EcdsaSecp256k1RecoverySignature2020.ts | 7 +- .../src/suites/Ed25519Signature2018.ts | 5 + .../src/suites/Ed25519Signature2020.ts | 6 +- .../src/suites/JsonWebSignature2020.ts | 6 +- packages/credential-ld/src/types.ts | 265 -------- .../src/__tests__/action-handler.test.ts | 197 ++---- ....test.ts => issue-verify-flow-w3c.test.ts} | 20 +- .../src/__tests__/message-handler.test.ts | 1 + .../src/abstract-credential-provider.ts | 116 ++++ packages/credential-w3c/src/action-handler.ts | 587 ++++-------------- packages/credential-w3c/src/index.ts | 2 + packages/credential-w3c/tsconfig.json | 18 +- packages/did-comm/src/plugin.schema.ts | 3 + packages/mediation-manager/README.md | 6 +- .../selective-disclosure/src/plugin.schema.ts | 3 + packages/test-react-app/package.json | 3 +- packages/test-react-app/src/veramo/setup.ts | 26 +- packages/utils/src/did-utils.ts | 16 + pnpm-lock.yaml | 111 +++- 54 files changed, 1821 insertions(+), 2492 deletions(-) rename packages/credential-eip712/src/agent/{CredentialEIP712.ts => CredentialProviderEIP712.ts} (76%) delete mode 100644 packages/credential-eip712/src/plugin.schema.ts delete mode 100644 packages/credential-eip712/src/types/ICredentialEIP712.ts create mode 100644 packages/credential-jwt/CHANGELOG.md create mode 100644 packages/credential-jwt/LICENSE create mode 100644 packages/credential-jwt/api-extractor.json create mode 100644 packages/credential-jwt/package.json create mode 100644 packages/credential-jwt/src/__tests__/issue-verify-flow-jwt.test.ts create mode 100644 packages/credential-jwt/src/agent/CredentialProviderJWT.ts create mode 100644 packages/credential-jwt/src/index.ts create mode 100644 packages/credential-jwt/tsconfig.json rename packages/credential-ld/src/{action-handler.ts => CredentialProviderLD.ts} (74%) rename packages/credential-ld/src/__tests__/{issue-verify-flow.test.ts => issue-verify-flow-ld.test.ts} (95%) delete mode 100644 packages/credential-ld/src/plugin.schema.ts rename packages/credential-w3c/src/__tests__/{issue-verify-flow.test.ts => issue-verify-flow-w3c.test.ts} (95%) create mode 100644 packages/credential-w3c/src/abstract-credential-provider.ts diff --git a/__tests__/localAgent.test.ts b/__tests__/localAgent.test.ts index 696448774..c1c459444 100644 --- a/__tests__/localAgent.test.ts +++ b/__tests__/localAgent.test.ts @@ -25,10 +25,10 @@ import { AliasDiscoveryProvider, DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' import { CredentialPlugin, W3cMessageHandler } from '../packages/credential-w3c/src' -import { CredentialIssuerEIP712, ICredentialIssuerEIP712 } from '../packages/credential-eip712/src' +import { CredentialProviderEIP712 } from '../packages/credential-eip712/src' +import { CredentialProviderJWT } from '../packages/credential-jwt/src' import { - CredentialIssuerLD, - ICredentialIssuerLD, + CredentialProviderLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018, @@ -102,17 +102,15 @@ const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa8 let agent: TAgent< IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialPlugin & - ICredentialIssuerLD & - ICredentialIssuerEIP712 & - ISelectiveDisclosure & - IDIDDiscovery + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialPlugin & + ISelectiveDisclosure & + IDIDDiscovery > let dbConnection: Promise let databaseFile: string @@ -135,19 +133,28 @@ const setup = async (options?: IAgentOptions): Promise => { const { provider, registry } = await createGanacheProvider() const ethersProvider = createEthersProvider() + const eip712 = new CredentialProviderEIP712() + const jwt = new CredentialProviderJWT() + const ld = new CredentialProviderLD({ + contextMaps: [LdDefaultContexts, credential_contexts as any], + suites: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018(), + new VeramoJsonWebSignature2020(), + new VeramoEd25519Signature2020(), + ], + }) agent = createAgent< IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialPlugin & - ICredentialIssuerLD & - ICredentialIssuerEIP712 & - ISelectiveDisclosure & - IDIDDiscovery + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialPlugin & + ISelectiveDisclosure & + IDIDDiscovery >({ ...options, context: { @@ -243,17 +250,7 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm({ transports: [new DIDCommHttpTransport()] }), - new CredentialPlugin(), - new CredentialIssuerEIP712(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts, credential_contexts as any], - suites: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018(), - new VeramoJsonWebSignature2020(), - new VeramoEd25519Signature2020(), - ], - }), + new CredentialPlugin({ issuers: [eip712, jwt, ld] }), new SelectiveDisclosure(), new DIDDiscovery({ providers: [ diff --git a/__tests__/localJsonStoreAgent.test.ts b/__tests__/localJsonStoreAgent.test.ts index d5bfd6be6..5e78119eb 100644 --- a/__tests__/localJsonStoreAgent.test.ts +++ b/__tests__/localJsonStoreAgent.test.ts @@ -23,10 +23,10 @@ import { DIDManager } from '../packages/did-manager/src' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' import { CredentialPlugin, W3cMessageHandler } from '../packages/credential-w3c/src' -import { CredentialIssuerEIP712, ICredentialIssuerEIP712 } from '../packages/credential-eip712/src' +import { CredentialProviderEIP712 } from '../packages/credential-eip712/src' +import { CredentialProviderJWT } from '../packages/credential-jwt/src' import { - CredentialIssuerLD, - ICredentialIssuerLD, + CredentialProviderLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018, @@ -88,16 +88,14 @@ const secretKey = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa8 let agent: TAgent< IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialPlugin & - ICredentialIssuerLD & - ICredentialIssuerEIP712 & - ISelectiveDisclosure + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialPlugin & + ISelectiveDisclosure > let databaseFile: string @@ -108,20 +106,22 @@ const setup = async (options?: IAgentOptions): Promise => { // and `DataStoreJson` if you want to use all the query capabilities of `DataStoreJson` databaseFile = options?.context?.databaseFile || `./tmp/local-database-${Math.random().toPrecision(5)}.json` + + // manually create the tmp directory + await fs.promises.mkdir('./tmp', { recursive: true }) + const jsonFileStore = await JsonFileStore.fromFile(databaseFile) agent = createAgent< IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialPlugin & - ICredentialIssuerLD & - ICredentialIssuerEIP712 & - ISelectiveDisclosure + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialPlugin & + ISelectiveDisclosure >({ ...options, context: { @@ -199,16 +199,20 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm(), - new CredentialPlugin(), - new CredentialIssuerEIP712(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts, credential_contexts as any], - suites: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018(), - new VeramoEd25519Signature2020(), - new VeramoJsonWebSignature2020(), - ], + new CredentialPlugin({ + issuers: [ + new CredentialProviderEIP712(), + new CredentialProviderJWT(), + new CredentialProviderLD({ + contextMaps: [LdDefaultContexts, credential_contexts as any], + suites: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018(), + new VeramoJsonWebSignature2020(), + new VeramoEd25519Signature2020(), + ], + }) + ] }), new SelectiveDisclosure(), ...(options?.plugins || []), diff --git a/__tests__/localMemoryStoreAgent.test.ts b/__tests__/localMemoryStoreAgent.test.ts index ca7758885..3dbcb5d4c 100644 --- a/__tests__/localMemoryStoreAgent.test.ts +++ b/__tests__/localMemoryStoreAgent.test.ts @@ -24,10 +24,10 @@ import { DataSource } from 'typeorm' import { DIDResolverPlugin } from '../packages/did-resolver/src' import { JwtMessageHandler } from '../packages/did-jwt/src' import { CredentialPlugin, W3cMessageHandler } from '../packages/credential-w3c/src' -import { CredentialIssuerEIP712, ICredentialIssuerEIP712 } from '../packages/credential-eip712/src' +import { CredentialProviderEIP712 } from '../packages/credential-eip712/src' +import { CredentialProviderJWT } from '../packages/credential-jwt/src' import { - CredentialIssuerLD, - ICredentialIssuerLD, + CredentialProviderLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018, @@ -80,16 +80,14 @@ const infuraProjectId = '3586660d179141e3801c3895de1c2eba' let agent: TAgent< IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialPlugin & - ICredentialIssuerLD & - ICredentialIssuerEIP712 & - ISelectiveDisclosure + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialPlugin & + ISelectiveDisclosure > let dbConnection: DataSource @@ -106,18 +104,28 @@ const setup = async (options?: IAgentOptions): Promise => { entities: Entities, }) + const eip712 = new CredentialProviderEIP712() + const jwt = new CredentialProviderJWT() + const ld = new CredentialProviderLD({ + contextMaps: [LdDefaultContexts, credential_contexts as any], + suites: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018(), + new VeramoJsonWebSignature2020(), + new VeramoEd25519Signature2020(), + ], + }) + agent = createAgent< IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialPlugin & - ICredentialIssuerLD & - ICredentialIssuerEIP712 & - ISelectiveDisclosure + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialPlugin & + ISelectiveDisclosure >({ ...options, context: { @@ -194,17 +202,7 @@ const setup = async (options?: IAgentOptions): Promise => { ], }), new DIDComm(), - new CredentialPlugin(), - new CredentialIssuerEIP712(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts, credential_contexts as any], - suites: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018(), - new VeramoJsonWebSignature2020(), - new VeramoEd25519Signature2020(), - ], - }), + new CredentialPlugin({ issuers: [eip712, jwt, ld] }), new SelectiveDisclosure(), ...(options?.plugins || []), ], diff --git a/__tests__/restAgent.test.ts b/__tests__/restAgent.test.ts index 3ccac72cf..722cead0a 100644 --- a/__tests__/restAgent.test.ts +++ b/__tests__/restAgent.test.ts @@ -31,10 +31,10 @@ import { ICredentialVerifier, W3cMessageHandler, } from '../packages/credential-w3c/src' -import { CredentialIssuerEIP712, ICredentialIssuerEIP712 } from '../packages/credential-eip712/src' +import { CredentialProviderEIP712 } from '../packages/credential-eip712/src' +import { CredentialProviderJWT } from '../packages/credential-jwt/src' import { - CredentialIssuerLD, - ICredentialIssuerLD, + CredentialProviderLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018, @@ -112,21 +112,21 @@ let dbConnection: Promise let serverAgent: IAgent let restServer: Server + + const getAgent = (options?: IAgentOptions) => createAgent< IDIDManager & - IKeyManager & - IDataStore & - IDataStoreORM & - IResolver & - IMessageHandler & - IDIDComm & - ICredentialIssuer & // import from old package to check compatibility - ICredentialVerifier & - ICredentialIssuerLD & - ICredentialIssuerEIP712 & - ISelectiveDisclosure & - IDIDDiscovery + IKeyManager & + IDataStore & + IDataStoreORM & + IResolver & + IMessageHandler & + IDIDComm & + ICredentialIssuer & // import from old package to check compatibility + ICredentialVerifier & + ISelectiveDisclosure & + IDIDDiscovery >({ ...options, plugins: [ @@ -150,6 +150,18 @@ const setup = async (options?: IAgentOptions): Promise => { entities: Entities, }).initialize() + const eip712 = new CredentialProviderEIP712() + const jwt = new CredentialProviderJWT() + const ld = new CredentialProviderLD({ + contextMaps: [LdDefaultContexts, credential_contexts as any], + suites: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018(), + new VeramoJsonWebSignature2020(), + new VeramoEd25519Signature2020(), + ], + }) + serverAgent = new Agent({ ...options, plugins: [ @@ -227,15 +239,13 @@ const setup = async (options?: IAgentOptions): Promise => { }), new DIDComm({ transports: [new DIDCommHttpTransport()] }), // intentionally use the deprecated name to test compatibility - new CredentialIssuer(), - new CredentialIssuerEIP712(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts, credential_contexts as any], - suites: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018(), - new VeramoJsonWebSignature2020(), - new VeramoEd25519Signature2020(), + new CredentialIssuer({ issuers: [eip712, jwt, ld] }), + new SelectiveDisclosure(), + new DIDDiscovery({ + providers: [ + new AliasDiscoveryProvider(), + new DataStoreDiscoveryProvider(), + new BrokenDiscoveryProvider(), ], }), new SelectiveDisclosure(), diff --git a/__tests__/shared/credentialPluginTests.ts b/__tests__/shared/credentialPluginTests.ts index 1eae9f8ca..39868508a 100644 --- a/__tests__/shared/credentialPluginTests.ts +++ b/__tests__/shared/credentialPluginTests.ts @@ -41,7 +41,7 @@ export default (testContext: { }) const options = await agent.listUsableProofFormats(iid) - expect(options).toEqual(['jwt', 'lds', 'EthereumEip712Signature2021']) + expect(options).toEqual(['EthereumEip712Signature2021', 'jwt', 'lds']) }) it('should list signing options for did:key with X25519 key', async () => { diff --git a/__tests__/shared/verifiableDataEIP712.ts b/__tests__/shared/verifiableDataEIP712.ts index 67afd9991..335b12a50 100644 --- a/__tests__/shared/verifiableDataEIP712.ts +++ b/__tests__/shared/verifiableDataEIP712.ts @@ -10,10 +10,9 @@ import { VerifiableCredential, VerifiablePresentation, } from '../../packages/core-types/src' -import { ICredentialIssuerEIP712 } from '../../packages/credential-eip712/src' type ConfiguredAgent = TAgent< - IDIDManager & ICredentialPlugin & ICredentialIssuerEIP712 & IDataStore & IDataStoreORM + IDIDManager & ICredentialPlugin & IDataStore & IDataStoreORM > export default (testContext: { @@ -72,11 +71,11 @@ export default (testContext: { }) it('should verify credential with EthereumEip712Signature2021 proof type', async () => { - const result = await agent.verifyCredentialEIP712({ + const result = await agent.verifyCredential({ credential: verifiableCredential, }) - expect(result).toEqual(true) + expect(result.verified).toEqual(true) }) it('should create verifiable presentation with EthereumEip712Signature2021 proof type', async () => { @@ -111,11 +110,11 @@ export default (testContext: { it.todo('should throw error when trying to sign presentation with unsuported attributes') it('should verify presentation with EthereumEip712Signature2021 proof type', async () => { - const result = await agent.verifyPresentationEIP712({ + const result = await agent.verifyPresentation({ presentation: verifiablePresentation, }) - expect(result).toEqual(true) + expect(result.verified).toEqual(true) }) }) } diff --git a/__tests__/utils/json-file-store.ts b/__tests__/utils/json-file-store.ts index f63f0af69..b798eb54f 100644 --- a/__tests__/utils/json-file-store.ts +++ b/__tests__/utils/json-file-store.ts @@ -25,8 +25,10 @@ export class JsonFileStore implements VeramoJsonStore { claims: Record presentations: Record messages: Record + private file: fs.PathLike - private constructor(private file: fs.PathLike) { + private constructor(file: fs.PathLike) { + this.file = file this.notifyUpdate = async (oldState: VeramoJsonCache, newState: VeramoJsonCache) => { await this.save(newState) } @@ -53,7 +55,7 @@ export class JsonFileStore implements VeramoJsonStore { } catch (e: any) { cache = {} } - ;({ + ; ({ dids: this.dids, keys: this.keys, credentials: this.credentials, diff --git a/packages/cli/default/default.yml b/packages/cli/default/default.yml index af54f8892..84f95d39f 100644 --- a/packages/cli/default/default.yml +++ b/packages/cli/default/default.yml @@ -73,10 +73,10 @@ dbConnection: synchronize: false migrationsRun: true migrations: - $require: '@veramo/data-store?t=object#migrations' + $require: "@veramo/data-store?t=object#migrations" logging: false entities: - $require: '@veramo/data-store?t=object#Entities' + $require: "@veramo/data-store?t=object#Entities" # Server configuration server: @@ -86,20 +86,20 @@ server: $ref: /constants/port use: # CORS - - - $require: 'cors?t=function#default' + - - $require: "cors?t=function#default" # Add agent to the request object - - - $require: '@veramo/remote-server?t=function#RequestWithAgentRouter' + - - $require: "@veramo/remote-server?t=function#RequestWithAgentRouter" $args: - agent: $ref: /agent # DID Documents - - - $require: '@veramo/remote-server?t=function#WebDidDocRouter' + - - $require: "@veramo/remote-server?t=function#WebDidDocRouter" # API base path - - /messaging - - $require: '@veramo/remote-server?t=function#MessagingRouter' + - $require: "@veramo/remote-server?t=function#MessagingRouter" $args: - metaData: type: DIDComm @@ -107,22 +107,22 @@ server: # API base path - - /agent - - $require: '@veramo/remote-server?t=function#apiKeyAuth' + - $require: "@veramo/remote-server?t=function#apiKeyAuth" $args: - apiKey: test123 - - $require: '@veramo/remote-server?t=function#AgentRouter' + - $require: "@veramo/remote-server?t=function#AgentRouter" $args: - exposedMethods: $ref: /constants/methods # Open API schema - - /open-api.json - - $require: '@veramo/remote-server?t=function#ApiSchemaRouter' + - $require: "@veramo/remote-server?t=function#ApiSchemaRouter" $args: - basePath: :3332/agent securityScheme: bearer apiName: Agent - apiVersion: '1.0.0' + apiVersion: "1.0.0" exposedMethods: $ref: /constants/methods @@ -133,11 +133,11 @@ server: $args: - null - swaggerOptions: - url: '/open-api.json' + url: "/open-api.json" # Execute during server initialization init: - - $require: '@veramo/remote-server?t=function#createDefaultDid' + - $require: "@veramo/remote-server?t=function#createDefaultDid" $args: - agent: $ref: /agent @@ -147,18 +147,18 @@ server: # Message handler plugin messageHandler: - $require: '@veramo/message-handler#MessageHandler' + $require: "@veramo/message-handler#MessageHandler" $args: - messageHandlers: - - $require: '@veramo/did-comm#DIDCommMessageHandler' - - $require: '@veramo/did-comm#TrustPingMessageHandler' - - $require: '@veramo/did-jwt#JwtMessageHandler' - - $require: '@veramo/credential-w3c#W3cMessageHandler' - - $require: '@veramo/selective-disclosure#SdrMessageHandler' + - $require: "@veramo/did-comm#DIDCommMessageHandler" + - $require: "@veramo/did-comm#TrustPingMessageHandler" + - $require: "@veramo/did-jwt#JwtMessageHandler" + - $require: "@veramo/credential-w3c#W3cMessageHandler" + - $require: "@veramo/selective-disclosure#SdrMessageHandler" # DID resolvers didResolver: - $require: '@veramo/did-resolver#DIDResolverPlugin' + $require: "@veramo/did-resolver#DIDResolverPlugin" $args: - ethr: $ref: /ethr-did-resolver @@ -167,11 +167,11 @@ didResolver: key: $ref: /did-key-resolver peer: - $require: '@veramo/did-provider-peer?t=function&p=/peer#getResolver' + $require: "@veramo/did-provider-peer?t=function&p=/peer#getResolver" jwk: - $require: '@veramo/did-provider-jwk?t=function&p=/jwk#getDidJwkResolver' + $require: "@veramo/did-provider-jwk?t=function&p=/jwk#getDidJwkResolver" pkh: - $require: '@veramo/did-provider-pkh?t=function&p=/pkh#getDidPkhResolver' + $require: "@veramo/did-provider-pkh?t=function&p=/pkh#getDidPkhResolver" elem: $ref: /universal-resolver io: @@ -190,44 +190,44 @@ web-did-resolver: $require: web-did-resolver?t=function&p=/web#getResolver universal-resolver: - $require: '@veramo/did-resolver#UniversalResolver' + $require: "@veramo/did-resolver#UniversalResolver" $args: - url: https://dev.uniresolver.io/1.0/identifiers/ did-key-resolver: - $require: '@veramo/did-provider-key?t=function&p=/key#getDidKeyResolver' + $require: "@veramo/did-provider-key?t=function&p=/key#getDidKeyResolver" # Key Manager keyManager: - $require: '@veramo/key-manager#KeyManager' + $require: "@veramo/key-manager#KeyManager" $args: - store: - $require: '@veramo/data-store#KeyStore' + $require: "@veramo/data-store#KeyStore" $args: - $ref: /dbConnection kms: local: - $require: '@veramo/kms-local#KeyManagementSystem' + $require: "@veramo/kms-local#KeyManagementSystem" $args: - - $require: '@veramo/data-store#PrivateKeyStore' + - $require: "@veramo/data-store#PrivateKeyStore" $args: - $ref: /dbConnection - - $require: '@veramo/kms-local#SecretBox' + - $require: "@veramo/kms-local#SecretBox" $args: - $ref: /constants/dbEncryptionKey # DID Manager didManager: - $require: '@veramo/did-manager#DIDManager' + $require: "@veramo/did-manager#DIDManager" $args: - store: - $require: '@veramo/data-store#DIDStore' + $require: "@veramo/data-store#DIDStore" $args: - $ref: /dbConnection defaultProvider: did:ethr:sepolia providers: did:ethr: - $require: '@veramo/did-provider-ethr#EthrDIDProvider' + $require: "@veramo/did-provider-ethr#EthrDIDProvider" $args: - defaultKms: local network: mainnet @@ -235,7 +235,7 @@ didManager: gas: 1000001 ttl: 31104001 did:ethr:sepolia: - $require: '@veramo/did-provider-ethr#EthrDIDProvider' + $require: "@veramo/did-provider-ethr#EthrDIDProvider" $args: - defaultKms: local network: sepolia @@ -243,52 +243,62 @@ didManager: gas: 1000001 ttl: 31104001 did:web: - $require: '@veramo/did-provider-web#WebDIDProvider' + $require: "@veramo/did-provider-web#WebDIDProvider" $args: - defaultKms: local did:key: - $require: '@veramo/did-provider-key#KeyDIDProvider' + $require: "@veramo/did-provider-key#KeyDIDProvider" $args: - defaultKms: local did:jwk: - $require: '@veramo/did-provider-jwk#JwkDIDProvider' + $require: "@veramo/did-provider-jwk#JwkDIDProvider" $args: - defaultKms: local did:peer: - $require: '@veramo/did-provider-peer#PeerDIDProvider' + $require: "@veramo/did-provider-peer#PeerDIDProvider" $args: - defaultKms: local did:pkh: - $require: '@veramo/did-provider-pkh#PkhDIDProvider' + $require: "@veramo/did-provider-pkh#PkhDIDProvider" $args: - defaultKms: local chainId: 1 didDiscovery: - $require: '@veramo/did-discovery#DIDDiscovery' + $require: "@veramo/did-discovery#DIDDiscovery" $args: - providers: - - $require: '@veramo/did-manager#AliasDiscoveryProvider' - - $require: '@veramo/data-store#DataStoreDiscoveryProvider' + - $require: "@veramo/did-manager#AliasDiscoveryProvider" + - $require: "@veramo/data-store#DataStoreDiscoveryProvider" # W3C credentialPlugin -credentialIssuerLD: - $require: '@veramo/credential-ld#CredentialIssuerLD' +CredentialProviderLD: + $require: "@veramo/credential-ld#CredentialProviderLD" $args: - suites: - - $require: '@veramo/credential-ld#VeramoEd25519Signature2018' - - $require: '@veramo/credential-ld#VeramoEd25519Signature2020' - - $require: '@veramo/credential-ld#VeramoJsonWebSignature2020' - - $require: '@veramo/credential-ld#VeramoEcdsaSecp256k1RecoverySignature2020' + - $require: "@veramo/credential-ld#VeramoEd25519Signature2018" + - $require: "@veramo/credential-ld#VeramoEd25519Signature2020" + - $require: "@veramo/credential-ld#VeramoJsonWebSignature2020" + - $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' + - $require: "@veramo/credential-ld?t=object#LdDefaultContexts" + - $require: "@transmute/credentials-context?t=object#contexts" # others should be included here +CredentialProviderJWT: + $require: "@veramo/credential-jwt#CredentialProviderJWT" + +credentialPlugin: + $require: "@veramo/credential-w3c#CredentialPlugin" + $args: + - issuers: + - $ref: /CredentialProviderLD + - $ref: /CredentialProviderJWT + # Agent agent: - $require: '@veramo/core#Agent' + $require: "@veramo/core#Agent" $args: - schemaValidation: false plugins: @@ -297,14 +307,12 @@ agent: - $ref: /didResolver - $ref: /didDiscovery - $ref: /messageHandler - - $require: '@veramo/did-comm#DIDComm' - - $require: '@veramo/credential-w3c#CredentialPlugin' - - $ref: /credentialIssuerLD - - $require: '@veramo/credential-eip712#CredentialIssuerEIP712' - - $require: '@veramo/selective-disclosure#SelectiveDisclosure' - - $require: '@veramo/data-store#DataStore' + - $require: "@veramo/did-comm#DIDComm" + - $ref: /credentialPlugin + - $require: "@veramo/selective-disclosure#SelectiveDisclosure" + - $require: "@veramo/data-store#DataStore" $args: - $ref: /dbConnection - - $require: '@veramo/data-store#DataStoreORM' + - $require: "@veramo/data-store#DataStoreORM" $args: - $ref: /dbConnection diff --git a/packages/cli/package.json b/packages/cli/package.json index 044c58cf1..ad76d46c4 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -41,6 +41,7 @@ "@veramo/core": "workspace:^", "@veramo/core-types": "workspace:^", "@veramo/credential-eip712": "workspace:^", + "@veramo/credential-jwt": "workspace:^", "@veramo/credential-ld": "workspace:^", "@veramo/credential-w3c": "workspace:^", "@veramo/data-store": "workspace:^", @@ -144,4 +145,4 @@ "node_modules", "src" ] -} +} \ No newline at end of file diff --git a/packages/core-types/src/plugin.schema.ts b/packages/core-types/src/plugin.schema.ts index f707329e3..75e5348b3 100644 --- a/packages/core-types/src/plugin.schema.ts +++ b/packages/core-types/src/plugin.schema.ts @@ -2287,6 +2287,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" @@ -3158,6 +3161,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" @@ -4004,6 +4010,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" @@ -4240,7 +4249,7 @@ export const schema = { "deprecated": "Please call\n{@link @veramo/core-types#IDataStore.dataStoreSaveVerifiableCredential | dataStoreSaveVerifiableCredential()} to\nsave the credential after creating it." }, "proofFormat": { - "$ref": "#/components/schemas/ProofFormat", + "type": "string", "description": "The desired format for the VerifiableCredential to be created." }, "removeOriginalFields": { @@ -4373,16 +4382,6 @@ export const schema = { ], "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", - "EthereumEip712Signature2021", - "bbs" - ], - "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": { @@ -4438,6 +4437,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" @@ -4475,8 +4477,8 @@ export const schema = { "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" + "type": "string", + "description": "The desired format for the VerifiablePresentation to be created." }, "removeOriginalFields": { "type": "boolean", @@ -4800,7 +4802,7 @@ export const schema = { "returnType": { "type": "array", "items": { - "$ref": "#/components/schemas/ProofFormat" + "type": "string" } } } @@ -4912,6 +4914,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" @@ -5163,7 +5168,7 @@ export const schema = { }, "methods": { "verifyCredential": { - "description": "Verifies a Verifiable Credential JWT, LDS Format or EIP712.", + "description": "Verifies a Verifiable Credential", "arguments": { "$ref": "#/components/schemas/IVerifyCredentialArgs" }, @@ -5282,6 +5287,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" @@ -5809,6 +5817,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" diff --git a/packages/core-types/src/types/ICredentialIssuer.ts b/packages/core-types/src/types/ICredentialIssuer.ts index ba509ec2c..48ceb2d11 100644 --- a/packages/core-types/src/types/ICredentialIssuer.ts +++ b/packages/core-types/src/types/ICredentialIssuer.ts @@ -12,14 +12,11 @@ import { IKeyManager } from './IKeyManager.js' import { IIdentifier, IKey } from "./IIdentifier.js"; import { UsingResolutionOptions } from './ICredentialVerifier.js' -/** - * 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' | 'EthereumEip712Signature2021' | 'bbs' +/* +* +* @public +*/ +export type ProofFormat = 'jwt' | 'lds' | 'EthereumEip712Signature2021' | 'bbs' | string /** * Encapsulates the parameters required to create a @@ -61,9 +58,8 @@ export interface ICreateVerifiablePresentationArgs extends UsingResolutionOption /** * The desired format for the VerifiablePresentation to be created. - * Currently, only JWT is supported */ - proofFormat: ProofFormat + proofFormat: string /** * Remove payload members during JWT-JSON transformation. Defaults to `true`. @@ -123,7 +119,7 @@ export interface ICreateVerifiableCredentialArgs extends UsingResolutionOptions /** * The desired format for the VerifiableCredential to be created. */ - proofFormat: ProofFormat + proofFormat: string /** * Remove payload members during JWT-JSON transformation. Defaults to `true`. @@ -159,6 +155,15 @@ export interface ICreateVerifiableCredentialArgs extends UsingResolutionOptions * @public */ +/** + * Encapsulates the parameters required to check if a credential type can be issued + * + * @public + */ +export interface ICanIssueCredentialTypeArgs { + proofFormat: string +} + /** * The interface definition for a plugin that can generate Verifiable Credentials and Presentations * @@ -210,16 +215,8 @@ export interface ICredentialIssuer extends IPluginMethodMap { * * @beta This API may change without a BREAKING CHANGE notice. */ - listUsableProofFormats(identifier: IIdentifier, context: IAgentContext<{}>): Promise> + listUsableProofFormats(identifier: IIdentifier, context: IAgentContext<{}>): Promise> - /** - * Checks if a key is suitable for signing JWT payloads. - * @param key - the key to check for compatibility - * @param context - This reserved param is automatically added and handled by the framework, *do not override* - * - * @internal - */ - matchKeyForJWT(key: IKey, context: IAgentContext): Promise } /** diff --git a/packages/core-types/src/types/ICredentialVerifier.ts b/packages/core-types/src/types/ICredentialVerifier.ts index 82944eb6d..d548e6491 100644 --- a/packages/core-types/src/types/ICredentialVerifier.ts +++ b/packages/core-types/src/types/ICredentialVerifier.ts @@ -145,11 +145,16 @@ export interface VerificationPolicies { } /** - * Encapsulates the response object to verifyPresentation method after verifying a - * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} - * + * Encapsulates the parameters required to check if a document can be verified + * * @public */ +export interface ICanVerifyDocumentTypeArgs { + /** + * The document to check against the verifier + */ + document: W3CVerifiableCredential | W3CVerifiablePresentation +} /** * The interface definition for a plugin that can generate Verifiable Credentials and Presentations @@ -161,7 +166,7 @@ export interface VerificationPolicies { */ export interface ICredentialVerifier extends IPluginMethodMap { /** - * Verifies a Verifiable Credential JWT, LDS Format or EIP712. + * Verifies a Verifiable Credential * * @param args - Arguments necessary to verify a VerifiableCredential * @param context - This reserved param is automatically added and handled by the framework, *do not override* diff --git a/packages/core-types/src/types/vc-data-model.ts b/packages/core-types/src/types/vc-data-model.ts index 206a62383..6ef0c8d93 100644 --- a/packages/core-types/src/types/vc-data-model.ts +++ b/packages/core-types/src/types/vc-data-model.ts @@ -18,7 +18,7 @@ export type CompactJWT = string * * @beta This API may change without a BREAKING CHANGE notice. */ -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 @@ -73,6 +73,8 @@ export type CredentialStatus = { export interface ProofType { type?: string + proofValue?: string + [x: string]: any } diff --git a/packages/credential-eip712/package.json b/packages/credential-eip712/package.json index faa8bd10d..71c5455dc 100644 --- a/packages/credential-eip712/package.json +++ b/packages/credential-eip712/package.json @@ -9,15 +9,11 @@ "types": "build/index.d.ts", "scripts": { "build": "tsc", - "generate-plugin-schema": "node ../cli/bin/veramo.js dev generate-plugin-schema" - }, - "veramo": { - "pluginInterfaces": { - "ICredentialIssuerEIP712": "./src/types/ICredentialEIP712.ts" - } + "extract-api": "node ../cli/bin/veramo.js dev extract-api" }, "dependencies": { "@metamask/eth-sig-util": "^7.0.0", + "@veramo/credential-w3c": "workspace:^", "@veramo/core-types": "workspace:^", "@veramo/utils": "workspace:^", "debug": "^4.3.3", @@ -62,4 +58,4 @@ "node_modules", "src" ] -} +} \ No newline at end of file diff --git a/packages/credential-eip712/src/agent/CredentialEIP712.ts b/packages/credential-eip712/src/agent/CredentialProviderEIP712.ts similarity index 76% rename from packages/credential-eip712/src/agent/CredentialEIP712.ts rename to packages/credential-eip712/src/agent/CredentialProviderEIP712.ts index ce9314144..562123df9 100644 --- a/packages/credential-eip712/src/agent/CredentialEIP712.ts +++ b/packages/credential-eip712/src/agent/CredentialProviderEIP712.ts @@ -1,11 +1,21 @@ import { CredentialPayload, IAgentPlugin, + ICreateVerifiableCredentialArgs, + ICanIssueCredentialTypeArgs, IIdentifier, IKey, + IssuerAgentContext, PresentationPayload, VerifiableCredential, VerifiablePresentation, + ICreateVerifiablePresentationArgs, + IVerifyCredentialArgs, + IVerifyResult, + IVerifyPresentationArgs, + VerifierAgentContext, + IAgentContext, + ICanVerifyDocumentTypeArgs, } from '@veramo/core-types' import { extractIssuer, @@ -19,43 +29,44 @@ import { removeDIDParameters, resolveDidOrThrow, } from '@veramo/utils' -import { schema } from '../plugin.schema.js' +import { AbstractCredentialProvider } from '@veramo/credential-w3c' import { recoverTypedSignature, SignTypedDataVersion } from '@metamask/eth-sig-util' -import { - ICreateVerifiableCredentialEIP712Args, - ICreateVerifiablePresentationEIP712Args, - ICredentialIssuerEIP712, - IRequiredContext, - IVerifyCredentialEIP712Args, - IVerifyPresentationEIP712Args, -} from '../types/ICredentialEIP712.js' import { getEthTypesFromInputDoc } from 'eip-712-types-generation' /** - * A Veramo plugin that implements the {@link ICredentialIssuerEIP712} methods. + * A Veramo plugin that implements the {@link ICredentialProviderEIP712} methods. * * @beta This API may change without a BREAKING CHANGE notice. */ -export class CredentialIssuerEIP712 implements IAgentPlugin { - readonly methods: ICredentialIssuerEIP712 - readonly schema = schema.ICredentialIssuerEIP712 - - constructor() { - this.methods = { - createVerifiableCredentialEIP712: this.createVerifiableCredentialEIP712.bind(this), - createVerifiablePresentationEIP712: this.createVerifiablePresentationEIP712.bind(this), - verifyCredentialEIP712: this.verifyCredentialEIP712.bind(this), - verifyPresentationEIP712: this.verifyPresentationEIP712.bind(this), - matchKeyForEIP712: this.matchKeyForEIP712.bind(this), - } +export class CredentialProviderEIP712 implements AbstractCredentialProvider { + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.matchKeyForType} */ + matchKeyForType(key: IKey): boolean { + return this.matchKeyForEIP712(key) + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.getTypeProofFormat} */ + getTypeProofFormat(): string { + return 'EthereumEip712Signature2021' + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.canIssueCredentialType} */ + canIssueCredentialType(args: ICanIssueCredentialTypeArgs): boolean { + return (args.proofFormat === 'EthereumEip712Signature2021') } - /** {@inheritdoc ICredentialIssuerEIP712.createVerifiableCredentialEIP712} */ - public async createVerifiableCredentialEIP712( - args: ICreateVerifiableCredentialEIP712Args, - context: IRequiredContext, + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.canVerifyDocumentType */ + canVerifyDocumentType(args: ICanVerifyDocumentTypeArgs): boolean { + const { document } = args + return ((document)?.proof?.type === 'EthereumEip712Signature2021') + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.createVerifiableCredential} */ + async createVerifiableCredential( + args: ICreateVerifiableCredentialArgs, + context: IssuerAgentContext, ): Promise { const credentialContext = processEntryToArray( args?.credential?.['@context'], @@ -138,12 +149,12 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { return credential as VerifiableCredential } - /** {@inheritdoc ICredentialIssuerEIP712.verifyCredentialEIP712} */ - private async verifyCredentialEIP712( - args: IVerifyCredentialEIP712Args, - context: IRequiredContext, - ): Promise { - const { credential } = args + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.verifyCredential} */ + async verifyCredential( + args: IVerifyCredentialArgs, + context: VerifierAgentContext, + ): Promise { + const credential = args.credential as VerifiableCredential if (!credential.proof || !credential.proof.proofValue) throw new Error('invalid_argument: proof is undefined') @@ -173,7 +184,7 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { const recovered = recoverTypedSignature({ data: objectToVerify, - signature: proofValue, + signature: proofValue!, version: SignTypedDataVersion.V4, }) @@ -187,20 +198,28 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { if (didDocument.verificationMethod) { for (const verificationMethod of didDocument.verificationMethod) { if (getEthereumAddress(verificationMethod)?.toLowerCase() === recovered.toLowerCase()) { - return true + return { + verified: true, + } } } } else { throw new Error('resolver_error: issuer DIDDocument does not contain any verificationMethods') } - return false + return { + verified: false, + error: { + message: 'invalid_signature: The signature does not match any of the issuer signing keys', + errorCode: 'invalid_signature', + } + } } - /** {@inheritdoc ICredentialIssuerEIP712.createVerifiablePresentationEIP712} */ - async createVerifiablePresentationEIP712( - args: ICreateVerifiablePresentationEIP712Args, - context: IRequiredContext, + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.createVerifiablePresentation} */ + async createVerifiablePresentation( + args: ICreateVerifiablePresentationArgs, + context: IssuerAgentContext, ): Promise { const presentationContext = processEntryToArray( args?.presentation?.['@context'], @@ -306,12 +325,12 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { return presentation as VerifiablePresentation } - /** {@inheritdoc ICredentialIssuerEIP712.verifyPresentationEIP712} */ - private async verifyPresentationEIP712( - args: IVerifyPresentationEIP712Args, - context: IRequiredContext, - ): Promise { - const { presentation } = args + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.verifyPresentation} */ + async verifyPresentation( + args: IVerifyPresentationArgs, + context: VerifierAgentContext, + ): Promise { + const presentation = args.presentation as VerifiablePresentation if (!presentation.proof || !presentation.proof.proofValue) throw new Error('Proof is undefined') const { proof, ...signingInput } = presentation @@ -340,7 +359,7 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { const recovered = recoverTypedSignature({ data: objectToVerify, - signature: proofValue, + signature: proofValue!, version: SignTypedDataVersion.V4, }) @@ -354,14 +373,22 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { if (didDocument.verificationMethod) { for (const verificationMethod of didDocument.verificationMethod) { if (getEthereumAddress(verificationMethod)?.toLowerCase() === recovered.toLowerCase()) { - return true + return { + verified: true, + } } } } else { throw new Error('resolver_error: holder DIDDocument does not contain any verificationMethods') } - return false + return { + verified: false, + error: { + message: 'invalid_signature: The signature does not match any of the holder signing keys', + errorCode: 'invalid_signature', + } + } } /** @@ -372,7 +399,7 @@ export class CredentialIssuerEIP712 implements IAgentPlugin { * * @internal */ - async matchKeyForEIP712(k: IKey): Promise { + matchKeyForEIP712(k: IKey): boolean { return ( intersect(k.meta?.algorithms ?? [], ['eth_signTypedData', 'EthereumEip712Signature2021']).length > 0 ) diff --git a/packages/credential-eip712/src/index.ts b/packages/credential-eip712/src/index.ts index 766be4979..2fc6807c7 100644 --- a/packages/credential-eip712/src/index.ts +++ b/packages/credential-eip712/src/index.ts @@ -1,3 +1 @@ -export { CredentialIssuerEIP712 } from './agent/CredentialEIP712.js' -export * from './types/ICredentialEIP712.js' -export { schema } from './plugin.schema.js' +export { CredentialProviderEIP712 } from './agent/CredentialProviderEIP712.js' diff --git a/packages/credential-eip712/src/plugin.schema.ts b/packages/credential-eip712/src/plugin.schema.ts deleted file mode 100644 index dea75d226..000000000 --- a/packages/credential-eip712/src/plugin.schema.ts +++ /dev/null @@ -1,432 +0,0 @@ -export const schema = { - "ICredentialIssuerEIP712": { - "components": { - "schemas": { - "ICreateVerifiableCredentialEIP712Args": { - "type": "object", - "properties": { - "resolutionOptions": { - "type": "object", - "properties": { - "publicKeyFormat": { - "type": "string" - }, - "accept": { - "type": "string" - } - }, - "description": "Options to be passed to the DID resolver." - }, - "credential": { - "$ref": "#/components/schemas/CredentialPayload", - "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`\n\n`@context`, 'type' and 'issuanceDate' will be added automatically if omitted" - }, - "keyRef": { - "type": "string", - "description": "Specific key to use for signing" - } - }, - "required": [ - "credential" - ], - "description": "Encapsulates the parameters required to create a {@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": { - "$ref": "#/components/schemas/ContextType" - }, - "issuanceDate": { - "$ref": "#/components/schemas/DateType" - }, - "expirationDate": { - "$ref": "#/components/schemas/DateType" - }, - "credentialStatus": { - "$ref": "#/components/schemas/CredentialStatusReference" - }, - "id": { - "type": "string" - } - }, - "required": [ - "issuer" - ], - "description": "Used as input when creating Verifiable Credentials" - }, - "IssuerType": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string" - } - }, - "required": [ - "id" - ] - }, - { - "type": "string" - } - ], - "description": "The issuer of a {@link VerifiableCredential } or the holder of a {@link VerifiablePresentation } .\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 }" - }, - "ContextType": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object" - }, - { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object" - } - ] - } - } - ], - "description": "The data type for `@context` properties of credentials, presentations, etc." - }, - "DateType": { - "type": "string", - "description": "Represents an issuance or expiration date for Credentials / Presentations. This is used as input when creating them." - }, - "CredentialStatusReference": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "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": { - "proof": { - "$ref": "#/components/schemas/ProofType" - }, - "issuer": { - "$ref": "#/components/schemas/IssuerType" - }, - "credentialSubject": { - "$ref": "#/components/schemas/CredentialSubject" - }, - "type": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "string" - } - ] - }, - "@context": { - "$ref": "#/components/schemas/ContextType" - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "credentialStatus": { - "$ref": "#/components/schemas/CredentialStatusReference" - }, - "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 {@link VerifiableCredential } or {@link VerifiablePresentation }" - }, - "ICreateVerifiablePresentationEIP712Args": { - "type": "object", - "properties": { - "resolutionOptions": { - "type": "object", - "properties": { - "publicKeyFormat": { - "type": "string" - }, - "accept": { - "type": "string" - } - }, - "description": "Options to be passed to the DID resolver." - }, - "presentation": { - "$ref": "#/components/schemas/PresentationPayload", - "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`\n\n`@context`, `type` and `issuanceDate` will be added automatically if omitted" - }, - "keyRef": { - "type": "string", - "description": "[Optional] The ID of the key that should sign this presentation. If this is not specified, the first matching key will be used." - } - }, - "required": [ - "presentation" - ], - "description": "Encapsulates the parameters required to create a {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation } using the {@link https://w3c-ccg.github.io/ethereum-eip712-signature-2021-spec/ | EthereumEip712Signature2021 } proof format." - }, - "PresentationPayload": { - "type": "object", - "properties": { - "holder": { - "type": "string" - }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/W3CVerifiableCredential" - } - }, - "type": { - "type": "array", - "items": { - "type": "string" - } - }, - "@context": { - "$ref": "#/components/schemas/ContextType" - }, - "verifier": { - "type": "array", - "items": { - "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 } See {@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/W3CVerifiableCredential" - } - }, - "type": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "string" - } - ] - }, - "@context": { - "$ref": "#/components/schemas/ContextType" - }, - "verifier": { - "type": "array", - "items": { - "type": "string" - } - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "id": { - "type": "string" - } - }, - "required": [ - "@context", - "holder", - "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 }" - }, - "IVerifyCredentialEIP712Args": { - "type": "object", - "properties": { - "resolutionOptions": { - "type": "object", - "properties": { - "publicKeyFormat": { - "type": "string" - }, - "accept": { - "type": "string" - } - }, - "description": "Options to be passed to the DID resolver." - }, - "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 Credential is chosen based on the `issuer.id` property of the `credential`" - } - }, - "required": [ - "credential" - ], - "description": "Encapsulates the parameters required to verify a {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" - }, - "IVerifyPresentationEIP712Args": { - "type": "object", - "properties": { - "resolutionOptions": { - "type": "object", - "properties": { - "publicKeyFormat": { - "type": "string" - }, - "accept": { - "type": "string" - } - }, - "description": "Options to be passed to the DID resolver." - }, - "presentation": { - "$ref": "#/components/schemas/VerifiablePresentation", - "description": "The Verifiable Presentation object according to the {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model } or the JWT representation.\n\nThe signer of the Presentation is verified based on the `holder` property of the `presentation` or the `iss` property of the JWT payload respectively" - } - }, - "required": [ - "presentation" - ], - "description": "Encapsulates the parameters required to verify a {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" - } - }, - "methods": { - "createVerifiableCredentialEIP712": { - "description": "Creates a Verifiable Credential. The payload, signer and format are chosen based on the ", - "arguments": { - "$ref": "#/components/schemas/ICreateVerifiableCredentialEIP712Args" - }, - "returnType": { - "$ref": "#/components/schemas/VerifiableCredential" - } - }, - "createVerifiablePresentationEIP712": { - "description": "Creates a Verifiable Presentation. The payload and signer are chosen based on the ", - "arguments": { - "$ref": "#/components/schemas/ICreateVerifiablePresentationEIP712Args" - }, - "returnType": { - "$ref": "#/components/schemas/VerifiablePresentation" - } - }, - "verifyCredentialEIP712": { - "description": "Verifies a Verifiable Credential in EIP712 Format.", - "arguments": { - "$ref": "#/components/schemas/IVerifyCredentialEIP712Args" - }, - "returnType": { - "type": "boolean" - } - }, - "verifyPresentationEIP712": { - "description": "Verifies a Verifiable Presentation EIP712 Format.", - "arguments": { - "$ref": "#/components/schemas/IVerifyPresentationEIP712Args" - }, - "returnType": { - "type": "boolean" - } - } - } - } - } -} \ No newline at end of file diff --git a/packages/credential-eip712/src/types/ICredentialEIP712.ts b/packages/credential-eip712/src/types/ICredentialEIP712.ts deleted file mode 100644 index 4b9606418..000000000 --- a/packages/credential-eip712/src/types/ICredentialEIP712.ts +++ /dev/null @@ -1,196 +0,0 @@ -import { - CredentialPayload, - IAgentContext, - IDIDManager, - IKey, - IKeyManager, - IPluginMethodMap, - IResolver, - PresentationPayload, - UsingResolutionOptions, - VerifiableCredential, - VerifiablePresentation, -} from '@veramo/core-types' - -/** - * The interface definition for a plugin that can issue and verify Verifiable Credentials and Presentations - * that use EIP712 proof format. - * - * @remarks Please see {@link https://www.w3.org/TR/vc-data-model | W3C Verifiable Credentials data model} - * @remarks Please see - * {@link https://w3c-ccg.github.io/ethereum-eip712-signature-2021-spec/ | EthereumEip712Signature2021} - * - * @beta This API may change without a BREAKING CHANGE notice. - */ -export interface ICredentialIssuerEIP712 extends IPluginMethodMap { - /** - * Creates a Verifiable Credential. - * The payload, signer and format are chosen based on the `args` parameter. - * - * @param args - Arguments necessary to create the Credential. - * @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-types#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 may change without a BREAKING CHANGE notice. - */ - createVerifiableCredentialEIP712( - args: ICreateVerifiableCredentialEIP712Args, - context: IRequiredContext, - ): Promise - - /** - * Verifies a Verifiable Credential in EIP712 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 may change without a BREAKING CHANGE notice. - */ - verifyCredentialEIP712(args: IVerifyCredentialEIP712Args, context: IRequiredContext): Promise - - /** - * Creates a Verifiable Presentation. - * The payload and signer 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-types#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 - * } - */ - createVerifiablePresentationEIP712( - args: ICreateVerifiablePresentationEIP712Args, - context: IRequiredContext, - ): Promise - - /** - * Verifies a Verifiable Presentation EIP712 Format. - * - * @param args - Arguments necessary to verify 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 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} - */ - verifyPresentationEIP712(args: IVerifyPresentationEIP712Args, context: IRequiredContext): Promise - - /** - * Checks if a key is suitable for signing EIP712 payloads. - * This relies on the metadata set by the key management system to determine if this key can sign EIP712 payloads. - * - * @param key - the key to check - * @param context - This reserved param is automatically added and handled by the framework, *do not override* - * - * @internal - */ - matchKeyForEIP712(key: IKey, context: IRequiredContext): Promise -} - -/** - * Encapsulates the parameters required to create a - * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} - * - * @beta This API may change without a BREAKING CHANGE notice. - */ -export interface ICreateVerifiableCredentialEIP712Args extends UsingResolutionOptions { - /** - * 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: CredentialPayload - - /** - * Specific key to use for signing - */ - keyRef?: string -} - -/** - * Encapsulates the parameters required to create a - * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} - * using the {@link https://w3c-ccg.github.io/ethereum-eip712-signature-2021-spec/ | EthereumEip712Signature2021} - * proof format. - * - * @beta This API may change without a BREAKING CHANGE notice. - */ -export interface ICreateVerifiablePresentationEIP712Args extends UsingResolutionOptions { - /** - * 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: PresentationPayload - - /** - * [Optional] The ID of the key that should sign this presentation. - * If this is not specified, the first matching key will be used. - */ - 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 may change without a BREAKING CHANGE notice. - */ -export interface IVerifyCredentialEIP712Args extends UsingResolutionOptions { - /** - * 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 IVerifyPresentationEIP712Args extends UsingResolutionOptions { - /** - * The Verifiable Presentation object according to the - * {@link https://www.w3.org/TR/vc-data-model/#presentations | canonical model} or the JWT representation. - * - * The signer of the Presentation is verified based on the `holder` property - * of the `presentation` or the `iss` property of the JWT payload respectively - * - */ - presentation: VerifiablePresentation -} - -/** - * 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 may change without a BREAKING CHANGE notice. - */ -export type IRequiredContext = IAgentContext diff --git a/packages/credential-jwt/CHANGELOG.md b/packages/credential-jwt/CHANGELOG.md new file mode 100644 index 000000000..e4d87c4d4 --- /dev/null +++ b/packages/credential-jwt/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. diff --git a/packages/credential-jwt/LICENSE b/packages/credential-jwt/LICENSE new file mode 100644 index 000000000..454ee3175 --- /dev/null +++ b/packages/credential-jwt/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 2022 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-jwt/api-extractor.json b/packages/credential-jwt/api-extractor.json new file mode 100644 index 000000000..409d7f16c --- /dev/null +++ b/packages/credential-jwt/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-jwt/package.json b/packages/credential-jwt/package.json new file mode 100644 index 000000000..594efce56 --- /dev/null +++ b/packages/credential-jwt/package.json @@ -0,0 +1,54 @@ +{ + "name": "@veramo/credential-jwt", + "description": "Veramo plugin for working with JWT Verifiable Credentials & Presentations.", + "version": "6.0.0", + "main": "build/index.js", + "exports": { + ".": "./build/index.js" + }, + "types": "build/index.d.ts", + "scripts": { + "build": "tsc", + "extract-api": "node ../cli/bin/veramo.js dev extract-api" + }, + "dependencies": { + "@veramo/credential-w3c": "workspace:^", + "@veramo/core-types": "workspace:^", + "@veramo/utils": "workspace:^", + "canonicalize": "^2.0.0", + "debug": "^4.3.3", + "did-jwt-vc": "^4.0.0" + }, + "devDependencies": { + "@types/debug": "4.1.8", + "typescript": "5.3.3" + }, + "files": [ + "build/**/*", + "src/**/*", + "README.md", + "LICENSE" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/decentralized-identity/veramo.git", + "directory": "packages/credential-jwt" + }, + "author": "Nick Reynolds ", + "keywords": [ + "Veramo", + "DID", + "Verifiable Credential", + "JWT", + "veramo-plugin" + ], + "license": "Apache-2.0", + "type": "module", + "moduleDirectories": [ + "node_modules", + "src" + ] +} \ No newline at end of file diff --git a/packages/credential-jwt/src/__tests__/issue-verify-flow-jwt.test.ts b/packages/credential-jwt/src/__tests__/issue-verify-flow-jwt.test.ts new file mode 100644 index 000000000..dfba6ae6b --- /dev/null +++ b/packages/credential-jwt/src/__tests__/issue-verify-flow-jwt.test.ts @@ -0,0 +1,127 @@ +import { + CredentialPayload, + ICredentialPlugin, + IDIDManager, + IIdentifier, + IKeyManager, + IResolver, + TAgent, +} from '../../../core-types/src/index.js' +import { createAgent } from '../../../core/src/index.js' +import { CredentialPlugin } from '../../../credential-w3c/src/index.js' +import { DIDManager, MemoryDIDStore } from '../../../did-manager/src/index.js' +import { KeyManager, MemoryKeyStore, MemoryPrivateKeyStore } from '../../../key-manager/src/index.js' +import { KeyManagementSystem } from '../../../kms-local/src/index.js' +import { getDidKeyResolver, KeyDIDProvider } from '../../../did-provider-key/src/index.js' +import { DIDResolverPlugin } from '../../../did-resolver/src/index.js' +import { EthrDIDProvider } from '../../../did-provider-ethr/src/index.js' +import { Resolver } from 'did-resolver' +import { getResolver as ethrDidResolver } from 'ethr-did-resolver' +import { jest } from '@jest/globals' + +import 'cross-fetch/polyfill' +import { CredentialProviderJWT } from '../agent/CredentialProviderJWT.js' + +jest.setTimeout(300000) + +const infuraProjectId = '3586660d179141e3801c3895de1c2eba' + +describe('credential-jwt full flow', () => { + let didKeyIdentifier: IIdentifier + let didEthrIdentifier: IIdentifier + let agent: TAgent + const jwt = new CredentialProviderJWT() + + beforeAll(async () => { + agent = createAgent({ + plugins: [ + new KeyManager({ + store: new MemoryKeyStore(), + kms: { + local: new KeyManagementSystem(new MemoryPrivateKeyStore()), + }, + }), + new DIDManager({ + providers: { + 'did:key': new KeyDIDProvider({ defaultKms: 'local' }), + 'did:ethr': new EthrDIDProvider({ + defaultKms: 'local', + network: 'mainnet', + }), + }, + store: new MemoryDIDStore(), + defaultProvider: 'did:key', + }), + new DIDResolverPlugin({ + resolver: new Resolver({ + ...getDidKeyResolver(), + ...ethrDidResolver({ infuraProjectId }), + }), + }), + new CredentialPlugin({ issuers: [jwt] }), + ], + }) + didKeyIdentifier = await agent.didManagerCreate() + didEthrIdentifier = await agent.didManagerCreate({ provider: 'did:ethr' }) + }) + + it('issues and verifies JWT credential', async () => { + const credential: CredentialPayload = { + issuer: { id: didEthrIdentifier.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', + }, + } + const verifiableCredential = await agent.createVerifiableCredential({ + credential, + proofFormat: 'jwt', + }) + + expect(verifiableCredential).toBeDefined() + + const result = await agent.verifyCredential({ + credential: verifiableCredential, + }) + + expect(result.verified).toBe(true) + }) + + it('issues credential and verifies presentation', async () => { + const credential: CredentialPayload = { + 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(), + credentialSubject: { + id: didKeyIdentifier.did, + name: 'Martin, the great', + }, + } + const verifiableCredential1 = await agent.createVerifiableCredential({ + credential, + proofFormat: 'jwt', + }) + + const verifiablePresentation = await agent.createVerifiablePresentation({ + presentation: { + verifiableCredential: [verifiableCredential1], + holder: didEthrIdentifier.did, + }, + challenge: 'VERAMO', + proofFormat: 'jwt', + }) + + expect(verifiablePresentation).toBeDefined() + + const result = await agent.verifyPresentation({ + presentation: verifiablePresentation, + challenge: 'VERAMO', + }) + + expect(result.verified).toBe(true) + }) +}) diff --git a/packages/credential-jwt/src/agent/CredentialProviderJWT.ts b/packages/credential-jwt/src/agent/CredentialProviderJWT.ts new file mode 100644 index 000000000..ce4f0703e --- /dev/null +++ b/packages/credential-jwt/src/agent/CredentialProviderJWT.ts @@ -0,0 +1,364 @@ +import { + CredentialPayload, + IAgentPlugin, + ICreateVerifiableCredentialArgs, + ICanIssueCredentialTypeArgs, + IIdentifier, + IKey, + IssuerAgentContext, + VerifiableCredential, + VerifiablePresentation, + IAgentContext, + IKeyManager, + ICreateVerifiablePresentationArgs, + IVerifyCredentialArgs, + IVerifyResult, + IVerifyPresentationArgs, + ICanVerifyDocumentTypeArgs, + VerifierAgentContext, +} from '@veramo/core-types' +import { + asArray, + extractIssuer, + intersect, + isDefined, + MANDATORY_CREDENTIAL_CONTEXT, + pickSigningKey, + processEntryToArray, + removeDIDParameters, +} from '@veramo/utils' +import { AbstractCredentialProvider } from '@veramo/credential-w3c' + +import canonicalize from 'canonicalize' + +import { + createVerifiableCredentialJwt, + createVerifiablePresentationJwt, + normalizeCredential, + normalizePresentation, + verifyCredential as verifyCredentialJWT, + verifyPresentation as verifyPresentationJWT, +} from 'did-jwt-vc' +import { Resolvable } from 'did-resolver' + +import { decodeJWT } from 'did-jwt' + +import Debug from 'debug' +const debug = Debug('veramo:credential-jwt:agent') + +/** + * A handler that implements the {@link AbstractCredentialProvider} methods. + * + * @beta This API may change without a BREAKING CHANGE notice. + */ +export class CredentialProviderJWT implements AbstractCredentialProvider { + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.matchKeyForType} */ + matchKeyForType(key: IKey): boolean { + return this.matchKeyForJWT(key) + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.getTypeProofFormat} */ + getTypeProofFormat(): string { + return 'jwt' + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.canIssueCredentialType} */ + canIssueCredentialType(args: ICanIssueCredentialTypeArgs): boolean { + return args.proofFormat === 'jwt' + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.canVerifyDocumentType */ + canVerifyDocumentType(args: ICanVerifyDocumentTypeArgs): boolean { + const { document } = args + return typeof document === 'string' || (document)?.proof?.jwt + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.createVerifiableCredential} */ + async createVerifiableCredential( + args: ICreateVerifiableCredentialArgs, + context: IssuerAgentContext, + ): Promise { + let { proofFormat, keyRef, removeOriginalFields, save, now, ...otherOptions } = args + + const credentialContext = processEntryToArray( + args?.credential?.['@context'], + MANDATORY_CREDENTIAL_CONTEXT, + ) + const credentialType = processEntryToArray(args?.credential?.type, 'VerifiableCredential') + const credential: CredentialPayload = { + ...args?.credential, + '@context': credentialContext, + type: credentialType, + } + + const issuer = extractIssuer(credential, { removeParameters: true }) + 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}`) + } + + const key = pickSigningKey(identifier, keyRef) + + debug('Signing VC with', identifier.did) + let alg = 'ES256K' + if (key.type === 'Ed25519') { + alg = 'EdDSA' + } else if (key.type === 'Secp256r1') { + alg = 'ES256' + } + + const signer = this.wrapSigner(context, key, alg) + const jwt = await createVerifiableCredentialJwt( + credential as any, + { did: identifier.did, signer, alg }, + { removeOriginalFields, ...otherOptions }, + ) + //FIXME: flagging this as a potential privacy leak. + debug(jwt) + return normalizeCredential(jwt) + } + + /** {@inheritdoc ICredentialVerifier.verifyCredential} */ + async verifyCredential( + args: IVerifyCredentialArgs, + context: VerifierAgentContext, + ): Promise { + let { credential, policies, ...otherOptions } = args + let verifiedCredential: VerifiableCredential + let verificationResult: IVerifyResult | undefined = { verified: false } + let jwt: string = typeof credential === 'string' ? credential : credential.proof.jwt + let errorCode, message + const resolver = { + resolve: (didUrl: string) => + context.agent.resolveDid({ + didUrl, + options: otherOptions?.resolutionOptions, + }), + } as Resolvable + try { + // needs broader credential as well to check equivalence with jwt + verificationResult = await verifyCredentialJWT(jwt, resolver, { + ...otherOptions, + policies: { + ...policies, + nbf: policies?.nbf ?? policies?.issuanceDate, + iat: policies?.iat ?? policies?.issuanceDate, + exp: policies?.exp ?? policies?.expirationDate, + aud: policies?.aud ?? policies?.audience, + }, + }) + verifiedCredential = verificationResult.verifiableCredential + + // if credential was presented with other fields, make sure those fields match what's in the JWT + if (typeof credential !== 'string' && credential.proof.type === 'JwtProof2020') { + const credentialCopy = JSON.parse(JSON.stringify(credential)) + delete credentialCopy.proof.jwt + + const verifiedCopy = JSON.parse(JSON.stringify(verifiedCredential)) + delete verifiedCopy.proof.jwt + + if (canonicalize(credentialCopy) !== canonicalize(verifiedCopy)) { + verificationResult.verified = false + verificationResult.error = new Error( + 'invalid_credential: Credential JSON does not match JWT payload', + ) + } + } + } catch (e: any) { + errorCode = e.errorCode + message = e.message + } + if (verificationResult.verified) { + return verificationResult + } + return { + verified: false, + error: { + message, + errorCode: errorCode ? errorCode : message?.split(':')[0], + }, + } + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.createVerifiablePresentation} */ + async createVerifiablePresentation( + args: ICreateVerifiablePresentationArgs, + context: IssuerAgentContext, + ): Promise { + let { + presentation, + proofFormat, + domain, + challenge, + removeOriginalFields, + keyRef, + save, + now, + ...otherOptions + } = args + const presentationContext: string[] = processEntryToArray( + args?.presentation?.['@context'], + MANDATORY_CREDENTIAL_CONTEXT, + ) + const presentationType = processEntryToArray(args?.presentation?.type, 'VerifiablePresentation') + presentation = { + ...presentation, + '@context': presentationContext, + type: presentationType, + } + + if (!isDefined(presentation.holder)) { + throw new Error('invalid_argument: presentation.holder must not be empty') + } + + if (presentation.verifiableCredential) { + presentation.verifiableCredential = 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 + } + }) + } + + const holder = removeDIDParameters(presentation.holder) + + let identifier: IIdentifier + try { + identifier = await context.agent.didManagerGet({ did: holder }) + } catch (e) { + throw new Error('invalid_argument: presentation.holder must be a DID managed by this agent') + } + const key = pickSigningKey(identifier, keyRef) + // only add issuanceDate for JWT + now = typeof now === 'number' ? new Date(now * 1000) : now + if (!Object.getOwnPropertyNames(presentation).includes('issuanceDate')) { + presentation.issuanceDate = (now instanceof Date ? now : new Date()).toISOString() + } + + debug('Signing VP with', identifier.did) + let alg = 'ES256K' + if (key.type === 'Ed25519') { + alg = 'EdDSA' + } else if (key.type === 'Secp256r1') { + alg = 'ES256' + } + + const signer = this.wrapSigner(context, key, alg) + const jwt = await createVerifiablePresentationJwt( + presentation as any, + { did: identifier.did, signer, alg }, + { removeOriginalFields, challenge, domain, ...otherOptions }, + ) + //FIXME: flagging this as a potential privacy leak. + debug(jwt) + return normalizePresentation(jwt) + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.verifyPresentation} */ + async verifyPresentation( + args: IVerifyPresentationArgs, + context: VerifierAgentContext, + ): Promise { + let { presentation, domain, challenge, fetchRemoteContexts, policies, ...otherOptions } = args + let jwt: string + if (typeof presentation === 'string') { + jwt = presentation + } else { + jwt = presentation.proof.jwt + } + const resolver = { + resolve: (didUrl: string) => + context.agent.resolveDid({ + didUrl, + options: otherOptions?.resolutionOptions, + }), + } as Resolvable + + let audience = 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 + } + } + } + + let message, errorCode + try { + const result = await verifyPresentationJWT(jwt, resolver, { + challenge, + domain, + audience, + policies: { + ...policies, + nbf: policies?.nbf ?? policies?.issuanceDate, + iat: policies?.iat ?? policies?.issuanceDate, + exp: policies?.exp ?? policies?.expirationDate, + aud: policies?.aud ?? policies?.audience, + }, + ...otherOptions, + }) + if (result) { + return { + verified: true, + verifiablePresentation: result, + } + } + } catch (e: any) { + message = e.message + errorCode = e.errorCode + } + return { + verified: false, + error: { + message, + errorCode: errorCode ? errorCode : message?.split(':')[0], + }, + } + } + + /** + * Checks if a key is suitable for signing JWT payloads. + * @param key - the key to check + * @param context - the Veramo agent context, unused here + * + * @beta + */ + matchKeyForJWT(key: IKey): boolean { + switch (key.type) { + case 'Ed25519': + case 'Secp256r1': + return true + case 'Secp256k1': + return intersect(key.meta?.algorithms ?? [], ['ES256K', 'ES256K-R']).length > 0 + default: + return false + } + } + + wrapSigner( + context: IAgentContext>, + key: IKey, + algorithm?: string, + ) { + return async (data: string | Uint8Array): Promise => { + const result = await context.agent.keyManagerSign({ keyRef: key.kid, data: data, algorithm }) + return result + } + } +} + diff --git a/packages/credential-jwt/src/index.ts b/packages/credential-jwt/src/index.ts new file mode 100644 index 000000000..8b6608926 --- /dev/null +++ b/packages/credential-jwt/src/index.ts @@ -0,0 +1 @@ +export { CredentialProviderJWT } from './agent/CredentialProviderJWT.js' diff --git a/packages/credential-jwt/tsconfig.json b/packages/credential-jwt/tsconfig.json new file mode 100644 index 000000000..d1bf01686 --- /dev/null +++ b/packages/credential-jwt/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-types" }, + { "path": "../utils" } + ], + "include": ["./**/*.ts"] +} diff --git a/packages/credential-ld/package.json b/packages/credential-ld/package.json index f3548ef51..5fe953430 100644 --- a/packages/credential-ld/package.json +++ b/packages/credential-ld/package.json @@ -9,12 +9,7 @@ "types": "build/index.d.ts", "scripts": { "build": "tsc", - "generate-plugin-schema": "node ../cli/bin/veramo.js dev generate-plugin-schema" - }, - "veramo": { - "pluginInterfaces": { - "ICredentialIssuerLD": "./src/action-handler.ts" - } + "extract-api": "node ../cli/bin/veramo.js dev extract-api" }, "dependencies": { "@digitalcredentials/ed25519-signature-2020": "^4.0.0", @@ -26,6 +21,7 @@ "@transmute/ed25519-signature-2018": "^0.7.0-unstable.81", "@transmute/json-web-signature": "^0.7.0-unstable.81", "@veramo-community/lds-ecdsa-secp256k1-recovery2020": "uport-project/EcdsaSecp256k1RecoverySignature2020", + "@veramo/credential-w3c": "workspace:^", "@veramo/core-types": "workspace:^", "@veramo/utils": "workspace:^", "cross-fetch": "^4.0.0", @@ -81,4 +77,4 @@ "node_modules", "src" ] -} +} \ No newline at end of file diff --git a/packages/credential-ld/src/action-handler.ts b/packages/credential-ld/src/CredentialProviderLD.ts similarity index 74% rename from packages/credential-ld/src/action-handler.ts rename to packages/credential-ld/src/CredentialProviderLD.ts index 7f4181191..b65c4eca1 100644 --- a/packages/credential-ld/src/action-handler.ts +++ b/packages/credential-ld/src/CredentialProviderLD.ts @@ -2,15 +2,23 @@ import { CredentialPayload, IAgentContext, IAgentPlugin, + ICanIssueCredentialTypeArgs, + ICreateVerifiableCredentialArgs, + ICreateVerifiablePresentationArgs, IIdentifier, IKey, IResolver, + IssuerAgentContext, + IVerifyCredentialArgs, + IVerifyPresentationArgs, + IVerifyResult, PresentationPayload, VerifiableCredential, VerifiablePresentation, + VerifierAgentContext, + ICanVerifyDocumentTypeArgs, } from '@veramo/core-types' import { VeramoLdSignature } from './index.js' -import { schema } from './plugin.schema.js' import Debug from 'debug' import { LdContextLoader } from './ld-context-loader.js' import { @@ -30,25 +38,19 @@ import { LdCredentialModule } from './ld-credential-module.js' import { LdSuiteLoader } from './ld-suite-loader.js' import { ContextDoc, - ICreateVerifiableCredentialLDArgs, - ICreateVerifiablePresentationLDArgs, - ICredentialIssuerLD, - IRequiredContext, - IVerifyCredentialLDArgs, - IVerifyPresentationLDArgs, } from './types.js' import { DIDResolutionOptions } from 'did-resolver' +import { AbstractCredentialProvider } from '@veramo/credential-w3c' + const debug = Debug('veramo:credential-ld:action-handler') /** - * A Veramo plugin that implements the {@link ICredentialIssuerLD} methods. + * A handler that implements the {@link AbstractCredentialProvider} methods. * * @public */ -export class CredentialIssuerLD implements IAgentPlugin { - readonly methods: ICredentialIssuerLD - readonly schema = schema.ICredentialIssuerLD +export class CredentialProviderLD implements AbstractCredentialProvider { private ldCredentialModule: LdCredentialModule @@ -57,20 +59,40 @@ export class CredentialIssuerLD implements IAgentPlugin { ldContextLoader: new LdContextLoader({ contextsPaths: options.contextMaps }), ldSuiteLoader: new LdSuiteLoader({ veramoLdSignatures: options.suites }), }) + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.matchKeyForType} */ + matchKeyForType(key: IKey): boolean { + return this.matchKeyForLDSuite(key) + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.getTypeProofFormat} */ + getTypeProofFormat(): string { + return 'lds' + } + + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.canIssueCredentialType} */ + canIssueCredentialType(args: ICanIssueCredentialTypeArgs): boolean { + return (args.proofFormat === 'lds') + } - this.methods = { - createVerifiablePresentationLD: this.createVerifiablePresentationLD.bind(this), - createVerifiableCredentialLD: this.createVerifiableCredentialLD.bind(this), - verifyCredentialLD: this.verifyCredentialLD.bind(this), - verifyPresentationLD: this.verifyPresentationLD.bind(this), - matchKeyForLDSuite: this.matchKeyForLDSuite.bind(this), + /** {@inheritdoc @veramo/credential-w3c#AbstractCredentialProvider.canVerifyDocumentType */ + canVerifyDocumentType(args: ICanVerifyDocumentTypeArgs): boolean { + const { document } = args + + for (const suite of this.ldCredentialModule.ldSuiteLoader.getAllSignatureSuites()) { + if (suite.getSupportedProofType() === (document)?.proof?.type || '') { + return true + } } + + return false } - /** {@inheritdoc ICredentialIssuerLD.createVerifiablePresentationLD} */ - public async createVerifiablePresentationLD( - args: ICreateVerifiablePresentationLDArgs, - context: IRequiredContext, + /** {@inheritdoc ICredentialIssuer.createVerifiablePresentationLD} */ + async createVerifiablePresentation( + args: ICreateVerifiablePresentationArgs, + context: IssuerAgentContext, ): Promise { const presentationContext = processEntryToArray( args?.presentation?.['@context'], @@ -139,10 +161,10 @@ export class CredentialIssuerLD implements IAgentPlugin { } } - /** {@inheritdoc ICredentialIssuerLD.createVerifiableCredentialLD} */ - public async createVerifiableCredentialLD( - args: ICreateVerifiableCredentialLDArgs, - context: IRequiredContext, + /** {@inheritdoc ICredentialIssuer.createVerifiableCredentialLD} */ + async createVerifiableCredential( + args: ICreateVerifiableCredentialArgs, + context: IssuerAgentContext, ): Promise { const credentialContext = processEntryToArray( args?.credential?.['@context'], @@ -193,27 +215,26 @@ export class CredentialIssuerLD implements IAgentPlugin { } } - /** {@inheritdoc ICredentialIssuerLD.verifyCredentialLD} */ - public async verifyCredentialLD( - args: IVerifyCredentialLDArgs, - context: IRequiredContext, - ): Promise { + /** {@inheritdoc ICredentialIssuer.verifyCredentialLD} */ + async verifyCredential(args: IVerifyCredentialArgs, context: VerifierAgentContext): Promise { + args.credential = args.credential as VerifiableCredential const credential = args.credential - let { now } = args - if (typeof now === 'number') { - now = new Date(now * 1000) + let now = new Date() + + if (args.policies?.now && typeof args.policies?.now === 'number') { + now = new Date(args.policies?.now * 1000) } return this.ldCredentialModule.verifyCredential(credential, { ...args, now }, context) } - /** {@inheritdoc ICredentialIssuerLD.verifyPresentationLD} */ - public async verifyPresentationLD( - args: IVerifyPresentationLDArgs, - context: IRequiredContext, - ): Promise { - const presentation = args.presentation + /** {@inheritdoc ICredentialVerifier.verifyPresentation} */ + async verifyPresentation( + args: IVerifyPresentationArgs, + context: VerifierAgentContext, + ): Promise { + const presentation = args.presentation as VerifiablePresentation let { now } = args if (typeof now === 'number') { now = new Date(now * 1000) @@ -271,7 +292,7 @@ export class CredentialIssuerLD implements IAgentPlugin { * * @internal */ - async matchKeyForLDSuite(k: IKey): Promise { + matchKeyForLDSuite(k: IKey): boolean { // prefilter based on key algorithms switch (k.type) { case 'Ed25519': diff --git a/packages/credential-ld/src/__tests__/issue-verify-ed25519-2020.test.ts b/packages/credential-ld/src/__tests__/issue-verify-ed25519-2020.test.ts index 944ed8808..39d0d403a 100644 --- a/packages/credential-ld/src/__tests__/issue-verify-ed25519-2020.test.ts +++ b/packages/credential-ld/src/__tests__/issue-verify-ed25519-2020.test.ts @@ -1,143 +1,143 @@ import { - createAgent, - CredentialPayload, - ICredentialPlugin, - IDIDManager, - IIdentifier, - IKeyManager, - IResolver, - TAgent, - } from '../../../core/src' - import { CredentialPlugin } from '../../../credential-w3c/src' - import { DIDManager, MemoryDIDStore } from '../../../did-manager/src' - import { KeyManager, MemoryKeyStore, MemoryPrivateKeyStore } from '../../../key-manager/src' - import { KeyManagementSystem } from '../../../kms-local/src' - import { DIDResolverPlugin } from '../../../did-resolver/src' - import { ContextDoc } from '../types' - import { CredentialIssuerLD } from '../action-handler' - import { LdDefaultContexts } from '../ld-default-contexts' - import { VeramoEd25519Signature2020 } from '../suites/Ed25519Signature2020' - import { Resolver } from 'did-resolver' - import { FakeDidProvider, FakeDidResolver } from '../../../test-utils/src/fake-did' - import { jest } from '@jest/globals' + createAgent, + CredentialPayload, + ICredentialPlugin, + IDIDManager, + IIdentifier, + IKeyManager, + IResolver, + TAgent, +} from '../../../core/src' +import { CredentialPlugin } from '../../../credential-w3c/src' +import { DIDManager, MemoryDIDStore } from '../../../did-manager/src' +import { KeyManager, MemoryKeyStore, MemoryPrivateKeyStore } from '../../../key-manager/src' +import { KeyManagementSystem } from '../../../kms-local/src' +import { DIDResolverPlugin } from '../../../did-resolver/src' +import { ContextDoc } from '../types' +import { CredentialProviderLD } from '../CredentialProviderLD' +import { LdDefaultContexts } from '../ld-default-contexts' +import { VeramoEd25519Signature2020 } from '../suites/Ed25519Signature2020' +import { Resolver } from 'did-resolver' +import { FakeDidProvider, FakeDidResolver } from '../../../test-utils/src/fake-did' +import { jest } from '@jest/globals' - jest.setTimeout(300000) +jest.setTimeout(300000) - const customContext: Record = { - 'custom:example.context': { - '@context': { - nothing: 'custom:example.context#blank', - }, +const customContext: Record = { + 'custom:example.context': { + '@context': { + nothing: 'custom:example.context#blank', }, - } + }, +} - describe('credential-LD full flow', () => { - let didFakeIdentifier: IIdentifier - let agent: TAgent +describe('credential-LD full flow', () => { + let didFakeIdentifier: IIdentifier + let agent: TAgent - beforeAll(async () => { - agent = createAgent({ - plugins: [ - new KeyManager({ - store: new MemoryKeyStore(), - kms: { - local: new KeyManagementSystem(new MemoryPrivateKeyStore()), - }, - }), - new DIDManager({ - providers: { - 'did:fake': new FakeDidProvider(), - }, - store: new MemoryDIDStore(), - defaultProvider: 'did:fake', - }), - new DIDResolverPlugin({ - resolver: new Resolver({ - ...new FakeDidResolver(() => agent, true).getDidFakeResolver(), - }), - }), - new CredentialPlugin(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts, customContext], - suites: [new VeramoEd25519Signature2020()], - }), - ], - }) - didFakeIdentifier = await agent.didManagerImport({ - did: 'did:fake:z6MkgbqNU4uF9NKSz5BqJQ4XKVHuQZYcUZP8pXGsJC8nTHwo', - keys: [ - { - type: 'Ed25519', - kid: 'didcomm-senderKey-1', - publicKeyHex: '1fe9b397c196ab33549041b29cf93be29b9f2bdd27322f05844112fad97ff92a', - privateKeyHex: - 'b57103882f7c66512dc96777cbafbeb2d48eca1e7a867f5a17a84e9a6740f7dc1fe9b397c196ab33549041b29cf93be29b9f2bdd27322f05844112fad97ff92a', - kms: 'local', + beforeAll(async () => { + const ld = new CredentialProviderLD({ + contextMaps: [LdDefaultContexts, customContext], + suites: [new VeramoEd25519Signature2020()], + }) + agent = createAgent({ + plugins: [ + new KeyManager({ + store: new MemoryKeyStore(), + kms: { + local: new KeyManagementSystem(new MemoryPrivateKeyStore()), }, - ], - services: [ - { - id: 'msg1', - type: 'DIDCommMessaging', - serviceEndpoint: 'http://localhost:3002/messaging', + }), + new DIDManager({ + providers: { + 'did:fake': new FakeDidProvider(), }, - ], - provider: 'did:fake', - alias: 'sender', - }) + store: new MemoryDIDStore(), + defaultProvider: 'did:fake', + }), + new DIDResolverPlugin({ + resolver: new Resolver({ + ...new FakeDidResolver(() => agent, true).getDidFakeResolver(), + }), + }), + new CredentialPlugin({ issuers: [ld] }), + ], }) - - it('works with Ed25519Signature2020 credential', async () => { - const credential: CredentialPayload = { - issuer: didFakeIdentifier.did, - '@context': ['custom:example.context'], - credentialSubject: { - nothing: 'else matters', + didFakeIdentifier = await agent.didManagerImport({ + did: 'did:fake:z6MkgbqNU4uF9NKSz5BqJQ4XKVHuQZYcUZP8pXGsJC8nTHwo', + keys: [ + { + type: 'Ed25519', + kid: 'didcomm-senderKey-1', + publicKeyHex: '1fe9b397c196ab33549041b29cf93be29b9f2bdd27322f05844112fad97ff92a', + privateKeyHex: + 'b57103882f7c66512dc96777cbafbeb2d48eca1e7a867f5a17a84e9a6740f7dc1fe9b397c196ab33549041b29cf93be29b9f2bdd27322f05844112fad97ff92a', + kms: 'local', + }, + ], + services: [ + { + id: 'msg1', + type: 'DIDCommMessaging', + serviceEndpoint: 'http://localhost:3002/messaging', }, - } - const verifiableCredential = await agent.createVerifiableCredential({ - credential, - proofFormat: 'lds', - }) + ], + provider: 'did:fake', + alias: 'sender', + }) + }) - expect(verifiableCredential).toBeDefined() + it('works with Ed25519Signature2020 credential', async () => { + const credential: CredentialPayload = { + issuer: didFakeIdentifier.did, + '@context': ['custom:example.context'], + credentialSubject: { + nothing: 'else matters', + }, + } + const verifiableCredential = await agent.createVerifiableCredential({ + credential, + proofFormat: 'lds', + }) - const result = await agent.verifyCredential({ - credential: verifiableCredential, - }) + expect(verifiableCredential).toBeDefined() - expect(result.verified).toBe(true) + const result = await agent.verifyCredential({ + credential: verifiableCredential, }) - it('works with Ed25519Signature2020 credential and presentation', async () => { - const credential: CredentialPayload = { - issuer: didFakeIdentifier.did, - '@context': ['custom:example.context'], - credentialSubject: { - nothing: 'else matters', - }, - } - const verifiableCredential1 = await agent.createVerifiableCredential({ - credential, - proofFormat: 'lds', - }) + expect(result.verified).toBe(true) + }) - const verifiablePresentation = await agent.createVerifiablePresentation({ - presentation: { - verifiableCredential: [verifiableCredential1], - holder: didFakeIdentifier.did, - }, - challenge: 'VERAMO', - proofFormat: 'lds', - }) + it('works with Ed25519Signature2020 credential and presentation', async () => { + const credential: CredentialPayload = { + issuer: didFakeIdentifier.did, + '@context': ['custom:example.context'], + credentialSubject: { + nothing: 'else matters', + }, + } + const verifiableCredential1 = await agent.createVerifiableCredential({ + credential, + proofFormat: 'lds', + }) - expect(verifiablePresentation).toBeDefined() + const verifiablePresentation = await agent.createVerifiablePresentation({ + presentation: { + verifiableCredential: [verifiableCredential1], + holder: didFakeIdentifier.did, + }, + challenge: 'VERAMO', + proofFormat: 'lds', + }) - const result = await agent.verifyPresentation({ - presentation: verifiablePresentation, - challenge: 'VERAMO', - }) + expect(verifiablePresentation).toBeDefined() - expect(result.verified).toBe(true) + const result = await agent.verifyPresentation({ + presentation: verifiablePresentation, + challenge: 'VERAMO', }) + + expect(result.verified).toBe(true) }) +}) diff --git a/packages/credential-ld/src/__tests__/issue-verify-flow.test.ts b/packages/credential-ld/src/__tests__/issue-verify-flow-ld.test.ts similarity index 95% rename from packages/credential-ld/src/__tests__/issue-verify-flow.test.ts rename to packages/credential-ld/src/__tests__/issue-verify-flow-ld.test.ts index 543d0632a..dbdd80009 100644 --- a/packages/credential-ld/src/__tests__/issue-verify-flow.test.ts +++ b/packages/credential-ld/src/__tests__/issue-verify-flow-ld.test.ts @@ -16,7 +16,7 @@ import { getDidKeyResolver, KeyDIDProvider } from '../../../did-provider-key/src import { DIDResolverPlugin } from '../../../did-resolver/src' import { EthrDIDProvider } from '../../../did-provider-ethr/src' import { ContextDoc } from '../types.js' -import { CredentialIssuerLD } from '../action-handler.js' +import { CredentialProviderLD } from '../CredentialProviderLD.js' import { LdDefaultContexts } from '../ld-default-contexts.js' import { VeramoEd25519Signature2018 } from '../suites/Ed25519Signature2018.js' import { Resolver } from 'did-resolver' @@ -43,6 +43,11 @@ describe('credential-LD full flow', () => { let didEthrIdentifier: IIdentifier let agent: TAgent + const ld = new CredentialProviderLD({ + contextMaps: [LdDefaultContexts, customContext], + suites: [new VeramoEd25519Signature2018(), new VeramoEcdsaSecp256k1RecoverySignature2020()], + }) + beforeAll(async () => { agent = createAgent({ plugins: [ @@ -69,11 +74,7 @@ describe('credential-LD full flow', () => { ...ethrDidResolver({ infuraProjectId }), }), }), - new CredentialPlugin(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts, customContext], - suites: [new VeramoEd25519Signature2018(), new VeramoEcdsaSecp256k1RecoverySignature2020()], - }), + new CredentialPlugin({ issuers: [ld] }), ], }) didKeyIdentifier = await agent.didManagerCreate() diff --git a/packages/credential-ld/src/index.ts b/packages/credential-ld/src/index.ts index fd30b8b23..5df6af7e8 100644 --- a/packages/credential-ld/src/index.ts +++ b/packages/credential-ld/src/index.ts @@ -1,7 +1,7 @@ /** - * Provides a {@link @veramo/credential-ld#CredentialIssuerLD | plugin} for the {@link @veramo/core#Agent} that + * Provides a {@link @veramo/credential-ld#CredentialProviderLD | handler} for the {@link @veramo/credential-w3c#CredentialPlugin} that * implements - * {@link @veramo/credential-ld#ICredentialIssuerLD} interface. + * {@link @veramo/core-types#AbstractCredentialProvider} interface. * * This plugin adds support for working with JSON-LD credentials. * When installed, this plugin will be automatically used by @@ -10,7 +10,7 @@ * * @packageDocumentation */ -export { CredentialIssuerLD } from './action-handler.js' +export { CredentialProviderLD } from './CredentialProviderLD.js' export * from './types.js' export { LdDefaultContexts } from './ld-default-contexts.js' export { VeramoLdSignature } from './ld-suites.js' @@ -18,4 +18,3 @@ export * from './suites/EcdsaSecp256k1RecoverySignature2020.js' export * from './suites/Ed25519Signature2018.js' export * from './suites/Ed25519Signature2020.js' export * from './suites/JsonWebSignature2020.js' -export { schema } from './plugin.schema.js' diff --git a/packages/credential-ld/src/ld-credential-module.ts b/packages/credential-ld/src/ld-credential-module.ts index acaea3e99..771e946ed 100644 --- a/packages/credential-ld/src/ld-credential-module.ts +++ b/packages/credential-ld/src/ld-credential-module.ts @@ -3,6 +3,7 @@ import { IAgentContext, IKey, IResolver, + IVerifyResult, PresentationPayload, UsingResolutionOptions, VerifiableCredential, @@ -22,7 +23,7 @@ const debug = Debug('veramo:w3c:ld-credential-module') type ForwardedOptions = UsingResolutionOptions & { fetchRemoteContexts?: boolean // defaults to false - now?: number // defaults to Date.now() + now?: Date // defaults to Date.now() } export class LdCredentialModule { @@ -170,7 +171,7 @@ export class LdCredentialModule { credential: VerifiableCredential, options: ForwardedOptions, context: IAgentContext, - ): Promise { + ): Promise { const fetchRemoteContexts = options.fetchRemoteContexts ?? false const result = await vc.verifyCredential({ ...options, @@ -195,7 +196,7 @@ export class LdCredentialModule { domain: string | undefined, options: ForwardedOptions, context: IAgentContext, - ): Promise { + ): Promise { const fetchRemoteContexts = options.fetchRemoteContexts ?? false const result = await vc.verify({ ...options, diff --git a/packages/credential-ld/src/ld-default-contexts.ts b/packages/credential-ld/src/ld-default-contexts.ts index 78c763859..50bd65ddf 100644 --- a/packages/credential-ld/src/ld-default-contexts.ts +++ b/packages/credential-ld/src/ld-default-contexts.ts @@ -20,7 +20,7 @@ import schema_org from './contexts/schema.org.json' assert { type: 'json' } * Provides a hardcoded map of common Linked Data `@context` definitions. * * You can use this to bootstrap the `@context` definitions used by - * {@link @veramo/credential-ld#CredentialIssuerLD | CredentialIssuerLD} with these common context definitions. + * {@link @veramo/credential-ld#CredentialProviderLD | CredentialProviderLD} with these common context definitions. * * @beta This API may change without a BREAKING CHANGE notice. */ diff --git a/packages/credential-ld/src/ld-suites.ts b/packages/credential-ld/src/ld-suites.ts index 61ad61609..1bf541d0c 100644 --- a/packages/credential-ld/src/ld-suites.ts +++ b/packages/credential-ld/src/ld-suites.ts @@ -25,6 +25,8 @@ export abstract class VeramoLdSignature { abstract getSupportedVerificationType(): string | string[] + abstract getSupportedProofType(): string + abstract getSupportedVeramoKeyType(): TKeyType abstract getSuiteForSigning( diff --git a/packages/credential-ld/src/plugin.schema.ts b/packages/credential-ld/src/plugin.schema.ts deleted file mode 100644 index c5766ea5a..000000000 --- a/packages/credential-ld/src/plugin.schema.ts +++ /dev/null @@ -1,476 +0,0 @@ -export const schema = { - "ICredentialIssuerLD": { - "components": { - "schemas": { - "ICreateVerifiableCredentialLDArgs": { - "type": "object", - "properties": { - "resolutionOptions": { - "type": "object", - "properties": { - "publicKeyFormat": { - "type": "string" - }, - "accept": { - "type": "string" - } - }, - "description": "Options to be passed to the DID resolver." - }, - "credential": { - "$ref": "#/components/schemas/CredentialPayload", - "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`\n\n`@context`, `type` and `issuanceDate` will be added automatically if omitted" - }, - "keyRef": { - "type": "string", - "description": "Optional. The key handle ( {@link @veramo/core-types#IKey.kid | IKey.kid } ) from the internal database." - }, - "fetchRemoteContexts": { - "type": "boolean", - "description": "Set this to true if you want the `@context` URLs to be fetched in case they are not preloaded.\n\nDefaults to `false`" - } - }, - "required": [ - "credential" - ], - "additionalProperties": { - "description": "Any other options that can be forwarded to the lower level libraries" - }, - "description": "Encapsulates the parameters required to create a {@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": { - "$ref": "#/components/schemas/ContextType" - }, - "issuanceDate": { - "$ref": "#/components/schemas/DateType" - }, - "expirationDate": { - "$ref": "#/components/schemas/DateType" - }, - "credentialStatus": { - "$ref": "#/components/schemas/CredentialStatusReference" - }, - "id": { - "type": "string" - } - }, - "required": [ - "issuer" - ], - "description": "Used as input when creating Verifiable Credentials" - }, - "IssuerType": { - "anyOf": [ - { - "type": "object", - "properties": { - "id": { - "type": "string" - } - }, - "required": [ - "id" - ] - }, - { - "type": "string" - } - ], - "description": "The issuer of a {@link VerifiableCredential } or the holder of a {@link VerifiablePresentation } .\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 }" - }, - "ContextType": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object" - }, - { - "type": "array", - "items": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object" - } - ] - } - } - ], - "description": "The data type for `@context` properties of credentials, presentations, etc." - }, - "DateType": { - "type": "string", - "description": "Represents an issuance or expiration date for Credentials / Presentations. This is used as input when creating them." - }, - "CredentialStatusReference": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "type": { - "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": { - "proof": { - "$ref": "#/components/schemas/ProofType" - }, - "issuer": { - "$ref": "#/components/schemas/IssuerType" - }, - "credentialSubject": { - "$ref": "#/components/schemas/CredentialSubject" - }, - "type": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "string" - } - ] - }, - "@context": { - "$ref": "#/components/schemas/ContextType" - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "credentialStatus": { - "$ref": "#/components/schemas/CredentialStatusReference" - }, - "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 {@link VerifiableCredential } or {@link VerifiablePresentation }" - }, - "ICreateVerifiablePresentationLDArgs": { - "type": "object", - "properties": { - "resolutionOptions": { - "type": "object", - "properties": { - "publicKeyFormat": { - "type": "string" - }, - "accept": { - "type": "string" - } - }, - "description": "Options to be passed to the DID resolver." - }, - "presentation": { - "$ref": "#/components/schemas/PresentationPayload", - "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`\n\n`@context`, `type` and `issuanceDate` will be added automatically if omitted." - }, - "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." - }, - "keyRef": { - "type": "string", - "description": "Optional. The key handle ( {@link @veramo/core-types#IKey.kid | IKey.kid } ) from the internal database." - }, - "fetchRemoteContexts": { - "type": "boolean", - "description": "Set this to true if you want the `@context` URLs to be fetched in case they are not preloaded.\n\nDefaults to `false`" - } - }, - "required": [ - "presentation" - ], - "additionalProperties": { - "description": "Any other options that can be forwarded to the lower level libraries" - }, - "description": "Encapsulates the parameters required to create a {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation }" - }, - "PresentationPayload": { - "type": "object", - "properties": { - "holder": { - "type": "string" - }, - "verifiableCredential": { - "type": "array", - "items": { - "$ref": "#/components/schemas/W3CVerifiableCredential" - } - }, - "type": { - "type": "array", - "items": { - "type": "string" - } - }, - "@context": { - "$ref": "#/components/schemas/ContextType" - }, - "verifier": { - "type": "array", - "items": { - "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 } See {@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/W3CVerifiableCredential" - } - }, - "type": { - "anyOf": [ - { - "type": "array", - "items": { - "type": "string" - } - }, - { - "type": "string" - } - ] - }, - "@context": { - "$ref": "#/components/schemas/ContextType" - }, - "verifier": { - "type": "array", - "items": { - "type": "string" - } - }, - "issuanceDate": { - "type": "string" - }, - "expirationDate": { - "type": "string" - }, - "id": { - "type": "string" - } - }, - "required": [ - "@context", - "holder", - "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 }" - }, - "IVerifyCredentialLDArgs": { - "type": "object", - "properties": { - "resolutionOptions": { - "type": "object", - "properties": { - "publicKeyFormat": { - "type": "string" - }, - "accept": { - "type": "string" - } - }, - "description": "Options to be passed to the DID resolver." - }, - "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 Credential is chosen based on the `issuer.id` property of the `credential`" - }, - "fetchRemoteContexts": { - "type": "boolean", - "description": "Set this to true if you want the `@context` URLs to be fetched in case they are not preloaded.\n\nDefaults to `false`" - } - }, - "required": [ - "credential" - ], - "additionalProperties": { - "description": "Any other options that can be forwarded to the lower level libraries" - }, - "description": "Encapsulates the parameters required to verify a {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential }" - }, - "IVerifyPresentationLDArgs": { - "type": "object", - "properties": { - "resolutionOptions": { - "type": "object", - "properties": { - "publicKeyFormat": { - "type": "string" - }, - "accept": { - "type": "string" - } - }, - "description": "Options to be passed to the DID resolver." - }, - "presentation": { - "$ref": "#/components/schemas/VerifiablePresentation", - "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`" - }, - "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" - }, - "fetchRemoteContexts": { - "type": "boolean", - "description": "Set this to true if you want the `@context` URLs to be fetched in case they are not preloaded.\n\nDefaults to `false`" - } - }, - "required": [ - "presentation" - ], - "additionalProperties": { - "description": "Any other options that can be forwarded to the lower level libraries" - }, - "description": "Encapsulates the parameters required to verify a {@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/ICreateVerifiableCredentialLDArgs" - }, - "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/ICreateVerifiablePresentationLDArgs" - }, - "returnType": { - "$ref": "#/components/schemas/VerifiablePresentation" - } - }, - "verifyCredentialLD": { - "description": "Verifies a Verifiable Credential JWT or LDS Format.", - "arguments": { - "$ref": "#/components/schemas/IVerifyCredentialLDArgs" - }, - "returnType": { - "type": "boolean" - } - }, - "verifyPresentationLD": { - "description": "Verifies a Verifiable Presentation JWT or LDS Format.", - "arguments": { - "$ref": "#/components/schemas/IVerifyPresentationLDArgs" - }, - "returnType": { - "type": "boolean" - } - } - } - } - } -} \ No newline at end of file diff --git a/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts index cdac124bb..7adfc5311 100644 --- a/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts +++ b/packages/credential-ld/src/suites/EcdsaSecp256k1RecoverySignature2020.ts @@ -17,6 +17,11 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature // 'JsonWebKey2020', 'Multikey'] } + getSupportedProofType(): string { + return 'EcdsaSecp256k1RecoverySignature2020' + } + + getSupportedVeramoKeyType(): TKeyType { return 'Secp256k1' } @@ -71,7 +76,7 @@ export class VeramoEcdsaSecp256k1RecoverySignature2020 extends VeramoLdSignature return new EcdsaSecp256k1RecoverySignature2020() } - preSigningCredModification(credential: CredentialPayload): void {} + preSigningCredModification(credential: CredentialPayload): void { } async preDidResolutionModification(didUrl: string, didDoc: DIDDocument): Promise { const ctx = asArray(didDoc['@context']) diff --git a/packages/credential-ld/src/suites/Ed25519Signature2018.ts b/packages/credential-ld/src/suites/Ed25519Signature2018.ts index 17b03a515..0b81b5a7c 100644 --- a/packages/credential-ld/src/suites/Ed25519Signature2018.ts +++ b/packages/credential-ld/src/suites/Ed25519Signature2018.ts @@ -14,6 +14,11 @@ export class VeramoEd25519Signature2018 extends VeramoLdSignature { // TODO: add support for ['Ed25519VerificationKey2020', 'Multikey'] } + getSupportedProofType(): string { + return 'Ed25519Signature2018' + } + + getSupportedVeramoKeyType(): TKeyType { return 'Ed25519' } diff --git a/packages/credential-ld/src/suites/Ed25519Signature2020.ts b/packages/credential-ld/src/suites/Ed25519Signature2020.ts index f90a273b9..ed917c2e5 100644 --- a/packages/credential-ld/src/suites/Ed25519Signature2020.ts +++ b/packages/credential-ld/src/suites/Ed25519Signature2020.ts @@ -35,6 +35,10 @@ export class VeramoEd25519Signature2020 extends VeramoLdSignature { // TODO: add support for ['JsonWebKey2020', 'Multikey'] } + getSupportedProofType(): string { + return 'Ed25519Signature2020' + } + getSupportedVeramoKeyType(): TKeyType { return 'Ed25519' } @@ -136,7 +140,7 @@ export class VeramoEd25519Signature2020 extends VeramoLdSignature { private transformVerificationMethod(vm: VerificationMethod): VerificationMethod { if (vm.type === 'Ed25519VerificationKey2020') { - ;(vm as any)['@context'] = 'https://w3id.org/security/suites/ed25519-2020/v1' + ; (vm as any)['@context'] = 'https://w3id.org/security/suites/ed25519-2020/v1' // publicKeyMultibase is required by this suite if (!vm.publicKeyMultibase) { const { publicKeyHex } = extractPublicKeyHex(vm) diff --git a/packages/credential-ld/src/suites/JsonWebSignature2020.ts b/packages/credential-ld/src/suites/JsonWebSignature2020.ts index adc7278af..fc4d4b793 100644 --- a/packages/credential-ld/src/suites/JsonWebSignature2020.ts +++ b/packages/credential-ld/src/suites/JsonWebSignature2020.ts @@ -19,7 +19,11 @@ import { export class VeramoJsonWebSignature2020 extends VeramoLdSignature { getSupportedVerificationType(): string { return 'JsonWebKey2020' - // TODO: add support for ['Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', 'Multikey'] and others + // TODO: add support for ['Ed25519VerificationKey2018', 'Ed25519VerificationKey2020', 'Multikey'] and others + } + + getSupportedProofType(): string { + return 'JsonWebSignature2020' } getSupportedVeramoKeyType(): TKeyType { diff --git a/packages/credential-ld/src/types.ts b/packages/credential-ld/src/types.ts index 8ec53c72a..0acdd5306 100644 --- a/packages/credential-ld/src/types.ts +++ b/packages/credential-ld/src/types.ts @@ -1,268 +1,3 @@ -import { - CredentialPayload, - IAgentContext, - IDIDManager, - IKey, - IKeyManager, - IPluginMethodMap, - IResolver, - PresentationPayload, - UsingResolutionOptions, - VerifiableCredential, - VerifiablePresentation, -} from '@veramo/core-types' - -/** - * The interface definition for a plugin that can issue and verify Verifiable Credentials and Presentations - * that use JSON-LD format (also called Data Integrity Proofs). - * - * @remarks Please see {@link https://www.w3.org/TR/vc-data-model | W3C Verifiable Credentials data model} - * @see {@link https://www.w3.org/TR/vc-data-model/#data-integrity-proofs | Data Integrity proofs} - * - * @beta This API may 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-types#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 may 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-types#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 may 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 may 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 may change without a BREAKING CHANGE notice. - */ - verifyPresentationLD(args: IVerifyPresentationLDArgs, context: IRequiredContext): Promise - - /** - * Returns true if the key is supported by any of the installed LD Signature suites - * @param key - the key to verify - * @param context - This reserved param is automatically added and handled by the framework, *do not override* - * - * @internal - */ - matchKeyForLDSuite(key: IKey, context: IAgentContext<{}>): Promise -} - -/** - * Encapsulates the parameters required to create a - * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} - * - * @beta This API may change without a BREAKING CHANGE notice. - */ -export interface ICreateVerifiablePresentationLDArgs extends UsingResolutionOptions { - /** - * 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: PresentationPayload - - /** - * 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 @veramo/core-types#IKey.kid | IKey.kid}) from the internal database. - */ - keyRef?: string - - /** - * Set this to true if you want the `@context` URLs to be fetched in case they are not preloaded. - * - * Defaults to `false` - */ - fetchRemoteContexts?: boolean - - /** - * Any other options that can be forwarded to the lower level libraries - */ - [x: string]: any -} - -/** - * Encapsulates the parameters required to create a - * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} - * - * @beta This API may change without a BREAKING CHANGE notice. - */ -export interface ICreateVerifiableCredentialLDArgs extends UsingResolutionOptions { - /** - * 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: CredentialPayload - - /** - * Optional. The key handle ({@link @veramo/core-types#IKey.kid | IKey.kid}) from the internal database. - */ - keyRef?: string - - /** - * Set this to true if you want the `@context` URLs to be fetched in case they are not preloaded. - * - * Defaults to `false` - */ - fetchRemoteContexts?: boolean - - /** - * Any other options that can be forwarded to the lower level libraries - */ - [x: string]: any -} - -/** - * Encapsulates the parameters required to verify a - * {@link https://www.w3.org/TR/vc-data-model/#credentials | W3C Verifiable Credential} - * - * @beta This API may change without a BREAKING CHANGE notice - */ -export interface IVerifyCredentialLDArgs extends UsingResolutionOptions { - /** - * 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 - - /** - * Set this to true if you want the `@context` URLs to be fetched in case they are not preloaded. - * - * Defaults to `false` - */ - fetchRemoteContexts?: boolean - - /** - * Any other options that can be forwarded to the lower level libraries - */ - [x: string]: any -} - -/** - * Encapsulates the parameters required to verify a - * {@link https://www.w3.org/TR/vc-data-model/#presentations | W3C Verifiable Presentation} - * - * @beta This API may change without a BREAKING CHANGE notice. - */ -export interface IVerifyPresentationLDArgs extends UsingResolutionOptions { - /** - * 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 - - /** - * Set this to true if you want the `@context` URLs to be fetched in case they are not preloaded. - * - * Defaults to `false` - */ - fetchRemoteContexts?: boolean - - /** - * Any other options that can be forwarded to the lower level libraries - */ - [x: string]: any -} - -/** - * 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 may change without a BREAKING CHANGE notice. - */ -export type IRequiredContext = IAgentContext< - IResolver & Pick & Pick -> - /** * Describes a document with a `@context` property. * diff --git a/packages/credential-w3c/src/__tests__/action-handler.test.ts b/packages/credential-w3c/src/__tests__/action-handler.test.ts index c2b29dc56..cf29aae47 100644 --- a/packages/credential-w3c/src/__tests__/action-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/action-handler.test.ts @@ -12,130 +12,73 @@ import { jest } from '@jest/globals' import { CredentialPayload, ICredentialPlugin, - IDataStore, IDIDManager, IIdentifier, - IKey, IKeyManager, IResolver, PresentationPayload, TAgent, - VerifiableCredential, } from '../../../core-types/src' import { CredentialPlugin } from '../action-handler.js' - -const mockIdentifiers: IIdentifier[] = [ - { - did: 'did:example:111', - provider: 'mock', - controllerKeyId: 'kid1', - keys: [ - { - kid: 'kid1', - publicKeyHex: 'pub', - type: 'Secp256k1', - kms: 'mock', - }, - ], - services: [], - }, - { - did: 'did:example:222', - provider: 'mock', - controllerKeyId: 'kid2', - keys: [ - { - kid: 'kid2a', - publicKeyHex: 'pub', - type: 'Ed25519', - kms: 'mock', - }, - { - kid: 'kid2b', - publicKeyHex: 'pub', - type: 'Secp256k1', - kms: 'mock', - }, - ], - services: [], - }, - { - did: 'did:example:333', - provider: 'mock', - controllerKeyId: 'kid3', - keys: [ - { - kid: 'kid3', - publicKeyHex: 'pub', - type: 'Ed25519', - kms: 'mock', - }, - ], - services: [], - }, - { - did: 'did:example:444?versionTime=2023-01-01T00:00:00Z', - provider: 'mock', - controllerKeyId: 'kid4', - keys: [ - { - kid: 'kid4', - publicKeyHex: 'pub', - type: 'Ed25519', - kms: 'mock', - }, - ], - services: [], - }, -] - -const w3c = new CredentialPlugin() - -let agent = { - execute: jest.fn(), - availableMethods: jest.fn(), - resolveDid: jest.fn(), - getDIDComponentById: jest.fn(), - emit: jest.fn(), - keyManagerSign: jest.fn(async (args): Promise => 'mockJWT'), - keyManagerGet: jest.fn( - async (args): Promise => ({ - kid: '', - kms: '', - type: 'Ed25519', - publicKeyHex: '', - }), - ), - dataStoreSaveVerifiableCredential: jest.fn(async (args): Promise => true), - dataStoreSaveVerifiablePresentation: jest.fn(async (args): Promise => true), - getSchema: jest.fn(), - didManagerGet: jest.fn(), - didManagerFind: jest.fn(), - createVerifiableCredentialLD: jest.fn(), - createVerifiablePresentationLD: jest.fn(), - verifyCredentialLD: jest.fn(), - verifyPresentationLD: jest.fn(), -} as any as TAgent +import { CredentialProviderJWT } from '../../../credential-jwt/src' +import { createAgent } from '../../../core/src/agent.js' +import { KeyManager } from '../../../key-manager/src/key-manager.js' +import { MemoryKeyStore, MemoryPrivateKeyStore } from '../../../key-manager/src' +import { KeyManagementSystem } from '../../../kms-local/src' +import { DIDManager, MemoryDIDStore } from '../../../did-manager/src' +import { getDidKeyResolver, KeyDIDProvider } from '../../../did-provider-key/src' +import { EthrDIDProvider } from '../../../did-provider-ethr/src' +import { DIDResolverPlugin } from '../../../did-resolver/src' +import { Resolver } from 'did-resolver' +import { getResolver as ethrDidResolver } from 'ethr-did-resolver' + +const infuraProjectId = '3586660d179141e3801c3895de1c2eba' + +let didKeyIdentifier: IIdentifier +let didEthrIdentifier: IIdentifier +let agent: TAgent describe('@veramo/credential-w3c', () => { - const keyManagerSign = agent.keyManagerSign as - | jest.Mock<(args: { algorithm: string; keyRef: string}) => Promise> - beforeEach(() => { - keyManagerSign.mockClear() - }); + beforeAll(async () => { - test.each(mockIdentifiers)('handles createVerifiableCredential', async (mockIdentifier) => { - expect.assertions(6) - - const issuerId = mockIdentifier.did; - mockIdentifier.did = mockIdentifier.did.replace(/\?.*$/, '') + const jwt = new CredentialProviderJWT() + agent = createAgent({ + plugins: [ + new KeyManager({ + store: new MemoryKeyStore(), + kms: { + local: new KeyManagementSystem(new MemoryPrivateKeyStore()), + }, + }), + new DIDManager({ + providers: { + 'did:key': new KeyDIDProvider({ defaultKms: 'local' }), + 'did:ethr': new EthrDIDProvider({ + defaultKms: 'local', + network: 'mainnet', + }), + }, + store: new MemoryDIDStore(), + defaultProvider: 'did:key', + }), + new DIDResolverPlugin({ + resolver: new Resolver({ + ...getDidKeyResolver(), + ...ethrDidResolver({ infuraProjectId }), + }), + }), + new CredentialPlugin({ issuers: [jwt] }), + ], + }) + didKeyIdentifier = await agent.didManagerCreate() + didEthrIdentifier = await agent.didManagerCreate({ provider: 'did:ethr' }) + }) - const keyRef = mockIdentifier.keys[1]?.kid // Second key or undefined - const expectedKey = mockIdentifier.keys[mockIdentifier.keys.length - 1] + test('handles createVerifiableCredential', async () => { + expect.assertions(1) - agent.didManagerGet = jest.fn(async (args): Promise => mockIdentifier) - const context = { agent } + const issuerId = didEthrIdentifier.did const credential: CredentialPayload = { '@context': ['https://www.w3.org/2018/credentials/v1', 'https://www.w3.org/2020/demo/4342323'], @@ -154,35 +97,22 @@ describe('@veramo/credential-w3c', () => { }, } - const vc = await w3c.createVerifiableCredential( + const vc = await agent.createVerifiableCredential( { credential, save: false, proofFormat: 'jwt', - keyRef, }, - context, ) - // TODO Update these after refactoring did-jwt-vc - expect(context.agent.didManagerGet).toBeCalledWith({ did: mockIdentifier.did }) - expect(context.agent.dataStoreSaveVerifiableCredential).not.toBeCalled() expect(vc.id).toEqual('vc1') - - expect(keyManagerSign).toBeCalled() - expect(keyManagerSign.mock.calls[0][0].keyRef).toEqual(expectedKey.kid) - expect(keyManagerSign.mock.calls[0][0].algorithm).toEqual(expectedKey.type === 'Ed25519' ? 'EdDSA' : 'ES256K') }) - test.each(mockIdentifiers)('handles createVerifiablePresentation', async (mockIdentifier) => { - expect.assertions(4) - - const issuerId = mockIdentifier.did; - mockIdentifier.did = mockIdentifier.did.replace(/\?.*$/, '') + test('handles createVerifiablePresentation', async () => { + expect.assertions(1) - agent.didManagerGet = jest.fn(async (args): Promise => mockIdentifier) - const context = { agent } + const issuerId = didEthrIdentifier.did; - const credential = await w3c.createVerifiableCredential( + const credential = await agent.createVerifiableCredential( { credential: { '@context': ['https://www.w3.org/2018/credentials/v1'], @@ -203,29 +133,24 @@ describe('@veramo/credential-w3c', () => { save: false, proofFormat: 'jwt', }, - context, ) const presentation: PresentationPayload = { '@context': ['https://www.w3.org/2018/credentials/v1'], type: ['VerifiablePresentation'], - holder: mockIdentifier.did + '?versionTime=2023-01-01T00:00:00Z', + holder: didEthrIdentifier.did + '?versionTime=2023-01-01T00:00:00Z', issuanceDate: new Date().toISOString(), verifiableCredential: [credential], } - const vp = await w3c.createVerifiablePresentation( + const vp = await agent.createVerifiablePresentation( { presentation, save: false, proofFormat: 'jwt', }, - context, ) - expect(context.agent.didManagerGet).toBeCalledWith({ did: mockIdentifier.did }) - expect(context.agent.didManagerGet).not.toBeCalledWith({ did: presentation.holder }) - expect(context.agent.dataStoreSaveVerifiablePresentation).not.toBeCalled() - expect(vp.holder).toEqual(mockIdentifier.did) + expect(vp.holder).toEqual(issuerId) }) }) diff --git a/packages/credential-w3c/src/__tests__/issue-verify-flow.test.ts b/packages/credential-w3c/src/__tests__/issue-verify-flow-w3c.test.ts similarity index 95% rename from packages/credential-w3c/src/__tests__/issue-verify-flow.test.ts rename to packages/credential-w3c/src/__tests__/issue-verify-flow-w3c.test.ts index e7846bcf6..acbe01e7e 100644 --- a/packages/credential-w3c/src/__tests__/issue-verify-flow.test.ts +++ b/packages/credential-w3c/src/__tests__/issue-verify-flow-w3c.test.ts @@ -9,7 +9,7 @@ import { VerifiableCredential, } from '../../../core-types/src' import { createAgent } from '../../../core/src' -import { CredentialIssuer } from '../../../credential-w3c/src' +import { CredentialIssuer } from '..' import { DIDManager, MemoryDIDStore } from '../../../did-manager/src' import { KeyManager, MemoryKeyStore, MemoryPrivateKeyStore } from '../../../key-manager/src' import { KeyManagementSystem } from '../../../kms-local/src' @@ -18,7 +18,7 @@ import { DIDResolverPlugin } from '../../../did-resolver/src' import { EthrDIDProvider } from '../../../did-provider-ethr/src' import { ContextDoc, - CredentialIssuerLD, + CredentialProviderLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018, @@ -26,6 +26,7 @@ import { import { Resolver } from 'did-resolver' import { getResolver as ethrDidResolver } from 'ethr-did-resolver' import { jest } from '@jest/globals' +import { CredentialProviderJWT } from '../../../credential-jwt/src' jest.setTimeout(300000) @@ -46,6 +47,11 @@ describe('credential-w3c full flow', () => { let credential: CredentialPayload beforeAll(async () => { + const jwt = new CredentialProviderJWT() + const ld = new CredentialProviderLD({ + contextMaps: [LdDefaultContexts, customContext], + suites: [new VeramoEd25519Signature2018(), new VeramoEcdsaSecp256k1RecoverySignature2020()], + }) agent = createAgent({ plugins: [ new KeyManager({ @@ -71,11 +77,7 @@ describe('credential-w3c full flow', () => { ...ethrDidResolver({ infuraProjectId }), }), }), - new CredentialIssuer(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts, customContext], - suites: [new VeramoEd25519Signature2018(), new VeramoEcdsaSecp256k1RecoverySignature2020()], - }), + new CredentialIssuer({ issuers: [jwt, ld] }), ], }) didKeyIdentifier = await agent.didManagerCreate() @@ -112,7 +114,7 @@ describe('credential-w3c full flow', () => { credential, proofFormat: 'lds', }) - const modifiedCredential: VerifiableCredential = { ...verifiableCredential1, issuer: { id: 'did:fake:wrong' }} + const modifiedCredential: VerifiableCredential = { ...verifiableCredential1, issuer: { id: 'did:fake:wrong' } } const verifyResult = await agent.verifyCredential({ credential: modifiedCredential }) expect(verifyResult.verified).toBeFalsy() }) @@ -124,7 +126,7 @@ describe('credential-w3c full flow', () => { proofFormat: 'jwt', }) - const modifiedCredential: VerifiableCredential = { ...verifiableCredential1, issuer: { id: 'did:fake:wrong' }} + const modifiedCredential: VerifiableCredential = { ...verifiableCredential1, issuer: { id: 'did:fake:wrong' } } const verifyResult = await agent.verifyCredential({ credential: modifiedCredential }) expect(verifyResult.verified).toBeFalsy() diff --git a/packages/credential-w3c/src/__tests__/message-handler.test.ts b/packages/credential-w3c/src/__tests__/message-handler.test.ts index 043437e8b..f34b07bea 100644 --- a/packages/credential-w3c/src/__tests__/message-handler.test.ts +++ b/packages/credential-w3c/src/__tests__/message-handler.test.ts @@ -64,6 +64,7 @@ describe('@veramo/credential-w3c', () => { } } }, + canVerifyDocumentType: jest.fn(), verifyCredential: jest.fn(), verifyPresentation: jest.fn(), getDIDComponentById: jest.fn(), diff --git a/packages/credential-w3c/src/abstract-credential-provider.ts b/packages/credential-w3c/src/abstract-credential-provider.ts new file mode 100644 index 000000000..e0593de6f --- /dev/null +++ b/packages/credential-w3c/src/abstract-credential-provider.ts @@ -0,0 +1,116 @@ +import { IAgentContext, ICanIssueCredentialTypeArgs, ICanVerifyDocumentTypeArgs, ICreateVerifiableCredentialArgs, ICreateVerifiablePresentationArgs, IIdentifier, IKey, IssuerAgentContext, IVerifyCredentialArgs, IVerifyPresentationArgs, IVerifyResult, VerifiableCredential, VerifiablePresentation, VerifierAgentContext } from "@veramo/core-types"; + +/** + * The interface definition for a plugin that can generate Verifiable Credentials and Presentations + * + * @see {@link @veramo/credential-w3c#CredentialPlugin} for an implementation. + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model | W3C Verifiable Credentials data model} + * + * @public + */ +export abstract class AbstractCredentialProvider { + /** + * 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-types#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 + * } + */ + abstract createVerifiablePresentation( + args: ICreateVerifiablePresentationArgs, + context: IssuerAgentContext, + ): Promise + + /** + * 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-types#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 + * } + */ + abstract canIssueCredentialType(args: ICanIssueCredentialTypeArgs): boolean + + + /** + * Matches a key against the type of proof supported by this issuer + * + * @param key - The key to match against the proof type(s) supported by this issuer + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns - a promise that resolves to a boolean indicating if the key can be used to sign a credential with this issuer + */ + abstract matchKeyForType(key: IKey): boolean + + /** + * Gets the proof type supported by this issuer + * + * @returns - a promise that resolves to a string of the proof format supported by this issuer + */ + abstract getTypeProofFormat(): string + + /** + * 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-types#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} + */ + abstract createVerifiableCredential( + args: ICreateVerifiableCredentialArgs, + context: IssuerAgentContext, + ): Promise + + /** + * Verifies a Verifiable Credential + * + * @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 an object containing a `verified` boolean property and an optional `error` + * for details + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#credentials | Verifiable Credential data model} + */ + abstract verifyCredential(args: IVerifyCredentialArgs, context: VerifierAgentContext): Promise + + + /** + * + * @param args - Arguments necessary to verify a document + * @param context - This reserved param is automatically added and handled by the framework, *do not override* + * + * @returns a promise that resolves to a boolean indicating if the document can be verified + */ + abstract canVerifyDocumentType(args: ICanVerifyDocumentTypeArgs): boolean + + /** + * 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 an object containing a `verified` boolean property and an optional `error` + * for details + * + * @remarks Please see {@link https://www.w3.org/TR/vc-data-model/#presentations | Verifiable Credential data model} + */ + abstract verifyPresentation(args: IVerifyPresentationArgs, context: VerifierAgentContext): Promise + +} \ 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 235b9f30f..30c883a1e 100644 --- a/packages/credential-w3c/src/action-handler.ts +++ b/packages/credential-w3c/src/action-handler.ts @@ -7,52 +7,27 @@ import { ICredentialStatusVerifier, IIdentifier, IKey, - IKeyManager, IssuerAgentContext, IVerifyCredentialArgs, IVerifyPresentationArgs, IVerifyResult, - ProofFormat, VerifiableCredential, VerifiablePresentation, VerifierAgentContext, - W3CVerifiableCredential, - W3CVerifiablePresentation, } from '@veramo/core-types' -import { schema } from '@veramo/core-types' - -import { - createVerifiableCredentialJwt, - createVerifiablePresentationJwt, - normalizeCredential, - normalizePresentation, - verifyCredential as verifyCredentialJWT, - verifyPresentation as verifyPresentationJWT, -} from 'did-jwt-vc' +import { AbstractCredentialProvider } from './abstract-credential-provider.js' -import { decodeJWT } from 'did-jwt' +import { schema } from '@veramo/core-types' import { - asArray, extractIssuer, removeDIDParameters, isDefined, MANDATORY_CREDENTIAL_CONTEXT, processEntryToArray, - intersect, } from '@veramo/utils' import Debug from 'debug' -import { Resolvable } from 'did-resolver' - -import canonicalize from 'canonicalize' - -const enum DocumentFormat { - JWT, - JSONLD, - EIP712, - BBS -} const debug = Debug('veramo:w3c:action-handler') @@ -75,127 +50,30 @@ export class CredentialPlugin implements IAgentPlugin { }, }, } + private issuers: AbstractCredentialProvider[] - constructor() { + constructor(options: { issuers: AbstractCredentialProvider[] }) { + this.issuers = options.issuers this.methods = { - createVerifiablePresentation: this.createVerifiablePresentation.bind(this), + listUsableProofFormats: this.listUsableProofFormats.bind(this), createVerifiableCredential: this.createVerifiableCredential.bind(this), verifyCredential: this.verifyCredential.bind(this), + createVerifiablePresentation: this.createVerifiablePresentation.bind(this), verifyPresentation: this.verifyPresentation.bind(this), - matchKeyForJWT: this.matchKeyForJWT.bind(this), - listUsableProofFormats: this.listUsableProofFormats.bind(this), } } - /** {@inheritdoc @veramo/core-types#ICredentialIssuer.createVerifiablePresentation} */ - async createVerifiablePresentation( - args: ICreateVerifiablePresentationArgs, - context: IssuerAgentContext, - ): Promise { - let { - presentation, - proofFormat, - domain, - challenge, - removeOriginalFields, - keyRef, - save, - now, - ...otherOptions - } = args - const presentationContext: string[] = processEntryToArray( - args?.presentation?.['@context'], - MANDATORY_CREDENTIAL_CONTEXT, - ) - const presentationType = processEntryToArray(args?.presentation?.type, 'VerifiablePresentation') - presentation = { - ...presentation, - '@context': presentationContext, - type: presentationType, - } - - if (!isDefined(presentation.holder)) { - throw new Error('invalid_argument: presentation.holder must not be empty') - } - - if (presentation.verifiableCredential) { - presentation.verifiableCredential = 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 - } - }) - } - - const holder = removeDIDParameters(presentation.holder) - - let identifier: IIdentifier - try { - identifier = await context.agent.didManagerGet({ did: holder }) - } catch (e) { - throw new Error('invalid_argument: presentation.holder must be a DID managed by this agent') - } - const key = pickSigningKey(identifier, keyRef) - - let verifiablePresentation: VerifiablePresentation - if (proofFormat === 'bbs') { - if (typeof context.agent.createVerifiablePresentationBbs === 'function') { - verifiablePresentation = await context. agent.createVerifiablePresentationBbs({ ...args, presentation }) - } - else { - throw new Error('invalid_setup: your agent does not seem to have ICredentialIssuerBbs plugin installed') + async listUsableProofFormats(did: IIdentifier, context: IssuerAgentContext): Promise { + const signingOptions: string[] = [] + const keys = did.keys + for (const key of keys) { + for (const issuer of this.issuers) { + if (issuer.matchKeyForType(key)) { + signingOptions.push(issuer.getTypeProofFormat()) } - } - else if (proofFormat === 'lds') { - if (typeof context.agent.createVerifiablePresentationLD === 'function') { - verifiablePresentation = await context.agent.createVerifiablePresentationLD({ ...args, presentation }) - } else { - throw new Error( - 'invalid_setup: your agent does not seem to have ICredentialIssuerLD plugin installed', - ) - } - } else if (proofFormat === 'EthereumEip712Signature2021') { - if (typeof context.agent.createVerifiablePresentationEIP712 === 'function') { - verifiablePresentation = await context.agent.createVerifiablePresentationEIP712({ - ...args, - presentation, - }) - } else { - throw new Error( - 'invalid_setup: your agent does not seem to have ICredentialIssuerEIP712 plugin installed', - ) - } - } else { - // only add issuanceDate for JWT - now = typeof now === 'number' ? new Date(now * 1000) : now - if (!Object.getOwnPropertyNames(presentation).includes('issuanceDate')) { - presentation.issuanceDate = (now instanceof Date ? now : new Date()).toISOString() - } - - debug('Signing VP with', identifier.did) - let alg = 'ES256K' - if (key.type === 'Ed25519') { - alg = 'EdDSA' - } else if (key.type === 'Secp256r1') { - alg = 'ES256' } - - const signer = wrapSigner(context, key, alg) - const jwt = await createVerifiablePresentationJwt( - presentation as any, - { did: identifier.did, signer, alg }, - { removeOriginalFields, challenge, domain, ...otherOptions }, - ) - //FIXME: flagging this as a potential privacy leak. - debug(jwt) - verifiablePresentation = normalizePresentation(jwt) - } - if (save) { - await context.agent.dataStoreSaveVerifiablePresentation({ verifiablePresentation }) } - return verifiablePresentation + return signingOptions } /** {@inheritdoc @veramo/core-types#ICredentialIssuer.createVerifiableCredential} */ @@ -232,52 +110,21 @@ export class CredentialPlugin implements IAgentPlugin { throw new Error(`invalid_argument: credential.issuer must be a DID managed by this agent. ${e}`) } try { - let verifiableCredential: VerifiableCredential - if (proofFormat === 'bbs') { - if (typeof context.agent.createVerifiableCredentialBbs === 'function') { - verifiableCredential = await context.agent.createVerifiableCredentialBbs({ ...args, credential }) - } - else { - throw new Error('invalid_setup: your agent does not seem to have ICredentialIssuerBbs plugin installed') + let verifiableCredential: VerifiableCredential | undefined + + async function getCredential(issuers: AbstractCredentialProvider[]) { + for (const issuer of issuers) { + if (issuer.canIssueCredentialType({ proofFormat })) { + return await issuer.createVerifiableCredential(args, context) } - } - else if (proofFormat === 'lds') { - if (typeof context.agent.createVerifiableCredentialLD === 'function') { - verifiableCredential = await context.agent.createVerifiableCredentialLD({ ...args, credential }) - } else { - throw new Error( - 'invalid_setup: your agent does not seem to have ICredentialIssuerLD plugin installed', - ) - } - } else if (proofFormat === 'EthereumEip712Signature2021') { - if (typeof context.agent.createVerifiableCredentialEIP712 === 'function') { - verifiableCredential = await context.agent.createVerifiableCredentialEIP712({ ...args, credential }) - } else { - throw new Error( - 'invalid_setup: your agent does not seem to have ICredentialIssuerEIP712 plugin installed', - ) - } - } else { - const key = pickSigningKey(identifier, keyRef) - - debug('Signing VC with', identifier.did) - let alg = 'ES256K' - if (key.type === 'Ed25519') { - alg = 'EdDSA' - } else if (key.type === 'Secp256r1') { - alg = 'ES256' } + } + verifiableCredential = await getCredential(this.issuers) - const signer = wrapSigner(context, key, alg) - const jwt = await createVerifiableCredentialJwt( - credential as any, - { did: identifier.did, signer, alg }, - { removeOriginalFields, ...otherOptions }, - ) - //FIXME: flagging this as a potential privacy leak. - debug(jwt) - verifiableCredential = normalizeCredential(jwt) + if (!verifiableCredential) { + throw new Error('invalid_setup: No issuer found for the requested proof format') } + if (save) { await context.agent.dataStoreSaveVerifiableCredential({ verifiableCredential }) } @@ -293,123 +140,20 @@ export class CredentialPlugin implements IAgentPlugin { async verifyCredential(args: IVerifyCredentialArgs, context: VerifierAgentContext): Promise { let { credential, policies, ...otherOptions } = args let verifiedCredential: VerifiableCredential - let verificationResult: IVerifyResult = { verified: false } - - const type: DocumentFormat = detectDocumentType(credential) - if (type == DocumentFormat.JWT) { - let jwt: string = typeof credential === 'string' ? credential : credential.proof.jwt - - const resolver = { - resolve: (didUrl: string) => - context.agent.resolveDid({ - didUrl, - options: otherOptions?.resolutionOptions, - }), - } as Resolvable - try { - // needs broader credential as well to check equivalence with jwt - verificationResult = await verifyCredentialJWT(jwt, resolver, { - ...otherOptions, - policies: { - ...policies, - nbf: policies?.nbf ?? policies?.issuanceDate, - iat: policies?.iat ?? policies?.issuanceDate, - exp: policies?.exp ?? policies?.expirationDate, - aud: policies?.aud ?? policies?.audience, - }, - }) - verifiedCredential = verificationResult.verifiableCredential - - // if credential was presented with other fields, make sure those fields match what's in the JWT - if (typeof credential !== 'string' && credential.proof.type === 'JwtProof2020') { - const credentialCopy = JSON.parse(JSON.stringify(credential)) - delete credentialCopy.proof.jwt - - const verifiedCopy = JSON.parse(JSON.stringify(verifiedCredential)) - delete verifiedCopy.proof.jwt - - if (canonicalize(credentialCopy) !== canonicalize(verifiedCopy)) { - verificationResult.verified = false - verificationResult.error = new Error( - 'invalid_credential: Credential JSON does not match JWT payload', - ) - } - } - } catch (e: any) { - let { message, errorCode } = e - return { - verified: false, - error: { - message, - errorCode: errorCode ? errorCode : message.split(':')[0], - }, - } - } - } else if (type == DocumentFormat.EIP712) { - if (typeof context.agent.verifyCredentialEIP712 !== 'function') { - throw new Error( - 'invalid_setup: your agent does not seem to have ICredentialIssuerEIP712 plugin installed', - ) - } + let verificationResult: IVerifyResult | undefined = { verified: false } - try { - const result = await context.agent.verifyCredentialEIP712(args) - if (result) { - verificationResult = { - verified: true, - } - } else { - verificationResult = { - verified: false, - error: { - message: 'invalid_signature: The signature does not match any of the issuer signing keys', - errorCode: 'invalid_signature', - }, - } - } - verifiedCredential = credential - } catch (e: any) { - debug('encountered error while verifying EIP712 credential: %o', e) - const { message, errorCode } = e - return { - verified: false, - error: { - message, - errorCode: errorCode ? errorCode : e.message.split(':')[0], - }, + async function getVerificationResult(issuers: AbstractCredentialProvider[]): Promise { + for (const issuer of issuers) { + if (issuer.canVerifyDocumentType({ document: credential })) { + return issuer.verifyCredential(args, context) } } - } else if (type == DocumentFormat.JSONLD) { - if (typeof context.agent.verifyCredentialLD !== 'function') { - throw new Error( - 'invalid_setup: your agent does not seem to have ICredentialIssuerLD plugin installed', - ) - } - - verificationResult = await context.agent.verifyCredentialLD({ ...args, now: policies?.now }) - verifiedCredential = credential - } else if (type == DocumentFormat.BBS) { - if (typeof context.agent.verifyCredentialBbs!== 'function') { - throw new Error('invalid_setup: your agent does not seem to have ICredentialIssuerBbs plugin installed') - } - try { - verificationResult = await context.agent.verifyCredentialBbs({ ...args, now: policies?.now }) - verifiedCredential = credential - } - catch (e) { - debug('encountered error while verifying BBS credential: %o', e) - const { message, errorCode } = e - return { - verified: false, - error: { - message, - errorCode: errorCode ? errorCode : e.message.split(':')[0], - }, - } - } - } else { - throw new Error('invalid_argument: Unknown credential type.') } + verificationResult = await getVerificationResult(this.issuers) + if (!verificationResult) { + throw new Error('invalid_setup: No verifier found for the provided credential') + } + verifiedCredential = credential if (policies?.credentialStatus !== false && (await isRevoked(verifiedCredential, context as any))) { verificationResult = { @@ -424,178 +168,99 @@ export class CredentialPlugin implements IAgentPlugin { return verificationResult } - /** {@inheritdoc @veramo/core-types#ICredentialVerifier.verifyPresentation} */ - async verifyPresentation( - args: IVerifyPresentationArgs, - context: VerifierAgentContext, - ): Promise { - let { presentation, domain, challenge, fetchRemoteContexts, policies, ...otherOptions } = args - const type: DocumentFormat = detectDocumentType(presentation) - if (type === DocumentFormat.JWT) { - // JWT - let jwt: string - if (typeof presentation === 'string') { - jwt = presentation - } else { - jwt = presentation.proof.jwt - } - const resolver = { - resolve: (didUrl: string) => - context.agent.resolveDid({ - didUrl, - options: otherOptions?.resolutionOptions, - }), - } as Resolvable - - let audience = 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 - } - } - } + /** {@inheritdoc @veramo/core-types#ICredentialIssuer.createVerifiablePresentation} */ + async createVerifiablePresentation( + args: ICreateVerifiablePresentationArgs, + context: IssuerAgentContext, + ): Promise { + let { + presentation, + proofFormat, + domain, + challenge, + removeOriginalFields, + keyRef, + save, + now, + ...otherOptions + } = args + const presentationContext: string[] = processEntryToArray( + args?.presentation?.['@context'], + MANDATORY_CREDENTIAL_CONTEXT, + ) + const presentationType = processEntryToArray(args?.presentation?.type, 'VerifiablePresentation') + presentation = { + ...presentation, + '@context': presentationContext, + type: presentationType, + } - try { - return await verifyPresentationJWT(jwt, resolver, { - challenge, - domain, - audience, - policies: { - ...policies, - nbf: policies?.nbf ?? policies?.issuanceDate, - iat: policies?.iat ?? policies?.issuanceDate, - exp: policies?.exp ?? policies?.expirationDate, - aud: policies?.aud ?? policies?.audience, - }, - ...otherOptions, - }) - } catch (e: any) { - let { message, errorCode } = e - return { - verified: false, - error: { - message, - errorCode: errorCode ? errorCode : message.split(':')[0], - }, - } - } - } else if (type === DocumentFormat.EIP712) { - // JSON-LD - if (typeof context.agent.verifyPresentationEIP712 !== 'function') { - throw new Error( - 'invalid_setup: your agent does not seem to have ICredentialIssuerEIP712 plugin installed', - ) - } - try { - const result = await context.agent.verifyPresentationEIP712(args) - if (result) { - return { - verified: true, - } + if (!isDefined(presentation.holder)) { + throw new Error('invalid_argument: presentation.holder must not be empty') + } + + if (presentation.verifiableCredential) { + presentation.verifiableCredential = presentation.verifiableCredential.map((cred) => { + // map JWT credentials to their canonical form + if (typeof cred !== 'string' && cred.proof.jwt) { + return cred.proof.jwt } else { - return { - verified: false, - error: { - message: 'invalid_signature: The signature does not match any of the issuer signing keys', - errorCode: 'invalid_signature', - }, - } + return cred } - } catch (e: any) { - const { message, errorCode } = e - return { - verified: false, - error: { - message, - errorCode: errorCode ? errorCode : e.message.split(':')[0], - }, - }; - } - } - else if (type === DocumentFormat.BBS) { - //Bbs - if (typeof context.agent.verifyPresentationBbs !== 'function') { - throw new Error('invalid_setup: your agent does not seem to have ICredentialIssuerBbs plugin installed') - } - try { - const result = await context.agent.verifyPresentationBbs(args) - return result; - } - catch (e) { - const { message, errorCode } = e; - return { - verified: false, - error: { - message, - errorCode: errorCode ? errorCode : e.message.split(':')[0], - }, - } - } - } else { - // JSON-LD - if (typeof context.agent.verifyPresentationLD === 'function') { - const result = await context.agent.verifyPresentationLD({ ...args, now: policies?.now }) - return result - } else { - throw new Error( - 'invalid_setup: your agent does not seem to have ICredentialIssuerLD plugin installed', - ) - } + }) } - } - /** - * Checks if a key is suitable for signing JWT payloads. - * @param key - the key to check - * @param context - the Veramo agent context, unused here - * - * @beta - */ - async matchKeyForJWT(key: IKey, context: IssuerAgentContext): Promise { - switch (key.type) { - case 'Ed25519': - case 'Secp256r1': - return true - case 'Secp256k1': - return intersect(key.meta?.algorithms ?? [], ['ES256K', 'ES256K-R']).length > 0 - default: - return false + const holder = removeDIDParameters(presentation.holder) + + let identifier: IIdentifier + try { + identifier = await context.agent.didManagerGet({ did: holder }) + } catch (e) { + throw new Error('invalid_argument: presentation.holder must be a DID managed by this agent') } - return false - } + const key = pickSigningKey(identifier, keyRef) - async listUsableProofFormats(did: IIdentifier, context: IssuerAgentContext): Promise { - const signingOptions: ProofFormat[] = [] - const keys = did.keys - for (const key of keys) { - if (context.agent.availableMethods().includes('matchKeyForJWT')) { - if (await context.agent.matchKeyForJWT(key)) { - signingOptions.push('jwt') - } - } - if (context.agent.availableMethods().includes('matchKeyForLDSuite')) { - if (await context.agent.matchKeyForLDSuite(key)) { - signingOptions.push('lds') - } - } - if (context.agent.availableMethods().includes('matchKeyForEIP712')) { - if (await context.agent.matchKeyForEIP712(key)) { - signingOptions.push('EthereumEip712Signature2021') + let verifiablePresentation: VerifiablePresentation | undefined + + async function getPresentation(issuers: AbstractCredentialProvider[]) { + for (const issuer of issuers) { + if (issuer.canIssueCredentialType({ proofFormat })) { + return await issuer.createVerifiablePresentation(args, context) } } - if (context.agent.availableMethods().includes('matchKeyForBbs')) { - if (await context.agent.matchKeyForBbs(key)) { - signingOptions.push('bbs'); + } + + verifiablePresentation = await getPresentation(this.issuers) + + if (!verifiablePresentation) { + throw new Error('invalid_setup: No issuer found for the requested proof format') + } + + if (save) { + await context.agent.dataStoreSaveVerifiablePresentation({ verifiablePresentation }) + } + return verifiablePresentation + } + + /** {@inheritdoc @veramo/core-types#ICredentialVerifier.verifyPresentation} */ + async verifyPresentation( + args: IVerifyPresentationArgs, + context: VerifierAgentContext, + ): Promise { + let { presentation, domain, challenge, fetchRemoteContexts, policies, ...otherOptions } = args + let result: IVerifyResult | undefined = { verified: false } + async function getVerificationResult(issuers: AbstractCredentialProvider[]): Promise { + for (const issuer of issuers) { + if (issuer.canVerifyDocumentType({ document: presentation })) { + return issuer.verifyPresentation(args, context) } } } - return signingOptions + result = await getVerificationResult(this.issuers) + if (!result) { + throw new Error('invalid_setup: No verifier found for the provided presentation') + } + return result } } @@ -615,24 +280,6 @@ function pickSigningKey(identifier: IIdentifier, keyRef?: string): IKey { return key as IKey } -function wrapSigner( - context: IAgentContext>, - key: IKey, - algorithm?: string, -) { - return async (data: string | Uint8Array): Promise => { - const result = await context.agent.keyManagerSign({ keyRef: key.kid, data: data, algorithm }) - return result - } -} - -function detectDocumentType(document: W3CVerifiableCredential | W3CVerifiablePresentation): DocumentFormat { - if (typeof document === 'string' || (document)?.proof?.jwt) return DocumentFormat.JWT - if ((document)?.proof?.type === 'EthereumEip712Signature2021') - return DocumentFormat.EIP712 - return DocumentFormat.JSONLD -} - async function isRevoked( credential: VerifiableCredential, context: IAgentContext, diff --git a/packages/credential-w3c/src/index.ts b/packages/credential-w3c/src/index.ts index ce71ea28e..b659ce02e 100644 --- a/packages/credential-w3c/src/index.ts +++ b/packages/credential-w3c/src/index.ts @@ -20,3 +20,5 @@ export { CredentialIssuer, CredentialPlugin } // For backward compatibility, re-export the plugin types that were moved to core in v4 export type { ICredentialIssuer, ICredentialVerifier } from '@veramo/core-types' + +export { AbstractCredentialProvider } from './abstract-credential-provider.js' \ No newline at end of file diff --git a/packages/credential-w3c/tsconfig.json b/packages/credential-w3c/tsconfig.json index ac238f140..8c39d7f69 100644 --- a/packages/credential-w3c/tsconfig.json +++ b/packages/credential-w3c/tsconfig.json @@ -8,9 +8,17 @@ "skipLibCheck": true }, "references": [ - { "path": "../core-types" }, - { "path": "../message-handler" }, - { "path": "../utils" } + { + "path": "../core-types" + }, + { + "path": "../message-handler" + }, + { + "path": "../utils" + } ], - "include": ["./**/*.ts"] -} + "include": [ + "./**/*.ts" + ] +} \ No newline at end of file diff --git a/packages/did-comm/src/plugin.schema.ts b/packages/did-comm/src/plugin.schema.ts index ba7d21e14..649e3ed80 100644 --- a/packages/did-comm/src/plugin.schema.ts +++ b/packages/did-comm/src/plugin.schema.ts @@ -439,6 +439,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" diff --git a/packages/mediation-manager/README.md b/packages/mediation-manager/README.md index 847fe0121..779b6795b 100644 --- a/packages/mediation-manager/README.md +++ b/packages/mediation-manager/README.md @@ -127,8 +127,10 @@ agent: - $ref: /mediationManager # <<<< include your mediationManager plugin here - $require: '@veramo/did-comm#DIDComm' - $require: '@veramo/credential-w3c#CredentialPlugin' - - $ref: /credentialIssuerLD - - $require: '@veramo/credential-eip712#CredentialIssuerEIP712' + $args: + - issuers: + - $ref: /CredentialProviderLD + - $ref: /CredentialProviderJWT - $require: '@veramo/selective-disclosure#SelectiveDisclosure' - $require: '@veramo/data-store#DataStore' $args: diff --git a/packages/selective-disclosure/src/plugin.schema.ts b/packages/selective-disclosure/src/plugin.schema.ts index 2f1af65f4..e1620c08b 100644 --- a/packages/selective-disclosure/src/plugin.schema.ts +++ b/packages/selective-disclosure/src/plugin.schema.ts @@ -100,6 +100,9 @@ export const schema = { "properties": { "type": { "type": "string" + }, + "proofValue": { + "type": "string" } }, "description": "A proof property of a {@link VerifiableCredential } or {@link VerifiablePresentation }" diff --git a/packages/test-react-app/package.json b/packages/test-react-app/package.json index 2b1046036..000aa9c2f 100644 --- a/packages/test-react-app/package.json +++ b/packages/test-react-app/package.json @@ -6,6 +6,7 @@ "dependencies": { "@veramo/core": "workspace:*", "@veramo/core-types": "workspace:*", + "@veramo/credential-jwt": "workspace:*", "@veramo/credential-ld": "workspace:*", "@veramo/credential-w3c": "workspace:*", "@veramo/data-store": "workspace:*", @@ -93,4 +94,4 @@ "typescript": "5.3.3" }, "type": "module" -} +} \ No newline at end of file diff --git a/packages/test-react-app/src/veramo/setup.ts b/packages/test-react-app/src/veramo/setup.ts index 1cc9164bd..374a4fb8b 100644 --- a/packages/test-react-app/src/veramo/setup.ts +++ b/packages/test-react-app/src/veramo/setup.ts @@ -22,8 +22,7 @@ import { DIDManager } from '@veramo/did-manager' import { JwtMessageHandler } from '@veramo/did-jwt' import { CredentialPlugin, W3cMessageHandler } from '@veramo/credential-w3c' import { - CredentialIssuerLD, - ICredentialIssuerLD, + CredentialProviderLD, LdDefaultContexts, VeramoEcdsaSecp256k1RecoverySignature2020, VeramoEd25519Signature2018, @@ -42,6 +41,7 @@ import { EthrDIDProvider } from '@veramo/did-provider-ethr' import { WebDIDProvider } from '@veramo/did-provider-web' import { DataStoreJson, DIDStoreJson, KeyStoreJson, PrivateKeyStoreJson } from '@veramo/data-store-json' import { FakeDidProvider, FakeDidResolver } from '@veramo/test-utils' +import { CredentialProviderJWT } from '@veramo/credential-jwt' const INFURA_PROJECT_ID = '33aab9e0334c44b0a2e0c57c15302608' const DB_SECRET_KEY = '29739248cad1bd1a0fc4d9b75cd4d2990de535baf5caadfdf8d8f86664aa83' @@ -61,7 +61,6 @@ type InstalledPlugins = IResolver & IKeyManager & IDIDManager & ICredentialPlugin & - ICredentialIssuerLD & IDataStoreORM & IDataStore & IMessageHandler & @@ -69,6 +68,16 @@ type InstalledPlugins = IResolver & IDIDComm export function getAgent(options?: IAgentOptions): TAgent { + const jwt = new CredentialProviderJWT() + const ld = new CredentialProviderLD({ + contextMaps: [LdDefaultContexts], + suites: [ + new VeramoEcdsaSecp256k1RecoverySignature2020(), + new VeramoEd25519Signature2018(), + new VeramoEd25519Signature2020(), + new VeramoJsonWebSignature2020(), + ], + }) const agent: TAgent = createAgent({ ...options, plugins: [ @@ -145,16 +154,7 @@ export function getAgent(options?: IAgentOptions): TAgent { ], }), new DIDComm(), - new CredentialPlugin(), - new CredentialIssuerLD({ - contextMaps: [LdDefaultContexts], - suites: [ - new VeramoEcdsaSecp256k1RecoverySignature2020(), - new VeramoEd25519Signature2018(), - new VeramoEd25519Signature2020(), - new VeramoJsonWebSignature2020(), - ], - }), + new CredentialPlugin({ issuers: [jwt, ld] }), new SelectiveDisclosure(), ...(options?.plugins || []), ], diff --git a/packages/utils/src/did-utils.ts b/packages/utils/src/did-utils.ts index c4dc5de4e..3eb4df6ef 100644 --- a/packages/utils/src/did-utils.ts +++ b/packages/utils/src/did-utils.ts @@ -353,3 +353,19 @@ export function extractPublicKeyHex( } return { publicKeyHex: bytesToHex(keyBytes), keyType } } + +export function pickSigningKey(identifier: IIdentifier, keyRef?: string): IKey { + let key: IKey | undefined + + if (!keyRef) { + key = identifier.keys.find( + (k) => k.type === 'Secp256k1' || k.type === 'Ed25519' || k.type === 'Secp256r1', + ) + if (!key) throw Error('key_not_found: No signing key for ' + identifier.did) + } else { + key = identifier.keys.find((k) => k.kid === keyRef) + if (!key) throw Error('key_not_found: No signing key for ' + identifier.did + ' with kid ' + keyRef) + } + + return key as IKey +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdff075f2..e89ff9205 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,7 +128,7 @@ importers: version: 10.9.2(@types/node@20.11.19)(typescript@5.3.3) typeorm: specifier: 0.3.17 - version: 0.3.17(sqlite3@5.1.6)(ts-node@10.9.2) + version: 0.3.17(pg@8.11.3)(sqlite3@5.1.7)(ts-node@10.9.2) typescript: specifier: 5.3.3 version: 5.3.3 @@ -168,6 +168,9 @@ importers: '@veramo/credential-eip712': specifier: workspace:^ version: link:../credential-eip712 + '@veramo/credential-jwt': + specifier: workspace:^ + version: link:../credential-jwt '@veramo/credential-ld': specifier: workspace:^ version: link:../credential-ld @@ -423,6 +426,9 @@ importers: '@veramo/core-types': specifier: workspace:^ version: link:../core-types + '@veramo/credential-w3c': + specifier: workspace:^ + version: link:../credential-w3c '@veramo/utils': specifier: workspace:^ version: link:../utils @@ -440,6 +446,34 @@ importers: specifier: 5.3.3 version: 5.3.3 + packages/credential-jwt: + dependencies: + '@veramo/core-types': + specifier: workspace:^ + version: link:../core-types + '@veramo/credential-w3c': + specifier: workspace:^ + version: link:../credential-w3c + '@veramo/utils': + specifier: workspace:^ + version: link:../utils + canonicalize: + specifier: ^2.0.0 + version: 2.0.0 + debug: + specifier: ^4.3.3 + version: 4.3.4 + did-jwt-vc: + specifier: ^4.0.0 + version: 4.0.0 + devDependencies: + '@types/debug': + specifier: 4.1.8 + version: 4.1.8 + typescript: + specifier: 5.3.3 + version: 5.3.3 + packages/credential-ld: dependencies: '@digitalcredentials/ed25519-signature-2020': @@ -472,6 +506,9 @@ importers: '@veramo/core-types': specifier: workspace:^ version: link:../core-types + '@veramo/credential-w3c': + specifier: workspace:^ + version: link:../credential-w3c '@veramo/utils': specifier: workspace:^ version: link:../utils @@ -1140,7 +1177,7 @@ importers: version: 3.0.1 typeorm: specifier: ^0.3.17 - version: 0.3.17(sqlite3@5.1.6)(ts-node@10.9.2) + version: 0.3.17(pg@8.11.3)(sqlite3@5.1.7)(ts-node@10.9.2) uint8arrays: specifier: ^4.0.6 version: 4.0.6 @@ -1339,6 +1376,9 @@ importers: '@veramo/core-types': specifier: workspace:* version: link:../core-types + '@veramo/credential-jwt': + specifier: workspace:* + version: link:../credential-jwt '@veramo/credential-ld': specifier: workspace:* version: link:../credential-ld @@ -1434,7 +1474,7 @@ importers: version: stream-browserify@3.0.0 typeorm: specifier: ^0.3.17 - version: 0.3.17(sqlite3@5.1.6)(ts-node@10.9.2) + version: 0.3.17(pg@8.11.3)(sqlite3@5.1.7)(ts-node@10.9.2) util: specifier: npm:util version: 0.12.5 @@ -4400,6 +4440,10 @@ packages: resolution: {integrity: sha512-cr/yjSP4ErZMKwoU/scqkfKL63AJupE27xakCHZVEfGzQChQH2RDXsNDGCXxsf2+DGfayGBhxayCXRdOcvvzng==} engines: {node: '>=16'} + '@trufflesuite/bigint-buffer@1.1.10': + resolution: {integrity: sha512-pYIQC5EcMmID74t26GCC67946mgTJFiLXOT/BYozgrd4UEY2JHEGLhWi9cMiQCt5BSqFEvKkCHNnoj82SRjiEw==} + engines: {node: '>= 14.0.0'} + '@trufflesuite/uws-js-unofficial@20.30.0-unofficial.0': resolution: {integrity: sha512-r5X0aOQcuT6pLwTRLD+mPnAM/nlKtvIK4Z+My++A8tTOR0qTjNRx8UB8jzRj3D+p9PMAp5LnpCUUGmz7/TppwA==} @@ -7642,9 +7686,11 @@ packages: glob@6.0.4: resolution: {integrity: sha512-MKZeRNyYZAVVVG1oZeLaWie1uweH40m9AZwIwxyPbTSX4hHrVYSzLg0Ro5Z5R7XKkIX+Cc6oD1rqeDJnwsB8/A==} + deprecated: Glob versions prior to v9 are no longer supported glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} + deprecated: Glob versions prior to v9 are no longer supported glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -8894,6 +8940,10 @@ packages: resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} engines: {node: '>=4.0'} + keccak@3.0.2: + resolution: {integrity: sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==} + engines: {node: '>=10.0.0'} + keyv@4.5.3: resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} @@ -8971,6 +9021,10 @@ packages: resolution: {integrity: sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==} engines: {node: '>=12'} + leveldown@6.1.0: + resolution: {integrity: sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w==} + engines: {node: '>=10.12.0'} + leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -9608,6 +9662,9 @@ packages: napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + napi-macros@2.0.0: + resolution: {integrity: sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==} + natural-compare-lite@1.4.0: resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} @@ -9713,6 +9770,10 @@ packages: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} + node-gyp-build@4.4.0: + resolution: {integrity: sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==} + hasBin: true + node-gyp-build@4.6.0: resolution: {integrity: sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==} hasBin: true @@ -10075,6 +10136,7 @@ packages: osenv@0.1.5: resolution: {integrity: sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==} + deprecated: This package is no longer supported. p-each-series@3.0.0: resolution: {integrity: sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==} @@ -11406,14 +11468,17 @@ packages: rimraf@2.4.5: resolution: {integrity: sha512-J5xnxTyqaiw06JjMftq7L9ouA448dw/E7dKghkP9WpKNuwmARNNg+Gk8/u5ryb9N/Yo2+z3MCwuqFK/+qPOPfQ==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@2.7.1: resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@3.0.2: @@ -11846,9 +11911,15 @@ packages: sqlite3@5.1.6: resolution: {integrity: sha512-olYkWoKFVNSSSQNvxVUfjiVbz3YtBwTJj+mfV5zpHmqW3sELx2Cf4QCdirMelhM5Zh+KDVaKgQHqCxrqiWHybw==} + peerDependenciesMeta: + node-gyp: + optional: true sqlite3@5.1.7: resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} + peerDependenciesMeta: + node-gyp: + optional: true ssri@10.0.4: resolution: {integrity: sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==} @@ -17478,7 +17549,7 @@ snapshots: '@noble/hashes': 1.3.3 '@types/debug': 4.1.8 debug: 4.3.4 - semver: 7.5.4 + semver: 7.6.0 superstruct: 1.0.3 transitivePeerDependencies: - supports-color @@ -18756,6 +18827,10 @@ snapshots: '@stablelib/x25519': 1.0.3 '@transmute/ld-key-pair': 0.7.0-unstable.81 + '@trufflesuite/bigint-buffer@1.1.10': + dependencies: + node-gyp-build: 4.4.0 + '@trufflesuite/uws-js-unofficial@20.30.0-unofficial.0': dependencies: ws: 8.13.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) @@ -21236,7 +21311,7 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.22.3 + '@babel/runtime': 7.23.6 date-fns@3.3.1: {} @@ -22630,6 +22705,7 @@ snapshots: ganache@7.9.2: dependencies: + '@trufflesuite/bigint-buffer': 1.1.10 '@trufflesuite/uws-js-unofficial': 20.30.0-unofficial.0 '@types/bn.js': 5.1.1 '@types/lru-cache': 5.1.1 @@ -22638,6 +22714,9 @@ snapshots: abstract-leveldown: 7.2.0 async-eventemitter: 0.2.4 emittery: 0.10.0 + keccak: 3.0.2 + leveldown: 6.1.0 + secp256k1: 4.0.3 optionalDependencies: bufferutil: 4.0.5 utf-8-validate: 5.0.7 @@ -24666,6 +24745,12 @@ snapshots: array-includes: 3.1.6 object.assign: 4.1.4 + keccak@3.0.2: + dependencies: + node-addon-api: 2.0.2 + node-gyp-build: 4.6.0 + readable-stream: 3.6.2 + keyv@4.5.3: dependencies: json-buffer: 3.0.1 @@ -24815,6 +24900,12 @@ snapshots: buffer: 6.0.3 module-error: 1.0.2 + leveldown@6.1.0: + dependencies: + abstract-leveldown: 7.2.0 + napi-macros: 2.0.0 + node-gyp-build: 4.6.0 + leven@3.1.0: {} levn@0.4.1: @@ -25052,7 +25143,7 @@ snapshots: cacache: 18.0.2 http-cache-semantics: 4.1.1 is-lambda: 1.0.1 - minipass: 7.0.2 + minipass: 7.0.4 minipass-fetch: 3.0.3 minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 @@ -25599,6 +25690,8 @@ snapshots: napi-build-utils@1.0.2: {} + napi-macros@2.0.0: {} + natural-compare-lite@1.4.0: {} natural-compare@1.4.0: {} @@ -25677,6 +25770,8 @@ snapshots: node-forge@1.3.1: {} + node-gyp-build@4.4.0: {} + node-gyp-build@4.6.0: {} node-gyp@10.0.1: @@ -25703,7 +25798,7 @@ snapshots: nopt: 5.0.0 npmlog: 6.0.2 rimraf: 3.0.2 - semver: 7.5.4 + semver: 7.6.0 tar: 6.2.0 which: 2.0.2 transitivePeerDependencies: @@ -28830,7 +28925,7 @@ snapshots: cli-highlight: 2.1.11 date-fns: 2.30.0 debug: 4.3.4 - dotenv: 16.3.1 + dotenv: 16.4.5 glob: 8.1.0 mkdirp: 2.1.6 reflect-metadata: 0.1.13