diff --git a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py index 60b1fcff9d..4abfedf09d 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/handlers/pres_request_handler.py @@ -1,5 +1,10 @@ """Presentation request message handler.""" +import base64 +import json +from aries_cloudagent.resolver.did_resolver import DIDResolver +from aries_cloudagent.wallet.base import BaseWallet +from aries_cloudagent.wallet.key_type import ED25519 from .....anoncreds.holder import AnonCredsHolderError from .....core.oob_processor import OobMessageProcessor from .....indy.holder import IndyHolderError @@ -64,6 +69,44 @@ async def handle(self, context: RequestContext, responder: BaseResponder): profile = context.profile pres_manager = V20PresManager(profile) + pres_request = context.message + if pres_request.verifier_did is not None: + verifier_did = pres_request.verifier_did + async with profile.session() as session: + did_resolver = session.inject(DIDResolver) + wallet = session.inject(BaseWallet) + did_document = await did_resolver.resolve( + profile=profile, did=verifier_did + ) + verification_method_list = did_document.get("verificationMethod", []) + request_verified = False + for method in verification_method_list: + verkey = method.get("publicKeyBase58") + key_type = ED25519 # need to change this to support other key types + sr_pres_request = pres_request.serialize() + sr_pres_request.pop("~thread", None) + sr_pres_request.pop("signature", None) + sr_pres_request_bytes = json.dumps(sr_pres_request).encode("utf-8") + if verkey: + try: + request_verified = await wallet.verify_message( + sr_pres_request_bytes, + base64.b64decode(pres_request.signature), + verkey, + key_type, + ) + if request_verified: + break + except Exception as e: + print( + f"Could not verify signature...Retrying with next verification method: {e}" # noqa: E501 + ) + continue + if not request_verified: + raise HandlerException( + "Presentation request signature verification failed. DID of verifier is not verifed" # noqa: E501 + ) + # Get pres ex record (holder initiated via proposal) # or create it (verifier sent request first) try: diff --git a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_request.py b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_request.py index 51e2d2dfb4..cf86d0c621 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_request.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/messages/pres_request.py @@ -1,5 +1,6 @@ """A presentation request content message.""" +import base64 from typing import Sequence from marshmallow import EXCLUDE, ValidationError, fields, validates_schema @@ -32,8 +33,10 @@ def __init__( _id: str = None, *, comment: str = None, + verifier_did: str = None, will_confirm: bool = None, formats: Sequence[V20PresFormat] = None, + signature: bytes = None, request_presentations_attach: Sequence[AttachDecorator] = None, **kwargs, ): @@ -42,10 +45,12 @@ def __init__( Args: _id (str, optional): The ID of the presentation request. comment (str, optional): An optional comment. + verifier_did (str, optional): The DID of the verifier. will_confirm (bool, optional): A flag indicating whether the presentation request will be confirmed. formats (Sequence[V20PresFormat], optional): A sequence of presentation formats. + signature (dict, optional): signature object for verifier did. request_presentations_attach (Sequence[AttachDecorator], optional): A sequence of proof request attachments. kwargs: Additional keyword arguments. @@ -53,8 +58,10 @@ def __init__( """ super().__init__(_id=_id, **kwargs) self.comment = comment + self.verifier_did = verifier_did self.will_confirm = will_confirm or False self.formats = list(formats) if formats else [] + self.signature = signature self.request_presentations_attach = ( list(request_presentations_attach) if request_presentations_attach else [] ) @@ -86,6 +93,10 @@ def attachment(self, fmt: V20PresFormat.Format = None) -> dict: else None ) + def add_signature(self, sign: bytes): + """Add signature to request.""" + self.signature = base64.b64encode(sign).decode("utf-8") + class V20PresRequestSchema(AgentMessageSchema): """Presentation request schema.""" @@ -103,12 +114,20 @@ class Meta: required=False, metadata={"description": "Whether verifier will send confirmation ack"}, ) + verifier_did = fields.Str( + required=False, + metadata={"description": "DID of the verifier"}, + ) formats = fields.Nested( V20PresFormatSchema, many=True, required=True, metadata={"description": "Acceptable attachment formats"}, ) + signature = fields.Str( + required=False, + metadata={"description": "signature for verifier did"}, + ) request_presentations_attach = fields.Nested( AttachDecoratorSchema, many=True, diff --git a/aries_cloudagent/protocols/present_proof/v2_0/routes.py b/aries_cloudagent/protocols/present_proof/v2_0/routes.py index b18b6041dc..aa8259812a 100644 --- a/aries_cloudagent/protocols/present_proof/v2_0/routes.py +++ b/aries_cloudagent/protocols/present_proof/v2_0/routes.py @@ -65,6 +65,7 @@ from .messages.pres_proposal import V20PresProposal from .messages.pres_request import V20PresRequest from .models.pres_exchange import V20PresExRecord, V20PresExRecordSchema +from ....wallet.base import BaseWallet class V20PresentProofModuleResponseSchema(OpenAPISchema): @@ -159,9 +160,7 @@ class V20PresProposalRequestSchema(AdminAPIMessageTracingSchema): allow_none=True, metadata={"description": "Human-readable comment"}, ) - presentation_proposal = fields.Nested( - V20PresProposalByFormatSchema(), required=True - ) + presentation_proposal = fields.Nested(V20PresProposalByFormatSchema(), required=True) auto_present = fields.Boolean( required=False, dump_default=False, @@ -409,9 +408,7 @@ def _formats_attach(by_format: Mapping, msg_type: str, spec: str) -> Mapping: attach = [] for fmt_api, item_by_fmt in by_format.items(): if fmt_api == V20PresFormat.Format.INDY.api: - attach.append( - AttachDecorator.data_base64(mapping=item_by_fmt, ident=fmt_api) - ) + attach.append(AttachDecorator.data_base64(mapping=item_by_fmt, ident=fmt_api)) elif fmt_api == V20PresFormat.Format.DIF.api: attach.append(AttachDecorator.data_json(mapping=item_by_fmt, ident=fmt_api)) return { @@ -604,9 +601,7 @@ async def present_proof_credentials_list(request: web.BaseRequest): input_descriptors_list = dif_pres_request.get( "presentation_definition", {} ).get("input_descriptors") - claim_fmt = dif_pres_request.get("presentation_definition", {}).get( - "format" - ) + claim_fmt = dif_pres_request.get("presentation_definition", {}).get("format") if claim_fmt and len(claim_fmt.keys()) > 0: claim_fmt = ClaimFormat.deserialize(claim_fmt) input_descriptors = [] @@ -658,16 +653,13 @@ async def present_proof_credentials_list(request: web.BaseRequest): elif ( len(proof_types) == 1 and ( - BbsBlsSignature2020.signature_type - not in proof_types + BbsBlsSignature2020.signature_type not in proof_types ) and ( - Ed25519Signature2018.signature_type - not in proof_types + Ed25519Signature2018.signature_type not in proof_types ) and ( - Ed25519Signature2020.signature_type - not in proof_types + Ed25519Signature2020.signature_type not in proof_types ) ): raise web.HTTPBadRequest( @@ -681,16 +673,13 @@ async def present_proof_credentials_list(request: web.BaseRequest): elif ( len(proof_types) >= 2 and ( - BbsBlsSignature2020.signature_type - not in proof_types + BbsBlsSignature2020.signature_type not in proof_types ) and ( - Ed25519Signature2018.signature_type - not in proof_types + Ed25519Signature2018.signature_type not in proof_types ) and ( - Ed25519Signature2020.signature_type - not in proof_types + Ed25519Signature2020.signature_type not in proof_types ) ): raise web.HTTPBadRequest( @@ -706,25 +695,18 @@ async def present_proof_credentials_list(request: web.BaseRequest): proof_format == Ed25519Signature2018.signature_type ): - proof_type = [ - Ed25519Signature2018.signature_type - ] + proof_type = [Ed25519Signature2018.signature_type] break elif ( proof_format == Ed25519Signature2020.signature_type ): - proof_type = [ - Ed25519Signature2020.signature_type - ] + proof_type = [Ed25519Signature2020.signature_type] break elif ( - proof_format - == BbsBlsSignature2020.signature_type + proof_format == BbsBlsSignature2020.signature_type ): - proof_type = [ - BbsBlsSignature2020.signature_type - ] + proof_type = [BbsBlsSignature2020.signature_type] break else: raise web.HTTPBadRequest( @@ -919,12 +901,24 @@ async def present_proof_create_request(request: web.BaseRequest): body = await request.json() comment = body.get("comment") + verifier_did = body.get("verifier_did") + verifier_verkey = None + if verifier_did is not None: + async with profile.session() as session: + wallet = session.inject(BaseWallet) + try: + didinfo = await wallet.get_local_did(did=verifier_did) + verifier_verkey = didinfo.verkey + except WalletNotFoundError as err: + raise web.HTTPBadRequest(reason="DID is not present in wallet!") from err + pres_request_spec = body.get("presentation_request") if pres_request_spec and V20PresFormat.Format.INDY.api in pres_request_spec: await _add_nonce(pres_request_spec[V20PresFormat.Format.INDY.api]) pres_request_message = V20PresRequest( comment=comment, + verifier_did=verifier_did, will_confirm=True, **_formats_attach(pres_request_spec, PRES_20_REQUEST, "request_presentations"), ) @@ -938,6 +932,18 @@ async def present_proof_create_request(request: web.BaseRequest): trace_msg, ) + if verifier_verkey is not None: + async with profile.session() as session: + wallet = session.inject(BaseWallet) + sr_pres_request_message = pres_request_message.serialize() + sr_pres_request_message_bytes: bytes = json.dumps( + sr_pres_request_message + ).encode("utf-8") + sign = await wallet.sign_message( + sr_pres_request_message_bytes, verifier_verkey + ) + pres_request_message.add_signature(sign) + pres_manager = V20PresManager(profile) pres_ex_record = None try: @@ -1003,11 +1009,23 @@ async def present_proof_send_free_request(request: web.BaseRequest): raise web.HTTPForbidden(reason=f"Connection {connection_id} not ready") comment = body.get("comment") + verifier_did = body.get("verifier_did") + verifier_verkey = None + if verifier_did is not None: + async with profile.session() as session: + wallet = session.inject(BaseWallet) + try: + didinfo = await wallet.get_local_did(did=verifier_did) + verifier_verkey = didinfo.verkey + except WalletNotFoundError as err: + raise web.HTTPBadRequest(reason="DID is not present in wallet!") from err + pres_request_spec = body.get("presentation_request") if pres_request_spec and V20PresFormat.Format.INDY.api in pres_request_spec: await _add_nonce(pres_request_spec[V20PresFormat.Format.INDY.api]) pres_request_message = V20PresRequest( comment=comment, + verifier_did=verifier_did, will_confirm=True, **_formats_attach(pres_request_spec, PRES_20_REQUEST, "request_presentations"), ) @@ -1021,6 +1039,19 @@ async def present_proof_send_free_request(request: web.BaseRequest): trace_msg, ) + if verifier_verkey is not None: + async with profile.session() as session: + wallet = session.inject(BaseWallet) + sr_pres_request_message = pres_request_message.serialize() + sr_pres_request_message_bytes: bytes = json.dumps( + sr_pres_request_message + ).encode("utf-8") + print(f"==========================>{sr_pres_request_message}") + sign = await wallet.sign_message( + sr_pres_request_message_bytes, verifier_verkey + ) + pres_request_message.add_signature(sign) + pres_manager = V20PresManager(profile) pres_ex_record = None try: @@ -1385,9 +1416,7 @@ async def present_proof_remove(request: web.BaseRequest): try: async with context.profile.session() as session: try: - pres_ex_record = await V20PresExRecord.retrieve_by_id( - session, pres_ex_id - ) + pres_ex_record = await V20PresExRecord.retrieve_by_id(session, pres_ex_id) await pres_ex_record.delete_record(session) except (BaseModelError, ValidationError): storage = session.inject(BaseStorage) diff --git a/plugins b/plugins new file mode 160000 index 0000000000..71ad41e23d --- /dev/null +++ b/plugins @@ -0,0 +1 @@ +Subproject commit 71ad41e23d9a9ef68ca6927135ecf9712f9059ad