commit 5d1a11fa4f479cac4d4987d9841889ea251b2eba Author: Damian Johnson atagar@torproject.org Date: Wed Sep 26 09:00:22 2012 -0700
Unit test for parsing a minimal key certificate
Test for minimal key certificate parsing, and related fixes for the KeyCertificate class. The main gotcha in this is that KeyCertificates don't need to have a prescribed order (unlike other network status document fields). --- run_tests.py | 2 + stem/descriptor/networkstatus.py | 14 +--- test/unit/descriptor/networkstatus/__init__.py | 2 +- .../descriptor/networkstatus/key_certificate.py | 79 ++++++++++++++++++++ 4 files changed, 85 insertions(+), 12 deletions(-)
diff --git a/run_tests.py b/run_tests.py index af2d09f..54e74b2 100755 --- a/run_tests.py +++ b/run_tests.py @@ -21,6 +21,7 @@ import test.unit.descriptor.reader import test.unit.descriptor.server_descriptor import test.unit.descriptor.extrainfo_descriptor import test.unit.descriptor.networkstatus.entry +import test.unit.descriptor.networkstatus.key_certificate import test.unit.descriptor.networkstatus.document import test.unit.response.control_line import test.unit.response.control_message @@ -117,6 +118,7 @@ UNIT_TESTS = ( test.unit.descriptor.server_descriptor.TestServerDescriptor, test.unit.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor, test.unit.descriptor.networkstatus.entry.TestRouterStatusEntry, + test.unit.descriptor.networkstatus.key_certificate.TestKeyCertificate, test.unit.descriptor.networkstatus.document.TestNetworkStatusDocument, test.unit.exit_policy.rule.TestExitPolicyRule, test.unit.exit_policy.policy.TestExitPolicy, diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 46adbfe..01fb2f3 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -801,7 +801,7 @@ class KeyCertificate(stem.descriptor.Descriptor): ***** mandatory attribute """
- def __init__(self, raw_content, validate): + def __init__(self, raw_content, validate = True): super(KeyCertificate, self).__init__(raw_content)
self.version = None @@ -817,7 +817,7 @@ class KeyCertificate(stem.descriptor.Descriptor):
self._unrecognized_lines = []
- self._parse(raw_contents, validate) + self._parse(raw_content, validate)
def _parse(self, content, validate): """ @@ -847,14 +847,6 @@ class KeyCertificate(stem.descriptor.Descriptor): entry_count = len(entries.get(keyword, [])) if entry_count > 1: raise ValueError("Key certificates can only have a single '%s' line, got %i:\n%s" % (keyword, entry_count, content)) - - # Check that our field's order matches the spec. This isn't explicitely - # stated in the spec, but the network status document requires a specific - # order so it stands to reason that the key certificate (which is in it) - # needs ot match a prescribed order too. - - fields = [attr[0] for attr in KEY_CERTIFICATE_PARAMS] - _check_for_misordered_fields(entries, fields)
for keyword, values in entries.items(): value, block_contents = values[0] @@ -936,7 +928,7 @@ class KeyCertificate(stem.descriptor.Descriptor): :returns: a list of unrecognized lines """
- return self.unrecognized_lines + return self._unrecognized_lines
# TODO: microdescriptors have a slightly different format (including a # 'method') - should probably be a subclass diff --git a/test/unit/descriptor/networkstatus/__init__.py b/test/unit/descriptor/networkstatus/__init__.py index d8483e2..d8c9657 100644 --- a/test/unit/descriptor/networkstatus/__init__.py +++ b/test/unit/descriptor/networkstatus/__init__.py @@ -2,5 +2,5 @@ Unit tests for stem.descriptor.networkstatus. """
-__all__ = ["entry", "document"] +__all__ = ["entry", "key_certificate", "document"]
diff --git a/test/unit/descriptor/networkstatus/key_certificate.py b/test/unit/descriptor/networkstatus/key_certificate.py new file mode 100644 index 0000000..2d779e7 --- /dev/null +++ b/test/unit/descriptor/networkstatus/key_certificate.py @@ -0,0 +1,79 @@ +""" +Unit tests for the KeyCertificate of stem.descriptor.networkstatus. +""" + +import datetime +import unittest + +from stem.descriptor.networkstatus import KeyCertificate + +sig_block = """\ +-----BEGIN %s----- +MIGJAoGBAJ5itcJRYNEM3Qf1OVWLRkwjqf84oXPc2ZusaJ5zOe7TVvBMra9GNyc0 +NM9y6zVkHCAePAjr4KbW/8P1olA6FUE2LV9bozaU1jFf6K8B2OELKs5FUEW+n+ic +GM0x6MhngyXonWOcKt5Gj+mAu5lrno9tpNbPkz2Utr/Pi0nsDhWlAgMBAAE= +-----END %s-----\ +""" + +RSA_SIG = sig_block % ("RSA PUBLIC KEY", "RSA PUBLIC KEY") +KEY_SIG = sig_block % ("SIGNATURE", "SIGNATURE") + +KEY_CERTIFICATE_ATTR = ( + ("dir-key-certificate-version", "3"), + ("fingerprint", "27B6B5996C426270A5C95488AA5BCEB6BCC86956"), + ("dir-key-published", "2011-11-28 21:51:04"), + ("dir-key-expires", "2012-11-28 21:51:04"), + ("dir-identity-key", "\n" + RSA_SIG), + ("dir-signing-key", "\n" + RSA_SIG), + ("dir-key-certification", "\n" + KEY_SIG), +) + +def get_key_certificate(attr = None, exclude = None): + """ + Constructs a minimal key certificate with the given attributes. + + :param dict attr: keyword/value mappings to be included in the entry + :param list exclude: mandatory keywords to exclude from the entry + + :returns: str with customized key certificate content + """ + + descriptor_lines = [] + if attr is None: attr = {} + if exclude is None: exclude = [] + attr = dict(attr) # shallow copy since we're destructive + + for keyword, value in KEY_CERTIFICATE_ATTR: + if keyword in exclude: continue + elif keyword in attr: + value = attr[keyword] + del attr[keyword] + + descriptor_lines.append("%s %s" % (keyword, value)) + + # dump in any unused attributes + for attr_keyword, attr_value in attr.items(): + descriptor_lines.append("%s %s" % (attr_keyword, attr_value)) + + return "\n".join(descriptor_lines) + +class TestKeyCertificate(unittest.TestCase): + def test_minimal(self): + """ + Parses a minimal key certificate. + """ + + certificate = KeyCertificate(get_key_certificate()) + + self.assertEqual(3, certificate.version) + self.assertEqual(None, certificate.address) + self.assertEqual(None, certificate.dir_port) + self.assertEqual("27B6B5996C426270A5C95488AA5BCEB6BCC86956", certificate.fingerprint) + self.assertEqual(RSA_SIG, certificate.identity_key) + self.assertEqual(datetime.datetime(2011, 11, 28, 21, 51, 4), certificate.published) + self.assertEqual(datetime.datetime(2012, 11, 28, 21, 51, 4), certificate.expires) + self.assertEqual(RSA_SIG, certificate.signing_key) + self.assertEqual(None, certificate.crosscert) + self.assertEqual(KEY_SIG, certificate.certification) + self.assertEqual([], certificate.get_unrecognized_lines()) +
tor-commits@lists.torproject.org