summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-03-12 22:48:23 -0400
committerEli Collins <elic@assurancetechnologies.com>2012-03-12 22:48:23 -0400
commitba4550d9dc9d12bf3ee714fb05040ebbf4adb8e8 (patch)
treee264eb2edf72efbeaf40d1616e4ffb2b528bde2e
parente89ebdf93b92dc018bd3ee1542cc4416b5024ab4 (diff)
downloadpasslib-ba4550d9dc9d12bf3ee714fb05040ebbf4adb8e8.tar.gz
misc bugfixes
* removed cisco_type7 config string, conflicted w/ empty password * fixed unicode type issue in cisco_type7, win32.nthash * bsdi_crypt.min_rounds now 1 (0 results in identical hashes) * fixed unicode type issue in UPASS_TABLE tests for plaintext, ldap_plaintext * relocated test vectors from test_win32 to lmhash/nthash * 8bit test for UnsaltedHash * fuzz testing expanded to use 5-99 char passwords, and 1/10000 are empty *
-rw-r--r--passlib/handlers/cisco.py14
-rw-r--r--passlib/handlers/des_crypt.py2
-rw-r--r--passlib/handlers/windows.py4
-rw-r--r--passlib/tests/test_handlers.py42
-rw-r--r--passlib/tests/test_utils_handlers.py17
-rw-r--r--passlib/tests/test_win32.py15
-rw-r--r--passlib/tests/utils.py23
7 files changed, 85 insertions, 32 deletions
diff --git a/passlib/handlers/cisco.py b/passlib/handlers/cisco.py
index 102d049..37d7233 100644
--- a/passlib/handlers/cisco.py
+++ b/passlib/handlers/cisco.py
@@ -11,7 +11,7 @@ from warnings import warn
#libs
#pkg
from passlib.utils import h64, to_bytes
-from passlib.utils.compat import b, bascii_to_str, unicode, u, join_byte_values, \
+from passlib.utils.compat import b, bascii_to_str, bytes, unicode, u, join_byte_values, \
join_byte_elems, byte_elem_value, iter_byte_values, uascii_to_str, str_to_uascii
import passlib.utils.handlers as uh
#local
@@ -109,7 +109,6 @@ class cisco_type7(uh.GenericHandler):
name = "cisco_type7"
setting_kwds = ("salt",)
checksum_chars = uh.UPPER_HEX_CHARS
- _stub_checksum = u("00")
max_salt_value = 52
@@ -117,8 +116,14 @@ class cisco_type7(uh.GenericHandler):
# methods
#=========================================================
@classmethod
+ def genconfig(cls):
+ return None
+
+ @classmethod
def from_string(cls, hash):
if not hash:
+ if hash is None:
+ return cls(use_defaults=True)
raise ValueError("no hash provided")
if len(hash) < 2:
raise ValueError("invalid cisco_type7 hash")
@@ -155,14 +160,13 @@ class cisco_type7(uh.GenericHandler):
return uh.rng.randint(0, 15)
def to_string(self):
- return "%02d%s" % (self.salt, uascii_to_str(self.checksum or
- self._stub_checksum))
+ return "%02d%s" % (self.salt, uascii_to_str(self.checksum))
def _calc_checksum(self, secret):
# XXX: no idea what unicode policy is, but all examples are
# 7-bit ascii compatible, so using UTF-8
secret = to_bytes(secret, "utf-8", errname="secret")
- return str_to_uascii(hexlify(self._cipher(secret, self.salt))).upper()
+ return hexlify(self._cipher(secret, self.salt)).decode("ascii").upper()
@classmethod
def decode(cls, hash, encoding="utf-8"):
diff --git a/passlib/handlers/des_crypt.py b/passlib/handlers/des_crypt.py
index f663a39..a65d147 100644
--- a/passlib/handlers/des_crypt.py
+++ b/passlib/handlers/des_crypt.py
@@ -275,7 +275,7 @@ class bsdi_crypt(uh.HasManyBackends, uh.HasRounds, uh.HasSalt, uh.GenericHandler
#--HasRounds--
default_rounds = 5001
- min_rounds = 0
+ min_rounds = 1
max_rounds = 16777215 # (1<<24)-1
rounds_cost = "linear"
diff --git a/passlib/handlers/windows.py b/passlib/handlers/windows.py
index 9805037..a60e911 100644
--- a/passlib/handlers/windows.py
+++ b/passlib/handlers/windows.py
@@ -10,7 +10,7 @@ from warnings import warn
#site
#libs
from passlib.utils import to_unicode, to_bytes
-from passlib.utils.compat import b, bytes, str_to_uascii, u, uascii_to_str
+from passlib.utils.compat import b, bytes, str_to_uascii, u, unicode, uascii_to_str
from passlib.utils.md4 import md4
import passlib.utils.handlers as uh
#pkg
@@ -161,7 +161,7 @@ class nthash(uh.StaticHandler):
"in Passlib 1.8, please use nthash.raw() instead",
DeprecationWarning)
ret = nthash.raw(secret)
- return hexlify(ret) if hex else ret
+ return hexlify(ret).decode("ascii") if hex else ret
#=========================================================
# eoc
diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py
index 29c77ef..5b32a60 100644
--- a/passlib/tests/test_handlers.py
+++ b/passlib/tests/test_handlers.py
@@ -10,7 +10,7 @@ import warnings
#site
#pkg
from passlib import hash
-from passlib.utils.compat import irange
+from passlib.utils.compat import irange, PY3
from passlib.tests.utils import TestCase, HandlerCase, create_backend_case, \
enable_option, b, catch_warnings, UserHandlerMixin, randintgauss
from passlib.utils.compat import u
@@ -25,6 +25,8 @@ UPASS_WAV = u('\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2')
UPASS_USD = u("\u20AC\u00A5$")
UPASS_TABLE = u("t\u00e1\u0411\u2113\u0259")
+PASS_TABLE_UTF8 = b('t\xc3\xa1\xd0\x91\xe2\x84\x93\xc9\x99') # utf-8
+
#=========================================================
#apr md5 crypt
#=========================================================
@@ -597,6 +599,8 @@ builtin_des_crypt_test = create_backend_case(_des_crypt_test, "builtin")
#django
#=========================================================
class _DjangoHelper(object):
+ # NOTE: not testing against Django < 1.0 since it doesn't support
+ # most of these hash formats.
def get_fuzz_verifiers(self):
verifiers = super(_DjangoHelper, self).get_fuzz_verifiers()
@@ -617,7 +621,7 @@ class _DjangoHelper(object):
return self.skipTest("no known correct hashes specified")
from passlib.tests.test_ext_django import has_django1
if not has_django1:
- return self.skipTest("Django not installed")
+ return self.skipTest("Django >= 1.0 not installed")
from django.contrib.auth.models import check_password
for secret, hash in self.iter_known_hashes():
self.assertTrue(check_password(secret, hash))
@@ -850,16 +854,27 @@ class ldap_plaintext_test(HandlerCase):
handler = hash.ldap_plaintext
known_correct_hashes = [
("password", 'password'),
- (UPASS_TABLE, 't\xc3\xa1\xd0\x91\xe2\x84\x93\xc9\x99'),
+ (UPASS_TABLE, UPASS_TABLE if PY3 else PASS_TABLE_UTF8),
+ (PASS_TABLE_UTF8, UPASS_TABLE if PY3 else PASS_TABLE_UTF8),
]
known_unidentified_hashes = [
- "{FOO}bar"
+ "{FOO}bar",
+
+ # XXX: currently we reject empty string as valid for this format.
+ "",
]
known_other_hashes = [
("ldap_md5", "{MD5}/F4DjTilcDIIVEHn/nAQsA==")
]
+ def get_fuzz_password(self):
+ # XXX: currently we reject empty string as valid for this format.
+ pwd = None
+ while not pwd:
+ pwd = super(ldap_plaintext_test, self).get_fuzz_password()
+ return pwd
+
#NOTE: since the ldap_{crypt} handlers are all wrappers,
# don't need separate test. have just one for end-to-end testing purposes.
@@ -933,6 +948,13 @@ class lmhash_test(HandlerCase):
known_correct_hashes = [
#
+ # http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx
+ #
+ ("OLDPASSWORD", "c9b81d939d6fd80cd408e6b105741864"),
+ ("NEWPASSWORD", '09eeab5aa415d6e4d408e6b105741864'),
+ ("welcome", "c23413a8a1e7665faad3b435b51404ee"),
+
+ #
# custom
#
('', 'aad3b435b51404eeaad3b435b51404ee'),
@@ -945,7 +967,7 @@ class lmhash_test(HandlerCase):
(u('encyclop\xE6dia'), 'fed6416bffc9750d48462b9d7aaac065'),
]
- # TODO: test encoding keyword.
+ # TODO: test encoding keyword.
known_unidentified_hashes = [
# bad char in otherwise correct hash
@@ -1343,6 +1365,12 @@ class nthash_test(HandlerCase):
known_correct_hashes = [
#
+ # http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx
+ #
+ ("OLDPASSWORD", u("6677b2c394311355b54f25eec5bfacf5")),
+ ("NEWPASSWORD", u("256781a62031289d3c2c98c14f1efc8c")),
+
+ #
# from JTR 1.7.9
#
@@ -1612,6 +1640,10 @@ class plaintext_test(HandlerCase):
known_correct_hashes = [
('',''),
('password', 'password'),
+
+ # ensure unicode uses utf-8
+ (UPASS_TABLE, UPASS_TABLE if PY3 else PASS_TABLE_UTF8),
+ (PASS_TABLE_UTF8, UPASS_TABLE if PY3 else PASS_TABLE_UTF8),
]
#=========================================================
diff --git a/passlib/tests/test_utils_handlers.py b/passlib/tests/test_utils_handlers.py
index 4788798..cffac95 100644
--- a/passlib/tests/test_utils_handlers.py
+++ b/passlib/tests/test_utils_handlers.py
@@ -572,21 +572,21 @@ class SaltedHash(uh.HasSalt, uh.GenericHandler):
#TODO: provide data samples for algorithms
# (positive knowns, negative knowns, invalid identify)
+UPASS_TEMP = u('\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2')
+
class UnsaltedHashTest(HandlerCase):
handler = UnsaltedHash
known_correct_hashes = [
("password", "61cfd32684c47de231f1f982c214e884133762c0"),
+ (UPASS_TEMP, '96b329d120b97ff81ada770042e44ba87343ad2b'),
]
def test_bad_kwds(self):
- if not JYTHON:
- #FIXME: annoyingly, the object() constructor of Jython (as of 2.5.2)
- # silently drops any extra kwds (old 2.4 behavior)
- # instead of raising TypeError (new 2.5 behavior).
- # we *could* use a custom base object to restore correct
- # behavior, but that's a lot of effort for a non-critical
- # border case. so just skipping this test instead...
+ if not PY_MAX_25:
+ # annoyingly, py25's ``super().__init__()`` doesn't throw TypeError
+ # when passing unknown keywords to object. just ignoring
+ # this issue for now, since it's a minor border case.
self.assertRaises(TypeError, UnsaltedHash, salt='x')
self.assertRaises(TypeError, UnsaltedHash.genconfig, rounds=1)
@@ -595,8 +595,7 @@ class SaltedHashTest(HandlerCase):
known_correct_hashes = [
("password", '@salt77d71f8fe74f314dac946766c1ac4a2a58365482c0'),
- (u('\u0399\u03c9\u03b1\u03bd\u03bd\u03b7\u03c2'),
- '@salt9f978a9bfe360d069b0c13f2afecd570447407fa7e48'),
+ (UPASS_TEMP, '@salt9f978a9bfe360d069b0c13f2afecd570447407fa7e48'),
]
def test_bad_kwds(self):
diff --git a/passlib/tests/test_win32.py b/passlib/tests/test_win32.py
index 917cbf6..09f5023 100644
--- a/passlib/tests/test_win32.py
+++ b/passlib/tests/test_win32.py
@@ -4,11 +4,11 @@
#=========================================================
#core
from binascii import hexlify
+import warnings
#site
#pkg
from passlib.tests.utils import TestCase
#module
-import passlib.win32 as mod
from passlib.utils.compat import u
#=========================================================
@@ -20,21 +20,30 @@ class UtilTest(TestCase):
##test hashes from http://msdn.microsoft.com/en-us/library/cc245828(v=prot.10).aspx
## among other places
+ def setUp(self):
+ TestCase.setUp(self)
+ warnings.filterwarnings("ignore",
+ "the 'passlib.win32' module is deprecated")
+
def test_lmhash(self):
+ from passlib.win32 import raw_lmhash
for secret, hash in [
("OLDPASSWORD", u("c9b81d939d6fd80cd408e6b105741864")),
("NEWPASSWORD", u('09eeab5aa415d6e4d408e6b105741864')),
("welcome", u("c23413a8a1e7665faad3b435b51404ee")),
]:
- result = mod.raw_lmhash(secret, hex=True)
+ result = raw_lmhash(secret, hex=True)
self.assertEqual(result, hash)
def test_nthash(self):
+ warnings.filterwarnings("ignore",
+ r"nthash\.raw_nthash\(\) is deprecated")
+ from passlib.win32 import raw_nthash
for secret, hash in [
("OLDPASSWORD", u("6677b2c394311355b54f25eec5bfacf5")),
("NEWPASSWORD", u("256781a62031289d3c2c98c14f1efc8c")),
]:
- result = mod.raw_nthash(secret, hex=True)
+ result = raw_nthash(secret, hex=True)
self.assertEqual(result, hash)
#=========================================================
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py
index 8e2883b..b3ce550 100644
--- a/passlib/tests/utils.py
+++ b/passlib/tests/utils.py
@@ -710,7 +710,7 @@ class HandlerCase(TestCase):
as possible.
"""
handler = self.handler
- alt_backend = _has_other_backends(handler, "os_crypt")
+ alt_backend = _find_alternate_backend(handler, "os_crypt")
if not alt_backend:
raise AssertionError("handler has no available backends!")
import passlib.utils as mod
@@ -921,7 +921,8 @@ class HandlerCase(TestCase):
return
raise self.failureException("failed to find different salt after "
"%d samples" % (samples,))
- sampler(self.do_genconfig)
+ if self.do_genconfig() is not None: # cisco_type7 has salt & no config
+ sampler(self.do_genconfig)
sampler(lambda : self.do_encrypt("stub"))
def test_12_min_salt_size(self):
@@ -1487,7 +1488,7 @@ class HandlerCase(TestCase):
while tick() <= stop:
# generate random password & options
secret = self.get_fuzz_password()
- other = secret.strip()[1:]
+ other = self.mangle_fuzz_password(secret)
if rng.randint(0,1):
secret = secret.encode(self.fuzz_password_encoding)
other = other.encode(self.fuzz_password_encoding)
@@ -1496,7 +1497,8 @@ class HandlerCase(TestCase):
# create new hash
hash = self.do_encrypt(secret, **kwds)
- ##log.debug("fuzz test: hash=%r secret=%r", hash, secret)
+ ##log.debug("fuzz test: hash=%r secret=%r other=%r",
+ ## hash, secret, other)
# run through all verifiers we found.
for verify in verifiers:
@@ -1564,7 +1566,14 @@ class HandlerCase(TestCase):
def get_fuzz_password(self):
"generate random passwords (for fuzz testing)"
- return getrandstr(rng, self.fuzz_password_alphabet, rng.randint(5,15))
+ if rng.random() < .0001:
+ return u('')
+ return getrandstr(rng, self.fuzz_password_alphabet, rng.randint(5,99))
+
+ def mangle_fuzz_password(self, secret):
+ "mangle fuzz-testing password so it doesn't match"
+ secret = secret.strip()[1:]
+ return secret or self.get_fuzz_password()
def get_fuzz_settings(self):
"generate random settings (for fuzz testing)"
@@ -1711,7 +1720,7 @@ def _enable_backend_case(handler, backend):
return True, None
from passlib.utils import has_crypt
if backend == "os_crypt" and has_crypt:
- if enable_option("cover") and _has_other_backends(handler, "os_crypt"):
+ if enable_option("cover") and _find_alternate_backend(handler, "os_crypt"):
#in this case, HandlerCase will monkeypatch os_crypt
#to use another backend, just so we can test os_crypt fully.
return True, None
@@ -1733,7 +1742,7 @@ def _is_default_backend(handler, name):
finally:
handler.set_backend(orig)
-def _has_other_backends(handler, ignore):
+def _find_alternate_backend(handler, ignore):
"helper to check if alternate backend is available"
for name in handler.backends:
if name != ignore and handler.has_backend(name):