[tor-commits] [flashproxy/master] factor out certificate checking and pinning into flashproxy.keys

infinity0 at torproject.org infinity0 at torproject.org
Wed Nov 6 21:34:32 UTC 2013


commit 1e541947c32f8d390b5df1cc1c42928e7c058dd5
Author: Ximin Luo <infinity0 at gmx.com>
Date:   Wed Nov 6 17:32:21 2013 +0000

    factor out certificate checking and pinning into flashproxy.keys
---
 Makefile                     |    2 +-
 flashproxy-reg-appspot       |   25 ++++---------------------
 flashproxy-reg-email         |   31 ++++---------------------------
 flashproxy/keys.py           |   31 +++++++++++++++++++++++++++++++
 flashproxy/test/test_keys.py |   22 ++++++++++++++++++++++
 setup-common.py              |    3 ++-
 6 files changed, 64 insertions(+), 50 deletions(-)

diff --git a/Makefile b/Makefile
index 2429f33..5623c4f 100644
--- a/Makefile
+++ b/Makefile
@@ -77,7 +77,7 @@ distclean:
 test: check
 check:
 	$(MAKE_CLIENT) check
-	$(PYTHON) setup-common.py check
+	$(PYTHON) setup-common.py test
 	cd facilitator && ./facilitator-test
 	cd proxy && ./flashproxy-test.js
 
diff --git a/flashproxy-reg-appspot b/flashproxy-reg-appspot
index c47579c..a261f10 100755
--- a/flashproxy-reg-appspot
+++ b/flashproxy-reg-appspot
@@ -13,9 +13,8 @@ import tempfile
 import urlparse
 import urllib2
 
-from flashproxy.keys import PIN_GOOGLE_CERT, PIN_GOOGLE_PUBKEY_SHA1
+from flashproxy.keys import PIN_GOOGLE_CERT, PIN_GOOGLE_PUBKEY_SHA1, check_certificate_pin, temp_cert
 from flashproxy.util import parse_addr_spec, format_addr
-from hashlib import sha1
 
 try:
     from M2Crypto import SSL, X509
@@ -142,31 +141,15 @@ class PinHTTPSConnection(httplib.HTTPSConnection):
         ctx = SSL.Context("tlsv1")
         ctx.set_verify(SSL.verify_peer, 3)
 
-        ca_certs_fd, ca_certs_path = tempfile.mkstemp(prefix="flashproxy-reg-appspot-",
-            dir=get_state_dir(), suffix=".crt")
-        try:
-            os.write(ca_certs_fd, PIN_GOOGLE_CERT)
-            os.close(ca_certs_fd)
-            ret = ctx.load_verify_locations(ca_certs_path)
+        with temp_cert(PIN_GOOGLE_CERT) as ca_file:
+            ret = ctx.load_verify_locations(ca_file.name)
             assert ret == 1
-        finally:
-            os.unlink(ca_certs_path)
 
         self.sock = SSL.Connection(ctx, sock)
         self.sock.connect((self.host, self.port))
 
         if options.use_certificate_pin:
-            found = []
-            for cert in self.sock.get_peer_cert_chain():
-                pubkey_der = cert.get_pubkey().as_der()
-                pubkey_digest = sha1(pubkey_der).digest()
-                if pubkey_digest in PIN_GOOGLE_PUBKEY_SHA1:
-                    break
-                found.append(pubkey_digest)
-            else:
-                found = "(" + ", ".join(x.encode("hex") for x in found) + ")"
-                expected = "(" + ", ".join(x.encode("hex") for x in PIN_GOOGLE_PUBKEY_SHA1) + ")"
-                raise ValueError("Public key does not match pin: got %s but expected any of %s" % (found, expected))
+            check_certificate_pin(self.sock, PIN_GOOGLE_PUBKEY_SHA1)
 
 class PinHTTPSHandler(urllib2.HTTPSHandler):
     def https_open(self, req):
diff --git a/flashproxy-reg-email b/flashproxy-reg-email
index 8bc8b6f..4f4599c 100755
--- a/flashproxy-reg-email
+++ b/flashproxy-reg-email
@@ -11,9 +11,8 @@ import sys
 import tempfile
 import urllib
 
-from flashproxy.keys import PIN_GOOGLE_CERT, PIN_GOOGLE_PUBKEY_SHA1, DEFAULT_FACILITATOR_PUBKEY_PEM
+from flashproxy.keys import PIN_GOOGLE_CERT, PIN_GOOGLE_PUBKEY_SHA1, DEFAULT_FACILITATOR_PUBKEY_PEM, check_certificate_pin, temp_cert
 from flashproxy.util import parse_addr_spec, format_addr
-from hashlib import sha1
 
 try:
     from M2Crypto import BIO, RSA, SSL, X509
@@ -185,11 +184,7 @@ try:
     ctx = SSL.Context("tlsv1")
     ctx.set_verify(SSL.verify_peer, 3)
 
-    ca_certs_fd, ca_certs_path = tempfile.mkstemp(prefix="flashproxy-reg-email-",
-        dir=get_state_dir(), suffix=".crt")
-    try:
-        os.write(ca_certs_fd, PIN_GOOGLE_CERT)
-        os.close(ca_certs_fd)
+    with temp_cert(PIN_GOOGLE_CERT) as ca_file:
         # We roll our own initial EHLO/STARTTLS because smtplib.SMTP.starttls
         # doesn't allow enough certificate validation.
         code, msg = smtp.docmd("EHLO", EHLO_FQDN)
