[tor-commits] [stem/master] Move some stuff back to hsv3_crypto.

atagar at torproject.org atagar at torproject.org
Sun Nov 17 23:40:39 UTC 2019


commit 9ffca2043c66544c0d54885ae3d19b10dcc652f1
Author: George Kadianakis <desnacked at riseup.net>
Date:   Thu Oct 10 20:56:17 2019 +0300

    Move some stuff back to hsv3_crypto.
    
    These functions are gonna be used both by the decoding code and the encoding
    code.
    
    We are now starting to write an encode-to-decode unittest and when debuggin the
    descriptor encryption it's very useful to have the encryption-related code
    centralized in one function.
---
 stem/descriptor/hidden_service.py | 31 +++++--------
 stem/descriptor/hsv3_crypto.py    | 98 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 110 insertions(+), 19 deletions(-)

diff --git a/stem/descriptor/hidden_service.py b/stem/descriptor/hidden_service.py
index 4a75bc88..305dfaf7 100644
--- a/stem/descriptor/hidden_service.py
+++ b/stem/descriptor/hidden_service.py
@@ -42,7 +42,9 @@ import stem.util.connection
 import stem.util.str_tools
 import stem.util.tor_tools
 
-from stem.descriptor.certificate import Ed25519Certificate
+import stem.descriptor.hsv3_crypto as hsv3_crypto
+
+from stem.descriptor.certificate import Ed25519Certificate, CertType
 
 from stem.descriptor import (
   PGP_BLOCK_END,
@@ -263,7 +265,7 @@ class IntroductionPointV3(object):
     return body
 
 class AuthorizedClient(collections.namedtuple('AuthorizedClient', ['id', 'iv', 'cookie'])):
-    """
+  """
   Client authorized to use a v3 hidden service.
 
   .. versionadded:: 1.8.0
@@ -318,9 +320,6 @@ def _decrypt_layer(encrypted_block, constant, revision_counter, subcredential, b
   from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
   from cryptography.hazmat.backends import default_backend
 
-  def pack(val):
-    return struct.pack('>Q', val)
-
   if encrypted_block.startswith('-----BEGIN MESSAGE-----\n') and encrypted_block.endswith('\n-----END MESSAGE-----'):
     encrypted_block = encrypted_block[24:-22]
 
@@ -336,14 +335,10 @@ def _decrypt_layer(encrypted_block, constant, revision_counter, subcredential, b
   ciphertext = encrypted[SALT_LEN:-MAC_LEN]
   expected_mac = encrypted[-MAC_LEN:]
 
-  kdf = hashlib.shake_256(blinded_key + subcredential + pack(revision_counter) + salt + constant)
-  keys = kdf.digest(S_KEY_LEN + S_IV_LEN + MAC_LEN)
+  secret_key, secret_iv, mac_key = hsv3_crypto.get_desc_keys(blinded_key, constant,
+                                                             subcredential, revision_counter, salt, )
 
-  secret_key = keys[:S_KEY_LEN]
-  secret_iv = keys[S_KEY_LEN:S_KEY_LEN + S_IV_LEN]
-  mac_key = keys[S_KEY_LEN + S_IV_LEN:]
-
-  mac = hashlib.sha3_256(pack(len(mac_key)) + mac_key + pack(len(salt)) + salt + ciphertext).digest()
+  mac = hsv3_crypto.get_desc_encryption_mac(mac_key, salt, ciphertext)
 
   if mac != expected_mac:
     raise ValueError('Malformed mac (expected %s, but was %s)' % (expected_mac, mac))
@@ -844,6 +839,7 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
     super(HiddenServiceDescriptorV3, self).__init__(raw_contents, lazy_load = not validate)
 
     self._inner_layer = None
+    self._outer_layer = None
     entries = _descriptor_components(raw_contents, validate)
 
     if validate:
@@ -889,15 +885,12 @@ class HiddenServiceDescriptorV3(BaseHiddenServiceDescriptor):
       if not blinded_key:
         raise ValueError('No signing key is present')
 
-      # credential = H('credential' | public-identity-key)
-      # subcredential = H('subcredential' | credential | blinded-public-key)
-
       identity_public_key = HiddenServiceDescriptorV3._public_key_from_address(onion_address)
-      credential = hashlib.sha3_256(b'credential%s' % (identity_public_key)).digest()
-      subcredential = hashlib.sha3_256(b'subcredential%s%s' % (credential, blinded_key)).digest()
 
-      outer_layer = OuterLayer._decrypt(self.superencrypted, self.revision_counter, subcredential, blinded_key)
-      self._inner_layer = InnerLayer._decrypt(outer_layer, self.revision_counter, subcredential, blinded_key)
+      subcredential = hsv3_crypto.get_subcredential(identity_public_key, blinded_key)
+
+      self._outer_layer = OuterLayer._decrypt(self.superencrypted, self.revision_counter, subcredential, blinded_key)
+      self._inner_layer = InnerLayer._decrypt(self._outer_layer, self.revision_counter, subcredential, blinded_key)
 
     return self._inner_layer
 
diff --git a/stem/descriptor/hsv3_crypto.py b/stem/descriptor/hsv3_crypto.py
index bd090265..ed4ad7b8 100644
--- a/stem/descriptor/hsv3_crypto.py
+++ b/stem/descriptor/hsv3_crypto.py
@@ -57,3 +57,101 @@ class HSv3PublicBlindedKey(object):
         """
         ext.slow_ed25519.checkvalid(signature, message, self.public_key)
 
+"""
+subcredential
+
+       subcredential = H("subcredential" | credential | blinded-public-ke
+       credential = H("credential" | public-identity-key)
+"""
+def get_subcredential(public_identity_key, blinded_key):
+    cred_bytes_constant = "credential".encode()
+    subcred_bytes_constant = "subcredential".encode()
+
+    credential = hashlib.sha3_256(b"%s%s" % (cred_bytes_constant, public_identity_key)).digest()
+    subcredential = hashlib.sha3_256(b"%s%s%s" % (subcred_bytes_constant, credential, blinded_key)).digest()
+
+    return subcredential
+
+
+"""
+Basic descriptor logic:
+
+       SALT = 16 bytes from H(random), changes each time we rebuld the
+              descriptor even if the content of the descriptor hasn't changed.
+              (So that we don't leak whether the intro point list etc. changed)
+
+       secret_input = SECRET_DATA | subcredential | INT_8(revision_counter)
+
+       keys = KDF(secret_input | salt | STRING_CONSTANT, S_KEY_LEN + S_IV_LEN + MAC_KEY_LEN)
+
+       SECRET_KEY = first S_KEY_LEN bytes of keys
+       SECRET_IV  = next S_IV_LEN bytes of keys
+       MAC_KEY    = last MAC_KEY_LEN bytes of keys
+
+
+Layer data:
+
+ 2.5.1.1. First layer encryption logic
+     SECRET_DATA = blinded-public-key
+     STRING_CONSTANT = "hsdir-superencrypted-data"
+
+ 2.5.2.1. Second layer encryption keys
+     SECRET_DATA = blinded-public-key | descriptor_cookie
+     STRING_CONSTANT = "hsdir-encrypted-data"
+"""
+
+SALT_LEN = 16
+MAC_LEN = 32
+
+S_KEY_LEN = 32
+S_IV_LEN = 16
+MAC_KEY_LEN = 32
+
+"""
+
+Descriptor encryption
+
+"""
+
+def pack(val):
+    return struct.pack('>Q', val)
+
+
+def get_desc_keys(secret_data, string_constant,
+                  subcredential, revision_counter, salt):
+    """
+       secret_input = SECRET_DATA | subcredential | INT_8(revision_counter)
+
+       keys = KDF(secret_input | salt | STRING_CONSTANT, S_KEY_LEN + S_IV_LEN + MAC_KEY_LEN)
+
+       SECRET_KEY = first S_KEY_LEN bytes of keys
+       SECRET_IV  = next S_IV_LEN bytes of keys
+       MAC_KEY    = last MAC_KEY_LEN bytes of keys
+
+    where
+
+    2.5.1.1. First layer encryption logic
+     SECRET_DATA = blinded-public-key
+     STRING_CONSTANT = "hsdir-superencrypted-data"
+
+    2.5.2.1. Second layer encryption keys
+     SECRET_DATA = blinded-public-key | descriptor_cookie
+     STRING_CONSTANT = "hsdir-encrypted-data"
+    """
+    secret_input = b"%s%s%s" % (secret_data, subcredential, pack(revision_counter))
+
+    kdf = hashlib.shake_256(secret_input + salt + string_constant)
+
+    keys = kdf.digest(S_KEY_LEN + S_IV_LEN + MAC_LEN)
+
+    secret_key = keys[:S_KEY_LEN]
+    secret_iv = keys[S_KEY_LEN:S_KEY_LEN + S_IV_LEN]
+    mac_key = keys[S_KEY_LEN + S_IV_LEN:]
+
+    return secret_key, secret_iv, mac_key
+
+def get_desc_encryption_mac(key, salt, ciphertext):
+
+    mac = hashlib.sha3_256(pack(len(key)) + key + pack(len(salt)) + salt + ciphertext).digest()
+    return mac
+





More information about the tor-commits mailing list