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 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 send_message_to_endpoint_and_key(self, my_ver_key, their_ver_key, their_endpoint, msg): wire_message = { 'to': their_ver_key, 'from': my_ver_key, 'payload': serialize_bytes_json(await crypto.auth_crypt( self.wallet_handle, my_ver_key, their_ver_key, str_to_bytes(msg.as_json()))) } wire_message = await crypto.pack_message(self.wallet_handle, Serializer.pack(msg), [their_ver_key], my_ver_key) async with aiohttp.ClientSession() as session: headers = {'content-type': 'application/ssi-agent-wire'} async with session.post(their_endpoint, data=wire_message, headers=headers) as resp: if resp.status != 202: print(resp.status) print(await resp.text())
async def send_response(to_did, agent): """ sends a connection response should be anon_encrypted. a connection response will include: - user DID, and verkey """ # find endpoint wallet_handle = agent.wallet_handle meta = json.loads(await did.get_did_metadata(wallet_handle, to_did)) endpoint = meta['endpoint'] endpoint_vk = meta['endpoint_vk'] their_vk = await did.key_for_local_did(wallet_handle, to_did) pairwise_json = json.loads(await pairwise.get_pairwise(wallet_handle, to_did)) my_did = pairwise_json['my_did'] my_vk = await did.key_for_local_did(wallet_handle, my_did) data = {'did': my_did, 'verkey': my_vk} data = await crypto.anon_crypt(their_vk, json.dumps(data).encode('utf-8')) envelope = json.dumps({'type': 'CONN_RES', 'id': to_did, 'data': data}) encrypted_envelope = await crypto.anon_crypt(endpoint_vk, Serializer.pack(envelope)) async with aiohttp.ClientSession() as session: async with session.post(endpoint, data=envelope) as resp: print(resp.status) print(await resp.text())
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 send_request(offer, agent): """ sends a connection request. a connection request contains: - data concerning the request: - Name of Sender - Purpose - DID@A:B - URL of agent - Public verkey """ conn_name = offer['name'] our_endpoint = agent.endpoint endpoint_vk = offer['verkey'] # get did and vk (my_did, my_vk) = await did.create_and_store_my_did(agent.wallet_handle, "{}") msg = Message( CONN.REQUEST, offer['nonce'], { 'did': my_did, 'verkey': my_vk, 'endpoint': { 'url': agent.endpoint, 'verkey': agent.endpoint_vk, }, #Extra Metadata 'owner': agent.owner, }) serialized_msg = Serializer.pack(msg) # add to queue agent.connections[my_did] = { "name": offer['name'], "endpoint": offer['endpoint'], "time": str(datetime.datetime.now()).split(' ')[1].split('.')[0], "status": "pending" } # send to server print("Sending to {}".format(offer['endpoint'])) async with aiohttp.ClientSession() as session: async with session.post(offer['endpoint'], data=serialized_msg) as resp: print(resp.status) print(await resp.text()) return { 'type': 'CONN_REQ_SENT', 'id': None, 'data': agent.connections[my_did] }
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 generate_invite(self, msg: Message) -> Message: """ Generate new connection invitation. 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. 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", "did": "did:peer:oiSqsNYhMrjHiqZDTUthsw", "key": "8HH5gYEeNc3z7PYXmd54d4x6qAfCNrqQqEB3nS7Zfu7K", "endpoint": "https://example.com/endpoint" } Currently, only peer DID is supported. """ connection_key = await did.create_key(self.agent.wallet_handle, "{}") # Store connection key await non_secrets.add_wallet_record( self.agent.wallet_handle, 'connection_key', connection_key, connection_key, '{}' ) invite_msg = Message({ '@type': Connection.INVITE, 'label': self.agent.owner, 'recipientKeys': [connection_key], 'serviceEndpoint': self.agent.endpoint, # routingKeys not specified, but here is where they would be put in the invite. }) b64_invite = \ base64.urlsafe_b64encode(bytes(Serializer.pack(invite_msg), 'utf-8')).decode('ascii') await self.agent.send_admin_message( Message({ '@type': AdminConnection.INVITE_GENERATED, 'invite': '{}?c_i={}'.format(self.agent.endpoint, b64_invite) }) )
async def send_admin_message(self, msg: Message): if self.agent_admin_key and self.admin_key: msg = await crypto.pack_message(self.wallet_handle, Serializer.pack(msg), [self.admin_key], self.agent_admin_key) msg = msg.decode('ascii') else: msg = msg.as_json() await self.outbound_admin_message_queue.put(msg)
async def send_message_to_endpoint_and_key(self, my_ver_key, their_ver_key, msg): ''' Message is encrypted and sent to the other user through the server ''' print("\nSend message to end and key") print(my_ver_key, their_ver_key, msg) wire_message = await crypto.pack_message(self.wallet_handle, Serializer.pack(msg), [their_ver_key], my_ver_key) print("wire:", wire_message) socket_client.send(wire_message) print("SEND MESSAGE")
async def send_message_to_endpoint_and_key(self, their_ver_key, their_endpoint, msg, my_ver_key=None): # If my_ver_key is omitted, anoncrypt is used inside pack. wire_message = await crypto.pack_message(self.wallet_handle, Serializer.pack(msg), [their_ver_key], my_ver_key) async with aiohttp.ClientSession() as session: headers = {'content-type': 'application/ssi-agent-wire'} async with session.post(their_endpoint, data=wire_message, headers=headers) as resp: if resp.status != 202: print(resp.status) print(await resp.text())
async def sender_send_offer_rejected(msg, agent): conn_name = msg.message['name'] nonce = msg.message['id'] receiver_endpoint = agent.pending_offers[nonce]['endpoint'] del agent.pending_offers[nonce] rej_msg = Message(CONN.SENDER_REJECTION, nonce, {'name': conn_name}) serialized_msg = Serializer.pack(rej_msg) async with aiohttp.ClientSession() as session: async with session.post(receiver_endpoint, data=serialized_msg) as resp: print(resp.status) print(await resp.text()) return Message(UI.SENDER_OFFER_REJECTED, agent.ui_token, { 'name': conn_name, 'id': nonce })
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 send_invite(self, msg: Message) -> Message: """ UI activated method. """ their_endpoint = msg['endpoint'] conn_name = msg['name'] (endpoint_did_str, connection_key) = await did.create_and_store_my_did( self.agent.wallet_handle, "{}") meta_json = json.dumps({"conn_name": conn_name}) await did.set_did_metadata(self.agent.wallet_handle, endpoint_did_str, meta_json) msg = Message({ '@type': Connection.INVITE, 'content': { 'name': conn_name, 'endpoint': { 'url': self.agent.endpoint, }, 'connection_key': connection_key } }) serialized_msg = Serializer.pack(msg) async with aiohttp.ClientSession() as session: async with session.post(their_endpoint, data=serialized_msg) as resp: print(resp.status) print(await resp.text()) await self.agent.send_admin_message( Message({ '@type': AdminConnection.INVITE_SENT, 'id': self.agent.ui_token, 'content': { 'name': conn_name } }))
async def credential_received(self, msg: Message) -> Message: ''' Prover receives the credential ''' print("\n\n CREDENTIAL RECIEVED\n\n") data = msg['data'] schematype = data['schematype'] nonce = data['nonce'] their_did = data['my_did'] # this message comes from other agent my_did = data['their_did'] # this message comes form other agent await anoncreds.prover_store_credential(self.agent.wallet_handle, None, data['cred_request_meta'], data['cred'], data['cred_def'], None) if (schematype == 'medical'): self.agent.medical_cred_def_id = data['cred_def_id'] self.agent.medical_cred_offer = data['cred_offer'] self.agent.got_medical_credential = True if (schematype == 'consent'): self.agent.got_consent_credential = True print("+++ Credential prover stores credential: their did: " + their_did) print("Credential: got credential. Sending to view." + self.agent.owner) credential_msg = Message({ '@type': Credential.CREDENTIAL_OBTAINED, 'label': self.agent.owner, 'schematype': schematype, 'data': data['cred'] }) b64_cred = \ base64.urlsafe_b64encode(bytes(Serializer.pack(credential_msg), 'utf-8')).decode('ascii') print("CREDENTIAL: done storing credential")
async def send_offer(msg, agent): endpoint = msg.message['endpoint'] name = msg.message['name'] nonce = uuid.uuid4().hex agent.pending_offers[nonce] = dict(name=name, endpoint=endpoint) msg = Message( CONN.OFFER, nonce, { 'name': name, 'endpoint': { 'url': agent.endpoint, 'verkey': agent.endpoint_vk, }, 'offer_endpoint': agent.offer_endpoint }) serialized_msg = Serializer.pack(msg) async with aiohttp.ClientSession() as session: async with session.post(endpoint, data=serialized_msg) as resp: print(resp.status) print(await resp.text()) return Message(UI.OFFER_SENT, agent.ui_token, {'name': name, 'id': nonce})
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 send_offer_accepted(msg, agent): nonce = msg.message['id'] receiver_endpoint = agent.received_offers[nonce]['endpoint'] conn_name = msg.message['name'] agent.connections[nonce] = dict(name=conn_name, endpoint=receiver_endpoint) del agent.received_offers[nonce] msg = Message(CONN.ACKNOWLEDGE, nonce, { 'name': conn_name, }) serialized_msg = Serializer.pack(msg) async with aiohttp.ClientSession() as session: async with session.post(receiver_endpoint, data=serialized_msg) as resp: print(resp.status) print(await resp.text()) return Message(UI.OFFER_ACCEPTED_SENT, agent.ui_token, { 'name': conn_name, 'id': nonce })
async def generate_invite(self): ''' Creates an invitation code to be send to the other user to process. ''' connection_key = await did.create_key(self.agent.wallet_handle, "{}") print("Connection Key: ", connection_key) # store key await non_secrets.add_wallet_record(self.agent.wallet_handle, 'connection_key', connection_key, connection_key, '{}') invite_msg = Message({ '@type': "GENERATE_INVITE", 'label': self.agent.owner, 'initialized': self.agent.initialized, 'recipientKeys': [connection_key], }) print("INVITE_MSG: ", invite_msg) invite_code = \ base64.urlsafe_b64encode( bytes(Serializer.pack(invite_msg), 'utf-8')).decode('ascii') return str(invite_code)
async def handle_request_received(msg, agent): """ Handle reception of request, storing to be accepted later. """ agent.received_requests[msg.did] = Serializer.pack(msg)
async def send_message(self, msg: Message) -> Message: """ UI activated method. """ their_did_str = msg['to'] message_to_send = msg['message'] pairwise_conn_info_str = await pairwise.get_pairwise(self.agent.wallet_handle, their_did_str) pairwise_conn_info_json = json.loads(pairwise_conn_info_str) my_did_str = pairwise_conn_info_json['my_did'] time_sent = time.time() data_to_send = json.dumps( { "timestamp": time_sent, "content": message_to_send } ) # store message in the wallet await non_secrets.add_wallet_record( self.agent.wallet_handle, "basicmessage", uuid.uuid4().hex, json.dumps({ 'from': my_did_str, 'timestamp': time_sent, 'content': message_to_send }), json.dumps({ "their_did": their_did_str }) ) data_to_send_bytes = str_to_bytes(data_to_send) metadata_json = json.loads(pairwise_conn_info_json['metadata']) conn_name = metadata_json['conn_name'] their_endpoint = metadata_json['their_endpoint'] their_verkey_str = metadata_json['their_verkey'] my_did_info_str = await did.get_my_did_with_meta(self.agent.wallet_handle, my_did_str) my_did_info_json = json.loads(my_did_info_str) my_verkey_str = my_did_info_json['verkey'] inner_msg = Message({ '@type': BASICMESSAGE.MESSAGE, 'from': my_did_str, 'message': serialize_bytes_json( await crypto.auth_crypt(self.agent.wallet_handle, my_verkey_str, their_verkey_str, data_to_send_bytes)) }) outer_msg = Message({ '@type': FORWARD.FORWARD, 'to': "ABC", 'content': inner_msg }) serialized_outer_msg = Serializer.pack(outer_msg) serialized_outer_msg_bytes = str_to_bytes(serialized_outer_msg) all_message = Message({ 'content': serialize_bytes_json(await crypto.anon_crypt(their_verkey_str, serialized_outer_msg_bytes)) }) serialized_msg = Serializer.pack(all_message) async with aiohttp.ClientSession() as session: async with session.post(their_endpoint, data=serialized_msg) as resp: print(resp.status) print(await resp.text()) return Message({ '@type': ADMIN_BASICMESSAGE.MESSAGE_SENT, 'id': self.agent.ui_token, 'with': their_did_str, 'message': { 'from': my_did_str, 'timestamp': time_sent, 'content': message_to_send } })
async def send_message(self, msg: Message) -> Message: """ UI activated method. """ their_did_str = msg['content']['their_did'] message_to_send = msg['content']['message'] pairwise_conn_info_str = await pairwise.get_pairwise( self.agent.wallet_handle, their_did_str) pairwise_conn_info_json = json.loads(pairwise_conn_info_str) my_did_str = pairwise_conn_info_json['my_did'] data_to_send = json.dumps({"message": message_to_send}) data_to_send_bytes = str_to_bytes(data_to_send) metadata_json = json.loads(pairwise_conn_info_json['metadata']) conn_name = metadata_json['conn_name'] their_endpoint = metadata_json['their_endpoint'] their_verkey_str = metadata_json['their_verkey'] my_did_info_str = await did.get_my_did_with_meta( self.agent.wallet_handle, my_did_str) my_did_info_json = json.loads(my_did_info_str) my_verkey_str = my_did_info_json['verkey'] inner_msg = Message({ 'type': CONN.MESSAGE, 'to': "did:sov:ABC", 'content': serialize_bytes_json(await crypto.auth_crypt(self.agent.wallet_handle, my_verkey_str, their_verkey_str, data_to_send_bytes)) }) outer_msg = Message({ 'type': FORWARD.FORWARD, 'to': "ABC", 'content': inner_msg }) serialized_outer_msg = Serializer.pack(outer_msg) serialized_outer_msg_bytes = str_to_bytes(serialized_outer_msg) all_message = Message({ 'content': serialize_bytes_json(await crypto.anon_crypt(their_verkey_str, serialized_outer_msg_bytes)) }) serialized_msg = Serializer.pack(all_message) async with aiohttp.ClientSession() as session: async with session.post(their_endpoint, data=serialized_msg) as resp: print(resp.status) print(await resp.text()) return Message({ 'type': CONN_UI.MESSAGE_SENT, 'id': self.agent.ui_token, 'content': { 'name': conn_name } })
async def request_received(self, msg: Message) -> Message: """ Received connection request. Request format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/request", "label": "Bob", "connection":{ "did": "B.did@B:A", "did_doc": { "@context": "https://w3id.org/did/v1", "publicKey": [{ "id": "did:example:123456789abcdefghi#keys-1", "type": "Ed25519VerificationKey2018", "controller": "did:example:123456789abcdefghi", "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" }], "service": [{ "type": "IndyAgent", "recipientKeys" : [ "<verkey>" ], //pick one "routingKeys": ["<example-agency-verkey>"], "serviceEndpoint": "https://example.agency.com", }] } } } """ connection_key = msg.context['to_key'] label = msg['label'] their_did = msg['connection']['did'] # NOTE: these values are pulled based on the minimal connectathon format. Full processing # will require full DIDDoc storage and evaluation. their_vk = msg['connection']['did_doc']['publicKey'][0]['publicKeyBase58'] their_endpoint = msg['connection']['did_doc']['service'][0]['serviceEndpoint'] # Store their information from request 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 my information for connection (my_did, my_vk) = await utils.create_and_store_my_did(self.agent.wallet_handle) # 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, 'req_id': msg['@id'], 'their_endpoint': their_endpoint, 'their_vk': their_vk, 'my_vk': my_vk, 'connection_key': connection_key # used to sign the response }) ) pending_connection = Message({ '@type': AdminConnection.REQUEST_RECEIVED, 'label': label, 'did': their_did, 'connection_key': connection_key, 'endpoint': their_endpoint, 'history': [{ 'date': str(datetime.datetime.now()), 'msg': msg.to_dict()}], 'status': "Request Received" # routingKeys not specified, but here is where they would be put in the invite. }) try: await non_secrets.add_wallet_record( self.agent.wallet_handle, 'invitations', connection_key, Serializer.pack(pending_connection), '{}' ) except error.IndyError as indy_error: if indy_error.error_code == error.ErrorCode.WalletItemAlreadyExists: pass raise indy_error await self.agent.send_admin_message(pending_connection)
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 request_received(self, msg: Message) -> Message: """ Received connection request. Request format: { "@type": "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/request", "label": "Bob", "connection":{ "did": "B.did@B:A", "did_doc": { "@context": "https://w3id.org/did/v1", "publicKey": [{ "id": "did:example:123456789abcdefghi#keys-1", "type": "Ed25519VerificationKey2018", "controller": "did:example:123456789abcdefghi", "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV" }], "service": [{ "type": "IndyAgent", "recipientKeys" : [ "<verkey>" ], //pick one "routingKeys": ["<example-agency-verkey>"], "serviceEndpoint": "https://example.agency.com", }] } } } """ r = await self.validate_common_message_blocks(msg, Connection.FAMILY) if not r: return r try: ConnectionMessage.Request.validate(msg) except Exception as e: 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 request ', e) else: # Sending an error message back to the sender err_msg = self.build_problem_report_for_connections(Connection.FAMILY, ConnectionMessage.REQUEST_NOT_ACCEPTED, str(e)) await self.agent.send_message_to_endpoint_and_key(vk, endpoint, err_msg) return connection_key = msg.context['to_key'] label = msg['label'] their_did, their_vk, their_endpoint = ConnectionMessage.extract_their_info(msg) # Store their information from request 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 my information for connection (my_did, my_vk) = await utils.create_and_store_my_did(self.agent.wallet_handle) # 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, 'req_id': msg['@id'], 'their_endpoint': their_endpoint, 'their_vk': their_vk, 'my_vk': my_vk, 'connection_key': connection_key # used to sign the response }) ) pending_connection = Message({ '@type': AdminConnection.REQUEST_RECEIVED, 'label': label, 'did': their_did, 'connection_key': connection_key, 'endpoint': their_endpoint, 'history': [{ 'date': str(datetime.datetime.now()), 'msg': msg.to_dict()}], 'status': "Request Received" # routingKeys not specified, but here is where they would be put in the invite. }) try: await non_secrets.add_wallet_record( self.agent.wallet_handle, 'invitations', connection_key, Serializer.pack(pending_connection), '{}' ) except error.IndyError as indy_error: if indy_error.error_code == error.ErrorCode.WalletItemAlreadyExists: pass raise indy_error await self.agent.send_admin_message(pending_connection)
async def send_request(self, msg: Message) -> Message: """ UI activated method. """ their_endpoint = msg['content']['endpoint'] conn_name = msg['content']['name'] their_connection_key = msg['content']['key'] my_endpoint_uri = self.agent.endpoint (my_endpoint_did_str, my_connection_key) = await did.create_and_store_my_did( self.agent.wallet_handle, "{}") data_to_send = json.dumps({ "did": my_endpoint_did_str, "key": my_connection_key }) data_to_send_bytes = str_to_bytes(data_to_send) meta_json = json.dumps({ "conn_name": conn_name, "their_endpoint": their_endpoint }) await did.set_did_metadata(self.agent.wallet_handle, my_endpoint_did_str, meta_json) inner_msg = Message({ 'type': CONN.REQUEST, 'to': "did:sov:ABC", 'endpoint': my_endpoint_uri, 'content': serialize_bytes_json(await crypto.auth_crypt(self.agent.wallet_handle, my_connection_key, their_connection_key, data_to_send_bytes)) }) outer_msg = Message({ 'type': FORWARD.FORWARD_TO_KEY, 'to': "ABC", 'content': inner_msg }) serialized_outer_msg = Serializer.pack(outer_msg) serialized_outer_msg_bytes = str_to_bytes(serialized_outer_msg) all_message = Message({ 'type': CONN.REQUEST, 'content': serialize_bytes_json(await crypto.anon_crypt(their_connection_key, serialized_outer_msg_bytes)) }) serialized_msg = Serializer.pack(all_message) async with aiohttp.ClientSession() as session: async with session.post(their_endpoint, data=serialized_msg) as resp: print(resp.status) print(await resp.text()) return Message({ 'type': CONN_UI.REQUEST_SENT, 'id': self.agent.ui_token, 'content': { 'name': conn_name } })
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 request_recieved(self, msg): ''' Request received from user to create pending connection. ''' print("request received") connection_key = msg.context['to_key'] label = msg['label'] their_did = msg['connection']['DID'] their_vk = msg['connection']['DIDDoc']['publicKey'][0][ 'publicKeyBase58'] # store info from request 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 my information for connection (my_did, my_vk) = await utils.create_and_store_my_did(self.agent.wallet_handle) # 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, 'req_id': msg['@id'], 'their_vk': their_vk, 'my_vk': my_vk, 'connection_key': connection_key # used to sign the response })) # pending connection message pending_connection = Message({ '@type': "REQUEST_RECIEVED", 'label': label, 'did': their_did, 'connection_key': connection_key, 'history': [{ 'date': str(datetime.datetime.now()), 'msg': msg.to_dict() }], 'status': "Request Received" }) print("CONNECTION REQUEST_RECEIVED: PENDING CONN: ", pending_connection) # Create pending connection between users try: await non_secrets.add_wallet_record( self.agent.wallet_handle, 'invitations', connection_key, Serializer.pack(pending_connection), '{}') except error.IndyError as indy_error: if indy_error.error_code == error.ErrorCode.WalletItemAlreadyExists: pass raise indy_error
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))