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

Remove superfluous non revoc intervals #699

Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 4 additions & 3 deletions aries_cloudagent/protocols/present_proof/v1_0/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from .messages.presentation import Presentation
from .message_types import ATTACH_DECO_IDS, PRESENTATION, PRESENTATION_REQUEST

LOGGER = logging.getLogger(__name__)


class PresentationManagerError(BaseError):
"""Presentation error."""
Expand All @@ -37,7 +39,6 @@ def __init__(self, context: InjectionContext):
"""

self._context = context
self._logger = logging.getLogger(__name__)

@property
def context(self) -> InjectionContext:
Expand Down Expand Up @@ -393,7 +394,7 @@ async def create_presentation(
)
)
except HolderError as e:
self._logger.error(
LOGGER.error(
f"Failed to create revocation state: {e.error_code}, {e.message}"
)
raise e
Expand Down Expand Up @@ -615,7 +616,7 @@ async def send_presentation_ack(
connection_id=presentation_exchange_record.connection_id,
)
else:
self._logger.warning(
LOGGER.warning(
"Configuration has no BaseResponder: cannot ack presentation on %s",
presentation_exchange_record.thread_id,
)
Expand Down
49 changes: 49 additions & 0 deletions aries_cloudagent/verifier/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,52 @@ def __init__(self, ledger: BaseLedger):
"""
self.ledger = ledger

def non_revoc_intervals(self, pres_req: dict, pres: dict, pop_global: bool):
"""
Remove superfluous non-revocation intervals in presentation request.

Indy rejects proof requests with non-revocation intervals lining up
with non-revocable credentials in proof: seek and remove.

Args:
pres_req: presentation request
pres: corresponding presentation
pop_global: whether to excise global non-revocation interval if present

"""

for (req_proof_key, pres_key) in {
"revealed_attrs": "requested_attributes",
"revealed_attr_groups": "requested_attributes",
"predicates": "requested_predicates",
}.items():
for (uuid, spec) in pres["requested_proof"].get(req_proof_key, {}).items():
if (
pres["identifiers"][spec["sub_proof_index"]].get("timestamp")
is None
):
if pres_req[pres_key][uuid].pop("non_revoked", None):
LOGGER.warning(
(
"Amended presentation request (nonce=%s): removed "
"non-revocation interval at %s referent "
"%s; no corresponding revocable credential in proof"
),
pres_req["nonce"],
pres_key,
uuid,
)

if pop_global:
if pres_req.pop("non_revoked", None):
LOGGER.warning(
(
"Amended presentation request (nonce=%s); removed global "
"non-revocation interval; no revocable credentials in proof"
),
pres_req["nonce"],
)

async def pre_verify(self, pres_req: dict, pres: dict) -> (PreVerifyResult, str):
"""
Check for essential components and tampering in presentation.
Expand Down Expand Up @@ -193,6 +239,9 @@ async def verify_presentation(
rev_reg_entries: revocation registry entries
"""

print(f"\n\n## Verifying, input: {json.dumps(presentation)}")
self.non_revoc_intervals(presentation_request, presentation, not rev_reg_defs)

