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
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
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)
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
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)
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]