[tor-commits] [bridgedb/master] Simplify Captcha and CaptchaProtectedResource keypair attributes.

isis at torproject.org isis at torproject.org
Fri May 16 18:52:53 UTC 2014


commit f0b9885eabb832e6d0ba688c7723266e9f6b0117
Author: Isis Lovecruft <isis at torproject.org>
Date:   Wed May 14 21:24:21 2014 +0000

    Simplify Captcha and CaptchaProtectedResource keypair attributes.
    
    All *Captcha and *CaptchaProtectedResource classes and subclasses (as
    well as their interface specifications) now have `publicKey` and
    `secretKey` attributes.
    
    For the GimpCaptcha class, this involved switching the ordering of the
    parameters for the initialisation method from:
    
      GimpCaptcha(secretKey, publicKey, ...)
    
    to:
    
      GimpCaptcha(publicKey, secretKey, ...)
    
    Make sure that the intialisations of all of these classes take the
    public key *first*. YOU HAVE BEEN WARNED.
---
 lib/bridgedb/HTTPServer.py           |  103 ++++++++++++++--------------
 lib/bridgedb/captcha.py              |  125 ++++++++++++++++++++++++----------
 lib/bridgedb/test/test_HTTPServer.py |    8 +--
 lib/bridgedb/test/test_captcha.py    |   54 +++++++--------
 4 files changed, 172 insertions(+), 118 deletions(-)

diff --git a/lib/bridgedb/HTTPServer.py b/lib/bridgedb/HTTPServer.py
index 7598755..ead6d60 100644
--- a/lib/bridgedb/HTTPServer.py
+++ b/lib/bridgedb/HTTPServer.py
@@ -20,6 +20,8 @@ import textwrap
 import time
 import os
 
+from functools import partial
+
 from ipaddr import IPv4Address
 from ipaddr import IPv6Address
 
@@ -138,8 +140,11 @@ class CaptchaProtectedResource(resource.Resource):
 
     isLeaf = True
 
-    def __init__(self, useForwardedHeader=False, protectedResource=None):
+    def __init__(self, publicKey=None, secretKey=None,
+                 useForwardedHeader=False, protectedResource=None):
         resource.Resource.__init__(self)
+        self.publicKey = publicKey
+        self.secretKey = secretKey
         self.useForwardedHeader = useForwardedHeader
         self.resource = protectedResource
 
@@ -270,9 +275,7 @@ class GimpCaptchaProtectedResource(CaptchaProtectedResource):
     .. _gimp-captcha: https://github.com/isislovecruft/gimp-captcha
     """
 
-    def __init__(self, secretKey=None, publicKey=None, hmacKey=None,
-                 captchaDir='', useForwardedHeader=False,
-                 protectedResource=None):
+    def __init__(self, hmacKey=None, captchaDir='', **kwargs):
         """Protect a resource via this one, using a local CAPTCHA cache.
 
         :param str secretkey: A PKCS#1 OAEP-padded, private RSA key, used for
@@ -294,10 +297,7 @@ class GimpCaptchaProtectedResource(CaptchaProtectedResource):
         :param protectedResource: The resource to serve if the client
             successfully passes the CAPTCHA challenge.
         """
-        CaptchaProtectedResource.__init__(self, useForwardedHeader,
-                                          protectedResource)
-        self.secretKey = secretKey
-        self.publicKey = publicKey
+        CaptchaProtectedResource.__init__(self, **kwargs)
         self.hmacKey = hmacKey
         self.captchaDir = captchaDir
 
@@ -340,7 +340,7 @@ class GimpCaptchaProtectedResource(CaptchaProtectedResource):
         # Create a new HMAC key, specific to requests from this client:
         clientIP = self.getClientIP(request)
         clientHMACKey = crypto.getHMAC(self.hmacKey, clientIP)
-        capt = captcha.GimpCaptcha(self.secretKey, self.publicKey,
+        capt = captcha.GimpCaptcha(self.publicKey, self.secretKey,
                                    clientHMACKey, self.captchaDir)
         try:
             capt.get()
@@ -391,13 +391,9 @@ class ReCaptchaProtectedResource(CaptchaProtectedResource):
     .. _reCaptcha: http://www.google.com/recaptcha
     """
 