(pv_result, pv_msg) = await self.pre_verify(presentation_request, presentation)
if pv_result != PreVerifyResult.OK:
LOGGER.error(
Expand Down
128 changes: 126 additions & 2 deletions aries_cloudagent/verifier/tests/test_indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,9 @@ async def test_verify_presentation(self, mock_verify):

with async_mock.patch.object(
self.verifier, "pre_verify", async_mock.CoroutineMock()
) as mock_pre_verify:
) as mock_pre_verify, async_mock.patch.object(
self.verifier, "non_revoc_intervals", async_mock.MagicMock()
) as mock_non_revox:
mock_pre_verify.return_value = (PreVerifyResult.OK, None)
verified = await self.verifier.verify_presentation(
"presentation_request",
Expand Down Expand Up @@ -343,7 +345,9 @@ async def test_verify_presentation_x_indy(self, mock_verify):

with async_mock.patch.object(
self.verifier, "pre_verify", async_mock.CoroutineMock()
) as mock_pre_verify:
) as mock_pre_verify, async_mock.patch.object(
self.verifier, "non_revoc_intervals", async_mock.MagicMock()
) as mock_non_revox:
mock_pre_verify.return_value = (PreVerifyResult.OK, None)
verified = await self.verifier.verify_presentation(
{"nonce": "1234567890"},
Expand All @@ -365,6 +369,126 @@ async def test_verify_presentation_x_indy(self, mock_verify):

assert not verified

@async_mock.patch("indy.anoncreds.verifier_verify_proof")
async def test_non_revoc_intervals(self, mock_verify):
big_pres_req = {
"nonce": "12301197819298309547817",
"name": "proof_req",
"version": "0.0",
"requested_attributes": {
"17_uuid": {
"name": ["favouriteDrink"],
"restrictions": [
{"cred_def_id": "LjgpST2rjsoxYegQDRm7EL:3:CL:17:tag"}
],
"non_revoked": {"from": 1579892963, "to": 1579892963},
},
"18_uuid": {
"names": [
"effectiveDate",
"jurisdictionId",
"endDate",
"legalName",
"orgTypeId",
],
"restrictions": [
{"cred_def_id": "LjgpST2rjsoxYegQDRm7EL:3:CL:18:tag"}
],
"non_revoked": {"from": 1579892963, "to": 1579892963},
},
},
"requested_predicates": {
"18_id_GE_uuid": {
"name": "id",
"p_type": ">=",
"p_value": 4,
"restrictions": [
{"cred_def_id": "LjgpST2rjsoxYegQDRm7EL:3:CL:18:tag"}
],
"non_revoked": {"from": 1579892963, "to": 1579892963},
},
"18_busid_GE_uuid": {
"name": "busId",
"p_type": ">=",
"p_value": 11198760,
"restrictions": [
{"cred_def_id": "LjgpST2rjsoxYegQDRm7EL:3:CL:18:tag"}
],
"non_revoked": {"from": 1579892963, "to": 1579892963},
},
},
"non_revoked": {"from": 1579892963, "to": 1579892963},
}
big_pres = {
"proof": {
"proofs": [
{
"primary_proof": "...",
"non_revoc_proof": "...",
}
],
"aggregated_proof": "...",
},
"requested_proof": {
"revealed_attrs": {
"17_uuid": {
"sub_proof_index": 0,
"raw": "martini",
"encoded": "943924110058781320304650334433495169960",
}
},
"revealed_attr_groups": {
"18_uuid": {
"sub_proof_index": 1,
"values": {
"effectiveDate": {
"raw": "2018-01-01",
"encoded": "200737126045061047957925549204159",
},
"endDate": {
"raw": "",
"encoded": "10993379397001115665086549",
},
"jurisdictionId": {"raw": "1", "encoded": "1"},
"legalName": {
"raw": "Flan Nebula",
"encoded": "119967766011746391966411797095112",
},
"orgTypeId": {"raw": "2", "encoded": "2"},
},
}
},
"self_attested_attrs": {},
"unrevealed_attrs": {},
"predicates": {
"18_busid_GE_uuid": {"sub_proof_index": 1},
"18_id_GE_uuid": {"sub_proof_index": 1},
},
},
"identifiers": [
{
"schema_id": "LjgpST2rjsoxYegQDRm7EL:2:bc-reg:1.0",
"cred_def_id": "LjgpST2rjsoxYegQDRm7EL:3:CL:17:tag",
"rev_reg_id": None,
"timestamp": None,
},
{
"schema_id": "LjgpST2rjsoxYegQDRm7EL:2:bc-reg:1.0",
"cred_def_id": "LjgpST2rjsoxYegQDRm7EL:3:CL:18:tag",
"rev_reg_id": None,
"timestamp": None,
},
],
}

self.verifier.non_revoc_intervals(big_pres_req, big_pres, True)

assert "non_revoked" not in big_pres_req
for spec in big_pres_req["requested_attributes"].values():
assert "non_revoked" not in spec
for spec in big_pres_req["requested_predicates"].values():
assert "non_revoked" not in spec

async def test_pre_verify(self):
assert (
PreVerifyResult.INCOMPLETE
Expand Down