Blame SOURCES/00329-fips.patch

a08450
From 355e975a386b60d787b98cc4cd08b98f876ff858 Mon Sep 17 00:00:00 2001
a08450
From: "Miss Islington (bot)"
a08450
 <31488909+miss-islington@users.noreply.github.com>
a08450
Date: Mon, 7 Feb 2022 00:08:10 -0800
a08450
Subject: [PATCH 01/11] bpo-40479: Fix undefined behavior in
a08450
 Modules/_hashopenssl.c (GH-31153)
a08450
a08450
va_end() must be called before returning.
a08450
(cherry picked from commit 59e004af63742361b67d1e1ae70229ff0db1059d)
a08450
a08450
Co-authored-by: Zackery Spytz <zspytz@gmail.com>
a08450
---
a08450
 .../Core and Builtins/2022-02-06-23-08-30.bpo-40479.zED3Zu.rst   | 1 +
a08450
 Modules/_hashopenssl.c                                           | 1 +
a08450
 2 files changed, 2 insertions(+)
a08450
 create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-02-06-23-08-30.bpo-40479.zED3Zu.rst
a08450
a08450
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-02-06-23-08-30.bpo-40479.zED3Zu.rst b/Misc/NEWS.d/next/Core and Builtins/2022-02-06-23-08-30.bpo-40479.zED3Zu.rst
a08450
new file mode 100644
a08450
index 00000000000..52701d53d8f
a08450
--- /dev/null
a08450
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-02-06-23-08-30.bpo-40479.zED3Zu.rst
a08450
@@ -0,0 +1 @@
a08450
+Add a missing call to ``va_end()`` in ``Modules/_hashopenssl.c``.
a08450
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
a08450
index a4889450821..4873bb11aa0 100644
a08450
--- a/Modules/_hashopenssl.c
a08450
+++ b/Modules/_hashopenssl.c
a08450
@@ -311,6 +311,7 @@ _setException(PyObject *exc, const char* altmsg, ...)
a08450
         } else {
a08450
             PyErr_FormatV(exc, altmsg, vargs);
a08450
         }
a08450
+        va_end(vargs);
a08450
         return NULL;
a08450
     }
a08450
     va_end(vargs);
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From b906713972396823c9e2e04421f9dbcfdc6a6c94 Mon Sep 17 00:00:00 2001
a08450
From: Petr Viktorin <encukou@gmail.com>
a08450
Date: Wed, 11 Aug 2021 16:51:03 +0200
a08450
Subject: [PATCH 02/11] Backport PyModule_AddObjectRef as
a08450
 _PyModule_AddObjectRef
a08450
a08450
Having PyModule_AddObjectRef available should make backporting
a08450
newer patches easier. The new API is much safer.
a08450
The backport adds an underscore so that we don't break extension
a08450
modules that define PyModule_AddObjectRef themselves on Python<=3.9
a08450
(which would be a virtuous thing to do).
a08450
---
a08450
 Include/modsupport.h | 10 ++++++++++
a08450
 Python/modsupport.c  | 13 +++++++++++--
a08450
 2 files changed, 21 insertions(+), 2 deletions(-)
a08450
a08450
diff --git a/Include/modsupport.h b/Include/modsupport.h
a08450
index 4c4aab6..d9fac52 100644
a08450
--- a/Include/modsupport.h
a08450
+++ b/Include/modsupport.h
a08450
@@ -136,7 +136,17 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords(
a08450
 void _PyArg_Fini(void);
a08450
 #endif   /* Py_LIMITED_API */
a08450
 
a08450
+// Add an attribute with name 'name' and value 'obj' to the module 'mod.
a08450
+// On success, return 0 on success.
a08450
+// On error, raise an exception and return -1.
a08450
+// Backported from Python 3.10, where it's available without the underscore
a08450
+// in the name, to ease porting patches to RHEL
a08450
+PyAPI_FUNC(int) _PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value);
a08450
+
a08450
+// Similar to PyModule_AddObjectRef() but steal a reference to 'obj'
a08450
+// (Py_DECREF(obj)) on success (if it returns 0).
a08450
 PyAPI_FUNC(int) PyModule_AddObject(PyObject *, const char *, PyObject *);
a08450
+
a08450
 PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long);
a08450
 PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char *);
a08450
 #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000
