• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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