def _parse_introduction_points_line(descriptor, entries):
    _, block_type, block_contents = entries['introduction-points'][0]

    if not block_contents or block_type != 'MESSAGE':
        raise ValueError(
            "'introduction-points' should be followed by a MESSAGE block, but was a %s"
            % block_type)

    descriptor.introduction_points_encoded = block_contents

    try:
        decoded_field = _bytes_for_block(block_contents)
    except TypeError:
        raise ValueError(
            "'introduction-points' isn't base64 encoded content:\n%s" %
            block_contents)

    auth_types = []

    while decoded_field.startswith(
            b'service-authentication ') and b'\n' in decoded_field:
        auth_line, decoded_field = decoded_field.split(b'\n', 1)
        auth_line_comp = auth_line.split(b' ')

        if len(auth_line_comp) < 3:
            raise ValueError(
                "Within introduction-points we expected 'service-authentication [auth_type] [auth_data]', but had '%s'"
                % auth_line)

        auth_types.append((auth_line_comp[1], auth_line_comp[2]))

    descriptor.introduction_points_auth = auth_types
    descriptor.introduction_points_content = decoded_field
def _parse_introduction_points_line(descriptor, entries):
  _, block_type, block_contents = entries['introduction-points'][0]

  if not block_contents or block_type != 'MESSAGE':
    raise ValueError("'introduction-points' should be followed by a MESSAGE block, but was a %s" % block_type)

  descriptor.introduction_points_encoded = block_contents

  try:
    decoded_field = _bytes_for_block(block_contents)
  except TypeError:
    raise ValueError("'introduction-points' isn't base64 encoded content:\n%s" % block_contents)

  auth_types = []

  while decoded_field.startswith(b'service-authentication ') and b'\n' in decoded_field:
    auth_line, decoded_field = decoded_field.split(b'\n', 1)
    auth_line_comp = auth_line.split(b' ')

    if len(auth_line_comp) < 3:
      raise ValueError("Within introduction-points we expected 'service-authentication [auth_type] [auth_data]', but had '%s'" % auth_line)

    auth_types.append((auth_line_comp[1], auth_line_comp[2]))

  descriptor.introduction_points_auth = auth_types
  descriptor.introduction_points_content = decoded_field
Example #3
0
  def __init__(self, raw_contents, validate = False, skip_crypto_validation = False):
    super(RelayDescriptor, self).__init__(raw_contents, validate)

    if validate:
      if self.fingerprint:
        key_hash = hashlib.sha1(_bytes_for_block(self.signing_key)).hexdigest()

        if key_hash != self.fingerprint.lower():
          raise ValueError('Fingerprint does not match the hash of our signing key (fingerprint: %s, signing key hash: %s)' % (self.fingerprint.lower(), key_hash))

      if not skip_crypto_validation:
        try:
          signed_digest = self._digest_for_signature(self.signing_key, self.signature)

          if signed_digest != self.digest():
            raise ValueError('Decrypted digest does not match local digest (calculated: %s, local: %s)' % (signed_digest, self.digest()))

          if self.onion_key_crosscert:
            onion_key_crosscert_digest = self._digest_for_signature(self.onion_key, self.onion_key_crosscert)

            if onion_key_crosscert_digest != self._onion_key_crosscert_digest():
              raise ValueError('Decrypted onion-key-crosscert digest does not match local digest (calculated: %s, local: %s)' % (onion_key_crosscert_digest, self._onion_key_crosscert_digest()))
        except ImportError:
          pass  # cryptography module unavailable

      if self.certificate:
        try:
          self.certificate.validate(self)
        except ImportError:
          pass  # cryptography module unavailable
