4646DUMMY_HIGH_PW = 'QZFnp7I-GZ'
4747DUMMY_MODERN_PW = 'QZFnCp7hmI1G'
4848DUMMY_WEAK_PW_MD5 = "3858f62230ac3c915f300c664312c63f"
49+ DUMMY_WEAK_PW_SHA256 = \
50+ "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"
4951DUMMY_WEAK_PW_PBKDF2 = \
5052 "PBKDF2$sha256$10000$MDAwMDAwMDAwMDAw$epib2rEg/HYTQZFnCp7hmIGZ6rzHnViy"
5153DUMMY_HOME_DIR = 'dummy_user_home'
5254DUMMY_SETTINGS_DIR = 'dummy_user_settings'
53- DUMMY_SERVICE = 'dummy-svc'
55+ # TODO: adjust password reset token helpers to handle configured services
56+ # it currently silently fails if not in migoid(c) or migcert
57+ # DUMMY_SERVICE = 'dummy-svc'
58+ DUMMY_SERVICE = 'migoid'
5459DUMMY_REALM = 'dummy-realm'
5560DUMMY_SALT = base64 .b16encode (os .urandom (16 ))
5661
@@ -74,6 +79,7 @@ def before_each(self):
7479 site_crypto_salt = DUMMY_SALT ,
7580 site_password_salt = DUMMY_SALT ,
7681 site_digest_salt = DUMMY_SALT ,
82+ site_login_methods = [DUMMY_SERVICE ],
7783 )
7884
7985 def test_best_crypt_salt (self ):
@@ -97,6 +103,15 @@ def test_best_crypt_salt(self):
97103 pass
98104 self .assertTrue (actual is None , "best crypt salt failed to err" )
99105
106+ def test_valid_login_password (self ):
107+ """Test valid login password checker which assures password strength"""
108+ allow_weak = valid_login_password (self .dummy_conf , DUMMY_WEAK_PW )
109+ self .assertFalse (allow_weak , "allowed login with weak pw" )
110+ allow_medium = valid_login_password (self .dummy_conf , DUMMY_MEDIUM_PW )
111+ self .assertTrue (allow_medium , "refused login with medium pw" )
112+ allow_modern = valid_login_password (self .dummy_conf , DUMMY_MODERN_PW )
113+ self .assertTrue (allow_modern , "refused login with modern pw" )
114+
100115 def test_make_simple_hash_fixed_seed (self ):
101116 """Test basic hashing of a fixed string to be constant"""
102117 expected = DUMMY_WEAK_PW_MD5
@@ -112,6 +127,21 @@ def test_make_simple_hash_constant_string(self):
112127 second = make_simple_hash (DUMMY_WEAK_PW )
113128 self .assertEqual (first , second , "simple hashing is not constant" )
114129
130+ def test_make_safe_hash_fixed_seed (self ):
131+ """Test basic hashing of a fixed string to be constant"""
132+ expected = DUMMY_WEAK_PW_SHA256
133+ actual = make_safe_hash (DUMMY_WEAK_PW )
134+ self .assertEqual (actual , expected , "mismatch safe hash string" )
135+
136+ def test_make_safe_hash_constant_string (self ):
137+ """Test basic hashing of a fixed string to be constant for a particular
138+ random seed. I.e. the value may differ across interpreter invocations
139+ but remains constant in same interpreter.
140+ """
141+ first = make_safe_hash (DUMMY_WEAK_PW )
142+ second = make_safe_hash (DUMMY_WEAK_PW )
143+ self .assertEqual (first , second , "safe hashing is not constant" )
144+
115145 def test_make_hash_fixed_seed (self ):
116146 """Test basic hashing of a fixed string to be constant for a fixed
117147 random seed.
@@ -212,7 +242,98 @@ def test_check_digest(self):
212242 DUMMY_SALT )
213243 self .assertTrue (result , "mismatch in digest check" )
214244
215- # TODO: migrate inline checks from module here instead
245+ def test_check_scramble (self ):
246+ """Test basic scramble checking of a random string"""
247+ random_pw = generate_random_password (self .dummy_conf )
248+ expected = make_scramble (DUMMY_MODERN_PW , DUMMY_SALT )
249+ result = check_scramble (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
250+ DUMMY_MODERN_PW , expected , DUMMY_SALT )
251+ self .assertTrue (result , "mismatch in scramble check" )
252+
253+ def test_fernet_encrypt_decrypt (self ):
254+ """Test basic fernet password encrypt and decrypt on a random string"""
255+ random_pw = generate_random_password (self .dummy_conf )
256+ expected = fernet_encrypt_password (self .dummy_conf , random_pw )
257+ result = fernet_decrypt_password (self .dummy_conf , expected )
258+ self .assertEqual (random_pw , result , "failed fernet enc+dec" )
259+
260+ def test_aesgcm_encrypt_decrypt (self ):
261+ """Test basic aesgcm password encrypt and decrypt on a random string"""
262+ random_pw = generate_random_password (self .dummy_conf )
263+ expected = aesgcm_encrypt_password (self .dummy_conf , random_pw )
264+ result = aesgcm_decrypt_password (self .dummy_conf , expected )
265+ self .assertEqual (random_pw , result , "failed aesgcm enc+dec" )
266+
267+ def test_aesgcm_encrypt_decrypt_static_iv (self ):
268+ """Test basic aesgcm password encrypt and decrypt on a random string
269+ with a fixed initialization vector.
270+ """
271+ random_pw = generate_random_password (self .dummy_conf )
272+ entropy = make_safe_hash (random_pw , False )
273+ static_iv = prepare_aesgcm_iv (self .dummy_conf , iv_entropy = entropy )
274+ expected = aesgcm_encrypt_password (self .dummy_conf , random_pw ,
275+ init_vector = static_iv )
276+ result = aesgcm_decrypt_password (self .dummy_conf , expected ,
277+ init_vector = static_iv )
278+ self .assertEqual (random_pw , result , "failed aesgcm static iv enc+dec" )
279+
280+ def test_make_encrypt_decrypt (self ):
281+ """Test default make encrypt and decrypt on a random string"""
282+ random_pw = generate_random_password (self .dummy_conf )
283+ expected = make_encrypt (self .dummy_conf , random_pw )
284+ result = make_decrypt (self .dummy_conf , expected )
285+ self .assertEqual (random_pw , result , "failed default enc+dec" )
286+
287+ def test_make_encrypt_variation (self ):
288+ """Test make encrypt output variation on a random string"""
289+ random_pw = generate_random_password (self .dummy_conf )
290+ # IMPORTANT: only aesgcm_static generates constant enc value!
291+ first = make_encrypt (self .dummy_conf , random_pw , algo = "fernet" )
292+ second = make_encrypt (self .dummy_conf , random_pw , algo = "fernet" )
293+ self .assertNotEqual (first , second , "aesgcm enc must not be constant" )
294+ first = make_encrypt (self .dummy_conf , random_pw , algo = "aesgcm" )
295+ second = make_encrypt (self .dummy_conf , random_pw , algo = "aesgcm" )
296+ self .assertNotEqual (first , second , "fernet enc must not be constant" )
297+ first = make_encrypt (self .dummy_conf , random_pw , algo = "aesgcm_static" )
298+ second = make_encrypt (self .dummy_conf , random_pw , algo = "aesgcm_static" )
299+ self .assertEqual (first , second , "aesgcm_static enc must be constant" )
300+
301+ def test_check_encrypt (self ):
302+ """Test basic password simple encrypt and decrypt on a random string"""
303+ random_pw = generate_random_password (self .dummy_conf )
304+ # IMPORTANT: only aesgcm_static generates constant enc value!
305+ encrypted = make_encrypt (self .dummy_conf , random_pw ,
306+ algo = "fernet" )
307+ result = check_encrypt (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
308+ random_pw , encrypted , algo = 'fernet' )
309+ self .assertFalse (result , "invalid match in fernet encrypt check" )
310+ encrypted = make_encrypt (self .dummy_conf , random_pw , algo = "aesgcm" )
311+ result = check_encrypt (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
312+ random_pw , encrypted , algo = 'aesgcm' )
313+ self .assertFalse (result , "invalid match in aesgcm encrypt check" )
314+ encrypted = make_encrypt (self .dummy_conf , random_pw ,
315+ algo = "aesgcm_static" )
316+ result = check_encrypt (self .dummy_conf , DUMMY_SERVICE , DUMMY_USER ,
317+ random_pw , encrypted , algo = 'aesgcm_static' )
318+ self .assertTrue (result , "mismatch in aesgcm_static encrypt check" )
319+
320+ def test_password_reset_token (self ):
321+ """Test basic password reset token handling on a random string"""
322+ random_pw = generate_random_password (self .dummy_conf )
323+ hashed_pw = make_hash (random_pw )
324+ dummy_user = {'distinguished_name' : DUMMY_USER }
325+ dummy_user ['password_hash' ] = hashed_pw
326+ timestamp = 42
327+ expected = generate_reset_token (self .dummy_conf , dummy_user ,
328+ DUMMY_SERVICE , timestamp )
329+ parsed = parse_reset_token (self .dummy_conf , expected , DUMMY_SERVICE )
330+ self .assertEqual (parsed [0 ], timestamp , "failed parse token time" )
331+ self .assertEqual (parsed [1 ], hashed_pw , "failed parse token hash" )
332+ result = verify_reset_token (self .dummy_conf , dummy_user , expected ,
333+ DUMMY_SERVICE , timestamp )
334+ self .assertTrue (result , "failed password reset token handling" )
335+
336+ # TODO: migrate remaining inline checks from module here instead
216337
217338
218339if __name__ == '__main__' :
0 commit comments