def decrypt_auth_response( self, auth_response_key: AES128Key ) -> Tuple[bytes, Optional[ENRAPI]]: """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
async def test_enr_response_handler_does_not_crash_on_invalid_responses(): discovery = MockDiscoveryService([]) token = b'' invalid_enr = b'garbage' payload = [token, invalid_enr] await discovery.recv_enr_response(discovery.this_node, payload, b'') enr = ENRFactory() enr._kv_pairs.pop(b'secp256k1') with pytest.raises(ValidationError): ENR.deserialize(ENR.serialize(enr)) payload = [token, ENR.serialize(enr)] await discovery.recv_enr_response(discovery.this_node, payload, b'') enr = ENRFactory() enr._signature = b'garbage' with pytest.raises(eth_keys.exceptions.ValidationError): enr.validate_signature() payload = [token, ENR.serialize(enr)] await discovery.recv_enr_response(discovery.this_node, payload, b'')
def test_auth_header_preparation( tag, auth_tag, id_nonce, initiator_key, auth_response_key, ephemeral_public_key ): enr = ENR( sequence_number=1, signature=b"", kv_pairs={b"id": b"v4", b"secp256k1": b"\x02" * 33}, ) 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, id_nonce=id_nonce, message=message, initiator_key=initiator_key, id_nonce_signature=id_nonce_signature, auth_response_key=auth_response_key, enr=enr, ephemeral_public_key=ephemeral_public_key, ) assert packet.tag == tag assert packet.auth_header.auth_tag == auth_tag assert packet.auth_header.id_nonce == id_nonce assert packet.auth_header.auth_scheme_name == AUTH_SCHEME_NAME assert packet.auth_header.ephemeral_public_key == ephemeral_public_key decrypted_auth_response = aesgcm_decrypt( key=auth_response_key, nonce=ZERO_NONCE, cipher_text=packet.auth_header.encrypted_auth_response, authenticated_data=b"", ) decoded_auth_response = rlp.decode(decrypted_auth_response) assert is_list_like(decoded_auth_response) and len(decoded_auth_response) == 3 assert decoded_auth_response[0] == int_to_big_endian(AUTH_RESPONSE_VERSION) assert decoded_auth_response[1] == id_nonce_signature assert ENR.deserialize(decoded_auth_response[2]) == enr decrypted_message = aesgcm_decrypt( key=initiator_key, nonce=auth_tag, cipher_text=packet.encrypted_message, authenticated_data=tag, ) assert decrypted_message[0] == message.message_type assert rlp.decode(decrypted_message[1:], PingMessage) == message