diff options
author | Eli Collins <elic@assurancetechnologies.com> | 2012-04-30 23:02:11 -0400 |
---|---|---|
committer | Eli Collins <elic@assurancetechnologies.com> | 2012-04-30 23:02:11 -0400 |
commit | 176153315bbd4ae3ec8542a5fc6704041d7de342 (patch) | |
tree | 8f555a4a9a44f9cfc0ee4706b588d5cc579acf55 | |
parent | 80c90ea391d20188f5f46de30ad32117f8b7258b (diff) | |
download | passlib-176153315bbd4ae3ec8542a5fc6704041d7de342.tar.gz |
removed annoying builtin-bcrypt warning, decreased rounds on some test vectors for speed
-rw-r--r-- | docs/lib/passlib.hash.bcrypt.rst | 9 | ||||
-rw-r--r-- | passlib/handlers/bcrypt.py | 14 | ||||
-rw-r--r-- | passlib/tests/test_apps.py | 2 | ||||
-rw-r--r-- | passlib/tests/test_handlers.py | 107 | ||||
-rw-r--r-- | passlib/tests/test_hosts.py | 2 |
5 files changed, 69 insertions, 65 deletions
diff --git a/docs/lib/passlib.hash.bcrypt.rst b/docs/lib/passlib.hash.bcrypt.rst index cb09e37..70c6341 100644 --- a/docs/lib/passlib.hash.bcrypt.rst +++ b/docs/lib/passlib.hash.bcrypt.rst @@ -50,11 +50,10 @@ Interface (primarily BSD-derived systems). 4. A *slow* pure-python implementation of BCrypt, built into Passlib. - It should be noted that the builtin pure-python implementation is too slow - to be both secure and responsive at the same time (except under PyPy > 1.7) - Because of this, it is disabled by default, unless - the environment variable ``PASSLIB_BUILTIN_BCRYPT="enabled"`` has been set - before Passlib is first loaded. + It should be noted that the pure-python implementation (#4) is too slow + to be useable, given the number of rounds currently required for security. + Because of this, it is disabled by default, unless the environment variable + ``PASSLIB_BUILTIN_BCRYPT="enabled"`` is set before Passlib is first loaded. If the first three backends are not available, and the builtin backend has not been enabled, :meth:`encrypt` and :meth:`verify` diff --git a/passlib/handlers/bcrypt.py b/passlib/handlers/bcrypt.py index 77a99da..5086b36 100644 --- a/passlib/handlers/bcrypt.py +++ b/passlib/handlers/bcrypt.py @@ -26,7 +26,7 @@ try: except ImportError: #pragma: no cover bcryptor_engine = None #libs -from passlib.exc import PasslibHashWarning, PasslibSecurityWarning +from passlib.exc import PasslibHashWarning from passlib.utils import bcrypt64, safe_crypt, repeat_string, \ classproperty, rng, getrandstr, test_crypt from passlib.utils.compat import bytes, b, u, uascii_to_str, unicode, str_to_uascii @@ -189,8 +189,10 @@ class bcrypt(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.HasManyBackends, uh. assert salt is not None, "HasSalt didn't generate new salt!" changed, salt = bcrypt64.check_repair_unused(salt) if changed: + # FIXME: if salt was provided by user, this message won't be + # correct. not sure if we want to throw error, or use different warning. warn( - "encountered a bcrypt hash with incorrectly set padding bits; " + "encountered a bcrypt salt with incorrectly set padding bits; " "you may want to use bcrypt.normhash() " "to fix this; see Passlib 1.5.3 changelog.", PasslibHashWarning) @@ -239,8 +241,7 @@ class bcrypt(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.HasManyBackends, uh. @classmethod def _no_backends_msg(cls): - return "no bcrypt backends available - please install 'py-bcrypt' or " \ - "'bcryptor' for bcrypt support" + return "no bcrypt backends available - please install py-bcrypt" def _calc_checksum_os_crypt(self, secret): config = self._get_config() @@ -300,11 +301,6 @@ class bcrypt(uh.HasManyIdents, uh.HasRounds, uh.HasSalt, uh.HasManyBackends, uh. return str_to_uascii(hash[-31:]) def _calc_checksum_builtin(self, secret): - # XXX: silence this warnings under pypy1.7? it's almost fast enough. - warn("SECURITY WARNING: Passlib is using it's pure-python bcrypt " - "implementation, which is TOO SLOW FOR PRODUCTION USE. It is " - "strongly recommended that you install py-bcrypt or bcryptor for " - "Passlib to use instead.", PasslibSecurityWarning) if isinstance(secret, unicode): secret = secret.encode("utf-8") if _BNULL in secret: diff --git a/passlib/tests/test_apps.py b/passlib/tests/test_apps.py index 1758c38..5632087 100644 --- a/passlib/tests/test_apps.py +++ b/passlib/tests/test_apps.py @@ -90,7 +90,7 @@ class AppsTest(TestCase): ]: self.assertTrue(ctx.verify("test", hash)) - h1 = '$2a$10$Ljj0Kgu7Ddob9xWoqzn0ae.uNfxPRofowWdksk.6jCUHKTGYLD.QG' + h1 = "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS" if hashmod.bcrypt.has_backend(): self.assertTrue(ctx.verify("test", h1)) self.assertEqual(ctx.default_scheme(), "bcrypt") diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py index b66cce1..a90daa3 100644 --- a/passlib/tests/test_handlers.py +++ b/passlib/tests/test_handlers.py @@ -189,25 +189,29 @@ class _bcrypt_test(HandlerCase): #=============================================================== # override some methods #=============================================================== - def setUpWarnings(self): - super(_bcrypt_test, self).setUpWarnings() - warnings.filterwarnings("ignore", - "SECURITY WARNING: .*pure-python bcrypt.*") - - def do_genconfig(self, **kwds): - # override default to speed up tests - kwds.setdefault("rounds", 5) + def setUp(self): + # ensure builtin is enabled for duration of test. + if TEST_MODE("full") and self.backend == "builtin": + key = "PASSLIB_BUILTIN_BCRYPT" + orig = os.environ.get(key) + if orig: + self.addCleanup(os.environ.__setitem__, key, orig) + else: + self.addCleanup(os.environ.__delitem__, key) + os.environ[key] = "enabled" + super(_bcrypt_test, self).setUp() + + def populate_settings(self, kwds): + # builtin is still just way too slow. + if self.backend == "builtin": + kwds.setdefault("rounds", 4) + + super(_bcrypt_test, self).populate_settings(kwds) # correct unused bits in provided salts, to silence some warnings. - if 'salt' in kwds: + if 'salt' in kwds and len(kwds['salt']) == 22: from passlib.utils import bcrypt64 kwds['salt'] = bcrypt64.repair_unused(kwds['salt']) - return self.handler.genconfig(**kwds) - - def do_encrypt(self, secret, **kwds): - # override default to speed up tests - kwds.setdefault("rounds", 5) - return self.handler.encrypt(secret, **kwds) #=============================================================== # fuzz testing @@ -282,23 +286,30 @@ class _bcrypt_test(HandlerCase): # password, bad hash, good hash # 2 bits of salt padding set - ("loppux", - "$2a$12$oaQbBqq8JnSM1NHRPQGXORm4GCUMqp7meTnkft4zgSnrbhoKdDV0C", - "$2a$12$oaQbBqq8JnSM1NHRPQGXOOm4GCUMqp7meTnkft4zgSnrbhoKdDV0C"), +# ("loppux", # \/ +# "$2a$12$oaQbBqq8JnSM1NHRPQGXORm4GCUMqp7meTnkft4zgSnrbhoKdDV0C", +# "$2a$12$oaQbBqq8JnSM1NHRPQGXOOm4GCUMqp7meTnkft4zgSnrbhoKdDV0C"), + ("test", # \/ + '$2a$04$oaQbBqq8JnSM1NHRPQGXORY4Vw3bdHKLIXTecPDRAcJ98cz1ilveO', + '$2a$04$oaQbBqq8JnSM1NHRPQGXOOY4Vw3bdHKLIXTecPDRAcJ98cz1ilveO'), # all 4 bits of salt padding set - ("Passlib11", - "$2a$12$M8mKpW9a2vZ7PYhq/8eJVcUtKxpo6j0zAezu0G/HAMYgMkhPu4fLK", - "$2a$12$M8mKpW9a2vZ7PYhq/8eJVOUtKxpo6j0zAezu0G/HAMYgMkhPu4fLK"), +# ("Passlib11", # \/ +# "$2a$12$M8mKpW9a2vZ7PYhq/8eJVcUtKxpo6j0zAezu0G/HAMYgMkhPu4fLK", +# "$2a$12$M8mKpW9a2vZ7PYhq/8eJVOUtKxpo6j0zAezu0G/HAMYgMkhPu4fLK"), + ("test", # \/ + "$2a$04$yjDgE74RJkeqC0/1NheSScrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS", + "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS"), # bad checksum padding - ("test", + ("test", # \/ "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIV", "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS"), ] def test_90_bcrypt_padding(self): "test passlib correctly handles bcrypt padding bits" + self.require_TEST_MODE("full") # # prevents reccurrence of issue 25 (https://code.google.com/p/passlib/issues/detail?id=25) # were some unused bits were incorrectly set in bcrypt salt strings. @@ -319,42 +330,40 @@ class _bcrypt_test(HandlerCase): for i in irange(3): check_padding(bcrypt.encrypt("bob", rounds=bcrypt.min_rounds)) - # some things that will raise warnings - with catch_warnings(record=True) as wlog: - # - # test genconfig() corrects invalid salts & issues warning. - # + # + # test genconfig() corrects invalid salts & issues warning. + # + with self.assertWarningList(["salt too large", corr_desc]): hash = bcrypt.genconfig(salt="."*21 + "A.", rounds=5, relaxed=True) - self.consumeWarningList(wlog, ["salt too large", corr_desc]) - self.assertEqual(hash, "$2a$05$" + "." * 22) - - # - # make sure genhash() corrects input - # - samples = self.known_incorrect_padding - for pwd, bad, good in samples: + self.assertEqual(hash, "$2a$05$" + "." * 22) + + # + # make sure genhash() corrects input + # + samples = self.known_incorrect_padding + for pwd, bad, good in samples: + with self.assertWarningList([corr_desc]): self.assertEqual(bcrypt.genhash(pwd, bad), good) - self.consumeWarningList(wlog, [corr_desc]) + with self.assertWarningList([]): self.assertEqual(bcrypt.genhash(pwd, good), good) - self.consumeWarningList(wlog) - # - # and that verify() works good & bad - # + # + # and that verify() works good & bad + # + with self.assertWarningList([corr_desc]): self.assertTrue(bcrypt.verify(pwd, bad)) - self.consumeWarningList(wlog, [corr_desc]) + with self.assertWarningList([]): self.assertTrue(bcrypt.verify(pwd, good)) - self.consumeWarningList(wlog) - # - # test normhash cleans things up correctly - # - for pwd, bad, good in samples: + # + # test normhash cleans things up correctly + # + for pwd, bad, good in samples: + with self.assertWarningList([corr_desc]): self.assertEqual(bcrypt.normhash(bad), good) - self.consumeWarningList(wlog, [corr_desc]) + with self.assertWarningList([]): self.assertEqual(bcrypt.normhash(good), good) - self.consumeWarningList(wlog) - self.assertEqual(bcrypt.normhash("$md5$abc"), "$md5$abc") + self.assertEqual(bcrypt.normhash("$md5$abc"), "$md5$abc") hash.bcrypt._no_backends_msg() #call this for coverage purposes diff --git a/passlib/tests/test_hosts.py b/passlib/tests/test_hosts.py index 69408da..33b2451 100644 --- a/passlib/tests/test_hosts.py +++ b/passlib/tests/test_hosts.py @@ -61,7 +61,7 @@ class HostsTest(TestCase): 'kAJJz.Rwp0A/I', ]: self.assertTrue(ctx.verify("test", hash)) - h1 = '$2a$10$Ljj0Kgu7Ddob9xWoqzn0ae.uNfxPRofowWdksk.6jCUHKTGYLD.QG' + h1 = "$2a$04$yjDgE74RJkeqC0/1NheSSOrvKeu9IbKDpcQf/Ox3qsrRS/Kw42qIS" if hashmod.bcrypt.has_backend(): self.assertTrue(ctx.verify("test", h1)) else: |