diff options
| author | Eli Collins <elic@assurancetechnologies.com> | 2012-03-10 16:43:05 -0500 |
|---|---|---|
| committer | Eli Collins <elic@assurancetechnologies.com> | 2012-03-10 16:43:05 -0500 |
| commit | 929d2168c5119c4b9b401e0ece4a39bf8b944b08 (patch) | |
| tree | bbe311ed8fa8cccd397166838fe6ccf488c7e326 /passlib/tests | |
| parent | 557d17ba4e0123bce7e1659002270aa8dedb2f24 (diff) | |
| download | passlib-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.py | 108 | ||||
| -rw-r--r-- | passlib/tests/utils.py | 68 |
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)) #========================================================= |
