async def recv_enr_response(self, node: NodeAPI, payload: Sequence[Any], msg_hash: Hash32) -> None: # The enr_response payload should have at least two elements: request_hash, enr. if len(payload) < 2: self.logger.warning( 'Ignoring ENR_RESPONSE msg with invalid payload: %s', payload) return token, serialized_enr = payload[:2] try: enr = ENR.deserialize(serialized_enr) except DeserializationError as error: raise ValidationError( "ENR in response is not properly encoded") from error try: channel = self.enr_response_channels.get_channel(node) except KeyError: self.logger.debug("Unexpected ENR_RESPONSE from %s", node) return enr.validate_signature() self.logger.debug( "Received ENR %s (%s) with expected response token: %s", enr, enr.items(), encode_hex(token)) try: await channel.send((enr, token)) except trio.BrokenResourceError: # This means the receiver has already closed, probably because it timed out. pass
def decrypt_auth_response( self, auth_response_key: AES128Key) -> Tuple[bytes, Optional[ENR]]: """Extract id nonce signature and optional ENR from auth header packet.""" plain_text = aesgcm_decrypt( key=auth_response_key, nonce=ZERO_NONCE, cipher_text=self.auth_header.encrypted_auth_response, authenticated_data=b"", ) try: decoded_rlp = rlp.decode(plain_text) except DecodingError: raise ValidationError( f"Auth response does not contain valid RLP: {encode_hex(plain_text)}" ) if not is_list_like(decoded_rlp): raise ValidationError( f"Auth response contains bytes instead of list: {encode_hex(decoded_rlp)}" ) if len(decoded_rlp) != 3: raise ValidationError( f"Auth response is a list of {len(decoded_rlp)} instead of three elements" ) version_bytes, id_nonce_signature, serialized_enr = decoded_rlp if not is_bytes(version_bytes): raise ValidationError( f"Version is a list instead of big endian encoded integer: {version_bytes}" ) version_int = big_endian_to_int(version_bytes) if version_int != AUTH_RESPONSE_VERSION: raise ValidationError( f"Expected auth response version {AUTH_RESPONSE_VERSION}, but got {version_int}" ) if not is_bytes(id_nonce_signature): raise ValidationError( f"Id nonce signature is a list instead of bytes: {id_nonce_signature}" ) if not is_list_like(serialized_enr): raise ValidationError( f"ENR is bytes instead of list: {encode_hex(serialized_enr)}") if len(serialized_enr) == 0: enr = None else: try: enr = ENR.deserialize(serialized_enr) except DeserializationError as error: raise ValidationError( "ENR in auth response is not properly encoded") from error return id_nonce_signature, enr
def test_auth_header_preparation(tag, auth_tag, initiator_key, auth_response_key, ephemeral_pubkey): enr = ENR( sequence_number=1, signature=b"", kv_pairs={ b"id": b"v4", } ) message = PingMessage( request_id=5, enr_seq=enr.sequence_number, ) id_nonce_signature = b"\x00" * 32 packet = AuthHeaderPacket.prepare( tag=tag, auth_tag=auth_tag, message=message, initiator_key=initiator_key, id_nonce_signature=id_nonce_signature, auth_response_key=auth_response_key, enr=enr, ephemeral_pubkey=ephemeral_pubkey ) assert packet.tag == tag assert packet.auth_header.auth_tag == auth_tag assert packet.auth_header.auth_scheme_name == AUTH_SCHEME_NAME assert packet.auth_header.ephemeral_pubkey == ephemeral_pubkey decrypted_auth_response = aesgcm_decrypt( key=auth_response_key, nonce=ZERO_NONCE, cipher_text=packet.auth_header.encrypted_auth_response, authenticated_data=tag, ) decoded_auth_response = rlp.decode(decrypted_auth_response) assert is_list_like(decoded_auth_response) and len(decoded_auth_response) == 2 assert decoded_auth_response[0] == id_nonce_signature assert ENR.deserialize(decoded_auth_response[1]) == enr decrypted_message = aesgcm_decrypt( key=initiator_key, nonce=auth_tag, cipher_text=packet.encrypted_message, authenticated_data=b"".join(( tag, rlp.encode(packet.auth_header), )) ) assert decrypted_message[0] == message.message_type assert rlp.decode(decrypted_message[1:], PingMessage) == message
def decrypt_auth_response( self, auth_response_key: AES128Key) -> Tuple[bytes, Optional[ENR]]: """Extract id nonce signature and optional ENR from auth header packet.""" plain_text = aesgcm_decrypt( key=auth_response_key, nonce=ZERO_NONCE, cipher_text=self.auth_header.encrypted_auth_response, authenticated_data=self.tag, ) try: decoded_rlp = rlp.decode(plain_text) except DecodingError as error: raise ValidationError( f"Auth response does not contain valid RLP: {encode_hex(plain_text)}" ) if not is_list_like(decoded_rlp): raise ValidationError( f"Auth response contains bytes instead of list: {encode_hex(decoded_rlp)}" ) if len(decoded_rlp) != 2: raise ValidationError( f"Auth response is a list of {len(decoded_rlp)} instead of two elements" ) id_nonce_signature, serialized_enr = decoded_rlp if not is_bytes(id_nonce_signature): raise ValidationError( f"Id nonce signature is a list instead of bytes: {id_nonce_signature}" ) if not is_list_like(serialized_enr): raise ValidationError( f"ENR is bytes instead of list: {encode_hex(serialized_enr)}") if len(serialized_enr) == 0: enr = None else: try: enr = ENR.deserialize(serialized_enr) except DeserializationError as error: raise ValidationError( "ENR in auth response is not properly encoded") from error return id_nonce_signature, enr