Esempio n. 1
0
  def unpack(content):
    if len(content) < ED25519_HEADER_LENGTH + ED25519_SIGNATURE_LENGTH:
      raise ValueError('Ed25519 certificate was %i bytes, but should be at least %i' % (len(content), ED25519_HEADER_LENGTH + ED25519_SIGNATURE_LENGTH))

    header, signature = split(content, len(content) - ED25519_SIGNATURE_LENGTH)

    version, header = Size.CHAR.pop(header)
    cert_type, header = Size.CHAR.pop(header)
    expiration_hours, header = Size.LONG.pop(header)
    key_type, header = Size.CHAR.pop(header)
    key, header = split(header, ED25519_KEY_LENGTH)
    extension_count, extension_data = Size.CHAR.pop(header)

    if version != 1:
      raise ValueError('Ed25519 v1 parser cannot read version %i certificates' % version)

    extensions = []

    for i in range(extension_count):
      extension, extension_data = Ed25519Extension.pop(extension_data)
      extensions.append(extension)

    if extension_data:
      raise ValueError('Ed25519 certificate had %i bytes of unused extension data' % len(extension_data))

    return Ed25519CertificateV1(cert_type, datetime.datetime.utcfromtimestamp(expiration_hours * 3600), key_type, key, extensions, signature)
Esempio n. 2
0
  def send(self, command, data = '', stream_id = 0):
    """
    Sends a message over the circuit.

    :param stem.client.RelayCommand command: command to be issued
    :param bytes data: message payload
    :param int stream_id: specific stream this concerns

    :returns: **list** of :class:`~stem.client.cell.RelayCell` responses
    """

    with self.relay._orport_lock:
      orig_digest = self.forward_digest.copy()
      orig_key = copy.copy(self.forward_key)

      # Digests and such are computed using the RELAY cell payload. This
      # doesn't include the initial circuit id and cell type fields.
      # Circuit ids vary in length depending on the protocol version.

      header_size = self.relay.link_protocol.circ_id_size.size + 1

      try:
        cell = stem.client.cell.RelayCell(self.id, command, data, 0, stream_id)
        payload_without_digest = cell.pack(self.relay.link_protocol)[header_size:]
        self.forward_digest.update(payload_without_digest)

        cell = stem.client.cell.RelayCell(self.id, command, data, self.forward_digest, stream_id)
        header, payload = split(cell.pack(self.relay.link_protocol), header_size)
        encrypted_payload = header + self.forward_key.update(payload)

        reply_cells = []
        self.relay._orport.send(encrypted_payload)
        reply = self.relay._orport.recv()

        # Check that we got the correct number of bytes for a series of RELAY cells

        relay_cell_size = header_size + stem.client.cell.FIXED_PAYLOAD_LEN
        relay_cell_cmd = stem.client.cell.RelayCell.VALUE

        if len(reply) % relay_cell_size != 0:
          raise stem.ProtocolError('Circuit response should be a series of RELAY cells, but received an unexpected size for a response: %i' % len(reply))

        while reply:
          circ_id, reply = self.relay.link_protocol.circ_id_size.pop(reply)
          command, reply = Size.CHAR.pop(reply)
          payload, reply = split(reply, stem.client.cell.FIXED_PAYLOAD_LEN)

          if command != relay_cell_cmd:
            raise stem.ProtocolError('RELAY cell responses should be %i but was %i' % (relay_cell_cmd, command))
          elif circ_id != self.id:
            raise stem.ProtocolError('Response should be for circuit id %i, not %i' % (self.id, circ_id))

          decrypted = self.backward_key.update(payload)
          reply_cells.append(stem.client.cell.RelayCell._unpack(decrypted, self.id, self.relay.link_protocol))

        return reply_cells
      except:
        self.forward_digest = orig_digest
        self.forward_key = orig_key
        raise
Esempio n. 3
0
  def _unpack(cls, content, circ_id, link_protocol):
    if len(content) < HASH_LEN * 2:
      raise ValueError('Key material and derivatived key should be %i bytes, but was %i' % (HASH_LEN * 2, len(content)))

    key_material, content = split(content, HASH_LEN)
    derivative_key, content = split(content, HASH_LEN)

    return CreatedFastCell(circ_id, derivative_key, key_material, content)
Esempio n. 4
0
File: cell.py Progetto: zmchu/stem
    def _unpack(
        cls, content: bytes, circ_id: int,
        link_protocol: 'stem.client.datatype.LinkProtocol'
    ) -> 'stem.client.cell.CreatedFastCell':
        if len(content) < HASH_LEN * 2:
            raise ValueError(
                'Key material and derivatived key should be %i bytes, but was %i'
                % (HASH_LEN * 2, len(content)))

        key_material, content = split(content, HASH_LEN)
        derivative_key, content = split(content, HASH_LEN)

        return CreatedFastCell(circ_id, derivative_key, key_material, content)
