[tor-commits] [stem/master] Stub descriptor signing

atagar at torproject.org atagar at torproject.org
Tue Jun 20 16:17:12 UTC 2017


commit 47dc11f4f3a4859a95d6e182d254d074fa6044b3
Author: Damian Johnson <atagar at torproject.org>
Date:   Wed May 3 12:05:24 2017 -0700

    Stub descriptor signing
    
    Nothing's implemented yet. Just wiring in the sign argument in all the
    descriptor creation functions.
---
 stem/descriptor/__init__.py                  | 21 ++++++++++++++++-----
 stem/descriptor/extrainfo_descriptor.py      |  8 ++++----
 stem/descriptor/hidden_service_descriptor.py |  8 ++++----
 stem/descriptor/microdescriptor.py           |  4 ++--
 stem/descriptor/networkstatus.py             | 24 ++++++++++++------------
 stem/descriptor/router_status_entry.py       | 12 ++++++------
 stem/descriptor/server_descriptor.py         | 12 ++++++------
 7 files changed, 50 insertions(+), 39 deletions(-)

diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index a73093c..13dc84c 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -335,7 +335,7 @@ def _parse_metrics_file(descriptor_type, major_version, minor_version, descripto
     raise TypeError("Unrecognized metrics descriptor format. type: '%s', version: '%i.%i'" % (descriptor_type, major_version, minor_version))
 
 
-def _descriptor_content(attr = None, exclude = (), header_template = (), footer_template = ()):
+def _descriptor_content(attr = None, exclude = (), sign = False, header_template = (), footer_template = ()):
   """
   Constructs a minimal descriptor with the given attributes. The content we
   provide back is of the form...
@@ -366,12 +366,18 @@ def _descriptor_content(attr = None, exclude = (), header_template = (), footer_
 
   :param dict attr: keyword/value mappings to be included in the descriptor
   :param list exclude: mandatory keywords to exclude from the descriptor
+  :param bool sign: includes cryptographic signatures and digests if True
   :param tuple header_template: key/value pairs for mandatory fields before unrecognized content
   :param tuple footer_template: key/value pairs for mandatory fields after unrecognized content
 
   :returns: str with the requested descriptor content
+
+  :raises: **ImportError** if cryptography is unavailable and sign is True
   """
 
+  if sign and not stem.prereq.is_crypto_available():
+    raise ImportError('Signing descriptors requries the cryptography module')
+
   header_content, footer_content = [], []
   attr = {} if attr is None else dict(attr)  # shallow copy since we're destructive
 
@@ -532,7 +538,7 @@ class Descriptor(object):
     self._unrecognized_lines = []
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
+  def content(cls, attr = None, exclude = (), sign = False):
     """
     Creates descriptor content with the given attributes. Mandatory fields are
     filled with dummy information unless data is supplied. This doesn't yet
@@ -543,16 +549,19 @@ class Descriptor(object):
     :param dict attr: keyword/value mappings to be included in the descriptor
     :param list exclude: mandatory keywords to exclude from the descriptor, this
       results in an invalid descriptor
+    :param bool sign: includes cryptographic signatures and digests if True
 
     :returns: **str** with the content of a descriptor
 
-    :raises: **NotImplementedError** if not implemented for this descriptor type
+    :raises:
+      * **ImportError** if cryptography is unavailable and sign is True
+      * **NotImplementedError** if not implemented for this descriptor type
     """
 
     raise NotImplementedError("The create and content methods haven't been implemented for %s" % cls.__name__)
 
   @classmethod
-  def create(cls, attr = None, exclude = (), validate = True):
+  def create(cls, attr = None, exclude = (), validate = True, sign = False):
     """
     Creates a descriptor with the given attributes. Mandatory fields are filled
     with dummy information unless data is supplied. This doesn't yet create a
@@ -565,15 +574,17 @@ class Descriptor(object):
       results in an invalid descriptor
     :param bool validate: checks the validity of the descriptor's content if
       **True**, skips these checks otherwise
+    :param bool sign: includes cryptographic signatures and digests if True
 
     :returns: :class:`~stem.descriptor.Descriptor` subclass
 
     :raises:
       * **ValueError** if the contents is malformed and validate is True
+      * **ImportError** if cryptography is unavailable and sign is True
       * **NotImplementedError** if not implemented for this descriptor type
     """
 
-    return cls(cls.content(attr, exclude), validate = validate)
+    return cls(cls.content(attr, exclude, sign), validate = validate)
 
   def get_path(self):
     """
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index fc4a124..847a2c7 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -973,8 +973,8 @@ class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
   })
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, RELAY_EXTRAINFO_HEADER, RELAY_EXTRAINFO_FOOTER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, RELAY_EXTRAINFO_HEADER, RELAY_EXTRAINFO_FOOTER)
 
   @lru_cache()
   def digest(self):
@@ -1009,8 +1009,8 @@ class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
   })
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, BRIDGE_EXTRAINFO_HEADER, BRIDGE_EXTRAINFO_FOOTER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, BRIDGE_EXTRAINFO_HEADER, BRIDGE_EXTRAINFO_FOOTER)
 
   def digest(self):
     return self._digest
diff --git a/stem/descriptor/hidden_service_descriptor.py b/stem/descriptor/hidden_service_descriptor.py
index a3b12b5..91d49b5 100644
--- a/stem/descriptor/hidden_service_descriptor.py
+++ b/stem/descriptor/hidden_service_descriptor.py
@@ -250,12 +250,12 @@ class HiddenServiceDescriptor(Descriptor):
   }
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, HIDDEN_SERVICE_HEADER, HIDDEN_SERVICE_FOOTER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, HIDDEN_SERVICE_HEADER, HIDDEN_SERVICE_FOOTER)
 
   @classmethod
-  def create(cls, attr = None, exclude = (), validate = True):
-    return cls(cls.content(attr, exclude), validate = validate, skip_crypto_validation = True)
+  def create(cls, attr = None, exclude = (), validate = True, sign = False):
+    return cls(cls.content(attr, exclude, sign), validate = validate, skip_crypto_validation = True)
 
   def __init__(self, raw_contents, validate = False, skip_crypto_validation = False):
     super(HiddenServiceDescriptor, self).__init__(raw_contents, lazy_load = not validate)
diff --git a/stem/descriptor/microdescriptor.py b/stem/descriptor/microdescriptor.py
index fb3c1e6..257067e 100644
--- a/stem/descriptor/microdescriptor.py
+++ b/stem/descriptor/microdescriptor.py
@@ -263,8 +263,8 @@ class Microdescriptor(Descriptor):
   }
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, MICRODESCRIPTOR)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, MICRODESCRIPTOR)
 
   def __init__(self, raw_contents, validate = False, annotations = None):
     super(Microdescriptor, self).__init__(raw_contents, lazy_load = not validate)
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py
index 942c524..4cea4eb 100644
--- a/stem/descriptor/networkstatus.py
+++ b/stem/descriptor/networkstatus.py
@@ -511,8 +511,8 @@ class NetworkStatusDocumentV2(NetworkStatusDocument):
   }
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, NETWORK_STATUS_DOCUMENT_HEADER_V2, NETWORK_STATUS_DOCUMENT_FOOTER_V2)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, NETWORK_STATUS_DOCUMENT_HEADER_V2, NETWORK_STATUS_DOCUMENT_FOOTER_V2)
 
   def __init__(self, raw_content, validate = False):
     super(NetworkStatusDocumentV2, self).__init__(raw_content, lazy_load = not validate)
@@ -959,7 +959,7 @@ class NetworkStatusDocumentV3(NetworkStatusDocument):
   }
 
   @classmethod
-  def content(cls, attr = None, exclude = (), authorities = None, routers = None):
+  def content(cls, attr = None, exclude = (), sign = False, authorities = None, routers = None):
     attr = {} if attr is None else dict(attr)
 
     is_vote = attr.get('vote-status') == 'vote'
@@ -974,7 +974,7 @@ class NetworkStatusDocumentV3(NetworkStatusDocument):
       elif k not in attr:
         attr[k] = v
 
-    desc_content = _descriptor_content(attr, exclude, NETWORK_STATUS_DOCUMENT_HEADER, NETWORK_STATUS_DOCUMENT_FOOTER)
+    desc_content = _descriptor_content(attr, exclude, sign, NETWORK_STATUS_DOCUMENT_HEADER, NETWORK_STATUS_DOCUMENT_FOOTER)
 
     # inject the authorities and/or routers between the header and footer
 
@@ -1009,8 +1009,8 @@ class NetworkStatusDocumentV3(NetworkStatusDocument):
     return desc_content
 
   @classmethod
-  def create(cls, attr = None, exclude = (), validate = True, authorities = None, routers = None):
-    return cls(cls.content(attr, exclude, authorities, routers), validate = validate)
+  def create(cls, attr = None, exclude = (), validate = True, sign = False, authorities = None, routers = None):
+    return cls(cls.content(attr, exclude, sign, authorities, routers), validate = validate)
 
   def __init__(self, raw_content, validate = False, default_params = True):
     """
@@ -1423,7 +1423,7 @@ class DirectoryAuthority(Descriptor):
   }
 
   @classmethod
