Skip to content
Snippets Groups Projects
00329-fips.patch 34 KiB
Newer Older
From 4345f8ea8a56a58ef8a48439c0e201702d1012a2 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Thu, 12 Dec 2019 16:58:31 +0100
Subject: [PATCH 1/7] Expose blake2b and blake2s hashes from OpenSSL

These aren't as powerful as Python's own implementation, but they can be
used under FIPS.
---
 Lib/test/test_hashlib.py        |   6 ++
 Modules/_hashopenssl.c          |  37 +++++++++++
 Modules/clinic/_hashopenssl.c.h | 106 +++++++++++++++++++++++++++++++-
 3 files changed, 148 insertions(+), 1 deletion(-)

diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 67becdd..6607ef7 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -363,6 +363,12 @@ class HashLibTestCase(unittest.TestCase):
         # 2 is for hashlib.name(...) and hashlib.new(name, ...)
         self.assertGreaterEqual(len(constructors), 2)
         for hash_object_constructor in constructors:
+
+            # OpenSSL's blake2s & blake2d don't support `key`
+            _name = hash_object_constructor.__name__
+            if 'key' in kwargs and _name.startswith('openssl_blake2'):
+                return
+
             m = hash_object_constructor(data, **kwargs)
             computed = m.hexdigest() if not shake else m.hexdigest(length)
             self.assertEqual(
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index 57d64bd..d0c3b9e 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -1078,6 +1078,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj,
 }
 
 
+/*[clinic input]
+_hashlib.openssl_blake2b
+    string as data_obj: object(py_default="b''") = NULL
+    *
+    usedforsecurity: bool = True
+Returns a blake2b hash object; optionally initialized with a string
+[clinic start generated code]*/
+
+static PyObject *
+_hashlib_openssl_blake2b_impl(PyObject *module, PyObject *data_obj,
+                              int usedforsecurity)
+/*[clinic end generated code: output=7a838b1643cde13e input=4ad7fd54268f3689]*/
+
+{
+    return py_evp_fromname(module, Py_hash_blake2b, data_obj, usedforsecurity);
+}
+
+/*[clinic input]
+_hashlib.openssl_blake2s
+    string as data_obj: object(py_default="b''") = NULL
+    *
+    usedforsecurity: bool = True
+Returns a blake2s hash object; optionally initialized with a string
+[clinic start generated code]*/
+
+static PyObject *
+_hashlib_openssl_blake2s_impl(PyObject *module, PyObject *data_obj,
+                              int usedforsecurity)
+/*[clinic end generated code: output=4eda6b40757471da input=1ed39481ffa4e26a]*/
+
+{
+    return py_evp_fromname(module, Py_hash_blake2s, data_obj, usedforsecurity);
+}
+
+
 #ifdef PY_OPENSSL_HAS_SHA3
 
 /*[clinic input]
@@ -2066,6 +2101,8 @@ static struct PyMethodDef EVP_functions[] = {
     _HASHLIB_OPENSSL_SHA256_METHODDEF
     _HASHLIB_OPENSSL_SHA384_METHODDEF
     _HASHLIB_OPENSSL_SHA512_METHODDEF
+    _HASHLIB_OPENSSL_BLAKE2B_METHODDEF
+    _HASHLIB_OPENSSL_BLAKE2S_METHODDEF
     _HASHLIB_OPENSSL_SHA3_224_METHODDEF
     _HASHLIB_OPENSSL_SHA3_256_METHODDEF
     _HASHLIB_OPENSSL_SHA3_384_METHODDEF
diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h
index 5d84f4a..011026a 100644
--- a/Modules/clinic/_hashopenssl.c.h
+++ b/Modules/clinic/_hashopenssl.c.h
@@ -530,6 +530,110 @@ exit:
     return return_value;
 }
 
+PyDoc_STRVAR(_hashlib_openssl_blake2b__doc__,
+"openssl_blake2b($module, /, string=b\'\', *, usedforsecurity=True)\n"
+"--\n"
+"\n"
+"Returns a blake2b hash object; optionally initialized with a string");
+
+#define _HASHLIB_OPENSSL_BLAKE2B_METHODDEF    \
+    {"openssl_blake2b", _PyCFunction_CAST(_hashlib_openssl_blake2b), METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_blake2b__doc__},
+
+static PyObject *
+_hashlib_openssl_blake2b_impl(PyObject *module, PyObject *data_obj,
+                              int usedforsecurity);
+
+static PyObject *
+_hashlib_openssl_blake2b(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+    PyObject *return_value = NULL;
+    static const char * const _keywords[] = {"string", "usedforsecurity", NULL};
+    static _PyArg_Parser _parser = {NULL, _keywords, "openssl_blake2b", 0};
+    PyObject *argsbuf[2];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
+    PyObject *data_obj = NULL;
+    int usedforsecurity = 1;
+
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
+    if (!args) {
+        goto exit;
+    }
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    if (args[0]) {
+        data_obj = args[0];
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+skip_optional_pos:
+    if (!noptargs) {
+        goto skip_optional_kwonly;
+    }
+    usedforsecurity = PyObject_IsTrue(args[1]);
+    if (usedforsecurity < 0) {
+        goto exit;
+    }
+skip_optional_kwonly:
+    return_value = _hashlib_openssl_blake2b_impl(module, data_obj, usedforsecurity);
+
+exit:
+    return return_value;
+}
+
+PyDoc_STRVAR(_hashlib_openssl_blake2s__doc__,
+"openssl_blake2s($module, /, string=b\'\', *, usedforsecurity=True)\n"
+"--\n"
+"\n"
+"Returns a blake2s hash object; optionally initialized with a string");
+
+#define _HASHLIB_OPENSSL_BLAKE2S_METHODDEF    \
+    {"openssl_blake2s", _PyCFunction_CAST(_hashlib_openssl_blake2s), METH_FASTCALL|METH_KEYWORDS, _hashlib_openssl_blake2s__doc__},
+
+static PyObject *
+_hashlib_openssl_blake2s_impl(PyObject *module, PyObject *data_obj,
+                              int usedforsecurity);
+
+static PyObject *
+_hashlib_openssl_blake2s(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+    PyObject *return_value = NULL;
+    static const char * const _keywords[] = {"string", "usedforsecurity", NULL};
+    static _PyArg_Parser _parser = {NULL, _keywords, "openssl_blake2s", 0};
+    PyObject *argsbuf[2];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
+    PyObject *data_obj = NULL;
+    int usedforsecurity = 1;
+
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
+    if (!args) {
+        goto exit;
+    }
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    if (args[0]) {
+        data_obj = args[0];
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+skip_optional_pos:
+    if (!noptargs) {
+        goto skip_optional_kwonly;
+    }
+    usedforsecurity = PyObject_IsTrue(args[1]);
+    if (usedforsecurity < 0) {
+        goto exit;
+    }
+skip_optional_kwonly:
+    return_value = _hashlib_openssl_blake2s_impl(module, data_obj, usedforsecurity);
+
+exit:
+    return return_value;
+}
+
 #if defined(PY_OPENSSL_HAS_SHA3)
 
 PyDoc_STRVAR(_hashlib_openssl_sha3_224__doc__,
@@ -1385,4 +1489,4 @@ exit:
 #ifndef _HASHLIB_SCRYPT_METHODDEF
     #define _HASHLIB_SCRYPT_METHODDEF
 #endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */
