commit ec4d6f99e55b2ca62b7def152d31364de605c2fd Author: Damian Johnson atagar@torproject.org Date: Wed Sep 4 17:34:00 2019 -0700
Only import crypto modules if available
Stem has a soft dependency on the cryptography module. If unavailable we should gracefully degrade or explicitly error with a message saying that cryptography is required.
Also, cryptography's ed25519 class is pretty new, so folks will have outdated module versions for a while...
====================================================================== ERROR: test_for_decrypt ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/atagar/Desktop/stem/test/unit/descriptor/hidden_service_v3.py", line 43, in test_for_decrypt onion_address="sltib6sxkuxh2scmtuvd5w2g7pahnzkovefxpo4e4ptnkzl5kkq5h2ad.onion")) File "/home/atagar/Desktop/stem/stem/descriptor/__init__.py", line 442, in parse_file for desc in parse(descriptor_file): File "/home/atagar/Desktop/stem/stem/descriptor/__init__.py", line 545, in _parse_metrics_file for desc in stem.descriptor.hidden_service._parse_file(descriptor_file, desc_type, validate = validate, **kwargs): File "/home/atagar/Desktop/stem/stem/descriptor/hidden_service.py", line 156, in _parse_file yield desc_type(bytes.join(b'', descriptor_content), validate, **kwargs) File "/home/atagar/Desktop/stem/stem/descriptor/hidden_service.py", line 571, in __init__ plaintext = self.decrypt_descriptor(desc_signing_cert) File "/home/atagar/Desktop/stem/stem/descriptor/hidden_service.py", line 579, in decrypt_descriptor identity_public_key = stem.descriptor.hsv3_crypto.decode_address(self.onion_address) File "/home/atagar/Desktop/stem/stem/descriptor/hsv3_crypto.py", line 30, in decode_address from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey ImportError: No module named ed25519 --- stem/descriptor/hidden_service.py | 20 +++++++++++--------- stem/descriptor/hsv3_crypto.py | 16 ++++++++++++---- stem/prereq.py | 10 +++++++++- 3 files changed, 32 insertions(+), 14 deletions(-)
diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py index 5397d057..150ba368 100644 --- a/stem/descriptor/hidden_service.py +++ b/stem/descriptor/hidden_service.py @@ -31,6 +31,8 @@ import collections import hashlib import io
+import stem.descriptor.certificate +import stem.descriptor.hsv3_crypto import stem.prereq import stem.util.connection import stem.util.str_tools @@ -470,9 +472,6 @@ class HiddenServiceDescriptorV2(BaseHiddenServiceDescriptor):
return introduction_points
-import stem.descriptor.certificate -import stem.descriptor.hsv3_crypto as hsv3_crypto -from cryptography.hazmat.primitives import serialization
class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor): """ @@ -568,29 +567,32 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor): # 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)
- plaintext = self.decrypt_descriptor(desc_signing_cert) + if not skip_crypto_validation and stem.prereq.is_crypto_available(): + plaintext = self.decrypt_descriptor(desc_signing_cert)
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 + blinded_key_bytes = desc_signing_cert.get_signing_key() - identity_public_key = hsv3_crypto.decode_address(self.onion_address) + identity_public_key = stem.descriptor.hsv3_crypto.decode_address(self.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) assert(len(blinded_key_bytes) == 32)
- subcredential_bytes = hsv3_crypto.get_subcredential(identity_public_key_bytes, blinded_key_bytes) + subcredential_bytes = stem.descriptor.hsv3_crypto.get_subcredential(identity_public_key_bytes, blinded_key_bytes)
####################################### Do the decryption ###################################
- outter_layer_plaintext = hsv3_crypto.decrypt_outter_layer(self.superencrypted, self.revision_counter, + outter_layer_plaintext = stem.descriptor.hsv3_crypto.decrypt_outter_layer(self.superencrypted, self.revision_counter, identity_public_key_bytes, blinded_key_bytes, subcredential_bytes)
# ATAGAR XXX this parsing function is a hack. need to replace it with some stem parsing. - inner_layer_ciphertext = hsv3_crypto.parse_superencrypted_plaintext(outter_layer_plaintext) + inner_layer_ciphertext = stem.descriptor.hsv3_crypto.parse_superencrypted_plaintext(outter_layer_plaintext)
- inner_layer_plaintext = hsv3_crypto.decrypt_inner_layer(inner_layer_ciphertext, self.revision_counter, + inner_layer_plaintext = stem.descriptor.hsv3_crypto.decrypt_inner_layer(inner_layer_ciphertext, self.revision_counter, identity_public_key_bytes, blinded_key_bytes, subcredential_bytes)
print(inner_layer_plaintext) diff --git a/stem/descriptor/hsv3_crypto.py b/stem/descriptor/hsv3_crypto.py index de88b7ac..8f304c52 100644 --- a/stem/descriptor/hsv3_crypto.py +++ b/stem/descriptor/hsv3_crypto.py @@ -1,10 +1,7 @@ import base64 import hashlib
-from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.backends import default_backend - +import stem.prereq
""" Onion addresses @@ -32,6 +29,14 @@ def decode_address(onion_address_str):
:raises: ValueError """ + + # TODO: note the module version + + if not stem.prereq.is_crypto_available(ed25519 = True): + raise ImportError('Onion address decoding requires cryptography version XXX') + + from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey + if (len(onion_address_str) != 56 + len(".onion")): raise ValueError("Wrong address length")
@@ -149,6 +154,9 @@ def _ciphertext_mac_is_valid(key, salt, ciphertext, mac): def _decrypt_descriptor_layer(ciphertext_blob_b64, revision_counter, public_identity_key, subcredential, secret_data, string_constant): + from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + from cryptography.hazmat.backends import default_backend + # decode the thing ciphertext_blob = base64.b64decode(ciphertext_blob_b64)
diff --git a/stem/prereq.py b/stem/prereq.py index 4cb51113..0aadb35a 100644 --- a/stem/prereq.py +++ b/stem/prereq.py @@ -114,11 +114,13 @@ def is_sqlite_available(): return False
-def is_crypto_available(): +def is_crypto_available(ed25519 = False): """ Checks if the cryptography functions we use are available. This is used for verifying relay descriptor signatures.
+ :param bool ed25519: check for ed25519 support + :returns: **True** if we can use the cryptography module and **False** otherwise """ @@ -134,6 +136,12 @@ def is_crypto_available(): if not hasattr(rsa.RSAPrivateKey, 'sign'): raise ImportError()
+ # TODO: Check when the cryptography module's ed25519 class was added + # (it's not present in 2.0.3). + + if ed25519: + from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey + return True except ImportError: from stem.util import log