Пример #1
0
    def from_wire_bytes(cls, data: bytes) -> "HandshakePacket":
        stream = BytesIO(data)
        auth_data_head = HandshakeHeader.from_wire_bytes(
            stream.read(HANDSHAKE_HEADER_PACKET_SIZE))
        expected_length = (HANDSHAKE_HEADER_PACKET_SIZE +
                           auth_data_head.signature_size +
                           auth_data_head.ephemeral_key_size)
        if len(data) < expected_length:
            raise DecodingError(
                f"Invalid length for HandshakePacket: "
                f"expected={expected_length}  actual={len(data)}  "
                f"data={data.hex()}")
        id_signature = stream.read(auth_data_head.signature_size)
        ephemeral_public_key = stream.read(auth_data_head.ephemeral_key_size)
        enr_bytes = stream.read()
        if len(enr_bytes) > 0:
            try:
                enr = rlp.decode(enr_bytes, sedes=ENRSedes)
            except rlp.DecodingError as err:
                # re-raise using the local library DecodingError instead of the
                # rlp one
                raise DecodingError(str(err)) from err
        else:
            enr = None

        return cls(
            auth_data_head,
            id_signature,
            ephemeral_public_key,
            enr,
        )
Пример #2
0
def decode_message(data: bytes) -> AlexandriaMessage[Any]:
    message_id = data[0]
    try:
        message_class = MESSAGE_REGISTRY[message_id]
    except KeyError:
        raise DecodingError(f"Unknown message type: id={message_id}")

    try:
        payload_args = ssz.decode(data[1:], sedes=message_class.sedes)
    except DeserializationError as err:
        raise DecodingError(str(err)) from err

    return message_class.from_payload_args(payload_args)
Пример #3
0
def decode_packet(
    data: bytes,
    local_node_id: NodeID,
) -> AnyPacket:
    iv = data[:16]
    masking_key = cast(AES128Key, local_node_id[:16])
    cipher_text_stream = aesctr_decrypt_stream(masking_key, iv, data[16:])

    # Decode the header
    header_bytes = bytes(take(HEADER_PACKET_SIZE, cipher_text_stream))
    header = Header.from_wire_bytes(header_bytes)

    auth_data_bytes = bytes(take(header.auth_data_size, cipher_text_stream))
    auth_data: Union[MessagePacket, WhoAreYouPacket, HandshakePacket]
    if header.flag == 0:
        auth_data = MessagePacket.from_wire_bytes(auth_data_bytes)
    elif header.flag == 1:
        auth_data = WhoAreYouPacket.from_wire_bytes(auth_data_bytes)
    elif header.flag == 2:
        auth_data = HandshakePacket.from_wire_bytes(auth_data_bytes)
    else:
        raise DecodingError(f"Unable to decode datagram: {data.hex()}", data)

    message_cipher_text = data[16 + HEADER_PACKET_SIZE +
                               header.auth_data_size:]

    return cast(
        AnyPacket,
        Packet(iv,
               header,
               auth_data,
               message_cipher_text,
               dest_node_id=local_node_id),
    )
Пример #4
0
 def from_wire_bytes(cls, data: bytes) -> "Header":
     if len(data) != HEADER_PACKET_SIZE:
         raise DecodingError(
             f"Invalid length for Header: actual={len(data)}  "
             f"expected={HEADER_PACKET_SIZE}  data={data.hex()}")
     stream = BytesIO(data)
     protocol_id = stream.read(6)
     if protocol_id != PROTOCOL_ID:
         raise DecodingError(f"Invalid protocol: {protocol_id!r}")
     version = stream.read(2)
     if version != b"\x00\x01":
         raise DecodingError(f"Unsupported version: {version!r}")
     flag = stream.read(1)[0]
     aes_gcm_nonce = Nonce(stream.read(12))
     auth_data_size = int.from_bytes(stream.read(2), "big")
     return cls(protocol_id, version, flag, aes_gcm_nonce, auth_data_size)
Пример #5
0
async def read_json(
    socket: trio.socket.SocketType,
    buffer: io.StringIO,
    decoder: json.JSONDecoder = decoder,
) -> JSON:
    request: JSON

    while True:
        data = await socket.recv(1024)
        buffer.write(data.decode())

        bad_prefix, raw_request = strip_non_json_prefix(buffer.getvalue())
        if bad_prefix:
            logger.info("Client started request with non json data: %r",
                        bad_prefix)
            await write_error(socket, f"Cannot parse json: {bad_prefix}")
            raise DecodingError(f"Invalid JSON payload: prefix={bad_prefix}")

        if len(raw_request) > MAXIMUM_RPC_PAYLOAD_SIZE:
            error_msg = (
                f"RPC payload exceeds maximum size: {len(raw_request)} "
                f"> {MAXIMUM_RPC_PAYLOAD_SIZE}")
            await write_error(socket, error_msg)
            raise DecodingError(error_msg)

        try:
            request, offset = decoder.raw_decode(raw_request)
        except json.JSONDecodeError:
            # invalid json request, keep reading data until a valid json is formed
            if raw_request:
                logger.debug(
                    "Invalid JSON, waiting for rest of message: %r",
                    raw_request,
                )
            else:
                await trio.sleep(0.01)
            continue

        buffer.seek(0)
        buffer.write(raw_request[offset:])
        buffer.truncate()

        break

    return request
