Skip to content

Commit

Permalink
fix: verify issuer id matches (#5)
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <timo@animo.id>
  • Loading branch information
TimoGlastra committed May 2, 2024
1 parent 373adcc commit 7cab490
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 7 deletions.
37 changes: 34 additions & 3 deletions src/anoncreds/DidWebAnonCredsRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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

Expand All @@ -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,
Expand Down Expand Up @@ -427,6 +457,7 @@ export class DidWebAnonCredsRegistry implements AnonCredsRegistry {
return {
response: await agentContext.config.agentDependencies.fetch(fetchResourceUrl, { method: 'GET' }),
resourceId,
did: parsedDid.did,
}
}
}
2 changes: 1 addition & 1 deletion test/__fixtures__/schema1.json
Original file line number Diff line number Diff line change
@@ -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"]
Expand Down
37 changes: 35 additions & 2 deletions test/credentialDefinition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`,
Expand Down Expand Up @@ -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: {},
})
})
})
33 changes: 33 additions & 0 deletions test/revocationRegistryDefinition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
})
})
})
35 changes: 34 additions & 1 deletion test/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, {
Expand Down

0 comments on commit 7cab490

Please sign in to comment.