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

Basic support for credential revocation and revocation registry handling #306

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
3f25d3f
initial merge of revocation registry handling
andrewwhitehead Dec 12, 2019
c9cc180
allow auto-remove of credential exchange records to be disabled; trac…
andrewwhitehead Dec 13, 2019
912ab7f
unit test fixes
andrewwhitehead Dec 13, 2019
996cd0b
Added admin API for publishing the current revocation registry to the…
jonathandolan Jan 22, 2020
7502c83
Updates based on PR comments and chats. Moved the publishing of a giv…
jonathandolan Jan 29, 2020
6dd5ceb
fix test issues after rebase
andrewwhitehead Jan 31, 2020
a43fa32
reformat to fix long line warnings
andrewwhitehead Jan 31, 2020
f421d72
Add support for the pydevd-pycharm debugger to aca-py and faber-alice…
pengyuc Feb 5, 2020
d3b9ae5
Remove the extra import
pengyuc Feb 11, 2020
a760867
Publish revocation registry with faber-alice demo change to use them
pengyuc Feb 11, 2020
a41861f
Change all revocation registry end-points to use revocation registry id.
pengyuc Feb 12, 2020
485609c
Store credential with revocation registry info
pengyuc Feb 14, 2020
b45f3a5
Holders to download tail file when storing credentials
pengyuc Feb 17, 2020
849df1d
Create presentation
pengyuc Feb 20, 2020
730c80f
Make consistent use of tails_* instead of tail_
pengyuc Feb 21, 2020
cdec2de
Create proof with correct timestamp and have the verifier verify a no…
pengyuc Feb 21, 2020
19963d3
Revoke credentials and tested that the verifier would detect the proo…
pengyuc Feb 21, 2020
2febb42
Fix style complaints with black
pengyuc Feb 24, 2020
281457c
Fix existing unit tests
pengyuc Feb 21, 2020
f136637
Fix existing indy unit tests
pengyuc Feb 24, 2020
aac849d
Fix existing indy unit tests style
pengyuc Feb 24, 2020
a7479c4
Update and clean up the performance demo.
pengyuc Feb 24, 2020
4baf48f
Catch revocation registry full error and throw a IssuerError exception
pengyuc Feb 25, 2020
15fc42a
Fixed agent admin response when revocation registry is full.
pengyuc Feb 25, 2020
edfca0e
Changed the faber-alice demo to better support revocation demo:
pengyuc Feb 25, 2020
8dd4fc0
Add document on how to do credential revocation.
pengyuc Feb 26, 2020
ff07c71
fix typo
andrewwhitehead Feb 28, 2020
3be1d9b
tail -> tails and remove print statements
andrewwhitehead Feb 28, 2020
75fd1be
address method signature mismatches
andrewwhitehead Mar 4, 2020
4522cee
Merge branch 'master' into feature/rev-reg
andrewwhitehead Mar 5, 2020
3b95740
minor cleanups
andrewwhitehead Mar 5, 2020
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,4 @@ $RECYCLE.BIN/

