aries-cloudagent-python icon indicating copy to clipboard operation
aries-cloudagent-python copied to clipboard

🐛 presenting a revocable credential, when verifier requested "non_revoked" before rev registry was created, leads to TypeError

Open ff137 opened this issue 1 year ago • 2 comments

Scenario:

  • a verifier posts an IndyProofRequest to a holder using /present-proof-2.0/send-request
    • the verifier's proof request specifies: non_revoked: to: <any timestamp from distant past>
  • the prover responds to this presentation (by posting to /present-proof-2.0/records/{pres_ex_id}/send-presentation)
    • the prover presents a cred_id pointing to a newly minted (non-revoked) revocable credential.
  • through some mightily difficult-to-read code in IndyPresExchHandler.return_presentation, since non_revoked.to was included in the request, we end up calling the "revocation registry delta" (in this case from the indy_vdr ledger).
  • the response we get from the ledger does not contain some expected keys, and leads to a TypeError:
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/asyncio/tasks.py", line 256, in __step
    result = coro.send(None)
  File "/home/aries/.local/lib/python3.9/site-packages/aries_cloudagent/protocols/present_proof/v2_0/routes.py", line 1199, in present_proof_send_presentation
    pres_ex_record, pres_message = await pres_manager.create_pres(
  File "/home/aries/.local/lib/python3.9/site-packages/aries_cloudagent/protocols/present_proof/v2_0/manager.py", line 277, in create_pres
    pres_tuple = await pres_exch_format.handler(self._profile).create_pres(
  File "/home/aries/.local/lib/python3.9/site-packages/aries_cloudagent/protocols/present_proof/v2_0/formats/indy/handler.py", line 162, in create_pres
    indy_proof = await indy_handler.return_presentation(
  File "/home/aries/.local/lib/python3.9/site-packages/aries_cloudagent/protocols/present_proof/indy/pres_exch_handler.py", line 155, in return_presentation
    (delta, delta_timestamp) = await ledger.get_revoc_reg_delta(
  File "/home/aries/.local/lib/python3.9/site-packages/aries_cloudagent/ledger/indy_vdr.py", line 1056, in get_revoc_reg_delta
	"accum": response_value["accum_to"]["value"]["accum"],
TypeError: 'NoneType' object is not subscriptable

Edit: Occurs in 0.11 as well as latest 0.12.1

The issue was first logged here: https://github.com/didx-xyz/aries-cloudapi-python/issues/811#issuecomment-2122133204, describing how it arose.

I could since learn that it specifically stems from the "to" timestamp that's specified in the non_revoked range. In our tests, we used nonsensical values (from: 0, to: 20), and noticed it would reliably fail when using revocable credentials instead of non-revocable credentials.

The reason is fairly simple: the existing code expects certain response keys must exist: ["accum_to"]["value"]["accum"].

Whenever the verifier requests a "to" timestamp from the past, or any point in time before the revocation registry was created, then we will get "accum_to": None in the response from indy_vdr, leading to the TypeError.

The error does not arise if non_revoked.to specifies any moment after the revocation registry was created (exact leeway unclear).


I can contribute a fix that avoids the KeyError, but I would perhaps need some advice for what is the desired behaviour.

In the return_presentation code, we iterate over requested_referents, and fetch rev reg deltas from the ledger:

        # Get delta with non-revocation interval defined in "non_revoked"
        # of the presentation request or attributes
        epoch_now = int(time.time())
        revoc_reg_deltas = {}
        for precis in requested_referents.values():  # cred_id, non-revoc interval
            credential_id = precis["cred_id"]
            if not credentials[credential_id].get("rev_reg_id"):
                continue
...
            async with ledger:
                reft_non_revoc_interval = precis.get("non_revoked")
                if reft_non_revoc_interval:
                    key = (
                        f"{rev_reg_id}_"
                        f"{reft_non_revoc_interval.get('from', 0)}_"
                        f"{reft_non_revoc_interval.get('to', epoch_now)}"
                    )
                    if key not in revoc_reg_deltas:
                        (delta, delta_timestamp) = await ledger.get_revoc_reg_delta(

I figure that we should also just continue within this for loop if we could know that the rev reg was created after the verifier's to request -- because of course the credential can't be revoked before it's defined.

What do you think? The verifier is asking that a credential is non_revoked at some point in time before the credential definition even existed. Currently that raises a KeyError/TypeError. The verification could just succeed, since it was technically not revoked then... But of course, the verifier may specifically want to know the credential was held and non-revoked at that point in time, in which case the desired outcome may be that the proof ought to be output as false. Hmm ... how ambiguous!

Any input is welcome!

ff137 avatar May 21 '24 19:05 ff137

:+1: I actually noticed this as well. I should have created a ticket for it.

jamshale avatar May 21 '24 22:05 jamshale

@cvarjao — is this possibly the issue with the mysterious fails for lawyer credentials? Is that still happening?

From the original message:

Whenever the verifier requests a "to" timestamp from the past, or any point in time before the revocation registry was created, then we will get "accum_to": None in the response from indy_vdr, leading to the TypeError.

swcurran avatar May 23 '24 23:05 swcurran

@ff137 @swcurran I was thinking that if the range is before the registry was created that the range could be adjusted to begin at the same timestamp as it was created, and then just proceed as normal. That would help with this question:

What do you think? The verifier is asking that a credential is non_revoked at some point in time before the credential definition even existed. Currently that raises a KeyError/TypeError. The verification could just succeed, since it was technically not revoked then... But of course, the verifier may specifically want to know the credential was held and non-revoked at that point in time, in which case the desired outcome may be that the proof ought to be output as false. Hmm ... how ambiguous!

This requires getting the registry timestamp and then re-querying the delta again though.

I think it makes sense that if the request was for before the object was created to just use the time of creation?

jamshale avatar May 27 '24 20:05 jamshale