Skip to content

Commit e33eaf1

Browse files
Support ADA RFC 003 Data Agreement Negotiation protocol in Aries RFC 037 Present Proof protocol
Present proof protocol messages are patched to support Data Agreement Context Decorator (ADA RFC 006) to carry Data Agreement Negotiation messages. Signed-off-by: George J Padayatti <[email protected]>
1 parent 52aa711 commit e33eaf1

File tree

11 files changed

+1049
-48
lines changed

11 files changed

+1049
-48
lines changed

mydata_did/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66
from .v1_0.message_types import MESSAGE_TYPES
77
from .definition import versions
88
from .patched_protocols.issue_credential.v1_0.message_types import MESSAGE_TYPES as ISSUE_CREDENTIAL_MESSAGE_TYPES
9+
from .patched_protocols.present_proof.v1_0.message_types import MESSAGE_TYPES as PRESENT_PROOF_MESSAGE_TYPES
910

1011
async def setup(context: InjectionContext):
1112
# Register patched message types.
1213
protocol_registry: ProtocolRegistry = await context.inject(ProtocolRegistry)
1314
protocol_registry.register_message_types(MESSAGE_TYPES, version_definition=versions[0])
1415
protocol_registry.register_message_types(ISSUE_CREDENTIAL_MESSAGE_TYPES, version_definition=versions[0])
16+
protocol_registry.register_message_types(PRESENT_PROOF_MESSAGE_TYPES, version_definition=versions[0])
1517

1618
# Register patched protocol plugins
1719
plugin_registry: PluginRegistry = await context.inject(PluginRegistry)
1820
plugin_registry.register_plugin("mydata_did.patched_protocols.issue_credential.v1_0")
21+
plugin_registry.register_plugin("mydata_did.patched_protocols.present_proof.v1_0")
1922

2023
# Unregister superseded protocols
21-
plugin_registry._plugins.pop("aries_cloudagent.protocols.issue_credential")
24+
plugin_registry._plugins.pop("aries_cloudagent.protocols.issue_credential")
25+
plugin_registry._plugins.pop("aries_cloudagent.protocols.present_proof")

mydata_did/patched_protocols/issue_credential/v1_0/routes.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
from aries_cloudagent.protocols.problem_report.v1_0 import internal_error
3838
from aries_cloudagent.protocols.problem_report.v1_0.message import ProblemReport
3939

40+
from mydata_did.v1_0.messages.problem_report import DataAgreementNegotiationProblemReportReason
41+
4042
from .manager import CredentialManager, CredentialManagerError
4143
from .message_types import SPEC_URI
4244
from .messages.credential_proposal import CredentialProposal
@@ -262,7 +264,7 @@ class V10CredentialOfferRequestSchema(AdminAPIMessageTracingSchema):
262264

263265
# Data agreement identifier
264266
data_agreement_id = fields.Str(
265-
description="Data agreement identifier", required=False
267+
description="Data agreement identifier", required=False, example=UUIDFour.EXAMPLE
266268
)
267269

268270

@@ -378,6 +380,12 @@ class DataAgreementBoundCredentialOfferMatchInfoSchema(OpenAPISchema):
378380
example=UUIDFour.EXAMPLE
379381
)
380382

383+
class SendDataAgreementNegotiationProblemReportRequestSchema(OpenAPISchema):
384+
"""Request schema for sending problem report."""
385+
386+
explain = fields.Str(description="Describe the problem", required=True, example="Data agreement context decorator not found in the didcomm message.")
387+
problem_code = fields.Str(description="Problem code", required=True, example=DataAgreementNegotiationProblemReportReason.DATA_AGREEMENT_CONTEXT_INVALID.value)
388+
381389

