[tor-commits] [stem/master] Validate that signing key hash matches fingerprint

atagar at torproject.org atagar at torproject.org
Fri May 25 16:06:25 UTC 2012


commit 40a971105ff162d0caa9e0a1c5fa3c0f7d3c2b72
Author: Damian Johnson <atagar at torproject.org>
Date:   Fri May 25 08:59:03 2012 -0700

    Validate that signing key hash matches fingerprint
    
    When parsing server descriptors checking that their fingerprints match a hash
    of their signing key as part of validation. This requires the rsa module which
    is neither built in, nor is it packaged for debian distros. Installation of it
    is easy, but requires pip or easy-install. Instructions are available at...
    http://stuvel.eu/files/python-rsa-doc/installation.html
    
    It looks like python's builtin crypto module might be capable of doing this as
    well...
    http://stackoverflow.com/questions/5000434/python-rsa-library
    https://www.dlitz.net/software/pycrypto/api/current/Crypto.PublicKey.RSA._RSAobj-class.html#exportKey
    
    However, those instructions include usage of a private member and the function
    that they suggest dosn't exist on my system (python 2.7.1), so I feel pretty
    justified in saying "the pycrypto builtin is crap for this use case, and
    patches welcome if someone can figure out how to make it work".
    
    All credit for this patch goes to Beck, who's been diving into the descriptor
    crypto on...
    https://trac.torproject.org/projects/tor/ticket/5810
---
 stem/descriptor/server_descriptor.py      |   36 ++++++++++++++++++++++++++++-
 test/unit/descriptor/server_descriptor.py |   26 +++++++++++++++++++++
 2 files changed, 61 insertions(+), 1 deletions(-)

diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index f7d3437..7bf781f 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -31,9 +31,17 @@ import datetime
 import stem.descriptor
 import stem.descriptor.extrainfo_descriptor
 import stem.version
+import stem.util.log as log
 import stem.util.connection
 import stem.util.tor_tools
 
+try:
+  import rsa
+  IS_RSA_AVAILABLE = True
+except ImportError:
+  log.info("Unable to import the rsa module. Because of this we'll be unable to verify server integrity.")
+  IS_RSA_AVAILABLE = False
+
 # relay descriptors must have exactly one of the following
 REQUIRED_FIELDS = (
   "router",
@@ -569,6 +577,17 @@ class RelayDescriptor(ServerDescriptor):
     self._digest = None
     
     ServerDescriptor.__init__(self, raw_contents, validate, annotations)
+    
+    # if we have a fingerprint then checks that our fingerprint is a hash of
+    # our signing key
+    
+    if IS_RSA_AVAILABLE and validate and self.fingerprint:
+      pubkey = rsa.PublicKey.load_pkcs1(self.signing_key)
+      der_encoded = pubkey.save_pkcs1(format = "DER")
+      key_hash = hashlib.sha1(der_encoded).hexdigest()
+      
+      if key_hash != self.fingerprint.lower():
+        raise ValueError("Hash of our signing key doesn't match our fingerprint. Signing key hash: %s, fingerprint: %s" % (key_hash, self.fingerprint.lower()))
   
   def is_valid(self):
     """
@@ -578,7 +597,22 @@ class RelayDescriptor(ServerDescriptor):
       True if our signature matches our content, False otherwise
     """
     
-    raise NotImplementedError # TODO: implement
+    raise NotImplementedError # TODO: finish implementing
+    
+    # without validation we may be missing our signature
+    if not self.signature: return False
+    
+    # gets base64 encoded bytes of our signature without newlines nor the
+    # "-----[BEGIN|END] SIGNATURE-----" header/footer
+    
+    sig_content = self.signature.replace("\n", "")[25:-23]
+    sig_bytes = base64.b64decode(sig_content)
+    
+    # TODO: Decrypt the signature bytes with the signing key and remove
+    # the PKCS1 padding to get the original message, and encode the message
+    # in hex and compare it to the digest of the descriptor.
+    
+    return True
   
   def digest(self):
     if self._digest is None:
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 53b4675..6ad92b2 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -309,6 +309,32 @@ class TestServerDescriptor(unittest.TestCase):
         self.assertEquals(None, desc.socks_port)
         self.assertEquals(None, desc.dir_port)
   
+  def test_fingerprint_valid(self):
+    """
+    Checks that a fingerprint matching the hash of our signing key will validate.
+    """
+    
+    if not stem.descriptor.server_descriptor.IS_RSA_AVAILABLE:
+      self.skipTest("(rsa module unavailable)")
+    
+    fingerprint = "4F0C 867D F0EF 6816 0568 C826 838F 482C EA7C FE44"
+    desc_text = _make_descriptor({"opt fingerprint": fingerprint})
+    desc = RelayDescriptor(desc_text)
+    self.assertEquals(fingerprint.replace(" ", ""), desc.fingerprint)
+  
+  def test_fingerprint_invalid(self):
+    """
+    Checks that, with a correctly formed fingerprint, we'll fail validation if
+    it doesn't match the hash of our signing key.
+    """
+    
+    if not stem.descriptor.server_descriptor.IS_RSA_AVAILABLE:
+      self.skipTest("(rsa module unavailable)")
+    
+    fingerprint = "4F0C 867D F0EF 6816 0568 C826 838F 482C EA7C FE45"
+    desc_text = _make_descriptor({"opt fingerprint": fingerprint})
+    self._expect_invalid_attr(desc_text, "fingerprint", fingerprint.replace(" ", ""))
+  
   def test_minimal_bridge_descriptor(self):
     """
     Basic sanity check that we can parse a descriptor with minimal attributes.



More information about the tor-commits mailing list