commit f0b9885eabb832e6d0ba688c7723266e9f6b0117 Author: Isis Lovecruft isis@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(