Exemplo n.º 1
0
    async def receive_invite_message(cls, msg: Message, agent_name: str, pass_phrase: str, my_label: str, my_endpoint: str, ttl: int) -> str:
        """ 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.
        """
        if not cls.endorsement(msg):
            return None
        connection_key = msg['recipientKeys'][0]
        state_machine_id = connection_key
        log_channel_name = 'invite-log/' + uuid.uuid4().hex
        await WalletAgent.start_state_machine(
            agent_name=agent_name,
            machine_class=DIDExchange.DIDExchangeInviteeStateMachine,
            machine_id=state_machine_id,
            ttl=ttl,
            endpoint=my_endpoint,
            label=my_label,
            status=DIDExchangeStatus.Null,
            log_channel_name=log_channel_name
        )
        await WalletAgent.invoke_state_machine(
            agent_name=agent_name,
            id_=state_machine_id,
            content_type=cls.MESSAGE_CONTENT_TYPE,
            data=msg.as_json()
        )
        return log_channel_name
Exemplo n.º 2
0
 async def handle(cls, agent_name: str, wire_message: bytes, my_label: str=None, my_endpoint: str=None) -> bool:
     unpacked = await WalletAgent.unpack_message(agent_name, wire_message)
     kwargs = json.loads(unpacked['message'])
     message = Message(**kwargs)
     if message.get('@type', None) is None:
         return False
     if message.type == cls.REQUEST:
         state_machine_id = unpacked['sender_verkey']
         machine_class = DIDExchange.DIDExchangeInviterStateMachine
         await WalletAgent.start_state_machine(
             agent_name=agent_name, machine_class=machine_class, machine_id=state_machine_id, endpoint=my_endpoint,
             label=my_label, status=DIDExchangeStatus.Invited
         )
         await WalletAgent.invoke_state_machine(
             agent_name=agent_name, id_=state_machine_id,
             content_type=cls.WIRED_CONTENT_TYPE, data=wire_message
         )
         return True
     elif message.type == DIDExchange.RESPONSE:
         state_machine_id = message['connection~sig']['signer']
         await WalletAgent.invoke_state_machine(
             agent_name=agent_name, id_=state_machine_id,
             content_type=cls.WIRED_CONTENT_TYPE, data=wire_message
         )
         return True
     elif message.type in [TrustPing.PING, TrustPing.PING_RESPONSE]:
         state_machine_id = unpacked['sender_verkey']
         await WalletAgent.invoke_state_machine(
             agent_name=agent_name, id_=state_machine_id,
             content_type=cls.WIRED_CONTENT_TYPE, data=wire_message
         )
         return True
     elif message.type == DIDExchange.PROBLEM_REPORT:
         state_machine_id = message.to_dict().get('connection~sig', {}).get('signer')
         if state_machine_id:
             await WalletAgent.invoke_state_machine(
                 agent_name=agent_name, id_=state_machine_id,
                 content_type=cls.WIRED_CONTENT_TYPE, data=wire_message
             )
             return True
         else:
             logging.error('Problem report', message.as_json())
             return True
     else:
         return False
Exemplo n.º 3
0
 async def validate_common_message_blocks(cls, msg: Message,
                                          problem_code: str,
                                          context: Context):
     try:
         msg.validate_common_blocks()
         return True, None
     except MessageValidationException as e:
         logging.exception('Validation error while parsing message: %s' %
                           msg.as_json())
         if context.their_did:
             err_msg = cls.build_problem_report_for_connections(
                 problem_code, str(e.exception), thread_id=msg.id)
             return False, err_msg
         else:
             return False, None
     except Exception as e:
         logging.exception('Validation error while parsing message: %s' %
                           str(e))
         return False, None
Exemplo n.º 4
0
    def serialize(msg: Message) -> bytes:
        """ Serialize from Message to json string or from dictionary to json string.
        """

        return msg.as_json().encode('utf-8')