-/*[clinic end generated code: output=69f2374071bff707 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=c6a9af5563972eda input=a9049054013a1b77]*/
-- 
From 1f79be1a11ad6811913c239da980c5bab0f1c538 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 1 Aug 2019 17:57:05 +0200
Subject: [PATCH 2/7] Use a stronger hash in multiprocessing handshake

Adapted from patch by David Malcolm,
https://bugs.python.org/issue17258
---
 Lib/multiprocessing/connection.py | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
index 59c61d2..7fc594e 100644
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -43,6 +43,10 @@ BUFSIZE = 8192
 # A very generous timeout when it comes to local connections...
 CONNECTION_TIMEOUT = 20.
 
+# The hmac module implicitly defaults to using MD5.
+# Support using a stronger algorithm for the challenge/response code:
+HMAC_DIGEST_NAME='sha256'
+
 _mmap_counter = itertools.count()
 
 default_family = 'AF_INET'
@@ -753,7 +757,7 @@ def deliver_challenge(connection, authkey):
             "Authkey must be bytes, not {0!s}".format(type(authkey)))
     message = os.urandom(MESSAGE_LENGTH)
     connection.send_bytes(CHALLENGE + message)
-    digest = hmac.new(authkey, message, 'md5').digest()
+    digest = hmac.new(authkey, message, HMAC_DIGEST_NAME).digest()
     response = connection.recv_bytes(256)        # reject large message
     if response == digest:
         connection.send_bytes(WELCOME)
