[stem/master] Separate hidden service decryption from constructor

commit 14a44b1c6e1438abdf5687a1c468536d88481f81 Author: Damian Johnson <atagar@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...
participants (1)
-
atagar@torproject.org