commit fd58bc5a565bca11d372188a4add167775f091c1 Author: Damian Johnson atagar@torproject.org Date: Sun Aug 19 17:55:25 2012 -0700
Replacing RouterStatusEntry's identity with fingerprint
Truncated base64 encoding of a relay's fingerprint? Not useful. Common hex encoded fingerprint that's used everywhere else? Very useful.
Decoding the RouterStatusEntry's identity value and adding a unit test for it. Unfortunately the networkstatus module didn't have any unit tests at all so this is gonna take a while to remedy... --- run_tests.py | 4 ++- stem/descriptor/networkstatus.py | 59 ++++++++++++++++++++++++++------ test/integ/descriptor/networkstatus.py | 14 ++++---- test/unit/descriptor/__init__.py | 2 +- test/unit/descriptor/networkstatus.py | 27 ++++++++++++++ 5 files changed, 86 insertions(+), 20 deletions(-)
diff --git a/run_tests.py b/run_tests.py index 8d115f1..d2e4da7 100755 --- a/run_tests.py +++ b/run_tests.py @@ -20,6 +20,7 @@ import test.unit.descriptor.export import test.unit.descriptor.reader import test.unit.descriptor.server_descriptor import test.unit.descriptor.extrainfo_descriptor +import test.unit.descriptor.networkstatus import test.unit.response.control_line import test.unit.response.control_message import test.unit.response.getinfo @@ -114,6 +115,7 @@ UNIT_TESTS = ( test.unit.descriptor.reader.TestDescriptorReader, test.unit.descriptor.server_descriptor.TestServerDescriptor, test.unit.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor, + test.unit.descriptor.networkstatus.TestNetworkStatus, test.unit.exit_policy.rule.TestExitPolicyRule, test.unit.exit_policy.policy.TestExitPolicy, test.unit.version.TestVersion, @@ -135,7 +137,7 @@ INTEG_TESTS = ( test.integ.descriptor.reader.TestDescriptorReader, test.integ.descriptor.server_descriptor.TestServerDescriptor, test.integ.descriptor.extrainfo_descriptor.TestExtraInfoDescriptor, - test.integ.descriptor.networkstatus.TestNetworkStatusDocument, + test.integ.descriptor.networkstatus.TestNetworkStatus, test.integ.version.TestVersion, test.integ.response.protocolinfo.TestProtocolInfo, test.integ.process.TestProcess, diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 7eab8da..485234f 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -41,6 +41,7 @@ The documents can be obtained from any of the following sources... """
import re +import base64 import datetime
try: @@ -325,7 +326,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor): documents.
:var str nickname: directory authority's nickname - :var str identity: uppercase hex fingerprint of the authority's identity key + :var str fingerprint: uppercase hex fingerprint of the authority's identity key :var str address: hostname :var str ip: current IP address :var int dirport: current directory port @@ -352,13 +353,13 @@ class DirectoryAuthority(stem.descriptor.Descriptor): """
super(DirectoryAuthority, self).__init__(raw_content) - self.nickname, self.identity, self.address, self.ip = None, None, None, None + self.nickname, self.fingerprint, self.address, self.ip = None, None, None, None self.dirport, self.orport, self.legacy_dir_key = None, None, None self.key_certificate, self.contact, self.vote_digest = None, None, None
content = StringIO(raw_content) dir_source = _read_keyword_line("dir-source", content, validate) - self.nickname, self.identity, self.address, self.ip, self.dirport, self.orport = dir_source.split(" ") + self.nickname, self.fingerprint, self.address, self.ip, self.dirport, self.orport = dir_source.split(" ") self.dirport = int(self.dirport) self.orport = int(self.orport)
@@ -431,13 +432,13 @@ class DirectorySignature(stem.descriptor.Descriptor):
class RouterStatusEntry(stem.descriptor.Descriptor): """ - Router descriptor object. Parses and stores router information in a router - entry read from a v3 network status document. + Information about an individual router stored within a network status + document.
- :var NetworkStatusDocument document: ***** document this descriptor came from + :var NetworkStatusDocument document: ***** document that this descriptor came from
:var str nickname: ***** router's nickname - :var str identity: ***** router's identity + :var str fingerprint: ***** router's fingerprint :var str digest: ***** router's digest :var datetime publication: ***** router's publication :var str ip: ***** router's IP address @@ -478,7 +479,7 @@ class RouterStatusEntry(stem.descriptor.Descriptor): self.document = document
self.nickname = None - self.identity = None + self.fingerprint = None self.digest = None self.publication = None self.ip = None @@ -519,7 +520,7 @@ class RouterStatusEntry(stem.descriptor.Descriptor): if r: seen_keywords.add("r") values = r.split(" ") - self.nickname, self.identity, self.digest = values[0], values[1], values[2] + self.nickname, self.fingerprint, self.digest = values[0], _decode_fingerprint(values[1]), values[2] self.publication = _strptime(" ".join((values[3], values[4])), validate) self.ip, self.orport, self.dirport = values[5], int(values[6]), int(values[7]) if self.dirport == 0: self.dirport = None @@ -640,7 +641,7 @@ class RouterMicrodescriptor(RouterStatusEntry): :var MicrodescriptorConsensus document: ***** document this descriptor came from
:var str nickname: ***** router's nickname - :var str identity: ***** router's identity + :var str fingerprint: ***** router's fingerprint :var datetime publication: ***** router's publication :var str ip: ***** router's IP address :var int orport: ***** router's ORPort @@ -695,7 +696,7 @@ class RouterMicrodescriptor(RouterStatusEntry): if r: seen_keywords.add("r") values = r.split(" ") - self.nickname, self.identity = values[0], values[1] + self.nickname, self.fingerprint = values[0], _decode_fingerprint(values[1]) self.publication = _strptime(" ".join((values[2], values[3])), validate) self.ip, self.orport, self.dirport = values[4], int(values[5]), int(values[6]) if self.dirport == 0: self.dirport = None @@ -764,3 +765,39 @@ class RouterMicrodescriptor(RouterStatusEntry): """
return self.unrecognized_lines + +def _decode_fingerprint(identity): + """ + Decodes the 'identity' value found in consensuses into the more common hex + encoding of the relay's fingerprint. For example... + + :: + + >>> _decode_fingerprint('p1aag7VwarGxqctS7/fS0y5FU+s') + 'A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB' + + :param str identity: encoded fingerprint from the consensus + + :returns: str with the uppercase hex encoding of the relay's fingerprint + """ + + # trailing equal signs were stripped from the identity + missing_padding = 28 - len(identity) + identity += "=" * missing_padding + + fingerprint = "" + for char in base64.b64decode(identity): + # Individual characters are either standard ascii or hex encoded, and each + # represent two hex digits. For instnace... + # + # >>> ord('\n') + # 10 + # >>> hex(10) + # '0xa' + # >>> '0xa'[2:].zfill(2).upper() + # '0A' + + fingerprint += hex(ord(char))[2:].zfill(2).upper() + + return fingerprint + diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py index d16929d..621bb14 100644 --- a/test/integ/descriptor/networkstatus.py +++ b/test/integ/descriptor/networkstatus.py @@ -17,7 +17,7 @@ import test.integ.descriptor def _strptime(string): return datetime.datetime.strptime(string, "%Y-%m-%d %H:%M:%S")
-class TestNetworkStatusDocument(unittest.TestCase): +class TestNetworkStatus(unittest.TestCase): def test_cached_consensus(self): """ Parses the cached-consensus file in our data directory. @@ -59,7 +59,7 @@ class TestNetworkStatusDocument(unittest.TestCase):
router = next(descriptors) self.assertEquals("sumkledi", router.nickname) - self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router.identity) + self.assertEquals("0013D22389CD50D0B784A3E4061CB31E8CE8CEB5", router.fingerprint) self.assertEquals("8mCr8Sl7RF4ENU4jb0FZFA/3do8", router.digest) self.assertEquals(_strptime("2012-07-12 04:01:55"), router.publication) self.assertEquals("178.218.213.229", router.ip) @@ -104,7 +104,7 @@ class TestNetworkStatusDocument(unittest.TestCase): self.assertEquals(expected_params, desc.params)
self.assertEquals("sumkledi", router1.nickname) - self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router1.identity) + self.assertEquals("0013D22389CD50D0B784A3E4061CB31E8CE8CEB5", router1.fingerprint) self.assertEquals("8mCr8Sl7RF4ENU4jb0FZFA/3do8", router1.digest) self.assertEquals(_strptime("2012-07-12 04:01:55"), router1.publication) self.assertEquals("178.218.213.229", router1.ip) @@ -114,7 +114,7 @@ class TestNetworkStatusDocument(unittest.TestCase):
self.assertEquals(8, len(desc.directory_authorities)) self.assertEquals("tor26", desc.directory_authorities[0].nickname) - self.assertEquals("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4", desc.directory_authorities[0].identity) + self.assertEquals("14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4", desc.directory_authorities[0].fingerprint) self.assertEquals("86.59.21.38", desc.directory_authorities[0].address) self.assertEquals("86.59.21.38", desc.directory_authorities[0].ip) self.assertEquals(80, desc.directory_authorities[0].dirport) @@ -152,7 +152,7 @@ I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
router = next(descriptors) self.assertEquals("sumkledi", router.nickname) - self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router.identity) + self.assertEquals("0013D22389CD50D0B784A3E4061CB31E8CE8CEB5", router.fingerprint) self.assertEquals("B5n4BiALAF8B5AqafxohyYiuj7E", router.digest) self.assertEquals(_strptime("2012-07-11 04:22:53"), router.publication) self.assertEquals("178.218.213.229", router.ip) @@ -189,7 +189,7 @@ I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY= self.assertEquals(expected_params, desc.params)
self.assertEquals("sumkledi", router1.nickname) - self.assertEquals("ABPSI4nNUNC3hKPkBhyzHozozrU", router1.identity) + self.assertEquals("0013D22389CD50D0B784A3E4061CB31E8CE8CEB5", router1.fingerprint) self.assertEquals("B5n4BiALAF8B5AqafxohyYiuj7E", router1.digest) self.assertEquals(_strptime("2012-07-11 04:22:53"), router1.publication) self.assertEquals("178.218.213.229", router1.ip) @@ -198,7 +198,7 @@ I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
self.assertEquals(1, len(desc.directory_authorities)) self.assertEquals("turtles", desc.directory_authorities[0].nickname) - self.assertEquals("27B6B5996C426270A5C95488AA5BCEB6BCC86956", desc.directory_authorities[0].identity) + self.assertEquals("27B6B5996C426270A5C95488AA5BCEB6BCC86956", desc.directory_authorities[0].fingerprint) self.assertEquals("76.73.17.194", desc.directory_authorities[0].address) self.assertEquals("76.73.17.194", desc.directory_authorities[0].ip) self.assertEquals(9030, desc.directory_authorities[0].dirport) diff --git a/test/unit/descriptor/__init__.py b/test/unit/descriptor/__init__.py index 1837a07..4793560 100644 --- a/test/unit/descriptor/__init__.py +++ b/test/unit/descriptor/__init__.py @@ -2,5 +2,5 @@ Unit tests for stem.descriptor. """
-__all__ = ["export", "reader", "extrainfo_descriptor", "server_descriptor"] +__all__ = ["export", "reader", "extrainfo_descriptor", "server_descriptor", "networkstatus"]
diff --git a/test/unit/descriptor/networkstatus.py b/test/unit/descriptor/networkstatus.py new file mode 100644 index 0000000..ec69367 --- /dev/null +++ b/test/unit/descriptor/networkstatus.py @@ -0,0 +1,27 @@ +""" +Unit tests for stem.descriptor.networkstatus. +""" + +import unittest + +from stem.descriptor import networkstatus + +class TestNetworkStatus(unittest.TestCase): + def test_fingerprint_decoding(self): + """ + Tests for the _decode_fingerprint() helper. + """ + + # consensus identity field and fingerprint for caerSidi and Amunet1-5 + test_values = { + 'p1aag7VwarGxqctS7/fS0y5FU+s': 'A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB', + 'IbhGa8T+8tyy/MhxCk/qI+EI2LU': '21B8466BC4FEF2DCB2FCC8710A4FEA23E108D8B5', + '20wYcbFGwFfMktmuffYj6Z1RM9k': 'DB4C1871B146C057CC92D9AE7DF623E99D5133D9', + 'nTv9AG1cZeFW2hXiSIEAF6JLRJ4': '9D3BFD006D5C65E156DA15E248810017A24B449E', + '/UKsQiOSGPi/6es0/ha1prNTeDI': 'FD42AC42239218F8BFE9EB34FE16B5A6B3537832', + '/nHdqoKZ6bKZixxAPzYt9Qen+Is': 'FE71DDAA8299E9B2998B1C403F362DF507A7F88B', + } + + for arg, expected in test_values.items(): + self.assertEqual(expected, networkstatus._decode_fingerprint(arg)) +