コード例 #1
0
    def test_that_serializing_and_deserializing_message_with_wrong_payload_type_should_raise_exception(
            self):
        middleman_message = GolemMessageFrame(Ping(), self.request_id)
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        payload_type_position = FRAME_SIGNATURE_BYTES_LENGTH + FRAME_REQUEST_ID_BYTES_LENGTH
        invalid_payload_type = 100

        # Sanity check for payload type in this case to be between expected bytes
        assert raw_message[payload_type_position:payload_type_position +
                           1] == b'\x00'
        assert invalid_payload_type not in PAYLOAD_TYPE_TO_MIDDLEMAN_MESSAGE_CLASS

        # Replace bytes with payload length
        raw_message = (
            raw_message[:payload_type_position] +
            bytes(bytearray([invalid_payload_type])) +
            raw_message[payload_type_position + FRAME_PAYLOAD_TYPE_LENGTH:])

        # Replace message signature
        new_signature = ecdsa_sign(CONCENT_PRIVATE_KEY,
                                   raw_message[FRAME_SIGNATURE_BYTES_LENGTH:])
        raw_message_with_new_signature = new_signature + raw_message[
            FRAME_SIGNATURE_BYTES_LENGTH:]

        with pytest.raises(PayloadTypeInvalidMiddlemanProtocolError):
            AbstractFrame.deserialize(raw_message_with_new_signature,
                                      CONCENT_PUBLIC_KEY)
コード例 #2
0
    def test_that__handle_connection_should_send_error_frame_if_payload_golem_message_type_cannot_be_deserialized(
            self):
        # Prepare frame payload which is Golem message that cannot be deserialized.
        middleman_message = GolemMessageFrame(
            payload=self._get_deserialized_transaction_signing_request(
                nonce=
                'not_int_nonce_which_will_fail_on_deserialization_causing_message_error'
            ),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        raw_message_received = self._prepare_and_execute_handle_connection(
            raw_message)

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=SIGNING_SERVICE_PUBLIC_KEY,
        )

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.InvalidPayload)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
コード例 #3
0
    def test_that_authenticate_should_send_authentication_response_if_authentication_challenge_is_correct(
            self):
        middleman_message = AuthenticationChallengeFrame(
            payload=self.authentication_challenge,
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        raw_message_received = self._prepare_and_execute_handle_connection(
            raw_message, )

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=SIGNING_SERVICE_PUBLIC_KEY,
        )

        assertpy.assert_that(deserialized_message).is_instance_of(
            AuthenticationResponseFrame)
        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(bytes)
        assertpy.assert_that(
            ecdsa_verify(
                SIGNING_SERVICE_PUBLIC_KEY,
                deserialized_message.payload,
                self.authentication_challenge,
            )).is_true()
コード例 #4
0
    def test_that__handle_connection_should_send_error_frame_if_payload_golem_message_type_is_unexpected(
            self):
        # Prepare frame payload which is Golem message other than TransactionSigningRequest.
        middleman_message = GolemMessageFrame(
            payload=Ping(),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        raw_message_received = self._prepare_and_execute_handle_connection(
            raw_message)

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=SIGNING_SERVICE_PUBLIC_KEY,
        )

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.UnexpectedMessage)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
コード例 #5
0
    def test_that_serializing_and_deserializing_message_too_short_should_raise_exception(
        self,
        middleman_message_type,
        payload,
    ):
        middleman_message = middleman_message_type(payload, self.request_id)
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        malformed_raw_message = raw_message[:-1]

        with pytest.raises(FrameInvalidMiddlemanProtocolError):
            AbstractFrame.deserialize(
                malformed_raw_message,
                CONCENT_PUBLIC_KEY,
            )