@@ -769,7 +773,7 @@ def answer_challenge(connection, authkey):
     message = connection.recv_bytes(256)         # reject large message
     assert message[:len(CHALLENGE)] == CHALLENGE, 'message = %r' % message
     message = message[len(CHALLENGE):]
-    digest = hmac.new(authkey, message, 'md5').digest()
+    digest = hmac.new(authkey, message, HMAC_DIGEST_NAME).digest()
     connection.send_bytes(digest)
     response = connection.recv_bytes(256)        # reject large message
     if response != WELCOME:
-- 
From e069ed2dcd0edf0de489eb387267fb35a92ed506 Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Thu, 25 Jul 2019 17:19:06 +0200
Subject: [PATCH 3/7] Disable Python's hash implementations in FIPS mode,
 forcing OpenSSL

---
 Lib/hashlib.py                 | 11 +++++++----
 Lib/test/test_hashlib.py       | 17 ++++++++++++-----
 Modules/_blake2/blake2b_impl.c |  4 ++++
 Modules/_blake2/blake2module.c |  3 +++
 Modules/_blake2/blake2s_impl.c |  4 ++++
 Modules/hashlib.h              | 23 +++++++++++++++++++++++
 configure.ac                   |  3 ++-
 7 files changed, 55 insertions(+), 10 deletions(-)

diff --git a/Lib/hashlib.py b/Lib/hashlib.py
index b546a3f..8b835a4 100644
--- a/Lib/hashlib.py
+++ b/Lib/hashlib.py
@@ -70,14 +70,17 @@ __all__ = __always_supported + ('new', 'algorithms_guaranteed',
 
 __builtin_constructor_cache = {}
 
-# Prefer our blake2 implementation
+# Prefer our blake2 implementation (unless in FIPS mode)
 # OpenSSL 1.1.0 comes with a limited implementation of blake2b/s. The OpenSSL
 # implementations neither support keyed blake2 (blake2 MAC) nor advanced
 # features like salt, personalization, or tree hashing. OpenSSL hash-only
 # variants are available as 'blake2b512' and 'blake2s256', though.
-__block_openssl_constructor = {
-    'blake2b', 'blake2s',
-}
+import _hashlib
+if _hashlib.get_fips_mode():
+    __block_openssl_constructor = set()
+else:
+    __block_openssl_constructor = {'blake2b', 'blake2s'}
+
 
 def __get_builtin_constructor(name):
     cache = __builtin_constructor_cache
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 6607ef7..01d12f5 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -40,14 +40,15 @@ else:
         m.strip() for m in builtin_hashes.strip('"').lower().split(",")
     }
 
-# hashlib with and without OpenSSL backend for PBKDF2
-# only import builtin_hashlib when all builtin hashes are available.
-# Otherwise import prints noise on stderr
+# RHEL: `_hashlib` is always importable and `hashlib` can't be imported
+# without it.
 openssl_hashlib = import_fresh_module('hashlib', fresh=['_hashlib'])
-if builtin_hashes == default_builtin_hashes:
+try:
     builtin_hashlib = import_fresh_module('hashlib', blocked=['_hashlib'])
-else:
+except ImportError:
     builtin_hashlib = None
+else:
+    raise AssertionError('hashlib is importablee without _hashlib')
 
 try:
     from _hashlib import HASH, HASHXOF, openssl_md_meth_names, get_fips_mode
@@ -118,6 +119,12 @@ class HashLibTestCase(unittest.TestCase):
         except ModuleNotFoundError as error:
             if self._warn_on_extension_import and module_name in builtin_hashes:
                 warnings.warn('Did a C extension fail to compile? %s' % error)
+        except ImportError:
+            if get_fips_mode() and module_name == '_blake2':
+                # blake2b & blake2s disabled under FIPS
+                return None
+            else:
+                raise
         return None
 
     def __init__(self, *args, **kwargs):
