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

Update to indy-credx 1.0 #2302

Merged
merged 5 commits into from
Jul 19, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions aries_cloudagent/config/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,17 @@ def add_arguments(self, parser: ArgumentParser):
"revocation received."
),
)
parser.add_argument(
"--anoncreds-legacy-revocation",
type=str,
default="accept",
choices=("accept", "reject"),
env_var="ACAPY_ANONCREDS_LEGACY_REVOCATION",
help=(
"Specify the handling of older proofs of non-revocation "
"for anoncreds credentials. Values are 'accept' or 'reject'."
),
)

def get_settings(self, args: Namespace) -> dict:
"""Extract revocation settings."""
Expand All @@ -780,6 +791,10 @@ def get_settings(self, args: Namespace) -> dict:
settings[
"revocation.monitor_notification"
] = args.monitor_revocation_notification
if args.anoncreds_legacy_revocation:
settings[
"revocation.anoncreds_legacy_support"
] = args.anoncreds_legacy_revocation
return settings


Expand Down
36 changes: 18 additions & 18 deletions aries_cloudagent/indy/credx/holder.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
Credential,
CredentialRequest,
CredentialRevocationState,
MasterSecret,
LinkSecret,
Presentation,
PresentCredentials,
)
Expand All @@ -28,7 +28,7 @@
LOGGER = logging.getLogger(__name__)

CATEGORY_CREDENTIAL = "credential"
CATEGORY_MASTER_SECRET = "master_secret"
CATEGORY_LINK_SECRET = "master_secret"


def _make_cred_info(cred_id, cred: Credential):
Expand All @@ -51,7 +51,7 @@ def _normalize_attr_name(name: str) -> str:
class IndyCredxHolder(IndyHolder):
"""Indy-credx holder class."""

MASTER_SECRET_ID = "default"
LINK_SECRET_ID = "default"