コード例 #6
0
    def test_that__handle_connection_should_send_golem_message_signed_transaction_if_warning_daily_threshold_exceeded(
            self):
        middleman_message = GolemMessageFrame(
            payload=self._get_deserialized_transaction_signing_request(),
            request_id=99,
        )
        middleman_message.payload.value = WARNING_DAILY_THRESHOLD + 1
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        def handle_connection_wrapper(signing_service, connection,
                                      receive_frame_generator):
            with mock.patch(
                    'signing_service.signing_service.SigningService._get_signed_transaction',
                    return_value=self._get_deserialized_signed_transaction(),
            ):
                with mock.patch(
                        'signing_service.signing_service.SigningService._add_payload_value_to_daily_transactions_sum'
                ):
                    signing_service._handle_connection(receive_frame_generator,
                                                       connection)

        raw_message_received = self._prepare_and_execute_handle_connection(
            raw_message,
            handle_connection_wrapper,
        )

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=SIGNING_SERVICE_PUBLIC_KEY,
        )

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(SignedTransaction)
コード例 #7
0
    def test_that__handle_connection_should_send_error_frame_if_frame_signature_is_wrong(
            self):
        # Prepare message with wrong signature.
        middleman_message = GolemMessageFrame(
            payload=self._get_deserialized_transaction_signing_request(),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)
        first_byte = 2 if raw_message[0] == 0 else raw_message[0]
        malformed_raw_message = bytes(bytearray([first_byte - 1
                                                 ])) + raw_message[1:]

        raw_message_received = self._prepare_and_execute_handle_connection(
            malformed_raw_message)

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=SIGNING_SERVICE_PUBLIC_KEY,
        )

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.InvalidFrameSignature)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
コード例 #8
0
ファイル: stream_async.py プロジェクト: robertdigital/concent
async def handle_frame_receive_async(reader: asyncio.StreamReader,
                                     public_key: bytes) -> AbstractFrame:
    raw_data = await reader.readuntil(FRAME_SEPARATOR)
    index = raw_data.index(FRAME_SEPARATOR)
    raw_data_without_separator = escape_decode_raw_message(raw_data[:index])
    deserialized_data = AbstractFrame.deserialize(raw_data_without_separator,
                                                  public_key)
    return deserialized_data
コード例 #9
0
    def test_that_serializing_and_deserializing_message_with_wrong_signature_should_raise_exception(
        self,
        middleman_message_type,
        payload,
    ):
        middleman_message = middleman_message_type(payload, self.request_id)
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        first_byte = 2 if raw_message[0] == 0 else raw_message[0]
        malformed_raw_message = bytes(bytearray([first_byte - 1
                                                 ])) + raw_message[1:]

        with pytest.raises(SignatureInvalidMiddlemanProtocolError):
            AbstractFrame.deserialize(
                malformed_raw_message,
                CONCENT_PUBLIC_KEY,
            )
コード例 #10
0
ファイル: signing_service.py プロジェクト: dad1x/concent
    def _authenticate(
        self,
        receive_frame_generator: Iterator[Optional[bytes]],
        tcp_socket: socket.socket
    ) -> None:
        """ Handles authentication challenge. """

        # Set timeout on socket, after which, if AuthenticationChallengeFrame is not received,
        # SigningService will have to reconnect.
        tcp_socket.settimeout(RECEIVE_AUTHENTICATION_CHALLENGE_TIMEOUT)

        # After establishing a TCP connection start listening for the AuthenticationChallengeFrame.
        try:
            raw_message_received = next(receive_frame_generator)
            authentication_challenge_frame = AbstractFrame.deserialize(
                raw_message_received,
                public_key=self.concent_public_key,
            )
            # If SigningService receive any other message that AuthenticationChallengeFrame
            # disconnect, log the incident and treat it as a failure to connect.
            if not isinstance(authentication_challenge_frame, AuthenticationChallengeFrame):
                logger.info(
                    f'SigningService received {type(authentication_challenge_frame)} instead of AuthenticationChallengeFrame.'
                )
                raise socket.error()
        # If received message is invalid or
        # if nothing was received in a predefined time,
        # disconnect, log the incident and treat it as a failure to connect.
        except (MiddlemanProtocolError, socket.timeout) as exception:
            logger.info(f'SigningService failed to receive AuthenticationChallengeFrame with exception: {exception}.')
            raise socket.error()

        # If you receive a valid challenge, sign it with the private key of the service and
        # send AuthenticationResponseFrame with signature as payload.
        authentication_response_frame = AuthenticationResponseFrame(
            payload=self._get_authentication_challenge_signature(
                authentication_challenge_frame.payload,
            ),
            request_id=authentication_challenge_frame.request_id,
        )

        try:
            send_over_stream(
                connection=tcp_socket,
                raw_message=authentication_response_frame,
                private_key=self.signing_service_private_key,
            )
        # If the server disconnects, log the incident and treat it as a failure to connect.
        except socket.error as exception:
            logger.info(
                f'MiddleMan server disconnects after receiving AuthenticationResponseFrame with exception: {exception}.'
            )
            raise socket.error()
        logger.info('Authentication successful. ')
