commit 92f46907b3d84c53ab10c8f19851d65be86f7b9f Author: Ravi Chandra Padmala neenaoffline@gmail.com Date: Tue Aug 7 13:19:57 2012 +0530
stem.descriptor.networkstatus.KeyCertificate => stem.descriptor.KeyCertificate
From section 4.7 the dir-spec,
A concatenated set of all the current key certificates should be available at: http://<hostname>/tor/keys/all.z
The key certificate for this server (if it is an authority) should be available at: http://<hostname>/tor/keys/authority.z
...
we might want to parse these seperately at some point, so, moving it out of stem.descriptor.networkstatus --- stem/descriptor/__init__.py | 108 ++++++++++++++++++++++++++++++++++++++ stem/descriptor/networkstatus.py | 105 +------------------------------------ 2 files changed, 110 insertions(+), 103 deletions(-)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py index 5f9d144..f5da97a 100644 --- a/stem/descriptor/__init__.py +++ b/stem/descriptor/__init__.py @@ -24,6 +24,7 @@ __all__ = [
import os import re +import datetime
KEYWORD_CHAR = "a-zA-Z0-9-" WHITESPACE = " \t" @@ -283,6 +284,12 @@ def _get_descriptor_components(raw_contents, validate, extra_keywords):
return entries, first_keyword, last_keyword, extra_entries
+def _strptime(string, validate = True, optional = False): + try: + return datetime.datetime.strptime(string, "%Y-%m-%d %H:%M:%S") + except ValueError, exc: + if validate or not optional: raise exc + class DescriptorParser: """ Helper class to parse documents. @@ -423,3 +430,104 @@ class DescriptorParser: else: return []
+class KeyCertificate(Descriptor): + """ + Directory key certificate. + + :var str key_certificate_version: ***** version of the key certificate (Should be "3") + :var str ip: IP address on which the directory authority is listening + :var int port: port on which the directory authority is listening + :var str fingerprint: ***** hex encoded fingerprint of the authority's identity key + :var str identity_key: ***** long term authority identity key + :var datetime published: ***** time (in GMT) when this document & the key were last generated + :var str expires: ***** time (in GMT) after which this key becomes invalid + :var str signing_key: ***** directory server's public signing key + :var str crosscert: signature made using certificate's signing key + :var str certification: ***** signature of this key certificate signed with the identity key + + ***** attribute is either required when we're parsed with validation or has a default value, others are left as None if undefined + """ + + def __init__(self, raw_content, validate = True): + """ + Parse a key certificate entry and provide a KeyCertificate object. + + :param str raw_content: raw key certificate information + :param bool validate: True if the document is to be validated, False otherwise + + :raises: ValueError if the raw data is invalid + """ + + super(KeyCertificate, self).__init__(raw_content) + self.key_certificate_version, self.ip, self.port = None, None, None + self.fingerprint, self.identity_key, self.published = None, None, None + self.expires, self.signing_key, self.crosscert = None, None, None + self.certification = None + parser = DescriptorParser(raw_content, validate) + peek_check_kw = lambda keyword: keyword == parser.peek_keyword() + seen_keywords = set() + + self.key_certificate_version = parser.read_keyword_line("dir-key-certificate-version") + if validate and self.key_certificate_version != "3": raise ValueError("Unrecognized dir-key-certificate-version") + + def _read_keyword_line(keyword): + if validate and keyword in seen_keywords: + raise ValueError("Invalid key certificate: '%s' appears twice" % keyword) + seen_keywords.add(keyword) + return parser.read_keyword_line(keyword) + + while parser.line: + if peek_check_kw("dir-address"): + line = _read_keyword_line("dir-address") + try: + self.ip, self.port = line.rsplit(":", 1) + self.port = int(self.port) + except Exception: + if validate: raise ValueError("Invalid dir-address line: %s" % line) + + elif peek_check_kw("fingerprint"): + self.fingerprint = _read_keyword_line("fingerprint") + + elif peek_check_kw("dir-identity-key"): + _read_keyword_line("dir-identity-key") + self.identity_key = parser.read_block("RSA PUBLIC KEY") + + elif peek_check_kw("dir-key-published"): + self.published = _strptime(_read_keyword_line("dir-key-published")) + + elif peek_check_kw("dir-key-expires"): + self.expires = _strptime(_read_keyword_line("dir-key-expires")) + + elif peek_check_kw("dir-signing-key"): + _read_keyword_line("dir-signing-key") + self.signing_key = parser.read_block("RSA PUBLIC KEY") + + elif peek_check_kw("dir-key-crosscert"): + _read_keyword_line("dir-key-crosscert") + self.crosscert = parser.read_block("ID SIGNATURE") + + elif peek_check_kw("dir-key-certification"): + _read_keyword_line("dir-key-certification") + self.certification = parser.read_block("SIGNATURE") + break + + elif validate: + raise ValueError("Key certificate contains unrecognized lines: %s" % parser.line) + + else: + # ignore unrecognized lines if we aren't validating + self._unrecognized_lines.append(parser.read_line()) + + self._unrecognized_lines = parser.remaining() + if self._unrecognized_lines and validate: + raise ValueError("Unrecognized trailing data in key certificate") + + def get_unrecognized_lines(self): + """ + Returns any unrecognized lines. + + :returns: a list of unrecognized lines + """ + + return self._unrecognized_lines + diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 876acd9..988c71c 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -249,7 +249,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor): :var int orport: current orport :var str contact: directory authority's contact information :var str legacy_dir_key: fingerprint of and obsolete identity key - :var :class:`stem.descriptor.networkstatus_descriptor.KeyCertificate` key_certificate: directory authority's current key certificate + :var :class:`stem.descriptor.KeyCertificate` key_certificate: directory authority's current key certificate :var str vote_digest: digest of the authority that contributed to the consensus """
@@ -278,114 +278,13 @@ class DirectoryAuthority(stem.descriptor.Descriptor): self.contact = parser.read_keyword_line("contact") if vote: self.legacy_dir_key = parser.read_keyword_line("legacy-dir-key", True) - self.key_certificate = KeyCertificate("\n".join(parser.remaining()), validate) + self.key_certificate = stem.descriptor.KeyCertificate("\n".join(parser.remaining()), validate) else: self.vote_digest = parser.read_keyword_line("vote-digest", True) rmng = parser.remaining() if rmng and validate: raise ValueError("Unrecognized trailing data in directory authority information")
-class KeyCertificate(stem.descriptor.Descriptor): - """ - Directory key certificate. - - :var str key_certificate_version: ***** version of the key certificate (Should be "3") - :var str ip: IP address on which the directory authority is listening - :var int port: port on which the directory authority is listening - :var str fingerprint: ***** hex encoded fingerprint of the authority's identity key - :var str identity_key: ***** long term authority identity key - :var datetime published: ***** time (in GMT) when this document & the key were last generated - :var str expires: ***** time (in GMT) after which this key becomes invalid - :var str signing_key: ***** directory server's public signing key - :var str crosscert: signature made using certificate's signing key - :var str certification: ***** signature of this key certificate signed with the identity key - - ***** attribute is either required when we're parsed with validation or has a default value, others are left as None if undefined - """ - - def __init__(self, raw_content, validate = True): - """ - Parse a key certificate entry and provide a KeyCertificate object. - - :param str raw_content: raw key certificate information - :param bool validate: True if the document is to be validated, False otherwise - - :raises: ValueError if the raw data is invalid - """ - - super(KeyCertificate, self).__init__(raw_content) - self.key_certificate_version, self.ip, self.port = None, None, None - self.fingerprint, self.identity_key, self.published = None, None, None - self.expires, self.signing_key, self.crosscert = None, None, None - self.certification = None - parser = stem.descriptor.DescriptorParser(raw_content, validate) - peek_check_kw = lambda keyword: keyword == parser.peek_keyword() - seen_keywords = set() - - self.key_certificate_version = parser.read_keyword_line("dir-key-certificate-version") - if validate and self.key_certificate_version != "3": raise ValueError("Unrecognized dir-key-certificate-version") - - def _read_keyword_line(keyword): - if validate and keyword in seen_keywords: - raise ValueError("Invalid key certificate: '%s' appears twice" % keyword) - seen_keywords.add(keyword) - return parser.read_keyword_line(keyword) - - while parser.line: - if peek_check_kw("dir-address"): - line = _read_keyword_line("dir-address") - try: - self.ip, self.port = line.rsplit(":", 1) - self.port = int(self.port) - except Exception: - if validate: raise ValueError("Invalid dir-address line: %s" % line) - - elif peek_check_kw("fingerprint"): - self.fingerprint = _read_keyword_line("fingerprint") - - elif peek_check_kw("dir-identity-key"): - _read_keyword_line("dir-identity-key") - self.identity_key = parser.read_block("RSA PUBLIC KEY") - - elif peek_check_kw("dir-key-published"): - self.published = _strptime(_read_keyword_line("dir-key-published")) - - elif peek_check_kw("dir-key-expires"): - self.expires = _strptime(_read_keyword_line("dir-key-expires")) - - elif peek_check_kw("dir-signing-key"): - _read_keyword_line("dir-signing-key") - self.signing_key = parser.read_block("RSA PUBLIC KEY") - - elif peek_check_kw("dir-key-crosscert"): - _read_keyword_line("dir-key-crosscert") - self.crosscert = parser.read_block("ID SIGNATURE") - - elif peek_check_kw("dir-key-certification"): - _read_keyword_line("dir-key-certification") - self.certification = parser.read_block("SIGNATURE") - break - - elif validate: - raise ValueError("Key certificate contains unrecognized lines: %s" % parser.line) - - else: - # ignore unrecognized lines if we aren't validating - self._unrecognized_lines.append(parser.read_line()) - - self._unrecognized_lines = parser.remaining() - if self._unrecognized_lines and validate: - raise ValueError("Unrecognized trailing data in key certificate") - - def get_unrecognized_lines(self): - """ - Returns any unrecognized lines. - - :returns: a list of unrecognized lines - """ - - return self._unrecognized_lines - class DirectorySignature(stem.descriptor.Descriptor): """ Contains directory signatures in a v3 network status document.
tor-commits@lists.torproject.org