Skip to content

Commit 61c9ae3

Browse files
committed
Extend pwcrypto unit tests to cover:
* make_safe_hash (sha256) * valid_login_password * make_scramble+check_scramble * make_encrypt+make_decrypt for fernet, aesgcm and aesgcm_static * make_encrypt output variation for fernet, aesgcm and aesgcm_static * make_encrypt+check_encrypt for fernet, aesgcm and aesgcm_static * generate/parse/verify_reset_token
1 parent d9be6c2 commit 61c9ae3

File tree

1 file changed

+123
-2
lines changed

1 file changed

+123
-2
lines changed

tests/test_mig_shared_pwcrypto.py

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,16 @@
4646
DUMMY_HIGH_PW = 'QZFnp7I-GZ'
4747
DUMMY_MODERN_PW = 'QZFnCp7hmI1G'
4848
DUMMY_WEAK_PW_MD5 = "3858f62230ac3c915f300c664312c63f"
49+
DUMMY_WEAK_PW_SHA256 = \
50+
"c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2"
4951
DUMMY_WEAK_PW_PBKDF2 = \
5052
"PBKDF2$sha256$10000$MDAwMDAwMDAwMDAw$epib2rEg/HYTQZFnCp7hmIGZ6rzHnViy"
5153
DUMMY_HOME_DIR = 'dummy_user_home'
5254
DUMMY_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'
5459
DUMMY_REALM = 'dummy-realm'
5560
DUMMY_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

218339
if __name__ == '__main__':

0 commit comments

Comments
 (0)