async def recieve_invite(self, msg): ''' Recieve invitation from user to create a pending connection. :param msg: code generated from user ''' invite_msg = Serializer.unpack( base64.urlsafe_b64decode(msg).decode('utf-8')) print("\nMESSAGE RECEIVED: ", invite_msg) pending_connection = Message({ '@type': "INVITE_RECEIVED", 'label': invite_msg['label'], 'connection_key': invite_msg['recipientKeys'][0], 'history': [{ 'date': str(datetime.datetime.now()), 'msg': invite_msg.to_dict() }], 'status': "Invite Received" }) print("\nPENDING CONNECTION", pending_connection) # Store invitation in the wallet await non_secrets.add_wallet_record( self.agent.wallet_handle, 'invitations', invite_msg['recipientKeys'][0], Serializer.pack(pending_connection), '{}') return True
async def ui_event_process(agent): ui_router = agent['ui_router'] ui_event_queue = agent['ui_event_queue'] await ui_router.register(UI.SEND_OFFER, connection.send_offer) await ui_router.register(UI.STATE_REQUEST, ui.ui_connect) await ui_router.register(UI.INITIALIZE, init.initialize_agent) await ui_router.register(UI.SEND_OFFER_ACCEPTED, connection.send_offer_accepted) await ui_router.register(UI.SENDER_SEND_OFFER_REJECTED, connection.sender_send_offer_rejected) await ui_router.register(UI.RECEIVER_SEND_OFFER_REJECTED, connection.receiver_send_offer_rejected) await ui_router.register(UI.SEND_CONN_REJECTED, connection.send_conn_rejected) while True: msg_bytes = await ui_event_queue.recv() try: msg = Serializer.unpack(msg_bytes) except Exception as e: print('Failed to unpack message: {}\n\nError: {}'.format(msg_bytes, e)) continue if msg.id != UI_TOKEN: print('Invalid token received, rejecting message: {}'.format(msg_bytes)) continue res = await ui_router.route(msg, agent['agent']) if res is not None: await ui_event_queue.send(Serializer.pack(res))
async def unpack_agent_message(self, wire_msg_bytes): unpacked = json.loads(await crypto.unpack_message(self.wallet_handle, wire_msg_bytes)) from_key = None if 'sender_verkey' in unpacked: from_key = unpacked['sender_verkey'] from_did = await utils.did_for_key(self.wallet_handle, unpacked['sender_verkey']) to_key = unpacked['recipient_verkey'] to_did = await utils.did_for_key(self.wallet_handle, unpacked['recipient_verkey']) msg = Serializer.unpack(unpacked['message']) msg.context = { 'from_did': from_did, # Could be None 'to_did': to_did, # Could be None 'from_key': from_key, # Could be None 'to_key': to_key } return msg
async def ui_event_process(agent): ui_router = agent['ui_router'] ui_event_queue = agent['ui_event_queue'] connection = agent['modules']['connection'] ui = agent['modules']['ui'] ui_router.register(CONN_UI.FAMILY, connection) ui_router.register(UI.FAMILY, ui) ui_router.register(ADMIN_WALLETCONNECTION.FAMILY, agent['modules']['admin_walletconnection']) while True: msg = await ui_event_queue.recv() if not isinstance(msg, Message): try: msg = Serializer.unpack(msg) except Exception as e: print('Failed to unpack message: {}\n\nError: {}'.format( msg, e)) continue if msg['ui_token'] != UI_TOKEN: print('Invalid token received, rejecting message: {}'.format(msg)) continue res = await ui_router.route(msg) if res is not None: await ui_event_queue.send(Serializer.pack(res))
async def start(self): """ Message processing loop task. """ while True: try: wire_msg_bytes = await self.message_queue.get() # Try to unpack message assuming it's not encrypted try: msg = Serializer.unpack(wire_msg_bytes) except Exception as e: print("Message encrypted, attempting to unpack...") # TODO: More graceful checking here # (This is an artifact of the provisional wire format and connection protocol) if not isinstance(msg, Message) or "@type" not in msg: # Message IS encrypted so unpack it try: msg = await self.unpack_agent_message(wire_msg_bytes) except Exception as e: print( 'Failed to unpack message: {}\n\nError: {}'.format( wire_msg_bytes, e)) continue # handle next message in loop await self.route_message_to_module(msg) except Exception as e: print("\n\n--- Message Processing failed --- \n\n") traceback.print_exc()
async def message_process(agent): """ Message processing loop task. Message routes are also defined here through the message router. """ msg_router = agent['msg_router'] msg_receiver = agent['msg_receiver'] ui_event_queue = agent['ui_event_queue'] await msg_router.register(CONN.REQUEST, connection.handle_request) await msg_router.register(CONN.RESPONSE, connection.handle_response) while True: encrypted_msg_bytes = await msg_receiver.recv() # TODO: make this logic work try: decrypted_msg_bytes = await crypto.anon_decrypt( agent.wallet_handle, agent.endpoint_vk, encrypted_msg_bytes ) except Exception as e: print('Could not decrypt message: {}\nError: {}'.format(msg_bytes, e)) continue try: msg = Serializer.unpack(encrypted_msg_bytes) except Exception as e: print('Failed to unpack message: {}\n\nError: {}'.format(msg_bytes, e)) continue res = await msg_router.route(msg, agent['agent']) if res is not None: await ui_event_queue.send(Serializer.pack(res))
async def unpack_agent_message(self, wire_msg_bytes): ''' Message passed is unpacked as bytes and returned ''' print("\nUnpack agent message") if isinstance(wire_msg_bytes, str): wire_msg_bytes = bytes(wire_msg_bytes, 'utf-8') unpacked = json.loads(await crypto.unpack_message(self.wallet_handle, bytes(wire_msg_bytes))) print("UNPACKED: ", unpacked) from_key = None from_did = None if 'sender_verkey' in unpacked: from_key = unpacked['sender_verkey'] from_did = await utils.did_for_key(self.wallet_handle, unpacked['sender_verkey']) to_key = unpacked['recipient_verkey'] to_did = await utils.did_for_key(self.wallet_handle, unpacked['recipient_verkey']) msg = Serializer.unpack(unpacked['message']) print(msg) msg.context = { 'from_did': from_did, # Could be None 'to_did': to_did, # Could be None 'from_key': from_key, # Could be None 'to_key': to_key } print("Message context: ", msg.context) return msg
async def receive_invite(self, msg: Message) -> Message: """ Receive and save invite. This interaction represents an out-of-band communication channel. In the future and in practice, these sort of invitations will be received over any number of channels such as SMS, Email, QR Code, NFC, etc. In this iteration, invite messages are received from the admin interface as a URL after being copied and pasted from another agent instance. The URL is formatted as follows: https://<domain>/<path>?c_i=<invitationstring> The invitation string is a base64 url encoded json string. Structure of an invite message: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation", "label": "Alice", "did": "did:sov:QmWbsNYhMrjHiqZDTUTEJs" } Or, in the case of a peer DID: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation", "label": "Alice", "key": "8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K", "endpoint": "https://example.com/endpoint" } Currently, only peer DID format is supported. """ # Parse invite string matches = re.match("(.+)?c_i=(.+)", msg['invite']) if not matches: raise BadInviteException("Invite string is improperly formatted") invite_msg = Serializer.unpack( base64.urlsafe_b64decode(matches.group(2)).decode('utf-8')) record = uuid.uuid4().hex await self.agent.send_admin_message( Message({ '@type': AdminConnection.INVITE_RECEIVED, 'label': invite_msg['label'], 'key': invite_msg['key'], 'endpoint': invite_msg['endpoint'] })) await non_secrets.add_wallet_record(self.agent.wallet_handle, 'invitation', invite_msg['key'], Serializer.pack(invite_msg), '{}')
async def message_process(agent): """ Message processing loop task. Message routes are also defined here through the message router. """ msg_router = agent['msg_router'] msg_receiver = agent['msg_receiver'] await msg_router.register("CONN_REQ", connection.handle_request_received) await msg_router.register("CONN_RES", connection.handle_response) while True: msg_bytes = await msg_receiver.recv() msg = Serializer.unpack(msg_bytes) await msg_router.route(msg, agent['agent'])
async def read_msg(self, wire_msg): ''' Message is unpacked depending on how its encrypted and returned unpacked ''' print("read wire msg: ", wire_msg) msg = "" try: msg = Serializer.unpack(wire_msg) except Exception as e: print("Message encrypted, trying to unpack", e) if not isinstance(msg, Message) or "@type" not in msg: try: msg = await self.unpack_agent_message(wire_msg) print("msg unpack: ", msg) return msg except Exception as e: print("Failed to unpack messgae", e)
async def conn_process(agent): conn_router = agent['conn_router'] conn_receiver = agent['conn_receiver'] ui_event_queue = agent['ui_event_queue'] connection = agent['modules']['connection'] conn_router.register(CONN.FAMILY, connection) while True: msg_bytes = await conn_receiver.recv() try: msg = Serializer.unpack(msg_bytes) except Exception as e: print('Failed to unpack message: {}\n\nError: {}'.format(msg_bytes, e)) continue res = await conn_router.route(msg) if res is not None: await ui_event_queue.send(Serializer.pack(res))
async def unpack_wire_msg(self, wire_msg) -> Optional: # Try to unpack message assuming it's not encrypted msg = "" try: msg = Serializer.unpack(wire_msg) except Exception as e: print("Message encrypted, attempting to unpack...") # TODO: More graceful checking here # (This is an artifact of the provisional wire format and connection protocol) if not isinstance(msg, Message) or "@type" not in msg: # Message IS encrypted so unpack it try: msg = await self.unpack_agent_message(wire_msg) except Exception as e: print('Failed to unpack message: {}\n\nError: {}'.format( wire_msg, e)) traceback.print_exc() return None return msg
async def conn_process(agent): conn_router = agent['conn_router'] conn_receiver = agent['conn_receiver'] ui_event_queue = agent['ui_event_queue'] await conn_router.register(CONN.OFFER, connection.offer_recv) await conn_router.register(CONN.ACKNOWLEDGE, connection.offer_accepted) await conn_router.register(CONN.SENDER_REJECTION, connection.receiver_offer_rejected) await conn_router.register(CONN.RECEIVER_REJECTION, connection.sender_offer_rejected) await conn_router.register(CONN.REJECTION, connection.conn_rejected) while True: msg_bytes = await conn_receiver.recv() try: msg = Serializer.unpack(msg_bytes) except Exception as e: print('Failed to unpack message: {}\n\nError: {}'.format(msg_bytes, e)) continue res = await conn_router.route(msg, agent['agent']) if res is not None: await ui_event_queue.send(Serializer.pack(res))
async def handle_request_accepted(request): """ Handle reception of accept connection request message. """ accept_did = request.match_info['did'] agent = request.app['agent'] wallet_handle = agent.wallet_handle if accept_did not in agent.received_requests: raise web.HTTPNotFound() msg = Serializer.unpack(agent.received_requests[accept_did]) #TODO: validate correct format for incoming data data = msg.data endpoint = data['endpoint'] verkey = data['verkey'] owner = data['owner'] ident_json = json.dumps({"did": accept_did, "verkey": verkey}) meta_json = json.dumps({"owner": owner, "endpoint": endpoint}) (my_did, _) = await did.create_and_store_my_did(wallet_handle, "{}") await did.store_their_did(wallet_handle, ident_json) await did.set_endpoint_for_did(wallet_handle, accept_did, endpoint, verkey) await did.set_did_metadata(wallet_handle, accept_did, meta_json) await pairwise.create_pairwise(wallet_handle, accept_did, my_did, json.dumps({"hello": "world"})) await send_response(accept_did, agent) raise web.HTTPFound('/')
async def send_response(self, msg): ''' When clicking on send response button, response message is send to other user to create pairwise connection ''' print("\nSEND RESPONSE") their_did = msg['did'] # pairwise connection info pairwise_info = json.loads(await pairwise.get_pairwise( self.agent.wallet_handle, their_did)) pairwise_meta = json.loads(pairwise_info['metadata']) my_did = pairwise_info['my_did'] label = pairwise_meta['label'] my_vk = await did.key_for_local_did(self.agent.wallet_handle, my_did) # response message generated response_msg = Message({ '@type': "SEND_RESPONSE", '@sendTo': label, '~thread': { 'thid': pairwise_meta['req_id'] }, 'connection': { 'DID': my_did, 'DIDDoc': { "@context": "https://w3id.org/did/v1", "id": my_did, "publicKey": [{ "id": my_did + "#keys-1", "type": "Ed25519VerificationKey2018", "controller": my_did, "publicKeyBase58": my_vk }], "service": [{ "id": my_did + ";indy", "type": "IndyAgent", "recipientKeys": [my_vk], }], } } }) print("SEND RESPONSE: ", response_msg) # apply signature to connection field, sign with key used in invitation and request response_msg[ 'connection~sig'] = await self.agent.sign_agent_message_field( response_msg['connection'], pairwise_meta['connection_key']) del response_msg['connection'] pending_connection = Serializer.unpack( json.loads(await non_secrets.get_wallet_record( self.agent.wallet_handle, 'invitations', pairwise_meta['connection_key'], '{}'))['value']) pending_connection['status'] = "Response Sent" pending_connection['@type'] = "RESPONSE SENT" pending_connection['history'].append({ 'date': str(datetime.datetime.now()), 'msg': Message(msg).to_dict() }) # send message to agent await self.agent.send_message_to_agent(their_did, response_msg) # delete invitation from records await non_secrets.delete_wallet_record(self.agent.wallet_handle, 'invitations', pairwise_meta['connection_key'])
async def send_request(self, msg: Message): """ Recall invite message from wallet and prepare and send request to the inviter. send_request message format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/admin_connections/1.0/send_request", "key": <key sent in invite> } Request format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/request", "label": "Bob", "DID": "B.did@B:A", "DIDDoc": { // did Doc here. } } """ invite = Serializer.unpack( json.loads(await non_secrets.get_wallet_record(self.agent.wallet_handle, 'invitation', msg['key'], '{}'))['value']) my_label = self.agent.owner label = invite['label'] their_connection_key = invite['key'] their_endpoint = invite['endpoint'] # Create my information for connection (my_did, my_vk) = await utils.create_and_store_my_did(self.agent.wallet_handle) await did.set_did_metadata( self.agent.wallet_handle, my_did, json.dumps({ 'label': label, 'their_endpoint': their_endpoint })) # Send Connection Request to inviter request = Message({ '@type': Connection.REQUEST, 'label': my_label, 'DID': my_did, 'DIDDoc': { 'key': my_vk, 'endpoint': self.agent.endpoint, } }) await self.agent.send_message_to_endpoint_and_key( my_vk, their_connection_key, their_endpoint, request) await self.agent.send_admin_message( Message({ '@type': AdminConnection.REQUEST_SENT, 'label': label }))
async def send_response(self, msg: Message) -> Message: """ Send response to request. send_response message format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/admin_connections/1.0/send_response", "did": <did of request sender> } Response format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/response", "did":"A.did@A:B", "did_doc": { //did doc } } """ their_did = msg['did'] pairwise_info = json.loads(await pairwise.get_pairwise(self.agent.wallet_handle, their_did)) pairwise_meta = json.loads(pairwise_info['metadata']) my_did = pairwise_info['my_did'] label = pairwise_meta['label'] my_vk = await did.key_for_local_did(self.agent.wallet_handle, my_did) response_msg = Message({ '@type': Connection.RESPONSE, '~thread': { 'thid': pairwise_meta['req_id'] }, 'connection': { 'did': my_did, 'did_doc': { "@context": "https://w3id.org/did/v1", "id": my_did, "publicKey": [{ "id": my_did + "#keys-1", "type": "Ed25519VerificationKey2018", "controller": my_did, "publicKeyBase58": my_vk }], "service": [{ "id": my_did + ";indy", "type": "IndyAgent", "recipientKeys": [my_vk], #"routingKeys": ["<example-agency-verkey>"], "serviceEndpoint": self.agent.endpoint, }], } } }) # Apply signature to connection field, sign it with the key used in the invitation and request response_msg['connection~sig'] = await self.agent.sign_agent_message_field(response_msg['connection'], pairwise_meta["connection_key"]) del response_msg['connection'] pending_connection = Serializer.unpack( json.loads( await non_secrets.get_wallet_record(self.agent.wallet_handle, 'invitations', pairwise_meta['connection_key'], '{}') )['value'] ) pending_connection['status'] = "Response Sent" pending_connection['@type'] = AdminConnection.RESPONSE_SENT pending_connection['history'].append({ 'date': str(datetime.datetime.now()), 'msg': msg.to_dict()}) await self.agent.send_message_to_agent(their_did, response_msg) await self.agent.send_admin_message(pending_connection) await non_secrets.delete_wallet_record(self.agent.wallet_handle, 'invitations', pairwise_meta['connection_key'])
async def response_received(self, msg: Message) -> Message: """ Process response Response format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/response", "did":"A.did@A:B", "did_doc": { //did doc } } """ my_did = msg.context['to_did'] my_vk = await did.key_for_local_did(self.agent.wallet_handle, my_did) #process signed field msg['connection'], sig_verified = await self.agent.unpack_and_verify_signed_agent_message_field(msg['connection~sig']) # connection~sig remains for metadata their_did = msg['connection']['did'] their_vk = msg['connection']['did_doc']['publicKey'][0]['publicKeyBase58'] their_endpoint = msg['connection']['did_doc']['service'][0]['serviceEndpoint'] msg_vk = msg.context['from_key'] # TODO: verify their_vk (from did doc) matches msg_vk # Retrieve connection information from DID metadata my_did_meta = json.loads( await did.get_did_metadata(self.agent.wallet_handle, my_did) ) label = my_did_meta['label'] # Clear DID metadata. This info will be stored in pairwise meta. await did.set_did_metadata(self.agent.wallet_handle, my_did, '') # In the final implementation, a signature will be provided to verify changes to # the keys and DIDs to be used long term in the relationship. # Both the signature and signature check are omitted for now until specifics of the # signature are decided. # Store their information from response await utils.store_their_did(self.agent.wallet_handle, their_did, their_vk) await did.set_did_metadata( self.agent.wallet_handle, their_did, json.dumps({ 'label': label, 'endpoint': their_endpoint }) ) # Create pairwise relationship between my did and their did await pairwise.create_pairwise( self.agent.wallet_handle, their_did, my_did, json.dumps({ 'label': label, 'their_endpoint': their_endpoint, 'their_vk': their_vk, 'my_vk': my_vk }) ) pending_connection = Serializer.unpack( json.loads( await non_secrets.get_wallet_record(self.agent.wallet_handle, 'invitations', msg.data['connection~sig']['signer'], '{}') )['value'] ) pending_connection['status'] = "Response Received" pending_connection['@type'] = AdminConnection.RESPONSE_RECEIVED pending_connection['history'].append({ 'date': str(datetime.datetime.now()), 'msg': msg.to_dict()}) # Pairwise connection between agents is established at this point await self.agent.send_admin_message(pending_connection) # Delete invitation await non_secrets.delete_wallet_record(self.agent.wallet_handle, 'invitations', msg.data['connection~sig']['signer'])
async def send_request(self, msg: Message): """ Recall invite message from wallet and prepare and send request to the inviter. send_request message format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/admin_connections/1.0/send_request", "key": <key sent in invite> } Request format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/request", "label": "Bob", "did": "B.did@B:A", "did_doc": { // did Doc here. } } """ pending_connection = Serializer.unpack( json.loads( await non_secrets.get_wallet_record( self.agent.wallet_handle, 'invitations', msg['connection_key'], '{}' ) )['value'] ) my_label = self.agent.owner label = pending_connection['label'] their_connection_key = pending_connection['connection_key'] their_endpoint = pending_connection['endpoint'] # Create my information for connection (my_did, my_vk) = await utils.create_and_store_my_did(self.agent.wallet_handle) await did.set_did_metadata( self.agent.wallet_handle, my_did, json.dumps({ 'label': label, 'their_endpoint': their_endpoint }) ) # Send Connection Request to inviter request = Message({ '@type': Connection.REQUEST, 'label': my_label, 'connection': { 'did': my_did, 'did_doc': { "@context": "https://w3id.org/did/v1", "id": my_did, "publicKey": [{ "id": my_did + "#keys-1", "type": "Ed25519VerificationKey2018", "controller": my_did, "publicKeyBase58": my_vk }], "service": [{ "id": my_did + ";indy", "type": "IndyAgent", "recipientKeys": [my_vk], #"routingKeys": ["<example-agency-verkey>"], "serviceEndpoint": self.agent.endpoint, }], } } }) await self.agent.send_message_to_endpoint_and_key( my_vk, their_connection_key, their_endpoint, request ) pending_connection['@type'] = AdminConnection.REQUEST_SENT pending_connection['status'] = "Request Sent" pending_connection['history'].append({ 'date': str(datetime.datetime.now()), 'msg': msg.to_dict()}) await non_secrets.update_wallet_record_value(self.agent.wallet_handle, 'invitations', pending_connection['connection_key'], Serializer.pack(pending_connection)) await self.agent.send_admin_message(pending_connection)
async def response_received(self, msg: Message) -> Message: """ Process response Response format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/response", "did":"A.did@A:B", "did_doc": { //did doc } } """ r = await self.validate_common_message_blocks(msg, Connection.FAMILY) if not r: return r my_did = msg.context['to_did'] if my_did is None: msg[ConnectionMessage.CONNECTION], sig_verified = await self.agent.unpack_and_verify_signed_agent_message_field( msg['connection~sig']) if not sig_verified: print('Encountered error parsing connection response. Connection request not found.') else: vk, endpoint = ConnectionMessage.extract_verkey_endpoint(msg) if None in (vk, endpoint): # Cannot extract verkey and endpoint hence won't send any message back. print('Encountered error parsing connection response. Connection request not found.') else: # Sending an error message back to the sender err_msg = self.build_problem_report_for_connections(Connection.FAMILY, ConnectionMessage.RESPONSE_FOR_UNKNOWN_REQUEST, "No corresponding connection request found") await self.agent.send_message_to_endpoint_and_key(vk, endpoint, err_msg) return # Following should return an error if key not found for given DID my_vk = await did.key_for_local_did(self.agent.wallet_handle, my_did) #process signed field msg[ConnectionMessage.CONNECTION], sig_verified = await self.agent.unpack_and_verify_signed_agent_message_field(msg['connection~sig']) # connection~sig remains for metadata their_did, their_vk, their_endpoint = ConnectionMessage.extract_their_info(msg) msg_vk = msg.context['from_key'] # TODO: verify their_vk (from did doc) matches msg_vk # Retrieve connection information from DID metadata my_did_meta = json.loads( await did.get_did_metadata(self.agent.wallet_handle, my_did) ) label = my_did_meta['label'] # Clear DID metadata. This info will be stored in pairwise meta. await did.set_did_metadata(self.agent.wallet_handle, my_did, '') # In the final implementation, a signature will be provided to verify changes to # the keys and DIDs to be used long term in the relationship. # Both the signature and signature check are omitted for now until specifics of the # signature are decided. # Store their information from response await utils.store_their_did(self.agent.wallet_handle, their_did, their_vk) await did.set_did_metadata( self.agent.wallet_handle, their_did, json.dumps({ 'label': label, 'endpoint': their_endpoint }) ) # Create pairwise relationship between my did and their did await pairwise.create_pairwise( self.agent.wallet_handle, their_did, my_did, json.dumps({ 'label': label, 'their_endpoint': their_endpoint, 'their_vk': their_vk, 'my_vk': my_vk, 'connection_key': msg.data['connection~sig']['signer'] }) ) pending_connection = Serializer.unpack( json.loads( await non_secrets.get_wallet_record(self.agent.wallet_handle, 'invitations', msg.data['connection~sig']['signer'], '{}') )['value'] ) pending_connection['status'] = "Response Received" pending_connection['@type'] = AdminConnection.RESPONSE_RECEIVED pending_connection['history'].append({ 'date': str(datetime.datetime.now()), 'msg': msg.to_dict()}) # Pairwise connection between agents is established at this point await self.agent.send_admin_message(pending_connection) # Delete invitation await non_secrets.delete_wallet_record(self.agent.wallet_handle, 'invitations', msg.data['connection~sig']['signer'])
async def response_recieved(self, msg: Message) -> Message: ''' Response is received from user and connection is set into place. ''' print("\nresponse received") my_did = msg.context['to_did'] my_vk = await did.key_for_local_did(self.agent.wallet_handle, my_did) # process signed field msg['connection'], sig_verified = await self.agent.unpack_and_verify_signed_agent_message_field( msg['connection~sig']) # connection~sig remains for metadata their_did = msg['connection']['DID'] their_vk = msg['connection']['DIDDoc']['publicKey'][0][ 'publicKeyBase58'] msg_vk = msg.context['from_key'] # Retrieve connection information from DID metadata my_did_meta = json.loads(await did.get_did_metadata(self.agent.wallet_handle, my_did)) label = my_did_meta['label'] # Clear DID metadata. This info will be stored in pairwise meta. await did.set_did_metadata(self.agent.wallet_handle, my_did, '') # In the final implementation, a signature will be provided to verify changes to # the keys and DIDs to be used long term in the relationship. # Both the signature and signature check are omitted for now until specifics of the # signature are decided. # Store their information from response await utils.store_their_did(self.agent.wallet_handle, their_did, their_vk) await did.set_did_metadata(self.agent.wallet_handle, their_did, json.dumps({'label': label})) # Create pairwise relationship between my did and their did await pairwise.create_pairwise( self.agent.wallet_handle, their_did, my_did, json.dumps({ 'label': label, 'their_vk': their_vk, 'my_vk': my_vk, 'connection_key': msg.data['connection~sig']['signer'] })) # Retrieve pending connection from records pending_connection = Serializer.unpack( json.loads(await non_secrets.get_wallet_record( self.agent.wallet_handle, 'invitations', msg.data['connection~sig']['signer'], '{}'))['value']) pending_connection['status'] = "Response Received" pending_connection['@type'] = "RESPONSE_RECIEVED" pending_connection['history'].append({ 'date': str(datetime.datetime.now()), 'msg': msg.to_dict() }) print("CONNECTION RESPONSE_RECEIVED: PENDING CONN: ", pending_connection) # Pairwise connection between agents is established at this point # Delete invitation await non_secrets.delete_wallet_record( self.agent.wallet_handle, 'invitations', msg.data['connection~sig']['signer'])
async def message_process(agent): """ Message processing loop task. Message routes are also defined here through the message router. """ msg_router = agent['msg_router'] msg_receiver = agent['msg_receiver'] ui_event_queue = agent['ui_event_queue'] connection = agent['modules']['connection'] msg_router.register(CONN.FAMILY, connection) while True: encrypted_msg_bytes = await msg_receiver.recv() try: encrypted_msg_str = Serializer.unpack(encrypted_msg_bytes) except Exception as e: print('Failed to unpack message: {}\n\nError: {}'.format( encrypted_msg_bytes, e)) continue encrypted_msg_bytes = base64.b64decode( encrypted_msg_str['content'].encode('utf-8')) agent_dids_str = await did.list_my_dids_with_meta( WEBAPP['agent'].wallet_handle) agent_dids_json = json.loads(agent_dids_str) this_did = "" # trying to find verkey for encryption for agent_did_data in agent_dids_json: try: decrypted_msg = await crypto.anon_decrypt( WEBAPP['agent'].wallet_handle, agent_did_data['verkey'], encrypted_msg_bytes) this_did = agent_did_data['did'] # decrypted -> found key, stop loop break except IndyError as e: # key did not work if e.error_code == error.ErrorCode.CommonInvalidStructure: print('Key did not work') continue else: # something else happened print('Could not decrypt message: {}\nError: {}'.format( encrypted_msg_bytes, e)) continue if not decrypted_msg: "Agent doesn't have needed verkey for anon_decrypt" continue try: msg = Serializer.unpack(decrypted_msg) except Exception as e: print('Failed to unpack message: {}\n\nError: {}'.format( decrypted_msg, e)) continue # pass this connections did with the message msg['content']['did'] = this_did msg = Serializer.unpack_dict(msg['content']) res = await msg_router.route(msg) if res is not None: await ui_event_queue.send(Serializer.pack(res))
async def send_request(self, invitation): ''' When button pressed, connection request is send to the other user. :param invitation: invitation from wallet record ''' pending_connection = Serializer.unpack( # recover invitation from wallet record json.loads(await non_secrets.get_wallet_record( self.agent.wallet_handle, 'invitations', invitation.get('connection_key'), '{}'))['value']) print("PENDING CONNECTION", pending_connection) my_label = self.agent.owner # agent name label = pending_connection['label'] # name of other agent their_connection_key = pending_connection['connection_key'] # create info for connection (my_did, my_vk) = await create_and_store_my_did(self.agent.wallet_handle) await did.set_did_metadata(self.agent.wallet_handle, my_did, json.dumps({'label': label})) # Send Connection Request to inviter request = Message({ '@type': "SEND_REQUEST", '@sendTo': label, 'label': my_label, 'connection': { 'DID': my_did, 'DIDDoc': { "@context": "https://w3id.org/did/v1", "id": my_did, "publicKey": [{ "id": my_did + "#keys-1", "type": "Ed25519VerificationKey2018", "controller": my_did, "publicKeyBase58": my_vk }], "service": [{ "id": my_did + ";indy", "type": "IndyAgent", "recipientKeys": [my_vk], }], } } }) print("REQUEST", request) print("SEND MESSAGE TO END AND KEY") await self.agent.send_message_to_endpoint_and_key( # Send request to other user my_vk, their_connection_key, request) # update pending request record pending_connection['@type'] = "REQUEST_SENT" pending_connection['status'] = "Request Sent" pending_connection['history'].append({ 'date': str(datetime.datetime.now()), 'msg': Message(invitation).to_dict() }) await non_secrets.update_wallet_record_value( self.agent.wallet_handle, 'invitations', pending_connection['connection_key'], Serializer.pack(pending_connection))