Blame SOURCES/00329-fips.patch

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