Blame SOURCES/00329-fips.patch

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