summaryrefslogtreecommitdiff
path: root/passlib/tests
diff options
context:
space:
mode:
authorEli Collins <elic@assurancetechnologies.com>2012-03-10 16:43:05 -0500
committerEli Collins <elic@assurancetechnologies.com>2012-03-10 16:43:05 -0500
commit929d2168c5119c4b9b401e0ece4a39bf8b944b08 (patch)
treebbe311ed8fa8cccd397166838fe6ccf488c7e326 /passlib/tests
parent557d17ba4e0123bce7e1659002270aa8dedb2f24 (diff)
downloadpasslib-929d2168c5119c4b9b401e0ece4a39bf8b944b08.tar.gz
added support for Cisco PIX & Type 7 hashes
* Cisco Type 5 appears to be same as md5_crypt * added requires_user=False support to HandlerCase * added more through salt-generation test (since cisco_pix has only 4 bits of salt) * added HandlerCase test to ensure user is used as salt
Diffstat (limited to 'passlib/tests')
-rw-r--r--passlib/tests/test_handlers.py108
-rw-r--r--passlib/tests/utils.py68
2 files changed, 162 insertions, 14 deletions
diff --git a/passlib/tests/test_handlers.py b/passlib/tests/test_handlers.py
index 6172039..1276fb7 100644
--- a/passlib/tests/test_handlers.py
+++ b/passlib/tests/test_handlers.py
@@ -359,6 +359,114 @@ os_crypt_bsdi_crypt_test = create_backend_case(_bsdi_crypt_test, "os_crypt")
builtin_bsdi_crypt_test = create_backend_case(_bsdi_crypt_test, "builtin")
#=========================================================
+# cisco pix
+#=========================================================
+class cisco_pix_test(UserHandlerMixin, HandlerCase):
+ handler = hash.cisco_pix
+ secret_size = 16
+ requires_user = False
+
+ known_correct_hashes = [
+ #
+ # http://www.perlmonks.org/index.pl?node_id=797623
+ #
+ ("cisco", "2KFQnbNIdI.2KYOU"),
+
+ #
+ # http://www.hsc.fr/ressources/breves/pix_crack.html.en
+ #
+ ("hsc", "YtT8/k6Np8F1yz2c"),
+
+ #
+ # www.freerainbowtables.com/phpBB3/viewtopic.php?f=2&t=1441
+ #
+ ("", "8Ry2YjIyt7RRXU24"),
+ (("cisco", "john"), "hN7LzeyYjw12FSIU"),
+ (("cisco", "jack"), "7DrfeZ7cyOj/PslD"),
+
+ #
+ # http://comments.gmane.org/gmane.comp.security.openwall.john.user/2529
+ #
+ (("ripper", "alex"), "h3mJrcH0901pqX/m"),
+ (("cisco", "cisco"), "3USUcOPFUiMCO4Jk"),
+ (("cisco", "cisco1"), "3USUcOPFUiMCO4Jk"),
+ (("CscFw-ITC!", "admcom"), "lZt7HSIXw3.QP7.R"),
+ ("cangetin", "TynyB./ftknE77QP"),
+ (("cangetin", "rramsey"), "jgBZqYtsWfGcUKDi"),
+
+ #
+ # http://openwall.info/wiki/john/sample-hashes
+ #
+ (("phonehome", "rharris"), "zyIIMSYjiPm0L7a6"),
+
+ #
+ # from JTR 1.7.9
+ #
+ ("test1", "TRPEas6f/aa6JSPL"),
+ ("test2", "OMT6mXmAvGyzrCtp"),
+ ("test3", "gTC7RIy1XJzagmLm"),
+ ("test4", "oWC1WRwqlBlbpf/O"),
+ ("password", "NuLKvvWGg.x9HEKO"),
+ ("0123456789abcdef", ".7nfVBEIEu4KbF/1"),
+
+ #
+ # custom
+ #
+ (("cisco1", "cisco1"), "jmINXNH6p1BxUppp"),
+
+ # ensures utf-8 used for unicode
+ (UPASS_TABLE, 'CaiIvkLMu2TOHXGT'),
+ ]
+
+#=========================================================
+# cisco type 7
+#=========================================================
+class cisco_type7_test(HandlerCase):
+ handler = hash.cisco_type7
+ salt_bits = 4
+
+ known_correct_hashes = [
+ #
+ # http://mccltd.net/blog/?p=1034
+ #
+ ("secure ", "04480E051A33490E"),
+
+ #
+ # http://insecure.org/sploits/cisco.passwords.html
+ #
+ ("Its time to go to lunch!",
+ "153B1F1F443E22292D73212D5300194315591954465A0D0B59"),
+
+ #
+ # http://blog.ioshints.info/2007/11/type-7-decryption-in-cisco-ios.html
+ #
+ ("t35t:pa55w0rd", "08351F1B1D431516475E1B54382F"),
+
+ #
+ # http://www.m00nie.com/2011/09/cisco-type-7-password-decryption-and-encryption-with-perl/
+ #
+ ("hiImTesting:)", "020E0D7206320A325847071E5F5E"),
+
+ #
+ # http://packetlife.net/forums/thread/54/
+ #
+ ("cisco123", "060506324F41584B56"),
+ ("cisco123", "1511021F07257A767B"),
+
+ #
+ # source ?
+ #
+ ('Supe&8ZUbeRp4SS', "06351A3149085123301517391C501918"),
+
+ #
+ # custom
+ #
+
+ # ensures utf-8 used for unicode
+ (UPASS_TABLE, '0958EDC8A9F495F6F8A5FD'),
+ ]
+
+#=========================================================
# crypt16
#=========================================================
class crypt16_test(HandlerCase):
diff --git a/passlib/tests/utils.py b/passlib/tests/utils.py
index 4b9ca99..fde0f87 100644
--- a/passlib/tests/utils.py
+++ b/passlib/tests/utils.py
@@ -848,14 +848,36 @@ class HandlerCase(TestCase):
if not cls.default_salt_chars:
raise AssertionError("default_salt_chars MUST be specified if salt_chars is empty")
+ @property
+ def salt_bits(self):
+ "calculate number of salt bits in hash"
+ handler = self.handler
+ assert has_salt_info(handler), "need explicit bit-size for " + handler.name
+ from math import log
+ # FIXME: this may be off for case-insensitive hashes, but that accounts
+ # for ~1 bit difference, which is good enough for test_11()
+ return int(handler.default_salt_size *
+ log(len(handler.default_salt_chars), 2))
+
def test_11_unique_salt(self):
"test encrypt() / genconfig() creates new salt each time"
self.require_salt()
- c1 = self.do_genconfig()
- c2 = self.do_genconfig()
- self.assertIsInstance(c1, str, "genconfig() must return native str:")
- self.assertIsInstance(c2, str, "genconfig() must return native str:")
- self.assertNotEqual(c1,c2)
+ # odds of picking 'n' identical salts at random is '(.5**salt_bits)**n'.
+ # we want to pick the smallest N needed s.t. odds are <1/1000, just
+ # to eliminate false-positives. which works out to n>7-salt_bits.
+ # n=1 is sufficient for most hashes, but a few border cases (e.g.
+ # cisco_type7) have < 7 bits of salt, requiring more.
+ samples = max(1,7-self.salt_bits)
+ def sampler(func):
+ value1 = func()
+ for i in irange(samples):
+ value2 = func()
+ if value1 != value2:
+ return
+ raise self.failureException("failed to find different salt after "
+ "%d samples" % (samples,))
+ sampler(self.do_genconfig)
+ sampler(lambda : self.do_encrypt("stub"))
def test_12_min_salt_size(self):
"test encrypt() / genconfig() honors min_salt_size"
@@ -1559,25 +1581,28 @@ class UserHandlerMixin(HandlerCase):
"""
__unittest_skip = True
default_user = "user"
+ requires_user = True
user_case_insensitive = False
def test_80_user(self):
"test user context keyword"
handler = self.handler
password = 'stub'
- hash = self.get_sample_hash()[1]
-
- handler.encrypt(password, u('user'))
-
- self.assertRaises(TypeError, handler.encrypt, password)
- self.assertRaises(TypeError, handler.genhash, password, hash)
- self.assertRaises(TypeError, handler.verify, password, hash)
+ hash = handler.encrypt(password, user=self.default_user)
- # TODO: user size? kinda dicey, depends on algorithm.
+ if self.requires_user:
+ self.assertRaises(TypeError, handler.encrypt, password)
+ self.assertRaises(TypeError, handler.genhash, password, hash)
+ self.assertRaises(TypeError, handler.verify, password, hash)
+ else:
+ # e.g. cisco_pix works with or without one.
+ handler.encrypt(password)
+ handler.genhash(password, hash)
+ handler.verify(password, hash)
def test_81_user_case(self):
"test user case sensitivity"
- lower = (self.default_user or 'user').lower()
+ lower = self.default_user.lower()
upper = lower.upper()
hash = self.do_encrypt('stub', user=lower)
if self.user_case_insensitive:
@@ -1587,6 +1612,17 @@ class UserHandlerMixin(HandlerCase):
self.assertFalse(self.do_verify('stub', hash, user=upper),
"user should be case sensitive")
+ def test_82_user_salt(self):
+ "test user used as salt"
+ config = self.do_genconfig()
+ h1 = self.do_genhash('stub', config, user='admin')
+ h2 = self.do_genhash('stub', config, user='admin')
+ self.assertEqual(h2, h1)
+ h3 = self.do_genhash('stub', config, user='root')
+ self.assertNotEqual(h3, h1)
+
+ # TODO: user size? kinda dicey, depends on algorithm.
+
def is_secret_8bit(self, secret):
secret = self._insert_user({}, secret)
return not is_ascii_safe(secret)
@@ -1595,6 +1631,8 @@ class UserHandlerMixin(HandlerCase):
"insert username into kwds"
if isinstance(secret, tuple):
secret, user = secret
+ elif not self.requires_user:
+ return secret
else:
user = self.default_user
if 'user' not in kwds:
@@ -1616,6 +1654,8 @@ class UserHandlerMixin(HandlerCase):
fuzz_user_alphabet = u("asdQWE123")
fuzz_settings = HandlerCase.fuzz_settings + ["user"]
def get_fuzz_user(self):
+ if not self.requires_user and rng.random() < .1:
+ return None
return getrandstr(rng, self.fuzz_user_alphabet, rng.randint(2,10))
#=========================================================