async def response_received(self, msg: Message) -> None: """ 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 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) # Verify that their_vk (from did doc) matches msg_vk msg_vk = msg.context['from_key'] if their_vk != msg_vk: err_msg = \ self.build_problem_report_for_connections( Connection.FAMILY, ConnectionMessage.KEY_ERROR, "Key provided in response does not match expected key") verkey, endpoint = ConnectionMessage.extract_verkey_endpoint(msg) await self.agent.send_message_to_endpoint_and_key(verkey, endpoint, err_msg) return # 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.deserialize( 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_response(self, msg: Message) -> None: """ 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'] my_vk = await did.key_for_local_did(self.agent.wallet_handle, my_did) response_msg = Message({ '@type': Connection.RESPONSE, '~thread': {Message.THREAD_ID: pairwise_meta['req_id'], Message.SENDER_ORDER: 0}, '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.deserialize( 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 request_received(self, msg: Message) -> None: """ 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 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.serialize(pending_connection).decode('utf-8'), '{}' ) 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) -> None: """ 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.deserialize( 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( their_connection_key, their_endpoint, request, my_vk ) 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.serialize(pending_connection).decode('utf-8')) await self.agent.send_admin_message(pending_connection)