[tor-commits] [stem/master] Randomize created server descirptor fields

atagar at torproject.org atagar at torproject.org
Tue Jun 27 15:35:07 UTC 2017


commit 25bf050edf71a7653970aa9ab41d70e4b4219b63
Author: Damian Johnson <atagar at torproject.org>
Date:   Tue Jun 27 08:31:40 2017 -0700

    Randomize created server descirptor fields
    
    Now that we're providing a method to create descriptors to callers (rather than
    just our own tests) we should randomize the default attributes. Gonna do this
    with all descriptor types, but starting with server descriptors since we just
    added signing for 'em.
---
 run_tests.py                              | 40 +++++++++++++------------------
 stem/descriptor/__init__.py               | 22 +++++++++++++++++
 stem/descriptor/server_descriptor.py      | 36 +++++++++++++++-------------
 test/unit/descriptor/__init__.py          |  6 ++---
 test/unit/descriptor/export.py            |  5 +++-
 test/unit/descriptor/server_descriptor.py |  9 ++++---
 test/unit/tutorial.py                     |  2 +-
 7 files changed, 71 insertions(+), 49 deletions(-)

diff --git a/run_tests.py b/run_tests.py
index 802a6b4..c0d9cbc 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -310,10 +310,8 @@ def main():
 
   _print_static_issues(static_check_issues)
 
-  runtime_label = '(%i seconds)' % (time.time() - start_time)
-
   if error_tracker.has_errors_occured():
-    println('TESTING FAILED %s' % runtime_label, ERROR, STDERR)
+    println('TESTING FAILED (%i seconds)' % (time.time() - start_time), ERROR, STDERR)
 
     for line in error_tracker:
       println('  %s' % line, ERROR, STDERR)
@@ -329,7 +327,7 @@ def main():
     if skipped_tests > 0:
       println('%i TESTS WERE SKIPPED' % skipped_tests, STATUS)
 
-    println('TESTING PASSED %s\n' % runtime_label, SUCCESS)
+    println('TESTING PASSED (%i seconds)\n' % (time.time() - start_time), SUCCESS)
 
   new_capabilities = test.get_new_capabilities()
 
@@ -369,26 +367,23 @@ def _print_static_issues(static_check_issues):
 def _run_test(args, test_class, output_filters, logging_buffer):
   start_time = time.time()
 
+  # Test classes look like...
+  #
+  #   test.unit.util.conf.TestConf.test_parse_enum_csv
+  #
+  # We want to strip the 'test.unit.' or 'test.integ.' prefix since it's
+  # redundant. We also want to drop the test class name. The individual test
+  # name at the end it optional (only present if we used the '--test'
+  # argument).
+
+  label_comp = test_class.split('.')[2:]
+  del label_comp[-1 if label_comp[-1][0].isupper() else -2]
+  test_label = '  %-52s' % ('.'.join(label_comp) + '...')
+
   if args.verbose:
     test.output.print_divider(test_class)
   else:
-    # Test classes look like...
-    #
-    #   test.unit.util.conf.TestConf.test_parse_enum_csv
-    #
-    # We want to strip the 'test.unit.' or 'test.integ.' prefix since it's
-    # redundant. We also want to drop the test class name. The individual test
-    # name at the end it optional (only present if we used the '--test'
-    # argument).
-
-    label_comp = test_class.split('.')[2:]
-    del label_comp[-1 if label_comp[-1][0].isupper() else -2]
-    label = '.'.join(label_comp)
-
-    label = '  %s...' % label
-    label = '%-54s' % label
-
-    println(label, STATUS, NO_NL)
+    println(test_label, STATUS, NO_NL)
 
   try:
     suite = unittest.TestLoader().loadTestsFromName(test_class)
@@ -413,7 +408,7 @@ def _run_test(args, test_class, output_filters, logging_buffer):
     println(' success (%0.2fs)' % (time.time() - start_time), SUCCESS)
   else:
     if args.quiet:
-      println(label, STATUS, NO_NL, STDERR)
+      println(test_label, STATUS, NO_NL, STDERR)
       println(' failed (%0.2fs)' % (time.time() - start_time), ERROR, STDERR)
       println(test.output.apply_filters(test_results.getvalue(), *output_filters), STDERR)
     else:
@@ -421,7 +416,6 @@ def _run_test(args, test_class, output_filters, logging_buffer):
       println(test.output.apply_filters(test_results.getvalue(), *output_filters), NO_NL)
 
   test.output.print_logging(logging_buffer)
-
   return run_result
 
 
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index bf336fb..80901af 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -42,6 +42,7 @@ import codecs
 import copy
 import hashlib
 import os
