Skip to content

Commit 5992c57

Browse files
authored
feat(anoncreds): expose methods and metadata (#1797)
Signed-off-by: Martin Auer <martin.auer97@gmail.com>
1 parent b83c517 commit 5992c57

5 files changed

Lines changed: 166 additions & 154 deletions

File tree

packages/anoncreds/src/formats/AnonCredsProofFormatService.ts

Lines changed: 5 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,12 @@
1-
import type {
2-
AnonCredsProofFormat,
3-
AnonCredsCredentialsForProofRequest,
4-
AnonCredsGetCredentialsForProofRequestOptions,
5-
} from './AnonCredsProofFormat'
1+
import type { AnonCredsProofFormat, AnonCredsGetCredentialsForProofRequestOptions } from './AnonCredsProofFormat'
62
import type {
73
AnonCredsCredentialDefinition,
8-
AnonCredsCredentialInfo,
94
AnonCredsProof,
10-
AnonCredsRequestedAttribute,
11-
AnonCredsRequestedAttributeMatch,
12-
AnonCredsRequestedPredicate,
13-
AnonCredsRequestedPredicateMatch,
145
AnonCredsSchema,
156
AnonCredsSelectedCredentials,
167
AnonCredsProofRequest,
178
} from '../models'
18-
import type { AnonCredsHolderService, AnonCredsVerifierService, GetCredentialsForProofRequestReturn } from '../services'
9+
import type { AnonCredsHolderService, AnonCredsVerifierService } from '../services'
1910
import type {
2011
ProofFormatService,
2112
AgentContext,
@@ -40,20 +31,17 @@ import { CredoError, Attachment, AttachmentData, JsonEncoder, ProofFormatSpec, J
4031
import { AnonCredsProofRequest as AnonCredsProofRequestClass } from '../models/AnonCredsProofRequest'
4132
import { AnonCredsVerifierServiceSymbol, AnonCredsHolderServiceSymbol } from '../services'
4233
import {
43-
sortRequestedCredentialsMatches,
4434
createRequestFromPreview,
4535
areAnonCredsProofRequestsEqual,
46-
assertBestPracticeRevocationInterval,
4736
checkValidCredentialValueEncoding,
4837
assertNoDuplicateGroupsNamesInProofRequest,
4938
getRevocationRegistriesForRequest,
5039
getRevocationRegistriesForProof,
5140
fetchSchema,
5241
fetchCredentialDefinition,
53-
fetchRevocationStatusList,
5442
} from '../utils'
5543
import { encodeCredentialValue } from '../utils/credential'
56-
import { dateToTimestamp } from '../utils/timestamp'
44+
import { getCredentialsForAnonCredsProofRequest } from '../utils/getCredentialsForAnonCredsRequest'
5745

5846
const ANONCREDS_PRESENTATION_PROPOSAL = 'anoncreds/proof-request@v1.0'
5947
const ANONCREDS_PRESENTATION_REQUEST = 'anoncreds/proof-request@v1.0'
@@ -254,7 +242,7 @@ export class AnonCredsProofFormatService implements ProofFormatService<AnonCreds
254242
// Set default values
255243
const { filterByNonRevocationRequirements = true } = proofFormats?.anoncreds ?? {}
256244

257-
const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequestJson, {
245+
const credentialsForRequest = await getCredentialsForAnonCredsProofRequest(agentContext, proofRequestJson, {
258246
filterByNonRevocationRequirements,
259247
})
260248

@@ -319,90 +307,12 @@ export class AnonCredsProofFormatService implements ProofFormatService<AnonCreds
319307
return supportedFormats.includes(formatIdentifier)
320308
}
321309

322-
private async _getCredentialsForRequest(
323-
agentContext: AgentContext,
324-
proofRequest: AnonCredsProofRequest,
325-
options: AnonCredsGetCredentialsForProofRequestOptions
326-
): Promise<AnonCredsCredentialsForProofRequest> {
327-
const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = {
328-
attributes: {},
329-
predicates: {},
330-
}
331-
332-
for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) {
333-
const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent)
334-
335-
credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches(
336-
await Promise.all(
337-
credentials.map(async (credential) => {
338-
const { isRevoked, timestamp } = await this.getRevocationStatus(
339-
agentContext,
340-
proofRequest,
341-
requestedAttribute,
342-
credential.credentialInfo
343-
)
344-
345-
return {
346-
credentialId: credential.credentialInfo.credentialId,
347-
revealed: true,
348-
credentialInfo: credential.credentialInfo,
349-
timestamp,
350-
revoked: isRevoked,
351-
} satisfies AnonCredsRequestedAttributeMatch
352-
})
353-
)
354-
)
355-
356-
// We only attach revoked state if non-revocation is requested. So if revoked is true it means
357-
// the credential is not applicable to the proof request
358-
if (options.filterByNonRevocationRequirements) {
359-
credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter(
360-
(r) => !r.revoked
361-
)
362-
}
363-
}
364-
365-
for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) {
366-
const credentials = await this.getCredentialsForProofRequestReferent(agentContext, proofRequest, referent)
367-
368-
credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches(
369-
await Promise.all(
370-
credentials.map(async (credential) => {
371-
const { isRevoked, timestamp } = await this.getRevocationStatus(
372-
agentContext,
373-
proofRequest,
374-
requestedPredicate,
375-
credential.credentialInfo
376-
)
377-
378-
return {
379-
credentialId: credential.credentialInfo.credentialId,
380-
credentialInfo: credential.credentialInfo,
381-
timestamp,
382-
revoked: isRevoked,
383-
} satisfies AnonCredsRequestedPredicateMatch
384-
})
385-
)
386-
)
387-
388-
// We only attach revoked state if non-revocation is requested. So if revoked is true it means
389-
// the credential is not applicable to the proof request
390-
if (options.filterByNonRevocationRequirements) {
391-
credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter(
392-
(r) => !r.revoked
393-
)
394-
}
395-
}
396-
397-
return credentialsForProofRequest
398-
}
399-
400310
private async _selectCredentialsForRequest(
401311
agentContext: AgentContext,
402312
proofRequest: AnonCredsProofRequest,
403313
options: AnonCredsGetCredentialsForProofRequestOptions
404314
): Promise<AnonCredsSelectedCredentials> {
405-
const credentialsForRequest = await this._getCredentialsForRequest(agentContext, proofRequest, options)
315+
const credentialsForRequest = await getCredentialsForAnonCredsProofRequest(agentContext, proofRequest, options)
406316

407317
const selectedCredentials: AnonCredsSelectedCredentials = {
408318
attributes: {},
@@ -431,21 +341,6 @@ export class AnonCredsProofFormatService implements ProofFormatService<AnonCreds
431341
return selectedCredentials
432342
}
433343

434-
private async getCredentialsForProofRequestReferent(
435-
agentContext: AgentContext,
436-
proofRequest: AnonCredsProofRequest,
437-
attributeReferent: string
438-
): Promise<GetCredentialsForProofRequestReturn> {
439-
const holderService = agentContext.dependencyManager.resolve<AnonCredsHolderService>(AnonCredsHolderServiceSymbol)
440-
441-
const credentials = await holderService.getCredentialsForProofRequest(agentContext, {
442-
proofRequest,
443-
attributeReferent,
444-
})
445-
446-
return credentials
447-
}
448-
449344
/**
450345
* Build schemas object needed to create and verify proof objects.
451346
*
@@ -486,49 +381,6 @@ export class AnonCredsProofFormatService implements ProofFormatService<AnonCreds
486381
return credentialDefinitions
487382
}
488383

489-
private async getRevocationStatus(
490-
agentContext: AgentContext,
491-
proofRequest: AnonCredsProofRequest,
492-
requestedItem: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate,
493-
credentialInfo: AnonCredsCredentialInfo
494-
) {
495-
const requestNonRevoked = requestedItem.non_revoked ?? proofRequest.non_revoked
496-
const credentialRevocationId = credentialInfo.credentialRevocationId
497-
const revocationRegistryId = credentialInfo.revocationRegistryId
498-
499-
// If revocation interval is not present or the credential is not revocable then we
500-
// don't need to fetch the revocation status
501-
if (!requestNonRevoked || credentialRevocationId === null || !revocationRegistryId) {
502-
return { isRevoked: undefined, timestamp: undefined }
503-
}
504-
505-
agentContext.config.logger.trace(
506-
`Fetching credential revocation status for credential revocation id '${credentialRevocationId}' with revocation interval with from '${requestNonRevoked.from}' and to '${requestNonRevoked.to}'`
507-
)
508-
509-
// Make sure the revocation interval follows best practices from Aries RFC 0441
510-
assertBestPracticeRevocationInterval(requestNonRevoked)
511-
512-
const { revocationStatusList } = await fetchRevocationStatusList(
513-
agentContext,
514-
revocationRegistryId,
515-
requestNonRevoked.to ?? dateToTimestamp(new Date())
516-
)
517-
518-
const isRevoked = revocationStatusList.revocationList[parseInt(credentialRevocationId)] === 1
519-
520-
agentContext.config.logger.trace(
521-
`Credential with credential revocation index '${credentialRevocationId}' is ${
522-
isRevoked ? '' : 'not '
523-
}revoked with revocation interval with to '${requestNonRevoked.to}' & from '${requestNonRevoked.from}'`
524-
)
525-
526-
return {
527-
isRevoked,
528-
timestamp: revocationStatusList.timestamp,
529-
}
530-
}
531-
532384
/**
533385
* Create anoncreds proof from a given proof request and requested credential object.
534386
*

packages/anoncreds/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ export {
2323
fetchSchema,
2424
fetchRevocationStatusList,
2525
} from './utils/anonCredsObjects'
26+
27+
export { AnonCredsCredentialMetadataKey } from './utils/metadata'
28+
export { getAnonCredsTagsFromRecord, AnonCredsCredentialTags } from './utils/w3cAnonCredsUtils'
29+
export { W3cAnonCredsCredentialMetadataKey } from './utils/metadata'
30+
export { getCredentialsForAnonCredsProofRequest } from './utils/getCredentialsForAnonCredsRequest'
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import type { AnonCredsCredentialsForProofRequest, AnonCredsGetCredentialsForProofRequestOptions } from '../formats'
2+
import type {
3+
AnonCredsCredentialInfo,
4+
AnonCredsProofRequest,
5+
AnonCredsRequestedAttribute,
6+
AnonCredsRequestedAttributeMatch,
7+
AnonCredsRequestedPredicate,
8+
AnonCredsRequestedPredicateMatch,
9+
} from '../models'
10+
import type { AnonCredsHolderService, GetCredentialsForProofRequestReturn } from '../services'
11+
import type { AgentContext } from '@credo-ts/core'
12+
13+
import { AnonCredsHolderServiceSymbol } from '../services'
14+
15+
import { fetchRevocationStatusList } from './anonCredsObjects'
16+
import { assertBestPracticeRevocationInterval } from './revocationInterval'
17+
import { sortRequestedCredentialsMatches } from './sortRequestedCredentialsMatches'
18+
import { dateToTimestamp } from './timestamp'
19+
20+
const getCredentialsForProofRequestReferent = async (
21+
agentContext: AgentContext,
22+
proofRequest: AnonCredsProofRequest,
23+
attributeReferent: string
24+
): Promise<GetCredentialsForProofRequestReturn> => {
25+
const holderService = agentContext.dependencyManager.resolve<AnonCredsHolderService>(AnonCredsHolderServiceSymbol)
26+
27+
const credentials = await holderService.getCredentialsForProofRequest(agentContext, {
28+
proofRequest,
29+
attributeReferent,
30+
})
31+
32+
return credentials
33+
}
34+
35+
const getRevocationStatus = async (
36+
agentContext: AgentContext,
37+
proofRequest: AnonCredsProofRequest,
38+
requestedItem: AnonCredsRequestedAttribute | AnonCredsRequestedPredicate,
39+
credentialInfo: AnonCredsCredentialInfo
40+
) => {
41+
const requestNonRevoked = requestedItem.non_revoked ?? proofRequest.non_revoked
42+
const credentialRevocationId = credentialInfo.credentialRevocationId
43+
const revocationRegistryId = credentialInfo.revocationRegistryId
44+
45+
// If revocation interval is not present or the credential is not revocable then we
46+
// don't need to fetch the revocation status
47+
if (!requestNonRevoked || credentialRevocationId === null || !revocationRegistryId) {
48+
return { isRevoked: undefined, timestamp: undefined }
49+
}
50+
51+
agentContext.config.logger.trace(
52+
`Fetching credential revocation status for credential revocation id '${credentialRevocationId}' with revocation interval with from '${requestNonRevoked.from}' and to '${requestNonRevoked.to}'`
53+
)
54+
55+
// Make sure the revocation interval follows best practices from Aries RFC 0441
56+
assertBestPracticeRevocationInterval(requestNonRevoked)
57+
58+
const { revocationStatusList } = await fetchRevocationStatusList(
59+
agentContext,
60+
revocationRegistryId,
61+
requestNonRevoked.to ?? dateToTimestamp(new Date())
62+
)
63+
64+
const isRevoked = revocationStatusList.revocationList[parseInt(credentialRevocationId)] === 1
65+
66+
agentContext.config.logger.trace(
67+
`Credential with credential revocation index '${credentialRevocationId}' is ${
68+
isRevoked ? '' : 'not '
69+
}revoked with revocation interval with to '${requestNonRevoked.to}' & from '${requestNonRevoked.from}'`
70+
)
71+
72+
return {
73+
isRevoked,
74+
timestamp: revocationStatusList.timestamp,
75+
}
76+
}
77+
78+
export const getCredentialsForAnonCredsProofRequest = async (
79+
agentContext: AgentContext,
80+
proofRequest: AnonCredsProofRequest,
81+
options: AnonCredsGetCredentialsForProofRequestOptions
82+
): Promise<AnonCredsCredentialsForProofRequest> => {
83+
const credentialsForProofRequest: AnonCredsCredentialsForProofRequest = {
84+
attributes: {},
85+
predicates: {},
86+
}
87+
88+
for (const [referent, requestedAttribute] of Object.entries(proofRequest.requested_attributes)) {
89+
const credentials = await getCredentialsForProofRequestReferent(agentContext, proofRequest, referent)
90+
91+
credentialsForProofRequest.attributes[referent] = sortRequestedCredentialsMatches(
92+
await Promise.all(
93+
credentials.map(async (credential) => {
94+
const { isRevoked, timestamp } = await getRevocationStatus(
95+
agentContext,
96+
proofRequest,
97+
requestedAttribute,
98+
credential.credentialInfo
99+
)
100+
101+
return {
102+
credentialId: credential.credentialInfo.credentialId,
103+
revealed: true,
104+
credentialInfo: credential.credentialInfo,
105+
timestamp,
106+
revoked: isRevoked,
107+
} satisfies AnonCredsRequestedAttributeMatch
108+
})
109+
)
110+
)
111+
112+
// We only attach revoked state if non-revocation is requested. So if revoked is true it means
113+
// the credential is not applicable to the proof request
114+
if (options.filterByNonRevocationRequirements) {
115+
credentialsForProofRequest.attributes[referent] = credentialsForProofRequest.attributes[referent].filter(
116+
(r) => !r.revoked
117+
)
118+
}
119+
}
120+
121+
for (const [referent, requestedPredicate] of Object.entries(proofRequest.requested_predicates)) {
122+
const credentials = await getCredentialsForProofRequestReferent(agentContext, proofRequest, referent)
123+
124+
credentialsForProofRequest.predicates[referent] = sortRequestedCredentialsMatches(
125+
await Promise.all(
126+
credentials.map(async (credential) => {
127+
const { isRevoked, timestamp } = await getRevocationStatus(
128+
agentContext,
129+
proofRequest,
130+
requestedPredicate,
131+
credential.credentialInfo
132+
)
133+
134+
return {
135+
credentialId: credential.credentialInfo.credentialId,
136+
credentialInfo: credential.credentialInfo,
137+
timestamp,
138+
revoked: isRevoked,
139+
} satisfies AnonCredsRequestedPredicateMatch
140+
})
141+
)
142+
)
143+
144+
// We only attach revoked state if non-revocation is requested. So if revoked is true it means
145+
// the credential is not applicable to the proof request
146+
if (options.filterByNonRevocationRequirements) {
147+
credentialsForProofRequest.predicates[referent] = credentialsForProofRequest.predicates[referent].filter(
148+
(r) => !r.revoked
149+
)
150+
}
151+
}
152+
153+
return credentialsForProofRequest
154+
}

packages/anoncreds/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ export {
3232
W3cAnonCredsCredentialMetadata,
3333
} from './metadata'
3434
export { getW3cRecordAnonCredsTags } from './w3cAnonCredsUtils'
35+
export { getCredentialsForAnonCredsProofRequest } from './getCredentialsForAnonCredsRequest'

packages/anoncreds/src/utils/metadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface W3cAnonCredsCredentialMetadata {
3030
export const AnonCredsCredentialMetadataKey = '_anoncreds/credential'
3131

3232
/**
33-
* Metadata key for strong metadata on an AnonCreds credential request.
33+
* Metadata key for storing metadata on an AnonCreds credential request.
3434
*
3535
* MUST be used with {@link AnonCredsCredentialRequestMetadata}
3636
*/

0 commit comments

Comments
 (0)