[tor-commits] [stem/master] Support multiple 'id' lines in microdescriptors

atagar at torproject.org atagar at torproject.org
Wed Aug 26 16:37:01 UTC 2015


commit 373d56f8fae1521a05f4983060ef50dd43417bd5
Author: Damian Johnson <atagar at torproject.org>
Date:   Wed Aug 26 09:38:10 2015 -0700

    Support multiple 'id' lines in microdescriptors
    
    With the ed25519 additions the 'id' lines of microdescriptors can now appear
    multiple times...
    
      https://gitweb.torproject.org/torspec.git/commit/?id=09ff9e2
---
 docs/change_log.rst                     |    1 +
 stem/descriptor/microdescriptor.py      |   44 +++++++++++++++++++++++--------
 stem/descriptor/server_descriptor.py    |    6 ++---
 test/unit/descriptor/microdescriptor.py |   28 ++++++++++++++++++++
 4 files changed, 65 insertions(+), 14 deletions(-)

diff --git a/docs/change_log.rst b/docs/change_log.rst
index 1f542ad..6de73f4 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -56,6 +56,7 @@ The following are only available within Stem's `git repository
   * Support for ed25519 descriptor fields (:spec:`5a79d67`)
   * Server descriptor validation fails with 'extra-info-digest line had an invalid value' from additions in proposal 228 (:trac:`16227`)
   * :class:`~stem.descriptor.server_descriptor.BridgeDescriptor` now has 'ntor_onion_key' like its unsanitized counterparts
+  * Replaced the :class:`~stem.descriptor.microdescriptor.Microdescriptor` identifier and identifier_type attributes with an identifiers hash since it can now appear multiple times (:spec:`09ff9e2`)
 
  * **Website**
 
diff --git a/stem/descriptor/microdescriptor.py b/stem/descriptor/microdescriptor.py
index 1362147..d626101 100644
--- a/stem/descriptor/microdescriptor.py
+++ b/stem/descriptor/microdescriptor.py
@@ -73,6 +73,7 @@ from stem.descriptor import (
   _get_descriptor_components,
   _read_until_keywords,
   _value,
+  _values,
   _parse_simple_line,
   _parse_key_block,
 )
@@ -159,14 +160,24 @@ def _parse_file(descriptor_file, validate = False, **kwargs):
 
 
 def _parse_id_line(descriptor, entries):
-  value = _value('id', entries)
-  value_comp = value.split()
+  identities = {}
 
-  if len(value_comp) >= 2:
-    descriptor.identifier_type = value_comp[0]
-    descriptor.identifier = value_comp[1]
-  else:
-    raise ValueError("'id' lines should contain both the key type and digest: id %s" % value)
+  for entry in _values('id', entries):
+    entry_comp = entry.split()
+
+    if len(entry_comp) >= 2:
+      key_type, key_value = entry_comp[0], entry_comp[1]
+
+      if key_type in identities:
+        raise ValueError("There can only be one 'id' line per a key type, but '%s' appeared multiple times" % key_type)
+
+      descriptor.identifier_type = key_type
+      descriptor.identifier = key_value
+      identities[key_type] = key_value
+    else:
+      raise ValueError("'id' lines should contain both the key type and digest: id %s" % entry)
+
+  descriptor.identifiers = identities
 
 
 _parse_digest = lambda descriptor, entries: setattr(descriptor, 'digest', hashlib.sha256(descriptor.get_bytes()).hexdigest().upper())
@@ -192,13 +203,23 @@ class Microdescriptor(Descriptor):
   :var list family: **\*** nicknames or fingerprints of declared family
   :var stem.exit_policy.MicroExitPolicy exit_policy: **\*** relay's exit policy
   :var stem.exit_policy.MicroExitPolicy exit_policy_v6: **\*** exit policy for IPv6
-  :var str identifier_type: identity digest key type
-  :var str identifier: base64 encoded identity digest, this is only used for collision prevention (:trac:`11743`)
+  :var hash identifiers: mapping of key types (like rsa1024 or ed25519) to
+    their base64 encoded identity, this is only used for collision prevention
+    (:trac:`11743`)
+
+  :var str identifier: base64 encoded identity digest (**deprecated**, use
+    identifiers instead)
+  :var str identifier_type: identity digest key type (**deprecated**, use
+    identifiers instead)
 
   **\*** attribute is required when we're parsed with validation
 
   .. versionchanged:: 1.1.0
      Added the identifier and identifier_type attributes.
