summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-08-02 13:38:45 -0400
committerEli Collins <elic@assurancetechnologies.com>2012-08-02 13:38:45 -0400
commit92e3505b25ca0c244da7bc3d2c1060afa560c82d (patch)
treefc1beb533356d7796adf079b8ce45d3412fae09e
parent14a546c7293a627b87e207222227491e31975d74 (diff)
downloadpasslib-92e3505b25ca0c244da7bc3d2c1060afa560c82d.tar.gz
rearranged/refactored HandlerCase fuzz testing functions somewhat
-rw-r--r--passlib/tests/test_handlers.py20
-rw-r--r--passlib/tests/utils.py169
2 files changed, 101 insertions, 88 deletions
diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py
index c18d772..2d4a49b 100644
--- a/passlib/tests/test_handlers.py
+++ b/passlib/tests/test_handlers.py
@@ -277,7 +277,7 @@ class _bcrypt_test(HandlerCase):
other = self.get_fuzz_password()
return secret, other, kwds
- def get_fuzz_rounds(self):
+ def fuzz_setting_rounds(self):
# decrease default rounds for fuzz testing to speed up volume.
return randintgauss(5, 8, 6, 1)
@@ -832,7 +832,7 @@ class django_salted_md5_test(HandlerCase, _DjangoHelper):
'md5$aa$bb',
]
- def get_fuzz_salt_size(self):
+ def fuzz_setting_salt_size(self):
# workaround for django14 regression --
# 1.4 won't accept hashes with empty salt strings, unlike 1.3 and earlier.
# looks to be fixed in a future release -- https://code.djangoproject.com/ticket/18144
@@ -873,7 +873,7 @@ class django_salted_sha1_test(HandlerCase, _DjangoHelper):
'sha1$c2e86$0f75',
]
- get_fuzz_salt_size = get_method_function(django_salted_md5_test.get_fuzz_salt_size)
+ fuzz_setting_salt_size = get_method_function(django_salted_md5_test.fuzz_setting_salt_size)
class django_pbkdf2_sha256_test(HandlerCase, _DjangoHelper):
"test django_pbkdf2_sha256"
@@ -930,11 +930,11 @@ class django_bcrypt_test(HandlerCase, _DjangoHelper):
kwds.setdefault("rounds", 4)
super(django_bcrypt_test, self).populate_settings(kwds)
- def get_fuzz_rounds(self):
+ def fuzz_setting_rounds(self):
# decrease default rounds for fuzz testing to speed up volume.
return randintgauss(5, 8, 6, 1)
- def get_fuzz_ident(self):
+ def fuzz_setting_ident(self):
# omit multi-ident tests, only $2a$ counts for this class
return None
@@ -1180,7 +1180,7 @@ class ldap_plaintext_test(HandlerCase):
known_unidentified_hashes = [
"{FOO}bar",
- # XXX: currently we reject empty string as valid for this format.
+ # NOTE: this hash currently rejects the empty string.
"",
]
@@ -1189,11 +1189,11 @@ class ldap_plaintext_test(HandlerCase):
]
def get_fuzz_password(self):
- # XXX: currently we reject empty string as valid for this format.
- pwd = None
- while not pwd:
+ # NOTE: this hash currently rejects the empty string.
+ while True:
pwd = super(ldap_plaintext_test, self).get_fuzz_password()
- return pwd
+ if pwd:
+ return pwd
class _ldap_md5_crypt_test(HandlerCase):
# NOTE: since the ldap_{crypt} handlers are all wrappers, don't need
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py
index 6257acb..29254c8 100644
--- a/passlib/tests/utils.py
+++ b/passlib/tests/utils.py
@@ -1655,38 +1655,23 @@ class HandlerCase(TestCase):
#===================================================================
# fuzz testing
#===================================================================
- """the following attempts to perform some basic fuzz testing
- of the handler, based on whatever information can be found about it.
- it does as much as it can within a fixed amount of time
- (defaults to 1 second, but can be overridden via $PASSLIB_TEST_FUZZ_TIME).
- it tests the following:
-
- * randomly generated passwords including extended unicode chars
- * randomly selected rounds values (if rounds supported)
- * randomly selected salt sizes (if salts supported)
- * randomly selected identifiers (if multiple found)
-
- * runs output of selected backend against other available backends
- (if any) to detect errors occurring between different backends.
- * runs output against other "external" verifiers such as OS crypt()
- """
-
- fuzz_password_alphabet = u('qwertyASDF1234<>.@*#! \u00E1\u0259\u0411\u2113')
- fuzz_password_encoding = "utf-8"
- fuzz_settings = ["rounds", "salt_size", "ident"]
-
- @property
- def max_fuzz_time(self):
- if TEST_MODE(max="quick"):
- default = 0
- elif TEST_MODE(max="default"):
- default = 1
- else:
- default = 5
- return float(os.environ.get("PASSLIB_TEST_FUZZ_TIME") or default)
-
def test_77_fuzz_input(self):
- """test random passwords and options"""
+ """test random passwords and options
+
+ This test attempts to perform some basic fuzz testing of the hash,
+ based on whatever information can be found about it.
+ It does as much as it can within a fixed amount of time
+ (defaults to 1 second, but can be overridden via $PASSLIB_TEST_FUZZ_TIME).
+ It tests the following:
+
+ * randomly generated passwords including extended unicode chars
+ * randomly selected rounds values (if rounds supported)
+ * randomly selected salt sizes (if salts supported)
+ * randomly selected identifiers (if multiple found)
+ * runs output of selected backend against other available backends
+ (if any) to detect errors occurring between different backends.
+ * runs output against other "external" verifiers such as OS crypt()
+ """
if self.is_disabled_handler:
raise self.skipTest("not applicable")
@@ -1739,6 +1724,36 @@ class HandlerCase(TestCase):
self.descriptionPrefix, count, len(verifiers),
", ".join(vname(v) for v in verifiers))
+ #---------------------------------------------------------------
+ # fuzz constants & helpers
+ #---------------------------------------------------------------
+
+ # alphabet for randomly generated passwords
+ fuzz_password_alphabet = u('qwertyASDF1234<>.@*#! \u00E1\u0259\u0411\u2113')
+
+ # encoding when testing bytes
+ fuzz_password_encoding = "utf-8"
+
+ @property
+ def max_fuzz_time(self):
+ "amount of time to spend on fuzz testing"
+ value = float(os.environ.get("PASSLIB_TEST_FUZZ_TIME") or 0)
+ if value:
+ return value
+ elif TEST_MODE(max="quick"):
+ return 0
+ elif TEST_MODE(max="default"):
+ return 1
+ else:
+ return 5
+
+ def os_supports_ident(self, ident):
+ "whether native OS crypt() supports particular ident value"
+ return True
+
+ #---------------------------------------------------------------
+ # fuzz verifiers
+ #---------------------------------------------------------------
def get_fuzz_verifiers(self):
"""return list of password verifiers (including external libs)
@@ -1784,10 +1799,6 @@ class HandlerCase(TestCase):
check_default.__doc__ = "self"
return check_default
- def os_supports_ident(self, ident):
- "skip verifier_crypt when OS doesn't support ident"
- return True
-
def fuzz_verifier_crypt(self):
"test results against OS crypt()"
handler = self.handler
@@ -1802,50 +1813,22 @@ class HandlerCase(TestCase):
return crypt(secret, hash) == hash
return check_crypt
- def get_fuzz_password(self):
- "generate random passwords (for fuzz testing)"
- # occasionally try an empty password
- if rng.random() < .0001:
- return u('')
- # otherwise alternate between large and small passwords.
- if rng.random() < .5:
- size = randintgauss(1, 50, 15, 15)
- else:
- size = randintgauss(50, 99, 70, 20)
- return getrandstr(rng, self.fuzz_password_alphabet, size)
-
- def mangle_fuzz_password(self, secret):
- "mangle fuzz-testing password so it doesn't match"
- other = secret.strip()[1:]
- if other:
- return other
- while True:
- other = self.get_fuzz_password()
- if other != secret:
- return other
-
- def get_fuzz_password_pair(self):
- "generate random password, and non-matching alternate password"
- secret = self.get_fuzz_password()
- other = self.mangle_fuzz_password(secret)
- if rng.randint(0,1):
- secret = secret.encode(self.fuzz_password_encoding)
- if rng.randint(0,1):
- other = other.encode(self.fuzz_password_encoding)
- return secret, other
-
+ #---------------------------------------------------------------
+ # fuzz settings generation
+ #---------------------------------------------------------------
def get_fuzz_settings(self):
"generate random password and options for fuzz testing"
+ prefix = "fuzz_setting_"
kwds = {}
- for name in self.fuzz_settings:
- func = getattr(self, "get_fuzz_" + name)
- value = func()
- if value is not None:
- kwds[name] = value
+ for name in dir(self):
+ if name.startswith(prefix):
+ value = getattr(self, name)()
+ if value is not None:
+ kwds[name[len(prefix):]] = value
secret, other = self.get_fuzz_password_pair()
return secret, other, kwds
- def get_fuzz_rounds(self):
+ def fuzz_setting_rounds(self):
handler = self.handler
if not has_rounds_info(handler):
return None
@@ -1857,7 +1840,7 @@ class HandlerCase(TestCase):
upper = min(default*2, handler.max_rounds)
return randintgauss(lower, upper, default, default*.5)
- def get_fuzz_salt_size(self):
+ def fuzz_setting_salt_size(self):
handler = self.handler
if not (has_salt_info(handler) and 'salt_size' in handler.setting_kwds):
return None
@@ -1866,7 +1849,7 @@ class HandlerCase(TestCase):
upper = handler.max_salt_size or default*4
return randintgauss(lower, upper, default, default*.5)
- def get_fuzz_ident(self):
+ def fuzz_setting_ident(self):
handler = self.handler
if 'ident' not in handler.setting_kwds or not hasattr(handler, "ident_values"):
return None
@@ -1879,6 +1862,38 @@ class HandlerCase(TestCase):
return None
return ident
+ #---------------------------------------------------------------
+ # fuzz password generation
+ #---------------------------------------------------------------
+ def get_fuzz_password(self):
+ "generate random passwords for fuzz testing"
+ # occasionally try an empty password
+ if rng.random() < .0001:
+ return u('')
+ # otherwise alternate between large and small passwords.
+ if rng.random() < .5:
+ size = randintgauss(1, 50, 15, 15)
+ else:
+ size = randintgauss(50, 99, 70, 20)
+ return getrandstr(rng, self.fuzz_password_alphabet, size)
+
+ def accept_fuzz_pair(self, secret, other):
+ "verify fuzz pair contains different passwords"
+ return secret != other
+
+ def get_fuzz_password_pair(self):
+ "generate random password, and non-matching alternate password"
+ secret = self.get_fuzz_password()
+ while True:
+ other = self.get_fuzz_password()
+ if self.accept_fuzz_pair(secret, other):
+ break
+ if rng.randint(0,1):
+ secret = secret.encode(self.fuzz_password_encoding)
+ if rng.randint(0,1):
+ other = other.encode(self.fuzz_password_encoding)
+ return secret, other
+
#===================================================================
# eoc
#===================================================================
@@ -2111,9 +2126,7 @@ class UserHandlerMixin(HandlerCase):
#===================================================================
fuzz_user_alphabet = u("asdQWE123")
- fuzz_settings = HandlerCase.fuzz_settings + ["user"]
-
- def get_fuzz_user(self):
+ def fuzz_setting_user(self):
if not self.requires_user and rng.random() < .1:
return None
return getrandstr(rng, self.fuzz_user_alphabet, rng.randint(2,10))