Skip to content

Commit 59d9a85

Browse files
committed
Refactor test_hashlib for better usedforsecurity & openssl fips mode environment support.
1 parent 7ea523f commit 59d9a85

File tree

1 file changed

+25
-20
lines changed

1 file changed

+25
-20
lines changed

Lib/test/test_hashlib.py

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ def get_fips_mode():
6666
SKIP_SHA3 = support.check_sanitizer(ub=True)
6767
requires_sha3 = unittest.skipUnless(not SKIP_SHA3, 'requires _sha3')
6868

69+
requires_usedforsecurity = unittest.skipIf(
70+
get_fips_mode(),
71+
"If an OpenSSL FIPS mode configuration has disabled any algorithms"+
72+
" in the default provider, this test would fail."
73+
)
6974

7075
def hexstr(s):
7176
assert isinstance(s, bytes), repr(s)
@@ -102,6 +107,7 @@ class HashLibTestCase(unittest.TestCase):
102107
'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
103108
'shake_128', 'shake_256')
104109

110+
blakes = {'blake2b', 'blake2s'}
105111
shakes = {'shake_128', 'shake_256'}
106112

107113
# gh-58898: Fallback modules are always compiled under POSIX.
@@ -121,9 +127,12 @@ def __init__(self, *args, **kwargs):
121127
for algorithm in self.supported_hash_names:
122128
algorithms.add(algorithm.lower())
123129

130+
# blake2s and blake2b *require* the _blake2 builtin.
124131
_blake2 = self._conditional_import_module('_blake2')
125132
if _blake2:
126-
algorithms.update({'blake2b', 'blake2s'})
133+
algorithms.update(self.blakes)
134+
else:
135+
algorithms.difference_update(self.blakes)
127136

128137
self.constructors_to_test = {}
129138
for algorithm in algorithms:
@@ -196,10 +205,6 @@ def hash_constructors(self):
196205
constructors = self.constructors_to_test.values()
197206
return itertools.chain.from_iterable(constructors)
198207

199-
@property
200-
def is_fips_mode(self):
201-
return get_fips_mode()
202-
203208
def test_hash_array(self):
204209
a = array.array("b", range(10))
205210
for cons in self.hash_constructors:
@@ -222,10 +227,9 @@ def test_algorithms_available(self):
222227
for name in hashlib.algorithms_available:
223228
digest = hashlib.new(name, usedforsecurity=False)
224229

230+
@requires_usedforsecurity
225231
def test_usedforsecurity_true(self):
226232
hashlib.new("sha256", usedforsecurity=True)
227-
if self.is_fips_mode:
228-
self.skipTest("skip in FIPS mode")
229233
for cons in self.hash_constructors:
230234
cons(usedforsecurity=True)
231235
cons(b'', usedforsecurity=True)
@@ -251,7 +255,7 @@ def test_unknown_hash(self):
251255
self.assertRaises(TypeError, hashlib.new, 1)
252256

253257
def test_new_upper_to_lower(self):
254-
self.assertEqual(hashlib.new("SHA256").name, "sha256")
258+
self.assertEqual(hashlib.new("SHA256", usedforsecurity=False).name, "sha256")
255259

