Skip to content

Commit c04807a

Browse files
Support ADA RFC 004 Data Agreement Proofs protocol (Audit functions)
Signed-off-by: George J Padayatti <[email protected]>
1 parent c8ac1d8 commit c04807a

9 files changed

+789
-3
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
from aries_cloudagent.messaging.base_handler import BaseHandler, BaseResponder, RequestContext
2+
from aries_cloudagent.storage.record import StorageRecord
3+
from aries_cloudagent.wallet.base import BaseWallet
4+
from aries_cloudagent.wallet.indy import IndyWallet
5+
6+
from ..messages.data_agreement_verify import DataAgreementVerify
7+
from ..messages.data_agreement_verify_response import DataAgreementVerifyResponse
8+
from ..models.exchange_records.data_agreement_record import DataAgreementV1Record
9+
from ..manager import ADAManager, ADAManagerError
10+
from ..utils.did.mydata_did import DIDMyData
11+
from ..utils.jsonld.data_agreement import verify_data_agreement, verify_data_agreement_with_proof_chain
12+
from ..models.exchange_records.auditor_didcomm_transaction_record import AuditorDIDCommTransactionRecord
13+
14+
import json
15+
16+
17+
class DataAgreementVerifyHandler(BaseHandler):
18+
"""
19+
Handler for data agreement verify request.
20+
"""
21+
22+
async def handle(self, context: RequestContext, responder: BaseResponder):
23+
"""
24+
Message handler logic for data agreement verify.
25+
"""
26+
27+
# Assert if received message is of type DataAgreementVerify
28+
assert isinstance(context.message, DataAgreementVerify)
29+
30+
self._logger.info(
31+
"Received data-agreement-proofs/1.0/verify-request message: \n%s",
32+
json.dumps(context.message.serialize(), indent=4)
33+
)
34+
35+
# Check if the connection is ready.
36+
if not context.connection_ready:
37+
self._logger.info(
38+
"Connection not active, skipping data-agreement-proofs/1.0/verify-request handler: %s",
39+
context.message_receipt.sender_did,
40+
)
41+
return
42+
43+
data_agreement_verify_request = context.message
44+
data_agreement = data_agreement_verify_request.body.data_agreement
45+
46+
# Wallet instance from request context
47+
wallet: IndyWallet = await context.inject(BaseWallet)
48+
49+
# Initialize ADA manager
50+
ada_manager = ADAManager(context)
51+
52+
# Create auditor didcomm transaction record
53+
auditor_didcomm_transaction_record = AuditorDIDCommTransactionRecord(
54+
thread_id=data_agreement_verify_request._id,
55+
messages_list=[data_agreement_verify_request.serialize()],
56+
connection_id=context.connection_record.connection_id,
57+
)
58+
59+
await auditor_didcomm_transaction_record.save(context)
60+
61+
try:
62+
63+
data_controller_did = DIDMyData.from_did(
64+
data_agreement.event[0].did)
65+
principle_did = DIDMyData.from_did(data_agreement.principle_did)
66+
67+
# Verify data controller did
68+
# Fetch controller did from did registry
69+
await ada_manager.resolve_remote_mydata_did(mydata_did=data_controller_did.did)
70+
71+
valid = False
72+
73+
if len(data_agreement.event) == 3:
74+
verkeys = []
75+
76+
for event in data_agreement.event:
77+
temp_verkey = DIDMyData.from_did(event.did).public_key_b58
78+
verkeys.append(temp_verkey)
79+
80+
valid = await verify_data_agreement(
81+
data_agreement.serialize(),
82+
verkeys[-1],
83+
wallet,
84+
drop_proof_chain=False
85+
)
86+
87+
# drop last event and proof
88+
data_agreement.event.pop()
89+
data_agreement.proof_chain.pop()
90+
91+
if len(data_agreement.event) == 2:
92+
# Verify signatures on data agreement
93+
valid = await verify_data_agreement_with_proof_chain(
94+
data_agreement.serialize(),
95+
[
96+
data_controller_did.public_key_b58,
97+
principle_did.public_key_b58
98+
],
99+
wallet
100+
)
101+
102+
# drop last event and proof
103+
data_agreement.event.pop()
104+
data_agreement.proof_chain.pop()
105+
106+
# Convert proof chain to single proof
107+
data_agreement.proof = data_agreement.proof_chain[0]
108+
data_agreement.proof_chain = None
109+
110+
if len(data_agreement.event) == 1:
111+
# Verify signatures on data agreement
112+
valid = await verify_data_agreement(
113+
data_agreement.serialize(),
114+
data_controller_did.public_key_b58,
115+
wallet
116+
)
117+
118+
if valid:
119+
120+
# Verification successful
121+
122+
self._logger.info(
123+
"Data-agreement-proofs/1.0/verify-request message verification successful: \n%s",
124+
json.dumps(data_agreement.serialize(), indent=4)
125+
)
126+
127+
data_agreement_verify_response = DataAgreementVerifyResponse(
128+
status="OK",
129+
explain=f"Signature verification successful."
130+
)
131+
132+
data_agreement_verify_response.assign_thread_id(
133+
thid=data_agreement_verify_request._id
134+
)
135+
136+
auditor_didcomm_transaction_record.messages_list.append(
137+
data_agreement_verify_response.serialize()
138+
)
139+
140+
await auditor_didcomm_transaction_record.save(context)
141+
142+
await responder.send_reply(data_agreement_verify_response)
143+
144+
else:
145+
146+
# Verification failed
147+
148+
data_agreement_verify_response = DataAgreementVerifyResponse(
149+
status="NOT OK",
150+
explain=f"Signature verification failed."
151+
)
152+
153+
data_agreement_verify_response.assign_thread_id(
154+
thid=data_agreement_verify_request._id
155+
)
156+
157+
auditor_didcomm_transaction_record.messages_list.append(
158+
data_agreement_verify_response.serialize()
159+
)
160+
161+
await auditor_didcomm_transaction_record.save(context)
162+
163+
await responder.send_reply(data_agreement_verify_response)
164+
165+
except (ADAManagerError, Exception) as err:
166+
self._logger.exception("Failed to verify data agreement")
167+
168+
data_agreement_verify_response = DataAgreementVerifyResponse(
169+
status="NOT OK",
170+
explain=f"Failed to verify data agreement: {err}"
171+
)
172+
173+
data_agreement_verify_response.assign_thread_id(
174+
thid=data_agreement_verify_request._id
175+
)
176+
177+
auditor_didcomm_transaction_record.messages_list.append(
178+
data_agreement_verify_response.serialize()
179+
)
180+
181+
await auditor_didcomm_transaction_record.save(context)
182+
183+
await responder.send_reply(data_agreement_verify_response)
184+
185+
return
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
from aries_cloudagent.messaging.base_handler import BaseHandler, BaseResponder, RequestContext
2+
from aries_cloudagent.storage.record import StorageRecord
3+
from aries_cloudagent.storage.error import StorageNotFoundError, StorageSearchError, StorageDuplicateError, StorageError
4+
from aries_cloudagent.wallet.base import BaseWallet
5+
from aries_cloudagent.wallet.indy import IndyWallet
6+
7+
from ..messages.data_agreement_verify_response import DataAgreementVerifyResponse
8+
from ..models.exchange_records.data_agreement_record import DataAgreementV1Record
9+
from ..manager import ADAManager, ADAManagerError
10+
from ..utils.did.mydata_did import DIDMyData
11+
from ..models.exchange_records.auditor_didcomm_transaction_record import AuditorDIDCommTransactionRecord
12+
13+
import json
14+
15+
16+
class DataAgreementVerifyResponseHandler(BaseHandler):
17+
"""
18+
Handler for data agreement verify response.
19+
"""
20+
21+
async def handle(self, context: RequestContext, responder: BaseResponder):
22+
"""
23+
Message handler logic for data agreement verify response.
24+
"""
25+
26+
# Assert if received message is of type DataAgreementVerifyResponse
27+
assert isinstance(context.message, DataAgreementVerifyResponse)
28+
29+
self._logger.info(
30+
"Received data-agreement-proofs/1.0/verify-response message: \n%s",
31+
json.dumps(context.message.serialize(), indent=4)
32+
)
33+
34+
# Check if the connection is ready.
35+
if not context.connection_ready:
36+
self._logger.info(
37+
"Connection not active, skipping data-agreement-proofs/1.0/verify-response handler: %s",
38+
context.message_receipt.sender_did,
39+
)
40+
return
41+
42+
data_agreement_verify_response = context.message
43+
44+
# Wallet instance from request context
45+
wallet: IndyWallet = await context.inject(BaseWallet)
46+
47+
# Initialize ADA manager
48+
ada_manager = ADAManager(context)
49+
50+
# Retrieve auditor didcomm transaction record
51+
try:
52+
auditor_didcomm_transaction_record: AuditorDIDCommTransactionRecord = await AuditorDIDCommTransactionRecord.retrieve_by_tag_filter(
53+
context,
54+
{
55+
"thread_id": data_agreement_verify_response._thread_id
56+
}
57+
)
58+
59+
# Update the auditor didcomm transaction record
60+
auditor_didcomm_transaction_record.messages_list.append(
61+
data_agreement_verify_response.serialize()
62+
)
63+
64+
await auditor_didcomm_transaction_record.save(context)
65+
66+
except (StorageNotFoundError, StorageDuplicateError) as e:
67+
# No record found
68+
self._logger.debug(
69+
"Failed to process data-agreement-proofs/1.0/verify-response message; "
70+
"No auditor didcomm transaction record found for thread_id: %s", data_agreement_verify_response._thread_id
71+
)
72+
return