Exemplo n.º 5
0
        async def __receive_connection_response(self, msg: Message):
            if self.status == DIDExchangeStatus.Requested:
                success, err_msg = await DIDExchange.validate_common_message_blocks(msg)
                if success:
                    my_did = msg.context['to_did']
                    if my_did is None:
                        msg[DIDExchange.CONNECTION], sig_verified = \
                            await self.agent.unpack_and_verify_signed_agent_message_field(msg['connection~sig'])
                        if not sig_verified:
                            logging.error('Encountered error parsing connection response. Connection request not found.')
                        else:
                            vk, endpoint = BasicMessage.extract_verkey_endpoint(msg)
                            if None in (vk, endpoint):
                                # Cannot extract verkey and endpoint hence won't send any message back.
                                logging.error('Encountered error parsing connection response. Connection request not found.')
                            else:
                                # Sending an error message back to the sender
                                err_msg = DIDExchange.build_problem_report_for_connections(
                                    DIDExchange.RESPONSE_FOR_UNKNOWN_REQUEST,
                                    "No corresponding connection request found",
                                    thread_id=msg.id
                                )
                                await DIDExchange.send_message_to_endpoint_and_key(vk, endpoint, err_msg)
                                await self.__log('Send report problem', err_msg.to_dict())
                    else:
                        # Following should return an error if key not found for given DID
                        my_vk = await self.get_wallet().key_for_local_did(my_did)
                        # process signed field
                        msg[DIDExchange.CONNECTION], sig_verified = \
                            await DIDExchange.unpack_and_verify_signed_agent_message_field(msg['connection~sig'])
                        their_did, their_vk, their_endpoint, their_routing_keys = BasicMessage.extract_their_info(
                            msg, DIDExchange.CONNECTION
                        )
                        # Verify that their_vk (from did doc) matches msg_vk
                        msg_vk = msg.context['from_key']
                        if their_vk != msg_vk:
                            err_msg = \
                                DIDExchange.build_problem_report_for_connections(
                                    DIDExchange.KEY_ERROR,
                                    "Key provided in response does not match expected key",
                                    thread_id=msg.id
                                )
                            verkey, endpoint = BasicMessage.extract_verkey_endpoint(msg, DIDExchange.CONNECTION)
                            logging.error("Key provided in response does not match expected key")
                            await DIDExchange.send_message_to_endpoint_and_key(verkey, endpoint, err_msg)
                            await self.__log('Send report problem', err_msg.to_dict())
                            return
                        my_did_meta = await self.get_wallet().get_did_metadata(my_did)
                        label = my_did_meta['label']
                        # Clear DID metadata. This info will be stored in pairwise meta.
                        await self.get_wallet().set_did_metadata(my_did, metadata=None)
                        # 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 indy_sdk_utils.store_their_did(self.get_wallet(), their_did, their_vk)
                        await self.get_wallet().set_did_metadata(
                            their_did,
                            {
                                'label': label,
                                'endpoint': their_endpoint
                            }
                        )
                        # Create pairwise relationship between my did and their did
                        creation_kwargs = dict(
                            their_did=their_did,
                            my_did=my_did,
                            metadata={
                                'label': label,
                                'their_endpoint': their_endpoint,
                                'their_vk': their_vk,
                                'my_vk': my_vk,
                                'their_routing_keys': their_routing_keys,
                                'connection_key': msg.data['connection~sig']['signer']
                            }
                        )
                        await self.get_wallet().create_pairwise(**creation_kwargs)
                        await self.__log_pairwise_creation(creation_kwargs)
                        # Send ACK
                        ack = TrustPing.Ping.build(comment='Connection established', response_requested=False)
                        await DIDExchange.send_message_to_agent(their_did, ack, self.get_wallet())
                        await self.__log('Send', ack.to_dict())
                        await self.done()
                elif err_msg:
                    their_did = msg.context.get('from_did')
                    if their_did:
                        await DIDExchange.send_message_to_agent(their_did, err_msg, self.get_wallet())
                        await self.__log('Send report problem', err_msg.to_dict())
                    logging.error('Validation error while parsing message: %s', msg.as_json())
                else:
                    logging.error('Validation error while parsing message: %s', msg.as_json())
            else:
                raise ErrorStatus()
Exemplo n.º 6
0
        async def __receive_connection_request(self, msg: Message):
            if self.status == DIDExchangeStatus.Invited:
                success, err_msg = await DIDExchange.validate_common_message_blocks(msg)
                if success:
                    try:
                        DIDExchange.Request.validate(msg)
                    except Exception as e:
                        logging.exception('Error while parsing message %s with error %s' % (msg.as_json(), str(e)))
                        vk, endpoint = BasicMessage.extract_verkey_endpoint(msg, DIDExchange.CONNECTION)
                        if None in (vk, endpoint):
                            # Cannot extract verkey and endpoint hence won't send any message back.
                            logging.error('Encountered error parsing connection request %s' % str(e))
                        else:
                            # Sending an error message back to the sender
                            err_msg = DIDExchange.build_problem_report_for_connections(
                                DIDExchange.REQUEST_NOT_ACCEPTED,
                                str(e),
                                thread_id=msg.id
                            )
                            await DIDExchange.send_message_to_endpoint_and_key(vk, endpoint, err_msg, self.get_wallet())
                            await self.__log('Send report problem', err_msg.to_dict())
                    else:
                        try:
                            connection_key = msg.context['to_key']
                            label = msg['label']
                            their_did, their_vk, their_endpoint, their_routing_keys = BasicMessage.extract_their_info(
                                msg, DIDExchange.CONNECTION
                            )
                            # Store their information from request
                            await indy_sdk_utils.store_their_did(self.get_wallet(), their_did, their_vk)
                            await self.get_wallet().set_did_metadata(
                                their_did,
                                metadata=dict(label=label, endpoint=their_endpoint)
                            )
                            my_did, my_vk = await indy_sdk_utils.create_and_store_my_did(self.get_wallet())

                            pairwise_kwargs = dict(
                                their_did=their_did,
                                my_did=my_did,
                                metadata={
                                    'label': label,
                                    'req_id': msg['@id'],
                                    'their_endpoint': their_endpoint,
                                    'their_vk': their_vk,
                                    'my_vk': my_vk,
                                    'their_routing_keys': their_routing_keys,
                                    'connection_key': connection_key  # used to sign the response
                                }
                            )
                            await self.get_wallet().create_pairwise(**pairwise_kwargs)
                            await self.__log_pairwise_creation(pairwise_kwargs)
                        except Exception as e:
                            logging.exception('Error while process invitee request')
                            raise
                        else:
                            response_msg = await self.__send_connection_response(their_did)
                            await self.__log('Send', response_msg.to_dict())
                            self.ack_message_id = response_msg.id
                            self.status = DIDExchangeStatus.Responded
                elif err_msg:
                    their_did = msg.context.get('from_did')
                    if their_did:
                        await DIDExchange.send_message_to_agent(their_did, err_msg, self.get_wallet())
                        await self.__log('Send report problem', err_msg.to_dict())
                    logging.error('Validation error while parsing message: %s', msg.as_json())
                else:
                    logging.error('Validation error while parsing message: %s', msg.as_json())
            else:
                raise ErrorStatus()