[tor-commits] [stem/master] Replacing RouterStatusEntry's identity with fingerprint

atagar at torproject.org atagar at torproject.org
Sat Oct 13 18:35:45 UTC 2012


commit fd58bc5a565bca11d372188a4add167775f091c1
Author: Damian Johnson <atagar at 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))
+





More information about the tor-commits mailing list