Esempio n. 5
0
File: cell.py Progetto: zmchu/stem
    def _unpack(
        cls: Type['stem.client.cell.AuthChallengeCell'], content: bytes,
        circ_id: int, link_protocol: 'stem.client.datatype.LinkProtocol'
    ) -> 'stem.client.cell.AuthChallengeCell':
        min_size = AUTH_CHALLENGE_SIZE + Size.SHORT.size
        if len(content) < min_size:
            raise ValueError(
                'AUTH_CHALLENGE payload should be at least %i bytes, but was %i'
                % (min_size, len(content)))

        challenge, content = split(content, AUTH_CHALLENGE_SIZE)
        method_count, content = Size.SHORT.pop(content)

        if len(content) < method_count * Size.SHORT.size:
            raise ValueError(
                'AUTH_CHALLENGE should have %i methods, but only had %i bytes for it'
                % (method_count, len(content)))

        methods = []

        for i in range(method_count):
            method, content = Size.SHORT.pop(content)
            methods.append(method)

        return AuthChallengeCell(methods, challenge, unused=content)
Esempio n. 6
0
File: cell.py Progetto: zmchu/stem
    def pop(
        content: bytes, link_protocol: 'stem.client.datatype.LinkProtocol'
    ) -> Tuple['stem.client.cell.Cell', bytes]:
        """
    Unpacks the first cell.

    :param content: payload to decode
    :param link_protocol: link protocol version

    :returns: (:class:`~stem.client.cell.Cell`, remainder) tuple

    :raises:
      * ValueError if content is malformed
      * NotImplementedError if unable to unpack this cell type
    """

        link_protocol = LinkProtocol(link_protocol)

        circ_id, content = link_protocol.circ_id_size.pop(content)
        command, content = CELL_TYPE_SIZE.pop(content)
        cls = Cell.by_value(command)

        if cls.IS_FIXED_SIZE:
            payload_len = FIXED_PAYLOAD_LEN
        else:
            payload_len, content = PAYLOAD_LEN_SIZE.pop(content)

        if len(content) < payload_len:
            raise ValueError(
                '%s cell should have a payload of %i bytes, but only had %i' %
                (cls.NAME, payload_len, len(content)))

        payload, content = split(content, payload_len)
        return cls._unpack(payload, circ_id, link_protocol), content
Esempio n. 7
0
  def _unpack(cls, content, circ_id, link_protocol):
    key_material, unused = split(content, HASH_LEN)

    if len(key_material) != HASH_LEN:
      raise ValueError('Key material should be %i bytes, but was %i' % (HASH_LEN, len(key_material)))

    return CreateFastCell(circ_id, key_material, unused)
Esempio n. 8
0
  def encrypt(self, link_protocol, key, digest):
    """
    Encrypts our cell content to be sent with the given key. This provides back
    a tuple of the form...

    ::

      (payload (bytes), new_key (CipherContext), new_digest (HASH))

    :param int link_protocol: link protocol version
    :param cryptography.hazmat.primitives.ciphers.CipherContext key:
      key established with the relay we're sending this cell to
    :param hashlib.HASH digest: running digest held with the relay

    :returns: **tuple** with our encrypted payload and updated key/digest
    """

    new_key = copy.copy(key)
    new_digest = digest.copy()

    # Digests are computed from our payload, not including our header's circuit
    # id (2 or 4 bytes) and command (1 byte).

    header_size = link_protocol.circ_id_size.size + 1
    payload_without_digest = self.pack(link_protocol)[header_size:]
    new_digest.update(payload_without_digest)

    # Pack a copy of ourselves with our newly calculated digest, and encrypt
    # the payload. Header remains plaintext.

    cell = RelayCell(self.circ_id, self.command, self.data, new_digest, self.stream_id, self.recognized, self.unused)
    header, payload = split(cell.pack(link_protocol), header_size)

    return header + new_key.update(payload), new_key, new_digest
Esempio n. 9
0
File: cell.py Progetto: zmchu/stem
    def _unpack(
        cls, content: bytes, circ_id: int,
        link_protocol: 'stem.client.datatype.LinkProtocol'
    ) -> 'stem.client.cell.CreateFastCell':
        key_material, unused = split(content, HASH_LEN)

        if len(key_material) != HASH_LEN:
            raise ValueError('Key material should be %i bytes, but was %i' %
                             (HASH_LEN, len(key_material)))

        return CreateFastCell(circ_id, key_material, unused)