@@ -198,10 +193,8 @@ try:
         code, msg = smtp.docmd("STARTTLS")
         if code != 220:
             raise ValueError("Got code %d after STARTTLS" % code)
-        ret = ctx.load_verify_locations(ca_certs_path)
+        ret = ctx.load_verify_locations(ca_file.name)
         assert ret == 1
-    finally:
-        os.unlink(ca_certs_path)
 
     smtp.sock = SSL.Connection(ctx, smtp.sock)
     smtp.sock.setup_ssl()
@@ -210,23 +203,7 @@ try:
     smtp.file = smtp.sock.makefile()
 
     if options.use_certificate_pin:
-        found = []
-        for cert in smtp.sock.get_peer_cert_chain():
-            pubkey_der = cert.get_pubkey().as_der()
-            pubkey_digest = sha1(pubkey_der).digest()
-            if pubkey_digest in PIN_GOOGLE_PUBKEY_SHA1:
-                break
-            found.append(pubkey_digest)
-        else:
-            found = "(" + ", ".join(x.encode("hex") for x in found) + ")"
-            expected = "(" + ", ".join(x.encode("hex") for x in PIN_GOOGLE_PUBKEY_SHA1) + ")"
-            raise ValueError("Public key does not match pin: got %s but expected any of %s" % (found, expected))
-
-    if options.use_certificate_pin and pubkey_digest not in PIN_GOOGLE_PUBKEY_SHA1:
-        expected = "(" + ", ".join(x.encode("hex") for x in PIN_GOOGLE_PUBKEY_SHA1) + ")"
-        raise ValueError("Public key does not match pin: got %s but expected any of %s" %
-            (pubkey_digest.encode("hex"), expected))
-
+        check_certificate_pin(smtp.sock, PIN_GOOGLE_PUBKEY_SHA1)
     smtp.ehlo(EHLO_FQDN)
 
     if not options.remote_addr[0]:
diff --git a/flashproxy/keys.py b/flashproxy/keys.py
index 71525c8..5b4b9fa 100644
--- a/flashproxy/keys.py
+++ b/flashproxy/keys.py
@@ -1,3 +1,7 @@
+import tempfile
+
+from hashlib import sha1
+
 # We trust no other CA certificate than this.
 #
 # To find the certificate to copy here,
@@ -52,3 +56,30 @@ M5SDDYYY4xxEPzokjFJfCQv+kcyAnzERNMQ9kR41ePTXG62bpngK5iWGeJ5XdkxG
 gwIDAQAB
 -----END PUBLIC KEY-----
 """
+
+def check_certificate_pin(sock, cert_pubkey):
+    found = []
+    for cert in sock.get_peer_cert_chain():
+        pubkey_der = cert.get_pubkey().as_der()
+        pubkey_digest = sha1(pubkey_der).digest()
+        if pubkey_digest in cert_pubkey:
+            break
+        found.append(pubkey_digest)
+    else:
+        found = "(" + ", ".join(x.encode("hex") for x in found) + ")"
+        expected = "(" + ", ".join(x.encode("hex") for x in cert_pubkey) + ")"
+        raise ValueError("Public key does not match pin: got %s but expected any of %s" % (found, expected))
+
+class temp_cert(object):
+    """Implements a with-statement over raw certificate data."""
+
+    def __init__(self, certdata):
+        self.fd = tempfile.NamedTemporaryFile(prefix="fp-cert-temp-", suffix=".crt", delete=True)
+        self.fd.write(certdata)
+        self.fd.flush()
+
+    def __enter__(self):
+        return self.fd
+
+    def __exit__(self, type, value, traceback):
+        self.fd.close()
diff --git a/flashproxy/test/__init__.py b/flashproxy/test/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/flashproxy/test/test_keys.py b/flashproxy/test/test_keys.py
new file mode 100644
index 0000000..de22279
--- /dev/null
+++ b/flashproxy/test/test_keys.py
@@ -0,0 +1,22 @@
+import os.path
+import unittest
+
+from flashproxy.keys import PIN_GOOGLE_CERT, PIN_GOOGLE_PUBKEY_SHA1, DEFAULT_FACILITATOR_PUBKEY_PEM, check_certificate_pin, temp_cert
+
+class TempCertTest(unittest.TestCase):
+    def test_temp_cert_success(self):
+        fn = None
+        with temp_cert(PIN_GOOGLE_CERT) as ca_file:
+            fn = ca_file.name
+            self.assertTrue(os.path.exists(fn))
+        self.assertFalse(os.path.exists(fn))
+
+    def test_temp_cert_raise(self):
+        fn = None
+        try:
+            with temp_cert(PIN_GOOGLE_CERT) as ca_file:
+                fn = ca_file.name
+                raise ValueError()
+            self.fail()
+        except ValueError:
+            self.assertFalse(os.path.exists(fn))
diff --git a/setup-common.py b/setup-common.py
index 5d98fd9..97d2099 100755
--- a/setup-common.py
+++ b/setup-common.py
@@ -32,7 +32,8 @@ setup(
     license = "BSD",
     keywords = ['tor', 'flashproxy'],
 
-    packages = find_packages(),
+    packages = find_packages(exclude=['*.test']),
+    test_suite='flashproxy.test',
 
     version = "1.4",
 



More information about the tor-commits mailing list