[tor-commits] [stem/master] Adding extra-info digest() method

atagar at torproject.org atagar at torproject.org
Sun Jun 10 21:30:22 UTC 2012


commit 9654beb5acb7ad0e8e90bbdf7b9e186ccd946654
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Jun 10 14:24:33 2012 -0700

    Adding extra-info digest() method
    
    Adding digest() methods for extra-info descriptors. I'm also swapping the
    server descriptor counterparts to provide a hex encoded digest as per Karsten's
    suggestion.
---
 stem/descriptor/extrainfo_descriptor.py       |   53 ++++++++++++++++++++++++-
 stem/descriptor/server_descriptor.py          |   12 +----
 test/integ/descriptor/extrainfo_descriptor.py |    6 +-
 test/integ/descriptor/server_descriptor.py    |    2 +-
 4 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 257b747..04f061a 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -45,10 +45,12 @@ Extra-info descriptors are available from a few sources...
     |  |- RelayExtraInfoDescriptor - Extra-info descriptor for a relay.
     |  +- BridgeExtraInfoDescriptor - Extra-info descriptor for a bridge.
     |
+    |- digest - calculates the digest value for our content
     +- get_unrecognized_lines - lines with unrecognized content
 """
 
 import re
+import hashlib
 import datetime
 
 import stem.descriptor
@@ -673,6 +675,16 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
       else:
         self._unrecognized_lines.append(line)
   
+  def digest(self):
+    """
+    Provides the hex encoded sha1 of our content. This value is part of the
+    server descriptor entry for this relay.
+    
+    :returns: str with the digest value for this server descriptor
+    """
+    
+    raise NotImplementedError("Unsupported Operation: this should be implemented by the ExtraInfoDescriptor subclass")
+  
   def _required_fields(self):
     return REQUIRED_FIELDS
   
@@ -695,9 +707,19 @@ class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
   
   def __init__(self, raw_contents, validate = True):
     self.signature = None
+    self._digest = None
     
     ExtraInfoDescriptor.__init__(self, raw_contents, validate)
   
+  def digest(self):
+    if self._digest is None:
+      # our digest is calculated from everything except our signature
+      raw_content, ending = str(self), "\nrouter-signature\n"
+      raw_content = raw_content[:raw_content.find(ending) + len(ending)]
+      self._digest = hashlib.sha1(raw_content).hexdigest().upper()
+    
+    return self._digest
+  
   def _parse(self, entries, validate):
     entries = dict(entries) # shallow copy since we're destructive
     
@@ -722,12 +744,41 @@ class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
   Bridge extra-info descriptor (`specification <https://metrics.torproject.org/formats.html#bridgedesc>`_)
   """
   
+  def __init__(self, raw_contents, validate = True):
+    self._digest = None
+    
+    ExtraInfoDescriptor.__init__(self, raw_contents, validate)
+  
+  def digest(self):
+    return self._digest
+  
+  def _parse(self, entries, validate):
+    entries = dict(entries) # shallow copy since we're destructive
+    
+    # handles fields only in server descriptors
+    for keyword, values in entries.items():
+      value, _ = values[0]
+      line = "%s %s" % (keyword, value) # original line
+      
+      if keyword == "router-digest":
+        if validate and not stem.util.tor_tools.is_hex_digits(value, 40):
+          raise ValueError("Router digest line had an invalid sha1 digest: %s" % line)
+        
+        self._digest = value
+        del entries["router-digest"]
+    
+    ExtraInfoDescriptor._parse(self, entries, validate)
+  
   def _required_fields(self):
     excluded_fields = (
       "router-signature",
     )
     
-    return filter(lambda e: not e in excluded_fields, REQUIRED_FIELDS)
+    included_fields = (
+      "router-digest",
+    )
+    
+    return included_fields + filter(lambda e: not e in excluded_fields, REQUIRED_FIELDS)
   
   def _last_keyword(self):
     return None
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index af31f03..ec49a55 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -254,12 +254,8 @@ class ServerDescriptor(stem.descriptor.Descriptor):
   
   def digest(self):
     """
-    Provides the base64 encoded sha1 of our content. This value is part of the
-    server descriptor entry for this relay.
-    
-    Note that network status entries exclude the padding, so you'll need to add
-    a '=' to it so they'll match (`explanation
-    <https://en.wikipedia.org/wiki/Base64#Padding>`_).
+    Provides the hex encoded sha1 of our content. This value is part of the
+    network status entry for this relay.
     
     :returns: str with the digest value for this server descriptor
     """
@@ -613,9 +609,7 @@ class RelayDescriptor(ServerDescriptor):
       # our digest is calculated from everything except our signature
       raw_content, ending = str(self), "\nrouter-signature\n"
       raw_content = raw_content[:raw_content.find(ending) + len(ending)]
-      
-      digest_sha1 = hashlib.sha1(raw_content).digest()
-      self._digest = base64.b64encode(digest_sha1)
+      self._digest = hashlib.sha1(raw_content).hexdigest().upper()
     
     return self._digest
   
diff --git a/test/integ/descriptor/extrainfo_descriptor.py b/test/integ/descriptor/extrainfo_descriptor.py
index 68b857b..d7244a9 100644
--- a/test/integ/descriptor/extrainfo_descriptor.py
+++ b/test/integ/descriptor/extrainfo_descriptor.py
@@ -43,6 +43,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_write_history_end)
     self.assertEquals(900, desc.dir_write_history_interval)
     self.assertEquals(expected_signature, desc.signature)
+    self.assertEquals("00A57A9AAB5EA113898E2DD02A755E31AFC27227", desc.digest())
     self.assertEquals([], desc.get_unrecognized_lines())
     
     # The read-history, write-history, dirreq-read-history, and
@@ -109,9 +110,8 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     self.assertEquals(900, desc.dir_read_history_interval)
     self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_write_history_end)
     self.assertEquals(900, desc.dir_write_history_interval)
-    
-    # TODO: uncomment when we handle router-digest entries
-    #self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEquals("00A2AECCEAD3FEE033CFE29893387143146728EC", desc.digest())
+    self.assertEquals([], desc.get_unrecognized_lines())
     
     read_values_start = [337920, 437248, 3995648, 48726016]
     self.assertEquals(read_values_start, desc.read_history_values[:4])
diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py
index 180e82c..449fb2f 100644
--- a/test/integ/descriptor/server_descriptor.py
+++ b/test/integ/descriptor/server_descriptor.py
@@ -83,7 +83,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     self.assertEquals(expected_signing_key, desc.signing_key)
     self.assertEquals(expected_signature, desc.signature)
     self.assertEquals([], desc.get_unrecognized_lines())
-    self.assertEquals("LHsnvqsEtOJFnYnKbVzRzF+Vpok" + "=", desc.digest())
+    self.assertEquals("2C7B27BEAB04B4E2459D89CA6D5CD1CC5F95A689", desc.digest())
   
   def test_old_descriptor(self):
     """



More information about the tor-commits mailing list