382390
@docs(tags=["issue-credential"], summary="Fetch all credential exchange records")
383391
@querystring_schema(V10CredentialExchangeListQueryStringSchema)
@@ -1536,6 +1544,7 @@ async def send_data_agreement_reject_message_for_credential_offer(request: web.B
15361544
tags=["issue-credential"], summary="Send data agreement negotiation problem report message"
15371545
)
15381546
@match_info_schema(CredExIdMatchInfoSchema())
1547+
@request_schema(SendDataAgreementNegotiationProblemReportRequestSchema())
15391548
async def send_data_agreement_negotiation_problem_report(request: web.BaseRequest):
15401549
"""
15411550
Request handler for sending data agreement negotiation problem report message
@@ -1553,6 +1562,11 @@ async def send_data_agreement_negotiation_problem_report(request: web.BaseReques
15531562
# Path parameters
15541563
credential_exchange_id = request.match_info["cred_ex_id"]
15551564

1565+
# Request payload
1566+
body = await request.json()
1567+
explain = body.get("explain", "")
1568+
problem_code = body.get("problem_code", "")
1569+
15561570
cred_ex_record = None
15571571
try:
15581572
# Fetch credential exchange record
@@ -1584,8 +1598,8 @@ async def send_data_agreement_negotiation_problem_report(request: web.BaseReques
15841598
data_agreement_negotiation_problem_report = await ada_manager.construct_data_agreement_negotiation_problem_report_message(
15851599
connection_record=connection_record,
15861600
data_agreement_id=cred_ex_record.data_agreement_id,
1587-
problem_code=None,
1588-
explain="Problem report message",
1601+
problem_code=problem_code,
1602+
explain=explain,
15891603
)
15901604

15911605
await ada_manager.send_data_agreement_negotiation_problem_report_message(
@@ -1599,9 +1613,6 @@ async def send_data_agreement_negotiation_problem_report(request: web.BaseReques
15991613
return web.json_response({})
16001614

16011615

1602-
pass
1603-
1604-
16051616
async def register(app: web.Application):
16061617
"""Register routes."""
16071618

mydata_did/patched_protocols/present_proof/v1_0/handlers/presentation_handler.py

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,34 @@
11
"""Presentation message handler."""
22

3+
import json
4+
35
from aries_cloudagent.messaging.base_handler import (
46
BaseHandler,
57
BaseResponder,
68
RequestContext,
9+
HandlerException
710
)
811

12+
from aries_cloudagent.wallet.base import BaseWallet
13+
from aries_cloudagent.wallet.indy import IndyWallet
14+
15+
from mydata_did.patched_protocols.present_proof.v1_0.models.presentation_exchange import V10PresentationExchange
16+
917
from ..manager import PresentationManager
1018
from ..messages.presentation import Presentation
1119

1220
from aries_cloudagent.utils.tracing import trace_event, get_timer
1321

1422

23+
from .....v1_0.manager import ADAManager, ADAManagerError
24+
from .....v1_0.messages.data_agreement_accept import DataAgreementNegotiationAcceptMessage
25+
from .....v1_0.messages.problem_report import DataAgreementNegotiationProblemReportReason
26+
from .....v1_0.models.data_agreement_negotiation_offer_model import DataAgreementNegotiationOfferBody, DataAgreementNegotiationOfferBodySchema
27+
from .....v1_0.models.data_agreement_instance_model import DataAgreementInstance
28+
from .....v1_0.utils.did.mydata_did import DIDMyData
29+
from .....v1_0.utils.jsonld.data_agreement import verify_data_agreement_with_proof_chain
30+
31+
1532
class PresentationHandler(BaseHandler):
1633
"""Message handler class for presentations."""
1734

@@ -35,7 +52,165 @@ async def handle(self, context: RequestContext, responder: BaseResponder):
3552

3653
presentation_manager = PresentationManager(context)
3754

38-
presentation_exchange_record = await presentation_manager.receive_presentation()
55+
presentation_exchange_record : V10PresentationExchange = await presentation_manager.receive_presentation()
56+
57+
# Wallet instance from request context
58+
wallet: IndyWallet = await context.inject(BaseWallet)
59+
60+
# Initialise ADA manager
61+
ada_manager = ADAManager(context)
62+
63+
# Process data agreement context decorator
64+
data_agreement_context_message = None
65+
try:
66+
67+
if presentation_exchange_record.data_agreement and presentation_exchange_record.data_agreement_status == V10PresentationExchange.DATA_AGREEMENT_OFFER:
68+
# If data agreement is present and it is in offer state, then check if the request message contains the
69+
# data agreement context decorator. If it does, then process it.
70+
71+
data_agreement_context_message: DataAgreementNegotiationAcceptMessage = await ada_manager.process_data_agreement_context_decorator(
72+
decorator_set=context.message._decorators
73+
)
74+
75+
if not data_agreement_context_message:
76+
77+
# Send problem report
78+
problem_report = await ada_manager.construct_data_agreement_negotiation_problem_report_message(
79+
connection_record=context.connection_record,
80+
data_agreement_id=presentation_exchange_record.data_agreement_id,
81+
problem_code=DataAgreementNegotiationProblemReportReason.DATA_AGREEMENT_CONTEXT_INVALID.value,
82+
explain="Data agreement context decorator not found in presentation message"
83+
)
84+
85+
await ada_manager.send_data_agreement_negotiation_problem_report_message(
86+
connection_record=context.connection_record,
87+
data_agreement_negotiation_problem_report_message=problem_report
88+
)
89+
90+
presentation_exchange_record.data_agreement_status = V10PresentationExchange.DATA_AGREEMENT_PROBLEM_REPORT
91+
presentation_exchange_record.data_agreement_problem_report = problem_report.serialize()
92+
await presentation_exchange_record.save(context)
93+
94+
95+
raise HandlerException(
96+
"Data agreement context decorator not found in presentation message")
97+
98+
99+
if isinstance(data_agreement_context_message, DataAgreementNegotiationAcceptMessage):
100+
101+
# Deserialise data agreement
102+
data_agreement_offer: DataAgreementNegotiationOfferBody = DataAgreementNegotiationOfferBodySchema().load(
103+
presentation_exchange_record.data_agreement
104+
)
105+
106+
# Construct data agreement with proof chain
107+
data_agreement_with_proof_chain = DataAgreementInstance(
108+
context=data_agreement_offer.context,
109+
data_agreement_id=data_agreement_offer.data_agreement_id,
110+
data_agreement_version=data_agreement_offer.data_agreement_version,
111+
data_agreement_template_id=data_agreement_offer.data_agreement_template_id,
112+
data_agreement_template_version=data_agreement_offer.data_agreement_template_version,
113+
pii_controller_name=data_agreement_offer.pii_controller_name,
114+
pii_controller_url=data_agreement_offer.pii_controller_url,
115+
usage_purpose=data_agreement_offer.usage_purpose,
116+
usage_purpose_description=data_agreement_offer.usage_purpose_description,
117+
legal_basis=data_agreement_offer.legal_basis,
118+
method_of_use=data_agreement_offer.method_of_use,
119+
data_policy=data_agreement_offer.data_policy,
120+
personal_data=data_agreement_offer.personal_data,
121+
dpia=data_agreement_offer.dpia,
122+
event=[
123+
data_agreement_offer.event[0],
124+
data_agreement_context_message.body.event
125+
],
126+
proof_chain=[
127+
data_agreement_offer.proof,
128+
data_agreement_context_message.body.proof
129+
],
130+
principle_did=data_agreement_offer.principle_did
131+
)
132+
133+
# Principle MyData DID (Data Subject)
134+
principle_did = DIDMyData.from_did(
135+
data_agreement_context_message.body.proof.verification_method)
136+
137+
# Controler MyData DID (Data Controller - Organisation)
138+
controller_did = DIDMyData.from_did(
139+
data_agreement_offer.proof.verification_method)
140+
141+
# Verify signatures on data agreement
142+
valid = await verify_data_agreement_with_proof_chain(
143+
data_agreement_with_proof_chain.serialize(),
144+
[
145+
controller_did.public_key_b58,
146+
principle_did.public_key_b58
147+
],
148+
wallet
149+
)
150+
151+
if not valid:
152+
self._logger.error(
153+
"Data agreement accept verification failed"
154+
)
155+
156+
# Send problem report
157+
problem_report = await ada_manager.construct_data_agreement_negotiation_problem_report_message(
158+
connection_record=context.connection_record,
159+
data_agreement_id=presentation_exchange_record.data_agreement_id,
160+
problem_code=DataAgreementNegotiationProblemReportReason.SIGNATURE_VERIFICATION_FAILED.value,
161+
explain="Data agreement accept signature verification failed"
162+
)
163+
164+
await ada_manager.send_data_agreement_negotiation_problem_report_message(
165+
connection_record=context.connection_record,
166+
data_agreement_negotiation_problem_report_message=problem_report
167+
)
168+
169+
presentation_exchange_record.data_agreement_status = V10PresentationExchange.DATA_AGREEMENT_PROBLEM_REPORT
170+
presentation_exchange_record.data_agreement_problem_report = problem_report.serialize()
171+
await presentation_exchange_record.save(context)
172+
173+
raise HandlerException(
174+
"Data agreement accept signature verification failed"
175+
)
176+
177+
# Update credential exchange record with data agreement metadata
178+
presentation_exchange_record.data_agreement = data_agreement_with_proof_chain.serialize()
179+
presentation_exchange_record.data_agreement_status = V10PresentationExchange.DATA_AGREEMENT_ACCEPT
180+
181+
await presentation_exchange_record.save(context)
182+
183+
self._logger.info(
184+
f"Data agreement negotiation accept context message: \n{json.dumps(data_agreement_context_message.serialize(), indent=4)}\n"
185+
)
186+
187+
188+
189+
except ADAManagerError as err:
190+
191+
self._logger.error(
192+
"Failed to process data agreement context decorator: %s", err
193+
)
194+
195+
# Send problem report
196+
problem_report = await ada_manager.construct_data_agreement_negotiation_problem_report_message(
197+
connection_record=context.connection_record,
198+
data_agreement_id=presentation_exchange_record.data_agreement_id,
199+
problem_code=None,
200+
explain=str(err)
201+
)
202+
203+
await ada_manager.send_data_agreement_negotiation_problem_report_message(
204+
connection_record=context.connection_record,
205+
data_agreement_negotiation_problem_report_message=problem_report
206+
)
207+
208+
presentation_exchange_record.data_agreement_status = V10PresentationExchange.DATA_AGREEMENT_PROBLEM_REPORT
209+
presentation_exchange_record.data_agreement_problem_report = problem_report.serialize()
210+
await presentation_exchange_record.save(context)
211+
212+
raise HandlerException(
213+
f"Error processing data agreement context decorator: {err}")
39214

40215
r_time = trace_event(
41216
context.settings,

0 commit comments

Comments
 (0)