mydata_did/v1_0/manager.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from .messages.data_agreement_accept import DataAgreementNegotiationAcceptMessage, DataAgreementNegotiationAcceptMessageSchema
5454
from .messages.data_agreement_reject import DataAgreementNegotiationRejectMessage, DataAgreementNegotiationRejectMessageSchema
5555
from .messages.data_agreement_terminate import DataAgreementTerminationTerminateMessage, DataAgreementTerminationTerminateMessageSchema
56+
from .messages.data_agreement_verify import DataAgreementVerify
5657

5758
from .models.data_agreement_model import DATA_AGREEMENT_V1_SCHEMA_CONTEXT, DataAgreementEventSchema, DataAgreementV1, DataAgreementPersonalData, DataAgreementV1Schema
5859
from .models.read_data_agreement_model import ReadDataAgreementBody
@@ -67,6 +68,7 @@
6768
from .models.data_agreement_negotiation_accept_model import DataAgreementNegotiationAcceptBody, DataAgreementNegotiationAcceptBodySchema
6869
from .models.data_agreement_negotiation_reject_model import DataAgreementNegotiationRejectBody, DataAgreementNegotiationRejectBodySchema
6970
from .models.data_agreement_termination_terminate_model import DataAgreementTerminationTerminateBody, DataAgreementTerminationTerminateBodySchema
71+
from .models.data_agreement_verify_model import DataAgreementVerifyBody, DataAgreementVerifyBodySchema
7072

