From 9b4a13695193caa8ac71083d2cada6cbd0fdb3f9 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 2 May 2024 14:05:08 +0200 Subject: [PATCH] fix: verify issuer id matches Signed-off-by: Timo Glastra --- src/anoncreds/DidWebAnonCredsRegistry.ts | 37 +++++++++++++++++++++-- test/__fixtures__/schema1.json | 2 +- test/credentialDefinition.test.ts | 37 +++++++++++++++++++++-- test/revocationRegistryDefinition.test.ts | 33 ++++++++++++++++++++ test/schema.test.ts | 35 ++++++++++++++++++++- 5 files changed, 137 insertions(+), 7 deletions(-) diff --git a/src/anoncreds/DidWebAnonCredsRegistry.ts b/src/anoncreds/DidWebAnonCredsRegistry.ts index 478fe0b..432e310 100644 --- a/src/anoncreds/DidWebAnonCredsRegistry.ts +++ b/src/anoncreds/DidWebAnonCredsRegistry.ts @@ -59,7 +59,7 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { } } try { - const { response, resourceId } = await this.parseIdAndFetchResource(agentContext, schemaId) + const { response, resourceId, did } = await this.parseIdAndFetchResource(agentContext, schemaId) if (response.status === 200) { const result = (await response.json()) as AnonCredsResourceResolutionResult @@ -69,6 +69,10 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { throw new Error('Wrong resource Id') } + if (did !== schema.issuerId) { + throw new Error(`issuerId in schema (${schema.issuerId}) does not match the did (${did})`) + } + if (this.cacheSettings.allowCaching) { const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache await cache.set( @@ -147,7 +151,7 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { } try { - const { response, resourceId } = await this.parseIdAndFetchResource(agentContext, credentialDefinitionId) + const { response, resourceId, did } = await this.parseIdAndFetchResource(agentContext, credentialDefinitionId) if (response.status === 200) { const result = (await response.json()) as AnonCredsResourceResolutionResult const credentialDefinition = result.resource as unknown as AnonCredsCredentialDefinition @@ -157,6 +161,12 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { throw new Error('Wrong resource Id') } + if (did !== credentialDefinition.issuerId) { + throw new Error( + `issuerId in credential definition (${credentialDefinition.issuerId}) does not match the did (${did})` + ) + } + if (this.cacheSettings.allowCaching) { const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache await cache.set( @@ -242,7 +252,10 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { } try { - const { response, resourceId } = await this.parseIdAndFetchResource(agentContext, revocationRegistryDefinitionId) + const { response, resourceId, did } = await this.parseIdAndFetchResource( + agentContext, + revocationRegistryDefinitionId + ) if (response.status === 200) { const result = (await response.json()) as AnonCredsResourceResolutionResult const revocationRegistryDefinition = result.resource as unknown as AnonCredsRevocationRegistryDefinition @@ -252,6 +265,12 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { throw new Error('Wrong resource Id') } + if (did !== revocationRegistryDefinition.issuerId) { + throw new Error( + `issuerId in revocation registry definition (${revocationRegistryDefinition.issuerId}) does not match the did (${did})` + ) + } + if (this.cacheSettings.allowCaching) { const cache = agentContext.dependencyManager.resolve(CacheModuleConfig).cache await cache.set( @@ -320,6 +339,11 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { try { // TODO: use cache to get Revocation Registry Definition data without fetching it again const revRegDefResult = await this.getRevocationRegistryDefinition(agentContext, revocationRegistryId) + if (!revRegDefResult.revocationRegistryDefinition) { + throw new Error( + `Error resolving revocation registry definition with id ${revocationRegistryId}. ${revRegDefResult.resolutionMetadata.error} ${revRegDefResult.resolutionMetadata.message}` + ) + } const baseEndpoint = revRegDefResult.revocationRegistryDefinitionMetadata.statusListEndpoint @@ -336,6 +360,12 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { const revocationStatusList = result.resource as unknown as AnonCredsRevocationStatusList const revocationStatusListMetadata = result.resourceMetadata + if (revocationStatusList.issuerId !== revRegDefResult.revocationRegistryDefinition.issuerId) { + throw new Error( + `issuerId in revocation status list (${revocationStatusList.issuerId}) does not match the issuer in the revocation registry definition (${revRegDefResult.revocationRegistryDefinition.issuerId})` + ) + } + return { revocationStatusList, revocationStatusListMetadata, @@ -427,6 +457,7 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry { return { response: await agentContext.config.agentDependencies.fetch(fetchResourceUrl, { method: 'GET' }), resourceId, + did: parsedDid.did, } } } diff --git a/test/__fixtures__/schema1.json b/test/__fixtures__/schema1.json index 926d44b..e4c21e3 100644 --- a/test/__fixtures__/schema1.json +++ b/test/__fixtures__/schema1.json @@ -1,5 +1,5 @@ { - "issuerId": "did:web:api.anoncreds.idlab.app:acme", + "issuerId": "did:web:ca.dev.2060.io", "name": "idlab_demo", "version": "0.0.1", "attrNames": ["id", "name"] diff --git a/test/credentialDefinition.test.ts b/test/credentialDefinition.test.ts index 5759cd8..772da20 100644 --- a/test/credentialDefinition.test.ts +++ b/test/credentialDefinition.test.ts @@ -29,12 +29,12 @@ describe('Credential Definition', () => { const registry = new DidWebAnonCredsRegistry() - const schemaResponse = await registry.getCredentialDefinition( + const credentialDefinitionResponse = await registry.getCredentialDefinition( agent.context, `did:web:ca.dev.2060.io?service=anoncreds&relativeRef=/credential-definition/${resourceId}` ) - expect(schemaResponse).toEqual({ + expect(credentialDefinitionResponse).toEqual({ resolutionMetadata: {}, credentialDefinition: credentialDefinition1, credentialDefinitionId: `did:web:ca.dev.2060.io?service=anoncreds&relativeRef=/credential-definition/${resourceId}`, @@ -68,4 +68,37 @@ describe('Credential Definition', () => { credentialDefinitionMetadata: {}, }) }) + + test('throws error when issuerId does not match with did', async () => { + const credentialDefinition = { + ...credentialDefinition1, + issuerId: 'random', + } + const resourceId = calculateResourceId(credentialDefinition) + + // did document + nock('https://ca.dev.2060.io').get('/.well-known/did.json').reply(200, didDocument1) + + // Get schema + nock('https://anoncreds.ca.dev.2060.io').get(`/v1/credential-definition/${resourceId}`).reply(200, { + resource: credentialDefinition, + resourceMetadata: {}, + }) + + const registry = new DidWebAnonCredsRegistry() + + const credentialDefinitionResponse = await registry.getCredentialDefinition( + agent.context, + `did:web:ca.dev.2060.io?service=anoncreds&relativeRef=/credential-definition/${resourceId}` + ) + + expect(credentialDefinitionResponse).toEqual({ + resolutionMetadata: { + error: 'invalid', + message: 'issuerId in credential definition (random) does not match the did (did:web:ca.dev.2060.io)', + }, + credentialDefinitionId: `did:web:ca.dev.2060.io?service=anoncreds&relativeRef=/credential-definition/${resourceId}`, + credentialDefinitionMetadata: {}, + }) + }) }) diff --git a/test/revocationRegistryDefinition.test.ts b/test/revocationRegistryDefinition.test.ts index a350ad3..0154d41 100644 --- a/test/revocationRegistryDefinition.test.ts +++ b/test/revocationRegistryDefinition.test.ts @@ -74,4 +74,37 @@ describe('Revocation Registry Definition', () => { revocationRegistryDefinitionMetadata: {}, }) }) + + test('throws error when revocation registry definition resourceId does not match', async () => { + const revocationRegistryDefinition = { + ...revocationRegistryDefinition1, + issuerId: 'random2', + } + + const resourceId = calculateResourceId(revocationRegistryDefinition) + // did document + nock('https://ca.dev.2060.io').get('/.well-known/did.json').reply(200, didDocument1) + + // Get schema + nock('https://anoncreds.ca.dev.2060.io').get(`/v1/revocation-registry/${resourceId}`).reply(200, { + resource: revocationRegistryDefinition, + resourceMetadata: {}, + }) + + const registry = new DidWebAnonCredsRegistry() + + const revocationRegistryDefinitionResponse = await registry.getRevocationRegistryDefinition( + agent.context, + `did:web:ca.dev.2060.io?service=anoncreds&relativeRef=/revocation-registry/${resourceId}` + ) + + expect(revocationRegistryDefinitionResponse).toEqual({ + resolutionMetadata: { + error: 'invalid', + message: 'issuerId in revocation registry definition (random2) does not match the did (did:web:ca.dev.2060.io)', + }, + revocationRegistryDefinitionId: `did:web:ca.dev.2060.io?service=anoncreds&relativeRef=/revocation-registry/${resourceId}`, + revocationRegistryDefinitionMetadata: {}, + }) + }) }) diff --git a/test/schema.test.ts b/test/schema.test.ts index 7a3864b..3bc7519 100644 --- a/test/schema.test.ts +++ b/test/schema.test.ts @@ -71,7 +71,40 @@ describe('Schema', () => { }) }) - test('register and resole did document with nested path', async () => { + test('throws error when issuerId does not match with did', async () => { + const schema = { + ...schema1, + issuerId: 'random', + } + const resourceId = calculateResourceId(schema) + + // did document + nock('https://ca.dev.2060.io').get('/.well-known/did.json').reply(200, didDocument1) + + // Get schema + nock('https://anoncreds.ca.dev.2060.io').get(`/v1/schema/${resourceId}`).reply(200, { + resource: schema, + resourceMetadata: {}, + }) + + const registry = new DidWebAnonCredsRegistry() + + const schemaResponse = await registry.getSchema( + agent.context, + `did:web:ca.dev.2060.io?service=anoncreds&relativeRef=/schema/${resourceId}` + ) + + expect(schemaResponse).toEqual({ + resolutionMetadata: { + error: 'invalid', + message: 'issuerId in schema (random) does not match the did (did:web:ca.dev.2060.io)', + }, + schemaId: `did:web:ca.dev.2060.io?service=anoncreds&relativeRef=/schema/${resourceId}`, + schemaMetadata: {}, + }) + }) + + test('register and resolve did document with nested path', async () => { const registry = new DidWebAnonCredsRegistry() const result = await registry.registerSchema(agent.context, {