🐛 presenting a revocable credential, when verifier requested "non_revoked" before rev registry was created, leads to TypeError
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 verifier's proof request specifies:
- 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, sincenon_revoked.towas 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!
:+1: I actually noticed this as well. I should have created a ticket for it.
@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.
@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?