1import sys 2import unittest 3 4 5try: 6 import crypt 7 IMPORT_ERROR = None 8except ImportError as ex: 9 if sys.platform != 'win32': 10 raise unittest.SkipTest(str(ex)) 11 crypt = None 12 IMPORT_ERROR = str(ex) 13 14 15@unittest.skipUnless(sys.platform == 'win32', 'This should only run on windows') 16@unittest.skipIf(crypt, 'import succeeded') 17class TestWhyCryptDidNotImport(unittest.TestCase): 18 19 def test_import_failure_message(self): 20 self.assertIn('not supported', IMPORT_ERROR) 21 22 23@unittest.skipUnless(crypt, 'crypt module is required') 24class CryptTestCase(unittest.TestCase): 25 26 def test_crypt(self): 27 cr = crypt.crypt('mypassword') 28 cr2 = crypt.crypt('mypassword', cr) 29 self.assertEqual(cr2, cr) 30 cr = crypt.crypt('mypassword', 'ab') 31 if cr is not None: 32 cr2 = crypt.crypt('mypassword', cr) 33 self.assertEqual(cr2, cr) 34 35 def test_salt(self): 36 self.assertEqual(len(crypt._saltchars), 64) 37 for method in crypt.methods: 38 salt = crypt.mksalt(method) 39 self.assertIn(len(salt) - method.salt_chars, {0, 1, 3, 4, 6, 7}) 40 if method.ident: 41 self.assertIn(method.ident, salt[:len(salt)-method.salt_chars]) 42 43 def test_saltedcrypt(self): 44 for method in crypt.methods: 45 cr = crypt.crypt('assword', method) 46 self.assertEqual(len(cr), method.total_size) 47 cr2 = crypt.crypt('assword', cr) 48 self.assertEqual(cr2, cr) 49 cr = crypt.crypt('assword', crypt.mksalt(method)) 50 self.assertEqual(len(cr), method.total_size) 51 52 def test_methods(self): 53 self.assertTrue(len(crypt.methods) >= 1) 54 if sys.platform.startswith('openbsd'): 55 self.assertEqual(crypt.methods, [crypt.METHOD_BLOWFISH]) 56 else: 57 self.assertEqual(crypt.methods[-1], crypt.METHOD_CRYPT) 58 59 @unittest.skipUnless( 60 crypt 61 and ( 62 crypt.METHOD_SHA256 in crypt.methods or crypt.METHOD_SHA512 in crypt.methods 63 ), 64 'requires support of SHA-2', 65 ) 66 def test_sha2_rounds(self): 67 for method in (crypt.METHOD_SHA256, crypt.METHOD_SHA512): 68 for rounds in 1000, 10_000, 100_000: 69 salt = crypt.mksalt(method, rounds=rounds) 70 self.assertIn('$rounds=%d$' % rounds, salt) 71 self.assertEqual(len(salt) - method.salt_chars, 72 11 + len(str(rounds))) 73 cr = crypt.crypt('mypassword', salt) 74 self.assertTrue(cr) 75 cr2 = crypt.crypt('mypassword', cr) 76 self.assertEqual(cr2, cr) 77 78 @unittest.skipUnless( 79 crypt and crypt.METHOD_BLOWFISH in crypt.methods, 'requires support of Blowfish' 80 ) 81 def test_blowfish_rounds(self): 82 for log_rounds in range(4, 11): 83 salt = crypt.mksalt(crypt.METHOD_BLOWFISH, rounds=1 << log_rounds) 84 self.assertIn('$%02d$' % log_rounds, salt) 85 self.assertIn(len(salt) - crypt.METHOD_BLOWFISH.salt_chars, {6, 7}) 86 cr = crypt.crypt('mypassword', salt) 87 self.assertTrue(cr) 88 cr2 = crypt.crypt('mypassword', cr) 89 self.assertEqual(cr2, cr) 90 91 def test_invalid_rounds(self): 92 for method in (crypt.METHOD_SHA256, crypt.METHOD_SHA512, 93 crypt.METHOD_BLOWFISH): 94 with self.assertRaises(TypeError): 95 crypt.mksalt(method, rounds='4096') 96 with self.assertRaises(TypeError): 97 crypt.mksalt(method, rounds=4096.0) 98 for rounds in (0, 1, -1, 1<<999): 99 with self.assertRaises(ValueError): 100 crypt.mksalt(method, rounds=rounds) 101 with self.assertRaises(ValueError): 102 crypt.mksalt(crypt.METHOD_BLOWFISH, rounds=1000) 103 for method in (crypt.METHOD_CRYPT, crypt.METHOD_MD5): 104 with self.assertRaisesRegex(ValueError, 'support'): 105 crypt.mksalt(method, rounds=4096) 106 107 108if __name__ == "__main__": 109 unittest.main() 110