[tor-commits] [stem/master] Change KDF to a named tuple

atagar at torproject.org atagar at torproject.org
Wed Feb 7 19:44:51 UTC 2018


commit 984add2da2e62b8c102f60a671717b240a004bac
Author: Damian Johnson <atagar at torproject.org>
Date:   Wed Jan 31 11:57:51 2018 -0800

    Change KDF to a named tuple
    
    Been going back and forth on this but yeah, think it's a bit nicer this way.
---
 stem/client/__init__.py                 | 46 +++++++++++++++------------------
 test/settings.cfg                       |  2 +-
 test/unit/client/__init__.py            |  2 +-
 test/unit/client/{kdf_tor.py => kdf.py} | 17 ++++--------
 4 files changed, 28 insertions(+), 39 deletions(-)

diff --git a/stem/client/__init__.py b/stem/client/__init__.py
index 4c6bdb12..f3e842c6 100644
--- a/stem/client/__init__.py
+++ b/stem/client/__init__.py
@@ -12,7 +12,9 @@ a wrapper for :class:`~stem.socket.RelaySocket`, much the same way as
 ::
 
   split - splits bytes into substrings
-  KDF - KDF-TOR key derivative for TAP, CREATE_FAST handshakes, and hidden serivces
+
+  KDF - KDF-TOR derivatived attributes
+    +- from_value - parses key material
 
   Field - Packable and unpackable datatype.
     |- Size - Field of a static size.
@@ -107,6 +109,7 @@ a wrapper for :class:`~stem.socket.RelaySocket`, much the same way as
   ===================== ===========
 """
 
+import collections
 import hashlib
 import io
 import struct
@@ -461,45 +464,38 @@ class Certificate(Field):
     return _hash_attr(self, 'type_int', 'value')
 
 
-class KDF(object):
+class KDF(collections.namedtuple('KDF', ['key_hash', 'forward_digest', 'backward_digest', 'forward_key', 'backward_key'])):
   """
-  Tor's derived key for TAP, CREATE_FAST handshakes, and hidden service
-  protocols as defined tor-spec section 5.2.1.
+  Computed KDF-TOR derived values for TAP, CREATE_FAST handshakes, and hidden
+  service protocols as defined tor-spec section 5.2.1.
 
-  :var bytes key_hash: expected derived key that proves knowledge of our shared
-    computed key
+  :var bytes key_hash: hash that proves knowledge of our shared key
   :var bytes forward_digest: forward digest hash seed
   :var bytes backward_digest: backward digest hash seed
   :var bytes forward_key: forward encryption key
   :var bytes backward_key: backward encryption key
   """
 
-  def __init__(self, key_material):
-    value = KDF._value(key_material)
-
-    self.key_hash, value = split(value, HASH_LEN)
-    self.forward_digest, value = split(value, HASH_LEN)
-    self.backward_digest, value = split(value, HASH_LEN)
-    self.forward_key, value = split(value, KEY_LEN)
-    self.backward_key, value = split(value, KEY_LEN)
-
   @staticmethod
-  def _value(key):
-    """
-    Computes the KDF-TOR value...
-
-      K = H(K0 | [00]) | H(K0 | [01]) | H(K0 | [02]) | ...
-    """
+  def from_value(key_material):
+    # Derived key material, as per...
+    #
+    #   K = H(K0 | [00]) | H(K0 | [01]) | H(K0 | [02]) | ...
 
     derived_key = ''
-    derived_key_len = KEY_LEN * 2 + HASH_LEN * 3
     counter = 0
 
-    while len(derived_key) < derived_key_len:
-      derived_key += hashlib.sha1(key + Size.CHAR.pack(counter)).digest()
+    while len(derived_key) < KEY_LEN * 2 + HASH_LEN * 3:
+      derived_key += hashlib.sha1(key_material + Size.CHAR.pack(counter)).digest()
       counter += 1
 
-    return derived_key[:derived_key_len]
+    key_hash, derived_key = split(derived_key, HASH_LEN)
+    forward_digest, derived_key = split(derived_key, HASH_LEN)
+    backward_digest, derived_key = split(derived_key, HASH_LEN)
+    forward_key, derived_key = split(derived_key, KEY_LEN)
+    backward_key, derived_key = split(derived_key, KEY_LEN)
+
+    return KDF(key_hash, forward_digest, backward_digest, forward_key, backward_key)
 
 
 setattr(Size, 'CHAR', Size('CHAR', 1, '!B'))
diff --git a/test/settings.cfg b/test/settings.cfg
index 7af57181..4080ca2a 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -233,7 +233,7 @@ test.unit_tests
 |test.unit.client.size.TestSize
 |test.unit.client.address.TestAddress
 |test.unit.client.certificate.TestCertificate
-|test.unit.client.kdf_tor.TestKdfTor
+|test.unit.client.kdf.TestKDF
 |test.unit.client.cell.TestCell
 |test.unit.connection.authentication.TestAuthenticate
 |test.unit.connection.connect.TestConnect
diff --git a/test/unit/client/__init__.py b/test/unit/client/__init__.py
index ba93b3e6..91d3b2be 100644
--- a/test/unit/client/__init__.py
+++ b/test/unit/client/__init__.py
@@ -6,7 +6,7 @@ __all__ = [
   'address',
   'cell',
   'certificate',
-  'kdf_tor',
+  'kdf',
   'size',
 ]
 
diff --git a/test/unit/client/kdf_tor.py b/test/unit/client/kdf.py
similarity index 59%
rename from test/unit/client/kdf_tor.py
rename to test/unit/client/kdf.py
index f356d2cd..56665d31 100644
--- a/test/unit/client/kdf_tor.py
+++ b/test/unit/client/kdf.py
@@ -1,5 +1,5 @@
 """