7173
from .utils.diddoc import DIDDoc
7274
from .utils.did.mydata_did import DIDMyData
@@ -2833,3 +2835,62 @@ async def query_data_agreement_instances(self, tag_query: dict = None) -> typing
28332835
pass
28342836

28352837
return data_agreement_instances
2838+
2839+
async def construct_data_agreement_verify_request(self, *, data_agreement_id: str) -> DataAgreementVerify:
2840+
"""Construct data agreement verify request message"""
2841+
2842+
# Wallet instance from context
2843+
wallet: IndyWallet = await self.context.inject(BaseWallet)
2844+
2845+
# Storage instance from context
2846+
storage: BaseStorage = await self.context.inject(BaseStorage)
2847+
2848+
# Fetch auditor connection record
2849+
auditor_connection_record, err = await self.fetch_auditor_connection_record()
2850+
if err:
2851+
raise ADAManagerError(
2852+
f"Failed to construct data agreement verify request message: {err}"
2853+
)
2854+
2855+
# from_did
2856+
pairwise_local_did_record = await wallet.get_local_did(auditor_connection_record.my_did)
2857+
from_did = DIDMyData.from_public_key_b58(
2858+
pairwise_local_did_record.verkey, key_type=KeyType.ED25519)
2859+
2860+
# to_did
2861+
pairwise_remote_did_record = await storage.search_records(
2862+
type_filter=ConnectionManager.RECORD_TYPE_DID_KEY,
2863+
tag_query={"did": auditor_connection_record.their_did}
2864+
).fetch_single()
2865+
to_did = DIDMyData.from_public_key_b58(
2866+
pairwise_remote_did_record.value, key_type=KeyType.ED25519)
2867+
2868+
# Fetch data agreement instance
2869+
data_agreement_instances = await self.query_data_agreement_instances(
2870+
{
2871+
"data_agreement_id": data_agreement_id
2872+
}
2873+
)
2874+
2875+
if len(data_agreement_instances) != 1:
2876+
raise ADAManagerError(
2877+
f"Failed to construct data agreement verify request message: "
2878+
f"{len(data_agreement_instances)} data agreement instances found for data agreement id: {data_agreement_id}"
2879+
)
2880+
2881+
data_agreement_instance: DataAgreementInstance = DataAgreementInstanceSchema().load(
2882+
data_agreement_instances[0].get("data_agreement")
2883+
)
2884+
2885+
# Construct data agreement verify message
2886+
2887+
data_agreement_verify = DataAgreementVerify(
2888+
from_did=from_did.did,
2889+
to_did=to_did.did,
2890+
created_time=str(int(datetime.datetime.utcnow().timestamp())),
2891+
body=DataAgreementVerifyBody(
2892+
data_agreement=data_agreement_instance
2893+
)
2894+
)
2895+
2896+
return data_agreement_verify

