2727
2828"""Unit tests for the migrid module pointed to in the filename"""
2929
30+ import base64
3031import os
3132import sys
3233
4041
4142DUMMY_USER = "dummy-user"
4243DUMMY_ID = "dummy-id"
43- DUMMY_PW = 'foobar'
44- DUMMY_PW_HASH = \
44+ DUMMY_WEAK_PW = 'foobar'
45+ DUMMY_MEDIUM_PW = 'QZFnCp7h'
46+ DUMMY_HIGH_PW = 'QZFnp7I-GZ'
47+ DUMMY_MODERN_PW = 'QZFnCp7hmI1G'
48+ DUMMY_WEAK_PW_MD5 = "3858f62230ac3c915f300c664312c63f"
49+ DUMMY_WEAK_PW_PBKDF2 = \
4550 "PBKDF2$sha256$10000$MDAwMDAwMDAwMDAw$epib2rEg/HYTQZFnCp7hmIGZ6rzHnViy"
4651DUMMY_HOME_DIR = 'dummy_user_home'
4752DUMMY_SETTINGS_DIR = 'dummy_user_settings'
48- DUMMY_SERVICE = 'svc'
53+ DUMMY_SERVICE = 'dummy-svc'
54+ DUMMY_REALM = 'dummy-realm'
55+ DUMMY_SALT = base64 .b16encode (os .urandom (16 ))
4956
5057
5158class MigSharedPwCrypto (MigTestCase ):
@@ -61,26 +68,151 @@ def before_each(self):
6168 # now create a configuration
6269 self .dummy_conf = FakeConfiguration (
6370 user_home = test_user_home , user_settings = test_user_settings ,
64- site_password_policy = POLICY_HIGH ,
71+ site_password_policy = "%s:12" % POLICY_MODERN ,
6572 site_password_legacy_policy = POLICY_MEDIUM ,
66- site_password_cracklib = False )
73+ site_password_cracklib = False ,
74+ site_crypto_salt = DUMMY_SALT ,
75+ site_password_salt = DUMMY_SALT ,
76+ site_digest_salt = DUMMY_SALT ,
77+ )
6778
68- def test_make_hash_constant_string (self ):
79+ def test_best_crypt_salt (self ):
80+ """Test selection of best salt based on salt availability in
81+ configuration. Disable best choice in turn and check fallback.
82+ """
83+ expected = DUMMY_SALT
84+ actual = best_crypt_salt (self .dummy_conf )
85+ self .assertEqual (actual , expected , "best crypt salt not found" )
86+ self .dummy_conf .site_crypto_salt = ''
87+ actual = best_crypt_salt (self .dummy_conf )
88+ self .assertEqual (actual , expected , "2nd best crypt salt not found" )
89+ self .dummy_conf .site_password_salt = ''
90+ actual = best_crypt_salt (self .dummy_conf )
91+ self .assertEqual (actual , expected , "3rd best crypt salt not found" )
92+ self .dummy_conf .site_digest_salt = ''
93+ actual = None
94+ try :
95+ actual = best_crypt_salt (self .dummy_conf )
96+ except Exception as exc :
97+ pass
98+ self .assertTrue (actual is None , "best crypt salt failed to err" )
99+
100+ def test_make_simple_hash_fixed_seed (self ):
101+ """Test basic hashing of a fixed string to be constant"""
102+ expected = DUMMY_WEAK_PW_MD5
103+ actual = make_simple_hash (DUMMY_WEAK_PW )
104+ self .assertEqual (actual , expected , "mismatch simple hash string" )
105+
106+ def test_make_simple_hash_constant_string (self ):
107+ """Test basic hashing of a fixed string to be constant for a particular
108+ random seed. I.e. the value may differ across interpreter invocations
109+ but remains constant in same interpreter.
110+ """
111+ first = make_simple_hash (DUMMY_WEAK_PW )
112+ second = make_simple_hash (DUMMY_WEAK_PW )
113+ self .assertEqual (first , second , "simple hashing is not constant" )
114+
115+ def test_make_hash_fixed_seed (self ):
69116 """Test basic hashing of a fixed string to be constant for a fixed
70117 random seed.
71118 """
72- actual = make_hash (DUMMY_PW , _urandom = lambda vlen : b'0' * vlen )
73- self .assertEqual (actual , DUMMY_PW_HASH , "mismatch hashing string" )
119+ expected = DUMMY_WEAK_PW_PBKDF2
120+ actual = make_hash (DUMMY_WEAK_PW , _urandom = lambda vlen : b'0' * vlen )
121+ self .assertEqual (actual , expected , "mismatch hashing string" )
122+
123+ def test_make_hash_constant_string (self ):
124+ """Test basic hashing of a fixed string to be constant for a particular
125+ random seed. I.e. the value may differ across interpreter invocations
126+ but remains constant in same interpreter.
127+ """
128+ first = make_hash (
129+ DUMMY_WEAK_PW , _urandom = lambda vlen : DUMMY_SALT [:vlen ])
130+ second = make_hash (
131+ DUMMY_WEAK_PW , _urandom = lambda vlen : DUMMY_SALT [:vlen ])
132+ self .assertEqual (first , second , "hashing is not constant" )
133+
134+ def test_check_hash_reject_weak (self ):
135+ """Test basic hash checking of a constant weak complexity password"""
136+ expected = make_hash (DUMMY_WEAK_PW )
137+ result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
138+ DUMMY_WEAK_PW , expected , strict_policy = True )
139+ self .assertFalse (result , "check hash should fail on weak pw" )
140+
141+ def test_check_hash_reject_medium_without_legacy (self ):
142+ """Test basic hash checking of a constant medium complexity password
143+ without legacy password support.
144+ """
145+ expected = make_hash (DUMMY_MEDIUM_PW )
146+ result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
147+ DUMMY_MEDIUM_PW , expected , strict_policy = True ,
148+ allow_legacy = False )
149+ self .assertFalse (result , "check hash strict should fail on medium pw" )
150+
151+ def test_check_hash_accept_medium_with_legacy (self ):
152+ """Test basic hash checking of a constant medium complexity password
153+ with legacy password support.
154+ """
155+ expected = make_hash (DUMMY_MEDIUM_PW )
156+ result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
157+ DUMMY_MEDIUM_PW , expected , strict_policy = True ,
158+ allow_legacy = True )
159+ self .assertTrue (result , "check hash with legacy must accept medium pw" )
160+
161+ def test_check_hash_accept_high (self ):
162+ """Test basic hash checking of a constant high complexity password
163+ without legacy password support.
164+ """
165+ expected = make_hash (DUMMY_HIGH_PW )
166+ self .dummy_conf .site_password_policy = POLICY_HIGH
167+ result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
168+ DUMMY_HIGH_PW , expected , strict_policy = True ,
169+ allow_legacy = False )
170+ self .assertTrue (result , "check hash must accept high complexity pw" )
171+
172+ def test_check_hash_accept_modern (self ):
173+ """Test basic hash checking of a constant modern complexity password
174+ without legacy password support.
175+ """
176+ expected = make_hash (DUMMY_MODERN_PW )
177+ result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
178+ DUMMY_MODERN_PW , expected , strict_policy = True ,
179+ allow_legacy = False )
180+ self .assertTrue (result , "check hash must accept modern complexity pw" )
74181
75- def test_check_hash (self ):
76- """Test basic hash checking of a fixed string"""
182+ def test_check_hash_constant (self ):
183+ """Test basic hash checking of a constant string"""
184+ expected = make_hash (DUMMY_MEDIUM_PW )
185+ result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
186+ DUMMY_MEDIUM_PW , expected , strict_policy = True )
187+ self .assertFalse (result , "check hash should reject medium pw" )
188+ result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
189+ DUMMY_MEDIUM_PW , expected , strict_policy = False ,
190+ allow_legacy = True )
191+ self .assertTrue (result , "check hash failed medium pw when not strict" )
192+ expected = make_hash (DUMMY_MODERN_PW )
193+ result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
194+ DUMMY_MODERN_PW , expected , strict_policy = True )
195+ self .assertTrue (result , "check hash failed modern pw" )
77196
197+ def test_check_hash_random (self ):
198+ """Test basic hash checking of a random string"""
78199 random_pw = generate_random_password (self .dummy_conf )
79200 expected = make_hash (random_pw )
80201 result = check_hash (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
81202 random_pw , expected )
203+ self .assertTrue (result , "mismatch in random hash check" )
204+
205+ def test_check_digest (self ):
206+ """Test basic digest checking of a random string"""
207+ random_pw = generate_random_password (self .dummy_conf )
208+ expected = make_digest (DUMMY_REALM , DUMMY_USER ,
209+ DUMMY_MODERN_PW , DUMMY_SALT )
210+ result = check_digest (self .dummy_conf , DUMMY_SERVICE , DUMMY_REALM ,
211+ DUMMY_USER , DUMMY_MODERN_PW , expected ,
212+ DUMMY_SALT )
213+ self .assertTrue (result , "mismatch in digest check" )
82214
83- self . assertTrue ( result , "mismatch in hash check" )
215+ # TODO: migrate inline checks from module here instead
84216
85217
86218if __name__ == '__main__' :
0 commit comments