Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 23 additions & 7 deletions Lib/test/test_hashlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,18 @@ def test_usedforsecurity_false(self):
self._hashlib.new("md5", usedforsecurity=False)
self._hashlib.openssl_md5(usedforsecurity=False)

@unittest.skipUnless(get_fips_mode(), "requires FIPS mode")
def test_md5_usedforsecurity_in_fips_mode(self):
# In FIPS mode, OpenSSL must allow MD5 only with usedforsecurity=False.
if self._hashlib is None:
self.skipTest("OpenSSL hashlib is not available")
self._hashlib.new("md5", usedforsecurity=False)
self._hashlib.openssl_md5(usedforsecurity=False)
with self.assertRaises(ValueError):
self._hashlib.new("md5", usedforsecurity=True)
with self.assertRaises(ValueError):
self._hashlib.openssl_md5(usedforsecurity=True)

@unittest.skipIf(get_fips_mode(), "skip in FIPS mode")
def test_clinic_signature(self):
for constructor in self.hash_constructors:
Expand Down Expand Up @@ -512,13 +524,17 @@ def check(self, name, data, hexdigest, shake=False, **kwargs):

def check_file_digest(self, name, data, hexdigest):
hexdigest = hexdigest.lower()
try:
hashlib.new(name)
except ValueError:
# skip, algorithm is blocked by security policy.
return
digests = [name]
digests.extend(self.constructors_to_test[name])
digests = []
for digest in [name, *self.constructors_to_test[name]]:
try:
if callable(digest):
digest(b"")
else:
hashlib.new(digest)
except ValueError:
# skip, algorithm is blocked by security policy.
continue
digests.append(digest)

with tempfile.TemporaryFile() as f:
f.write(data)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Fix :mod:`hashlib` in OpenSSL FIPS mode: the :mod:`!_hashlib` digest lookup
introduced by the fix for :gh:`149816` used the FIPS property query the wrong
way round, so ``usedforsecurity=False`` digests such as MD5 failed to load and
``usedforsecurity=True`` ones were no longer refused. Python then silently
fell back to the built-in implementation of the algorithm.
3 changes: 3 additions & 0 deletions Modules/_hashopenssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,9 @@ py_digest_by_name(PyObject *module, const char *name, enum Py_hash_type py_ht)
if (fips == -1) {
goto finally;
}
// 'fips' must be 1 for usedforsecurity=True: the opposite of what
// disable_fips_property() returns.
fips = !fips;

if (entry != NULL) {
PY_EVP_MD **cached = fips ? &entry->evp : &entry->evp_nosecurity;
Expand Down
Loading