diff --git a/Modules/_blake2/blake2b_impl.c b/Modules/_blake2/blake2b_impl.c
index c2cac98..55b1677 100644
--- a/Modules/_blake2/blake2b_impl.c
+++ b/Modules/_blake2/blake2b_impl.c
@@ -98,6 +98,8 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
     BLAKE2bObject *self = NULL;
     Py_buffer buf;
 
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
+
     self = new_BLAKE2bObject(type);
     if (self == NULL) {
         goto error;
@@ -276,6 +278,8 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data)
 {
     Py_buffer buf;
 
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
+
     GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
 
     if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
diff --git a/Modules/_blake2/blake2module.c b/Modules/_blake2/blake2module.c
index 93478f5..e3a024d 100644
--- a/Modules/_blake2/blake2module.c
+++ b/Modules/_blake2/blake2module.c
@@ -13,6 +13,7 @@
 #endif
 
 #include "Python.h"
+#include "../hashlib.h"
 #include "blake2module.h"
 
 extern PyType_Spec blake2b_type_spec;
@@ -83,6 +84,7 @@ _blake2_free(void *module)
 static int
 blake2_exec(PyObject *m)
 {
+
     Blake2State* st = blake2_get_state(m);
 
     st->blake2b_type = (PyTypeObject *)PyType_FromModuleAndSpec(
@@ -154,5 +156,6 @@ static struct PyModuleDef blake2_module = {
 PyMODINIT_FUNC
 PyInit__blake2(void)
 {
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ImportError, "blake2");
     return PyModuleDef_Init(&blake2_module);
 }
\ No newline at end of file
diff --git a/Modules/_blake2/blake2s_impl.c b/Modules/_blake2/blake2s_impl.c
index 1c47328..cd4a202 100644
--- a/Modules/_blake2/blake2s_impl.c
+++ b/Modules/_blake2/blake2s_impl.c
@@ -98,6 +98,8 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
     BLAKE2sObject *self = NULL;
     Py_buffer buf;
 
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
+
     self = new_BLAKE2sObject(type);
     if (self == NULL) {
         goto error;
@@ -276,6 +278,8 @@ _blake2_blake2s_update(BLAKE2sObject *self, PyObject *data)
 {
     Py_buffer buf;
 
+    FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
+
     GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
 
     if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
diff --git a/Modules/hashlib.h b/Modules/hashlib.h
index 56ae7a5..45fb403 100644
--- a/Modules/hashlib.h
+++ b/Modules/hashlib.h
@@ -1,5 +1,11 @@
 /* Common code for use by all hashlib related modules. */
 
+// RHEL: use OpenSSL to turn off unsupported modules under FIPS mode
+// EVP_default_properties_is_fips_enabled() on OpenSSL >= 3.0.0
+#include <openssl/evp.h>
+// FIPS_mode() on OpenSSL < 3.0.0
+#include <openssl/crypto.h>
+
 /*
  * Given a PyObject* obj, fill in the Py_buffer* viewp with the result
  * of PyObject_GetBuffer.  Sets an exception and issues the erraction
@@ -57,3 +63,20 @@
  * to allow the user to optimize based on the platform they're using. */
 #define HASHLIB_GIL_MINSIZE 2048
 
+__attribute__((__unused__))
+static int
+_Py_hashlib_fips_error(PyObject *exc, char *name) {
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+    if (EVP_default_properties_is_fips_enabled(NULL)) {
+#else
+    if (FIPS_mode()) {
+#endif
+        PyErr_Format(exc, "%s is not available in FIPS mode", name);
+        return 1;
+    }
+    return 0;
+}
+
+#define FAIL_RETURN_IN_FIPS_MODE(exc, name) do { \
+    if (_Py_hashlib_fips_error(exc, name)) return NULL; \
+} while (0)
diff --git a/configure.ac b/configure.ac
index 7b4000f..8e2f0ad 100644
--- a/configure.ac
+++ b/configure.ac
@@ -7070,7 +7070,8 @@ PY_STDLIB_MOD([_sha512], [test "$with_builtin_sha512" = yes])
 PY_STDLIB_MOD([_sha3], [test "$with_builtin_sha3" = yes])
 PY_STDLIB_MOD([_blake2],
   [test "$with_builtin_blake2" = yes], [],
-  [$LIBB2_CFLAGS], [$LIBB2_LIBS])
+  [$LIBB2_CFLAGS $OPENSSL_INCLUDES],
+  [$LIBB2_LIBS $OPENSSL_LDFLAGS $OPENSSL_LDFLAGS_RPATH $OPENSSL_LIBS])
 
 PY_STDLIB_MOD([_crypt],
   [], [test "$ac_cv_crypt_crypt" = yes],
-- 
From 2e0c5086f4a52803595e19795111278c3c80ee2f Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Fri, 29 Jan 2021 14:16:21 +0100
Subject: [PATCH 4/7] Use python's fall back crypto implementations only if we
 are not in FIPS mode

---
 Lib/hashlib.py           | 72 +++-------------------------------------
 Lib/test/test_hashlib.py | 23 ++++++++++++-
 2 files changed, 27 insertions(+), 68 deletions(-)

diff --git a/Lib/hashlib.py b/Lib/hashlib.py
index 8b835a4..d0834c2 100644
--- a/Lib/hashlib.py
+++ b/Lib/hashlib.py
@@ -83,6 +83,8 @@ else:
 
 
 def __get_builtin_constructor(name):
+    if _hashlib.get_fips_mode():
+        raise ValueError('unsupported hash type ' + name + '(in FIPS mode)')
     cache = __builtin_constructor_cache
     constructor = cache.get(name)
     if constructor is not None:
@@ -178,83 +180,19 @@ try:
 except ImportError:
     _hashlib = None
     new = __py_new
-    __get_hash = __get_builtin_constructor
+    raise  # importing _hashlib should never fail on RHEL
 
 try:
     # OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA
     from _hashlib import pbkdf2_hmac
 except ImportError:
-    from warnings import warn as _warn
-    _trans_5C = bytes((x ^ 0x5C) for x in range(256))
-    _trans_36 = bytes((x ^ 0x36) for x in range(256))
-
-    def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
-        """Password based key derivation function 2 (PKCS #5 v2.0)
-
-        This Python implementations based on the hmac module about as fast
-        as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster
-        for long passwords.
-        """
-        _warn(
-            "Python implementation of pbkdf2_hmac() is deprecated.",
-            category=DeprecationWarning,
-            stacklevel=2
-        )
-        if not isinstance(hash_name, str):
-            raise TypeError(hash_name)
-
-        if not isinstance(password, (bytes, bytearray)):
-            password = bytes(memoryview(password))
-        if not isinstance(salt, (bytes, bytearray)):
-            salt = bytes(memoryview(salt))
-
-        # Fast inline HMAC implementation
-        inner = new(hash_name)
-        outer = new(hash_name)
-        blocksize = getattr(inner, 'block_size', 64)
-        if len(password) > blocksize:
-            password = new(hash_name, password).digest()
-        password = password + b'\x00' * (blocksize - len(password))
-        inner.update(password.translate(_trans_36))
-        outer.update(password.translate(_trans_5C))
-
-        def prf(msg, inner=inner, outer=outer):
-            # PBKDF2_HMAC uses the password as key. We can re-use the same
-            # digest objects and just update copies to skip initialization.
-            icpy = inner.copy()
-            ocpy = outer.copy()
-            icpy.update(msg)
-            ocpy.update(icpy.digest())
-            return ocpy.digest()
-
-        if iterations < 1:
-            raise ValueError(iterations)
-        if dklen is None:
-            dklen = outer.digest_size
-        if dklen < 1:
-            raise ValueError(dklen)
-
-        dkey = b''
-        loop = 1
-        from_bytes = int.from_bytes
-        while len(dkey) < dklen:
-            prev = prf(salt + loop.to_bytes(4))
-            # endianness doesn't matter here as long to / from use the same
-            rkey = from_bytes(prev)
-            for i in range(iterations - 1):
-                prev = prf(prev)
-                # rkey = rkey ^ prev
-                rkey ^= from_bytes(prev)
-            loop += 1
-            dkey += rkey.to_bytes(inner.digest_size)
-
-        return dkey[:dklen]
+    raise  # importing _hashlib should never fail on RHEL
 
 try:
     # OpenSSL's scrypt requires OpenSSL 1.1+
     from _hashlib import scrypt
 except ImportError:
-    pass
+    raise  # importing _hashlib should never fail on RHEL
 
 
 def file_digest(fileobj, digest, /, *, _bufsize=2**18):
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 01d12f5..a7cdb07 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -171,7 +171,13 @@ class HashLibTestCase(unittest.TestCase):
                         constructors.add(constructor)
 
         def add_builtin_constructor(name):
-            constructor = getattr(hashlib, "__get_builtin_constructor")(name)
+            try:
+                constructor = getattr(hashlib, "__get_builtin_constructor")(name)
+            except ValueError:
+                if get_fips_mode():
+                    return
+                else:
+                    raise
             self.constructors_to_test[name].add(constructor)
 
         _md5 = self._conditional_import_module('_md5')
@@ -266,6 +272,20 @@ class HashLibTestCase(unittest.TestCase):
     def test_new_upper_to_lower(self):
         self.assertEqual(hashlib.new("SHA256").name, "sha256")
 
+    @unittest.skipUnless(get_fips_mode(), "Builtin constructor only usable in FIPS mode")
+    def test_get_builtin_constructor_fips(self):
+        get_builtin_constructor = getattr(hashlib,
+                                          '__get_builtin_constructor')
+        with self.assertRaises(ValueError):
+            get_builtin_constructor('md5')
+        with self.assertRaises(ValueError):
+            get_builtin_constructor('sha256')
+        with self.assertRaises(ValueError):
+            get_builtin_constructor('blake2s')
+        with self.assertRaises(ValueError):
+            get_builtin_constructor('test')
+
+    @unittest.skipIf(get_fips_mode(), "No builtin constructors in FIPS mode")
     def test_get_builtin_constructor(self):
         get_builtin_constructor = getattr(hashlib,
                                           '__get_builtin_constructor')
@@ -1111,6 +1131,7 @@ class KDFTests(unittest.TestCase):
                 iterations=1, dklen=None)
             self.assertEqual(out, self.pbkdf2_results['sha1'][0][0])
 
+    @unittest.skip("The python implementation of pbkdf2_hmac has been removed")
     @unittest.skipIf(builtin_hashlib is None, "test requires builtin_hashlib")
     def test_pbkdf2_hmac_py(self):
         with warnings_helper.check_warnings():
-- 
From 0e1d2a67ef66cccc9afa4a515dc34ce587946f22 Mon Sep 17 00:00:00 2001
From: Charalampos Stratakis <cstratak@redhat.com>
Date: Wed, 31 Jul 2019 15:43:43 +0200
Subject: [PATCH 5/7] Test equivalence of hashes for the various digests with
 usedforsecurity=True/False

---
 Lib/test/test_fips.py    | 24 ++++++++++++++++++++
 Lib/test/test_hashlib.py | 47 ++++++++++++++++++++++++++++++----------
 2 files changed, 60 insertions(+), 11 deletions(-)
 create mode 100644 Lib/test/test_fips.py

diff --git a/Lib/test/test_fips.py b/Lib/test/test_fips.py
new file mode 100644
index 0000000..1f99dd7
--- /dev/null
+++ b/Lib/test/test_fips.py
@@ -0,0 +1,24 @@
+import unittest
+import hashlib, _hashlib
+
+
+
+class HashlibFipsTests(unittest.TestCase):
+
+    @unittest.skipUnless(_hashlib.get_fips_mode(), "Test only when FIPS is enabled")
+    def test_fips_imports(self):
+        """blake2s and blake2b should fail to import in FIPS mode
+        """
+        with self.assertRaises(ValueError, msg='blake2s not available in FIPS'):
+            m = hashlib.blake2s()
+        with self.assertRaises(ValueError, msg='blake2b not available in FIPS'):
+            m = hashlib.blake2b()
+
+    @unittest.skipIf(_hashlib.get_fips_mode(), "blake2 hashes are not available under FIPS")
+    def test_blake2_hashes(self):
+        self.assertEqual(hashlib.blake2b(b'abc').hexdigest(), _hashlib.openssl_blake2b(b'abc').hexdigest())
+        self.assertEqual(hashlib.blake2s(b'abc').hexdigest(), _hashlib.openssl_blake2s(b'abc').hexdigest())
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index a7cdb07..c071f28 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -25,6 +25,7 @@ from test.support import os_helper
 from test.support import threading_helper
 from test.support import warnings_helper
 from http.client import HTTPException
+from functools import partial
 
 # Were we compiled --with-pydebug or with #define Py_DEBUG?
 COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount')
@@ -60,6 +61,11 @@ except ImportError:
     def get_fips_mode():
         return 0
 
+if get_fips_mode():
+    FIPS_DISABLED = {'md5'}
+else:
+    FIPS_DISABLED = set()
+
 try:
     import _blake2
 except ImportError:
@@ -98,6 +104,11 @@ def read_vectors(hash_name):
             parts[0] = bytes.fromhex(parts[0])
             yield parts
 
+def _is_blake2_constructor(constructor):
+    if isinstance(constructor, partial):
+        constructor = constructor.func
+    return  getattr(constructor, '__name__', '').startswith('openssl_blake2')
+
 
 class HashLibTestCase(unittest.TestCase):
     supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1',
@@ -142,15 +153,21 @@ class HashLibTestCase(unittest.TestCase):
                 continue
             self.constructors_to_test[algorithm] = set()
 
+        def _add_constructor(algorithm, constructor):
+            constructors.add(partial(constructor, usedforsecurity=False))
+            if algorithm not in FIPS_DISABLED:
+                constructors.add(constructor)
+                constructors.add(partial(constructor, usedforsecurity=True))
+
         # For each algorithm, test the direct constructor and the use
         # of hashlib.new given the algorithm name.
         for algorithm, constructors in self.constructors_to_test.items():
-            constructors.add(getattr(hashlib, algorithm))
+            _add_constructor(algorithm, getattr(hashlib, algorithm))
             def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs):
                 if data is None:
                     return hashlib.new(_alg, **kwargs)
                 return hashlib.new(_alg, data, **kwargs)
-            constructors.add(_test_algorithm_via_hashlib_new)
+            _add_constructor(algorithm, _test_algorithm_via_hashlib_new)
 
         _hashlib = self._conditional_import_module('_hashlib')
         self._hashlib = _hashlib
@@ -162,13 +179,7 @@ class HashLibTestCase(unittest.TestCase):
             for algorithm, constructors in self.constructors_to_test.items():
                 constructor = getattr(_hashlib, 'openssl_'+algorithm, None)
                 if constructor:
-                    try:
-                        constructor()
-                    except ValueError:
-                        # default constructor blocked by crypto policy
-                        pass
-                    else:
-                        constructors.add(constructor)
+                    _add_constructor(algorithm, constructor)
 
         def add_builtin_constructor(name):
             try:
@@ -346,6 +357,8 @@ class HashLibTestCase(unittest.TestCase):
                 self.assertIn(h.name, self.supported_hash_names)
             else:
                 self.assertNotIn(h.name, self.supported_hash_names)
+            if not h.name.startswith('blake2') and h.name not in FIPS_DISABLED:
+                self.assertEqual(h.name, hashlib.new(h.name).name)
             self.assertEqual(
                 h.name,
                 hashlib.new(h.name, usedforsecurity=False).name
@@ -392,8 +405,10 @@ class HashLibTestCase(unittest.TestCase):
         for hash_object_constructor in constructors:
 
             # OpenSSL's blake2s & blake2d don't support `key`
-            _name = hash_object_constructor.__name__
-            if 'key' in kwargs and _name.startswith('openssl_blake2'):
+            if (
+                'key' in kwargs
+                and _is_blake2_constructor(hash_object_constructor)
+            ):
                 return
 
             m = hash_object_constructor(data, **kwargs)
@@ -1024,6 +1039,16 @@ class HashLibTestCase(unittest.TestCase):
                     with self.assertRaisesRegex(TypeError, "immutable type"):
                         hash_type.value = False
 
+    @unittest.skipUnless(get_fips_mode(), 'Needs FIPS mode.')
+    def test_usedforsecurity_repeat(self):
+        """Make sure usedforsecurity flag isn't copied to other contexts"""
+        for i in range(3):
+            for cons in hashlib.md5, partial(hashlib.new, 'md5'):
+                self.assertRaises(ValueError, cons)
+                self.assertRaises(ValueError, partial(cons, usedforsecurity=True))
+                self.assertEqual(cons(usedforsecurity=False).hexdigest(),
+                                'd41d8cd98f00b204e9800998ecf8427e')
+
 
 class KDFTests(unittest.TestCase):
 
-- 
From f1c9ecbb2e2f08d792fb0557058824eed23abb7b Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pviktori@redhat.com>
Date: Mon, 26 Aug 2019 19:39:48 +0200
Subject: [PATCH 6/7] Guard against Python HMAC in FIPS mode

---
 Lib/hmac.py           | 12 +++++++++---
 Lib/test/test_hmac.py | 10 ++++++++++
 2 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/Lib/hmac.py b/Lib/hmac.py
index 8b4eb2f..8930bda 100644
--- a/Lib/hmac.py
+++ b/Lib/hmac.py
@@ -16,8 +16,9 @@ else:
 
 import hashlib as _hashlib
 
-trans_5C = bytes((x ^ 0x5C) for x in range(256))
-trans_36 = bytes((x ^ 0x36) for x in range(256))
+if not _hashopenssl.get_fips_mode():
+    trans_5C = bytes((x ^ 0x5C) for x in range(256))
+    trans_36 = bytes((x ^ 0x36) for x in range(256))
 
 # The size of the digests returned by HMAC depends on the underlying
 # hashing module used.  Use digest_size from the instance of HMAC instead.
@@ -55,10 +56,12 @@ class HMAC:
         if not digestmod:
             raise TypeError("Missing required argument 'digestmod'.")
 
-        if _hashopenssl and isinstance(digestmod, (str, _functype)):
+        if _hashopenssl.get_fips_mode() or (_hashopenssl and isinstance(digestmod, (str, _functype))):
             try:
                 self._init_hmac(key, msg, digestmod)
             except _hashopenssl.UnsupportedDigestmodError:
+                if _hashopenssl.get_fips_mode():
+                    raise
                 self._init_old(key, msg, digestmod)
         else:
             self._init_old(key, msg, digestmod)
@@ -69,6 +72,9 @@ class HMAC:
         self.block_size = self._hmac.block_size
 
     def _init_old(self, key, msg, digestmod):
+        if _hashopenssl.get_fips_mode():
+            # In FIPS mode, use OpenSSL anyway: raise the appropriate error
+            return self._init_hmac(key, msg, digestmod)
         if callable(digestmod):
             digest_cons = digestmod
         elif isinstance(digestmod, str):
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
index 1502fba..e40ca4b 100644
--- a/Lib/test/test_hmac.py
+++ b/Lib/test/test_hmac.py
@@ -5,6 +5,7 @@ import hashlib
 import unittest
 import unittest.mock
 import warnings
+from _hashlib import get_fips_mode
 
 from test.support import hashlib_helper, check_disallow_instantiation
 
@@ -339,6 +340,7 @@ class TestVectorsTestCase(unittest.TestCase):
     def test_sha512_rfc4231(self):
         self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)
 
+    #unittest.skipIf(get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.')
     @hashlib_helper.requires_hashdigest('sha256')
     def test_legacy_block_size_warnings(self):
         class MockCrazyHash(object):
@@ -351,6 +353,11 @@ class TestVectorsTestCase(unittest.TestCase):
             def digest(self):
                 return self._x.digest()
 
+        if get_fips_mode():
+            with self.assertRaises(ValueError):
+                hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash)
+            return
+
         with warnings.catch_warnings():
             warnings.simplefilter('error', RuntimeWarning)
             with self.assertRaises(RuntimeWarning):
@@ -453,6 +460,7 @@ class ConstructorTestCase(unittest.TestCase):
         with self.assertRaisesRegex(TypeError, "immutable type"):
             C_HMAC.value = None
 
+    @unittest.skipIf(get_fips_mode(), "_sha256 unavailable in FIPS mode")
     @unittest.skipUnless(sha256_module is not None, 'need _sha256')
     def test_with_sha256_module(self):
         h = hmac.HMAC(b"key", b"hash this!", digestmod=sha256_module.sha256)
@@ -489,6 +497,7 @@ class UpdateTestCase(unittest.TestCase):
 
 class CopyTestCase(unittest.TestCase):
 
+    @unittest.skipIf(get_fips_mode(), "_init_old unavailable in FIPS mode")
     @hashlib_helper.requires_hashdigest('sha256')
     def test_attributes_old(self):
         # Testing if attributes are of same type.
@@ -500,6 +509,7 @@ class CopyTestCase(unittest.TestCase):
         self.assertEqual(type(h1._outer), type(h2._outer),
             "Types of outer don't match.")
 
+    @unittest.skipIf(get_fips_mode(), "_init_old unavailable in FIPS mode")
     @hashlib_helper.requires_hashdigest('sha256')
     def test_realcopy_old(self):
         # Testing if the copy method created a real copy.
-- 
From a0c3f9ac5a4e60ab22418a3196ae46ba34e9477b Mon Sep 17 00:00:00 2001
From: Nikita Sobolev <mail@sobolevn.me>
Date: Thu, 24 Nov 2022 01:47:31 +0300
Subject: [PATCH 7/7] closes gh-99508: fix `TypeError` in
 `Lib/importlib/_bootstrap_external.py` (GH-99635)
 Lib/importlib/_bootstrap_external.py                           | 3 ++-
 .../next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst | 2 ++
 2 files changed, 4 insertions(+), 1 deletion(-)
 create mode 100644 Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst
diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py
index e53f6ac..bdc491e 100644
--- a/Lib/importlib/_bootstrap_external.py
+++ b/Lib/importlib/_bootstrap_external.py
@@ -1077,7 +1077,8 @@ class SourceLoader(_LoaderBasics):
                 source_mtime is not None):
             if hash_based:
                 if source_hash is None:
-                    source_hash = _imp.source_hash(source_bytes)
+                    source_hash = _imp.source_hash(_RAW_MAGIC_NUMBER,
+                                                   source_bytes)
                 data = _code_to_hash_pyc(code_object, source_hash, check_source)
             else:
                 data = _code_to_timestamp_pyc(code_object, source_mtime,
diff --git a/Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst b/Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst
new file mode 100644
index 0000000..82720d1
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-11-21-10-45-54.gh-issue-99508.QqVbby.rst
@@ -0,0 +1,2 @@
+Fix ``TypeError`` in ``Lib/importlib/_bootstrap_external.py`` while calling
+``_imp.source_hash()``.