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
Beispiel #2
0
def _decrypt_message(key: AES128Key,
                     auth_tag: Nonce,
                     encrypted_message: bytes,
                     authenticated_data: bytes,
                     message_type_registry: MessageTypeRegistry,
                     ) -> BaseMessage:
    plain_text = aesgcm_decrypt(
        key=key,
        nonce=auth_tag,
        cipher_text=encrypted_message,
        authenticated_data=authenticated_data,
    )

    try:
        message_type = plain_text[0]
    except IndexError:
        raise ValidationError("Decrypted message is empty")

    try:
        message_class = message_type_registry[message_type]
    except KeyError:
        raise ValidationError(f"Unknown message type {message_type}")

    try:
        message = rlp.decode(plain_text[1:], message_class)
    except DecodingError as error:
        raise ValidationError("Encrypted message does not contain valid RLP") from error

    return message
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] == []
Beispiel #4
0
    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
Beispiel #5
0
def test_decryption_with_wrong_inputs():
    key = AES128Key(b"\x00" * 16)
    nonce = Nonce(b"\x11" * 12)
    plain_text = b"\x33" * 5
    aad = b"\x44" * 5
    cipher_text = aesgcm_encrypt(key, nonce, plain_text, aad)

    assert aesgcm_decrypt(key, nonce, cipher_text, aad) == plain_text
    with pytest.raises(ValidationError):
        aesgcm_decrypt(b"", nonce, cipher_text, aad)
    with pytest.raises(ValidationError):
        aesgcm_decrypt(key, b"", cipher_text, aad)
    with pytest.raises(DecryptionError):
        aesgcm_decrypt(key, nonce, b"", aad)
    with pytest.raises(DecryptionError):
        aesgcm_decrypt(key, nonce, cipher_text, b"")
Beispiel #6
0
    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
def test_auth_tag_packet_preparation(tag, auth_tag, key):
    message = PingMessage(
        request_id=5,
        enr_seq=3,
    )

    packet = AuthTagPacket.prepare(
        tag=tag,
        auth_tag=auth_tag,
        message=message,
        key=key,
    )
    assert packet.tag == tag
    assert packet.auth_tag == auth_tag
    decrypted_message = aesgcm_decrypt(
        key=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
Beispiel #8
0
def test_roundtrip(key, nonce, plain_text, aad):
    cipher_text = aesgcm_encrypt(key, nonce, plain_text, aad)
    plain_text_recovered = aesgcm_decrypt(key, nonce, cipher_text, aad)
    assert plain_text_recovered == plain_text
def test_encryption_official(key, nonce, plain_text, aad, cipher_text):
    encrypted = aesgcm_encrypt(key, nonce, plain_text, aad)
    assert encrypted == cipher_text
    assert aesgcm_decrypt(key, nonce, cipher_text, aad) == plain_text