-    def __init__(self, recaptchaPrivKey='', recaptchaPubKey='', remoteip='',
-                 useForwardedHeader=False, protectedResource=None):
-        CaptchaProtectedResource.__init__(self, useForwardedHeader,
-                                          protectedResource)
-        self.recaptchaPrivKey = recaptchaPrivKey
-        self.recaptchaPubKey = recaptchaPubKey
-        self.recaptchaRemoteIP = remoteip
+    def __init__(self, remoteIP=None, **kwargs):
+        CaptchaProtectedResource.__init__(self, **kwargs)
+        self.remoteIP = remoteIP
 
     def _renderDeferred(self, checkedRequest):
         """Render this resource asynchronously.
@@ -442,7 +438,7 @@ class ReCaptchaProtectedResource(CaptchaProtectedResource):
             - ``image`` is a string holding a binary, JPEG-encoded image.
             - ``challenge`` is a unique string associated with the request.
         """
-        capt = captcha.ReCaptcha(self.recaptchaPubKey, self.recaptchaPrivKey)
+        capt = captcha.ReCaptcha(self.publicKey, self.secretKey)
 
         try:
             capt.get()
@@ -464,8 +460,8 @@ class ReCaptchaProtectedResource(CaptchaProtectedResource):
         :rtype: str
         :returns: A fake IP address to report to the reCaptcha API server.
         """
-        if self.recaptchaRemoteIP:
-            remoteIP = self.recaptchaRemoteIP
+        if self.remoteIP:
+            remoteIP = self.remoteIP
         else:
             # generate a random IP for the captcha submission
             remoteIP = IPv4Address(random.randint(0, 2**32-1)).compressed
@@ -520,7 +516,7 @@ class ReCaptchaProtectedResource(CaptchaProtectedResource):
                              % (clientIP, solution.error_code))
                 return (False, request)
 
-        d = txrecaptcha.submit(challenge, response, self.recaptchaPrivKey,
+        d = txrecaptcha.submit(challenge, response, self.secretKey,
                                remoteIP).addCallback(checkResponse, request)
         return d
 
@@ -862,6 +858,13 @@ def addWebServer(cfg, dist, sched):
     :rtype: :api:`twisted.web.server.Site`
     :returns: A webserver.
     """