Пример #6
0
    async def _request(
        self,
        node_id: NodeID,
        endpoint: Endpoint,
        request: AlexandriaMessage[Any],
        response_class: Type[TAlexandriaMessage],
        request_id: Optional[bytes] = None,
    ) -> TAlexandriaMessage:
        #
        # Request ID Shenanigans
        #
        # We need a subscription API for alexandria messages in order to be
        # able to cleanly implement functionality like responding to ping
        # messages with pong messages.
        #
        # We accomplish this by monitoring all TALKREQUEST and TALKRESPONSE
        # messages that occur on the alexandria protocol. For TALKREQUEST based
        # messages we can naively monitor incoming messages and match them
        # against the protocol_id. For the TALKRESPONSE however, the only way
        # for us to know that an incoming message belongs to this protocol is
        # to match it against an in-flight request (which we implicitely know
        # is part of the alexandria protocol).
        #
        # In order to do this, we need to know which request ids are active for
        # Alexandria protocol messages. We do this by using a separate
        # RequestTrackerAPI. The `request_id` for messages is still acquired
        # from the core tracker located on the base protocol `ClientAPI`.  We
        # then feed this into our local tracker, which allows us to query it
        # upon receiving an incoming TALKRESPONSE to see if the response is to
        # a message from this protocol.
        if request.type != AlexandriaMessageType.REQUEST:
            raise TypeError("%s is not of type REQUEST", request)
        if request_id is None:
            request_id = self.network.client.request_tracker.get_free_request_id(
                node_id)
        request_data = request.to_wire_bytes()
        if len(request_data) > MAX_PAYLOAD_SIZE:
            raise Exception(
                "Payload too large:  payload=%d  max_size=%d",
                len(request_data),
                MAX_PAYLOAD_SIZE,
            )
        with self.request_tracker.reserve_request_id(node_id, request_id):
            response_data = await self.network.talk(
                node_id,
                protocol=ALEXANDRIA_PROTOCOL_ID,
                payload=request_data,
                endpoint=endpoint,
                request_id=request_id,
            )

        response = decode_message(response_data)
        if type(response) is not response_class:
            raise DecodingError(
                f"Invalid response. expected={response_class}  got={type(response)}"
            )
        return response  # type: ignore
Пример #7
0
 def from_wire_bytes(cls, data: bytes) -> "WhoAreYouPacket":
     if len(data) != WHO_ARE_YOU_PACKET_SIZE:
         raise DecodingError(
             f"Invalid length for WhoAreYouPacket: length={len(data)}  data={data.hex()}"
         )
     stream = BytesIO(data)
     id_nonce = cast(IDNonce, stream.read(16))
     enr_sequence_number = int.from_bytes(stream.read(8), "big")
     return cls(id_nonce, enr_sequence_number)
Пример #8
0
 def from_wire_bytes(cls, data: bytes) -> "HandshakeHeader":
     if len(data) != 3:
         raise DecodingError(
             f"Invalid length for HandshakeHeader: length={len(data)}  data={data.hex()}"
         )
     version = data[0]
     signature_size = data[1]
     ephemeral_key_size = data[2]
     return cls(version, signature_size, ephemeral_key_size)
Пример #9
0
 def from_wire_bytes(cls, data: bytes) -> "HandshakeHeader":
     if len(data) != HANDSHAKE_HEADER_PACKET_SIZE:
         raise DecodingError(
             f"Invalid length for HandshakeHeader: length={len(data)}  data={data.hex()}"
         )
     stream = BytesIO(data)
     source_node_id = NodeID(stream.read(32))
     signature_size = stream.read(1)[0]
     ephemeral_key_size = stream.read(1)[0]
     return cls(source_node_id, signature_size, ephemeral_key_size)
Пример #10
0
 def from_wire_bytes(cls, data: bytes) -> "WhoAreYouPacket":
     if len(data) != 52:
         raise DecodingError(
             f"Invalid length for WhoAreYouPacket: length={len(data)}  data={data.hex()}"
         )
     stream = BytesIO(data)
     request_nonce = cast(Nonce, stream.read(12))
     id_nonce = cast(IDNonce, stream.read(32))
     enr_sequence_number = int.from_bytes(stream.read(8), "big")
     return cls(request_nonce, id_nonce, enr_sequence_number)
Пример #11
0
 def from_wire_bytes(cls, data: bytes) -> "Header":
     if len(data) != 43:
         raise DecodingError(
             f"Invalid length for Header: length={len(data)}  data={data.hex()}"
         )
     protocol_id = data[:8]
     remote_node_id = cast(NodeID, data[8:40])
     flag = data[40]
     auth_data_size = int.from_bytes(data[41:43], "big")
     return cls(protocol_id, remote_node_id, flag, auth_data_size)
Пример #12
0
 def from_wire_bytes(cls, data: bytes) -> "MessagePacket":
     if len(data) != MESSAGE_PACKET_SIZE:
         raise DecodingError(
             f"Invalid length for MessagePacket: length={len(data)}  data={data.hex()}"
         )
     return cls(NodeID(data))
Пример #13
0
 def from_wire_bytes(cls, data: bytes) -> "MessagePacket":
     if len(data) != 12:
         raise DecodingError(
             f"Invalid length for MessagePacket: length={len(data)}  data={data.hex()}"
         )
     return cls(cast(Nonce, data))