+import random
 import re
 import string
 import tarfile
@@ -952,6 +953,27 @@ def _get_pseudo_pgp_block(remaining_contents):
     return None
 
 
+def _random_ipv4_address():
+  return '%i.%i.%i.%i' % (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
+
+
+def _random_date():
+  return '%i-%02i-%02i %02i:%02i:%02i' % (random.randint(2000, 2015), random.randint(1, 12), random.randint(1, 20), random.randint(0, 23), random.randint(0, 59), random.randint(0, 59))
+
+
+def _random_crypto_blob(block_type = None):
+  """
+  Provides a random string that can be used for crypto blocks.
+  """
+
+  crypto_blob = stem.util.str_tools._split_by_length(base64.b64encode('%0140x' % random.randrange(16 ** 140)), 64)
+
+  if block_type:
+    return '\n-----BEGIN %s-----\n%s\n-----END %s-----' % (block_type, crypto_blob, block_type)
+  else:
+    return crypto_blob
+
+
 def _descriptor_components(raw_contents, validate, extra_keywords = (), non_ascii_fields = ()):
   """
   Initial breakup of the server descriptor contents to make parsing easier.
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 8dbd0f0..607908d 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -35,7 +35,9 @@ import base64
 import binascii
 import functools
 import hashlib
+import random
 import re
+import sys
 
 import stem.descriptor.certificate
 import stem.descriptor.extrainfo_descriptor
@@ -49,7 +51,6 @@ import stem.version
 from stem.util import str_type
 
 from stem.descriptor import (
-  CRYPTO_BLOB,
   PGP_BLOCK_END,
   Descriptor,
   _descriptor_content,
@@ -65,6 +66,9 @@ from stem.descriptor import (
   _parse_forty_character_hex,
   _parse_protocol_line,
   _parse_key_block,
+  _random_ipv4_address,
+  _random_date,
+  _random_crypto_blob,
 )
 
 try:
@@ -112,19 +116,6 @@ SINGLE_FIELDS = (
 DEFAULT_IPV6_EXIT_POLICY = stem.exit_policy.MicroExitPolicy('reject 1-65535')
 REJECT_ALL_POLICY = stem.exit_policy.ExitPolicy('reject *:*')
 
-RELAY_SERVER_HEADER = (
-  ('router', 'caerSidi 71.35.133.197 9001 0 0'),
-  ('published', '2012-03-01 17:15:27'),
-  ('bandwidth', '153600 256000 104590'),
-  ('reject', '*:*'),
-  ('onion-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
-  ('signing-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
-)
-
-RELAY_SERVER_FOOTER = (
-  ('router-signature', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB),
-)
-
 BRIDGE_SERVER_HEADER = (
   ('router', 'Unnamed 10.45.227.253 9001 0 0'),
   ('router-digest', '006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4'),
@@ -818,6 +809,19 @@ class RelayDescriptor(ServerDescriptor):
 
   @classmethod
   def content(cls, attr = None, exclude = (), sign = False, private_signing_key = None):
+    base_header = (
+      ('router', 'Unnamed%i %s 9001 0 0' % (random.randint(0, sys.maxint), _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')),
+    )
+
+    base_footer = (
+      ('router-signature', _random_crypto_blob('SIGNATURE')),
+    )
+
     if sign:
       if not stem.prereq.is_crypto_available():
         raise ImportError('Signing requires the cryptography module')
@@ -861,12 +865,12 @@ class RelayDescriptor(ServerDescriptor):
         format = serialization.PublicFormat.PKCS1,
       ).strip()
 
-      content = _descriptor_content(attr, exclude, sign, RELAY_SERVER_HEADER) + b'\nrouter-signature\n'
+      content = _descriptor_content(attr, exclude, sign, base_header) + b'\nrouter-signature\n'
       signature = base64.b64encode(private_signing_key.sign(content, padding.PKCS1v15(), hashes.SHA1()))
 
       return content + b'\n'.join([b'-----BEGIN SIGNATURE-----'] + stem.util.str_tools._split_by_length(signature, 64) + [b'-----END SIGNATURE-----\n'])
     else:
-      return _descriptor_content(attr, exclude, sign, RELAY_SERVER_HEADER, RELAY_SERVER_FOOTER)
+      return _descriptor_content(attr, exclude, sign, base_header, base_footer)
 
   @classmethod
   def create(cls, attr = None, exclude = (), validate = True, sign = False, private_signing_key = None):
diff --git a/test/unit/descriptor/__init__.py b/test/unit/descriptor/__init__.py
index cd35ae1..bf0e861 100644
--- a/test/unit/descriptor/__init__.py
+++ b/test/unit/descriptor/__init__.py
@@ -29,7 +29,7 @@ def base_expect_invalid_attr(cls, default_attr, default_value, test, desc_attrs,
   return base_expect_invalid_attr_for_text(cls, default_attr, default_value, test, cls.content(desc_attrs), attr, expected_value)
 
 
-def base_expect_invalid_attr_for_text(cls, default_attr, default_value, test, desc_text, attr = None, expected_value = None):
+def base_expect_invalid_attr_for_text(cls, default_attr, default_prefix, test, desc_text, attr = None, expected_value = None):
   """
   Asserts that construction will fail due to desc_text having a malformed
   attribute. If an attr is provided then we check that it matches an expected
@@ -44,7 +44,7 @@ def base_expect_invalid_attr_for_text(cls, default_attr, default_value, test, de
     # constructed without validation
 
     test.assertEqual(expected_value, getattr(desc, attr))
-  elif default_attr and default_value:
-    test.assertEqual(default_value, getattr(desc, default_attr))  # check a default attribute
+  elif default_attr and default_prefix:
+    test.assertTrue(getattr(desc, default_attr).startswith(default_prefix))  # check a default attribute
 
   return desc
diff --git a/test/unit/descriptor/export.py b/test/unit/descriptor/export.py
index cd005d1..a33c1d4 100644
--- a/test/unit/descriptor/export.py
+++ b/test/unit/descriptor/export.py
@@ -25,7 +25,10 @@ class TestExport(unittest.TestCase):
       self.skipTest('(header added in python 2.7)')
       return
 
-    desc = RelayDescriptor.create()
+    desc = RelayDescriptor.create({
+      'router': 'caerSidi 71.35.133.197 9001 0 0',
+      'published': '2012-03-01 17:15:27',
+    })
 
     desc_csv = export_csv(desc, included_fields = ('nickname', 'address', 'published'), header = False)
     expected = 'caerSidi,71.35.133.197,2012-03-01 17:15:27\n'
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 227df12..2c47bdf 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -40,8 +40,8 @@ TARFILE_FINGERPRINTS = set([
   str_type('1F43EE37A0670301AD9CB555D94AFEC2C89FDE86'),
 ])
 
-expect_invalid_attr = functools.partial(base_expect_invalid_attr, RelayDescriptor, 'nickname', 'caerSidi')
-expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, RelayDescriptor, 'nickname', 'caerSidi')
+expect_invalid_attr = functools.partial(base_expect_invalid_attr, RelayDescriptor, 'nickname', 'Unnamed')
+expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, RelayDescriptor, 'nickname', 'Unnamed')
 
 
 class TestServerDescriptor(unittest.TestCase):
