[tor-commits] [stem/master] Digest method for microdescriptors

atagar at torproject.org atagar at torproject.org
Wed Nov 21 20:29:36 UTC 2018


commit 5495c5725ed492055fe888f6269ab062baa52e38
Author: Damian Johnson <atagar at torproject.org>
Date:   Wed Nov 21 12:21:32 2018 -0800

    Digest method for microdescriptors
    
    Revamp digesting of Microdescriptors in the same way as server and extrainfo
    descriptors...
    
      https://trac.torproject.org/projects/tor/ticket/28398
    
    This is definitely better *but* is backward incompatible with the class' old
    'digest' attribute. Unfortunately I can't avoid this. The method needs to be
    called digest() for consistency, and python cannot have name conflicts between
    methods and attributes.
    
    The old digest value was hex rather than base64 encoded which made it
    relatively useless (it couldn't be used to fetch or validate microdescriptors,
    the sole point of the damn thing) so fingers crossed that no one was using it.
    
    I try very hard to provide backward compatibility in minor version bumps of
    Stem but in this case I don't think we should be a slave to that here.
---
 docs/change_log.rst                     |  1 +
 stem/descriptor/microdescriptor.py      | 42 ++++++++++++++++++++++++++-------
 test/unit/descriptor/microdescriptor.py |  1 +
 3 files changed, 35 insertions(+), 9 deletions(-)

diff --git a/docs/change_log.rst b/docs/change_log.rst
index 396ec105..48311df4 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -58,6 +58,7 @@ The following are only available within Stem's `git repository
   * Added the network status vote's new bandwidth_file_digest attribute (:spec:`1b686ef`)
   * Added :func:`~stem.descriptor.networkstatus.NetworkStatusDocumentV3.is_valid` and :func:`~stem.descriptor.networkstatus.NetworkStatusDocumentV3.is_fresh` methods (:trac:`28448`)
   * Replaced :func:`~stem.descriptor.router_status_entry.RouterStatusEntryMicroV3` hex encoded **digest** attribute with a base64 encoded **microdescriptor_digest**
+  * Replaced the **digest** attribute of :class:`~stem.descriptor.microdescriptor.Microdescriptor` with a method by the same name (:trac:`28398`)
   * DescriptorDownloader crashed if **use_mirrors** is set (:trac:`28393`)
   * Don't download from Serge, a bridge authority that frequently timeout
 
diff --git a/stem/descriptor/microdescriptor.py b/stem/descriptor/microdescriptor.py
index 74a01071..8672f039 100644
--- a/stem/descriptor/microdescriptor.py
+++ b/stem/descriptor/microdescriptor.py
@@ -71,6 +71,8 @@ import stem.prereq
 
 from stem.descriptor import (
   Descriptor,
+  DigestHash,
+  DigestEncoding,
   _descriptor_content,
   _descriptor_components,
   _read_until_keywords,
@@ -183,10 +185,6 @@ def _parse_id_line(descriptor, entries):
   descriptor.identifiers = identities
 
 
-def _parse_digest(descriptor, entries):
-  setattr(descriptor, 'digest', hashlib.sha256(descriptor.get_bytes()).hexdigest().upper())
-
-
 _parse_onion_key_line = _parse_key_block('onion-key', 'onion_key', 'RSA PUBLIC KEY')
 _parse_ntor_onion_key_line = _parse_simple_line('ntor-onion-key', 'ntor_onion_key')
 _parse_family_line = _parse_simple_line('family', 'family', func = lambda v: v.split(' '))
@@ -199,9 +197,6 @@ class Microdescriptor(Descriptor):
   Microdescriptor (`descriptor specification
   <https://gitweb.torproject.org/torspec.git/tree/dir-spec.txt>`_)
 
-  :var str digest: **\*** hex digest for this microdescriptor, this can be used
-    to match against the corresponding digest attribute of a
-    :class:`~stem.descriptor.router_status_entry.RouterStatusEntryMicroV3`
   :var str onion_key: **\*** key used to encrypt EXTEND cells
   :var str ntor_onion_key: base64 key used to encrypt EXTEND in the ntor protocol
   :var list or_addresses: **\*** alternative for our address/or_port attributes, each
@@ -231,6 +226,13 @@ class Microdescriptor(Descriptor):
 
   .. versionchanged:: 1.6.0
      Added the protocols attribute.
+
+  .. versionchanged:: 1.8.0
+     Replaced our **digest** attribute with a much more flexible **digest()**
+     method. Unfortunately I cannot do this in a backward compatible way
+     because of the name conflict. The old digest had multiple problems (for
+     instance, being hex rather than base64 encoded), so hopefully no one was
+     using it. Very sorry if this causes trouble for anyone.
   """
 
   TYPE_ANNOTATION_NAME = 'microdescriptor'
@@ -246,7 +248,6 @@ class Microdescriptor(Descriptor):
     'identifier': (None, _parse_id_line),  # deprecated in favor of identifiers
     'identifiers': ({}, _parse_id_line),
     'protocols': ({}, _parse_pr_line),
-    'digest': (None, _parse_digest),
   }
 
   PARSER_FOR_LINE = {
@@ -275,12 +276,35 @@ class Microdescriptor(Descriptor):
     entries = _descriptor_components(raw_contents, validate)
 
     if validate:
-      self.digest = hashlib.sha256(self.get_bytes()).hexdigest().upper()
       self._parse(entries, validate)
       self._check_constraints(entries)
     else:
       self._entries = entries
 
+  def digest(self, hash_type = DigestHash.SHA256, encoding = DigestEncoding.BASE64):
+    """
+    Digest of this microdescriptor. These are referenced by...
+
+      * **Microdescriptor Consensus**
+
+        * Referer: :class:`~stem.descriptor.router_status_entry.RouterStatusEntryMicroV3` **digest** attribute
+        * Format: **SHA256/BASE64**
+
+    .. versionadded:: 1.8.0
+
+    :param stem.descriptor.DigestHash hash_type: digest hashing algorithm
+    :param stem.descriptor.DigestEncoding encoding: digest encoding
+
+    :returns: **hashlib.HASH** or **str** based on our encoding argument
+    """
+
+    if hash_type == DigestHash.SHA1:
+      return stem.descriptor._encode_digest(hashlib.sha1(self.get_bytes()), encoding)
+    elif hash_type == DigestHash.SHA256:
+      return stem.descriptor._encode_digest(hashlib.sha256(self.get_bytes()), encoding)
+    else:
+      raise NotImplementedError('Microdescriptor digests are only available in sha1 and sha256, not %s' % hash_type)
+
   @lru_cache()
   def get_annotations(self):
     """
diff --git a/test/unit/descriptor/microdescriptor.py b/test/unit/descriptor/microdescriptor.py
index bb4b91a2..2afccc48 100644
--- a/test/unit/descriptor/microdescriptor.py
+++ b/test/unit/descriptor/microdescriptor.py
@@ -74,6 +74,7 @@ class TestMicrodescriptor(unittest.TestCase):
       self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
       self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
 
+      self.assertEqual('uhCGfIM6RbeD1Z/C6e9ct41+NIl9EbpgP8wG7uZT2Rw', router.digest())
       self.assertEqual('@type microdescriptor 1.0', str(router.type_annotation()))
 
   def test_minimal_microdescriptor(self):



More information about the tor-commits mailing list