def test_official_auth_header_packet_preparation(tag,
                                                 auth_tag,
                                                 id_nonce,
                                                 encoded_message,
                                                 local_private_key,
                                                 auth_response_key,
                                                 encryption_key,
                                                 ephemeral_public_key,
                                                 auth_message_rlp):
    message_type_id = encoded_message[0]
    message_type = default_message_type_registry[message_type_id]
    message = rlp.decode(encoded_message[1:], message_type)
    assert message.to_bytes() == encoded_message

    id_nonce_signature = V4IdentityScheme.create_id_nonce_signature(
        id_nonce=id_nonce,
        ephemeral_public_key=ephemeral_public_key,
        private_key=local_private_key,
    )

    packet = AuthHeaderPacket.prepare(
        tag=tag,
        auth_tag=auth_tag,
        id_nonce=id_nonce,
        message=message,
        initiator_key=encryption_key,
        id_nonce_signature=id_nonce_signature,
        auth_response_key=auth_response_key,
        enr=None,
        ephemeral_public_key=ephemeral_public_key,
    )
    packet_wire_bytes = packet.to_wire_bytes()
    assert packet_wire_bytes == auth_message_rlp
def test_auth_header_preparation_without_enr(tag,
                                             auth_tag,
                                             id_nonce,
                                             initiator_key,
                                             auth_response_key,
                                             ephemeral_public_key):
    message = PingMessage(
        request_id=5,
        enr_seq=1,
    )
    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=None,
        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 decoded_auth_response[2] == []
Example #3
0
def test_oversize_auth_header_packet_encoding():
    auth_header = AuthHeader(
        auth_tag=b"\x00" * NONCE_SIZE,
        auth_scheme_name=AUTH_SCHEME_NAME,
        ephemeral_pubkey=b"\x00" * 32,
        encrypted_auth_response=32,
    )
    header_size = len(rlp.encode(auth_header))
    encrypted_message_size = MAX_PACKET_SIZE - TAG_SIZE - header_size + 1
    encrypted_message = b"\x00" * encrypted_message_size
    packet = AuthHeaderPacket(
        tag=b"\x00" * TAG_SIZE,
        auth_header=auth_header,
        encrypted_message=encrypted_message,
    )
    with pytest.raises(ValidationError):
        packet.to_wire_bytes()
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
Example #5
0
    def complete_handshake(self, response_packet: Packet) -> HandshakeResult:
        if not self.is_response_packet(response_packet):
            raise ValueError(
                f"Packet {response_packet} is not the expected response packet"
            )
        if not isinstance(response_packet, WhoAreYouPacket):
            raise TypeError(
                "Invariant: Only WhoAreYou packets are valid responses")
        who_are_you_packet = response_packet

        # compute session keys
        (
            ephemeral_private_key,
            ephemeral_public_key,
        ) = self.identity_scheme.create_handshake_key_pair()

        remote_public_key_object = PublicKey.from_compressed_bytes(
            self.remote_enr.public_key)
        remote_public_key_uncompressed = remote_public_key_object.to_bytes()
        session_keys = self.identity_scheme.compute_session_keys(
            local_private_key=ephemeral_private_key,
            remote_public_key=remote_public_key_uncompressed,
            local_node_id=self.local_enr.node_id,
            remote_node_id=self.remote_node_id,
            id_nonce=who_are_you_packet.id_nonce,
            is_locally_initiated=True,
        )

        # prepare response packet
        id_nonce_signature = self.identity_scheme.create_id_nonce_signature(
            id_nonce=who_are_you_packet.id_nonce,
            ephemeral_public_key=ephemeral_public_key,
            private_key=self.local_private_key,
        )

        if who_are_you_packet.enr_sequence_number < self.local_enr.sequence_number:
            enr = self.local_enr
        else:
            enr = None

        auth_header_packet = AuthHeaderPacket.prepare(
            tag=self.tag,
            auth_tag=get_random_auth_tag(),
            id_nonce=who_are_you_packet.id_nonce,
            message=self.initial_message,
            initiator_key=session_keys.encryption_key,
            id_nonce_signature=id_nonce_signature,
            auth_response_key=session_keys.auth_response_key,
            enr=enr,
            ephemeral_public_key=ephemeral_public_key,
        )

        return HandshakeResult(
            session_keys=session_keys,
            enr=None,
            message=None,
            auth_header_packet=auth_header_packet,
        )