Esempio n. 10
0
  def _unpack(cls, content, circ_id, link_protocol):
    command, content = Size.CHAR.pop(content)
    recognized, content = Size.SHORT.pop(content)  # 'recognized' field
    stream_id, content = Size.SHORT.pop(content)
    digest, content = Size.LONG.pop(content)
    data_len, content = Size.SHORT.pop(content)
    data, unused = split(content, data_len)

    if len(data) != data_len:
      raise ValueError('%s cell said it had %i bytes of data, but only had %i' % (cls.NAME, data_len, len(data)))

    return RelayCell(circ_id, command, data, digest, stream_id, recognized, unused)
Esempio n. 11
0
  def pop(content):
    if len(content) < 4:
      raise ValueError('Ed25519 extension is missing header fields')

    data_size, content = Size.SHORT.pop(content)
    ext_type, content = Size.CHAR.pop(content)
    flags, content = Size.CHAR.pop(content)
    data, content = split(content, data_size)

    if len(data) != data_size:
      raise ValueError("Ed25519 extension is truncated. It should have %i bytes of data but there's only %i." % (data_size, len(data)))

    return Ed25519Extension(ext_type, flags, data), content
Esempio n. 12
0
    def _recv(self, raw=False):
        """
    Reads the next cell from our ORPort. If none is present this blocks
    until one is available.

    :param bool raw: provides bytes rather than parsing as a cell if **True**

    :returns: next :class:`~stem.client.cell.Cell`
    """

        with self._orport_lock:
            # cells begin with [circ_id][cell_type][...]

            circ_id_size = self.link_protocol.circ_id_size.size

            while len(self._orport_buffer) < (circ_id_size +
                                              CELL_TYPE_SIZE.size):
                self._orport_buffer += self._orport.recv(
                )  # read until we know the cell type

            cell_type = Cell.by_value(
                CELL_TYPE_SIZE.pop(self._orport_buffer[circ_id_size:])[0])

            if cell_type.IS_FIXED_SIZE:
                cell_size = circ_id_size + CELL_TYPE_SIZE.size + FIXED_PAYLOAD_LEN
            else:
                # variable length, our next field is the payload size

                while len(self._orport_buffer) < (circ_id_size +
                                                  CELL_TYPE_SIZE.size +
                                                  FIXED_PAYLOAD_LEN.size):
                    self._orport_buffer += self._orport.recv(
                    )  # read until we know the cell size

                payload_len = FIXED_PAYLOAD_LEN.pop(
                    self._orport_buffer[circ_id_size +
                                        CELL_TYPE_SIZE.size:])[0]
                cell_size = circ_id_size + CELL_TYPE_SIZE.size + FIXED_PAYLOAD_LEN.size + payload_len

            while len(self._orport_buffer) < cell_size:
                self._orport_buffer += self._orport.recv(
                )  # read until we have the full cell

            if raw:
                content, self._orport_buffer = split(self._orport_buffer,
                                                     cell_size)
                return content
            else:
                cell, self._orport_buffer = Cell.pop(self._orport_buffer,
                                                     self.link_protocol)
                return cell
Esempio n. 13
0
  def send(self, command, data = '', stream_id = 0):
    """
    Sends a message over the circuit.

    :param stem.client.datatype.RelayCommand command: command to be issued
    :param bytes data: message payload
    :param int stream_id: specific stream this concerns

    :returns: **list** of :class:`~stem.client.cell.RelayCell` responses
    """

    with self.relay._orport_lock:
      # Encrypt and send the cell. Our digest/key only updates if the cell is
      # successfully sent.

      cell = stem.client.cell.RelayCell(self.id, command, data, stream_id = stream_id)
      payload, forward_key, forward_digest = cell.encrypt(self.relay.link_protocol, self.forward_key, self.forward_digest)
      self.relay._orport.send(payload)

      self.forward_digest = forward_digest
      self.forward_key = forward_key

      # Decrypt relay cells received in response. Again, our digest/key only
      # updates when handled successfully.

      reply = self.relay._orport.recv()
      reply_cells = []

      if len(reply) % self.relay.link_protocol.fixed_cell_length != 0:
        raise stem.ProtocolError('Circuit response should be a series of RELAY cells, but received an unexpected size for a response: %i' % len(reply))

      while reply:
        encrypted_cell, reply = split(reply, self.relay.link_protocol.fixed_cell_length)
        decrypted_cell, backward_key, backward_digest = stem.client.cell.RelayCell.decrypt(self.relay.link_protocol, encrypted_cell, self.backward_key, self.backward_digest)

        if self.id != decrypted_cell.circ_id:
          raise stem.ProtocolError('Response should be for circuit id %i, not %i' % (self.id, decrypted_cell.circ_id))

        self.backward_digest = backward_digest
        self.backward_key = backward_key

        reply_cells.append(decrypted_cell)

      return reply_cells