# Docs build
_build/
**/*.iml
5 changes: 3 additions & 2 deletions aries_cloudagent/config/default_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ async def bind_providers(self, context: InjectionContext):
StatsProvider(
LedgerProvider(),
(
"create_and_send_credential_definition",
"create_and_send_schema",
"get_credential_definition",
"get_schema",
"send_credential_definition",
"send_schema",
),
)
),
Expand Down Expand Up @@ -154,6 +154,7 @@ async def load_plugins(self, context: InjectionContext):
"aries_cloudagent.messaging.credential_definitions"
)
plugin_registry.register_plugin("aries_cloudagent.messaging.schemas")
plugin_registry.register_plugin("aries_cloudagent.revocation")
plugin_registry.register_plugin("aries_cloudagent.wallet")

# Register external plugins
Expand Down
103 changes: 101 additions & 2 deletions aries_cloudagent/holder/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
"""Base holder class."""

from abc import ABC
from abc import ABC, ABCMeta, abstractmethod
from typing import Union


class BaseHolder(ABC):
class BaseHolder(ABC, metaclass=ABCMeta):
"""Base class for holder."""

def __repr__(self) -> str:
Expand All @@ -15,3 +16,101 @@ def __repr__(self) -> str:

"""
return "<{}>".format(self.__class__.__name__)

@abstractmethod
async def get_credential(self, credential_id: str):
"""
Get a credential stored in the wallet.

Args:
credential_id: Credential id to retrieve

"""

@abstractmethod
async def delete_credential(self, credential_id: str):
"""
Remove a credential stored in the wallet.

Args:
credential_id: Credential id to remove

"""

@abstractmethod
async def get_mime_type(
self, credential_id: str, attr: str = None
) -> Union[dict, str]:
"""
Get MIME type per attribute (or for all attributes).

Args:
credential_id: credential id
attr: attribute of interest or omit for all

Returns: Attribute MIME type or dict mapping attribute names to MIME types
attr_meta_json = all_meta.tags.get(attr)

"""

@abstractmethod
async def create_presentation(
self,
presentation_request: dict,
requested_credentials: dict,
schemas: dict,
credential_definitions: dict,
rev_states_json: dict = None,
):
"""
Get credentials stored in the wallet.

Args:
presentation_request: Valid indy format presentation request
requested_credentials: Indy format requested_credentials
schemas: Indy formatted schemas_json
credential_definitions: Indy formatted schemas_json
rev_states_json: Indy format revocation states
"""

@abstractmethod
async def create_credential_request(
self, credential_offer, credential_definition, holder_did: str
):
"""
Create a credential offer for the given credential definition id.

Args:
credential_offer: The credential offer to create request for
credential_definition: The credential definition to create an offer for
holder_did: the DID of the agent making the request

Returns:
A credential request

"""

@abstractmethod
async def store_credential(
self,
credential_definition,
credential_data,
credential_request_metadata,
credential_attr_mime_types=None,
credential_id=None,
rev_reg_def_json=None,
):
"""
Store a credential in the wallet.

Args:
credential_definition: Credential definition for this credential
credential_data: Credential data generated by the issuer
credential_request_metadata: credential request metadata generated
by the issuer
credential_attr_mime_types: dict mapping attribute names to (optional)
MIME types to store as non-secret record, if specified
credential_id: optionally override the stored credential id
rev_reg_def_json: optional revocation registry definition in json

"""
42 changes: 22 additions & 20 deletions aries_cloudagent/holder/indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ def __init__(self, wallet):
self.wallet = wallet

async def create_credential_request(
self, credential_offer, credential_definition, did
self, credential_offer, credential_definition, holder_did: str
):
"""
Create a credential offer for the given credential definition id.

Args:
credential_offer: The credential offer to create request for
credential_definition: The credential definition to create an offer for
holder_did: the DID of the agent making the request

Returns:
A credential request
Expand All @@ -54,7 +55,7 @@ async def create_credential_request(
credential_request_metadata_json,
) = await indy.anoncreds.prover_create_credential_req(
self.wallet.handle,
did,
holder_did,
json.dumps(credential_offer),
json.dumps(credential_definition),
self.wallet.master_secret_id,
Expand All @@ -77,7 +78,8 @@ async def store_credential(
credential_data,
credential_request_metadata,
credential_attr_mime_types=None,
credential_id=None
credential_id=None,
rev_reg_def_json=None,
):
"""
Store a credential in the wallet.
Expand All @@ -89,15 +91,17 @@ async def store_credential(
by the issuer
credential_attr_mime_types: dict mapping attribute names to (optional)
MIME types to store as non-secret record, if specified
credential_id: optionally override the stored credential id
rev_reg_def_json: revocation registry definition in json

"""
credential_id = await indy.anoncreds.prover_store_credential(
self.wallet.handle,
credential_id,
json.dumps(credential_request_metadata),
json.dumps(credential_data),
json.dumps(credential_definition),
None, # We don't support revocation yet
wallet_handle=self.wallet.handle,
cred_id=credential_id,
cred_req_metadata_json=json.dumps(credential_request_metadata),
cred_json=json.dumps(credential_data),
cred_def_json=json.dumps(credential_definition),
rev_reg_def_json=json.dumps(rev_reg_def_json) if rev_reg_def_json else None,
)

if credential_attr_mime_types:
Expand All @@ -111,7 +115,7 @@ async def store_credential(
type=IndyHolder.RECORD_TYPE_MIME_TYPES,
value=credential_id,
tags=mime_types,
id=f"{IndyHolder.RECORD_TYPE_MIME_TYPES}::{credential_id}"
id=f"{IndyHolder.RECORD_TYPE_MIME_TYPES}::{credential_id}",
)
indy_stor = IndyStorage(self.wallet)
await indy_stor.add_record(record)
Expand All @@ -129,8 +133,7 @@ async def get_credentials(self, start: int, count: int, wql: dict):

"""
search_handle, record_count = await indy.anoncreds.prover_search_credentials(
self.wallet.handle,
json.dumps(wql)
self.wallet.handle, json.dumps(wql)
)

# We need to move the database cursor position manually...
Expand All @@ -139,8 +142,7 @@ async def get_credentials(self, start: int, count: int, wql: dict):
await indy.anoncreds.prover_fetch_credentials(search_handle, start)

credentials_json = await indy.anoncreds.prover_fetch_credentials(
search_handle,
count
search_handle, count
)
await indy.anoncreds.prover_close_credentials_search(search_handle)

Expand Down Expand Up @@ -248,7 +250,7 @@ async def delete_credential(self, credential_id: str):
indy_stor = IndyStorage(self.wallet)
mime_types_record = await indy_stor.get_record(
IndyHolder.RECORD_TYPE_MIME_TYPES,
f"{IndyHolder.RECORD_TYPE_MIME_TYPES}::{credential_id}"
f"{IndyHolder.RECORD_TYPE_MIME_TYPES}::{credential_id}",
)
await indy_stor.delete_record(mime_types_record)
except StorageNotFoundError:
Expand All @@ -267,9 +269,7 @@ async def delete_credential(self, credential_id: str):
raise

async def get_mime_type(
self,
credential_id: str,
attr: str = None
self, credential_id: str, attr: str = None
) -> Union[dict, str]:
"""
Get MIME type per attribute (or for all attributes).
Expand All @@ -285,7 +285,7 @@ async def get_mime_type(
try:
mime_types_record = await IndyStorage(self.wallet).get_record(
IndyHolder.RECORD_TYPE_MIME_TYPES,
f"{IndyHolder.RECORD_TYPE_MIME_TYPES}::{credential_id}"
f"{IndyHolder.RECORD_TYPE_MIME_TYPES}::{credential_id}",
)
except StorageError:
return None # no MIME types: not an error
Expand All @@ -298,6 +298,7 @@ async def create_presentation(
requested_credentials: dict,
schemas: dict,
credential_definitions: dict,
rev_states_json: dict = None,
):
"""
Get credentials stored in the wallet.
Expand All @@ -307,6 +308,7 @@ async def create_presentation(
requested_credentials: Indy format requested_credentials
schemas: Indy formatted schemas_json
credential_definitions: Indy formatted schemas_json
rev_states_json: Indy format revocation states

"""

Expand All @@ -317,7 +319,7 @@ async def create_presentation(
self.wallet.master_secret_id,
json.dumps(schemas),
json.dumps(credential_definitions),
json.dumps({}) # We don't support revocation currently.
json.dumps(rev_states_json) if rev_states_json else "{}",
)

presentation = json.loads(presentation_json)
Expand Down
12 changes: 6 additions & 6 deletions aries_cloudagent/holder/tests/test_indy.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ async def test_store_credential(self, mock_store_cred):
)

mock_store_cred.assert_called_once_with(
mock_wallet.handle,
None,
json.dumps("credential_request_metadata"),
json.dumps("credential_data"),
json.dumps("credential_definition"),
None,
wallet_handle=mock_wallet.handle,
cred_id=None,
cred_req_metadata_json=json.dumps("credential_request_metadata"),
cred_json=json.dumps("credential_data"),
cred_def_json=json.dumps("credential_definition"),
rev_reg_def_json=None
)

assert cred_id == "cred_id"
Expand Down
58 changes: 56 additions & 2 deletions aries_cloudagent/issuer/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Ledger issuer class."""

from abc import ABC
from abc import ABC, ABCMeta, abstractmethod


class BaseIssuer(ABC):
class BaseIssuer(ABC, metaclass=ABCMeta):
"""Base class for issuer."""

def __repr__(self) -> str:
Expand All @@ -15,3 +15,57 @@ def __repr__(self) -> str:

"""
return "<{}>".format(self.__class__.__name__)

@abstractmethod
def create_credential_offer(self, credential_definition_id):
"""
Create a credential offer for the given credential definition id.

Args:
credential_definition_id: The credential definition to create an offer for

Returns:
A credential offer

"""
pass

@abstractmethod
async def create_credential(
self,
schema,
credential_offer,
credential_request,
credential_values,
revoc_reg_id: str = None,
tails_reader_handle: int = None,
):
"""
Create a credential.

Args
schema: Schema to create credential for
credential_offer: Credential Offer to create credential for
credential_request: Credential request to create credential for
credential_values: Values to go in credential
revoc_reg_id: ID of the revocation registry
tails_reader_handle: Handle for the tails file blob reader

Returns:
A tuple of created credential, revocation id

"""
pass

@abstractmethod
def revoke_credential(self, revoc_reg_id, tails_reader_handle, cred_revoc_id):
"""
Revoke a credential.

Args
revoc_reg_id: ID of the revocation registry
tails_reader_handle: handle for the registry tails file
cred_revoc_id: index of the credential in the revocation registry

"""
pass
Loading