コード例 #11
0
ファイル: test_stream.py プロジェクト: robertdigital/concent
    def test_that_exceeding_maximum_frame_length_should_treat_exceeded_frame_as_invalid(
            self, unused_tcp_port):
        first_middleman_message = GolemMessageFrame(Ping(), self.request_id)
        first_raw_message = append_frame_separator(
            escape_encode_raw_message(
                first_middleman_message.serialize(
                    private_key=CONCENT_PRIVATE_KEY)))
        second_middleman_message = AuthenticationChallengeFrame(
            payload=b'',
            request_id=100,
        )
        second_raw_message = append_frame_separator(
            escape_encode_raw_message(
                second_middleman_message.serialize(
                    private_key=CONCENT_PRIVATE_KEY)))

        assert len(first_raw_message) > len(second_raw_message) + 10

        with closing(socket.socket(socket.AF_INET,
                                   socket.SOCK_STREAM)) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     True)
            with closing(socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)) as client_socket:
                client_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, True)
                server_socket.bind(('127.0.0.1', unused_tcp_port))
                server_socket.listen(1)

                client_socket.connect(('127.0.0.1', unused_tcp_port))

                (connection, _address) = server_socket.accept()

                client_socket.send(first_raw_message)
                client_socket.send(second_raw_message)

                with mock.patch(
                        'middleman_protocol.stream.MAXIMUM_FRAME_LENGTH',
                        len(first_raw_message) - 10):
                    raw_message_received = next(
                        unescape_stream(connection=connection))

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=CONCENT_PUBLIC_KEY,
        )

        assertpy.assert_that(deserialized_message.request_id).is_equal_to(100)
コード例 #12
0
    def test_that_serializing_and_deserializing_message_should_preserve_original_data(
        self,
        middleman_message_type,
        payload,
    ):
        middleman_message = middleman_message_type(payload, self.request_id)
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)
        deserialized_message = AbstractFrame.deserialize(
            raw_message,
            public_key=CONCENT_PUBLIC_KEY,
        )

        assertpy.assert_that(deserialized_message).is_instance_of(
            middleman_message_type)
        assertpy.assert_that(deserialized_message.payload).is_instance_of(
            type(payload))
        assertpy.assert_that(deserialized_message.payload).is_equal_to(payload)
コード例 #13
0
ファイル: test_stream.py プロジェクト: robertdigital/concent
    def test_that_receiving_a_series_of_messages_should_be_handled_correctly(
            self, unused_tcp_port):
        payload = Ping()
        middleman_message = GolemMessageFrame(payload, self.request_id)

        with closing(socket.socket(socket.AF_INET,
                                   socket.SOCK_STREAM)) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     True)
            with closing(socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)) as client_socket:
                client_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, True)
                server_socket.bind(('127.0.0.1', unused_tcp_port))
                server_socket.listen(1)

                client_socket.connect(('127.0.0.1', unused_tcp_port))

                (connection, _address) = server_socket.accept()

                for _i in range(10):
                    send_over_stream(connection=client_socket,
                                     raw_message=middleman_message,
                                     private_key=CONCENT_PRIVATE_KEY)

                unescape_stream_generator = unescape_stream(
                    connection=connection)

                for _i in range(10):
                    raw_message_received = next(unescape_stream_generator)

                    deserialized_message = AbstractFrame.deserialize(
                        raw_message=raw_message_received,
                        public_key=CONCENT_PUBLIC_KEY,
                    )

                    assertpy.assert_that(deserialized_message).is_instance_of(
                        GolemMessageFrame)
                    assertpy.assert_that(
                        deserialized_message.payload).is_instance_of(Ping)
                    assertpy.assert_that(
                        deserialized_message.payload).is_equal_to(payload)