a08450
diff --git a/Python/modsupport.c b/Python/modsupport.c
a08450
index 13482c6..fca1083 100644
a08450
--- a/Python/modsupport.c
a08450
+++ b/Python/modsupport.c
a08450
@@ -631,7 +631,7 @@ va_build_stack(PyObject **small_stack, Py_ssize_t small_stack_len,
a08450
 
a08450
 
a08450
 int
a08450
-PyModule_AddObject(PyObject *m, const char *name, PyObject *o)
a08450
+_PyModule_AddObjectRef(PyObject *m, const char *name, PyObject *o)
a08450
 {
a08450
     PyObject *dict;
a08450
     if (!PyModule_Check(m)) {
a08450
@@ -655,10 +655,19 @@ PyModule_AddObject(PyObject *m, const char *name, PyObject *o)
a08450
     }
a08450
     if (PyDict_SetItemString(dict, name, o))
a08450
         return -1;
a08450
-    Py_DECREF(o);
a08450
     return 0;
a08450
 }
a08450
 
a08450
+int
a08450
+PyModule_AddObject(PyObject *mod, const char *name, PyObject *value)
a08450
+{
a08450
+    int res = _PyModule_AddObjectRef(mod, name, value);
a08450
+    if (res == 0) {
a08450
+        Py_DECREF(value);
a08450
+    }
a08450
+    return res;
a08450
+}
a08450
+
a08450
 int
a08450
 PyModule_AddIntConstant(PyObject *m, const char *name, long value)
a08450
 {
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From c67b383ffd3ccacedacbeb91c3bdeaf5f829ca09 Mon Sep 17 00:00:00 2001
a08450
From: Petr Viktorin <encukou@gmail.com>
a08450
Date: Fri, 13 Aug 2021 13:16:43 +0200
a08450
Subject: [PATCH 03/11] _hashopenssl: Uncomment and use initialization function
a08450
 list
a08450
a08450
This simplifies backporting of future changes.
a08450
a08450
We use this change instead of Python 3.10's:
a08450
    bpo-1635741: Port _hashlib to multiphase initialization (GH-23358)
a08450
---
a08450
 Modules/_hashopenssl.c | 30 +++++-------------------------
a08450
 1 file changed, 5 insertions(+), 25 deletions(-)
a08450
a08450
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
a08450
index a488945..62cf769 100644
a08450
--- a/Modules/_hashopenssl.c
a08450
+++ b/Modules/_hashopenssl.c
a08450
@@ -2216,7 +2216,6 @@ hashlib_init_hmactype(PyObject *module)
a08450
     return 0;
a08450
 }
a08450
 
a08450
-#if 0
a08450
 static PyModuleDef_Slot hashlib_slots[] = {
a08450
     /* OpenSSL 1.0.2 and LibreSSL */
a08450
     {Py_mod_exec, hashlib_openssl_legacy_init},
a08450
@@ -2227,7 +2226,6 @@ static PyModuleDef_Slot hashlib_slots[] = {
a08450
     {Py_mod_exec, hashlib_md_meth_names},
a08450
     {0, NULL}
a08450
 };
a08450
-#endif
a08450
 
a08450
 static struct PyModuleDef _hashlibmodule = {
a08450
     PyModuleDef_HEAD_INIT,
a08450
@@ -2255,29 +2253,11 @@ PyInit__hashlib(void)
a08450
         return NULL;
a08450
     }
a08450
 
a08450
-    if (hashlib_openssl_legacy_init(m) < 0) {
a08450
-        Py_DECREF(m);
a08450
-        return NULL;
a08450
-    }
a08450
-    if (hashlib_init_hashtable(m) < 0) {
a08450
-        Py_DECREF(m);
a08450
-        return NULL;
a08450
-    }
a08450
-    if (hashlib_init_evptype(m) < 0) {
a08450
-        Py_DECREF(m);
a08450
-        return NULL;
a08450
-    }
a08450
-    if (hashlib_init_evpxoftype(m) < 0) {
a08450
-        Py_DECREF(m);
a08450
-        return NULL;
a08450
-    }
a08450
-    if (hashlib_init_hmactype(m) < 0) {
a08450
-        Py_DECREF(m);
a08450
-        return NULL;
a08450
-    }
a08450
-    if (hashlib_md_meth_names(m) == -1) {
a08450
-        Py_DECREF(m);
a08450
-        return NULL;
a08450
+    for (int i=0; hashlib_slots[i].slot; i++) {
a08450
+        if (((int (*)(PyObject*))hashlib_slots[i].value)(m) < 0) {
a08450
+            Py_DECREF(m);
a08450
+            return NULL;
a08450
+        }
a08450
     }
a08450
 
a08450
     return m;
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From c49c1416d22fffc78204d66987f40e6d17a95c01 Mon Sep 17 00:00:00 2001
a08450
From: Christian Heimes <christian@python.org>
a08450
Date: Sat, 27 Mar 2021 14:55:03 +0100
a08450
Subject: [PATCH 04/11] bpo-40645: use C implementation of HMAC (GH-24920,
a08450
 GH-25063, GH-26079)
a08450
a08450
This backports the feature and 2 subsequent bugfixes
a08450
from: https://bugs.python.org/issue40645
a08450
a08450
Signed-off-by: Christian Heimes <christian@python.org>
a08450
Co-authored-by: Erlend Egeberg Aasland <erlend.aasland@innova.no>
a08450
Co-Authored-By: Pablo Galindo <Pablogsal@gmail.com>
a08450
---
a08450
 Lib/hashlib.py                                |   1 +
a08450
 Lib/hmac.py                                   |  86 ++++++----
a08450
 Lib/test/test_hmac.py                         | 114 +++++++------
a08450
 .../2021-03-19-10-22-17.bpo-40645.5pXhb-.rst  |   2 +
a08450
 Modules/_hashopenssl.c                        | 150 ++++++++++++++++--
a08450
 Modules/clinic/_hashopenssl.c.h               |  38 +----
a08450
 6 files changed, 265 insertions(+), 126 deletions(-)
a08450
 create mode 100644 Misc/NEWS.d/next/Library/2021-03-19-10-22-17.bpo-40645.5pXhb-.rst
a08450
a08450
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
a08450
index 58c340d..ffa3be0 100644
a08450
--- a/Lib/hashlib.py
a08450
+++ b/Lib/hashlib.py
a08450
@@ -173,6 +173,7 @@ try:
a08450
     algorithms_available = algorithms_available.union(
a08450
             _hashlib.openssl_md_meth_names)
a08450
 except ImportError:
a08450
+    _hashlib = None
a08450
     new = __py_new
a08450
     __get_hash = __get_builtin_constructor
a08450
 
a08450
diff --git a/Lib/hmac.py b/Lib/hmac.py
a08450
index 180bc37..8b4f920 100644
a08450
--- a/Lib/hmac.py
a08450
+++ b/Lib/hmac.py
a08450
@@ -8,11 +8,12 @@ try:
a08450
     import _hashlib as _hashopenssl
a08450
 except ImportError:
a08450
     _hashopenssl = None
a08450
-    _openssl_md_meths = None
a08450
+    _functype = None
a08450
     from _operator import _compare_digest as compare_digest
a08450
 else:
a08450
-    _openssl_md_meths = frozenset(_hashopenssl.openssl_md_meth_names)
a08450
     compare_digest = _hashopenssl.compare_digest
a08450
+    _functype = type(_hashopenssl.openssl_sha256)  # builtin type
a08450
+
a08450
 import hashlib as _hashlib
a08450
 
a08450
 trans_5C = bytes((x ^ 0x5C) for x in range(256))
a08450
@@ -23,7 +24,6 @@ trans_36 = bytes((x ^ 0x36) for x in range(256))
a08450
 digest_size = None
a08450
 
a08450
 
a08450
-
a08450
 class HMAC:
a08450
     """RFC 2104 HMAC class.  Also complies with RFC 4231.
a08450
 
a08450
@@ -32,7 +32,7 @@ class HMAC:
a08450
     blocksize = 64  # 512-bit HMAC; can be changed in subclasses.
a08450
 
a08450
     __slots__ = (
a08450
-        "_digest_cons", "_inner", "_outer", "block_size", "digest_size"
a08450
+        "_hmac", "_inner", "_outer", "block_size", "digest_size"
a08450
     )
a08450
 
a08450
     def __init__(self, key, msg=None, digestmod=''):
a08450
@@ -55,15 +55,30 @@ class HMAC:
a08450
         if not digestmod:
a08450
             raise TypeError("Missing required parameter 'digestmod'.")
a08450
 
a08450
+        if _hashopenssl and isinstance(digestmod, (str, _functype)):
a08450
+            try:
a08450
+                self._init_hmac(key, msg, digestmod)
a08450
+            except _hashopenssl.UnsupportedDigestmodError:
a08450
+                self._init_old(key, msg, digestmod)
a08450
+        else:
a08450
+            self._init_old(key, msg, digestmod)
a08450
+
a08450
+    def _init_hmac(self, key, msg, digestmod):
a08450
+        self._hmac = _hashopenssl.hmac_new(key, msg, digestmod=digestmod)
a08450
+        self.digest_size = self._hmac.digest_size
a08450
+        self.block_size = self._hmac.block_size
a08450
+
a08450
+    def _init_old(self, key, msg, digestmod):
a08450
         if callable(digestmod):
a08450
-            self._digest_cons = digestmod
a08450
+            digest_cons = digestmod
a08450
         elif isinstance(digestmod, str):
a08450
-            self._digest_cons = lambda d=b'': _hashlib.new(digestmod, d)
a08450
+            digest_cons = lambda d=b'': _hashlib.new(digestmod, d)
a08450
         else:
a08450
-            self._digest_cons = lambda d=b'': digestmod.new(d)
a08450
+            digest_cons = lambda d=b'': digestmod.new(d)
a08450
 
a08450
-        self._outer = self._digest_cons()
a08450
-        self._inner = self._digest_cons()
a08450
+        self._hmac = None
a08450
+        self._outer = digest_cons()
a08450
+        self._inner = digest_cons()
a08450
         self.digest_size = self._inner.digest_size
a08450
 
a08450
         if hasattr(self._inner, 'block_size'):
a08450
@@ -79,13 +94,13 @@ class HMAC:
a08450
                            RuntimeWarning, 2)
a08450
             blocksize = self.blocksize
a08450
 
a08450
+        if len(key) > blocksize:
a08450
+            key = digest_cons(key).digest()
a08450
+
a08450
         # self.blocksize is the default blocksize. self.block_size is
a08450
         # effective block size as well as the public API attribute.
a08450
         self.block_size = blocksize
a08450
 
a08450
-        if len(key) > blocksize:
a08450
-            key = self._digest_cons(key).digest()
a08450
-
a08450
         key = key.ljust(blocksize, b'\0')
a08450
         self._outer.update(key.translate(trans_5C))
a08450
         self._inner.update(key.translate(trans_36))
a08450
@@ -94,23 +109,15 @@ class HMAC:
a08450
 
a08450
     @property
a08450
     def name(self):
a08450
-        return "hmac-" + self._inner.name
a08450
-
a08450
-    @property
a08450
-    def digest_cons(self):
a08450
-        return self._digest_cons
a08450
-
a08450
-    @property
a08450
-    def inner(self):
a08450
-        return self._inner
a08450
-
a08450
-    @property
a08450
-    def outer(self):
a08450
-        return self._outer
a08450
+        if self._hmac:
a08450
+            return self._hmac.name
a08450
+        else:
a08450
+            return f"hmac-{self._inner.name}"
a08450
 
a08450
     def update(self, msg):
a08450
         """Feed data from msg into this hashing object."""
a08450
-        self._inner.update(msg)
a08450
+        inst = self._hmac or self._inner
a08450
+        inst.update(msg)
a08450
 
a08450
     def copy(self):
a08450
         """Return a separate copy of this hashing object.
a08450
@@ -119,10 +126,14 @@ class HMAC:
a08450
         """
a08450
         # Call __new__ directly to avoid the expensive __init__.
a08450
         other = self.__class__.__new__(self.__class__)
a08450
-        other._digest_cons = self._digest_cons
a08450
         other.digest_size = self.digest_size
a08450
-        other._inner = self._inner.copy()
a08450
-        other._outer = self._outer.copy()
a08450
+        if self._hmac:
a08450
+            other._hmac = self._hmac.copy()
a08450
+            other._inner = other._outer = None
a08450
+        else:
a08450
+            other._hmac = None
a08450
+            other._inner = self._inner.copy()
a08450
+            other._outer = self._outer.copy()
a08450
         return other
a08450
 
a08450
     def _current(self):
a08450
@@ -130,9 +141,12 @@ class HMAC:
a08450
 
a08450
         To be used only internally with digest() and hexdigest().
a08450
         """
a08450
-        h = self._outer.copy()
a08450
-        h.update(self._inner.digest())
a08450
-        return h
a08450
+        if self._hmac:
a08450
+            return self._hmac
a08450
+        else:
a08450
+            h = self._outer.copy()
a08450
+            h.update(self._inner.digest())
a08450
+            return h
a08450
 
a08450
     def digest(self):
a08450
         """Return the hash value of this hashing object.
a08450
@@ -179,9 +193,11 @@ def digest(key, msg, digest):
a08450
             A hashlib constructor returning a new hash object. *OR*
a08450
             A module supporting PEP 247.
a08450
     """
a08450
-    if (_hashopenssl is not None and
a08450
-            isinstance(digest, str) and digest in _openssl_md_meths):
a08450
-        return _hashopenssl.hmac_digest(key, msg, digest)
a08450
+    if _hashopenssl is not None and isinstance(digest, (str, _functype)):
a08450
+        try:
a08450
+            return _hashopenssl.hmac_digest(key, msg, digest)
a08450
+        except _hashopenssl.UnsupportedDigestmodError:
a08450
+            pass
a08450
 
a08450
     if callable(digest):
a08450
         digest_cons = digest
a08450
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
a08450
index 6daf22c..adf52ad 100644
a08450
--- a/Lib/test/test_hmac.py
a08450
+++ b/Lib/test/test_hmac.py
a08450
@@ -11,14 +11,21 @@ from test.support import hashlib_helper
a08450
 from _operator import _compare_digest as operator_compare_digest
a08450
 
a08450
 try:
a08450
+    import _hashlib as _hashopenssl
a08450
     from _hashlib import HMAC as C_HMAC
a08450
     from _hashlib import hmac_new as c_hmac_new
a08450
     from _hashlib import compare_digest as openssl_compare_digest
a08450
 except ImportError:
a08450
+    _hashopenssl = None
a08450
     C_HMAC = None
a08450
     c_hmac_new = None
a08450
     openssl_compare_digest = None
a08450
 
a08450
+try:
a08450
+    import _sha256 as sha256_module
a08450
+except ImportError:
a08450
+    sha256_module = None
a08450
+
a08450
 
a08450
 def ignore_warning(func):
a08450
     @functools.wraps(func)
a08450
@@ -32,22 +39,27 @@ def ignore_warning(func):
a08450
 
a08450
 class TestVectorsTestCase(unittest.TestCase):
a08450
 
a08450
-    def asssert_hmac(
a08450
-        self, key, data, digest, hashfunc, hashname, digest_size, block_size
a08450
+    def assert_hmac_internals(
a08450
+            self, h, digest, hashname, digest_size, block_size
a08450
     ):
a08450
-        h = hmac.HMAC(key, data, digestmod=hashfunc)
a08450
         self.assertEqual(h.hexdigest().upper(), digest.upper())
a08450
         self.assertEqual(h.digest(), binascii.unhexlify(digest))
a08450
         self.assertEqual(h.name, f"hmac-{hashname}")
a08450
         self.assertEqual(h.digest_size, digest_size)
a08450
         self.assertEqual(h.block_size, block_size)
a08450
 
a08450
+    def assert_hmac(
a08450
+        self, key, data, digest, hashfunc, hashname, digest_size, block_size
a08450
+    ):
a08450
+        h = hmac.HMAC(key, data, digestmod=hashfunc)
a08450
+        self.assert_hmac_internals(
a08450
+            h, digest, hashname, digest_size, block_size
a08450
+        )
a08450
+
a08450
         h = hmac.HMAC(key, data, digestmod=hashname)
a08450
-        self.assertEqual(h.hexdigest().upper(), digest.upper())
a08450
-        self.assertEqual(h.digest(), binascii.unhexlify(digest))
a08450
-        self.assertEqual(h.name, f"hmac-{hashname}")
a08450
-        self.assertEqual(h.digest_size, digest_size)
a08450
-        self.assertEqual(h.block_size, block_size)
a08450
+        self.assert_hmac_internals(
a08450
+            h, digest, hashname, digest_size, block_size
a08450
+        )
a08450
 
a08450
         h = hmac.HMAC(key, digestmod=hashname)
a08450
         h2 = h.copy()
a08450
@@ -56,11 +68,9 @@ class TestVectorsTestCase(unittest.TestCase):
a08450
         self.assertEqual(h.hexdigest().upper(), digest.upper())
a08450
 
a08450
         h = hmac.new(key, data, digestmod=hashname)
a08450
-        self.assertEqual(h.hexdigest().upper(), digest.upper())
a08450
-        self.assertEqual(h.digest(), binascii.unhexlify(digest))
a08450
-        self.assertEqual(h.name, f"hmac-{hashname}")
a08450
-        self.assertEqual(h.digest_size, digest_size)
a08450
-        self.assertEqual(h.block_size, block_size)
a08450
+        self.assert_hmac_internals(
a08450
+            h, digest, hashname, digest_size, block_size
a08450
+        )
a08450
 
a08450
         h = hmac.new(key, None, digestmod=hashname)
a08450
         h.update(data)
a08450
@@ -81,23 +91,18 @@ class TestVectorsTestCase(unittest.TestCase):
a08450
             hmac.digest(key, data, digest=hashfunc),
a08450
             binascii.unhexlify(digest)
a08450
         )
a08450
-        with unittest.mock.patch('hmac._openssl_md_meths', {}):
a08450
-            self.assertEqual(
a08450
-                hmac.digest(key, data, digest=hashname),
a08450
-                binascii.unhexlify(digest)
a08450
-            )
a08450
-            self.assertEqual(
a08450
-                hmac.digest(key, data, digest=hashfunc),
a08450
-                binascii.unhexlify(digest)
a08450
-            )
a08450
+
a08450
+        h = hmac.HMAC.__new__(hmac.HMAC)
a08450
+        h._init_old(key, data, digestmod=hashname)
a08450
+        self.assert_hmac_internals(
a08450
+            h, digest, hashname, digest_size, block_size
a08450
+        )
a08450
 
a08450
         if c_hmac_new is not None:
a08450
             h = c_hmac_new(key, data, digestmod=hashname)
a08450
-            self.assertEqual(h.hexdigest().upper(), digest.upper())
a08450
-            self.assertEqual(h.digest(), binascii.unhexlify(digest))
a08450
-            self.assertEqual(h.name, f"hmac-{hashname}")
a08450
-            self.assertEqual(h.digest_size, digest_size)
a08450
-            self.assertEqual(h.block_size, block_size)
a08450
+            self.assert_hmac_internals(
a08450
+                h, digest, hashname, digest_size, block_size
a08450
+            )
a08450
 
a08450
             h = c_hmac_new(key, digestmod=hashname)
a08450
             h2 = h.copy()
a08450
@@ -105,12 +110,24 @@ class TestVectorsTestCase(unittest.TestCase):
a08450
             h.update(data)
a08450
             self.assertEqual(h.hexdigest().upper(), digest.upper())
a08450
 
a08450
+            func = getattr(_hashopenssl, f"openssl_{hashname}")
a08450
+            h = c_hmac_new(key, data, digestmod=func)
a08450
+            self.assert_hmac_internals(
a08450
+                h, digest, hashname, digest_size, block_size
a08450
+            )
a08450
+
a08450
+            h = hmac.HMAC.__new__(hmac.HMAC)
a08450
+            h._init_hmac(key, data, digestmod=hashname)
a08450
+            self.assert_hmac_internals(
a08450
+                h, digest, hashname, digest_size, block_size
a08450
+            )
a08450
+
a08450
     @hashlib_helper.requires_hashdigest('md5', openssl=True)
a08450
     def test_md5_vectors(self):
a08450
         # Test the HMAC module against test vectors from the RFC.
a08450
 
a08450
         def md5test(key, data, digest):
a08450
-            self.asssert_hmac(
a08450
+            self.assert_hmac(
a08450
                 key, data, digest,
a08450
                 hashfunc=hashlib.md5,
a08450
                 hashname="md5",
a08450
@@ -150,7 +167,7 @@ class TestVectorsTestCase(unittest.TestCase):
a08450
     @hashlib_helper.requires_hashdigest('sha1', openssl=True)
a08450
     def test_sha_vectors(self):
a08450
         def shatest(key, data, digest):
a08450
-            self.asssert_hmac(
a08450
+            self.assert_hmac(
a08450
                 key, data, digest,
a08450
                 hashfunc=hashlib.sha1,
a08450
                 hashname="sha1",
a08450
@@ -191,7 +208,7 @@ class TestVectorsTestCase(unittest.TestCase):
a08450
         def hmactest(key, data, hexdigests):
a08450
             digest = hexdigests[hashfunc]
a08450
 
a08450
-            self.asssert_hmac(
a08450
+            self.assert_hmac(
a08450
                 key, data, digest,
a08450
                 hashfunc=hashfunc,
a08450
                 hashname=hash_name,
a08450
@@ -427,6 +444,15 @@ class ConstructorTestCase(unittest.TestCase):
a08450
         ):
a08450
             C_HMAC()
a08450
 
a08450
+    @unittest.skipUnless(sha256_module is not None, 'need _sha256')
a08450
+    def test_with_sha256_module(self):
a08450
+        h = hmac.HMAC(b"key", b"hash this!", digestmod=sha256_module.sha256)
a08450
+        self.assertEqual(h.hexdigest(), self.expected)
a08450
+        self.assertEqual(h.name, "hmac-sha256")
a08450
+
a08450
+        digest = hmac.digest(b"key", b"hash this!", sha256_module.sha256)
a08450
+        self.assertEqual(digest, binascii.unhexlify(self.expected))
a08450
+
a08450
 
a08450
 class SanityTestCase(unittest.TestCase):
a08450
 
a08450
@@ -447,21 +473,21 @@ class SanityTestCase(unittest.TestCase):
a08450
 class CopyTestCase(unittest.TestCase):
a08450
 
a08450
     @hashlib_helper.requires_hashdigest('sha256')
a08450
-    def test_attributes(self):
a08450
+    def test_attributes_old(self):
a08450
         # Testing if attributes are of same type.
a08450
-        h1 = hmac.HMAC(b"key", digestmod="sha256")
a08450
+        h1 = hmac.HMAC.__new__(hmac.HMAC)
a08450
+        h1._init_old(b"key", b"msg", digestmod="sha256")
a08450
         h2 = h1.copy()
a08450
-        self.assertTrue(h1._digest_cons == h2._digest_cons,
a08450
-            "digest constructors don't match.")
a08450
         self.assertEqual(type(h1._inner), type(h2._inner),
a08450
             "Types of inner don't match.")
a08450
         self.assertEqual(type(h1._outer), type(h2._outer),
a08450
             "Types of outer don't match.")
a08450
 
a08450
     @hashlib_helper.requires_hashdigest('sha256')
a08450
-    def test_realcopy(self):
a08450
+    def test_realcopy_old(self):
a08450
         # Testing if the copy method created a real copy.
a08450
-        h1 = hmac.HMAC(b"key", digestmod="sha256")
a08450
+        h1 = hmac.HMAC.__new__(hmac.HMAC)
a08450
+        h1._init_old(b"key", b"msg", digestmod="sha256")
a08450
         h2 = h1.copy()
a08450
         # Using id() in case somebody has overridden __eq__/__ne__.
a08450
         self.assertTrue(id(h1) != id(h2), "No real copy of the HMAC instance.")
a08450
@@ -469,17 +495,15 @@ class CopyTestCase(unittest.TestCase):
a08450
             "No real copy of the attribute 'inner'.")
a08450
         self.assertTrue(id(h1._outer) != id(h2._outer),
a08450
             "No real copy of the attribute 'outer'.")
a08450
-        self.assertEqual(h1._inner, h1.inner)
a08450
-        self.assertEqual(h1._outer, h1.outer)
a08450
-        self.assertEqual(h1._digest_cons, h1.digest_cons)
a08450
+        self.assertIs(h1._hmac, None)
a08450
 
a08450
+    @unittest.skipIf(_hashopenssl is None, "test requires _hashopenssl")
a08450
     @hashlib_helper.requires_hashdigest('sha256')
a08450
-    def test_properties(self):
a08450
-        # deprecated properties
a08450
-        h1 = hmac.HMAC(b"key", digestmod="sha256")
a08450
-        self.assertEqual(h1._inner, h1.inner)
a08450
-        self.assertEqual(h1._outer, h1.outer)
a08450
-        self.assertEqual(h1._digest_cons, h1.digest_cons)
a08450
+    def test_realcopy_hmac(self):
a08450
+        h1 = hmac.HMAC.__new__(hmac.HMAC)
a08450
+        h1._init_hmac(b"key", b"msg", digestmod="sha256")
a08450
+        h2 = h1.copy()
a08450
+        self.assertTrue(id(h1._hmac) != id(h2._hmac))
a08450
 
a08450
     @hashlib_helper.requires_hashdigest('sha256')
a08450
     def test_equality(self):
a08450
diff --git a/Misc/NEWS.d/next/Library/2021-03-19-10-22-17.bpo-40645.5pXhb-.rst b/Misc/NEWS.d/next/Library/2021-03-19-10-22-17.bpo-40645.5pXhb-.rst
a08450
new file mode 100644
a08450
index 0000000..a9ab1c0
a08450
--- /dev/null
a08450
+++ b/Misc/NEWS.d/next/Library/2021-03-19-10-22-17.bpo-40645.5pXhb-.rst
a08450
@@ -0,0 +1,2 @@
a08450
+The :mod:`hmac` module now uses OpenSSL's HMAC implementation when digestmod
a08450
+argument is a hash name or builtin hash function.
a08450
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
a08450
index 62cf769..71ac832 100644
a08450
--- a/Modules/_hashopenssl.c
a08450
+++ b/Modules/_hashopenssl.c
a08450
@@ -260,6 +260,8 @@ typedef struct {
a08450
     PyTypeObject *EVPXOFtype;
a08450
 #endif
a08450
     _Py_hashtable_t *hashtable;
a08450
+    PyObject *constructs;
a08450
+    PyObject *unsupported_digestmod_error;
a08450
 } _hashlibstate;
a08450
 
a08450
 static inline _hashlibstate*
a08450
@@ -420,6 +422,48 @@ py_digest_by_name(PyObject *module, const char *name, enum Py_hash_type py_ht)
a08450
     return digest;
a08450
 }
a08450
 
a08450
+/* Get digest EVP from object
a08450
+ *
a08450
+ * * string
a08450
+ * * _hashopenssl builtin function
a08450
+ *
a08450
+ * on error returns NULL with exception set.
a08450
+ */
a08450
+static PY_EVP_MD*
a08450
+py_digest_by_digestmod(PyObject *module, PyObject *digestmod, enum Py_hash_type py_ht) {
a08450
+    PY_EVP_MD* evp;
a08450
+    PyObject *name_obj = NULL;
a08450
+    const char *name;
a08450
+
a08450
+    if (PyUnicode_Check(digestmod)) {
a08450
+        name_obj = digestmod;
a08450
+    } else {
a08450
+        _hashlibstate *state = get_hashlib_state(module);
a08450
+        // borrowed ref
a08450
+        name_obj = PyDict_GetItem(state->constructs, digestmod);
a08450
+    }
a08450
+    if (name_obj == NULL) {
a08450
+        _hashlibstate *state = get_hashlib_state(module);
a08450
+        PyErr_Clear();
a08450
+        PyErr_Format(
a08450
+            state->unsupported_digestmod_error,
a08450
+            "Unsupported digestmod %R", digestmod);
a08450
+        return NULL;
a08450
+    }
a08450
+
a08450
+    name = PyUnicode_AsUTF8(name_obj);
a08450
+    if (name == NULL) {
a08450
+        return NULL;
a08450
+    }
a08450
+
a08450
+    evp = py_digest_by_name(module, name, py_ht);
a08450
+    if (evp == NULL) {
a08450
+        return NULL;
a08450
+    }
a08450
+
a08450
+    return evp;
a08450
+}
a08450
+
a08450
 static EVPobject *
a08450
 newEVPobject(PyTypeObject *type)
a08450
 {
a08450
@@ -1238,7 +1282,6 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name,
a08450
 
a08450
     PY_EVP_MD *digest = py_digest_by_name(module, hash_name, Py_ht_pbkdf2);
a08450
     if (digest == NULL) {
a08450
-        PyErr_SetString(PyExc_ValueError, "unsupported hash type");
a08450
         goto end;
a08450
     }
a08450
 
a08450
@@ -1443,25 +1486,21 @@ _hashlib.hmac_digest as _hashlib_hmac_singleshot
a08450
 
a08450
     key: Py_buffer
a08450
     msg: Py_buffer
a08450
-    digest: str
a08450
+    digest: object
a08450
 
a08450
 Single-shot HMAC.
a08450
 [clinic start generated code]*/
a08450
 
a08450
 static PyObject *
a08450
 _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key,
a08450
-                              Py_buffer *msg, const char *digest)
a08450
-/*[clinic end generated code: output=15658ede5ab98185 input=019dffc571909a46]*/
a08450
+                              Py_buffer *msg, PyObject *digest)
a08450
+/*[clinic end generated code: output=82f19965d12706ac input=0a0790cc3db45c2e]*/
a08450
 {
a08450
     unsigned char md[EVP_MAX_MD_SIZE] = {0};
a08450
     unsigned int md_len = 0;
a08450
     unsigned char *result;
a08450
     PY_EVP_MD *evp;
a08450
 
a08450
-    evp = py_digest_by_name(module, digest, Py_ht_mac);
a08450
-    if (evp == NULL) {
a08450
-        return NULL;
a08450
-    }
a08450
     if (key->len > INT_MAX) {
a08450
         PyErr_SetString(PyExc_OverflowError,
a08450
                         "key is too long.");
a08450
@@ -1473,7 +1512,7 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key,
a08450
         return NULL;
a08450
     }
a08450
 
a08450
-    evp = py_digest_by_name(module, digest, Py_ht_mac);
a08450
+    evp = py_digest_by_digestmod(module, digest, Py_ht_mac);
a08450
     if (evp == NULL) {
a08450
         return NULL;
a08450
     }
a08450
@@ -1505,15 +1544,15 @@ _hashlib.hmac_new
a08450
 
a08450
     key: Py_buffer
a08450
     msg as msg_obj: object(c_default="NULL") = b''
a08450
-    digestmod: str(c_default="NULL") = None
a08450
+    digestmod: object(c_default="NULL") = None
a08450
 
a08450
 Return a new hmac object.
a08450
 [clinic start generated code]*/
a08450
 
a08450
 static PyObject *
a08450
 _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
a08450
-                       const char *digestmod)
a08450
-/*[clinic end generated code: output=9a35673be0cbea1b input=a0878868eb190134]*/
a08450
+                       PyObject *digestmod)
a08450
+/*[clinic end generated code: output=c20d9e4d9ed6d219 input=5f4071dcc7f34362]*/
a08450
 {
a08450
     PyTypeObject *type = get_hashlib_state(module)->HMACtype;
a08450
     PY_EVP_MD *digest;
a08450
@@ -1527,14 +1566,14 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
a08450
         return NULL;
a08450
     }
a08450
 
a08450
-    if ((digestmod == NULL) || !strlen(digestmod)) {
a08450
+    if (digestmod == NULL) {
a08450
         PyErr_SetString(
a08450
             PyExc_TypeError, "Missing required parameter 'digestmod'.");
a08450
         return NULL;
a08450
     }
a08450
 
a08450
-    digest = py_digest_by_name(module, digestmod, Py_ht_mac);
a08450
-    if (!digest) {
a08450
+    digest = py_digest_by_digestmod(module, digestmod, Py_ht_mac);
a08450
+    if (digest == NULL) {
a08450
         return NULL;
a08450
     }
a08450
 
a08450
@@ -2106,6 +2145,8 @@ hashlib_traverse(PyObject *m, visitproc visit, void *arg)
a08450
 #ifdef PY_OPENSSL_HAS_SHAKE
a08450
     Py_VISIT(state->EVPXOFtype);
a08450
 #endif
a08450
+    Py_VISIT(state->constructs);
a08450
+    Py_VISIT(state->unsupported_digestmod_error);
a08450
     return 0;
a08450
 }
a08450
 
a08450
@@ -2118,10 +2159,14 @@ hashlib_clear(PyObject *m)
a08450
 #ifdef PY_OPENSSL_HAS_SHAKE
a08450
     Py_CLEAR(state->EVPXOFtype);
a08450
 #endif
a08450
+    Py_CLEAR(state->constructs);
a08450
+    Py_CLEAR(state->unsupported_digestmod_error);
a08450
+
a08450
     if (state->hashtable != NULL) {
a08450
         _Py_hashtable_destroy(state->hashtable);
a08450
         state->hashtable = NULL;
a08450
     }
a08450
+
a08450
     return 0;
a08450
 }
a08450
 
a08450
@@ -2216,6 +2261,79 @@ hashlib_init_hmactype(PyObject *module)
a08450
     return 0;
a08450
 }
a08450
 
a08450
+static int
a08450
+hashlib_init_constructors(PyObject *module)
a08450
+{
a08450
+    /* Create dict from builtin openssl_hash functions to name
a08450
+     * {_hashlib.openssl_sha256: "sha256", ...}
a08450
+     */
a08450
+    PyModuleDef *mdef;
a08450
+    PyMethodDef *fdef;
a08450
+    PyObject *proxy;
a08450
+    PyObject *func, *name_obj;
a08450
+    _hashlibstate *state = get_hashlib_state(module);
a08450
+
a08450
+    mdef = PyModule_GetDef(module);
a08450
+    if (mdef == NULL) {
a08450
+        return -1;
a08450
+    }
a08450
+
a08450
+    state->constructs = PyDict_New();
a08450
+    if (state->constructs == NULL) {
a08450
+        return -1;
a08450
+    }
a08450
+
a08450
+    for (fdef = mdef->m_methods; fdef->ml_name != NULL; fdef++) {
a08450
+        if (strncmp(fdef->ml_name, "openssl_", 8)) {
a08450
+            continue;
a08450
+        }
a08450
+        name_obj = PyUnicode_FromString(fdef->ml_name + 8);
a08450
+        if (name_obj == NULL) {
a08450
+            return -1;
a08450
+        }
a08450
+        func  = PyObject_GetAttrString(module, fdef->ml_name);
a08450
+        if (func == NULL) {
a08450
+            Py_DECREF(name_obj);
a08450
+            return -1;
a08450
+        }
a08450
+        int rc = PyDict_SetItem(state->constructs, func, name_obj);
a08450
+        Py_DECREF(func);
a08450
+        Py_DECREF(name_obj);
a08450
+        if (rc < 0) {
a08450
+            return -1;
a08450
+        }
a08450
+    }
a08450
+
a08450
+    proxy = PyDictProxy_New(state->constructs);
a08450
+    if (proxy == NULL) {
a08450
+        return -1;
a08450
+    }
a08450
+
a08450
+    int rc = _PyModule_AddObjectRef(module, "_constructors", proxy);
a08450
+    Py_DECREF(proxy);
a08450
+    if (rc < 0) {
a08450
+        return -1;
a08450
+    }
a08450
+    return 0;
a08450
+}
a08450
+
a08450
+static int
a08450
+hashlib_exception(PyObject *module)
a08450
+{
a08450
+    _hashlibstate *state = get_hashlib_state(module);
a08450
+    state->unsupported_digestmod_error = PyErr_NewException(
a08450
+        "_hashlib.UnsupportedDigestmodError", PyExc_ValueError, NULL);
a08450
+    if (state->unsupported_digestmod_error == NULL) {
a08450
+        return -1;
a08450
+    }
a08450
+    if (_PyModule_AddObjectRef(module, "UnsupportedDigestmodError",
a08450
+                              state->unsupported_digestmod_error) < 0) {
a08450
+        return -1;
a08450
+    }
a08450
+    return 0;
a08450
+}
a08450
+
a08450
+
a08450
 static PyModuleDef_Slot hashlib_slots[] = {
a08450
     /* OpenSSL 1.0.2 and LibreSSL */
a08450
     {Py_mod_exec, hashlib_openssl_legacy_init},
a08450
@@ -2224,6 +2342,8 @@ static PyModuleDef_Slot hashlib_slots[] = {
a08450
     {Py_mod_exec, hashlib_init_evpxoftype},
a08450
     {Py_mod_exec, hashlib_init_hmactype},
a08450
     {Py_mod_exec, hashlib_md_meth_names},
a08450
+    {Py_mod_exec, hashlib_init_constructors},
a08450
+    {Py_mod_exec, hashlib_exception},
a08450
     {0, NULL}
a08450
 };
a08450
 
a08450
diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h
a08450
index 68aa765..4466ec4 100644
a08450
--- a/Modules/clinic/_hashopenssl.c.h
a08450
+++ b/Modules/clinic/_hashopenssl.c.h
a08450
@@ -1106,7 +1106,7 @@ PyDoc_STRVAR(_hashlib_hmac_singleshot__doc__,
a08450
 
a08450
 static PyObject *
a08450
 _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key,
a08450
-                              Py_buffer *msg, const char *digest);
a08450
+                              Py_buffer *msg, PyObject *digest);
a08450
 
a08450
 static PyObject *
a08450
 _hashlib_hmac_singleshot(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
a08450
@@ -1117,7 +1117,7 @@ _hashlib_hmac_singleshot(PyObject *module, PyObject *const *args, Py_ssize_t nar
a08450
     PyObject *argsbuf[3];
a08450
     Py_buffer key = {NULL, NULL};
a08450
     Py_buffer msg = {NULL, NULL};
a08450
-    const char *digest;
a08450
+    PyObject *digest;
a08450
 
a08450
     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf);
a08450
     if (!args) {
a08450
@@ -1137,19 +1137,7 @@ _hashlib_hmac_singleshot(PyObject *module, PyObject *const *args, Py_ssize_t nar
a08450
         _PyArg_BadArgument("hmac_digest", "argument 'msg'", "contiguous buffer", args[1]);
a08450
         goto exit;
a08450
     }
a08450
-    if (!PyUnicode_Check(args[2])) {
a08450
-        _PyArg_BadArgument("hmac_digest", "argument 'digest'", "str", args[2]);
a08450
-        goto exit;
a08450
-    }
a08450
-    Py_ssize_t digest_length;
a08450
-    digest = PyUnicode_AsUTF8AndSize(args[2], &digest_length);
a08450
-    if (digest == NULL) {
a08450
-        goto exit;
a08450
-    }
a08450
-    if (strlen(digest) != (size_t)digest_length) {
a08450
-        PyErr_SetString(PyExc_ValueError, "embedded null character");
a08450
-        goto exit;
a08450
-    }
a08450
+    digest = args[2];
a08450
     return_value = _hashlib_hmac_singleshot_impl(module, &key, &msg, digest);
a08450
 
a08450
 exit:
a08450
@@ -1176,7 +1164,7 @@ PyDoc_STRVAR(_hashlib_hmac_new__doc__,
a08450
 
a08450
 static PyObject *
a08450
 _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
a08450
-                       const char *digestmod);
a08450
+                       PyObject *digestmod);
a08450
 
a08450
 static PyObject *
a08450
 _hashlib_hmac_new(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
a08450
@@ -1188,7 +1176,7 @@ _hashlib_hmac_new(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO
a08450
     Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
a08450
     Py_buffer key = {NULL, NULL};
a08450
     PyObject *msg_obj = NULL;
a08450
-    const char *digestmod = NULL;
a08450
+    PyObject *digestmod = NULL;
a08450
 
a08450
     args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf);
a08450
     if (!args) {
a08450
@@ -1210,19 +1198,7 @@ _hashlib_hmac_new(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO
a08450
             goto skip_optional_pos;
a08450
         }
a08450
     }
a08450
-    if (!PyUnicode_Check(args[2])) {
a08450
-        _PyArg_BadArgument("hmac_new", "argument 'digestmod'", "str", args[2]);
a08450
-        goto exit;
a08450
-    }
a08450
-    Py_ssize_t digestmod_length;
a08450
-    digestmod = PyUnicode_AsUTF8AndSize(args[2], &digestmod_length);
a08450
-    if (digestmod == NULL) {
a08450
-        goto exit;
a08450
-    }
a08450
-    if (strlen(digestmod) != (size_t)digestmod_length) {
a08450
-        PyErr_SetString(PyExc_ValueError, "embedded null character");
a08450
-        goto exit;
a08450
-    }
a08450
+    digestmod = args[2];
a08450
 skip_optional_pos:
a08450
     return_value = _hashlib_hmac_new_impl(module, &key, msg_obj, digestmod);
a08450
 
a08450
@@ -1442,4 +1418,4 @@ exit:
a08450
 #ifndef _HASHLIB_GET_FIPS_MODE_METHODDEF
a08450
     #define _HASHLIB_GET_FIPS_MODE_METHODDEF
a08450
 #endif /* !defined(_HASHLIB_GET_FIPS_MODE_METHODDEF) */
a08450
-/*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/
a08450
+/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From f7ce31ebf3200952dadff556bfcbf2876139c823 Mon Sep 17 00:00:00 2001
a08450
From: Charalampos Stratakis <cstratak@redhat.com>
a08450
Date: Thu, 12 Dec 2019 16:58:31 +0100
a08450
Subject: [PATCH 05/11] Expose blake2b and blake2s hashes from OpenSSL
a08450
a08450
These aren't as powerful as Python's own implementation, but they can be
a08450
used under FIPS.
a08450
---
a08450
 Lib/test/test_hashlib.py        |   6 ++
a08450
 Modules/_hashopenssl.c          |  37 +++++++++++
a08450
 Modules/clinic/_hashopenssl.c.h | 106 +++++++++++++++++++++++++++++++-
a08450
 3 files changed, 148 insertions(+), 1 deletion(-)
a08450
a08450
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
a08450
index 969e5e4..5b46016 100644
a08450
--- a/Lib/test/test_hashlib.py
a08450
+++ b/Lib/test/test_hashlib.py
a08450
@@ -354,6 +354,12 @@ class HashLibTestCase(unittest.TestCase):
a08450
         # 2 is for hashlib.name(...) and hashlib.new(name, ...)
a08450
         self.assertGreaterEqual(len(constructors), 2)
a08450
         for hash_object_constructor in constructors:
a08450
+
a08450
+            # OpenSSL's blake2s & blake2d don't support `key`
a08450
+            _name = hash_object_constructor.__name__
a08450
+            if 'key' in kwargs and _name.startswith('openssl_blake2'):
a08450
+                return
a08450
+
a08450
             m = hash_object_constructor(data, **kwargs)
a08450
             computed = m.hexdigest() if not shake else m.hexdigest(length)
a08450
             self.assertEqual(
a08450
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
a08450
index 71ac832..0b2c65e 100644
a08450
--- a/Modules/_hashopenssl.c
a08450
+++ b/Modules/_hashopenssl.c
a08450
@@ -1138,6 +1138,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj,
a08450
 }
a08450
 
a08450
 
a08450
+/*[clinic input]
a08450
+_hashlib.openssl_blake2b
a08450
+    string as data_obj: object(py_default="b''") = NULL
a08450
+    *
a08450
+    usedforsecurity: bool = True
a08450
+Returns a blake2b hash object; optionally initialized with a string
a08450
+[clinic start generated code]*/
a08450
+
a08450
+static PyObject *
a08450
+_hashlib_openssl_blake2b_impl(PyObject *module, PyObject *data_obj,
a08450
+                              int usedforsecurity)
a08450
+/*[clinic end generated code: output=7a838b1643cde13e input=4ad7fd54268f3689]*/
a08450
+
a08450
+{
a08450
+    return py_evp_fromname(module, Py_hash_blake2b, data_obj, usedforsecurity);
a08450
+}
a08450
+
a08450
+/*[clinic input]
a08450
+_hashlib.openssl_blake2s
a08450
+    string as data_obj: object(py_default="b''") = NULL
a08450
+    *
a08450
+    usedforsecurity: bool = True
a08450
+Returns a blake2s hash object; optionally initialized with a string
a08450
+[clinic start generated code]*/
a08450
+
a08450
+static PyObject *
a08450
+_hashlib_openssl_blake2s_impl(PyObject *module, PyObject *data_obj,
a08450
+                              int usedforsecurity)
a08450
+/*[clinic end generated code: output=4eda6b40757471da input=1ed39481ffa4e26a]*/
a08450
+
a08450
+{
a08450
+    return py_evp_fromname(module, Py_hash_blake2s, data_obj, usedforsecurity);
a08450
+}
a08450
+
a08450
+
a08450
 #ifdef PY_OPENSSL_HAS_SHA3
a08450
 
a08450
 /*[clinic input]
a08450
@@ -2124,6 +2159,8 @@ static struct PyMethodDef EVP_functions[] = {
a08450
     _HASHLIB_OPENSSL_SHA256_METHODDEF
a08450
     _HASHLIB_OPENSSL_SHA384_METHODDEF
a08450
     _HASHLIB_OPENSSL_SHA512_METHODDEF
a08450
+    _HASHLIB_OPENSSL_BLAKE2B_METHODDEF
a08450
+    _HASHLIB_OPENSSL_BLAKE2S_METHODDEF
a08450
     _HASHLIB_OPENSSL_SHA3_224_METHODDEF
a08450
     _HASHLIB_OPENSSL_SHA3_256_METHODDEF
a08450
     _HASHLIB_OPENSSL_SHA3_384_METHODDEF
a08450
diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h
a08450
index 4466ec4..54c22b2 100644
a08450
--- a/Modules/clinic/_hashopenssl.c.h
a08450
+++ b/Modules/clinic/_hashopenssl.c.h
a08450
@@ -540,6 +540,110 @@ exit:
a08450
     return return_value;
a08450
 }
a08450
 
a08450
+PyDoc_STRVAR(_hashlib_openssl_blake2b__doc__,
a08450
+"openssl_blake2b($module, /, string=b\'\', *, usedforsecurity=True)\n"
a08450
+"--\n"
a08450
+"\n"
a08450
+"Returns a blake2b hash object; optionally initialized with a string");
a08450
+
a08450
+#define _HASHLIB_OPENSSL_BLAKE2B_METHODDEF    \
a08450
+    {"openssl_blake2b", (PyCFunction)(void(*)(void))_hashlib_openssl_blake2b, METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_blake2b__doc__},
a08450
+
a08450
+static PyObject *
a08450
+_hashlib_openssl_blake2b_impl(PyObject *module, PyObject *data_obj,
a08450
+                              int usedforsecurity);
a08450
+
a08450
+static PyObject *
a08450
+_hashlib_openssl_blake2b(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
a08450
+{
a08450
+    PyObject *return_value = NULL;
a08450
+    static const char * const _keywords[] = {"string", "usedforsecurity", NULL};
a08450
+    static _PyArg_Parser _parser = {NULL, _keywords, "openssl_blake2b", 0};
a08450
+    PyObject *argsbuf[2];
a08450
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
a08450
+    PyObject *data_obj = NULL;
a08450
+    int usedforsecurity = 1;
a08450
+
a08450
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
a08450
+    if (!args) {
a08450
+        goto exit;
a08450
+    }
a08450
+    if (!noptargs) {
a08450
+        goto skip_optional_pos;
a08450
+    }
a08450
+    if (args[0]) {
a08450
+        data_obj = args[0];
a08450
+        if (!--noptargs) {
a08450
+            goto skip_optional_pos;
a08450
+        }
a08450
+    }
a08450
+skip_optional_pos:
a08450
+    if (!noptargs) {
a08450
+        goto skip_optional_kwonly;
a08450
+    }
a08450
+    usedforsecurity = PyObject_IsTrue(args[1]);
a08450
+    if (usedforsecurity < 0) {
a08450
+        goto exit;
a08450
+    }
a08450
+skip_optional_kwonly:
a08450
+    return_value = _hashlib_openssl_blake2b_impl(module, data_obj, usedforsecurity);
a08450
+
a08450
+exit:
a08450
+    return return_value;
a08450
+}
a08450
+
a08450
+PyDoc_STRVAR(_hashlib_openssl_blake2s__doc__,
a08450
+"openssl_blake2s($module, /, string=b\'\', *, usedforsecurity=True)\n"
a08450
+"--\n"
a08450
+"\n"
a08450
+"Returns a blake2s hash object; optionally initialized with a string");
a08450
+
a08450
+#define _HASHLIB_OPENSSL_BLAKE2S_METHODDEF    \
a08450
+    {"openssl_blake2s", (PyCFunction)(void(*)(void))_hashlib_openssl_blake2s, METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_blake2s__doc__},
a08450
+
a08450
+static PyObject *
a08450
+_hashlib_openssl_blake2s_impl(PyObject *module, PyObject *data_obj,
a08450
+                              int usedforsecurity);
a08450
+
a08450
+static PyObject *
a08450
+_hashlib_openssl_blake2s(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
a08450
+{
a08450
+    PyObject *return_value = NULL;
a08450
+    static const char * const _keywords[] = {"string", "usedforsecurity", NULL};
a08450
+    static _PyArg_Parser _parser = {NULL, _keywords, "openssl_blake2s", 0};
a08450
+    PyObject *argsbuf[2];
a08450
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
a08450
+    PyObject *data_obj = NULL;
a08450
+    int usedforsecurity = 1;
a08450
+
a08450
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
a08450
+    if (!args) {
a08450
+        goto exit;
a08450
+    }
a08450
+    if (!noptargs) {
a08450
+        goto skip_optional_pos;
a08450
+    }
a08450
+    if (args[0]) {
a08450
+        data_obj = args[0];
a08450
+        if (!--noptargs) {
a08450
+            goto skip_optional_pos;
a08450
+        }
a08450
+    }
a08450
+skip_optional_pos:
a08450
+    if (!noptargs) {
a08450
+        goto skip_optional_kwonly;
a08450
+    }
a08450
+    usedforsecurity = PyObject_IsTrue(args[1]);
a08450
+    if (usedforsecurity < 0) {
a08450
+        goto exit;
a08450
+    }
a08450
+skip_optional_kwonly:
a08450
+    return_value = _hashlib_openssl_blake2s_impl(module, data_obj, usedforsecurity);
a08450
+
a08450
+exit:
a08450
+    return return_value;
a08450
+}
a08450
+
a08450
 #if defined(PY_OPENSSL_HAS_SHA3)
a08450
 
a08450
 PyDoc_STRVAR(_hashlib_openssl_sha3_224__doc__,
a08450
@@ -1418,4 +1522,4 @@ exit:
a08450
 #ifndef _HASHLIB_GET_FIPS_MODE_METHODDEF
a08450
     #define _HASHLIB_GET_FIPS_MODE_METHODDEF
a08450
 #endif /* !defined(_HASHLIB_GET_FIPS_MODE_METHODDEF) */
a08450
-/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/
a08450
+/*[clinic end generated code: output=fab05055e982f112 input=a9049054013a1b77]*/
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From b8956168975170b8e7a797b6aa23e0d356f5ebec Mon Sep 17 00:00:00 2001
a08450
From: Petr Viktorin <pviktori@redhat.com>
a08450
Date: Thu, 1 Aug 2019 17:57:05 +0200
a08450
Subject: [PATCH 06/11] Use a stronger hash in multiprocessing handshake
a08450
a08450
Adapted from patch by David Malcolm,
a08450
https://bugs.python.org/issue17258
a08450
---
a08450
 Lib/multiprocessing/connection.py | 8 ++++++--
a08450
 1 file changed, 6 insertions(+), 2 deletions(-)
a08450
a08450
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
a08450
index 510e4b5..b68f2fb 100644
a08450
--- a/Lib/multiprocessing/connection.py
a08450
+++ b/Lib/multiprocessing/connection.py
a08450
@@ -42,6 +42,10 @@ BUFSIZE = 8192
a08450
 # A very generous timeout when it comes to local connections...
a08450
 CONNECTION_TIMEOUT = 20.
a08450
 
a08450
+# The hmac module implicitly defaults to using MD5.
a08450
+# Support using a stronger algorithm for the challenge/response code:
a08450
+HMAC_DIGEST_NAME='sha256'
a08450
+
a08450
 _mmap_counter = itertools.count()
a08450
 
a08450
 default_family = 'AF_INET'
a08450
@@ -741,7 +745,7 @@ def deliver_challenge(connection, authkey):
a08450
             "Authkey must be bytes, not {0!s}".format(type(authkey)))
a08450
     message = os.urandom(MESSAGE_LENGTH)
a08450
     connection.send_bytes(CHALLENGE + message)
a08450
-    digest = hmac.new(authkey, message, 'md5').digest()
a08450
+    digest = hmac.new(authkey, message, HMAC_DIGEST_NAME).digest()
a08450
     response = connection.recv_bytes(256)        # reject large message
a08450
     if response == digest:
a08450
         connection.send_bytes(WELCOME)
a08450
@@ -757,7 +761,7 @@ def answer_challenge(connection, authkey):
a08450
     message = connection.recv_bytes(256)         # reject large message
a08450
     assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
a08450
     message = message[len(CHALLENGE):]
a08450
-    digest = hmac.new(authkey, message, 'md5').digest()
a08450
+    digest = hmac.new(authkey, message, HMAC_DIGEST_NAME).digest()
a08450
     connection.send_bytes(digest)
a08450
     response = connection.recv_bytes(256)        # reject large message
a08450
     if response != WELCOME:
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From 20d86957b863e80d1f71b5681fccdb1fd16128b9 Mon Sep 17 00:00:00 2001
a08450
From: Petr Viktorin <pviktori@redhat.com>
a08450
Date: Thu, 25 Jul 2019 17:19:06 +0200
a08450
Subject: [PATCH 07/11] Disable Python's hash implementations in FIPS mode,
a08450
 forcing OpenSSL
a08450
a08450
---
a08450
 Lib/hashlib.py                 | 11 +++++++----
a08450
 Lib/test/test_hashlib.py       | 17 ++++++++++++-----
a08450
 Modules/_blake2/blake2b_impl.c |  4 ++++
a08450
 Modules/_blake2/blake2module.c |  3 +++
a08450
 Modules/_blake2/blake2s_impl.c |  4 ++++
a08450
 Modules/hashlib.h              | 23 +++++++++++++++++++++++
a08450
 setup.py                       | 27 ++++++++++++++++-----------
a08450
 7 files changed, 69 insertions(+), 20 deletions(-)
a08450
a08450
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
a08450
index ffa3be0..3e3f4dd 100644
a08450
--- a/Lib/hashlib.py
a08450
+++ b/Lib/hashlib.py
a08450
@@ -70,14 +70,17 @@ __all__ = __always_supported + ('new', 'algorithms_guaranteed',
a08450
 
a08450
 __builtin_constructor_cache = {}
a08450
 
a08450
-# Prefer our blake2 implementation
a08450
+# Prefer our blake2 implementation (unless in FIPS mode)
a08450
 # OpenSSL 1.1.0 comes with a limited implementation of blake2b/s. The OpenSSL
a08450
 # implementations neither support keyed blake2 (blake2 MAC) nor advanced
a08450
 # features like salt, personalization, or tree hashing. OpenSSL hash-only
a08450
 # variants are available as 'blake2b512' and 'blake2s256', though.
a08450
-__block_openssl_constructor = {
a08450
-    'blake2b', 'blake2s',
a08450
-}
a08450
+import _hashlib
a08450
+if _hashlib.get_fips_mode():
a08450
+    __block_openssl_constructor = set()
a08450
+else:
a08450
+    __block_openssl_constructor = {'blake2b', 'blake2s'}
a08450
+
a08450
 
a08450
 def __get_builtin_constructor(name):
a08450
     cache = __builtin_constructor_cache
a08450
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
a08450
index 5b46016..72fdc67 100644
a08450
--- a/Lib/test/test_hashlib.py
a08450
+++ b/Lib/test/test_hashlib.py
a08450
@@ -35,14 +35,15 @@ else:
a08450
         m.strip() for m in builtin_hashes.strip('"').lower().split(",")
a08450
     }
a08450
 
a08450
-# hashlib with and without OpenSSL backend for PBKDF2
a08450
-# only import builtin_hashlib when all builtin hashes are available.
a08450
-# Otherwise import prints noise on stderr
a08450
+# RHEL: `_hashlib` is always importable and `hashlib` can't be imported
a08450
+# without it.
a08450
 openssl_hashlib = import_fresh_module('hashlib', fresh=['_hashlib'])
a08450
-if builtin_hashes == default_builtin_hashes:
a08450
+try:
a08450
     builtin_hashlib = import_fresh_module('hashlib', blocked=['_hashlib'])
a08450
-else:
a08450
+except ImportError:
a08450
     builtin_hashlib = None
a08450
+else:
a08450
+    raise AssertionError('hashlib is importablee without _hashlib')
a08450
 
a08450
 try:
a08450
     from _hashlib import HASH, HASHXOF, openssl_md_meth_names, get_fips_mode
a08450
@@ -116,6 +117,12 @@ class HashLibTestCase(unittest.TestCase):
a08450
         except ModuleNotFoundError as error:
a08450
             if self._warn_on_extension_import and module_name in builtin_hashes:
a08450
                 warnings.warn('Did a C extension fail to compile? %s' % error)
a08450
+        except ImportError:
a08450
+            if get_fips_mode() and module_name == '_blake2':
a08450
+                # blake2b & blake2s disabled under FIPS
a08450
+                return None
a08450
+            else:
a08450
+                raise
a08450
         return None
a08450
 
a08450
     def __init__(self, *args, **kwargs):
a08450
diff --git a/Modules/_blake2/blake2b_impl.c b/Modules/_blake2/blake2b_impl.c
a08450
index 7fb1296..bc01cd5 100644
a08450
--- a/Modules/_blake2/blake2b_impl.c
a08450
+++ b/Modules/_blake2/blake2b_impl.c
a08450
@@ -96,6 +96,8 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
a08450
     BLAKE2bObject *self = NULL;
a08450
     Py_buffer buf;
a08450
 
a08450
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
a08450
+
a08450
     self = new_BLAKE2bObject(type);
a08450
     if (self == NULL) {
a08450
         goto error;
a08450
@@ -274,6 +276,8 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data)
a08450
 {
a08450
     Py_buffer buf;
a08450
 
a08450
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
a08450
+
a08450
     GET_BUFFER_VIEW_OR_ERROUT(data, &buf;;
a08450
 
a08450
     if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
a08450
diff --git a/Modules/_blake2/blake2module.c b/Modules/_blake2/blake2module.c
a08450
index ff142c9..144ec03 100644
a08450
--- a/Modules/_blake2/blake2module.c
a08450
+++ b/Modules/_blake2/blake2module.c
a08450
@@ -9,6 +9,7 @@
a08450
  */
a08450
 
a08450
 #include "Python.h"
a08450
+#include "../hashlib.h"
a08450
 
a08450
 #include "impl/blake2.h"
a08450
 
a08450
@@ -57,6 +58,8 @@ PyInit__blake2(void)
a08450
     PyObject *m;
a08450
     PyObject *d;
a08450
 
a08450
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ImportError, "blake2");
a08450
+
a08450
     m = PyModule_Create(&blake2_module);
a08450
     if (m == NULL)
a08450
         return NULL;
a08450
diff --git a/Modules/_blake2/blake2s_impl.c b/Modules/_blake2/blake2s_impl.c
a08450
index e3e90d0..e45f8f6 100644
a08450
--- a/Modules/_blake2/blake2s_impl.c
a08450
+++ b/Modules/_blake2/blake2s_impl.c
a08450
@@ -96,6 +96,8 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
a08450
     BLAKE2sObject *self = NULL;
a08450
     Py_buffer buf;
a08450
 
a08450
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
a08450
+
a08450
     self = new_BLAKE2sObject(type);
a08450
     if (self == NULL) {
a08450
         goto error;
a08450
@@ -274,6 +276,8 @@ _blake2_blake2s_update(BLAKE2sObject *self, PyObject *data)
a08450
 {
a08450
     Py_buffer buf;
a08450
 
a08450
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
a08450
+
a08450
     GET_BUFFER_VIEW_OR_ERROUT(data, &buf;;
a08450
 
a08450
     if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
a08450
diff --git a/Modules/hashlib.h b/Modules/hashlib.h
a08450
index 56ae7a5..45fb403 100644
a08450
--- a/Modules/hashlib.h
a08450
+++ b/Modules/hashlib.h
a08450
@@ -1,5 +1,11 @@
a08450
 /* Common code for use by all hashlib related modules. */
a08450
 
a08450
+// RHEL: use OpenSSL to turn off unsupported modules under FIPS mode
a08450
+// EVP_default_properties_is_fips_enabled() on OpenSSL >= 3.0.0
a08450
+#include <openssl/evp.h>
a08450
+// FIPS_mode() on OpenSSL < 3.0.0
a08450
+#include <openssl/crypto.h>
a08450
+
a08450
 /*
a08450
  * Given a PyObject* obj, fill in the Py_buffer* viewp with the result
a08450
  * of PyObject_GetBuffer.  Sets an exception and issues the erraction
a08450
@@ -57,3 +63,20 @@
a08450
  * to allow the user to optimize based on the platform they're using. */
a08450
 #define HASHLIB_GIL_MINSIZE 2048
a08450
 
a08450
+__attribute__((__unused__))
a08450
+static int
a08450
+_Py_hashlib_fips_error(PyObject *exc, char *name) {
a08450
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
a08450
+    if (EVP_default_properties_is_fips_enabled(NULL)) {
a08450
+#else
a08450
+    if (FIPS_mode()) {
a08450
+#endif
a08450
+        PyErr_Format(exc, "%s is not available in FIPS mode", name);
a08450
+        return 1;
a08450
+    }
a08450
+    return 0;
a08450
+}
a08450
+
a08450
+#define FAIL_RETURN_IN_FIPS_MODE(exc, name) do { \
a08450
+    if (_Py_hashlib_fips_error(exc, name)) return NULL; \
a08450
+} while (0)
a08450
diff --git a/setup.py b/setup.py
a08450
index c6023e1..371674c 100644
a08450
--- a/setup.py
a08450
+++ b/setup.py
a08450
@@ -2313,7 +2313,7 @@ class PyBuildExt(build_ext):
a08450
                            sources=sources,
a08450
                            depends=depends))
a08450
 
a08450
-    def detect_openssl_hashlib(self):
a08450
+    def detect_openssl_args(self):
a08450
         # Detect SSL support for the socket module (via _ssl)
a08450
         config_vars = sysconfig.get_config_vars()
a08450
 
a08450
@@ -2333,16 +2333,14 @@ class PyBuildExt(build_ext):
a08450
         openssl_libs = split_var('OPENSSL_LIBS', '-l')
a08450
         if not openssl_libs:
a08450
             # libssl and libcrypto not found
a08450
-            self.missing.extend(['_ssl', '_hashlib'])
a08450
-            return None, None
a08450
+            raise ValueError('Cannot build for RHEL without OpenSSL')
a08450
 
a08450
         # Find OpenSSL includes
a08450
         ssl_incs = find_file(
a08450
             'openssl/ssl.h', self.inc_dirs, openssl_includes
a08450
         )
a08450
         if ssl_incs is None:
a08450
-            self.missing.extend(['_ssl', '_hashlib'])
a08450
-            return None, None
a08450
+            raise ValueError('Cannot build for RHEL without OpenSSL')
a08450
 
a08450
         # OpenSSL 1.0.2 uses Kerberos for KRB5 ciphers
a08450
         krb5_h = find_file(
a08450
@@ -2352,12 +2350,20 @@ class PyBuildExt(build_ext):
a08450
         if krb5_h:
a08450
             ssl_incs.extend(krb5_h)
a08450
 
a08450
+        return {
a08450
+            'include_dirs': openssl_includes,
a08450
+            'library_dirs': openssl_libdirs,
a08450
+            'libraries': openssl_libs,
a08450
+        }
a08450
+
a08450
+    def detect_openssl_hashlib(self):
a08450
+
a08450
+        config_vars = sysconfig.get_config_vars()
a08450
+
a08450
         if config_vars.get("HAVE_X509_VERIFY_PARAM_SET1_HOST"):
a08450
             self.add(Extension(
a08450
                 '_ssl', ['_ssl.c'],
a08450
-                include_dirs=openssl_includes,
a08450
-                library_dirs=openssl_libdirs,
a08450
-                libraries=openssl_libs,
a08450
+                **self.detect_openssl_args(),
a08450
                 depends=[
a08450
                     'socketmodule.h',
a08450
                     '_ssl/debughelpers.c',
a08450
@@ -2370,9 +2376,7 @@ class PyBuildExt(build_ext):
a08450
 
a08450
         self.add(Extension('_hashlib', ['_hashopenssl.c'],
a08450
                            depends=['hashlib.h'],
a08450
-                           include_dirs=openssl_includes,
a08450
-                           library_dirs=openssl_libdirs,
a08450
-                           libraries=openssl_libs))
a08450
+                           **self.detect_openssl_args()) )
a08450
 
a08450
     def detect_hash_builtins(self):
a08450
         # By default we always compile these even when OpenSSL is available
a08450
@@ -2429,6 +2433,7 @@ class PyBuildExt(build_ext):
a08450
                     '_blake2/blake2b_impl.c',
a08450
                     '_blake2/blake2s_impl.c'
a08450
                 ],
a08450
+                **self.detect_openssl_args(),  # for FIPS_mode verification
a08450
                 depends=blake2_deps
a08450
             ))
a08450
 
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From 76d17b46469d642f2acda31bb5e9e636d69fe945 Mon Sep 17 00:00:00 2001
a08450
From: Charalampos Stratakis <cstratak@redhat.com>
a08450
Date: Fri, 29 Jan 2021 14:16:21 +0100
a08450
Subject: [PATCH 08/11] Use python's fall back crypto implementations only if
a08450
 we are not in FIPS mode
a08450
a08450
---
a08450
 Lib/hashlib.py           | 69 +++-------------------------------------
a08450
 Lib/test/test_hashlib.py | 23 +++++++++++++-
a08450
 2 files changed, 27 insertions(+), 65 deletions(-)
a08450
a08450
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
a08450
index 3e3f4dd..b842f5f 100644
a08450
--- a/Lib/hashlib.py
a08450
+++ b/Lib/hashlib.py
a08450
@@ -67,7 +67,6 @@ algorithms_available = set(__always_supported)
a08450
 __all__ = __always_supported + ('new', 'algorithms_guaranteed',
a08450
                                 'algorithms_available', 'pbkdf2_hmac')
a08450
 
a08450
-
a08450
 __builtin_constructor_cache = {}
a08450
 
a08450
 # Prefer our blake2 implementation (unless in FIPS mode)
a08450
@@ -83,6 +82,8 @@ else:
a08450
 
a08450
 
a08450
 def __get_builtin_constructor(name):
a08450
+    if _hashlib.get_fips_mode():
a08450
+        raise ValueError('unsupported hash type ' + name + '(in FIPS mode)')
a08450
     cache = __builtin_constructor_cache
a08450
     constructor = cache.get(name)
a08450
     if constructor is not None:
a08450
@@ -176,79 +177,19 @@ try:
a08450
     algorithms_available = algorithms_available.union(
a08450
             _hashlib.openssl_md_meth_names)
a08450
 except ImportError:
a08450
-    _hashlib = None
a08450
-    new = __py_new
a08450
-    __get_hash = __get_builtin_constructor
a08450
+    raise  # importing _hashlib should never fail on RHEL
a08450
 
a08450
 try:
a08450
     # OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA
a08450
     from _hashlib import pbkdf2_hmac
a08450
 except ImportError:
a08450
-    _trans_5C = bytes((x ^ 0x5C) for x in range(256))
a08450
-    _trans_36 = bytes((x ^ 0x36) for x in range(256))
a08450
-
a08450
-    def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
a08450
-        """Password based key derivation function 2 (PKCS #5 v2.0)
a08450
-
a08450
-        This Python implementations based on the hmac module about as fast
a08450
-        as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster
a08450
-        for long passwords.
a08450
-        """
a08450
-        if not isinstance(hash_name, str):
a08450
-            raise TypeError(hash_name)
a08450
-
a08450
-        if not isinstance(password, (bytes, bytearray)):
a08450
-            password = bytes(memoryview(password))
a08450
-        if not isinstance(salt, (bytes, bytearray)):
a08450
-            salt = bytes(memoryview(salt))
a08450
-
a08450
-        # Fast inline HMAC implementation
a08450
-        inner = new(hash_name)
a08450
-        outer = new(hash_name)
a08450
-        blocksize = getattr(inner, 'block_size', 64)
a08450
-        if len(password) > blocksize:
a08450
-            password = new(hash_name, password).digest()
a08450
-        password = password + b'\x00' * (blocksize - len(password))
a08450
-        inner.update(password.translate(_trans_36))
a08450
-        outer.update(password.translate(_trans_5C))
a08450
-
a08450
-        def prf(msg, inner=inner, outer=outer):
a08450
-            # PBKDF2_HMAC uses the password as key. We can re-use the same
a08450
-            # digest objects and just update copies to skip initialization.
a08450
-            icpy = inner.copy()
a08450
-            ocpy = outer.copy()
a08450
-            icpy.update(msg)
a08450
-            ocpy.update(icpy.digest())
a08450
-            return ocpy.digest()
a08450
-
a08450
-        if iterations < 1:
a08450
-            raise ValueError(iterations)
a08450
-        if dklen is None:
a08450
-            dklen = outer.digest_size
a08450
-        if dklen < 1:
a08450
-            raise ValueError(dklen)
a08450
-
a08450
-        dkey = b''
a08450
-        loop = 1
a08450
-        from_bytes = int.from_bytes
a08450
-        while len(dkey) < dklen:
a08450
-            prev = prf(salt + loop.to_bytes(4, 'big'))
a08450
-            # endianness doesn't matter here as long to / from use the same
a08450
-            rkey = int.from_bytes(prev, 'big')
a08450
-            for i in range(iterations - 1):
a08450
-                prev = prf(prev)
a08450
-                # rkey = rkey ^ prev
a08450
-                rkey ^= from_bytes(prev, 'big')
a08450
-            loop += 1
a08450
-            dkey += rkey.to_bytes(inner.digest_size, 'big')
a08450
-
a08450
-        return dkey[:dklen]
a08450
+    raise  # importing _hashlib should never fail on RHEL
a08450
 
a08450
 try:
a08450
     # OpenSSL's scrypt requires OpenSSL 1.1+
a08450
     from _hashlib import scrypt
a08450
 except ImportError:
a08450
-    pass
a08450
+    raise  # importing _hashlib should never fail on RHEL
a08450
 
a08450
 
a08450
 for __func_name in __always_supported:
a08450
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
a08450
index 72fdc67..ac9c057 100644
a08450
--- a/Lib/test/test_hashlib.py
a08450
+++ b/Lib/test/test_hashlib.py
a08450
@@ -167,7 +167,13 @@ class HashLibTestCase(unittest.TestCase):
a08450
                         constructors.add(constructor)
a08450
 
a08450
         def add_builtin_constructor(name):
a08450
-            constructor = getattr(hashlib, "__get_builtin_constructor")(name)
a08450
+            try:
a08450
+                constructor = getattr(hashlib, "__get_builtin_constructor")(name)
a08450
+            except ValueError:
a08450
+                if get_fips_mode():
a08450
+                    return
a08450
+                else:
a08450
+                    raise
a08450
             self.constructors_to_test[name].add(constructor)
a08450
 
a08450
         _md5 = self._conditional_import_module('_md5')
a08450
@@ -257,6 +263,20 @@ class HashLibTestCase(unittest.TestCase):
a08450
     def test_new_upper_to_lower(self):
a08450
         self.assertEqual(hashlib.new("SHA256").name, "sha256")
a08450
 
a08450
+    @unittest.skipUnless(get_fips_mode(), "Builtin constructor only usable in FIPS mode")
a08450
+    def test_get_builtin_constructor_fips(self):
a08450
+        get_builtin_constructor = getattr(hashlib,
a08450
+                                          '__get_builtin_constructor')
a08450
+        with self.assertRaises(ValueError):
a08450
+            get_builtin_constructor('md5')
a08450
+        with self.assertRaises(ValueError):
a08450
+            get_builtin_constructor('sha256')
a08450
+        with self.assertRaises(ValueError):
a08450
+            get_builtin_constructor('blake2s')
a08450
+        with self.assertRaises(ValueError):
a08450
+            get_builtin_constructor('test')
a08450
+
a08450
+    @unittest.skipIf(get_fips_mode(), "No builtin constructors in FIPS mode")
a08450
     def test_get_builtin_constructor(self):
a08450
         get_builtin_constructor = getattr(hashlib,
a08450
                                           '__get_builtin_constructor')
a08450
@@ -1052,6 +1072,7 @@ class KDFTests(unittest.TestCase):
a08450
                 iterations=1, dklen=None)
a08450
             self.assertEqual(out, self.pbkdf2_results['sha1'][0][0])
a08450
 
a08450
+    @unittest.skip("The python implementation of pbkdf2_hmac has been removed")
a08450
     @unittest.skipIf(builtin_hashlib is None, "test requires builtin_hashlib")
a08450
     def test_pbkdf2_hmac_py(self):
a08450
         self._test_pbkdf2_hmac(builtin_hashlib.pbkdf2_hmac, builtin_hashes)
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From 95c861dda1659f5bc47d56bed8d096f4debbe281 Mon Sep 17 00:00:00 2001
a08450
From: Charalampos Stratakis <cstratak@redhat.com>
a08450
Date: Wed, 31 Jul 2019 15:43:43 +0200
a08450
Subject: [PATCH 09/11] Test equivalence of hashes for the various digests with
a08450
 usedforsecurity=True/False
a08450
a08450
---
a08450
 Lib/test/test_fips.py    | 24 +++++++++++++++++++++
a08450
 Lib/test/test_hashlib.py | 46 ++++++++++++++++++++++++++++++----------
a08450
 2 files changed, 59 insertions(+), 11 deletions(-)
a08450
 create mode 100644 Lib/test/test_fips.py
a08450
a08450
diff --git a/Lib/test/test_fips.py b/Lib/test/test_fips.py
a08450
new file mode 100644
a08450
index 0000000..1f99dd7
a08450
--- /dev/null
a08450
+++ b/Lib/test/test_fips.py
a08450
@@ -0,0 +1,24 @@
a08450
+import unittest
a08450
+import hashlib, _hashlib
a08450
+
a08450
+
a08450
+
a08450
+class HashlibFipsTests(unittest.TestCase):
a08450
+
a08450
+    @unittest.skipUnless(_hashlib.get_fips_mode(), "Test only when FIPS is enabled")
a08450
+    def test_fips_imports(self):
a08450
+        """blake2s and blake2b should fail to import in FIPS mode
a08450
+        """
a08450
+        with self.assertRaises(ValueError, msg='blake2s not available in FIPS'):
a08450
+            m = hashlib.blake2s()
a08450
+        with self.assertRaises(ValueError, msg='blake2b not available in FIPS'):
a08450
+            m = hashlib.blake2b()
a08450
+
a08450
+    @unittest.skipIf(_hashlib.get_fips_mode(), "blake2 hashes are not available under FIPS")
a08450
+    def test_blake2_hashes(self):
a08450
+        self.assertEqual(hashlib.blake2b(b'abc').hexdigest(), _hashlib.openssl_blake2b(b'abc').hexdigest())
a08450
+        self.assertEqual(hashlib.blake2s(b'abc').hexdigest(), _hashlib.openssl_blake2s(b'abc').hexdigest())
a08450
+
a08450
+
a08450
+if __name__ == "__main__":
a08450
+    unittest.main()
a08450
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
a08450
index ac9c057..0aa0129 100644
a08450
--- a/Lib/test/test_hashlib.py
a08450
+++ b/Lib/test/test_hashlib.py
a08450
@@ -20,6 +20,7 @@ import warnings
a08450
 from test import support
a08450
 from test.support import _4G, bigmemtest, import_fresh_module
a08450
 from http.client import HTTPException
a08450
+from functools import partial
a08450
 
a08450
 # Were we compiled --with-pydebug or with #define Py_DEBUG?
a08450
 COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount')
a08450
@@ -55,6 +56,11 @@ except ImportError:
a08450
     def get_fips_mode():
a08450
         return 0
a08450
 
a08450
+if get_fips_mode():
a08450
+    FIPS_DISABLED = {'md5'}
a08450
+else:
a08450
+    FIPS_DISABLED = set()
a08450
+
a08450
 try:
a08450
     import _blake2
a08450
 except ImportError:
a08450
@@ -96,6 +102,11 @@ def read_vectors(hash_name):
a08450
             parts[0] = bytes.fromhex(parts[0])
a08450
             yield parts
a08450
 
a08450
+def _is_blake2_constructor(constructor):
a08450
+    if isinstance(constructor, partial):
a08450
+        constructor = constructor.func
a08450
+    return  getattr(constructor, '__name__', '').startswith('openssl_blake2')
a08450
+
a08450
 
a08450
 class HashLibTestCase(unittest.TestCase):
a08450
     supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1',
a08450
@@ -138,15 +149,21 @@ class HashLibTestCase(unittest.TestCase):
a08450
         for algorithm in algorithms:
a08450
             self.constructors_to_test[algorithm] = set()
a08450
 
a08450
+        def _add_constructor(algorithm, constructor):
a08450
+            constructors.add(partial(constructor, usedforsecurity=False))
a08450
+            if algorithm not in FIPS_DISABLED:
a08450
+                constructors.add(constructor)
a08450
+                constructors.add(partial(constructor, usedforsecurity=True))
a08450
+
a08450
         # For each algorithm, test the direct constructor and the use
a08450
         # of hashlib.new given the algorithm name.
a08450
         for algorithm, constructors in self.constructors_to_test.items():
a08450
-            constructors.add(getattr(hashlib, algorithm))
a08450
+            _add_constructor(algorithm, getattr(hashlib, algorithm))
a08450
             def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs):
a08450
                 if data is None:
a08450
                     return hashlib.new(_alg, **kwargs)
a08450
                 return hashlib.new(_alg, data, **kwargs)
a08450
-            constructors.add(_test_algorithm_via_hashlib_new)
a08450
+            _add_constructor(algorithm, _test_algorithm_via_hashlib_new)
a08450
 
a08450
         _hashlib = self._conditional_import_module('_hashlib')
a08450
         self._hashlib = _hashlib
a08450
@@ -158,13 +175,7 @@ class HashLibTestCase(unittest.TestCase):
a08450
             for algorithm, constructors in self.constructors_to_test.items():
a08450
                 constructor = getattr(_hashlib, 'openssl_'+algorithm, None)
a08450
                 if constructor:
a08450
-                    try:
a08450
-                        constructor()
a08450
-                    except ValueError:
a08450
-                        # default constructor blocked by crypto policy
a08450
-                        pass
a08450
-                    else:
a08450
-                        constructors.add(constructor)
a08450
+                    _add_constructor(algorithm, constructor)
a08450
 
a08450
         def add_builtin_constructor(name):
a08450
             try:
a08450
@@ -337,6 +348,8 @@ class HashLibTestCase(unittest.TestCase):
a08450
                 self.assertIn(h.name, self.supported_hash_names)
a08450
             else:
a08450
                 self.assertNotIn(h.name, self.supported_hash_names)
a08450
+            if not h.name.startswith('blake2') and h.name not in FIPS_DISABLED:
a08450
+                self.assertEqual(h.name, hashlib.new(h.name).name)
a08450
             self.assertEqual(
a08450
                 h.name,
a08450
                 hashlib.new(h.name, usedforsecurity=False).name
a08450
@@ -383,8 +396,10 @@ class HashLibTestCase(unittest.TestCase):
a08450
         for hash_object_constructor in constructors:
a08450
 
a08450
             # OpenSSL's blake2s & blake2d don't support `key`
a08450
-            _name = hash_object_constructor.__name__
a08450
-            if 'key' in kwargs and _name.startswith('openssl_blake2'):
a08450
+            if (
a08450
+                'key' in kwargs
a08450
+                and _is_blake2_constructor(hash_object_constructor)
a08450
+            ):
a08450
                 return
a08450
 
a08450
             m = hash_object_constructor(data, **kwargs)
a08450
@@ -965,6 +980,15 @@ class HashLibTestCase(unittest.TestCase):
a08450
         ):
a08450
             HASHXOF()
a08450
 
a08450
+    @unittest.skipUnless(get_fips_mode(), 'Needs FIPS mode.')
a08450
+    def test_usedforsecurity_repeat(self):
a08450
+        """Make sure usedforsecurity flag isn't copied to other contexts"""
a08450
+        for i in range(3):
a08450
+            for cons in hashlib.md5, partial(hashlib.new, 'md5'):
a08450
+                self.assertRaises(ValueError, cons)
a08450
+                self.assertRaises(ValueError, partial(cons, usedforsecurity=True))
a08450
+                self.assertEqual(cons(usedforsecurity=False).hexdigest(),
a08450
+                                'd41d8cd98f00b204e9800998ecf8427e')
a08450
 
a08450
 class KDFTests(unittest.TestCase):
a08450
 
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From 62cf5b80d205ff6d6e719286feccca6bd6fdd862 Mon Sep 17 00:00:00 2001
a08450
From: Petr Viktorin <pviktori@redhat.com>
a08450
Date: Mon, 26 Aug 2019 19:39:48 +0200
a08450
Subject: [PATCH 10/11] Guard against Python HMAC in FIPS mode
a08450
a08450
---
a08450
 Lib/hmac.py           | 13 +++++++++----
a08450
 Lib/test/test_hmac.py | 10 ++++++++++
a08450
 2 files changed, 19 insertions(+), 4 deletions(-)
a08450
a08450
diff --git a/Lib/hmac.py b/Lib/hmac.py
a08450
index 8b4f920..20ef96c 100644
a08450
--- a/Lib/hmac.py
a08450
+++ b/Lib/hmac.py
a08450
@@ -16,8 +16,9 @@ else:
a08450
 
a08450
 import hashlib as _hashlib
a08450
 
a08450
-trans_5C = bytes((x ^ 0x5C) for x in range(256))
a08450
-trans_36 = bytes((x ^ 0x36) for x in range(256))
a08450
+if not _hashopenssl.get_fips_mode():
a08450
+    trans_5C = bytes((x ^ 0x5C) for x in range(256))
a08450
+    trans_36 = bytes((x ^ 0x36) for x in range(256))
a08450
 
a08450
 # The size of the digests returned by HMAC depends on the underlying
a08450
 # hashing module used.  Use digest_size from the instance of HMAC instead.
a08450
@@ -48,17 +49,18 @@ class HMAC:
a08450
                    msg argument.  Passing it as a keyword argument is
a08450
                    recommended, though not required for legacy API reasons.
a08450
         """
a08450
-
a08450
         if not isinstance(key, (bytes, bytearray)):
a08450
             raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)
a08450
 
a08450
         if not digestmod:
a08450
             raise TypeError("Missing required parameter 'digestmod'.")
a08450
 
a08450
-        if _hashopenssl and isinstance(digestmod, (str, _functype)):
a08450
+        if _hashopenssl.get_fips_mode() or (_hashopenssl and isinstance(digestmod, (str, _functype))):
a08450
             try:
a08450
                 self._init_hmac(key, msg, digestmod)
a08450
             except _hashopenssl.UnsupportedDigestmodError:
a08450
+                if _hashopenssl.get_fips_mode():
a08450
+                    raise
a08450
                 self._init_old(key, msg, digestmod)
a08450
         else:
a08450
             self._init_old(key, msg, digestmod)
a08450
@@ -69,6 +71,9 @@ class HMAC:
a08450
         self.block_size = self._hmac.block_size
a08450
 
a08450
     def _init_old(self, key, msg, digestmod):
a08450
+        if _hashopenssl.get_fips_mode():
a08450
+            # In FIPS mode, use OpenSSL anyway: raise the appropriate error
a08450
+            return self._init_hmac(key, msg, digestmod)
a08450
         if callable(digestmod):
a08450
             digest_cons = digestmod
a08450
         elif isinstance(digestmod, str):
a08450
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
a08450
index adf52ad..41e6a14 100644
a08450
--- a/Lib/test/test_hmac.py
a08450
+++ b/Lib/test/test_hmac.py
a08450
@@ -5,6 +5,7 @@ import hashlib
a08450
 import unittest
a08450
 import unittest.mock
a08450
 import warnings
a08450
+from _hashlib import get_fips_mode
a08450
 
a08450
 from test.support import hashlib_helper
a08450
 
a08450
@@ -339,6 +340,7 @@ class TestVectorsTestCase(unittest.TestCase):
a08450
     def test_sha512_rfc4231(self):
a08450
         self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)
a08450
 
a08450
+    #unittest.skipIf(get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.')
a08450
     @hashlib_helper.requires_hashdigest('sha256')
a08450
     def test_legacy_block_size_warnings(self):
a08450
         class MockCrazyHash(object):
a08450
@@ -351,6 +353,11 @@ class TestVectorsTestCase(unittest.TestCase):
a08450
             def digest(self):
a08450
                 return self._x.digest()
a08450
 
a08450
+        if get_fips_mode():
a08450
+            with self.assertRaises(ValueError):
a08450
+                hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
a08450
+            return
a08450
+
a08450
         with warnings.catch_warnings():
a08450
             warnings.simplefilter('error', RuntimeWarning)
a08450
             with self.assertRaises(RuntimeWarning):
a08450
@@ -444,6 +451,7 @@ class ConstructorTestCase(unittest.TestCase):
a08450
         ):
a08450
             C_HMAC()
a08450
 
a08450
+    @unittest.skipIf(get_fips_mode(), "_sha256 unavailable in FIPS mode")
a08450
     @unittest.skipUnless(sha256_module is not None, 'need _sha256')
a08450
     def test_with_sha256_module(self):
a08450
         h = hmac.HMAC(b"key", b"hash this!", digestmod=sha256_module.sha256)
a08450
@@ -472,6 +480,7 @@ class SanityTestCase(unittest.TestCase):
a08450
 
a08450
 class CopyTestCase(unittest.TestCase):
a08450
 
a08450
+    @unittest.skipIf(get_fips_mode(), "_init_old unavailable in FIPS mode")
a08450
     @hashlib_helper.requires_hashdigest('sha256')
a08450
     def test_attributes_old(self):
a08450
         # Testing if attributes are of same type.
a08450
@@ -483,6 +492,7 @@ class CopyTestCase(unittest.TestCase):
a08450
         self.assertEqual(type(h1._outer), type(h2._outer),
a08450
             "Types of outer don't match.")
a08450
 
a08450
+    @unittest.skipIf(get_fips_mode(), "_init_old unavailable in FIPS mode")
a08450
     @hashlib_helper.requires_hashdigest('sha256')
a08450
     def test_realcopy_old(self):
a08450
         # Testing if the copy method created a real copy.
a08450
-- 
a08450
2.34.1
a08450
a08450
a08450
From ff7f518d32b7f1c47f35b841da78f5869470e381 Mon Sep 17 00:00:00 2001
a08450
From: Petr Viktorin <encukou@gmail.com>
a08450
Date: Wed, 25 Aug 2021 16:44:43 +0200
a08450
Subject: [PATCH 11/11] Disable hash-based PYCs in FIPS mode
a08450
a08450
If FIPS mode is on, we can't use siphash-based HMAC
a08450
(_Py_KeyedHash), so:
a08450
a08450
- Unchecked hash PYCs can be imported, but not created
a08450
- Checked hash PYCs can not be imported nor created
a08450
- The default mode is timestamp-based PYCs, even if
a08450
  SOURCE_DATE_EPOCH is set.
a08450
a08450
If FIPS mode is off, there are no changes in behavior.
a08450
a08450
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1835169
a08450
---
a08450
 Lib/py_compile.py                             |  2 ++
a08450
 Lib/test/support/__init__.py                  | 14 +++++++++++++
a08450
 Lib/test/test_cmd_line_script.py              |  2 ++
a08450
 Lib/test/test_compileall.py                   | 11 +++++++++-
a08450
 Lib/test/test_imp.py                          |  2 ++
a08450
 .../test_importlib/source/test_file_loader.py |  6 ++++++
a08450
 Lib/test/test_py_compile.py                   | 11 ++++++++--
a08450
 Lib/test/test_zipimport.py                    |  2 ++
a08450
 Python/import.c                               | 20 +++++++++++++++++++
a08450
 9 files changed, 67 insertions(+), 3 deletions(-)
a08450
a08450
diff --git a/Lib/py_compile.py b/Lib/py_compile.py
a08450
index bba3642..02db901 100644
a08450
--- a/Lib/py_compile.py
a08450
+++ b/Lib/py_compile.py
a08450
@@ -70,7 +70,9 @@ class PycInvalidationMode(enum.Enum):
a08450
 
a08450
 
a08450
 def _get_default_invalidation_mode():
a08450
+    import _hashlib
a08450
     if (os.environ.get('SOURCE_DATE_EPOCH') and not
a08450
+            _hashlib.get_fips_mode() and not
a08450
             os.environ.get('RPM_BUILD_ROOT')):
a08450
         return PycInvalidationMode.CHECKED_HASH
a08450
     else:
a08450
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
a08450
index 4ced130..11818ac 100644
a08450
--- a/Lib/test/support/__init__.py
a08450
+++ b/Lib/test/support/__init__.py
a08450
@@ -3249,3 +3249,17 @@ def clear_ignored_deprecations(*tokens: object) -> None:
a08450
     if warnings.filters != new_filters:
a08450
         warnings.filters[:] = new_filters
a08450
         warnings._filters_mutated()
a08450
+
a08450
+
a08450
+def fails_in_fips_mode(expected_error):
a08450
+    import _hashlib
a08450
+    if _hashlib.get_fips_mode():
a08450
+        def _decorator(func):
a08450
+            def _wrapper(self, *args, **kwargs):
a08450
+                with self.assertRaises(expected_error):
a08450
+                    func(self, *args, **kwargs)
a08450
+            return _wrapper
a08450
+    else:
a08450
+        def _decorator(func):
a08450
+            return func
a08450
+    return _decorator
a08450
diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py
a08450
index 7cb1370..61df232 100644
a08450
--- a/Lib/test/test_cmd_line_script.py
a08450
+++ b/Lib/test/test_cmd_line_script.py
a08450
@@ -282,6 +282,7 @@ class CmdLineTest(unittest.TestCase):
a08450
             self._check_script(zip_name, run_name, zip_name, zip_name, '',
a08450
                                zipimport.zipimporter)
a08450
 
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def test_zipfile_compiled_checked_hash(self):
a08450
         with support.temp_dir() as script_dir:
a08450
             script_name = _make_test_script(script_dir, '__main__')
a08450
@@ -292,6 +293,7 @@ class CmdLineTest(unittest.TestCase):
a08450
             self._check_script(zip_name, run_name, zip_name, zip_name, '',
a08450
                                zipimport.zipimporter)
a08450
 
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def test_zipfile_compiled_unchecked_hash(self):
a08450
         with support.temp_dir() as script_dir:
a08450
             script_name = _make_test_script(script_dir, '__main__')
a08450
diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py
a08450
index 6e1f4b2..ec6b165 100644
a08450
--- a/Lib/test/test_compileall.py
a08450
+++ b/Lib/test/test_compileall.py
a08450
@@ -773,14 +773,23 @@ class CommandLineTestsBase:
a08450
         out = self.assertRunOK('badfilename')
a08450
         self.assertRegex(out, b"Can't list 'badfilename'")
a08450
 
a08450
-    def test_pyc_invalidation_mode(self):
a08450
+    @support.fails_in_fips_mode(AssertionError)
a08450
+    def test_pyc_invalidation_mode_checked(self):
a08450
         script_helper.make_script(self.pkgdir, 'f1', '')
a08450
         pyc = importlib.util.cache_from_source(
a08450
             os.path.join(self.pkgdir, 'f1.py'))
a08450
+
a08450
         self.assertRunOK('--invalidation-mode=checked-hash', self.pkgdir)
a08450
         with open(pyc, 'rb') as fp:
a08450
             data = fp.read()
a08450
         self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b11)
a08450
+
a08450
+    @support.fails_in_fips_mode(AssertionError)
a08450
+    def test_pyc_invalidation_mode_unchecked(self):
a08450
+        script_helper.make_script(self.pkgdir, 'f1', '')
a08450
+        pyc = importlib.util.cache_from_source(
a08450
+            os.path.join(self.pkgdir, 'f1.py'))
a08450
+
a08450
         self.assertRunOK('--invalidation-mode=unchecked-hash', self.pkgdir)
a08450
         with open(pyc, 'rb') as fp:
a08450
             data = fp.read()
a08450
diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py
a08450
index fe394dc..802f0e8 100644
a08450
--- a/Lib/test/test_imp.py
a08450
+++ b/Lib/test/test_imp.py
a08450
@@ -343,6 +343,7 @@ class ImportTests(unittest.TestCase):
a08450
         import _frozen_importlib
a08450
         self.assertEqual(_frozen_importlib.__spec__.origin, "frozen")
a08450
 
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def test_source_hash(self):
a08450
         self.assertEqual(_imp.source_hash(42, b'hi'), b'\xc6\xe7Z\r\x03:}\xab')
a08450
         self.assertEqual(_imp.source_hash(43, b'hi'), b'\x85\x9765\xf8\x9a\x8b9')
a08450
@@ -362,6 +363,7 @@ class ImportTests(unittest.TestCase):
a08450
             res = script_helper.assert_python_ok(*args)
a08450
             self.assertEqual(res.out.strip().decode('utf-8'), expected)
a08450
 
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def test_find_and_load_checked_pyc(self):
a08450
         # issue 34056
a08450
         with support.temp_cwd():
a08450
diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py
a08450
index ab44722..480cc81 100644
a08450
--- a/Lib/test/test_importlib/source/test_file_loader.py
a08450
+++ b/Lib/test/test_importlib/source/test_file_loader.py
a08450
@@ -17,6 +17,7 @@ import types
a08450
 import unittest
a08450
 import warnings
a08450
 
a08450
+from test import support
a08450
 from test.support import make_legacy_pyc, unload
a08450
 
a08450
 from test.test_py_compile import without_source_date_epoch
a08450
@@ -239,6 +240,7 @@ class SimpleTest(abc.LoaderTests):
a08450
                 loader.load_module('bad name')
a08450
 
a08450
     @util.writes_bytecode_files
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def test_checked_hash_based_pyc(self):
a08450
         with util.create_modules('_temp') as mapping:
a08450
             source = mapping['_temp']
a08450
@@ -270,6 +272,7 @@ class SimpleTest(abc.LoaderTests):
a08450
             )
a08450
 
a08450
     @util.writes_bytecode_files
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def test_overridden_checked_hash_based_pyc(self):
a08450
         with util.create_modules('_temp') as mapping, \
a08450
              unittest.mock.patch('_imp.check_hash_based_pycs', 'never'):
a08450
@@ -295,6 +298,7 @@ class SimpleTest(abc.LoaderTests):
a08450
             self.assertEqual(mod.state, 'old')
a08450
 
a08450
     @util.writes_bytecode_files
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def test_unchecked_hash_based_pyc(self):
a08450
         with util.create_modules('_temp') as mapping:
a08450
             source = mapping['_temp']
a08450
@@ -325,6 +329,7 @@ class SimpleTest(abc.LoaderTests):
a08450
             )
a08450
 
a08450
     @util.writes_bytecode_files
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def test_overridden_unchecked_hash_based_pyc(self):
a08450
         with util.create_modules('_temp') as mapping, \
a08450
              unittest.mock.patch('_imp.check_hash_based_pycs', 'always'):
a08450
@@ -434,6 +439,7 @@ class BadBytecodeTest:
a08450
                                                del_source=del_source)
a08450
             test('_temp', mapping, bc_path)
a08450
 
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def _test_partial_hash(self, test, *, del_source=False):
a08450
         with util.create_modules('_temp') as mapping:
a08450
             bc_path = self.manipulate_bytecode(
a08450
diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py
a08450
index b2d3dcf..7e4b0c5 100644
a08450
--- a/Lib/test/test_py_compile.py
a08450
+++ b/Lib/test/test_py_compile.py
a08450
@@ -139,13 +139,16 @@ class PyCompileTestsBase:
a08450
             importlib.util.cache_from_source(bad_coding)))
a08450
 
a08450
     def test_source_date_epoch(self):
a08450
+        import _hashlib
a08450
         py_compile.compile(self.source_path, self.pyc_path)
a08450
         self.assertTrue(os.path.exists(self.pyc_path))
a08450
         self.assertFalse(os.path.exists(self.cache_path))
a08450
         with open(self.pyc_path, 'rb') as fp:
a08450
             flags = importlib._bootstrap_external._classify_pyc(
a08450
                 fp.read(), 'test', {})
a08450
-        if os.environ.get('SOURCE_DATE_EPOCH'):
a08450
+        if _hashlib.get_fips_mode():
a08450
+            expected_flags = 0b00
a08450
+        elif os.environ.get('SOURCE_DATE_EPOCH'):
a08450
             expected_flags = 0b11
a08450
         else:
a08450
             expected_flags = 0b00
a08450
@@ -176,7 +179,8 @@ class PyCompileTestsBase:
a08450
         # Specifying optimized bytecode should lead to a path reflecting that.
a08450
         self.assertIn('opt-2', py_compile.compile(self.source_path, optimize=2))
a08450
 
a08450
-    def test_invalidation_mode(self):
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
+    def test_invalidation_mode_checked(self):
a08450
         py_compile.compile(
a08450
             self.source_path,
a08450
             invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH,
a08450
@@ -185,6 +189,9 @@ class PyCompileTestsBase:
a08450
             flags = importlib._bootstrap_external._classify_pyc(
a08450
                 fp.read(), 'test', {})
a08450
         self.assertEqual(flags, 0b11)
a08450
+
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
+    def test_invalidation_mode_unchecked(self):
a08450
         py_compile.compile(
a08450
             self.source_path,
a08450
             invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH,
a08450
diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py
a08450
index 2e24388..11e7978 100644
a08450
--- a/Lib/test/test_zipimport.py
a08450
+++ b/Lib/test/test_zipimport.py
a08450
@@ -186,6 +186,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
a08450
                  TESTMOD + pyc_ext: (NOW, test_pyc)}
a08450
         self.doTest(pyc_ext, files, TESTMOD)
a08450
 
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     def testUncheckedHashBasedPyc(self):
a08450
         source = b"state = 'old'"
a08450
         source_hash = importlib.util.source_hash(source)
a08450
@@ -200,6 +201,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase):
a08450
             self.assertEqual(mod.state, 'old')
a08450
         self.doTest(None, files, TESTMOD, call=check)
a08450
 
a08450
+    @support.fails_in_fips_mode(ImportError)
a08450
     @unittest.mock.patch('_imp.check_hash_based_pycs', 'always')
a08450
     def test_checked_hash_based_change_pyc(self):
a08450
         source = b"state = 'old'"
a08450
diff --git a/Python/import.c b/Python/import.c
a08450
index 8358d70..1b7fb85 100644
a08450
--- a/Python/import.c
a08450
+++ b/Python/import.c
a08450
@@ -2354,6 +2354,26 @@ static PyObject *
a08450
 _imp_source_hash_impl(PyObject *module, long key, Py_buffer *source)
a08450
 /*[clinic end generated code: output=edb292448cf399ea input=9aaad1e590089789]*/
a08450
 {
a08450
+    PyObject *_hashlib = PyImport_ImportModule("_hashlib");
a08450
+    if (_hashlib == NULL) {
a08450
+        return NULL;
a08450
+    }
a08450
+    PyObject *fips_mode_obj = PyObject_CallMethod(_hashlib, "get_fips_mode", NULL);
a08450
+    Py_DECREF(_hashlib);
a08450
+    if (fips_mode_obj == NULL) {
a08450
+        return NULL;
a08450
+    }
a08450
+    int fips_mode = PyObject_IsTrue(fips_mode_obj);
a08450
+    Py_DECREF(fips_mode_obj);
a08450
+    if (fips_mode < 0) {
a08450
+        return NULL;
a08450
+    }
a08450
+    if (fips_mode) {
a08450
+        PyErr_SetString(
a08450
+            PyExc_ImportError,
a08450
+            "hash-based PYC validation (siphash24) not available in FIPS mode");
a08450
+        return NULL;
a08450
+    };
a08450
     union {
a08450
         uint64_t x;
a08450
         char data[sizeof(uint64_t)];
a08450
-- 
a08450
2.34.1
a08450