+
+  .. versionchanged:: 1.5.0
+     Added the identifiers attribute, and deprecated identifier and
+     identifier_type since the field can now appear multiple times.
   """
 
   ATTRIBUTES = {
@@ -208,8 +229,9 @@ class Microdescriptor(Descriptor):
     'family': ([], _parse_family_line),
     'exit_policy': (stem.exit_policy.MicroExitPolicy('reject 1-65535'), _parse_p_line),
     'exit_policy_v6': (None, _parse_p6_line),
-    'identifier_type': (None, _parse_id_line),
-    'identifier': (None, _parse_id_line),
+    'identifier_type': (None, _parse_id_line),  # deprecated in favor of identifiers
+    'identifier': (None, _parse_id_line),  # deprecated in favor of identifiers
+    'identifiers': ({}, _parse_id_line),
     'digest': (None, _parse_digest),
   }
 
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index be40f62..07b224a 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -420,7 +420,7 @@ class ServerDescriptor(Descriptor):
 
   :var str address: **\*** IPv4 address of the relay
   :var int or_port: **\*** port used for relaying
-  :var int socks_port: **\*** port used as client (deprecated, always **None**)
+  :var int socks_port: **\*** port used as client (**deprecated**, always **None**)
   :var int dir_port: **\*** port used for descriptor mirroring
 
   :var bytes platform: line with operating system and tor version
@@ -442,13 +442,13 @@ class ServerDescriptor(Descriptor):
   :var bool allow_single_hop_exits: **\*** flag if single hop exiting is allowed
   :var bool extra_info_cache: **\*** flag if a mirror for extra-info documents
   :var str extra_info_digest: upper-case hex encoded digest of our extra-info document
-  :var bool eventdns: flag for evdns backend (deprecated, always unset)
+  :var bool eventdns: flag for evdns backend (**deprecated**, always unset)
   :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 entry is a tuple of the form (address (**str**), port
     (**int**), is_ipv6 (**bool**))
 
-  Deprecated, moved to extra-info descriptor...
+  **Deprecated**, moved to extra-info descriptor...
 
   :var datetime read_history_end: end of the sampling interval
   :var int read_history_interval: seconds per interval
diff --git a/test/unit/descriptor/microdescriptor.py b/test/unit/descriptor/microdescriptor.py
index 598f58e..f21e518 100644
--- a/test/unit/descriptor/microdescriptor.py
+++ b/test/unit/descriptor/microdescriptor.py
@@ -95,6 +95,7 @@ class TestMicrodescriptor(unittest.TestCase):
     self.assertEqual([], desc.family)
     self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), desc.exit_policy)
     self.assertEqual(None, desc.exit_policy_v6)
+    self.assertEqual({}, desc.identifiers)
     self.assertEqual(None, desc.identifier_type)
     self.assertEqual(None, desc.identifier)
     self.assertEqual([], desc.get_unrecognized_lines())
@@ -170,5 +171,32 @@ class TestMicrodescriptor(unittest.TestCase):
     """
 
     desc = get_microdescriptor({'id': 'rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4'})
+    self.assertEqual({'rsa1024': 'Cd47okjCHD83YGzThGBDptXs9Z4'}, desc.identifiers)
     self.assertEqual('rsa1024', desc.identifier_type)
     self.assertEqual('Cd47okjCHD83YGzThGBDptXs9Z4', desc.identifier)
+
+    # check when there's multiple key types
+
+    desc_text = b'\n'.join((get_microdescriptor(content = True),
+                            b'id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4',
+                            b'id ed25519 50f6ddbecdc848dcc6b818b14d1'))
+
+    desc = Microdescriptor(desc_text, validate = True)
+    self.assertEqual({'rsa1024': 'Cd47okjCHD83YGzThGBDptXs9Z4', 'ed25519': '50f6ddbecdc848dcc6b818b14d1'}, desc.identifiers)
+    self.assertEqual('ed25519', desc.identifier_type)
+    self.assertEqual('50f6ddbecdc848dcc6b818b14d1', desc.identifier)
+
+    # check when there's conflicting keys
+
+    desc_text = b'\n'.join((get_microdescriptor(content = True),
+                            b'id rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4',
+                            b'id rsa1024 50f6ddbecdc848dcc6b818b14d1'))
+
+    desc = Microdescriptor(desc_text)
+    self.assertEqual({}, desc.identifiers)
+
+    try:
+      Microdescriptor(desc_text, validate = True)
+      self.fail('constructor validation should fail')
+    except ValueError as exc:
+      self.assertEqual("There can only be one 'id' line per a key type, but 'rsa1024' appeared multiple times", str(exc))



More information about the tor-commits mailing list