@@ -440,11 +440,10 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     attributes.
     """
 
-    desc = RelayDescriptor.create()
+    desc = RelayDescriptor.create({'router': 'caerSidi 71.35.133.197 9001 0 0'})
     self.assertEqual('caerSidi', desc.nickname)
     self.assertEqual('71.35.133.197', desc.address)
     self.assertEqual(None, desc.fingerprint)
-    self.assertTrue(stem.descriptor.CRYPTO_BLOB in desc.onion_key)
 
   def test_with_opt(self):
     """
@@ -625,7 +624,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     self.assertRaises(ValueError, list, desc_iter)
 
     desc_text = b'@pepperjack very tasty\n at mushrooms not so much\n'
-    desc_text += RelayDescriptor.content()
+    desc_text += RelayDescriptor.content({'router': 'caerSidi 71.35.133.197 9001 0 0'})
     desc_iter = stem.descriptor.server_descriptor._parse_file(io.BytesIO(desc_text))
 
     desc_entries = list(desc_iter)
diff --git a/test/unit/tutorial.py b/test/unit/tutorial.py
index cdfb014..b0f5b71 100644
--- a/test/unit/tutorial.py
+++ b/test/unit/tutorial.py
@@ -162,7 +162,7 @@ class TestTutorial(unittest.TestCase):
   @patch('stem.descriptor.reader.DescriptorReader', spec = DescriptorReader)
   def test_mirror_mirror_on_the_wall_4(self, reader_mock, stdout_mock):
     reader = reader_mock().__enter__()
-    reader.__iter__.return_value = iter([RelayDescriptor.create()])
+    reader.__iter__.return_value = iter([RelayDescriptor.create({'router': 'caerSidi 71.35.133.197 9001 0 0'})])
 
     exec_documentation_example('past_descriptors.py')
     self.assertEqual('found relay caerSidi (None)\n', stdout_mock.getvalue())



More information about the tor-commits mailing list