コード例 #14
0
    def test_that__handle_connection_should_send_error_frame_if_payload_type_is_invalid(
            self):
        # Prepare frame with malformed payload_type.
        middleman_message = GolemMessageFrame(
            payload=self._get_deserialized_transaction_signing_request(),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)

        payload_type_position = FRAME_SIGNATURE_BYTES_LENGTH + FRAME_REQUEST_ID_BYTES_LENGTH
        invalid_payload_type = 100

        # Replace bytes with payload length.
        malformed_raw_message = (
            raw_message[:payload_type_position] +
            bytes(bytearray([invalid_payload_type])) +
            raw_message[payload_type_position + FRAME_PAYLOAD_TYPE_LENGTH:])

        # Replace message signature
        new_signature = ecdsa_sign(
            CONCENT_PRIVATE_KEY,
            malformed_raw_message[FRAME_SIGNATURE_BYTES_LENGTH:])
        malformed_raw_message_with_new_signature = new_signature + malformed_raw_message[
            FRAME_SIGNATURE_BYTES_LENGTH:]

        raw_message_received = self._prepare_and_execute_handle_connection(
            malformed_raw_message_with_new_signature)

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=SIGNING_SERVICE_PUBLIC_KEY,
        )

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.InvalidFrame)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
コード例 #15
0
    def test_that_sending_message_over_tcp_socket_should_preserve_original_data(
        self,
        middleman_message_type,
        payload,
        unused_tcp_port,
    ):
        middleman_message = middleman_message_type(payload, self.request_id)

        with closing(socket.socket(socket.AF_INET,
                                   socket.SOCK_STREAM)) as server_socket:
            server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
                                     True)
            with closing(socket.socket(socket.AF_INET,
                                       socket.SOCK_STREAM)) as client_socket:
                client_socket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, True)
                server_socket.bind(('127.0.0.1', unused_tcp_port))
                server_socket.listen(1)

                client_socket.connect(('127.0.0.1', unused_tcp_port))

                (connection, _address) = server_socket.accept()

                send_over_stream(connection=client_socket,
                                 raw_message=middleman_message,
                                 private_key=CONCENT_PRIVATE_KEY)
                raw_message_received = next(
                    unescape_stream(connection=connection))

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=CONCENT_PUBLIC_KEY,
        )

        assertpy.assert_that(deserialized_message).is_instance_of(
            middleman_message_type)
        assertpy.assert_that(deserialized_message.payload).is_instance_of(
            type(payload))
        assertpy.assert_that(deserialized_message.payload).is_equal_to(payload)
コード例 #16
0
    def test_that__handle_connection_should_send_error_frame_if_payload_is_invalid(
            self):
        # Prepare frame payload which is not Golem message.
        middleman_message = GolemMessageFrame(
            payload=Ping(),
            request_id=99,
        )
        raw_message = middleman_message.serialize(
            private_key=CONCENT_PRIVATE_KEY)
        malformed_raw_message = (
            raw_message[:FRAME_PAYLOAD_STARTING_BYTE] +
            AbstractFrame.get_frame_format(
            ).signed_part_of_the_frame.payload.build(b'\x00' * 100))

        # Replace message signature
        new_signature = ecdsa_sign(
            CONCENT_PRIVATE_KEY,
            malformed_raw_message[FRAME_SIGNATURE_BYTES_LENGTH:])
        malformed_raw_message_with_new_signature = new_signature + malformed_raw_message[
            FRAME_SIGNATURE_BYTES_LENGTH:]

        raw_message_received = self._prepare_and_execute_handle_connection(
            malformed_raw_message_with_new_signature)

        deserialized_message = AbstractFrame.deserialize(
            raw_message=raw_message_received,
            public_key=SIGNING_SERVICE_PUBLIC_KEY,
        )

        assertpy.assert_that(
            deserialized_message.payload).is_instance_of(tuple)
        assertpy.assert_that(deserialized_message.payload).is_length(2)
        assertpy.assert_that(deserialized_message.payload[0]).is_equal_to(
            ErrorCode.InvalidPayload)
        assertpy.assert_that(deserialized_message.request_id).is_equal_to(
            REQUEST_ID_FOR_RESPONSE_FOR_INVALID_FRAME)