256260
def test_get_builtin_constructor(self):
257261
get_builtin_constructor = getattr(hashlib,
@@ -309,10 +313,6 @@ def test_name_attribute(self):
309313
for cons in self.hash_constructors:
310314
h = cons(usedforsecurity=False)
311315
self.assertIsInstance(h.name, str)
312-
if h.name in self.supported_hash_names:
313-
self.assertIn(h.name, self.supported_hash_names)
314-
else:
315-
self.assertNotIn(h.name, self.supported_hash_names)
316316
self.assertEqual(
317317
h.name,
318318
hashlib.new(h.name, usedforsecurity=False).name
@@ -353,7 +353,7 @@ def test_large_update(self):
353353
@requires_resource('cpu')
354354
def test_sha256_update_over_4gb(self):
355355
zero_1mb = b"\0" * 1024 * 1024
356-
h = hashlib.sha256()
356+
h = hashlib.sha256(usedforsecurity=False)
357357
for i in range(0, 4096):
358358
h.update(zero_1mb)
359359
h.update(b"hello world")
@@ -362,24 +362,27 @@ def test_sha256_update_over_4gb(self):
362362
@requires_resource('cpu')
363363
def test_sha3_256_update_over_4gb(self):
364364
zero_1mb = b"\0" * 1024 * 1024
365-
h = hashlib.sha3_256()
365+
h = hashlib.sha3_256(usedforsecurity=False)
366366
for i in range(0, 4096):
367367
h.update(zero_1mb)
368368
h.update(b"hello world")
369369
self.assertEqual(h.hexdigest(), "e2d4535e3b613135c14f2fe4e026d7ad8d569db44901740beffa30d430acb038")
370370

371+
@requires_blake2
371372
@requires_resource('cpu')
372373
def test_blake2_update_over_4gb(self):
373374
# blake2s or blake2b doesn't matter based on how our C code is structured, this tests the
374375
# common loop macro logic.
375376
zero_1mb = b"\0" * 1024 * 1024
376-
h = hashlib.blake2s()
377+
h = hashlib.blake2s(usedforsecurity=False)
377378
for i in range(0, 4096):
378379
h.update(zero_1mb)
379380
h.update(b"hello world")
380381
self.assertEqual(h.hexdigest(), "8a268e83dd30528bc0907fa2008c91de8f090a0b6e0e60a5ff0d999d8485526f")
381382

382383
def check(self, name, data, hexdigest, shake=False, **kwargs):
384+
if 'usedforsecurity' not in kwargs:
385+
kwargs['usedforsecurity'] = False
383386
length = len(hexdigest)//2
384387
hexdigest = hexdigest.lower()
385388
constructors = self.constructors_to_test[name]
@@ -404,6 +407,8 @@ def check(self, name, data, hexdigest, shake=False, **kwargs):
404407
# skip shake and blake2 extended parameter tests
405408
self.check_file_digest(name, data, hexdigest)
406409

410+
# defaults True because file_digest doesn't support the parameter.
411+
@requires_usedforsecurity
407412
def check_file_digest(self, name, data, hexdigest):
408413
hexdigest = hexdigest.lower()
409414
try:
@@ -434,7 +439,8 @@ def check_no_unicode(self, algorithm_name):
434439
# Unicode objects are not allowed as input.
435440
constructors = self.constructors_to_test[algorithm_name]
436441
for hash_object_constructor in constructors:
437-
self.assertRaises(TypeError, hash_object_constructor, 'spam')
442+
with self.assertRaises(TypeError):
443+
hash_object_constructor('spam', usedforsecurity=False)
438444

439445
def test_no_unicode(self):
440446
self.check_no_unicode('md5')
@@ -497,7 +503,7 @@ def test_blocksize_name_sha3(self):
497503
def check_sha3(self, name, capacity, rate, suffix):
498504
constructors = self.constructors_to_test[name]
499505
for hash_object_constructor in constructors:
500-
m = hash_object_constructor()
506+
m = hash_object_constructor(usedforsecurity=False)
501507
if HASH is not None and isinstance(m, HASH):
502508
# _hashopenssl's variant does not have extra SHA3 attributes
503509
continue
@@ -661,7 +667,7 @@ def check_blake2(self, constructor, salt_size, person_size, key_size,
661667
digest_size, max_offset):
662668
self.assertEqual(constructor.SALT_SIZE, salt_size)
663669
for i in range(salt_size + 1):
664-
constructor(salt=b'a' * i)
670+
constructor(salt=b'a' * i, usedforsecurity=False)
665671
salt = b'a' * (salt_size + 1)
666672
self.assertRaises(ValueError, constructor, salt=salt)
667673

@@ -975,8 +981,7 @@ def hash_in_chunks(chunk_size):
975981
self.assertEqual(expected_hash, hasher.hexdigest())
976982

977983
def test_get_fips_mode(self):
978-
fips_mode = self.is_fips_mode
979-
if fips_mode is not None:
984+
if (fips_mode := get_fips_mode()) is not None:
980985
self.assertIsInstance(fips_mode, int)
981986

982987
@support.cpython_only

0 commit comments

Comments
 (0)