mydata_did/v1_0/message_types.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@
4040
DATA_AGREEMENT_TERMINATION_PROBLEM_REPORT = f"data-agreement-termination/1.0/problem-report"
4141

4242
# Message types for ADA RFC 0004 - Data Agreement Proofs Protocol 1.0
43-
DATA_AGREEMENT_PROOFS_VERIFY = f"data-agreement-proofs/1.0/verify"
44-
DATA_AGREEMENT_PROOFS_VERIFIED = f"data-agreement-proofs/1.0/verified"
45-
DATA_AGREEMENT_PROOFS_UNVERIFIED = f"data-agreement-proofs/1.0/unverified"
43+
DATA_AGREEMENT_PROOFS_VERIFY = f"data-agreement-proofs/1.0/verify-request"
44+
DATA_AGREEMENT_PROOFS_VERIFY_RESPONSE = f"data-agreement-proofs/1.0/verify-response"
4645

4746
# Protocol package path
4847
PROTOCOL_PACKAGE = "mydata_did.v1_0"
@@ -94,6 +93,12 @@
9493
),
9594
DATA_AGREEMENT_TERMINATION_PROBLEM_REPORT: (
9695
f"{PROTOCOL_PACKAGE}.messages.problem_report.DataAgreementTerminationProblemReport"
96+
),
97+
DATA_AGREEMENT_PROOFS_VERIFY: (
98+
f"{PROTOCOL_PACKAGE}.messages.data_agreement_verify.DataAgreementVerify"
99+
),
100+
DATA_AGREEMENT_PROOFS_VERIFY_RESPONSE: (
101+
f"{PROTOCOL_PACKAGE}.messages.data_agreement_verify_response.DataAgreementVerifyResponse"
97102
)
98103
},
99104
)

0 commit comments

Comments
 (0)