Example #4
0
    def content(
            cls: Type['stem.descriptor.server_descriptor.RelayDescriptor'],
            attr: Optional[Mapping[str, str]] = None,
            exclude: Sequence[str] = (),
            sign: bool = False,
            signing_key: Optional['stem.descriptor.SigningKey'] = None,
            exit_policy: Optional['stem.exit_policy.ExitPolicy'] = None
    ) -> bytes:
        attr = dict(attr) if attr else {}

        if exit_policy is None:
            exit_policy = REJECT_ALL_POLICY

        base_header = [
            ('router', '%s %s 9001 0 0' %
             (_random_nickname(), _random_ipv4_address())),
            ('published', _random_date()),
            ('bandwidth', '153600 256000 104590'),
        ] + [
            tuple(line.split(' ', 1))
            for line in str(exit_policy).splitlines()  # type: ignore
        ] + [
            ('onion-key', _random_crypto_blob('RSA PUBLIC KEY')),
            ('signing-key', _random_crypto_blob('RSA PUBLIC KEY')),
        ]

        if sign or signing_key:
            if attr and 'signing-key' in attr:
                raise ValueError(
                    'Cannot sign the descriptor if a signing-key has been provided'
                )
            elif attr and 'router-signature' in attr:
                raise ValueError(
                    'Cannot sign the descriptor if a router-signature has been provided'
                )

            if signing_key is None:
                signing_key = create_signing_key()

            if 'fingerprint' not in attr:
                fingerprint = hashlib.sha1(
                    _bytes_for_block(
                        stem.util.str_tools._to_unicode(
                            signing_key.public_digest.strip()))).hexdigest(
                            ).upper()
                attr['fingerprint'] = ' '.join(
                    stem.util.str_tools._split_by_length(fingerprint, 4))

            attr['signing-key'] = signing_key.public_digest

            content = _descriptor_content(
                attr, exclude, base_header) + b'\nrouter-signature\n'
            return _append_router_signature(content, signing_key.private)
        else:
            return _descriptor_content(attr, exclude, base_header, (
                ('router-sig-ed25519', None),
                ('router-signature', _random_crypto_blob('SIGNATURE')),
            ))
Example #5
0
def _parse_introduction_points_line(descriptor, entries):
  _, block_type, block_contents = entries['introduction-points'][0]

  if not block_contents or block_type != 'MESSAGE':
    raise ValueError("'introduction-points' should be followed by a MESSAGE block, but was a %s" % block_type)

  descriptor.introduction_points_encoded = block_contents
  descriptor.introduction_points_auth = []  # field was never implemented in tor (#15190)

  try:
    descriptor.introduction_points_content = _bytes_for_block(block_contents)
  except TypeError:
    raise ValueError("'introduction-points' isn't base64 encoded content:\n%s" % block_contents)
Example #6
0
  def _onion_key_crosscert_digest(self):
    """
    Provides the digest of the onion-key-crosscert data. This consists of the
    RSA identity key sha1 and ed25519 identity key.

    :returns: **unicode** digest encoded in uppercase hex

    :raises: ValueError if the digest cannot be calculated
    """

    signing_key_digest = hashlib.sha1(_bytes_for_block(self.signing_key)).digest()
    data = signing_key_digest + base64.b64decode(stem.util.str_tools._to_bytes(self.ed25519_master_key) + b'=')
    return stem.util.str_tools._to_unicode(binascii.hexlify(data).upper())
def _parse_introduction_points_line(descriptor, entries):
  _, block_type, block_contents = entries['introduction-points'][0]

  if not block_contents or block_type != 'MESSAGE':
    raise ValueError("'introduction-points' should be followed by a MESSAGE block, but was a %s" % block_type)

  descriptor.introduction_points_encoded = block_contents
  descriptor.introduction_points_auth = []  # field was never implemented in tor (#15190)

  try:
    descriptor.introduction_points_content = _bytes_for_block(block_contents)
  except TypeError:
    raise ValueError("'introduction-points' isn't base64 encoded content:\n%s" % block_contents)
