commit 47dc11f4f3a4859a95d6e182d254d074fa6044b3 Author: Damian Johnson <atagar@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