示例#1
0
def parse_frame_header(header_bytes):
    """
    Parses frame header
    :param header_bytes: frame header bytes
    :return: tuple (body size, protocol id, sequence id, total payload size)
    """

    sequence_id = None
    total_payload_size = None

    body_size = struct.unpack(">I", b"\x00" + header_bytes[:3])[0]

    try:
        header_data = rlp.decode(header_bytes[3:],
                                 sedes=Frame.header_data_serializer,
                                 strict=False)
    except rlp.RLPException:
        raise ParseError("Invalid rlp data in frame header")

    protocol_id = header_data[0]

    if len(header_data) > 1:
        sequence_id = header_data[1]

    if len(header_data) == 3:
        total_payload_size = header_data[2]

    return body_size, protocol_id, sequence_id, total_payload_size
示例#2
0
    def peek_message(self, input_buffer):
        """
        Peeks message from input frame
        :param input_buffer: input buffer
        :return: tuple (flag if full message is received, message type)
        """

        if not isinstance(input_buffer, InputBuffer):
            raise ValueError("Expected type InputBuffer")

        if self._full_message_received:
            raise ValueError("Get full message before trying to peek another one")

        if not self._receiving_frame and input_buffer.length >= eth_common_constants.FRAME_HDR_TOTAL_LEN:
            enc_header_bytes = input_buffer.remove_bytes(eth_common_constants.FRAME_HDR_TOTAL_LEN)
            header_bytes = self._rlpx_cipher.decrypt_frame_header(enc_header_bytes)
            body_size, protocol_id, sequence_id, total_payload_len = frame_utils.parse_frame_header(header_bytes)

            self._current_frame_body_size = body_size
            self._current_frame_protocol_id = protocol_id
            self._current_frame_size = frame_utils.get_full_frame_size(body_size)
            self._current_frame_enc_body_size = self._current_frame_size - eth_common_constants.FRAME_HDR_TOTAL_LEN
            self._current_frame_sequence_id = sequence_id

            if sequence_id == 0 and total_payload_len is not None:
                self._chunked_frames_in_progress = True
                self._chunked_frames_total_body_size = total_payload_len

            self._receiving_frame = True

        if self._receiving_frame and input_buffer.length >= self._current_frame_enc_body_size:
            frame_enc_body_bytes = input_buffer.remove_bytes(self._current_frame_enc_body_size)

            body = self._rlpx_cipher.decrypt_frame_body(frame_enc_body_bytes, self._current_frame_body_size)

            msg_type_is_expected = not self._chunked_frames_in_progress or self._current_frame_sequence_id == 0
            payload, msg_type = frame_utils.parse_frame_body(body, msg_type_is_expected)

            if msg_type_is_expected:
                self._current_msg_type = msg_type

            self._payload_buffer.add_bytes(bytearray(rlp_utils.str_to_bytes(payload)))

            if not self._chunked_frames_in_progress:
                self._full_message_received = True
            else:
                self._chunked_frames_body_size_received += len(body)

                if self._chunked_frames_body_size_received > self._chunked_frames_total_body_size:
                    raise ParseError("Expected total body length for frame message is {0} but received {1}"
                                     .format(self._chunked_frames_total_body_size,
                                             self._chunked_frames_body_size_received))

                if self._chunked_frames_body_size_received == self._chunked_frames_total_body_size:
                    self._full_message_received = True

            self._receiving_frame = False

        return self._full_message_received, self._current_msg_type
示例#3
0
def consume_length_prefix(rlp, start):
    """
    Ethereum RLP Utils:
    Read a length prefix from an RLP string.

    :param rlp: the rlp string to read from
    :param start: the position at which to start reading
    :returns: a tuple ``(type, length, end)``, where ``type`` is either ``str``
              or ``list`` depending on the type of the following payload,
              ``length`` is the length of the payload in bytes, and ``end`` is
              the position of the first payload byte in the rlp string
    """
    if not isinstance(rlp, memoryview):
        raise TypeError(
            "Only memoryview is allowed for RLP content for best performance. Type provided was: {}"
            .format(type(rlp)))

    if start is None:
        raise ValueError("Argument start is required")

    first_byte = safe_ord(rlp[start])
    if first_byte < 128:  # single byte
        return (str, 1, start)
    elif first_byte < 128 + 56:  # short string
        if first_byte - 128 == 1 and safe_ord(rlp[start + 1]) < 128:
            raise ParseError(
                "Encoded as short string although single byte was possible")
        return (str, first_byte - 128, start + 1)
    elif first_byte < 192:  # long string
        long_length = first_byte - 128 - 56 + 1
        if rlp[start + 1:start + 2] == b"\x00":
            raise ParseError("Length starts with zero bytes")
        length = big_endian_to_int(rlp[start + 1:start + 1 + long_length])
        if length < 56:
            raise ParseError("Long string prefix used for short string")
        return (str, length, start + 1 + long_length)
    elif first_byte < 192 + 56:  # short list
        return (list, first_byte - 192, start + 1)
    else:  # long list
        long_length = first_byte - 192 - 56 + 1
        if rlp[start + 1:start + 2] == b"\x00":
            raise ParseError("Length starts with zero bytes")
        length = big_endian_to_int(rlp[start + 1:start + 1 + long_length])
        if length < 56:
            raise ParseError("Long list prefix used for short list")
        return (list, length, start + 1 + long_length)