コード例 #17
0
ファイル: signing_service.py プロジェクト: dad1x/concent
    def _handle_connection(
        self,
        receive_frame_generator: Iterator[Optional[bytes]],
        tcp_socket: socket.socket
    ) -> None:
        """ Inner loop that handles data exchange over socket. """
        # Set socket back blocking mode.
        tcp_socket.setblocking(True)
        for raw_message_received in receive_frame_generator:
            try:
                middleman_message = AbstractFrame.deserialize(
                    raw_message_received,
                    public_key=self.concent_public_key,
                )
                # Heartbeat is received: connection is still active and Signing Service doesn't have to respond.
                if middleman_message.payload_type == PayloadType.HEARTBEAT:
                    continue

                if (
                    not middleman_message.payload_type == PayloadType.GOLEM_MESSAGE or
                    not isinstance(middleman_message.payload, TransactionSigningRequest)
                ):
                    raise SigningServiceUnexpectedMessageError

            # Is the frame correct according to the protocol? If not, error code is InvalidFrame.
            except (
                FrameInvalidMiddlemanProtocolError,
                PayloadTypeInvalidMiddlemanProtocolError,
                RequestIdInvalidTypeMiddlemanProtocolError,
            ) as exception:
                middleman_message_response = self._prepare_error_response(ErrorCode.InvalidFrame, exception)
            # Is frame signature correct? If not, error code is InvalidFrameSignature.
            except SignatureInvalidMiddlemanProtocolError as exception:
                middleman_message_response = self._prepare_error_response(ErrorCode.InvalidFrameSignature, exception)
            # Is the content of the message valid? Do types match the schema and all values are within allowed ranges?
            # If not, error code is InvalidPayload.
            # Can the payload be decoded as a Golem message? If not, error code is InvalidPayload.
            # Is payload message signature correct? If not, error code is InvalidPayload.
            except (MessageError, PayloadInvalidMiddlemanProtocolError) as exception:
                middleman_message_response = self._prepare_error_response(ErrorCode.InvalidPayload, exception)
            # Is frame type GOLEM_MESSAGE? If not, error code is UnexpectedMessage.
            # Is Golem message type TransactionSigningRequest? If not, error code is UnexpectedMessage.
            except SigningServiceUnexpectedMessageError as exception:
                middleman_message_response = self._prepare_error_response(ErrorCode.UnexpectedMessage, exception)
            # If received frame is correct, validate transaction.
            else:
                self._update_daily_transactions_limit_file_name()
                golem_message_response = self._get_signed_transaction(middleman_message.payload)
                if isinstance(golem_message_response, SignedTransaction):
                    transaction_sum_combined = self.signing_service_daily_transaction_sum_so_far + middleman_message.payload.value
                    if transaction_sum_combined > MAXIMUM_DAILY_THRESHOLD:
                        logger.warning(
                            f'Signing Service is unable to transact more then {MAXIMUM_DAILY_THRESHOLD} GNTB today.'
                            f'Transaction from {middleman_message.payload.from_address} rejected.'
                        )
                        self.notifier.send(
                            f'Signing Service is unable to transact more then {MAXIMUM_DAILY_THRESHOLD} GNTB today.'
                        )
                        golem_message_response = TransactionRejected(
                            reason=TransactionRejected.REASON.DailyLimitExceeded,
                            nonce=middleman_message.payload.nonce,
                        )
                    elif transaction_sum_combined > WARNING_DAILY_THRESHOLD:
                        logger.warning(f'Signing Service has signed transactions worth {transaction_sum_combined} GNTB today.')
                        self.notifier.send(
                            f'Signing Service has signed transactions worth {transaction_sum_combined} GNTB today.'
                        )
                        self._add_payload_value_to_daily_transactions_sum(transaction_sum_combined)
                    else:
                        self._add_payload_value_to_daily_transactions_sum(transaction_sum_combined)
                golem_message_response.sign_message(private_key=self.signing_service_private_key)
                middleman_message_response = GolemMessageFrame(
                    payload=golem_message_response,
                    request_id=middleman_message.request_id,
                )

            logger.info(
                f'Sending Middleman protocol message with request_id: {middleman_message_response.request_id}.'
            )
            send_over_stream(
                connection=tcp_socket,
                raw_message=middleman_message_response,
                private_key=self.signing_service_private_key,
            )