[tor-commits] [stem/master] Separate hidden service decryption from constructor

atagar at torproject.org atagar at torproject.org
Sun Oct 6 02:07:34 UTC 2019


commit 14a44b1c6e1438abdf5687a1c468536d88481f81
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Sep 22 14:02:06 2019 -0700

    Separate hidden service decryption from constructor
    
    Moving hidden service description from the constructor into a method for a
    couple reasons...
    
      1. Wherever possible we lazy rather than eagerly evaluate descriptor content.
         We do not know upfront what fields our user will want, so best to avoid
         doing unnecessarily decryption (which is particularly cpu intensive)
         unless our user indicates that they want this content.
    
      2. We cannot modify constructor signatures without plumbing arguments through
         several other methods. Not necessarily a no-go, but decryption requires an
         onion_address whereas construction does not so separating this into its
         own method sidesteps that headache.
---
 stem/descriptor/hidden_service.py         | 32 ++++++++++---------------------
 test/unit/descriptor/hidden_service_v3.py |  5 +++--
 2 files changed, 13 insertions(+), 24 deletions(-)

diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py
index fc898f93..fda5147b 100644
--- a/stem/descriptor/hidden_service.py
+++ b/stem/descriptor/hidden_service.py
@@ -530,16 +530,10 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
   def create(cls, attr = None, exclude = (), validate = True, sign = False):
     return cls(cls.content(attr, exclude, sign), validate = validate, skip_crypto_validation = not sign)
 
-  def __init__(self, raw_contents, validate = False, onion_address = None, skip_crypto_validation = False):
-    """
-    The onion_address is needed so that we can decrypt the descriptor, which is
-    impossible without the full onion address.
-    """
+  def __init__(self, raw_contents, validate = False, skip_crypto_validation = False):
     super(HiddenServiceDescriptorV3, self).__init__(raw_contents, lazy_load = not validate)
     entries = _descriptor_components(raw_contents, validate)
-    self.onion_address = onion_address
 
-    # XXX Do this parsing in its own function
     if validate:
       for keyword in REQUIRED_V3_FIELDS:
         if keyword not in entries:
@@ -556,23 +550,17 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
     else:
       self._entries = entries
 
-    # crypto validation (check skip_crypto_validation)
-    # ASN XXX need to verify descriptor signing certificate (for now we trust Tor to do it)
-    # ASN XXX need to verify descriptor signature (for now we trust Tor to do it)
-
-    if not skip_crypto_validation and stem.prereq.is_crypto_available():
-      if self.onion_address is None:
-        raise ValueError('Onion address is required to decrypt v3 hidden service descriptors')
+  # TODO: The following is only marked as private because it is a work in
+  # progress. This will probably become something like "body()" which decrypts
+  # and parses the internal descriptor content.
 
-      # ATAGAR XXX need to do this cert extraction in the parsing handler
-      assert(self.signing_cert)
-      cert_lines = self.signing_cert.split('\n')
-      assert(cert_lines[0] == '-----BEGIN ED25519 CERT-----' and cert_lines[-1] == '-----END ED25519 CERT-----')
+  def _decrypt(self, onion_address):
+    assert(self.signing_cert)
+    cert_lines = self.signing_cert.split('\n')
+    assert(cert_lines[0] == '-----BEGIN ED25519 CERT-----' and cert_lines[-1] == '-----END ED25519 CERT-----')
 
-      desc_signing_cert = stem.descriptor.certificate.Ed25519Certificate.parse(''.join(cert_lines[1:-1]))
-      self.plaintext = self.decrypt_descriptor(desc_signing_cert)
+    desc_signing_cert = stem.descriptor.certificate.Ed25519Certificate.parse(''.join(cert_lines[1:-1]))
 
-  def decrypt_descriptor(self, desc_signing_cert):
     # Get crypto material.
     # ASN XXX Extract to its own function and assign them to class variables
     from cryptography.hazmat.primitives import serialization
@@ -585,7 +573,7 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
     if not blinded_key_bytes:
       raise ValueError('No signing key extension present')
 
-    identity_public_key = stem.descriptor.hsv3_crypto.decode_address(self.onion_address)
+    identity_public_key = stem.descriptor.hsv3_crypto.decode_address(onion_address)
     identity_public_key_bytes = identity_public_key.public_bytes(encoding=serialization.Encoding.Raw,
                                                                  format=serialization.PublicFormat.Raw)
     assert(len(identity_public_key_bytes) == 32)
diff --git a/test/unit/descriptor/hidden_service_v3.py b/test/unit/descriptor/hidden_service_v3.py
index 98de246e..ac2f8ea7 100644
--- a/test/unit/descriptor/hidden_service_v3.py
+++ b/test/unit/descriptor/hidden_service_v3.py
@@ -44,13 +44,14 @@ class TestHiddenServiceDescriptorV3(unittest.TestCase):
       return
 
     with open(get_resource('hidden_service_v3_test'), 'rb') as descriptor_file:
-      desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor-3 1.0', validate = True,
-                  onion_address = 'sltib6sxkuxh2scmtuvd5w2g7pahnzkovefxpo4e4ptnkzl5kkq5h2ad.onion'))
+      desc = next(stem.descriptor.parse_file(descriptor_file, 'hidden-service-descriptor-3 1.0', validate = True))
 
     self.assertEqual(3, desc.version)
     self.assertEqual(180, desc.lifetime)
     self.assertEqual(42, desc.revision_counter)
 
+    desc._decrypt('sltib6sxkuxh2scmtuvd5w2g7pahnzkovefxpo4e4ptnkzl5kkq5h2ad.onion')
+
   def test_for_riseup(self):
     """
     Parse riseup's descriptor...





More information about the tor-commits mailing list