Example #8
0
  def _onion_key_crosscert_digest(self):
    """
    Provides the digest of the onion-key-crosscert data. This consists of the
    RSA identity key sha1 and ed25519 identity key.

    :returns: **unicode** digest encoded in uppercase hex

    :raises: ValueError if the digest cannot be calculated
    """

    signing_key_digest = hashlib.sha1(_bytes_for_block(self.signing_key)).digest()
    data = signing_key_digest + base64.b64decode(stem.util.str_tools._to_bytes(self.ed25519_master_key) + b'=')
    return stem.util.str_tools._to_unicode(binascii.hexlify(data).upper())
Example #9
0
    def content(cls, attr=None, exclude=(), sign=False, signing_key=None):
        if signing_key:
            sign = True

        if attr is None:
            attr = {}

        base_header = (
            ('router',
             '%s %s 9001 0 0' % (_random_nickname(), _random_ipv4_address())),
            ('published', _random_date()),
            ('bandwidth', '153600 256000 104590'),
            ('reject', '*:*'),
            ('onion-key', _random_crypto_blob('RSA PUBLIC KEY')),
            ('signing-key', _random_crypto_blob('RSA PUBLIC KEY')),
        )

        if sign:
            if attr and 'signing-key' in attr:
                raise ValueError(
                    'Cannot sign the descriptor if a signing-key has been provided'
                )
            elif attr and 'router-signature' in attr:
                raise ValueError(
                    'Cannot sign the descriptor if a router-signature has been provided'
                )

            if signing_key is None:
                signing_key = create_signing_key()

            if 'fingerprint' not in attr:
                fingerprint = hashlib.sha1(
                    _bytes_for_block(
                        stem.util.str_tools._to_unicode(
                            signing_key.public_digest.strip()))).hexdigest(
                            ).upper()
                attr['fingerprint'] = ' '.join(
                    stem.util.str_tools._split_by_length(fingerprint, 4))

            attr['signing-key'] = signing_key.public_digest

            content = _descriptor_content(
                attr, exclude, base_header) + b'\nrouter-signature\n'
            return _append_router_signature(content, signing_key.private)
        else:
            return _descriptor_content(attr, exclude, base_header, (
                ('router-sig-ed25519', None),
                ('router-signature', _random_crypto_blob('SIGNATURE')),
            ))
Example #10
0
  def __init__(self, raw_contents, validate = False, annotations = None):
    super(RelayDescriptor, self).__init__(raw_contents, validate, annotations)

    if validate:
      if self.fingerprint:
        key_hash = hashlib.sha1(_bytes_for_block(self.signing_key)).hexdigest()

        if key_hash != self.fingerprint.lower():
          raise ValueError('Fingerprint does not match the hash of our signing key (fingerprint: %s, signing key hash: %s)' % (self.fingerprint.lower(), key_hash))

      if stem.prereq.is_crypto_available():
        signed_digest = self._digest_for_signature(self.signing_key, self.signature)

        if signed_digest != self.digest():
          raise ValueError('Decrypted digest does not match local digest (calculated: %s, local: %s)' % (signed_digest, self.digest()))
Example #11
0
    def __init__(self,
                 raw_contents,
                 validate=False,
                 annotations=None,
                 skip_crypto_validation=False):
        super(RelayDescriptor, self).__init__(raw_contents, validate,
                                              annotations)

        if validate:
            if self.fingerprint:
                key_hash = hashlib.sha1(_bytes_for_block(
                    self.signing_key)).hexdigest()

                if key_hash != self.fingerprint.lower():
                    raise ValueError(
                        'Fingerprint does not match the hash of our signing key (fingerprint: %s, signing key hash: %s)'
                        % (self.fingerprint.lower(), key_hash))

            if not skip_crypto_validation and stem.prereq.is_crypto_available(
            ):
                signed_digest = self._digest_for_signature(
                    self.signing_key, self.signature)

                if signed_digest != self.digest():
                    raise ValueError(
                        'Decrypted digest does not match local digest (calculated: %s, local: %s)'
                        % (signed_digest, self.digest()))

                if self.onion_key_crosscert and stem.prereq.is_crypto_available(
                ):
                    onion_key_crosscert_digest = self._digest_for_signature(
                        self.onion_key, self.onion_key_crosscert)

                    if onion_key_crosscert_digest != self._onion_key_crosscert_digest(
                    ):
                        raise ValueError(
                            'Decrypted onion-key-crosscert digest does not match local digest (calculated: %s, local: %s)'
                            % (onion_key_crosscert_digest,
                               self._onion_key_crosscert_digest()))

            if stem.prereq._is_crypto_ed25519_supported() and self.certificate:
                self.certificate.validate(self)
