Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: filter retrieved credential by revocation state #641

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/modules/proofs/ProofsModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ export class ProofsModule {

return this.proofService.getRequestedCredentialsForProofRequest(indyProofRequest, {
presentationProposal: presentationPreview,
filterByNonRevocationRequirements: config?.filterByNonRevocationRequirements,
})
}

Expand Down Expand Up @@ -478,4 +479,10 @@ export interface GetRequestedCredentialsConfig {
* containing a presentation preview.
*/
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.
*/
filterByNonRevocationRequirements?: boolean
}
116 changes: 64 additions & 52 deletions packages/core/src/modules/proofs/services/ProofService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,7 @@ export class ProofService {
proofRequest: ProofRequest,
config: {
presentationProposal?: PresentationPreview
filterByNonRevocationRequirements?: boolean
} = {}
): Promise<RetrievedCredentials> {
const retrievedCredentials = new RetrievedCredentials({})
Expand Down Expand Up @@ -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,
Expand All @@ -837,39 +817,26 @@ 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()) {
const credentials = await this.getCredentialsForProofRequest(proofRequest, referent)

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,
Expand All @@ -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
Expand Down Expand Up @@ -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(
TimoGlastra marked this conversation as resolved.
Show resolved Hide resolved
credentialRevocationId,
revocationRegistryId,
requestNonRevoked
)

return { revoked: status.revoked, deltaTimestamp: status.deltaTimestamp }
}

return { revoked: undefined, deltaTimestamp: undefined }
}

/**
* Update the record to a new state and emit an state changed event. Also updates the record
* in storage.
Expand Down