[tor-commits] [stem/master] Base hidden service descriptor class

atagar at torproject.org atagar at torproject.org
Sun Aug 25 00:20:44 UTC 2019


commit 6176a1759fadd4625ff8e1c9155053916202c1ee
Author: Damian Johnson <atagar at torproject.org>
Date:   Wed Aug 21 15:01:20 2019 -0700

    Base hidden service descriptor class
    
    Common parent for v2 and v3 hidden service descriptors. I'm not yet sure what
    (if anything) they'll share, but this class is proper regardless for object
    orientation (see router status entries for what this will exemplify).
    
    Existing usage of the HiddenServiceDescriptor class name is unfortunate since
    that is what our common base class should be called. Oh well - yet another
    thing to correct when we break backward compatibility in Stem 2.x.
---
 stem/control.py                                    |  2 +-
 stem/descriptor/__init__.py                        |  6 ++--
 stem/descriptor/hidden_service.py                  | 32 ++++++++++++++++------
 stem/response/events.py                            |  2 +-
 test/settings.cfg                                  |  2 +-
 .../{hidden_service.py => hidden_service_v2.py}    | 28 +++++++++----------
 6 files changed, 44 insertions(+), 28 deletions(-)

diff --git a/stem/control.py b/stem/control.py
index 81cb1682..86f4e787 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -2136,7 +2136,7 @@ class Controller(BaseController):
     :param list servers: requrest the descriptor from these specific servers
     :param float timeout: seconds to wait when **await_result** is **True**
 