-  def content(cls, attr = None, exclude = (), is_vote = False):
+  def content(cls, attr = None, exclude = (), sign = False, is_vote = False):
     attr = {} if attr is None else dict(attr)
 
     # include mandatory 'vote-digest' if a consensus
@@ -1431,7 +1431,7 @@ class DirectoryAuthority(Descriptor):
     if not is_vote and not ('vote-digest' in attr or (exclude and 'vote-digest' in exclude)):
       attr['vote-digest'] = '0B6D1E9A300B895AA2D0B427F92917B6995C3C1C'
 
-    content = _descriptor_content(attr, exclude, AUTHORITY_HEADER)
+    content = _descriptor_content(attr, exclude, sign, AUTHORITY_HEADER)
 
     if is_vote:
       content += b'\n' + KeyCertificate.content()
@@ -1439,8 +1439,8 @@ class DirectoryAuthority(Descriptor):
     return content
 
   @classmethod
-  def create(cls, attr = None, exclude = (), validate = True, is_vote = False):
-    return cls(cls.content(attr, exclude, is_vote), validate = validate, is_vote = is_vote)
+  def create(cls, attr = None, exclude = (), validate = True, sign = False, is_vote = False):
+    return cls(cls.content(attr, exclude, sign, is_vote), validate = validate, is_vote = is_vote)
 
   def __init__(self, raw_content, validate = False, is_vote = False):
     """
@@ -1617,8 +1617,8 @@ class KeyCertificate(Descriptor):
   }
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, KEY_CERTIFICATE_HEADER, KEY_CERTIFICATE_FOOTER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, KEY_CERTIFICATE_HEADER, KEY_CERTIFICATE_FOOTER)
 
   def __init__(self, raw_content, validate = False):
     super(KeyCertificate, self).__init__(raw_content, lazy_load = not validate)
diff --git a/stem/descriptor/router_status_entry.py b/stem/descriptor/router_status_entry.py
index 973f833..19d4d3c 100644
--- a/stem/descriptor/router_status_entry.py
+++ b/stem/descriptor/router_status_entry.py
@@ -528,8 +528,8 @@ class RouterStatusEntryV2(RouterStatusEntry):
   })
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_V2_HEADER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, ROUTER_STATUS_ENTRY_V2_HEADER)
 
   def _name(self, is_plural = False):
     return 'Router status entries (v2)' if is_plural else 'Router status entry (v2)'
@@ -625,8 +625,8 @@ class RouterStatusEntryV3(RouterStatusEntry):
   })
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_V3_HEADER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, ROUTER_STATUS_ENTRY_V3_HEADER)
 
   def _name(self, is_plural = False):
     return 'Router status entries (v3)' if is_plural else 'Router status entry (v3)'
@@ -698,8 +698,8 @@ class RouterStatusEntryMicroV3(RouterStatusEntry):
   })
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, ROUTER_STATUS_ENTRY_MICRO_V3_HEADER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, ROUTER_STATUS_ENTRY_MICRO_V3_HEADER)
 
   def _name(self, is_plural = False):
     return 'Router status entries (micro v3)' if is_plural else 'Router status entry (micro v3)'
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 9367d1c..ce3300b 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -817,12 +817,12 @@ class RelayDescriptor(ServerDescriptor):
         self.certificate.validate(self)
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, RELAY_SERVER_HEADER, RELAY_SERVER_FOOTER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, RELAY_SERVER_HEADER, RELAY_SERVER_FOOTER)
 
   @classmethod
-  def create(cls, attr = None, exclude = (), validate = True):
-    return cls(cls.content(attr, exclude), validate = validate, skip_crypto_validation = True)
+  def create(cls, attr = None, exclude = (), validate = True, sign = False):
+    return cls(cls.content(attr, exclude, sign), validate = validate, skip_crypto_validation = True)
 
   @lru_cache()
   def digest(self):
@@ -909,8 +909,8 @@ class BridgeDescriptor(ServerDescriptor):
   })
 
   @classmethod
-  def content(cls, attr = None, exclude = ()):
-    return _descriptor_content(attr, exclude, BRIDGE_SERVER_HEADER)
+  def content(cls, attr = None, exclude = (), sign = False):
+    return _descriptor_content(attr, exclude, sign, BRIDGE_SERVER_HEADER)
 
   def digest(self):
     return self._digest





More information about the tor-commits mailing list