+    captcha = None
+    fwdHeaders = cfg.HTTP_USE_IP_FROM_FORWARDED_HEADER
+    numBridges = cfg.HTTPS_N_BRIDGES_PER_ANSWER
+    fprInclude = cfg.HTTPS_INCLUDE_FINGERPRINTS
+
+    logging.info("Starting web servers...")
+
     httpdist = resource.Resource()
     httpdist.putChild('', WebRoot())
     httpdist.putChild('robots.txt',
@@ -873,58 +876,56 @@ def addWebServer(cfg, dist, sched):
     httpdist.putChild('options', WebResourceOptions())
     httpdist.putChild('howto', WebResourceHowto())
 
-    bridgesResource = WebResourceBridges(
-        dist, sched, cfg.HTTPS_N_BRIDGES_PER_ANSWER,
-        cfg.HTTP_USE_IP_FROM_FORWARDED_HEADER,
-        includeFingerprints=cfg.HTTPS_INCLUDE_FINGERPRINTS)
-
     if cfg.RECAPTCHA_ENABLED:
-        protected = ReCaptchaProtectedResource(
-                recaptchaPrivKey=cfg.RECAPTCHA_SEC_KEY,
-                recaptchaPubKey=cfg.RECAPTCHA_PUB_KEY,
-                remoteip=cfg.RECAPTCHA_REMOTEIP,
-                useForwardedHeader=cfg.HTTP_USE_IP_FROM_FORWARDED_HEADER,
-                protectedResource=bridgesResource)
-        httpdist.putChild('bridges', protected)
-
+        publicKey = cfg.RECAPTCHA_PUB_KEY
+        secretKey = cfg.RECAPTCHA_SEC_KEY
+        captcha = partial(ReCaptchaProtectedResource,
+                          remoteIP=cfg.RECAPTCHA_REMOTEIP)
     elif cfg.GIMP_CAPTCHA_ENABLED:
-        # Get the HMAC secret key for CAPTCHA challenges and create a new key
-        # from it for use on the server:
+        # Get the master HMAC secret key for CAPTCHA challenges, and then
+        # create a new HMAC key from it for use on the server.
         captchaKey = crypto.getKey(cfg.GIMP_CAPTCHA_HMAC_KEYFILE)
         hmacKey = crypto.getHMAC(captchaKey, "Captcha-Key")
-
         # Load or create our encryption keys:
         secretKey, publicKey = crypto.getRSAKey(cfg.GIMP_CAPTCHA_RSA_KEYFILE)
-
-        protected = GimpCaptchaProtectedResource(
-            secretKey=secretKey,
-            publicKey=publicKey,
-            hmacKey=hmacKey,
-            captchaDir=cfg.GIMP_CAPTCHA_DIR,
-            useForwardedHeader=cfg.HTTP_USE_IP_FROM_FORWARDED_HEADER,
-            protectedResource=bridgesResource)
+        captcha = partial(GimpCaptchaProtectedResource,
+                          hmacKey=hmacKey,
+                          captchaDir=cfg.GIMP_CAPTCHA_DIR)
+
+    bridges = WebResourceBridges(dist, sched, numBridges,
+                                 fwdHeaders, includeFingerprints=fprInclude)
+    if captcha:
+        # Protect the 'bridges' page with a CAPTCHA, if configured to do so:
+        protected = captcha(publicKey=publicKey,
+                            secretKey=secretKey,
+                            useForwardedHeader=fwdHeaders,
+                            protectedResource=bridges)
         httpdist.putChild('bridges', protected)
+        logging.info("Protecting resources with %s." % captcha.func.__name__)
     else:
-        httpdist.putChild('bridges', bridgesResource)
+        httpdist.putChild('bridges', bridges)
 
     site = server.Site(httpdist)
 
     if cfg.HTTP_UNENCRYPTED_PORT:
         ip = cfg.HTTP_UNENCRYPTED_BIND_IP or ""
+        port = cfg.HTTP_UNENCRYPTED_PORT or 80
         try:
-            reactor.listenTCP(cfg.HTTP_UNENCRYPTED_PORT, site, interface=ip)
+            reactor.listenTCP(port, site, interface=ip)
         except CannotListenError as error:
             raise SystemExit(error)
+        logging.info("Started HTTP server on %s:%d" % (str(ip), int(port)))
 
     if cfg.HTTPS_PORT:
-        from twisted.internet.ssl import DefaultOpenSSLContextFactory
-        #from OpenSSL.SSL import SSLv3_METHOD
         ip = cfg.HTTPS_BIND_IP or ""
-        factory = DefaultOpenSSLContextFactory(cfg.HTTPS_KEY_FILE,
-                                               cfg.HTTPS_CERT_FILE)
+        port = cfg.HTTPS_PORT or 443
         try:
-            reactor.listenSSL(cfg.HTTPS_PORT, site, factory, interface=ip)
+            from twisted.internet.ssl import DefaultOpenSSLContextFactory
+            factory = DefaultOpenSSLContextFactory(cfg.HTTPS_KEY_FILE,
+                                                   cfg.HTTPS_CERT_FILE)
+            reactor.listenSSL(port, site, factory, interface=ip)
         except CannotListenError as error:
             raise SystemExit(error)
+        logging.info("Started HTTPS server on %s:%d" % (str(ip), int(port)))
 
     return site
diff --git a/lib/bridgedb/captcha.py b/lib/bridgedb/captcha.py
index ca2575c..e10bc81 100644
--- a/lib/bridgedb/captcha.py
+++ b/lib/bridgedb/captcha.py
@@ -124,22 +124,28 @@ class Captcha(object):
 
 
 class ReCaptcha(Captcha):
-    """A reCaptcha CAPTCHA.
+    """A CAPTCHA obtained from a remote reCaptcha_ API server.
 
     :ivar str image: The CAPTCHA image.
     :ivar str challenge: The ``'recaptcha_challenge_response'`` HTTP form
-        field to pass to the client along with the CAPTCHA image.
+        field to pass to the client, along with the CAPTCHA image. See
+        :doc:`BridgeDB's captcha.html <templates/captcha.html>` Mako_ template
+        for an example usage.
+    :ivar str publicKey: The public reCaptcha API key.
+    :ivar str secretKey: The private reCaptcha API key.
+
+    .. _reCaptcha: https://code.google.com/p/recaptcha/
+    .. _Mako: http://docs.makotemplates.org/en/latest/syntax.html#page
     """
 
-    def __init__(self, pubkey=None, privkey=None):
+    def __init__(self, publicKey=None, secretKey=None):
         """Create a new ReCaptcha CAPTCHA.
 
-        :param str pubkey: The public reCaptcha API key.
-        :param str privkey: The private reCaptcha API key.
+        :param str publicKey: The public reCaptcha API key.
+        :param str secretKey: The private reCaptcha API key.
         """
-        super(ReCaptcha, self).__init__()
-        self.pubkey = pubkey
-        self.privkey = privkey
+        super(ReCaptcha, self).__init__(publicKey=publicKey,
+                                        secretKey=secretKey)
 
     def get(self):
         """Retrieve a CAPTCHA from the reCaptcha API server.
@@ -154,11 +160,11 @@ class ReCaptcha(Captcha):
             :ivar:`secretKey` are missing.
         :raises HTTPError: If the server returned any HTTP error status code.
         """
-        if not self.pubkey or not self.privkey:
+        if not self.publicKey or not self.secretKey:
             raise CaptchaKeyError('You must supply recaptcha API keys')
 
         urlbase = API_SSL_SERVER
-        form = "/noscript?k=%s" % self.pubkey
+        form = "/noscript?k=%s" % self.publicKey
 
         # Extract and store image from recaptcha
         html = urllib2.urlopen(urlbase + form).read()
@@ -172,27 +178,45 @@ class ReCaptcha(Captcha):
 
 
 class GimpCaptcha(Captcha):
-    """A cached CAPTCHA image which was created with Gimp."""
+    """A locally cached CAPTCHA image which was created with gimp-captcha_.
+
+    :ivar str secretKey: A PKCS#1 OAEP-padded, private RSA key, used for
+        verifying the client's solution to the CAPTCHA.
+    :ivar str publickey: A PKCS#1 OAEP-padded, public RSA key. This is used to
+        hide the correct CAPTCHA solution within the
+        ``captcha_challenge_field`` HTML form field. That form field is given
+        to the a client along with the :ivar:`image` during the initial
+        CAPTCHA request, and the client *should* give it back to us later
+        during the CAPTCHA solution verification step.
+    :ivar bytes hmacKey: A client-specific HMAC secret key.
+    :ivar str cacheDir: The local directory which pre-generated CAPTCHA images
+        have been stored in. This can be set via the ``GIMP_CAPTCHA_DIR``
+        setting in the config file.
+    :type sched: :class:`bridgedb.schedule.ScheduledInterval`
+    :ivar sched: An time interval. After this much time has passed, the
+        CAPTCHA is considered stale, and all solutions are considered invalid
+        regardless of their correctness.
+
+    .. _gimp-captcha: https://github.com/isislovecruft/gimp-captcha
+    """
 
-    def __init__(self, secretKey=None, publicKey=None, hmacKey=None,
+    def __init__(self, publicKey=None, secretKey=None, hmacKey=None,
                  cacheDir=None):
         """Create a ``GimpCaptcha`` which retrieves images from **cacheDir**.
 
-        :param str secretkey: A PKCS#1 OAEP-padded, private RSA key, used for
-            verifying the client's solution to the CAPTCHA.
         :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 str secretKey: A PKCS#1 OAEP-padded, private RSA key, used for
+            verifying the client's solution to the CAPTCHA.
         :param bytes hmacKey: A client-specific HMAC secret key.
         :param str cacheDir: The local directory which pre-generated CAPTCHA
             images have been stored in. This can be set via the
             ``GIMP_CAPTCHA_DIR`` setting in the config file.
-        :raises GimpCaptchaError: if **cacheDir** is not a directory.
-        :raises CaptchaKeyError: if any of **secretKey**, **publicKey**,
-            or **hmacKey** is invalid, or missing.
+        :raises GimpCaptchaError: if :ivar:`cacheDir` is not a directory.
+        :raises CaptchaKeyError: if any of :ivar:`secretKey`,
+            :ivar:`publicKey`, or :ivar:`hmacKey` are invalid or missing.
         """
-        super(GimpCaptcha, self).__init__()
-
         if not cacheDir or not os.path.isdir(cacheDir):
             raise GimpCaptchaError("Gimp captcha cache isn't a directory: %r"
                                    % cacheDir)
@@ -201,10 +225,10 @@ class GimpCaptcha(Captcha):
                 "Invalid key supplied to GimpCaptcha: SK=%r PK=%r HMAC=%r"
                 % (secretKey, publicKey, hmacKey))
 
-        self.secretKey = secretKey
-        self.publicKey = publicKey
-        self.cacheDir = cacheDir
+        super(GimpCaptcha, self).__init__(publicKey=publicKey,
+                                          secretKey=secretKey)
         self.hmacKey = hmacKey
+        self.cacheDir = cacheDir
         self.answer = None
 
     @classmethod
@@ -215,7 +239,7 @@ class GimpCaptcha(Captcha):
             ``'captcha_challenge_field'`` HTTP form field.
         :param str solution: The client's proposed solution to the CAPTCHA
             that they were presented with.
-        :param str secretkey: A PKCS#1 OAEP-padded, private RSA key, used for
+        :param str secretKey: A PKCS#1 OAEP-padded, private RSA key, used for
             verifying the client's solution to the CAPTCHA.
         :param bytes hmacKey: A private key for generating HMACs.
         :rtype: bool
@@ -248,22 +272,51 @@ class GimpCaptcha(Captcha):
             return False
 
     def createChallenge(self, answer):
-        """Encrypt the CAPTCHA **answer** and HMAC the encrypted data.
-
-        Take a string containing the answer to a CAPTCHA and encrypts it to
-        :attr:`publicKey`. The resulting encrypted blob is then HMACed with a
-        client-specific :attr:`hmacKey`. These two strings are then joined
-        together in the form:
-
-                HMAC ";" ENCRYPTED_ANSWER
-
-        where the HMAC MUST be the first 20 bytes. Lastly base64-encoded (in a
-        URL safe manner).
+        """Encrypt-then-HMAC the CAPTCHA **answer**.
+
+        A challenge string consists of a URL-safe, base64-encoded string which
+        contains an ``HMAC`` concatenated with an ``ENC_BLOB``, in the
+        following form::
+
+            CHALLENGE := B64( HMAC | ENC_BLOB )
+            ENC_BLOB := RSA_ENC( ANSWER_BLOB )
+            ANSWER_BLOB := ( TIMESTAMP | ANSWER )
+
+        where
+          * ``B64`` is a URL-safe base64-encode function,
+          * ``RSA_ENC`` is the PKCS#1 RSA-OAEP encryption function,
+          * and the remaining feilds are specified as follows:
+
+        +-------------+--------------------------------------------+----------+
+        | Field       | Description                                | Length   |
+        +=============+============================================+==========+
+        | HMAC        | An HMAC of the ``ENC_BLOB``, created with  | 20 bytes |
+        |             | the client-specific :ivar:`hmacKey`, by    |          |
+        |             | applying :func:`~crypto.getHMAC` to the    |          |
+        |             | ``ENC_BLOB``.                              |          |
+        +-------------+--------------------------------------------+----------+
+        | ENC_BLOB    | An encrypted ``ANSWER``, created with      | varies   |
+        |             | a PKCS#1 OAEP-padded RSA :ivar:`publicKey`.|          |
+        +-------------+--------------------------------------------+----------+
+        | ANSWER      | A string containing answer to this         | 8 bytes  |
+        |             | CAPTCHA :ivar:`image`.                     |          |
+        +-------------+--------------------------------------------+----------+
+
+        The steps taken to produce a ``CHALLENGE`` are then:
+
+          1. Encrypt the ``ANSWER`` to :ivar:`publicKey` to create
+             the ``ENC_BLOB``.
+
+          2. Use the client-specific :ivar:`hmacKey` to apply the
+             :func:`~crypto.getHMAC` function to the ``ENC_BLOB``, obtaining
+             an ``HMAC``.
+
+          3. Create the final ``CHALLENGE`` string by concatenating the
+             ``HMAC`` and ``ENC_BLOB``, then base64-encoding the result.
 
         :param str answer: The answer to a CAPTCHA.
         :rtype: str
-        :returns: An HMAC of, as well as a string containing the URL-safe,
-            base64-encoded encrypted **answer**.
+        :returns: A challenge string.
         """
         encrypted = self.publicKey.encrypt(answer)
         hmac = crypto.getHMAC(self.hmacKey, encrypted)
diff --git a/lib/bridgedb/test/test_HTTPServer.py b/lib/bridgedb/test/test_HTTPServer.py
index 570fe9c..8bc1da7 100644
--- a/lib/bridgedb/test/test_HTTPServer.py
+++ b/lib/bridgedb/test/test_HTTPServer.py
@@ -274,9 +274,9 @@ class ReCaptchaProtectedResourceTests(unittest.TestCase):
         # (None, None) is the (distributor, scheduleInterval):
         self.protectedResource = HTTPServer.WebResourceBridges(None, None)
         self.captchaResource = HTTPServer.ReCaptchaProtectedResource(
-            recaptchaPrivKey='42',
-            recaptchaPubKey='23',
-            remoteip='111.111.111.111',
+            publicKey='23',
+            secretKey='42',
+            remoteIP='111.111.111.111',
             useForwardedHeader=True,
             protectedResource=self.protectedResource)
 
@@ -365,7 +365,7 @@ class ReCaptchaProtectedResourceTests(unittest.TestCase):
 
     def test_getRemoteIP_useRandomIP(self):
         """Check that removing our remoteip setting produces a random IP."""
-        self.captchaResource.recaptchaRemoteIP = None
+        self.captchaResource.remoteIP = None
         ip = self.captchaResource.getRemoteIP()
         realishIP = ipaddr.IPv4Address(ip).compressed
         self.assertTrue(realishIP)
diff --git a/lib/bridgedb/test/test_captcha.py b/lib/bridgedb/test/test_captcha.py
index a383545..536f29e 100644
--- a/lib/bridgedb/test/test_captcha.py
+++ b/lib/bridgedb/test/test_captcha.py
@@ -50,8 +50,8 @@ class ReCaptchaTests(unittest.TestCase):
 
     def test_init(self):
         """Check the ReCaptcha class stored the private and public keys."""
-        self.assertEquals(self.c.privkey, 'sekrit')
-        self.assertEquals(self.c.pubkey, 'publik')
+        self.assertEquals(self.c.secretKey, 'sekrit')
+        self.assertEquals(self.c.publicKey, 'publik')
 
     def test_get(self):
         """Test get() method."""
@@ -109,32 +109,32 @@ class GimpCaptchaTests(unittest.TestCase):
         a CaptchaKeyError.
         """
         self.assertRaises(captcha.CaptchaKeyError, captcha.GimpCaptcha,
-                          None, self.publik, self.hmacKey, self.cacheDir)
+                          self.publik, None, self.hmacKey, self.cacheDir)
 
     def test_init_noPublicKey(self):
         """__init__() without publicKey should raise a CaptchaKeyError."""
         self.assertRaises(captcha.CaptchaKeyError, captcha.GimpCaptcha,
-                          self.sekrit, None, self.hmacKey, self.cacheDir)
+                          None, self.sekrit, self.hmacKey, self.cacheDir)
 
     def test_init_noHMACKey(self):
         """__init__() without hmacKey should raise a CaptchaKeyError."""
         self.assertRaises(captcha.CaptchaKeyError, captcha.GimpCaptcha,
-                          self.sekrit, self.publik, None, self.cacheDir)
+                          self.publik, self.sekrit, None, self.cacheDir)
 
     def test_init_noCacheDir(self):
         """__init__() without cacheDir should raise a CaptchaKeyError."""
         self.assertRaises(captcha.GimpCaptchaError, captcha.GimpCaptcha,
-                          self.sekrit, self.publik, self.hmacKey, None)
+                          self.publik, self.sekrit, self.hmacKey, None)
 
     def test_init_badCacheDir(self):
         """GimpCaptcha with bad cacheDir should raise GimpCaptchaError."""
         self.assertRaises(captcha.GimpCaptchaError, captcha.GimpCaptcha,
-                          self.sekrit, self.publik, self.hmacKey,
+                          self.publik, self.sekrit, self.hmacKey,
                           self.cacheDir.rstrip('chas'))
 
     def test_init(self):
         """Test that __init__ correctly initialised all the values."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         self.assertIsNone(c.answer)
         self.assertIsNone(c.image)
@@ -142,14 +142,14 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_createChallenge(self):
         """createChallenge() should return the encrypted CAPTCHA answer."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         challenge = c.createChallenge('w00t')
         self.assertIsInstance(challenge, basestring)
 
     def test_createChallenge_base64(self):
         """createChallenge() return value should be urlsafe base64-encoded."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         challenge = c.createChallenge('w00t')
         decoded = urlsafe_b64decode(challenge)
@@ -157,7 +157,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_createChallenge_hmacValid(self):
         """The HMAC in createChallenge() return value should be valid."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         challenge = c.createChallenge('ShouldHaveAValidHMAC')
         decoded = urlsafe_b64decode(challenge)
@@ -168,7 +168,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_createChallenge_decryptedAnswerMatches(self):
         """The HMAC in createChallenge() return value should be valid."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         answer = 'ThisAnswerShouldDecryptToThis'
         challenge = c.createChallenge(answer)
@@ -182,7 +182,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_get(self):
         """GimpCaptcha.get() should return image and challenge strings."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         self.assertIsInstance(image, basestring)
@@ -191,7 +191,7 @@ class GimpCaptchaTests(unittest.TestCase):
     def test_get_emptyCacheDir(self):
         """An empty cacheDir should raise GimpCaptchaError."""
         os.makedirs(self.badCacheDir)
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.badCacheDir)
         self.assertRaises(captcha.GimpCaptchaError, c.get)
         shutil.rmtree(self.badCacheDir)
@@ -205,7 +205,7 @@ class GimpCaptchaTests(unittest.TestCase):
             fh.flush()
         os.chmod(badFile, 0266)
 
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.badCacheDir)
         # This should hit the second `except:` clause in get():
         self.assertRaises(captcha.GimpCaptchaError, c.get)
@@ -213,7 +213,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check(self):
         """A correct answer and valid challenge should return True."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         self.assertEquals(
@@ -222,7 +222,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_blankAnswer(self):
         """A blank answer and valid challenge should return False."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         self.assertEquals(
@@ -231,7 +231,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_nonBase64(self):
         """Valid answer and challenge with invalid base64 returns False."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         challengeBadB64 = challenge.rstrip('==') + "\x42\x42\x42"
@@ -244,7 +244,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_caseInsensitive_lowercase(self):
         """A correct answer in lowercase characters should return True."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         solution = c.answer.lower()
@@ -254,7 +254,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_caseInsensitive_uppercase(self):
         """A correct answer in uppercase characters should return True."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         solution = c.answer.upper()
@@ -264,7 +264,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_encoding_utf8(self):
         """A correct answer in utf-8 lowercase should return True."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         solution = c.answer.encode('utf8')
@@ -274,7 +274,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_encoding_ascii(self):
         """A correct answer in utf-8 lowercase should return True."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         solution = c.answer.encode('ascii')
@@ -284,7 +284,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_encoding_unicode(self):
         """A correct answer in utf-8 lowercase should return True."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         solution = unicode(c.answer)
@@ -294,7 +294,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_missingHMACbytes(self):
         """A challenge that is missing part of the HMAC should return False."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         challengeBadHMAC = challenge[:10] + challenge[20:]
@@ -304,7 +304,7 @@ class GimpCaptchaTests(unittest.TestCase):
 
     def test_check_missingAnswerbytes(self):
         """Partial encrypted answers in challenges should return False."""
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         challengeBadOrig = challenge[:20] + challenge[30:]
@@ -315,7 +315,7 @@ class GimpCaptchaTests(unittest.TestCase):
     def test_check_badHMACkey(self):
         """A challenge with a bad HMAC key should return False."""
         hmacKeyBad = crypto.getKey('test_gimpCaptcha_badHMACkey')
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         self.assertEquals(
@@ -325,7 +325,7 @@ class GimpCaptchaTests(unittest.TestCase):
     def test_check_badRSAkey(self):
         """A challenge with a bad RSA secret key should return False."""
         secretKeyBad, publicKeyBad = crypto.getRSAKey('test_gimpCaptcha_badRSAkey')
-        c = captcha.GimpCaptcha(self.sekrit, self.publik, self.hmacKey,
+        c = captcha.GimpCaptcha(self.publik, self.sekrit, self.hmacKey,
                                 self.cacheDir)
         image, challenge = c.get()
         self.assertEquals(





More information about the tor-commits mailing list