-    :returns: :class:`~stem.descriptor.hidden_service.HiddenServiceDescriptor`
+    :returns: :class:`~stem.descriptor.hidden_service.HiddenServiceDescriptorV2`
       for the given service if **await_result** is **True**, or **None** otherwise
 
     :raises:
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index fd96c042..9415454c 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -329,7 +329,7 @@ def parse_file(descriptor_file, descriptor_type = None, validate = False, docume
   torperf 1.0                               **unsupported**
   bridge-pool-assignment 1.0                **unsupported**
   tordnsel 1.0                              :class:`~stem.descriptor.tordnsel.TorDNSEL`
-  hidden-service-descriptor 1.0             :class:`~stem.descriptor.hidden_service.HiddenServiceDescriptor`
+  hidden-service-descriptor 1.0             :class:`~stem.descriptor.hidden_service.HiddenServiceDescriptorV2`
   ========================================= =====
 
   If you're using **python 3** then beware that the open() function defaults to
@@ -536,8 +536,8 @@ def _parse_metrics_file(descriptor_type, major_version, minor_version, descripto
 
     for desc in stem.descriptor.tordnsel._parse_file(descriptor_file, validate = validate, **kwargs):
       yield desc
-  elif descriptor_type == stem.descriptor.hidden_service.HiddenServiceDescriptor.TYPE_ANNOTATION_NAME and major_version == 1:
-    document_type = stem.descriptor.hidden_service.HiddenServiceDescriptor
+  elif descriptor_type == stem.descriptor.hidden_service.HiddenServiceDescriptorV2.TYPE_ANNOTATION_NAME and major_version == 1:
+    document_type = stem.descriptor.hidden_service.HiddenServiceDescriptorV2
 
     for desc in stem.descriptor.hidden_service._parse_file(descriptor_file, validate = validate, **kwargs):
       yield desc
diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py
index 665d8664..ab5699f6 100644
--- a/stem/descriptor/hidden_service.py
+++ b/stem/descriptor/hidden_service.py
@@ -16,7 +16,8 @@ These are only available through the Controller's
 
 ::
 
-  HiddenServiceDescriptor - Tor hidden service descriptor.
+  BaseHiddenServiceDescriptor - Common parent for hidden service descriptors
+    +- HiddenServiceDescriptorV2 - Version 2 hidden service descriptor
 
 .. versionadded:: 1.4.0
 """
@@ -111,7 +112,7 @@ def _parse_file(descriptor_file, validate = False, **kwargs):
     **True**, skips these checks otherwise
   :param dict kwargs: additional arguments for the descriptor constructor
 
-  :returns: iterator for :class:`~stem.descriptor.hidden_service.HiddenServiceDescriptor`
+  :returns: iterator for :class:`~stem.descriptor.hidden_service.HiddenServiceDescriptorV2`
     instances in the file
 
   :raises:
@@ -130,7 +131,7 @@ def _parse_file(descriptor_file, validate = False, **kwargs):
       if descriptor_content[0].startswith(b'@type'):
         descriptor_content = descriptor_content[1:]
 
-      yield HiddenServiceDescriptor(bytes.join(b'', descriptor_content), validate, **kwargs)
+      yield HiddenServiceDescriptorV2(bytes.join(b'', descriptor_content), validate, **kwargs)
     else:
       break  # done parsing file
 
@@ -181,7 +182,17 @@ _parse_publication_time_line = _parse_timestamp_line('publication-time', 'publis
 _parse_signature_line = _parse_key_block('signature', 'signature', 'SIGNATURE')
 
 
-class HiddenServiceDescriptor(Descriptor):
+class BaseHiddenServiceDescriptor(Descriptor):
+  """
+  Hidden service descriptor.
+
+  .. versionadded:: 1.8.0
+  """
+
+  # TODO: rename this class to HiddenServiceDescriptor in stem 2.x
+
+
+class HiddenServiceDescriptorV2(BaseHiddenServiceDescriptor):
   """
   Hidden service descriptor.
 
@@ -261,7 +272,7 @@ class HiddenServiceDescriptor(Descriptor):
     return cls(cls.content(attr, exclude, sign), validate = validate, skip_crypto_validation = not sign)
 
   def __init__(self, raw_contents, validate = False, skip_crypto_validation = False):
-    super(HiddenServiceDescriptor, self).__init__(raw_contents, lazy_load = not validate)
+    super(HiddenServiceDescriptorV2, self).__init__(raw_contents, lazy_load = not validate)
     entries = _descriptor_components(raw_contents, validate, non_ascii_fields = ('introduction-points'))
 
     if validate:
@@ -317,9 +328,9 @@ class HiddenServiceDescriptor(Descriptor):
       authentication_type = int(binascii.hexlify(content[0:1]), 16)
 
       if authentication_type == BASIC_AUTH:
-        content = HiddenServiceDescriptor._decrypt_basic_auth(content, authentication_cookie)
+        content = HiddenServiceDescriptorV2._decrypt_basic_auth(content, authentication_cookie)
       elif authentication_type == STEALTH_AUTH:
-        content = HiddenServiceDescriptor._decrypt_stealth_auth(content, authentication_cookie)
+        content = HiddenServiceDescriptorV2._decrypt_stealth_auth(content, authentication_cookie)
       else:
         raise DecryptionFailure("Unrecognized authentication type '%s', currently we only support basic auth (%s) and stealth auth (%s)" % (authentication_type, BASIC_AUTH, STEALTH_AUTH))
 
@@ -328,7 +339,7 @@ class HiddenServiceDescriptor(Descriptor):
     elif not content.startswith(b'introduction-point '):
       raise DecryptionFailure('introduction-points content is encrypted, you need to provide its authentication_cookie')
 
-    return HiddenServiceDescriptor._parse_introduction_points(content)
+    return HiddenServiceDescriptorV2._parse_introduction_points(content)
 
   @staticmethod
   def _decrypt_basic_auth(content, authentication_cookie):
@@ -439,3 +450,8 @@ class HiddenServiceDescriptor(Descriptor):
       introduction_points.append(IntroductionPoints(**attr))
 
     return introduction_points
+
+
+# TODO: drop this alias in stem 2.x
+
+HiddenServiceDescriptor = HiddenServiceDescriptorV2
diff --git a/stem/response/events.py b/stem/response/events.py
index 7a5f749b..27a3e405 100644
--- a/stem/response/events.py
+++ b/stem/response/events.py
@@ -701,7 +701,7 @@ class HSDescContentEvent(Event):
   :var str directory: hidden service directory servicing the request
   :var str directory_fingerprint: hidden service directory's finterprint
   :var str directory_nickname: hidden service directory's nickname if it was provided
-  :var stem.descriptor.hidden_service.HiddenServiceDescriptor descriptor: descriptor that was retrieved
+  :var stem.descriptor.hidden_service.HiddenServiceDescriptorV2 descriptor: descriptor that was retrieved
   """
 
   _VERSION_ADDED = stem.version.Requirement.EVENT_HS_DESC_CONTENT
diff --git a/test/settings.cfg b/test/settings.cfg
index 3308c104..2e6b29dd 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -258,7 +258,7 @@ test.unit_tests
 |test.unit.descriptor.networkstatus.document_v2.TestNetworkStatusDocument
 |test.unit.descriptor.networkstatus.document_v3.TestNetworkStatusDocument
 |test.unit.descriptor.networkstatus.bridge_document.TestBridgeNetworkStatusDocument
-|test.unit.descriptor.hidden_service.TestHiddenServiceDescriptor
+|test.unit.descriptor.hidden_service_v2.TestHiddenServiceDescriptorV2
 |test.unit.descriptor.certificate.TestEd25519Certificate
 |test.unit.descriptor.bandwidth_file.TestBandwidthFile
 |test.unit.exit_policy.rule.TestExitPolicyRule
diff --git a/test/unit/descriptor/hidden_service.py b/test/unit/descriptor/hidden_service_v2.py
similarity index 95%
rename from test/unit/descriptor/hidden_service.py
rename to test/unit/descriptor/hidden_service_v2.py
index c49741d3..8442376b 100644
--- a/test/unit/descriptor/hidden_service.py
+++ b/test/unit/descriptor/hidden_service_v2.py
@@ -13,7 +13,7 @@ import test.require
 from stem.descriptor.hidden_service import (
   REQUIRED_FIELDS,
   DecryptionFailure,
-  HiddenServiceDescriptor,
+  HiddenServiceDescriptorV2,
 )
 
 from test.unit.descriptor import (
@@ -236,14 +236,14 @@ lj/7xMZWDrfyw5H86L0QiaZnkmD+nig1+S+Rn39mmuEgl2iwZO/ihlncUJQTEULb
 -----END MESSAGE-----\
 """
 
-expect_invalid_attr = functools.partial(base_expect_invalid_attr, HiddenServiceDescriptor, 'descriptor_id', 'y3olqqblqw2gbh6phimfuiroechjjafa')
-expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, HiddenServiceDescriptor, 'descriptor_id', 'y3olqqblqw2gbh6phimfuiroechjjafa')
+expect_invalid_attr = functools.partial(base_expect_invalid_attr, HiddenServiceDescriptorV2, 'descriptor_id', 'y3olqqblqw2gbh6phimfuiroechjjafa')
+expect_invalid_attr_for_text = functools.partial(base_expect_invalid_attr_for_text, HiddenServiceDescriptorV2, 'descriptor_id', 'y3olqqblqw2gbh6phimfuiroechjjafa')
 
 
-class TestHiddenServiceDescriptor(unittest.TestCase):
+class TestHiddenServiceDescriptorV2(unittest.TestCase):
   def test_from_str(self):
-    sig = HiddenServiceDescriptor.create()
-    self.assertEqual(sig, HiddenServiceDescriptor.from_str(str(sig)))
+    sig = HiddenServiceDescriptorV2.create()
+    self.assertEqual(sig, HiddenServiceDescriptorV2.from_str(str(sig)))
 
   def test_for_duckduckgo_with_validation(self):
     """
@@ -279,7 +279,7 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
 
   @test.require.cryptography
   def test_descriptor_signing(self):
-    self.assertRaisesWith(NotImplementedError, 'Signing of HiddenServiceDescriptor not implemented', HiddenServiceDescriptor.create, sign = True)
+    self.assertRaisesWith(NotImplementedError, 'Signing of HiddenServiceDescriptorV2 not implemented', HiddenServiceDescriptorV2.create, sign = True)
 
   @test.require.cryptography
   def test_with_basic_auth(self):
@@ -418,7 +418,7 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
     Basic sanity check that we can parse a hidden service descriptor with minimal attributes.
     """
 
-    desc = HiddenServiceDescriptor.create()
+    desc = HiddenServiceDescriptorV2.create()
 
     self.assertEqual('y3olqqblqw2gbh6phimfuiroechjjafa', desc.descriptor_id)
     self.assertEqual(2, desc.version)
@@ -435,7 +435,7 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
     Includes unrecognized content in the descriptor.
     """
 
-    desc = HiddenServiceDescriptor.create({'pepperjack': 'is oh so tasty!'})
+    desc = HiddenServiceDescriptorV2.create({'pepperjack': 'is oh so tasty!'})
     self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
 
   def test_proceeding_line(self):
@@ -443,14 +443,14 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
     Includes a line prior to the 'rendezvous-service-descriptor' entry.
     """
 
-    expect_invalid_attr_for_text(self, b'hibernate 1\n' + HiddenServiceDescriptor.content())
+    expect_invalid_attr_for_text(self, b'hibernate 1\n' + HiddenServiceDescriptorV2.content())
 
   def test_trailing_line(self):
     """
     Includes a line after the 'router-signature' entry.
     """
 
-    expect_invalid_attr_for_text(self, HiddenServiceDescriptor.content() + b'\nhibernate 1')
+    expect_invalid_attr_for_text(self, HiddenServiceDescriptorV2.content() + b'\nhibernate 1')
 
   def test_required_fields(self):
     """
@@ -469,7 +469,7 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
     }
 
     for line in REQUIRED_FIELDS:
-      desc_text = HiddenServiceDescriptor.content(exclude = (line,))
+      desc_text = HiddenServiceDescriptorV2.content(exclude = (line,))
 
       expected = [] if line == 'protocol-versions' else None
       expect_invalid_attr_for_text(self, desc_text, line_to_attr[line], expected)
@@ -514,14 +514,14 @@ class TestHiddenServiceDescriptor(unittest.TestCase):
     are valid according to the spec.
     """
 
-    missing_field_desc = HiddenServiceDescriptor.create(exclude = ('introduction-points',))
+    missing_field_desc = HiddenServiceDescriptorV2.create(exclude = ('introduction-points',))
 
     self.assertEqual(None, missing_field_desc.introduction_points_encoded)
     self.assertEqual([], missing_field_desc.introduction_points_auth)
     self.assertEqual(None, missing_field_desc.introduction_points_content)
     self.assertEqual([], missing_field_desc.introduction_points())
 
-    empty_field_desc = HiddenServiceDescriptor.create({'introduction-points': MESSAGE_BLOCK % ''})
+    empty_field_desc = HiddenServiceDescriptorV2.create({'introduction-points': MESSAGE_BLOCK % ''})
 
     self.assertEqual((MESSAGE_BLOCK % '').strip(), empty_field_desc.introduction_points_encoded)
     self.assertEqual([], empty_field_desc.introduction_points_auth)





More information about the tor-commits mailing list