[tor-commits] [bridgedb/develop] Rewrite GimpCaptchaProtectedResource to use RSA & HMAC keys for verification.

isis at torproject.org isis at torproject.org
Sun Mar 16 16:38:45 UTC 2014


commit c9496cd45c295e292b5966ad022e2c305f2dbe37
Author: Isis Lovecruft <isis at torproject.org>
Date:   Wed Mar 12 00:03:18 2014 +0000

    Rewrite GimpCaptchaProtectedResource to use RSA & HMAC keys for verification.
---
 lib/bridgedb/HTTPServer.py |   56 ++++++++++++++++++++++++++++++++++----------
 1 file changed, 43 insertions(+), 13 deletions(-)

diff --git a/lib/bridgedb/HTTPServer.py b/lib/bridgedb/HTTPServer.py
index 4deac25..ff9c7f3 100644
--- a/lib/bridgedb/HTTPServer.py
+++ b/lib/bridgedb/HTTPServer.py
@@ -115,9 +115,10 @@ def replaceErrorPage(error, template_name=None):
 class CaptchaProtectedResource(twisted.web.resource.Resource):
     """A general resource protected by some form of CAPTCHA."""
 
+    isLeaf = True
+
     def __init__(self, useForwardedHeader=False, resource=None):
         twisted.web.resource.Resource.__init__(self)
-        self.isLeaf = resource.isLeaf
         self.useForwardedHeader = useForwardedHeader
         self.resource = resource
 
@@ -234,9 +235,33 @@ class GimpCaptchaProtectedResource(CaptchaProtectedResource):
     .. _gimp-captcha: https://github.com/isislovecruft/gimp-captcha
     """
 
-    def __init__(self, captchaDir='',
-                 useForwardedHeader=False, resource=None):
+    def __init__(self, secretKey=None, publicKey=None, hmacKey=None,
+                 captchaDir='', useForwardedHeader=False, resource=None):
+        """Protect a **resource** via this one, using a local CAPTCHA cache.
+
+        :param str secretkey: A PKCS#1 OAEP-padded, private RSA key, used for
+            verifying the client's solution to the CAPTCHA. See
+            :func:`bridgedb.crypto.getRSAKey` and the
+            ``GIMP_CAPTCHA_RSA_KEYFILE`` config setting.
+        :param str publickey: A PKCS#1 OAEP-padded, public RSA key, used for
+            creating the ``captcha_challenge_field`` string to give to a
+            client.
+        :param bytes hmacKey: The master HMAC key, used for validating CAPTCHA
+            challenge strings in :meth:`captcha.GimpCaptcha.check`. The file
+            where this key is stored can be set via the
+            ``GIMP_CAPTCHA_HMAC_KEYFILE`` option in the config file.
+        :param str captchaDir: The directory where the cached CAPTCHA images
+            are stored. See the ``GIMP_CAPTCHA_DIR`` config setting.
+        :param bool useForwardedHeader: If ``True``, obtain the client's IP
+            address from the ``X-Forwarded-For`` HTTP header.
+        :type resource: :api:`twisted.web.resource.Resource`
+        :param resource: The resource to serve if the client successfully
+            passes the CAPTCHA challenge.
+        """
         CaptchaProtectedResource.__init__(self, useForwardedHeader, resource)
+        self.secretKey = secretKey
+        self.publicKey = publicKey
+        self.hmacKey = hmacKey
         self.captchaDir = captchaDir
 
     def checkSolution(self, request):
@@ -255,12 +280,15 @@ class GimpCaptchaProtectedResource(CaptchaProtectedResource):
         :rtupe: bool
         :returns: True, if the CAPTCHA solution was valid; False otherwise.
         """
-        challenge, response = self.extractClientSolution(request)
+        challenge, solution = self.extractClientSolution(request)
         clientIP = self.getClientIP(request)
-        solution = captcha.GimpCaptcha.check(challenge, response, clientIP)
-        logging.debug("Captcha from %r. Parameters: %r"
-                      % (Util.logSafely(clientIP), request.args))
-        return solution
+        clientHMACKey = crypto.getHMAC(self.hmacKey, clientIP)
+        valid = captcha.GimpCaptcha.check(challenge, solution,
+                                          self.secretKey, clientHMACKey)
+        logging.debug("%sorrect captcha from %r: %r." % (
+            "C" if valid else "Inc", Util.logSafely(clientIP), solution))
+
+        return valid
 
     def getCaptchaImage(self, request):
         """Get a random CAPTCHA image from our **captchaDir**.
@@ -276,18 +304,20 @@ class GimpCaptchaProtectedResource(CaptchaProtectedResource):
             - ``image`` is a string holding a binary, JPEG-encoded image.
             - ``challenge`` is a unique string associated with the request.
         """
+        # Create a new HMAC key, specific to requests from this client:
         clientIP = self.getClientIP(request)
-        c = captcha.GimpCaptcha(self.captchaDir, clientIP)
-
+        clientHMACKey = crypto.getHMAC(self.hmacKey, clientIP)
+        capt = captcha.GimpCaptcha(self.secretKey, self.publicKey,
+                                   clientHMACKey, self.captchaDir)
         try:
-            c.get()
+            capt.get()
         except captcha.GimpCaptchaError as error:
             logging.error(error)
         except Exception as error:
             logging.error("Unhandled error while retrieving Gimp captcha!")
-            logging.error(error)
+            logging.exception(error)
 
-        return (c.image, c.challenge)
+        return (capt.image, capt.challenge)
 
     def render_GET(self, request):
         """Get a random CAPTCHA from our local cache directory and serve it to





More information about the tor-commits mailing list