From 5912c0ce2dbc8f773cec5324ffb19c40b15009b0 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Wed, 16 Feb 2022 11:56:47 +0100 Subject: [PATCH] feat: filter retrieved credential by revocation state (#641) Signed-off-by: Timo Glastra --- .../indy/services/IndyRevocationService.ts | 2 +- .../core/src/modules/proofs/ProofsModule.ts | 12 ++ .../modules/proofs/services/ProofService.ts | 116 ++++++++++-------- 3 files changed, 77 insertions(+), 53 deletions(-) diff --git a/packages/core/src/modules/indy/services/IndyRevocationService.ts b/packages/core/src/modules/indy/services/IndyRevocationService.ts index 0edf9078c1..6d9b4b6e90 100644 --- a/packages/core/src/modules/indy/services/IndyRevocationService.ts +++ b/packages/core/src/modules/indy/services/IndyRevocationService.ts @@ -167,7 +167,7 @@ export class IndyRevocationService { const revoked: boolean = revocationRegistryDelta.value.revoked?.includes(parseInt(credentialRevocationId)) || false this.logger.trace( - `Credental with Credential Revocation Id '${credentialRevocationId}' is ${ + `Credential with Credential Revocation Id '${credentialRevocationId}' is ${ revoked ? '' : 'not ' }revoked with revocation interval with to '${requestRevocationInterval.to}' & from '${ requestRevocationInterval.from diff --git a/packages/core/src/modules/proofs/ProofsModule.ts b/packages/core/src/modules/proofs/ProofsModule.ts index b4a934c9f1..80a2232ded 100644 --- a/packages/core/src/modules/proofs/ProofsModule.ts +++ b/packages/core/src/modules/proofs/ProofsModule.ts @@ -356,6 +356,7 @@ export class ProofsModule { return this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, { presentationProposal: presentationPreview, + filterByNonRevocationRequirements: config?.filterByNonRevocationRequirements ?? true, }) } @@ -476,6 +477,17 @@ export interface GetRequestedCredentialsConfig { * Whether to filter the retrieved credentials using the presentation preview. * This configuration will only have effect if a presentation proposal message is available * containing a presentation preview. + * + * @default false */ filterByPresentationPreview?: boolean + + /** + * Whether to filter the retrieved credentials using the non-revocation request in the proof request. + * This configuration will only have effect if the proof request requires proof on non-revocation of any kind. + * Default to true + * + * @default true + */ + filterByNonRevocationRequirements?: boolean } diff --git a/packages/core/src/modules/proofs/services/ProofService.ts b/packages/core/src/modules/proofs/services/ProofService.ts index df31a93751..e06b5b4e71 100644 --- a/packages/core/src/modules/proofs/services/ProofService.ts +++ b/packages/core/src/modules/proofs/services/ProofService.ts @@ -766,6 +766,7 @@ export class ProofService { proofRequest: ProofRequest, config: { presentationProposal?: PresentationPreview + filterByNonRevocationRequirements?: boolean } = {} ): Promise { const retrievedCredentials = new RetrievedCredentials({}) @@ -801,32 +802,11 @@ export class ProofService { retrievedCredentials.requestedAttributes[referent] = await Promise.all( credentialMatch.map(async (credential: Credential) => { - const requestNonRevoked = requestedAttribute.nonRevoked ?? proofRequest.nonRevoked - const credentialRevocationId = credential.credentialInfo.credentialRevocationId - const revocationRegistryId = credential.credentialInfo.revocationRegistryId - let revoked: boolean | undefined - let deltaTimestamp: number | undefined - - // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display - if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - this.logger.trace( - `Presentation is requesting proof of non revocation for referent '${referent}', getting revocation status for credential`, - { - requestNonRevoked, - credentialRevocationId, - revocationRegistryId, - } - ) - - // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await this.indyRevocationService.getRevocationStatus( - credentialRevocationId, - revocationRegistryId, - requestNonRevoked - ) - revoked = status.revoked - deltaTimestamp = status.deltaTimestamp - } + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem({ + proofRequest, + requestedItem: requestedAttribute, + credential, + }) return new RequestedAttribute({ credentialId: credential.credentialInfo.referent, @@ -837,6 +817,14 @@ export class ProofService { }) }) ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (config.filterByNonRevocationRequirements) { + retrievedCredentials.requestedAttributes[referent] = retrievedCredentials.requestedAttributes[referent].filter( + (r) => !r.revoked + ) + } } for (const [referent, requestedPredicate] of proofRequest.requestedPredicates.entries()) { @@ -844,32 +832,11 @@ export class ProofService { retrievedCredentials.requestedPredicates[referent] = await Promise.all( credentials.map(async (credential) => { - const requestNonRevoked = requestedPredicate.nonRevoked ?? proofRequest.nonRevoked - const credentialRevocationId = credential.credentialInfo.credentialRevocationId - const revocationRegistryId = credential.credentialInfo.revocationRegistryId - let revoked: boolean | undefined - let deltaTimestamp: number | undefined - - // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display - if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { - this.logger.trace( - `Presentation is requesting proof of non revocation for referent '${referent}', getting revocation status for credential`, - { - requestNonRevoked, - credentialRevocationId, - revocationRegistryId, - } - ) - - // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals - const status = await this.indyRevocationService.getRevocationStatus( - credentialRevocationId, - revocationRegistryId, - requestNonRevoked - ) - revoked = status.revoked - deltaTimestamp = status.deltaTimestamp - } + const { revoked, deltaTimestamp } = await this.getRevocationStatusForRequestedItem({ + proofRequest, + requestedItem: requestedPredicate, + credential, + }) return new RequestedPredicate({ credentialId: credential.credentialInfo.referent, @@ -879,6 +846,14 @@ export class ProofService { }) }) ) + + // We only attach revoked state if non-revocation is requested. So if revoked is true it means + // the credential is not applicable to the proof request + if (config.filterByNonRevocationRequirements) { + retrievedCredentials.requestedPredicates[referent] = retrievedCredentials.requestedPredicates[referent].filter( + (r) => !r.revoked + ) + } } return retrievedCredentials @@ -1067,6 +1042,43 @@ export class ProofService { return JsonTransformer.fromJSON(credentialsJson, Credential) as unknown as Credential[] } + private async getRevocationStatusForRequestedItem({ + proofRequest, + requestedItem, + credential, + }: { + proofRequest: ProofRequest + requestedItem: ProofAttributeInfo | ProofPredicateInfo + credential: Credential + }) { + const requestNonRevoked = requestedItem.nonRevoked ?? proofRequest.nonRevoked + const credentialRevocationId = credential.credentialInfo.credentialRevocationId + const revocationRegistryId = credential.credentialInfo.revocationRegistryId + + // If revocation interval is present and the credential is revocable then fetch the revocation status of credentials for display + if (requestNonRevoked && credentialRevocationId && revocationRegistryId) { + this.logger.trace( + `Presentation is requesting proof of non revocation, getting revocation status for credential`, + { + requestNonRevoked, + credentialRevocationId, + revocationRegistryId, + } + ) + + // Note presentation from-to's vs ledger from-to's: https://github.com/hyperledger/indy-hipe/blob/master/text/0011-cred-revocation/README.md#indy-node-revocation-registry-intervals + const status = await this.indyRevocationService.getRevocationStatus( + credentialRevocationId, + revocationRegistryId, + requestNonRevoked + ) + + return status + } + + return { revoked: undefined, deltaTimestamp: undefined } + } + /** * Update the record to a new state and emit an state changed event. Also updates the record * in storage.