[tor-commits] [bridgedb/master] Remove old scripts/gen_bridge_descriptors; use leekspin.
isis at torproject.org
isis at torproject.org
Wed Mar 26 05:49:32 UTC 2014
commit c9ba8cffb49e063690c456f97978aaaf279f92df
Author: Isis Lovecruft <isis at torproject.org>
Date: Mon Mar 24 03:27:30 2014 +0000
Remove old scripts/gen_bridge_descriptors; use leekspin.
---
scripts/gen_bridge_descriptors | 870 ----------------------------------------
1 file changed, 870 deletions(-)
diff --git a/scripts/gen_bridge_descriptors b/scripts/gen_bridge_descriptors
deleted file mode 100644
index 362ed3d..0000000
--- a/scripts/gen_bridge_descriptors
+++ /dev/null
@@ -1,870 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-"""Generate valid and signed descriptors for mocked Tor relays or bridges.
-
-.. todo:: Finish enough CFFI_ bindings for the newer PyNaCl_ (or enough of the
- SWIG_ bindings for the older pynacl_) to be able to emulate the following
- curvecp_ command (the ``curvecp*`` commands require libchloride_):
-
- $ curvecpmakekey ntor-key
- $ curvecpprintkey ntor-key > ntor-key.hex
- $ python -c 'import binascii, sys; \
- key_hex=open('./ntor-key.hex','rb').read();\
- key_b64=binascii.b2a_base64(binascii.unhexlify(key_hex));\
- sys.stdout.write(key_b64);'
-
- .. _CFFI: https://cffi.readthedocs.org
- .. _PyNaCl: https://github.com/seanlynch/pynacl
- .. _SWIG: https://github.com/swig/swig
- .. _pynacl: https://github.com/seanlynch/pynacl
- .. _curvecp: http://curvecp.org/
- .. _libchloride: https://github.com/jedisct1/libchloride
-
-.. authors:: Isis Lovecruft <isis at torproject.org> 0xA3ADB67A2CDB8B35
- Matthew Finkel <sysrqb at torproject.org>
-.. licence:: distributed with BridgeDB, see included LICENSE file
-.. copyright:: (c) 2013 Matthew Finkel, Isis Lovecruft, The Tor Project, Inc.
-"""
-
-from __future__ import print_function
-from __future__ import absolute_import
-from __future__ import unicode_literals
-
-import argparse
-import binascii
-import hashlib
-import ipaddr
-import math
-import os
-import sys
-import random
-import re
-import time
-import traceback
-
-from datetime import datetime
-from codecs import open as open
-
-try:
- import OpenSSL.crypto
-except (ImportError, NameError) as error:
- print("This script requires pyOpenSSL>=0.13.0")
- raise SystemExit(error.message)
-try:
- from bridgedb.parse import versions
-except (ImportError, NameError) as error:
- print(error.message)
- print("WARNING: Cannot import bridgedb package!",
- "Generated descriptor content won't accurately reflect descriptor",
- "information created by different Tor versions.", sep='\n\t')
-try:
- import nacl
- import nacl.secret
-except (ImportError, NameError, IOError):
- nacl = secret = None
-
-
-#: The version of this script
-__version__ = '0.2.0'
-
-#: The <major>.<minor>.<micro>.<rev> version numbers for tor, taken from the
-#: 'server-versions' line of a consensus file
-SERVER_VERSIONS = """0.2.2.39,0.2.3.24-rc,0.2.3.25,
-0.2.4.5-alpha,0.2.4.6-alpha,0.2.4.7-alpha,0.2.4.8-alpha,0.2.4.9-alpha,
-0.2.4.10-alpha,0.2.4.11-alpha,0.2.4.12-alpha,0.2.4.14-alpha,0.2.4.15-rc,
-0.2.4.16-rc,0.2.4.17-rc,0.2.5.1-alpha""".replace('\n', '').split(',')
-
-#: Strings found in PEM-encoded objects created by Tor
-TOR_BEGIN_KEY = "-----BEGIN RSA PUBLIC KEY-----"
-TOR_END_KEY = "-----END RSA PUBLIC KEY-----"
-TOR_BEGIN_SK = "-----BEGIN RSA PRIVATE KEY-----"
-TOR_END_SK = "-----END RSA PRIVATE KEY-----"
-TOR_BEGIN_SIG = "-----BEGIN SIGNATURE-----"
-TOR_END_SIG = "-----END SIGNATURE-----"
-
-#: Strings found in PEM-encoded objects created by OpenSSL
-OPENSSL_BEGIN_KEY = "-----BEGIN PRIVATE KEY-----"
-OPENSSL_END_KEY = "-----END PRIVATE KEY-----"
-OPENSSL_BEGIN_CERT = "-----BEGIN CERTIFICATE-----"
-OPENSSL_END_CERT = "-----END CERTIFICATE-----"
-
-PEM = OpenSSL.crypto.FILETYPE_PEM
-ASN1 = OpenSSL.crypto.FILETYPE_ASN1
-
-
-class OpenSSLKeyGenError(Exception):
- """Raised when there is a problem generating a new key."""
-
-
-def getArgParser():
- """Get our :class:`~argparse.ArgumentParser`."""
- parser = argparse.ArgumentParser(add_help=True)
- parser.version = __version__
- parser.description = "Generate a signed set of network-status, "
- parser.description += "extra-info, and server descriptor documents "
- parser.description += "for mock Tor relays or bridges."
- infoargs = parser.add_mutually_exclusive_group()
- verbargs = parser.add_mutually_exclusive_group()
- infoargs.add_argument("-v", "--verbose", action="store_true",
- help="print information to stdout")
- infoargs.add_argument("-q", "--quiet", action="store_true",
- help="don't print anything")
- verbargs.add_argument("--version", action="store_true",
- help="print the %s version and exit".format(
- parser.prog))
- group = parser.add_argument_group()
- group.title = "required arguments"
- group.add_argument("-n", "--descriptors", default=0,
- help="generate <n> descriptor sets", type=int)
- return parser
-
-def randomIP():
- """Create a random IPv4 or IPv6 address."""
- maybe = int(random.getrandbits(1))
- ip = randomIPv4() if maybe else randomIPv6()
- return ip
-
-def randomIPv4():
- """Create a random IPv4 address."""
- return ipaddr.IPv4Address(random.getrandbits(32))
-
-def randomIPv6():
- """Create a random IPv6 address."""
- return ipaddr.IPv6Address(random.getrandbits(128))
-
-def randomPort():
- """Get a random integer in the range [1024, 65535]."""
- return random.randint(1025, 65535)
-
-def getHexString(size):
- """Get a capitalised hexidecimal string ``size`` bytes long.
-
- :param integer size: The number of bytes in the returned string.
- :rtype: string
- :returns: A hex string.
- """
- s = ""
- for i in xrange(size):
- s += random.choice("ABCDEF0123456789")
- return s
-
-def makeTimeStamp(now=None, fmt=None, variation=False, period=None):
- """Get a random timestamp suitable for a bridge server descriptor.
-
- :param int now: The time, in seconds since the Epoch, to generate the
- timestamp for (and to consider as the maximum time, if other options
- are enabled).
- :param string fmt: A strftime(3) format string for the timestamp. If not
- given, defaults to ISO-8601 format without the 'T' separator.
- :param bool variation: If True, enable timestamp variation. Otherwise,
- make all timestamps be set to the current time.
- :type period: int or None
- :param period: If given, vary the generated timestamps to be a random time
- between ``period`` hours ago and the current time. If None, generate
- completely random timestamps which are anywhere between the Unix Epoch
- and the current time. This parameter only has an effect if
- ``variation`` is enabled.
- """
- now = int(now) if now is not None else int(time.time())
- fmt = fmt if fmt else "%Y-%m-%d %H:%M:%S"
-
- if variation:
- then = 1
- if period is not None:
- secs = int(period) * 3600
- then = now - secs
- # Get a random number between one epochseconds number and another
- diff = random.randint(then, now)
- # Then rewind the clock
- now = diff
-
- return time.strftime(fmt, time.localtime(now))
-
-def shouldHaveOptPrefix(version):
- """Returns true if a tor ``version`` should have the 'opt ' prefix.
-
- In tor, up to and including, version 0.2.3.25, server-descriptors (bridge
- or regular) prefixed several lines with 'opt '. For the 0.2.3.x series,
- these lines were:
- - 'protocols'
- - 'fingerprint'
- - 'hidden-service-dir'
- - 'extra-info-digest'
-
- :param string version: One of ``SERVER_VERSIONS``.
- :rtype: bool
- :returns: True if we should include the 'opt ' prefix.
- """
- changed_in = versions.Version('0.2.4.1-alpha', package='tor')
- our_version = versions.Version(version, package='tor')
- if our_version < changed_in:
- return True
- return False
-
-def makeProtocolsLine(version=None):
- """Generate an appropriate [bridge-]server-descriptor 'protocols' line.
-
- :param string version: One of ``SERVER_VERSIONS``.
- :rtype: string
- :returns: An '@type [bridge-]server-descriptor' 'protocols' line.
- """
- line = ''
- if (version is not None) and shouldHaveOptPrefix(version):
- line += 'opt '
- line += 'protocols Link 1 2 Circuit 1'
- return line
-
-def convertToSpaceyFingerprint(fingerprint):
- """Convert to a space-delimited 40 character fingerprint
-
- Given a 40 character string, usually the the SHA-1 hash of the
- DER encoding of an ASN.1 RSA public key, such as:
- |
- | 72C2F0AE1C14F40ED37ED5F5434B64711A658E46
- |
-
- convert it to the following format:
- |
- | 72C2 F0AE 1C14 F40E D37E D5F5 434B 6471 1A65 8E46
- |
-
- :param string fingerprint: A 40 character hex fingerprint.
- :rtype: string
- :returns: A 4-character space-delimited fingerprint.
- """
-
- assert len(fingerprint) == 40
- return " ".join([fingerprint[i:i+4] for i in xrange(0, 40, 4)])
-
-def makeFingerprintLine(fingerprint, version=None):
- """Generate an appropriate [bridge-]server-descriptor 'fingerprint' line.
-
- For example, for tor-0.2.3.25 and prior versions, this would look like:
- |
- | opt fingerprint D4BB C339 2560 1B7F 226E 133B A85F 72AF E734 0B29
- |
-
- :param string fingerprint: A public key fingerprint in groups of four,
- separated by spaces.
- :param string version: One of ``SERVER_VERSIONS``.
- :rtype: string
- :returns: An '@type [bridge-]server-descriptor' 'published' line.
- """
- line = ''
- if (version is not None) and shouldHaveOptPrefix(version):
- line += 'opt '
- line += 'fingerprint %s' % convertToSpaceyFingerprint(fingerprint)
- return line
-
-def makeBandwidthLine(variance=30):
- """Create a random 'bandwidth' line with some plausible burst variance.
-
- From torspec.git/dir-spec.txt, §2.1 "Router descriptors":
- | "bandwidth" bandwidth-avg bandwidth-burst bandwidth-observed NL
- |
- | [Exactly once]
- |
- | Estimated bandwidth for this router, in bytes per second. The
- | "average" bandwidth is the volume per second that the OR is willing
- | to sustain over long periods; the "burst" bandwidth is the volume
- | that the OR is willing to sustain in very short intervals. The
- | "observed" value is an estimate of the capacity this relay can
- | handle. The relay remembers the max bandwidth sustained output over
- | any ten second period in the past day, and another sustained input.
- | The "observed" value is the lesser of these two numbers.
-
- The "observed" bandwidth, in this function, is taken as some random value,
- bounded between 20KB/s and 2MB/s. For example, say:
-
- >>> import math
- >>> variance = 25
- >>> observed = 180376
- >>> percentage = float(variance) / 100.
- >>> percentage
- 0.25
-
- The ``variance`` in this context is the percentage of the "observed"
- bandwidth, which will be added to the "observed" bandwidth, and becomes
- the value for the "burst" bandwidth:
-
- >>> burst = observed + math.ceil(observed * percentage)
- >>> assert burst > observed
-
- This doesn't do much, since the "burst" bandwidth in a real
- [bridge-]server-descriptor is reported by the OR; this function mostly
- serves to avoid generating completely-crazy, totally-implausible bandwidth
- values. The "average" bandwidth value is then just the mean value of the
- other two.
-
- :param integer variance: The percent of the fake "observed" bandwidth to
- increase the "burst" bandwidth by.
- :rtype: string
- :returns: A "bandwidth" line for a [bridge-]server-descriptor.
- """
- observed = random.randint(20 * 2**10, 2 * 2**30)
- percentage = float(variance) / 100.
- burst = int(observed + math.ceil(observed * percentage))
- bandwidths = [burst, observed]
- nitems = len(bandwidths) if (len(bandwidths) > 0) else float('nan')
- avg = int(math.ceil(float(sum(bandwidths)) / nitems))
- line = "bandwidth %s %s %s" % (avg, burst, observed)
- return line
-
-def makeExtraInfoDigestLine(hexdigest, version):
- """Create a line to embed the hex SHA-1 digest of the extrainfo.
-
- :param string hexdigest: Should be the hex-encoded (uppercase) output of
- the SHA-1 digest of the generated extrainfo document (this is the
- extra-info descriptor, just without the signature at the end). This is
- the same exact digest which gets signed by the OR server identity key,
- and that signature is appended to the extrainfo document to create the
- extra-info descriptor.
- :param string version: One of ``SERVER_VERSIONS``.
- :rtype: string
- :returns: An ``@type [bridge-]server-descriptor`` 'extra-info-digest'
- line.
- """
- line = ''
- if (version is not None) and shouldHaveOptPrefix(version):
- line += 'opt '
- line += 'extra-info-digest %s' % hexdigest
- return line
-
-def makeHSDirLine(version):
- """This line doesn't do much⦠all the cool kids are HSDirs these days.
-
- :param string version: One of ``SERVER_VERSIONS``.
- :rtype: string
- :returns: An ``@type [bridge-]server-descriptor`` 'hidden-service-dir'
- line.
- """
- line = ''
- if (version is not None) and shouldHaveOptPrefix(version):
- line += 'opt '
- line += 'hidden-service-dir'
- return line
-
-def createRSAKey(bits=1024):
- """Create a new RSA keypair.
-
- :param integer bits: The bitlength of the keypair to generate.
- :rtype: :class:`OpenSSL.crypto.PKey`
- :returns: An RSA keypair of bitlength ``bits``.
- """
- key = OpenSSL.crypto.PKey()
- key.generate_key(OpenSSL.crypto.TYPE_RSA, bits)
- if not key.check():
- raise OpenSSLKeyGenError("Couldn't create new RSA 1024-bit key")
- return key
-
-def createNTORKey():
- """Create a Curve25519 key."""
- if nacl is None:
- raise NotImplementedError
-
-def createKey(selfsign=True, digest='sha1'):
- """Create a set of public and private RSA keypairs and corresponding certs.
-
- :param boolean selfsign: If True, use the private key to sign the public
- certificate (otherwise, the private key will only sign the private
- certificate to which it is attached).
- :param string digest: The digest to use. (default: 'sha1')
- :rtype: 4-tuple
- :returns: (private_key, private_cert, public_key, public_cert)
- """
- privateKey = createRSAKey()
- privateCert = attachKey(privateKey, createTLSCert())
- publicKey = privateCert.get_pubkey()
- publicCert = attachKey(publicKey, createTLSCert(), selfsign=False)
-
- if selfsign:
- # We already signed the publicCert with the publicKey, now we need to
- # sign the publicCert with the privateKey
- publicCert.sign(privateKey, digest)
-
- return (privateKey, privateCert, publicKey, publicCert)
-
-def attachKey(key, cert, selfsign=True, digest='sha1', pem=False):
- """Attach a key to a cert and optionally self-sign the cert.
-
- :type key: :class:`OpenSSL.crypto.PKey`
- :param key: A previously generated key, used to generate the other half of
- the keypair.
- :type cert: :class:`OpenSSL.crypto.X509`
- :param cert: A TLS certificate without a public key attached to it, such
- as one created with :func:`createTLSCert`.
- :param boolean selfsign: If True, use the ``key`` to self-sign the
- ``cert``. Note that this will result in several nasty OpenSSL errors
- if you attempt to export the public key of a cert in order to create
- another cert which *only* holds the public key. (Otherwise, if you
- used the first cert in the following example, it contains both halves
- of the RSA keypair.) Do this instead:
-
- >>> secret_key = createRSAKey()
- >>> secret_cert = attachKey(secret_key, createTLSCert(selfsign=True))
- >>> public_key = secret_cert.get_pubkey()
- >>> public_cert = attachKey(public_key, createTLSCert, selfsign=False)
-
- :param string digest: The digest to use. Check your OpenSSL installation
- to see which are supported. We pretty much only care about 'sha1' and
- 'sha256' here.
- :param boolean pem: If True, return a 3-tuple of PEM-encoded strings, one
- for each of (certificate, private_key, public_key), where
- 'certificate' is the original ``cert`` with the ``key`` attached,
- 'private_key' is the private RSA modulus, primes, and exponents
- exported from the 'certificate', and 'public_key' is the public RSA
- modulus exported from the cert. NOTE: Using this when passing in a key
- with only the public RSA modulus (as described above) will result in
- nasty OpenSSL errors. Trust me, you do *not* want to try to parse
- OpenSSL's errors.
- :raises: An infinite, labyrinthine mire of non-Euclidean OpenSSL errors
- with non-deterministic messages and self-referential errorcodes,
- tangled upon itself in contempt of sanity, hope, and decent software
- engineering practices.
- :returns: If ``pem`` is True, then the values described there are
- returned. Otherwise, returns the ``cert`` with the ``key`` attached to
- it.
- """
- # Attach the key to the certificate
- cert.set_pubkey(key)
-
- if selfsign:
- # Self-sign the cert with the key, using the specified hash digest
- cert.sign(key, digest)
-
- if pem:
- certificate = OpenSSL.crypto.dump_certificate(PEM, cert)
- private_key = OpenSSL.crypto.dump_privatekey(PEM, key)
- public_key = OpenSSL.crypto.dump_privatekey(PEM, cert.get_pubkey())
- return certificate, private_key, public_key
- return cert
-
-def createTLSCert(lifetime=None):
- """Create a TLS certificate.
-
- :param integer lifetime: The time, in seconds, that the certificate should
- remain valid for.
- :rtype: :class:`OpenSSL.crypto.X509`
- :returns: A certificate, unsigned, and without a key attached to it.
- """
- if not lifetime:
- # see `router_initialize_tls_context()` in src/or/router.c
- lifetime = 5 + random.randint(0, 361)
- lifetime = lifetime * 24 * 3600
- if int(random.getrandbits(1)):
- lifetime -= 1
-
- cert = OpenSSL.crypto.X509()
-
- timeFormat = lambda x: time.strftime("%Y%m%d%H%M%SZ", x)
- now = time.time()
- before = time.gmtime(now)
- after = time.gmtime(now + lifetime)
- cert.set_notBefore(timeFormat(before))
- cert.set_notAfter(timeFormat(after))
-
- return cert
-
-def createTLSLinkCert(lifetime=7200):
- """Create a certificate for the TLS link layer.
-
- The TLS certificate used for the link layer between Tor relays, and
- between clients and their bridges/guards, has a shorter lifetime than the
- other certificates. Currently, these certs expire after two hours.
-
- :param integer lifetime: The time, in seconds, that the certificate should
- remain valid for.
- :rtype: :class:`OpenSSL.crypto.X509`
- :returns: A certificate, unsigned, and without a key attached to it.
- """
- cert = createTLSCert(lifetime)
- cert.get_subject().CN = 'www.' + getHexString(16) + '.net'
- cert.get_issuer().CN = 'www.' + getHexString(10) + '.com'
- return cert
-
-def getPEMPublicKey(cert):
- publicKey = OpenSSL.crypto.dump_privatekey(PEM, cert.get_pubkey())
- # It says "PRIVATE KEY" just because the stupid pyOpenSSL wrapper is
- # braindamaged. You can check that it doesn't include the RSA private
- # exponents and primes by substituting ``OpenSSL.crypto.FILETYPE_TEXT``
- # for the above ``PEM``.
- publicKey = re.sub(OPENSSL_BEGIN_KEY, TOR_BEGIN_KEY, publicKey)
- publicKey = re.sub(OPENSSL_END_KEY, TOR_END_KEY, publicKey)
- return publicKey
-
-def getPEMPrivateKey(key):
- privateKey = OpenSSL.crypto.dump_privatekey(PEM, key)
- privateKey = re.sub(OPENSSL_BEGIN_KEY, TOR_BEGIN_SK, privateKey)
- privateKey = re.sub(OPENSSL_END_KEY, TOR_END_SK, privateKey)
- return privateKey
-
-def makeOnionKeys(bridge=True, digest='sha1'):
- """Make all the keys and certificates necessary to fake an OR.
-
- The encodings for the various key and descriptor digests needed are
- described in dir-spec.txt and tor-spec.txt, the latter mostly for the
- padding and encoding used in the creation of an OR's keys.
-
- For the "router" line in a networkstatus document, the following encodings
- are specified:
-
- From dir-spec.txt, commit 36761c7d5, L1504-1512:
- |
- | [â¦] "Identity" is a hash of its
- | identity key, encoded in base64, with trailing equals sign(s)
- | removed. "Digest" is a hash of its most recent descriptor as
- | signed (that is, not including the signature), encoded in base64.
- |
-
- Before the hash digest of an OR's identity key is base64-encoded for
- inclusion in a networkstatus document, the hash digest is created in the
- following manner:
-
- From tor-spec.txt, commit 36761c7d5, L109-110:
- |
- | When we refer to "the hash of a public key", we mean the SHA-1 hash of the
- | DER encoding of an ASN.1 RSA public key (as specified in PKCS.1).
- |
-
- From tor-spec.txt, commit 36761c7d5, L785-787:
- |
- | The "legacy identity" and "identity fingerprint" fields are the SHA1
- | hash of the PKCS#1 ASN1 encoding of the next onion router's identity
- | (signing) key. (See 0.3 above.)
- |
-
- :param boolean bridge: If False, generate a server OR ID key, a signing
- key, and a TLS certificate/key pair. If True, generate a client ID key
- as well.
- :param string digest: The digest to use. (default: 'sha1')
- :returns: The server ID key, and a tuple of strings (fingerprint,
- onion-key, signing-key), where onion-key and secret key are the strings
- which should directly go into a server-descriptor. There are a *ton* of
- keys and certs in the this function. If you need more for some reason,
- this is definitely the thing you want to modify.
- """
- serverID = createKey(True)
- SIDSKey, SIDSCert, SIDPKey, SIDPCert = serverID
- serverLinkCert = createTLSLinkCert()
- serverLinkCert.sign(SIDSKey, digest)
-
- if bridge:
- # For a bridge, a "client" ID key is used to generate the fingerprint
- clientID = createKey(True)
- CIDSKey, CIDSCert, CIDPKey, CIDPCert = clientID
-
- # XXX I think we're missing some of the signatures
- # see torspec.git/tor-spec.txt §4.2 on CERTS cells
- clientLinkCert = createTLSLinkCert()
- clientLinkCert.sign(CIDSKey, digest)
- else:
- CIDSKey, CIDSCert, CIDPKey, CIDPCert = serverID
-
- signing = createKey()
- signSKey, signSCert, signPKey, signPCert = signing
- onion = createKey()
- onionSKey, onionSCert, onionPKey, onionPCert = onion
-
- onionKeyString = 'onion-key\n%s' % getPEMPublicKey(onionPCert)
- signingKeyString = 'signing-key\n%s' % getPEMPublicKey(signPCert)
-
- return SIDSKey, SIDPCert, (onionKeyString, signingKeyString)
-
-def generateExtraInfo(fingerprint, ts, ipv4, port):
- """Create an OR extra-info document.
-
- See §2.2 "Extra-info documents" in torspec.git/dir-spec.txt.
-
- :param string fingerprint: A space-separated, hex-encoded, SHA-1 digest of
- the OR's private identity key. See :func:`convertToSpaceyFingerprint`.
- :param string ts: An ISO-8601 timestamp. See :func:`makeTimeStamp`.
- :param string ipv4: An IPv4 address.
- :param string port: The OR's ORPort.
- :rtype: string
- :returns: An extra-info document (unsigned).
- """
- extra = []
- extra.append("extra-info Unnamed %s" % fingerprint)
- extra.append("published %s" % ts)
- extra.append("write-history %s (900 s)\n3188736,2226176,2866176" % ts)
- extra.append("read-history %s (900 s)\n3891200,2483200,2698240" % ts)
- extra.append("dirreq-write-history %s (900 s)\n1024,0,2048" % ts)
- extra.append("dirreq-read-history %s (900 s)\n0,0,0" % ts)
- extra.append("geoip-db-digest %s\ngeoip6-db-digest %s"
- % (getHexString(40), getHexString(40)))
- extra.append("dirreq-stats-end %s (86400 s)\ndirreq-v3-ips" % ts)
- extra.append("dirreq-v3-reqs\ndirreq-v3-resp")
- extra.append(
- "ok=16,not-enough-sigs=0,unavailable=0,not-found=0,not-modified=0,busy=0")
- extra.append("dirreq-v3-direct-dl complete=0,timeout=0,running=0")
- extra.append("dirreq-v3-tunneled-dl complete=12,timeout=0,running=0")
- extra.append("transport obfs3 %s:%d" % (ipv4, port + 1))
- extra.append("transport obfs2 %s:%d" % (ipv4, port + 2))
- extra.append("bridge-stats-end %s (86400 s)\nbridge-ips ca=8" % ts)
- extra.append("bridge-ip-versions v4=8,v6=0\nbridge-ip-transports <OR>=8")
- extra.append("router-signature\n")
-
- return '\n'.join(extra)
-
-def generateNetstatus(idkey_digest, server_desc_digest, timestamp,
- ipv4, orport, ipv6=None, dirport=None,
- flags='Fast Guard Running Stable Valid',
- bandwidth_line=None):
- """Generate an ``@type networkwork-status`` document (unsigned).
-
- DOCDOC
-
- :param string idkey_digest: The SHA-1 digest of the router's public identity
- key.
- :param XXX server_desc_digest: The SHA-1 digest of the router's
- ``@type [bridge-]server-descriptor``, before the descriptor is signed.
- :param XXX timestamp:
- """
-
- idkey_b64 = binascii.b2a_base64(idkey_digest)
- idb64 = str(idkey_b64).strip().rstrip('==')
- server_b64 = binascii.b2a_base64(server_desc_digest)
- srvb64 = str(server_b64).strip().rstrip('==')
-
- if bandwidth_line is not None:
- bw = int(bandwidth_line.split()[-1]) / 1024 # The 'observed' value
- dirport = dirport if dirport else 0
-
- status = []
- status.append("r Unnamed %s %s %s %s %s %d" % (idb64, srvb64, timestamp,
- ipv4, orport, dirport))
- if ipv6 is not None:
- status.append("a [%s]:%s" % (ipv6, orport))
- status.append("s %s\nw Bandwidth=%s\np reject 1-65535\n" % (flags, bw))
-
- return '\n'.join(status)
-
-def signDescriptorDigest(key, descriptorDigest, digest='sha1'):
- """Ugh...I hate OpenSSL.
-
- The extra-info-digest is a SHA-1 hash digest of the extrainfo document,
- that is, the entire extrainfo descriptor up until the end of the
- 'router-signature' line and including the newline, but not the actual
- signature.
-
- The signature at the end of the extra-info descriptor is a signature of
- the above extra-info-digest. This signature is appended to the end of the
- extrainfo document, and the extra-info-digest is added to the
- 'extra-info-digest' line of the [bridge-]server-descriptor.
-
- The first one of these was created with a raw digest, the second with a
- hexdigest. They both encode the the 'sha1' digest type if you check the
- `-asnparse` output (instead of `-raw -hexdump`).
-
- .. command:: openssl rsautl -inkey eiprivkey -verify -in eisig1 -raw -hexdump
- |
- | 0000 - 00 01 ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0010 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0020 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0030 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0040 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0050 - ff ff ff ff ff ff ff ff-ff ff ff ff 00 30 21 30 .............0!0
- | 0060 - 09 06 05 2b 0e 03 02 1a-05 00 04 14 42 25 41 fb ...+........B%A.
- | 0070 - 82 ef 11 f4 5f 2c 95 53-67 2d bb fe 7f c2 34 7f ...._,.Sg-....4.
-
- .. command:: openssl rsautl -inkey eiprivkey -verify -in eisig2 -raw -hexdump
- |
- | 0000 - 00 01 ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0010 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0020 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0030 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0040 - ff ff ff ff ff ff ff ff-ff ff ff ff ff ff ff ff ................
- | 0050 - ff ff ff ff ff ff ff ff-ff ff ff ff 00 30 21 30 .............0!0
- | 0060 - 09 06 05 2b 0e 03 02 1a-05 00 04 14 44 30 ab 90 ...+........D0..
- | 0070 - 93 d1 08 21 df 87 c2 39-2a 04 1c a5 bb 34 44 cd ...!...9*....4D.
-
- .. todo:: See the RSA PKCS_ Standard v2.2 for why this function is totally
- wrong.
-
- .. _PKCS: http://www.emc.com/collateral/white-papers/h11300-pkcs-1v2-2-rsa-cryptography-standard-wp.pdf
-
- :type key: :class:`OpenSSL.crypto.PKey`
- :param key: An RSA private key.
- :param string descriptorDigest: The raw SHA-1 digest of any descriptor
- document.
- :param string digest: The digest to use. (default: 'sha1')
- """
- sig = binascii.b2a_base64(OpenSSL.crypto.sign(key, descriptorDigest,
- digest))
- sigCopy = sig
- originalLength = len(sigCopy.replace('\n', ''))
-
- # Only put 64 bytes of the base64 signature per line:
- sigSplit = []
- while len(sig) > 0:
- sigSplit.append(sig[:64])
- sig = sig[64:]
- sigFormatted = '\n'.join(sigSplit)
-
- sigFormattedCopy = sigFormatted
- formattedLength = len(sigFormattedCopy.replace('\n', ''))
-
- if originalLength != formattedLength:
- print("WARNING: signDescriptorDocument(): %s"
- % "possible bad reformatting for signature.")
- print("DEBUG: signDescriptorDocument(): original=%d formatted=%d"
- % (originalLength, formattedLength))
- print("DEBUG: original:\n%s\nformatted:\n%s"
- % (sigCopy, sigFormatted))
-
- sigWithHeaders = TOR_BEGIN_SIG + '\n' \
- + sigFormatted \
- + TOR_END_SIG + '\n'
- return sigWithHeaders
-
-def generateDescriptors():
- """Create keys, certs, signatures, documents and descriptors for an OR.
-
- :returns:
- A 3-tuple of strings:
- - a ``@type [bridge-]extra-info`` descriptor,
- - a ``@type [bridge-]server-descriptor``, and
- - a ``@type network-status`` document
- for a mock Tor relay/bridge.
- """
- ipv4 = randomIPv4()
- ipv6 = randomIPv6()
- port = randomPort()
-
- vers = random.choice(SERVER_VERSIONS)
- uptime = int(random.randint(1800, 63072000))
- bandwidth = makeBandwidthLine()
- timestamp = makeTimeStamp(variation=True, period=36)
- protocols = makeProtocolsLine(vers)
-
- SIDSKey, SIDPCert, (onionkey, signingkey) = makeOnionKeys()
- idkey_private = getPEMPrivateKey(SIDSKey)
- idkey_digest = hashlib.sha1(idkey_private).digest()
-
- idkey_public = OpenSSL.crypto.dump_privatekey(ASN1,
- SIDPCert.get_pubkey())
- idkey_public = re.sub(OPENSSL_BEGIN_KEY, '', idkey_public)
- idkey_public = re.sub(OPENSSL_END_KEY, '', idkey_public)
- idkey_public = idkey_public.strip()
-
- ident_digest = hashlib.sha1(idkey_public).digest()
- fingerprint = hashlib.sha1(idkey_public).hexdigest().upper()
- fpr = convertToSpaceyFingerprint(fingerprint)
-
- extrainfo_document = generateExtraInfo(fingerprint, timestamp, ipv4, port)
- extrainfo_digest = hashlib.sha1(extrainfo_document).digest()
- extrainfo_hexdigest = hashlib.sha1(extrainfo_document).hexdigest().upper()
- extrainfo_sig = signDescriptorDigest(SIDSKey, extrainfo_digest)
- extrainfo_desc = extrainfo_document + extrainfo_sig
-
- server = []
- server.append("@purpose bridge")
- server.append("router Unnamed %s %s 0 0" % (ipv4, port))
- server.append("or-address [%s]:%s" % (ipv6, port))
- server.append("platform Tor %s on Linux" % vers)
- server.append("%s\npublished %s" % (protocols, timestamp))
- server.append("%s" % makeFingerprintLine(fingerprint, vers))
- server.append("uptime %s\n%s" % (uptime, bandwidth))
- server.append("%s" % makeExtraInfoDigestLine(extrainfo_hexdigest, vers))
- server.append("%s%s%s" % (onionkey, signingkey, makeHSDirLine(vers)))
- server.append("contact Somebody <somebody at example.com>")
- if nacl is not None:
- server.append("ntor-onion-key %s"
- % binascii.b2a_base64(createNTORKey()))
- server.append("reject *:*\nrouter-signature\n")
- server_desc = '\n'.join(server)
-
- server_desc_digest = hashlib.sha1(server_desc).digest()
- netstatus_desc = generateNetstatus(ident_digest, server_desc_digest,
- timestamp, ipv4, port, ipv6=ipv6,
- bandwidth_line=bandwidth)
- server_desc += signDescriptorDigest(SIDSKey, server_desc_digest)
- return extrainfo_desc, server_desc, netstatus_desc
-
-def writeDescToFile(filename, descriptors):
- """Open ``filename`` and write a string containing descriptors into it.
-
- :param string filename: The name of the file to write to.
- :param string descriptors: A giant string containing descriptors,
- newlines, formatting, whatever is necessary to make it look like a
- file tor would generate.
- """
- encoding = sys.getfilesystemencoding()
- descript = descriptors.encode(encoding, 'replace')
- try:
- with open(filename, 'wb', encoding=encoding, errors='replace') as fh:
- fh.write(descript)
- fh.flush()
- except (IOError, OSError) as err:
- print("Failure while attempting to write descriptors to file '%s': %s"
- % (filename, err.message))
-
-def create(count):
- """Generate all types of descriptors and write them to files.
-
- :param integer count: How many sets of descriptors to generate, i.e. how
- many mock bridges/relays to create.
- """
- if nacl is None:
- print("WARNING: Can't import PyNaCl. NTOR key generation is disabled.")
- print("Generating %d bridge descriptors..." % int(count))
-
- server_descriptors = list()
- netstatus_consensus = list()
- extrainfo_descriptors = list()
- try:
- for i in xrange(int(count)):
- print(".", end='')
- try:
- extrainfo, server, netstatus = generateDescriptors()
- except Exception as error:
- err, msg, tb = sys.exc_info()
- print(traceback.print_tb(tb))
- print(error)
- else:
- server_descriptors.append(server)
- netstatus_consensus.append(netstatus)
- extrainfo_descriptors.append(extrainfo)
- except KeyboardInterrupt as keyint:
- print("Received keyboard interrupt.")
- print("Stopping descriptor creation and exiting.")
- code = 1515
- finally:
- print("\nWriting descriptors to files...", end="")
-
- cached = "cached-extrainfo.new"
- descriptor_files = {
- "networkstatus-bridges": ''.join(netstatus_consensus),
- "bridge-descriptors": ''.join(server_descriptors),
- "cached-extrainfo.new": ''.join(extrainfo_descriptors)}
-
- if not os.path.isfile(cached):
- with open(cached, 'wb') as fh:
- fh.flush()
- if os.path.isfile(cached):
- os.rename(cached, "cached-extrainfo")
-
- for fn, giantstring in descriptor_files.items():
- writeDescToFile(fn, giantstring)
- print("Done.")
- code = 0
- sys.exit(code)
-
-if __name__ == "__main__":
- try:
- parser = getArgParser()
- options = parser.parse_args()
-
- if options.quiet:
- print = lambda x: True
- if options.version:
- print("gen_bridge_descriptors-%s" % __version__)
- sys.exit(0)
- if options.descriptors and (options.descriptors > 0):
- create(options.descriptors)
- else:
- raise SystemExit(parser.format_help())
-
- except Exception as error:
- raise SystemExit(error)
More information about the tor-commits
mailing list