def __init__(self, profile: AskarProfile):
"""
Expand All @@ -68,37 +68,37 @@ def profile(self) -> AskarProfile:
"""Accessor for the profile instance."""
return self._profile

async def get_master_secret(self) -> MasterSecret:
"""Get or create the default master secret."""
async def get_link_secret(self) -> LinkSecret:
"""Get or create the default link secret."""

while True:
async with self._profile.session() as session:
try:
record = await session.handle.fetch(
CATEGORY_MASTER_SECRET, IndyCredxHolder.MASTER_SECRET_ID
CATEGORY_LINK_SECRET, IndyCredxHolder.LINK_SECRET_ID
)
except AskarError as err:
raise IndyHolderError("Error fetching master secret") from err
raise IndyHolderError("Error fetching link secret") from err
if record:
try:
secret = MasterSecret.load(record.raw_value)
secret = LinkSecret.load(record.raw_value)
except CredxError as err:
raise IndyHolderError("Error loading master secret") from err
raise IndyHolderError("Error loading link secret") from err
break
else:
try:
secret = MasterSecret.create()
secret = LinkSecret.create()
except CredxError as err:
raise IndyHolderError("Error creating master secret") from err
raise IndyHolderError("Error creating link secret") from err
try:
await session.handle.insert(
CATEGORY_MASTER_SECRET,
IndyCredxHolder.MASTER_SECRET_ID,
CATEGORY_LINK_SECRET,
IndyCredxHolder.LINK_SECRET_ID,
secret.to_json_buffer(),
)
except AskarError as err:
if err.code != AskarErrorCode.DUPLICATE:
raise IndyHolderError("Error saving master secret") from err
raise IndyHolderError("Error saving link secret") from err
# else: lost race to create record, retry
else:
break
Expand All @@ -120,7 +120,7 @@ async def create_credential_request(

"""
try:
secret = await self.get_master_secret()
secret = await self.get_link_secret()
(
cred_req,
cred_req_metadata,
Expand All @@ -130,7 +130,7 @@ async def create_credential_request(
holder_did,
credential_definition,
secret,
IndyCredxHolder.MASTER_SECRET_ID,
IndyCredxHolder.LINK_SECRET_ID,
credential_offer,
)
except CredxError as err:
Expand Down Expand Up @@ -176,7 +176,7 @@ async def store_credential(

"""
try:
secret = await self.get_master_secret()
secret = await self.get_link_secret()
cred = Credential.load(credential_data)
cred_recvd = await asyncio.get_event_loop().run_in_executor(
None,
Expand Down Expand Up @@ -523,7 +523,7 @@ def get_rev_state(cred_id: str, detail: dict):
)

try:
secret = await self.get_master_secret()
secret = await self.get_link_secret()
presentation = await asyncio.get_event_loop().run_in_executor(
None,
Presentation.create,
Expand Down
47 changes: 38 additions & 9 deletions aries_cloudagent/indy/credx/issuer.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
CredxError,
RevocationRegistry,
RevocationRegistryDefinition,
RevocationRegistryDefinitionPrivate,
RevocationRegistryDelta,
Schema,
)
Expand Down Expand Up @@ -332,7 +333,6 @@ async def create_credential(
rev_reg.raw_value,
rev_reg_index,
rev_info.get("used_ids") or [],
tails_file_path,
)
credential_revocation_id = str(rev_reg_index)
else:
Expand Down Expand Up @@ -362,6 +362,7 @@ async def create_credential(

async def revoke_credentials(
self,
cred_def_id: str,
revoc_reg_id: str,
tails_file_path: str,
cred_revoc_ids: Sequence[str],
Expand All @@ -370,6 +371,7 @@ async def revoke_credentials(
Revoke a set of credentials in a revocation registry.

Args:
cred_def_id: ID of the credential definition
revoc_reg_id: ID of the revocation registry
tails_file_path: path to the local tails file
cred_revoc_ids: sequences of credential indexes in the revocation registry
Expand All @@ -390,29 +392,60 @@ async def revoke_credentials(
raise IndyIssuerError("Repeated conflict attempting to update registry")
try:
async with self._profile.session() as session:
cred_def = await session.handle.fetch(
CATEGORY_CRED_DEF, cred_def_id
)
rev_reg_def = await session.handle.fetch(
CATEGORY_REV_REG_DEF, revoc_reg_id
)
rev_reg_def_private = await session.handle.fetch(
CATEGORY_REV_REG_DEF_PRIVATE, revoc_reg_id
)
rev_reg = await session.handle.fetch(CATEGORY_REV_REG, revoc_reg_id)
rev_reg_info = await session.handle.fetch(
CATEGORY_REV_REG_INFO, revoc_reg_id
)
if not cred_def:
raise IndyIssuerError("Credential definition not found")
if not rev_reg_def:
raise IndyIssuerError("Revocation registry definition not found")
if not rev_reg_def_private:
raise IndyIssuerError(
"Revocation registry definition private key not found"
)
if not rev_reg:
raise IndyIssuerError("Revocation registry not found")
if not rev_reg_info:
raise IndyIssuerError("Revocation registry metadata not found")
except AskarError as err:
raise IndyIssuerError("Error retrieving revocation registry") from err

try:
cred_def = CredentialDefinition.load(cred_def.raw_value)
except CredxError as err:
raise IndyIssuerError("Error loading credential definition") from err

try:
rev_reg_def = RevocationRegistryDefinition.load(rev_reg_def.raw_value)
except CredxError as err:
raise IndyIssuerError(
"Error loading revocation registry definition"
) from err

try:
rev_reg_def_private = RevocationRegistryDefinitionPrivate.load(
rev_reg_def_private.raw_value
)
except CredxError as err:
raise IndyIssuerError(
"Error loading revocation registry private key"
) from err

try:
rev_reg = RevocationRegistry.load(rev_reg.raw_value)
except CredxError as err:
raise IndyIssuerError("Error loading revocation registry") from err

rev_crids = set()
failed_crids = set()
max_cred_num = rev_reg_def.max_cred_num
Expand Down Expand Up @@ -451,19 +484,15 @@ async def revoke_credentials(
if not rev_crids:
break

try:
rev_reg = RevocationRegistry.load(rev_reg.raw_value)
except CredxError as err:
raise IndyIssuerError("Error loading revocation registry") from err

try:
delta = await asyncio.get_event_loop().run_in_executor(
None,
lambda: rev_reg.update(
cred_def,
rev_reg_def,
None, # issued
list(rev_crids), # revoked
tails_file_path,
rev_reg_def_private,
issued=None,
revoked=list(rev_crids), # revoked
),
)
except CredxError as err:
Expand Down
2 changes: 1 addition & 1 deletion aries_cloudagent/indy/credx/tests/test_cred_issuance.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ async def test_issue_store_rev(self):
rev_delta_init = {"ver": "1.0", "value": rev_state_init["rev_reg"]}

(rev_delta_2_json, skipped_ids) = await self.issuer.revoke_credentials(
reg_id, tails_path, (1,)
cd_id, reg_id, tails_path, (1,)
)
assert not skipped_ids
rev_delta_2 = json.loads(rev_delta_2_json)
Expand Down
9 changes: 8 additions & 1 deletion aries_cloudagent/indy/credx/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import asyncio
import logging

from typing import Tuple

from indy_credx import CredxError, Presentation

from ...core.profile import Profile
Expand Down Expand Up @@ -33,7 +35,7 @@ async def verify_presentation(
credential_definitions,
rev_reg_defs,
rev_reg_entries,
) -> (bool, list):
) -> Tuple[bool, list]:
"""
Verify a presentation.

Expand All @@ -46,6 +48,10 @@ async def verify_presentation(
rev_reg_entries: revocation registry entries
"""

accept_legacy_revocation = (
self.profile.settings.get("revocation.anoncreds_legacy_support", "accept")
== "accept"
)
msgs = []
try:
msgs += self.non_revoc_intervals(pres_req, pres, credential_definitions)
Expand All @@ -72,6 +78,7 @@ async def verify_presentation(
credential_definitions.values(),
rev_reg_defs.values(),
rev_reg_entries,
accept_legacy_revocation,
)
except CredxError as err:
s = str(err)
Expand Down
2 changes: 2 additions & 0 deletions aries_cloudagent/indy/issuer.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ async def create_credential(
@abstractmethod
async def revoke_credentials(
self,
cred_def_id: str,
revoc_reg_id: str,
tails_file_path: str,
cred_rev_ids: Sequence[str],
Expand All @@ -152,6 +153,7 @@ async def revoke_credentials(
Revoke a set of credentials in a revocation registry.

Args:
cred_def_id: ID of the credential definition
revoc_reg_id: ID of the revocation registry
tails_file_path: path to the local tails file
cred_rev_ids: sequences of credential indexes in the revocation registry
Expand Down
2 changes: 2 additions & 0 deletions aries_cloudagent/indy/sdk/issuer.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ async def create_credential(

async def revoke_credentials(
self,
cred_def_id: str,
rev_reg_id: str,
tails_file_path: str,
cred_rev_ids: Sequence[str],
Expand All @@ -252,6 +253,7 @@ async def revoke_credentials(
Revoke a set of credentials in a revocation registry.

Args:
cred_def_id: ID of the credential definition
rev_reg_id: ID of the revocation registry
tails_file_path: path to the local tails file
cred_rev_ids: sequences of credential indexes in the revocation registry
Expand Down
10 changes: 8 additions & 2 deletions aries_cloudagent/indy/sdk/tests/test_issuer.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ async def test_create_revoke_credentials(
mock_indy_revoke_credential.return_value = json.dumps(TEST_RR_DELTA)
mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA)
(result, failed) = await self.issuer.revoke_credentials(
REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids
CRED_DEF_ID,
REV_REG_ID,
tails_file_path="dummy",
cred_rev_ids=test_cred_rev_ids,
)
assert json.loads(result) == TEST_RR_DELTA
assert not failed
Expand Down Expand Up @@ -296,7 +299,10 @@ def mock_revoke(_h, _t, _r, cred_rev_id):
mock_indy_revoke_credential.side_effect = mock_revoke
mock_indy_merge_rr_deltas.return_value = json.dumps(TEST_RR_DELTA)
(result, failed) = await self.issuer.revoke_credentials(
REV_REG_ID, tails_file_path="dummy", cred_rev_ids=test_cred_rev_ids
CRED_DEF_ID,
REV_REG_ID,
tails_file_path="dummy",
cred_rev_ids=test_cred_rev_ids,
)
assert json.loads(result) == TEST_RR_DELTA
assert failed == ["54", "103"]
Expand Down
4 changes: 3 additions & 1 deletion aries_cloudagent/indy/sdk/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import json
import logging

from typing import Tuple

import indy.anoncreds
from indy.error import IndyError

Expand Down Expand Up @@ -34,7 +36,7 @@ async def verify_presentation(
credential_definitions,
rev_reg_defs,
rev_reg_entries,
) -> (bool, list):
) -> Tuple[bool, list]:
"""
Verify a presentation.

Expand Down
4 changes: 2 additions & 2 deletions aries_cloudagent/indy/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from abc import ABC, ABCMeta, abstractmethod
from enum import Enum
from time import time
from typing import Mapping
from typing import Mapping, Tuple

from ..core.profile import Profile
from ..ledger.multiple_ledger.ledger_requests_executor import (
Expand Down Expand Up @@ -397,7 +397,7 @@ def verify_presentation(
credential_definitions,
rev_reg_defs,
rev_reg_entries,
) -> (bool, list):
) -> Tuple[bool, list]:
"""
Verify a presentation.

Expand Down