Esempio n. 1
0
    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'])
Esempio n. 2
0
    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'])
Esempio n. 3
0
    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)
Esempio n. 4
0
    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)