commit 9654beb5acb7ad0e8e90bbdf7b9e186ccd946654 Author: Damian Johnson atagar@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): """
tor-commits@lists.torproject.org