示例#4
0
def eth_bdn_tx_to_bx_tx(
        raw_tx: Union[bytes, bytearray, memoryview],
        network_num: int,
        transaction_flag: Optional[TransactionFlag] = None) -> TxMessage:
    if isinstance(raw_tx, bytes):
        raw_tx = bytearray(raw_tx)
    bx_tx, tx_item_length, tx_item_start = eth_common_utils.raw_tx_to_bx_tx(
        raw_tx, 0, network_num, transaction_flag)
    if tx_item_length + tx_item_start != len(raw_tx):
        raise ParseError(raw_tx)
    return bx_tx
示例#5
0
def eth_bdn_tx_to_bx_tx(
        raw_tx: Union[bytes, bytearray, memoryview],
        network_num: int,
        transaction_flag: Optional[TransactionFlag] = None,
        account_id: str = constants.DECODED_EMPTY_ACCOUNT_ID) -> TxMessage:
    if isinstance(raw_tx, bytes):
        raw_tx = bytearray(raw_tx)
    bx_tx, _, tx_item_length, tx_item_start = eth_common_utils.raw_tx_to_bx_tx(
        raw_tx, 0, network_num, transaction_flag, account_id)
    if tx_item_length + tx_item_start != len(raw_tx):
        raise ParseError(raw_tx)
    return bx_tx
    def create_message_from_buffer(self, buf):
        """
        Parses a full message from a buffer based on its command into one of the loaded message types.
        """
        if len(buf) < self.base_message_type.HEADER_LENGTH:
            raise ParseError("Message was too short to be parsed. Raw data: {0}".format(buf))

        unpacked_args = self.base_message_type.unpack(buf)
        self.base_message_type.validate_payload(buf, unpacked_args)

        command = unpacked_args[0]
        if command not in self.message_type_mapping:
            raise UnrecognizedCommandError("Message not recognized: {0}".format(command), convert.bytes_to_hex(buf))

        return self.create_message(command, buf, unpacked_args)
    def create_message_from_buffer(self, buf):
        """
        Parses a full message from a buffer based on its command into one of the loaded message types.
        """

        if self._expected_msg_type is not None:
            return self.base_message_type.initialize_class(
                RawEthProtocolMessage, buf, None)

        if len(buf) != 0:
            raise ParseError(
                "All bytes are expected to be already read by self._framed_input_buffer"
            )

        message_bytes, command = self._framed_input_buffer.get_full_message()

        return self.create_message(command, message_bytes)
示例#8
0
    def decrypt_frame_body(self, data, body_size):
        """
        Decrypts frame body
        :param data: frame data
        :param body_size: body size
        :return: decrypted frame body
        """

        if not self._is_ready:
            raise CipherNotInitializedError(
                "failed to decrypt frame body, the cipher was never initialized!"
            )

        # frame-size: 3-byte integer size of frame, big endian encoded (excludes padding)
        # frame relates to body w/o padding w/o mac

        read_size = crypto_utils.get_padded_len_16(body_size)

        if not len(data) >= read_size + eth_common_constants.FRAME_MAC_LEN:
            raise ParseError("Insufficient body length")

        frame_cipher_text = data[:read_size]
        frame_mac = data[read_size:read_size +
                         eth_common_constants.FRAME_MAC_LEN]

        # ingres-mac.update(aes(mac-secret,ingres-mac) ^
        # left128(ingres-mac.update(frame-ciphertext).digest))
        frame_mac_seed = self.mac_ingress(frame_cipher_text)
        expected_frame_mac = self.mac_ingress(
            crypto_utils.string_xor(
                self._mac_enc(
                    self.mac_ingress()[:eth_common_constants.FRAME_MAC_LEN]),
                frame_mac_seed[:eth_common_constants.FRAME_MAC_LEN])
        )[:eth_common_constants.FRAME_MAC_LEN]

        if not frame_mac == expected_frame_mac:
            raise AuthenticationError("Invalid frame mac")

        return self.aes_decode(frame_cipher_text)[:body_size]