-Unit tests for stem.client.kdf_tor.
+Unit tests for stem.client.KDF.
 """
 
 import unittest
@@ -9,24 +9,17 @@ import stem.client
 KEY_1 = '\xec\xec.\xeb7R\xf2\n\xcb\xce\x97\xf4\x86\x82\x19#\x10\x0f\x08\xf0\xa2Z\xdeJ\x8f2\x8cc\xf6\xfa\x0e\t\x83f\xc5\xe2\xb3\x94\xa8\x13'
 KEY_2 = '\xe0v\xe4\xfaTB\x91\x1c\x81Gz\xa0\tI\xcb{\xc56\xcfV\xc2\xa0\x19\x9c\x98\x9a\x06\x0e\xc5\xfa\xb0z\x83\xa6\x10\xf6r"<b'
 
-DERIVED_1 = '\xca+\x81\x05\x14\x9d)o\xa6\x82\xe9B\xa8?\xf2\xaf\x85\x1b]6\xac\xcc\xbc\x91\xb1\xaf\xd7\xe0\xe9\x9dF#\xd8\xdbz\xe8\xe6\xca\x83,*\xe5scX\xbb+\xca \xcb\xa4\xbc\xad\x0f\x95\x0cO\xcc\xac\xf1\xc3\xbe\xc9\xe1\xf4\x90f\xdai\xf3\xf3\xf5\x14\xb5\xb9\x03U\xaf\x1e\x1b\xb1q||\x86A<_\xf7\xa0%\x86'
-DERIVED_2 = '\xbc0\xf99\x8e;Te\xbb+\xdb\xabR3l\xb9f?\x07KZC8\xe7\xa15\xd1IS\xd9\xd4\x1e\x96\xf6\xcd\x82\x91\x0b}r\x7f\xc5\xc0\xb1/[\x97dW\xba\x82g\xe7m^\x06[\xe6\xf8\xb4\x83f>c\x8b\x0f\x03\xcc\x98\x1f~t\x88\xe1\x83\xec\xbf*_\x8cF\x0e1\xa9\x17\xce\xa6\xa3\xd1+]\x1f'
 
-
-class TestKdfTor(unittest.TestCase):
-  def test_kdf_value(self):
-    self.assertEqual(DERIVED_1, stem.client.KDF._value(KEY_1))
-    self.assertEqual(DERIVED_2, stem.client.KDF._value(KEY_2))
-
-  def test_kdf_attributes(self):
-    k1 = stem.client.KDF(KEY_1)
+class TestKDF(unittest.TestCase):
+  def test_parsing(self):
+    k1 = stem.client.KDF.from_value(KEY_1)
     self.assertEqual('\xca+\x81\x05\x14\x9d)o\xa6\x82\xe9B\xa8?\xf2\xaf\x85\x1b]6', k1.key_hash)
     self.assertEqual('\xac\xcc\xbc\x91\xb1\xaf\xd7\xe0\xe9\x9dF#\xd8\xdbz\xe8\xe6\xca\x83,', k1.forward_digest)
     self.assertEqual('*\xe5scX\xbb+\xca \xcb\xa4\xbc\xad\x0f\x95\x0cO\xcc\xac\xf1', k1.backward_digest)
     self.assertEqual('\xc3\xbe\xc9\xe1\xf4\x90f\xdai\xf3\xf3\xf5\x14\xb5\xb9\x03', k1.forward_key)
     self.assertEqual('U\xaf\x1e\x1b\xb1q||\x86A<_\xf7\xa0%\x86', k1.backward_key)
 
-    k2 = stem.client.KDF(KEY_1)
+    k2 = stem.client.KDF.from_value(KEY_1)
     self.assertEqual('\xca+\x81\x05\x14\x9d)o\xa6\x82\xe9B\xa8?\xf2\xaf\x85\x1b]6', k2.key_hash)
     self.assertEqual('\xac\xcc\xbc\x91\xb1\xaf\xd7\xe0\xe9\x9dF#\xd8\xdbz\xe8\xe6\xca\x83,', k2.forward_digest)
     self.assertEqual('*\xe5scX\xbb+\xca \xcb\xa4\xbc\xad\x0f\x95\x0cO\xcc\xac\xf1', k2.backward_digest)





More information about the tor-commits mailing list