Example #12
0
    def __init__(self, raw_contents, validate=False, annotations=None):
        super(RelayDescriptor, self).__init__(raw_contents, validate,
                                              annotations)

        if validate:
            if self.fingerprint:
                key_hash = hashlib.sha1(_bytes_for_block(
                    self.signing_key)).hexdigest()

                if key_hash != self.fingerprint.lower():
                    raise ValueError(
                        'Fingerprint does not match the hash of our signing key (fingerprint: %s, signing key hash: %s)'
                        % (self.fingerprint.lower(), key_hash))

            if stem.prereq.is_crypto_available():
                signed_digest = self._digest_for_signature(
                    self.signing_key, self.signature)

                if signed_digest != self.digest():
                    raise ValueError(
                        'Decrypted digest does not match local digest (calculated: %s, local: %s)'
                        % (signed_digest, self.digest()))
Example #13
0
  def content(cls, attr = None, exclude = (), sign = False, signing_key = None):
    if signing_key:
      sign = True

    if attr is None:
      attr = {}

    base_header = (
      ('router', '%s %s 9001 0 0' % (_random_nickname(), _random_ipv4_address())),
      ('published', _random_date()),
      ('bandwidth', '153600 256000 104590'),
      ('reject', '*:*'),
      ('onion-key', _random_crypto_blob('RSA PUBLIC KEY')),
      ('signing-key', _random_crypto_blob('RSA PUBLIC KEY')),
    )

    if sign:
      if attr and 'signing-key' in attr:
        raise ValueError('Cannot sign the descriptor if a signing-key has been provided')
      elif attr and 'router-signature' in attr:
        raise ValueError('Cannot sign the descriptor if a router-signature has been provided')

      if signing_key is None:
        signing_key = create_signing_key()

      if 'fingerprint' not in attr:
        fingerprint = hashlib.sha1(_bytes_for_block(stem.util.str_tools._to_unicode(signing_key.public_digest.strip()))).hexdigest().upper()
        attr['fingerprint'] = ' '.join(stem.util.str_tools._split_by_length(fingerprint, 4))

      attr['signing-key'] = signing_key.public_digest

      content = _descriptor_content(attr, exclude, base_header) + b'\nrouter-signature\n'
      return _append_router_signature(content, signing_key.private)
    else:
      return _descriptor_content(attr, exclude, base_header, (
        ('router-sig-ed25519', None),
        ('router-signature', _random_crypto_blob('SIGNATURE')),
      ))
Example #14
0
def _parse_superencrypted_line(descriptor, entries):
    _, block_type, block_contents = entries['superencrypted'][0]

    if not block_contents or block_type != 'MESSAGE':
        raise ValueError(
            "'superencrypted' should be followed by a MESSAGE block, but was a %s"
            % block_type)

    descriptor.superencrypted_encoded = block_contents

    try:
        descriptor.introduction_points_content = _bytes_for_block(
            block_contents)
    except TypeError:
        raise ValueError(
            "'introduction-points' isn't base64 encoded content:\n%s" %
            block_contents)

    descriptor.introduction_points_salt = descriptor.introduction_points_content[:
                                                                                 16]
    descriptor.introduction_points_encrypted = descriptor.introduction_points_content[
        16:-32]
    descriptor.introduction_points_mac = descriptor.introduction_points_content[
        -32:]