Example #1
0
 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
Example #2
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
Example #3
0
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
Example #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=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