Example #6
0
    def decrypt_and_validate_auth_response(
        self,
        auth_header_packet: AuthHeaderPacket,
        auth_response_key: AES128Key,
        id_nonce: IDNonce,
    ) -> Optional[ENR]:
        try:
            id_nonce_signature, enr = auth_header_packet.decrypt_auth_response(
                auth_response_key)
        except DecryptionError as error:
            raise HandshakeFailure(
                "Unable to decrypt auth response") from error
        except ValidationError as error:
            raise HandshakeFailure("Invalid auth response content") from error

        # validate ENR if present
        if enr is None:
            if self.remote_enr is None:
                raise HandshakeFailure("Peer failed to send their ENR")
            else:
                current_remote_enr = self.remote_enr
        else:
            try:
                enr.validate_signature()
            except ValidationError as error:
                raise HandshakeFailure(
                    "ENR in auth response contains invalid signature"
                ) from error

            if self.remote_enr is not None:
                if enr.sequence_number <= self.remote_enr.sequence_number:
                    raise HandshakeFailure(
                        "ENR in auth response is not newer than what we already have"
                    )

            if enr.node_id != self.remote_node_id:
                raise HandshakeFailure(
                    f"ENR received from peer belongs to different node ({encode_hex(enr.node_id)} "
                    f"instead of {encode_hex(self.remote_node_id)})")

            current_remote_enr = enr

        try:
            self.identity_scheme.validate_id_nonce_signature(
                signature=id_nonce_signature,
                id_nonce=id_nonce,
                ephemeral_public_key=auth_header_packet.auth_header.
                ephemeral_public_key,
                public_key=current_remote_enr.public_key,
            )
        except ValidationError as error:
            raise HandshakeFailure(
                "Invalid id nonce signature in auth response") from error

        return enr
Example #7
0
 def decrypt_and_validate_message(self,
                                  auth_header_packet: AuthHeaderPacket,
                                  decryption_key: AES128Key) -> BaseMessage:
     try:
         return auth_header_packet.decrypt_message(decryption_key)
     except DecryptionError as error:
         raise HandshakeFailure(
             "Failed to decrypt message in AuthHeader packet with newly established session keys"
         ) from error
     except ValidationError as error:
         raise HandshakeFailure("Received invalid message") from error
Example #8
0
def test_auth_header_packet_encoding_decoding(tag,
                                              auth_tag,
                                              ephemeral_pubkey,
                                              encrypted_auth_response,
                                              encrypted_message_size):
    auth_header = AuthHeader(
        auth_tag=auth_tag,
        auth_scheme_name=AUTH_SCHEME_NAME,
        ephemeral_pubkey=ephemeral_pubkey,
        encrypted_auth_response=encrypted_auth_response,
    )
    encrypted_message = b"\x00" * encrypted_message_size
    original_packet = AuthHeaderPacket(
        tag=tag,
        auth_header=auth_header,
        encrypted_message=encrypted_message,
    )
    encoded_packet = original_packet.to_wire_bytes()
    decoded_packet = decode_message_packet(encoded_packet)
    assert isinstance(decoded_packet, AuthHeaderPacket)
    assert decoded_packet == original_packet
def test_auth_header_message_decryption(tag, auth_tag, id_nonce, initiator_key,
                                        auth_response_key,
                                        ephemeral_public_key, enr, message):
    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)

    decrypted_message = packet.decrypt_message(initiator_key)
    assert decrypted_message == message
Example #10
0
def test_invalid_auth_header_decryption_with_wrong_key(tag, auth_tag, id_nonce,
                                                       initiator_key,
                                                       ephemeral_public_key,
                                                       message):
    id_nonce_signature = b"\x00" * 32
    encryption_key = b"\x00" * AES128_KEY_SIZE
    decryption_key = b"\x11" * AES128_KEY_SIZE
    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=encryption_key,
        enr=None,
        ephemeral_public_key=ephemeral_public_key)
    with pytest.raises(DecryptionError):
        packet.decrypt_auth_response(decryption_key)
Example #11
0
def test_auth_header_decryption_without_enr(tag, auth_tag, id_nonce,
                                            initiator_key, auth_response_key,
                                            ephemeral_public_key, message):
    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=None,
        ephemeral_public_key=ephemeral_public_key)

    recovered_id_nonce_signature, recovered_enr = packet.decrypt_auth_response(
        auth_response_key)
    assert recovered_id_nonce_signature == id_nonce_signature
    assert recovered_enr is None