11import logging
22from typing import cast
33
4+ from hiero_sdk_python import PublicKey
5+
6+ from ..utils .encoding import b58_to_bytes , b64_to_bytes
47from ..utils .ipfs import download_ipfs_document_by_cid
58from ..utils .serializable import Serializable
69from .did_document_operation import DidDocumentOperation
1518from .hcs .events .verification_relationship .hcs_did_update_verification_relationship_event import (
1619 HcsDidUpdateVerificationRelationshipEvent ,
1720)
18- from .hcs .hcs_did_message import HcsDidMessage
21+ from .hcs .hcs_did_message import HcsDidMessage , HcsDidMessageEnvelope
1922
2023LOGGER = logging .getLogger (__name__ )
2124
@@ -56,21 +59,36 @@ def __init__(self, id_: str):
5659 DidDocumentJsonProperties .CAPABILITY_DELEGATION .value : [],
5760 }
5861
59- async def process_messages (self , messages : list [HcsDidMessage ]):
62+ self ._public_key : PublicKey | None = None
63+
64+ async def process_messages (self , envelopes : list [HcsDidMessageEnvelope ]):
6065 """
6166 Process HCS DID messages - apply DID document state changes according to events.
6267
6368 Args:
64- messages : HCS DID messages to process
69+ envelopes : HCS DID message envelopes (message + signature) to process
6570
6671 """
67- for message in messages :
68- if not self .controller and message .operation == DidDocumentOperation .CREATE :
72+ for envelope in envelopes :
73+ message = cast (HcsDidMessage , envelope .message )
74+
75+ if not self .controller :
6976 event_target = message .event .event_target
7077 if event_target != HcsDidEventTarget .DID_OWNER and event_target != HcsDidEventTarget .DID_DOCUMENT :
71- LOGGER .warning ("DID document is not registered, skipping DID update event..." )
78+ LOGGER .warning ("DID document is not registered, skipping DID event..." )
7279 continue
7380
81+ # TODO: Find a good way to support CID-based DID Document creation without workarounds and redundancy
82+ # It's possible that we want to drop support for this case instead
83+ is_signature_valid = (
84+ message .event .event_target == HcsDidEventTarget .DID_DOCUMENT
85+ or self ._is_message_signature_valid (message , cast (str , envelope .signature ))
86+ )
87+
88+ if not is_signature_valid :
89+ LOGGER .warning ("HCS DID message signature is invalid, skipping event..." )
90+ continue
91+
7492 match message .operation :
7593 case DidDocumentOperation .CREATE :
7694 await self ._process_create_message (message )
@@ -154,6 +172,14 @@ async def _process_create_message(self, message: HcsDidMessage): # noqa: C901
154172 for verificationMethod in document .get (DidDocumentJsonProperties .VERIFICATION_METHOD , [])
155173 }
156174
175+ root_verification_method = next (
176+ filter (
177+ lambda verification_method : "#did-root-key" in verification_method ["id" ],
178+ self .verification_methods .values (),
179+ )
180+ )
181+ self ._public_key = PublicKey .from_bytes (b58_to_bytes (root_verification_method ["publicKeyBase58" ]))
182+
157183 self .verification_relationships [DidDocumentJsonProperties .ASSERTION_METHOD ] = document .get (
158184 DidDocumentJsonProperties .ASSERTION_METHOD , []
159185 )
@@ -174,7 +200,10 @@ async def _process_create_message(self, message: HcsDidMessage): # noqa: C901
174200 LOGGER .warning (f"DID owner is already registered: { self .controller } , skipping event..." )
175201 return
176202
177- self .controller = cast (HcsDidUpdateDidOwnerEvent , event ).get_owner_def ()
203+ did_owner_event = cast (HcsDidUpdateDidOwnerEvent , event )
204+
205+ self .controller = did_owner_event .get_owner_def ()
206+ self ._public_key = did_owner_event .public_key
178207 self ._on_activated (message .timestamp )
179208 case HcsDidEventTarget .SERVICE :
180209 update_service_event = cast (HcsDidUpdateServiceEvent , event )
@@ -228,7 +257,10 @@ def _process_update_message(self, message: HcsDidMessage):
228257
229258 match event .event_target :
230259 case HcsDidEventTarget .DID_OWNER :
231- self .controller = cast (HcsDidUpdateDidOwnerEvent , event ).get_owner_def ()
260+ did_owner_event = cast (HcsDidUpdateDidOwnerEvent , event )
261+
262+ self .controller = did_owner_event .get_owner_def ()
263+ self ._public_key = did_owner_event .public_key
232264 self ._on_updated (message .timestamp )
233265 case HcsDidEventTarget .SERVICE :
234266 update_service_event = cast (HcsDidUpdateServiceEvent , event )
@@ -354,6 +386,32 @@ def _process_delete_message(self, message: HcsDidMessage):
354386 case _:
355387 LOGGER .warning (f"Delete { event .event_target } operation is not supported, skipping event..." )
356388
389+ def _is_message_signature_valid (self , message : HcsDidMessage , signature : str ) -> bool :
390+ is_create_or_update_event = (
391+ message .operation == DidDocumentOperation .CREATE or message .operation == DidDocumentOperation .UPDATE
392+ )
393+ is_did_owner_change_event = (
394+ is_create_or_update_event and message .event .event_target == HcsDidEventTarget .DID_OWNER
395+ )
396+
397+ public_key = (
398+ cast (HcsDidUpdateDidOwnerEvent , message .event ).public_key if is_did_owner_change_event else self ._public_key
399+ )
400+
401+ if not public_key :
402+ raise Exception ("Cannot verify HCS DID Message signature - controller public key is not defined" )
403+
404+ message_bytes = message .to_json ().encode ()
405+ signature_bytes = b64_to_bytes (signature )
406+
407+ try :
408+ public_key .verify (signature_bytes , message_bytes )
409+ except Exception as error :
410+ LOGGER .warning (f"HCS DID Message signature verification failed with error: { error !s} " )
411+ return False
412+
413+ return True
414+
357415 def _on_activated (self , timestamp : float ):
358416 self .created = timestamp
359417 self .updated = timestamp
0 commit comments