diff --git a/00329-fips.patch b/00329-fips.patch index 5a6a99a..732d878 100644 --- a/00329-fips.patch +++ b/00329-fips.patch @@ -1,7 +1,8 @@ -From 5943fa462c4b2afe297b06aaef911efcf935c0d7 Mon Sep 17 00:00:00 2001 +From a6d1a09943ab05b9253eda4b3b73c8a4fe9efbf6 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 11 Aug 2021 16:51:03 +0200 -Subject: [PATCH 1/5] Backport PyModule_AddObjectRef as _PyModule_AddObjectRef +Subject: [PATCH 01/10] Backport PyModule_AddObjectRef as + _PyModule_AddObjectRef Having PyModule_AddObjectRef available should make backporting newer patches easier. The new API is much safer. @@ -14,7 +15,7 @@ modules that define PyModule_AddObjectRef themselves on Python<=3.9 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Include/modsupport.h b/Include/modsupport.h -index 4c4aab65ba..d9fac52521 100644 +index 4c4aab6..d9fac52 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -136,7 +136,17 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords( @@ -36,7 +37,7 @@ index 4c4aab65ba..d9fac52521 100644 PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char *); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x03090000 diff --git a/Python/modsupport.c b/Python/modsupport.c -index 13482c6508..fca1083e2d 100644 +index 13482c6..fca1083 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -631,7 +631,7 @@ va_build_stack(PyObject **small_stack, Py_ssize_t small_stack_len, @@ -70,13 +71,13 @@ index 13482c6508..fca1083e2d 100644 PyModule_AddIntConstant(PyObject *m, const char *name, long value) { -- -2.31.1 +2.34.1 -From d6ecb3ac9a626af2ecd782d6c7b452251713c4a6 Mon Sep 17 00:00:00 2001 +From c67b383ffd3ccacedacbeb91c3bdeaf5f829ca09 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Fri, 13 Aug 2021 13:16:43 +0200 -Subject: [PATCH 2/5] _hashopenssl: Uncomment and use initialization function +Subject: [PATCH 02/10] _hashopenssl: Uncomment and use initialization function list This simplifies backporting of future changes. @@ -84,14 +85,14 @@ This simplifies backporting of future changes. We use this change instead of Python 3.10's: bpo-1635741: Port _hashlib to multiphase initialization (GH-23358) --- - Modules/_hashopenssl.c | 26 +++++--------------------- - 1 file changed, 5 insertions(+), 21 deletions(-) + Modules/_hashopenssl.c | 30 +++++------------------------- + 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c -index ff3a1aef5e..db661dc5f7 100644 +index a488945..62cf769 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c -@@ -2085,7 +2085,6 @@ hashlib_init_hmactype(PyObject *module) +@@ -2215,7 +2215,6 @@ hashlib_init_hmactype(PyObject *module) return 0; } @@ -99,7 +100,7 @@ index ff3a1aef5e..db661dc5f7 100644 static PyModuleDef_Slot hashlib_slots[] = { /* OpenSSL 1.0.2 and LibreSSL */ {Py_mod_exec, hashlib_openssl_legacy_init}, -@@ -2095,7 +2094,6 @@ static PyModuleDef_Slot hashlib_slots[] = { +@@ -2226,7 +2225,6 @@ static PyModuleDef_Slot hashlib_slots[] = { {Py_mod_exec, hashlib_md_meth_names}, {0, NULL} }; @@ -107,7 +108,7 @@ index ff3a1aef5e..db661dc5f7 100644 static struct PyModuleDef _hashlibmodule = { PyModuleDef_HEAD_INIT, -@@ -2123,25 +2121,11 @@ PyInit__hashlib(void) +@@ -2254,29 +2252,11 @@ PyInit__hashlib(void) return NULL; } @@ -115,6 +116,10 @@ index ff3a1aef5e..db661dc5f7 100644 - Py_DECREF(m); - return NULL; - } +- if (hashlib_init_hashtable(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } - if (hashlib_init_evptype(m) < 0) { - Py_DECREF(m); - return NULL; @@ -139,13 +144,13 @@ index ff3a1aef5e..db661dc5f7 100644 return m; -- -2.31.1 +2.34.1 -From ce54104e69b9f3e9b3f49cec2054b66e84a5cbb8 Mon Sep 17 00:00:00 2001 +From c49c1416d22fffc78204d66987f40e6d17a95c01 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sat, 27 Mar 2021 14:55:03 +0100 -Subject: [PATCH 3/5] bpo-40645: use C implementation of HMAC (GH-24920, +Subject: [PATCH 03/10] bpo-40645: use C implementation of HMAC (GH-24920, GH-25063, GH-26079) This backports the feature and 2 subsequent bugfixes @@ -157,17 +162,15 @@ Co-Authored-By: Pablo Galindo --- Lib/hashlib.py | 1 + Lib/hmac.py | 86 ++++++---- - Lib/test/test_hmac.py | 114 ++++++++----- - .../2021-03-29-11-55-06.bpo-40645.PhaT-B.rst | 2 + + Lib/test/test_hmac.py | 114 +++++++------ .../2021-03-19-10-22-17.bpo-40645.5pXhb-.rst | 2 + - Modules/_hashopenssl.c | 158 ++++++++++++++++-- + Modules/_hashopenssl.c | 150 ++++++++++++++++-- Modules/clinic/_hashopenssl.c.h | 38 +---- - 7 files changed, 276 insertions(+), 125 deletions(-) - create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-03-29-11-55-06.bpo-40645.PhaT-B.rst + 6 files changed, 265 insertions(+), 126 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-03-19-10-22-17.bpo-40645.5pXhb-.rst diff --git a/Lib/hashlib.py b/Lib/hashlib.py -index 58c340d56e..ffa3be049a 100644 +index 58c340d..ffa3be0 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -173,6 +173,7 @@ try: @@ -179,7 +182,7 @@ index 58c340d56e..ffa3be049a 100644 __get_hash = __get_builtin_constructor diff --git a/Lib/hmac.py b/Lib/hmac.py -index 180bc378b5..8b4f920db9 100644 +index 180bc37..8b4f920 100644 --- a/Lib/hmac.py +++ b/Lib/hmac.py @@ -8,11 +8,12 @@ try: @@ -347,7 +350,7 @@ index 180bc378b5..8b4f920db9 100644 if callable(digest): digest_cons = digest diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py -index 6daf22ca06..adf52adbf2 100644 +index 6daf22c..adf52ad 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -11,14 +11,21 @@ from test.support import hashlib_helper @@ -569,44 +572,28 @@ index 6daf22ca06..adf52adbf2 100644 @hashlib_helper.requires_hashdigest('sha256') def test_equality(self): -diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-03-29-11-55-06.bpo-40645.PhaT-B.rst b/Misc/NEWS.d/next/Core and Builtins/2021-03-29-11-55-06.bpo-40645.PhaT-B.rst -new file mode 100644 -index 0000000000..9ca9843947 ---- /dev/null -+++ b/Misc/NEWS.d/next/Core and Builtins/2021-03-29-11-55-06.bpo-40645.PhaT-B.rst -@@ -0,0 +1,2 @@ -+Fix reference leak in the :mod:`_hashopenssl` extension. Patch by Pablo -+Galindo. 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 new file mode 100644 -index 0000000000..a9ab1c0915 +index 0000000..a9ab1c0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-03-19-10-22-17.bpo-40645.5pXhb-.rst @@ -0,0 +1,2 @@ +The :mod:`hmac` module now uses OpenSSL's HMAC implementation when digestmod +argument is a hash name or builtin hash function. diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c -index db661dc5f7..fd3b3c0719 100644 +index 62cf769..71ac832 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c -@@ -92,6 +92,8 @@ typedef struct { - #ifdef PY_OPENSSL_HAS_SHAKE +@@ -260,6 +260,8 @@ typedef struct { PyTypeObject *EVPXOFtype; #endif + _Py_hashtable_t *hashtable; + PyObject *constructs; + PyObject *unsupported_digestmod_error; } _hashlibstate; static inline _hashlibstate* -@@ -295,9 +297,56 @@ py_digest_by_name(const char *name) - #endif - } - -+ if (digest == NULL) { -+ PyErr_Format(PyExc_ValueError, "unsupported hash type %s", name); -+ return NULL; -+ } -+ +@@ -419,6 +421,48 @@ py_digest_by_name(PyObject *module, const char *name, enum Py_hash_type py_ht) return digest; } @@ -617,9 +604,9 @@ index db661dc5f7..fd3b3c0719 100644 + * + * on error returns NULL with exception set. + */ -+static const EVP_MD* -+py_digest_by_digestmod(PyObject *module, PyObject *digestmod) { -+ const EVP_MD* evp; ++static PY_EVP_MD* ++py_digest_by_digestmod(PyObject *module, PyObject *digestmod, enum Py_hash_type py_ht) { ++ PY_EVP_MD* evp; + PyObject *name_obj = NULL; + const char *name; + @@ -644,7 +631,7 @@ index db661dc5f7..fd3b3c0719 100644 + return NULL; + } + -+ evp = py_digest_by_name(name); ++ evp = py_digest_by_name(module, name, py_ht); + if (evp == NULL) { + return NULL; + } @@ -655,40 +642,15 @@ index db661dc5f7..fd3b3c0719 100644 static EVPobject * newEVPobject(PyTypeObject *type) { -@@ -822,7 +871,7 @@ EVP_new_impl(PyObject *module, PyObject *name_obj, PyObject *data_obj, - /*[clinic end generated code: output=ddd5053f92dffe90 input=c24554d0337be1b0]*/ - { - Py_buffer view = { 0 }; -- PyObject *ret_obj; -+ PyObject *ret_obj = NULL; - char *name; - const EVP_MD *digest = NULL; +@@ -1237,7 +1281,6 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name, -@@ -835,11 +884,15 @@ EVP_new_impl(PyObject *module, PyObject *name_obj, PyObject *data_obj, - GET_BUFFER_VIEW_OR_ERROUT(data_obj, &view); - - digest = py_digest_by_name(name); -+ if (digest == NULL) { -+ goto exit; -+ } - - ret_obj = EVPnew(module, digest, - (unsigned char*)view.buf, view.len, - usedforsecurity); - -+exit: - if (data_obj) - PyBuffer_Release(&view); - return ret_obj; -@@ -1130,7 +1183,6 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name, - - digest = py_digest_by_name(hash_name); + PY_EVP_MD *digest = py_digest_by_name(module, hash_name, Py_ht_pbkdf2); if (digest == NULL) { - PyErr_SetString(PyExc_ValueError, "unsupported hash type"); goto end; } -@@ -1334,26 +1386,26 @@ _hashlib.hmac_digest as _hashlib_hmac_singleshot +@@ -1442,25 +1485,21 @@ _hashlib.hmac_digest as _hashlib_hmac_singleshot key: Py_buffer msg: Py_buffer @@ -708,19 +670,25 @@ index db661dc5f7..fd3b3c0719 100644 unsigned char md[EVP_MAX_MD_SIZE] = {0}; unsigned int md_len = 0; unsigned char *result; - const EVP_MD *evp; + PY_EVP_MD *evp; -- evp = py_digest_by_name(digest); -+ evp = py_digest_by_digestmod(module, digest); - if (evp == NULL) { -- PyErr_SetString(PyExc_ValueError, "unsupported hash type"); - return NULL; - } -+ +- evp = py_digest_by_name(module, digest, Py_ht_mac); +- if (evp == NULL) { +- return NULL; +- } if (key->len > INT_MAX) { PyErr_SetString(PyExc_OverflowError, "key is too long."); -@@ -1391,15 +1443,15 @@ _hashlib.hmac_new +@@ -1472,7 +1511,7 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, + return NULL; + } + +- evp = py_digest_by_name(module, digest, Py_ht_mac); ++ evp = py_digest_by_digestmod(module, digest, Py_ht_mac); + if (evp == NULL) { + return NULL; + } +@@ -1504,15 +1543,15 @@ _hashlib.hmac_new key: Py_buffer msg as msg_obj: object(c_default="NULL") = b'' @@ -738,8 +706,8 @@ index db661dc5f7..fd3b3c0719 100644 +/*[clinic end generated code: output=c20d9e4d9ed6d219 input=5f4071dcc7f34362]*/ { PyTypeObject *type = get_hashlib_state(module)->HMACtype; - const EVP_MD *digest; -@@ -1413,15 +1465,14 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, + PY_EVP_MD *digest; +@@ -1526,14 +1565,14 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, return NULL; } @@ -750,15 +718,14 @@ index db661dc5f7..fd3b3c0719 100644 return NULL; } -- digest = py_digest_by_name(digestmod); +- digest = py_digest_by_name(module, digestmod, Py_ht_mac); - if (!digest) { -- PyErr_SetString(PyExc_ValueError, "unknown hash function"); -+ digest = py_digest_by_digestmod(module, digestmod); ++ digest = py_digest_by_digestmod(module, digestmod, Py_ht_mac); + if (digest == NULL) { return NULL; } -@@ -1992,6 +2043,8 @@ hashlib_traverse(PyObject *m, visitproc visit, void *arg) +@@ -2105,6 +2144,8 @@ hashlib_traverse(PyObject *m, visitproc visit, void *arg) #ifdef PY_OPENSSL_HAS_SHAKE Py_VISIT(state->EVPXOFtype); #endif @@ -767,16 +734,22 @@ index db661dc5f7..fd3b3c0719 100644 return 0; } -@@ -2004,6 +2057,8 @@ hashlib_clear(PyObject *m) +@@ -2117,10 +2158,14 @@ hashlib_clear(PyObject *m) #ifdef PY_OPENSSL_HAS_SHAKE Py_CLEAR(state->EVPXOFtype); #endif + Py_CLEAR(state->constructs); + Py_CLEAR(state->unsupported_digestmod_error); ++ + if (state->hashtable != NULL) { + _Py_hashtable_destroy(state->hashtable); + state->hashtable = NULL; + } ++ return 0; } -@@ -2085,6 +2140,79 @@ hashlib_init_hmactype(PyObject *module) +@@ -2215,6 +2260,79 @@ hashlib_init_hmactype(PyObject *module) return 0; } @@ -856,7 +829,7 @@ index db661dc5f7..fd3b3c0719 100644 static PyModuleDef_Slot hashlib_slots[] = { /* OpenSSL 1.0.2 and LibreSSL */ {Py_mod_exec, hashlib_openssl_legacy_init}, -@@ -2092,6 +2220,8 @@ static PyModuleDef_Slot hashlib_slots[] = { +@@ -2223,6 +2341,8 @@ static PyModuleDef_Slot hashlib_slots[] = { {Py_mod_exec, hashlib_init_evpxoftype}, {Py_mod_exec, hashlib_init_hmactype}, {Py_mod_exec, hashlib_md_meth_names}, @@ -866,7 +839,7 @@ index db661dc5f7..fd3b3c0719 100644 }; diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h -index 68aa765e52..4466ec4330 100644 +index 68aa765..4466ec4 100644 --- a/Modules/clinic/_hashopenssl.c.h +++ b/Modules/clinic/_hashopenssl.c.h @@ -1106,7 +1106,7 @@ PyDoc_STRVAR(_hashlib_hmac_singleshot__doc__, @@ -954,24 +927,24 @@ index 68aa765e52..4466ec4330 100644 -/*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/ -- -2.31.1 +2.34.1 -From 547952d3ae9d94d9e38b71d4e309c1d4c2ec963b Mon Sep 17 00:00:00 2001 +From f7ce31ebf3200952dadff556bfcbf2876139c823 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Thu, 12 Dec 2019 16:58:31 +0100 -Subject: [PATCH 4/5] Expose blake2b and blake2s hashes from OpenSSL +Subject: [PATCH 04/10] 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 | 43 +++++++++++++ + Modules/_hashopenssl.c | 37 +++++++++++ Modules/clinic/_hashopenssl.c.h | 106 +++++++++++++++++++++++++++++++- - 3 files changed, 154 insertions(+), 1 deletion(-) + 3 files changed, 148 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py -index 86f31a5587..f843022b48 100644 +index 969e5e4..5b46016 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -354,6 +354,12 @@ class HashLibTestCase(unittest.TestCase): @@ -988,23 +961,10 @@ index 86f31a5587..f843022b48 100644 computed = m.hexdigest() if not shake else m.hexdigest(length) self.assertEqual( diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c -index fd3b3c0719..065c6c0612 100644 +index 71ac832..0b2c65e 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c -@@ -294,6 +294,12 @@ py_digest_by_name(const char *name) - else if (!strcmp(name, "blake2b512")) { - digest = EVP_blake2b512(); - } -+ else if (!strcmp(name, "blake2s")) { -+ digest = EVP_blake2s256(); -+ } -+ else if (!strcmp(name, "blake2b")) { -+ digest = EVP_blake2b512(); -+ } - #endif - } - -@@ -1038,6 +1044,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj, +@@ -1137,6 +1137,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj, } @@ -1022,7 +982,7 @@ index fd3b3c0719..065c6c0612 100644 +/*[clinic end generated code: output=7a838b1643cde13e input=4ad7fd54268f3689]*/ + +{ -+ return EVP_fast_new(module, data_obj, EVP_blake2b512(), usedforsecurity); ++ return py_evp_fromname(module, Py_hash_blake2b, data_obj, usedforsecurity); +} + +/*[clinic input] @@ -1039,14 +999,14 @@ index fd3b3c0719..065c6c0612 100644 +/*[clinic end generated code: output=4eda6b40757471da input=1ed39481ffa4e26a]*/ + +{ -+ return EVP_fast_new(module, data_obj, EVP_blake2s256(), usedforsecurity); ++ return py_evp_fromname(module, Py_hash_blake2s, data_obj, usedforsecurity); +} + + #ifdef PY_OPENSSL_HAS_SHA3 /*[clinic input] -@@ -2022,6 +2063,8 @@ static struct PyMethodDef EVP_functions[] = { +@@ -2123,6 +2158,8 @@ static struct PyMethodDef EVP_functions[] = { _HASHLIB_OPENSSL_SHA256_METHODDEF _HASHLIB_OPENSSL_SHA384_METHODDEF _HASHLIB_OPENSSL_SHA512_METHODDEF @@ -1056,7 +1016,7 @@ index fd3b3c0719..065c6c0612 100644 _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 4466ec4330..54c22b25d3 100644 +index 4466ec4..54c22b2 100644 --- a/Modules/clinic/_hashopenssl.c.h +++ b/Modules/clinic/_hashopenssl.c.h @@ -540,6 +540,110 @@ exit: @@ -1177,13 +1137,13 @@ index 4466ec4330..54c22b25d3 100644 -/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fab05055e982f112 input=a9049054013a1b77]*/ -- -2.31.1 +2.34.1 -From 4d2a433760548bb9813139844e574c06ea3fdb7a Mon Sep 17 00:00:00 2001 +From b8956168975170b8e7a797b6aa23e0d356f5ebec Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Thu, 1 Aug 2019 17:57:05 +0200 -Subject: [PATCH 5/5] Use a stronger hash in multiprocessing handshake +Subject: [PATCH 05/10] Use a stronger hash in multiprocessing handshake Adapted from patch by David Malcolm, https://bugs.python.org/issue17258 @@ -1192,7 +1152,7 @@ https://bugs.python.org/issue17258 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py -index 510e4b5aba..b68f2fb837 100644 +index 510e4b5..b68f2fb 100644 --- a/Lib/multiprocessing/connection.py +++ b/Lib/multiprocessing/connection.py @@ -42,6 +42,10 @@ BUFSIZE = 8192 @@ -1225,5 +1185,991 @@ index 510e4b5aba..b68f2fb837 100644 response = connection.recv_bytes(256) # reject large message if response != WELCOME: -- -2.31.1 +2.34.1 + + +From 20d86957b863e80d1f71b5681fccdb1fd16128b9 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Thu, 25 Jul 2019 17:19:06 +0200 +Subject: [PATCH 06/10] 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 +++++++++++++++++++++++ + setup.py | 27 ++++++++++++++++----------- + 7 files changed, 69 insertions(+), 20 deletions(-) + +diff --git a/Lib/hashlib.py b/Lib/hashlib.py +index ffa3be0..3e3f4dd 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 5b46016..72fdc67 100644 +--- a/Lib/test/test_hashlib.py ++++ b/Lib/test/test_hashlib.py +@@ -35,14 +35,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 +@@ -116,6 +117,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 7fb1296..bc01cd5 100644 +--- a/Modules/_blake2/blake2b_impl.c ++++ b/Modules/_blake2/blake2b_impl.c +@@ -96,6 +96,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; +@@ -274,6 +276,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 ff142c9..144ec03 100644 +--- a/Modules/_blake2/blake2module.c ++++ b/Modules/_blake2/blake2module.c +@@ -9,6 +9,7 @@ + */ + + #include "Python.h" ++#include "../hashlib.h" + + #include "impl/blake2.h" + +@@ -57,6 +58,8 @@ PyInit__blake2(void) + PyObject *m; + PyObject *d; + ++ FAIL_RETURN_IN_FIPS_MODE(PyExc_ImportError, "blake2"); ++ + m = PyModule_Create(&blake2_module); + if (m == NULL) + return NULL; +diff --git a/Modules/_blake2/blake2s_impl.c b/Modules/_blake2/blake2s_impl.c +index e3e90d0..e45f8f6 100644 +--- a/Modules/_blake2/blake2s_impl.c ++++ b/Modules/_blake2/blake2s_impl.c +@@ -96,6 +96,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; +@@ -274,6 +276,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 ++// FIPS_mode() on OpenSSL < 3.0.0 ++#include ++ + /* + * 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/setup.py b/setup.py +index c6023e1..371674c 100644 +--- a/setup.py ++++ b/setup.py +@@ -2313,7 +2313,7 @@ class PyBuildExt(build_ext): + sources=sources, + depends=depends)) + +- def detect_openssl_hashlib(self): ++ def detect_openssl_args(self): + # Detect SSL support for the socket module (via _ssl) + config_vars = sysconfig.get_config_vars() + +@@ -2333,16 +2333,14 @@ class PyBuildExt(build_ext): + openssl_libs = split_var('OPENSSL_LIBS', '-l') + if not openssl_libs: + # libssl and libcrypto not found +- self.missing.extend(['_ssl', '_hashlib']) +- return None, None ++ raise ValueError('Cannot build for RHEL without OpenSSL') + + # Find OpenSSL includes + ssl_incs = find_file( + 'openssl/ssl.h', self.inc_dirs, openssl_includes + ) + if ssl_incs is None: +- self.missing.extend(['_ssl', '_hashlib']) +- return None, None ++ raise ValueError('Cannot build for RHEL without OpenSSL') + + # OpenSSL 1.0.2 uses Kerberos for KRB5 ciphers + krb5_h = find_file( +@@ -2352,12 +2350,20 @@ class PyBuildExt(build_ext): + if krb5_h: + ssl_incs.extend(krb5_h) + ++ return { ++ 'include_dirs': openssl_includes, ++ 'library_dirs': openssl_libdirs, ++ 'libraries': openssl_libs, ++ } ++ ++ def detect_openssl_hashlib(self): ++ ++ config_vars = sysconfig.get_config_vars() ++ + if config_vars.get("HAVE_X509_VERIFY_PARAM_SET1_HOST"): + self.add(Extension( + '_ssl', ['_ssl.c'], +- include_dirs=openssl_includes, +- library_dirs=openssl_libdirs, +- libraries=openssl_libs, ++ **self.detect_openssl_args(), + depends=[ + 'socketmodule.h', + '_ssl/debughelpers.c', +@@ -2370,9 +2376,7 @@ class PyBuildExt(build_ext): + + self.add(Extension('_hashlib', ['_hashopenssl.c'], + depends=['hashlib.h'], +- include_dirs=openssl_includes, +- library_dirs=openssl_libdirs, +- libraries=openssl_libs)) ++ **self.detect_openssl_args()) ) + + def detect_hash_builtins(self): + # By default we always compile these even when OpenSSL is available +@@ -2429,6 +2433,7 @@ class PyBuildExt(build_ext): + '_blake2/blake2b_impl.c', + '_blake2/blake2s_impl.c' + ], ++ **self.detect_openssl_args(), # for FIPS_mode verification + depends=blake2_deps + )) + +-- +2.34.1 + + +From 76d17b46469d642f2acda31bb5e9e636d69fe945 Mon Sep 17 00:00:00 2001 +From: Charalampos Stratakis +Date: Fri, 29 Jan 2021 14:16:21 +0100 +Subject: [PATCH 07/10] Use python's fall back crypto implementations only if + we are not in FIPS mode + +--- + Lib/hashlib.py | 69 +++------------------------------------- + Lib/test/test_hashlib.py | 23 +++++++++++++- + 2 files changed, 27 insertions(+), 65 deletions(-) + +diff --git a/Lib/hashlib.py b/Lib/hashlib.py +index 3e3f4dd..b842f5f 100644 +--- a/Lib/hashlib.py ++++ b/Lib/hashlib.py +@@ -67,7 +67,6 @@ algorithms_available = set(__always_supported) + __all__ = __always_supported + ('new', 'algorithms_guaranteed', + 'algorithms_available', 'pbkdf2_hmac') + +- + __builtin_constructor_cache = {} + + # Prefer our blake2 implementation (unless in FIPS mode) +@@ -83,6 +82,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: +@@ -176,79 +177,19 @@ try: + algorithms_available = algorithms_available.union( + _hashlib.openssl_md_meth_names) + 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: +- _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. +- """ +- 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, 'big')) +- # endianness doesn't matter here as long to / from use the same +- rkey = int.from_bytes(prev, 'big') +- for i in range(iterations - 1): +- prev = prf(prev) +- # rkey = rkey ^ prev +- rkey ^= from_bytes(prev, 'big') +- loop += 1 +- dkey += rkey.to_bytes(inner.digest_size, 'big') +- +- 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 + + + for __func_name in __always_supported: +diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py +index 72fdc67..ac9c057 100644 +--- a/Lib/test/test_hashlib.py ++++ b/Lib/test/test_hashlib.py +@@ -167,7 +167,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') +@@ -257,6 +263,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') +@@ -1052,6 +1072,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): + self._test_pbkdf2_hmac(builtin_hashlib.pbkdf2_hmac, builtin_hashes) +-- +2.34.1 + + +From 95c861dda1659f5bc47d56bed8d096f4debbe281 Mon Sep 17 00:00:00 2001 +From: Charalampos Stratakis +Date: Wed, 31 Jul 2019 15:43:43 +0200 +Subject: [PATCH 08/10] Test equivalence of hashes for the various digests with + usedforsecurity=True/False + +--- + Lib/test/test_fips.py | 24 +++++++++++++++++++++ + Lib/test/test_hashlib.py | 46 ++++++++++++++++++++++++++++++---------- + 2 files changed, 59 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 ac9c057..0aa0129 100644 +--- a/Lib/test/test_hashlib.py ++++ b/Lib/test/test_hashlib.py +@@ -20,6 +20,7 @@ import warnings + from test import support + from test.support import _4G, bigmemtest, import_fresh_module + 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') +@@ -55,6 +56,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: +@@ -96,6 +102,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', +@@ -138,15 +149,21 @@ class HashLibTestCase(unittest.TestCase): + for algorithm in algorithms: + 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 +@@ -158,13 +175,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: +@@ -337,6 +348,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 +@@ -383,8 +396,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) +@@ -965,6 +980,15 @@ class HashLibTestCase(unittest.TestCase): + ): + HASHXOF() + ++ @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): + +-- +2.34.1 + + +From 62cf5b80d205ff6d6e719286feccca6bd6fdd862 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Mon, 26 Aug 2019 19:39:48 +0200 +Subject: [PATCH 09/10] Guard against Python HMAC in FIPS mode + +--- + Lib/hmac.py | 13 +++++++++---- + Lib/test/test_hmac.py | 10 ++++++++++ + 2 files changed, 19 insertions(+), 4 deletions(-) + +diff --git a/Lib/hmac.py b/Lib/hmac.py +index 8b4f920..20ef96c 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. +@@ -48,17 +49,18 @@ class HMAC: + msg argument. Passing it as a keyword argument is + recommended, though not required for legacy API reasons. + """ +- + if not isinstance(key, (bytes, bytearray)): + raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) + + if not digestmod: + raise TypeError("Missing required parameter '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 +71,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 adf52ad..41e6a14 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 + +@@ -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): +@@ -444,6 +451,7 @@ class ConstructorTestCase(unittest.TestCase): + ): + C_HMAC() + ++ @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) +@@ -472,6 +480,7 @@ class SanityTestCase(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. +@@ -483,6 +492,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. +-- +2.34.1 + + +From ff7f518d32b7f1c47f35b841da78f5869470e381 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Wed, 25 Aug 2021 16:44:43 +0200 +Subject: [PATCH 10/10] Disable hash-based PYCs in FIPS mode + +If FIPS mode is on, we can't use siphash-based HMAC +(_Py_KeyedHash), so: + +- Unchecked hash PYCs can be imported, but not created +- Checked hash PYCs can not be imported nor created +- The default mode is timestamp-based PYCs, even if + SOURCE_DATE_EPOCH is set. + +If FIPS mode is off, there are no changes in behavior. + +Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1835169 +--- + Lib/py_compile.py | 2 ++ + Lib/test/support/__init__.py | 14 +++++++++++++ + Lib/test/test_cmd_line_script.py | 2 ++ + Lib/test/test_compileall.py | 11 +++++++++- + Lib/test/test_imp.py | 2 ++ + .../test_importlib/source/test_file_loader.py | 6 ++++++ + Lib/test/test_py_compile.py | 11 ++++++++-- + Lib/test/test_zipimport.py | 2 ++ + Python/import.c | 20 +++++++++++++++++++ + 9 files changed, 67 insertions(+), 3 deletions(-) + +diff --git a/Lib/py_compile.py b/Lib/py_compile.py +index bba3642..02db901 100644 +--- a/Lib/py_compile.py ++++ b/Lib/py_compile.py +@@ -70,7 +70,9 @@ class PycInvalidationMode(enum.Enum): + + + def _get_default_invalidation_mode(): ++ import _hashlib + if (os.environ.get('SOURCE_DATE_EPOCH') and not ++ _hashlib.get_fips_mode() and not + os.environ.get('RPM_BUILD_ROOT')): + return PycInvalidationMode.CHECKED_HASH + else: +diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py +index 4ced130..11818ac 100644 +--- a/Lib/test/support/__init__.py ++++ b/Lib/test/support/__init__.py +@@ -3249,3 +3249,17 @@ def clear_ignored_deprecations(*tokens: object) -> None: + if warnings.filters != new_filters: + warnings.filters[:] = new_filters + warnings._filters_mutated() ++ ++ ++def fails_in_fips_mode(expected_error): ++ import _hashlib ++ if _hashlib.get_fips_mode(): ++ def _decorator(func): ++ def _wrapper(self, *args, **kwargs): ++ with self.assertRaises(expected_error): ++ func(self, *args, **kwargs) ++ return _wrapper ++ else: ++ def _decorator(func): ++ return func ++ return _decorator +diff --git a/Lib/test/test_cmd_line_script.py b/Lib/test/test_cmd_line_script.py +index 7cb1370..61df232 100644 +--- a/Lib/test/test_cmd_line_script.py ++++ b/Lib/test/test_cmd_line_script.py +@@ -282,6 +282,7 @@ class CmdLineTest(unittest.TestCase): + self._check_script(zip_name, run_name, zip_name, zip_name, '', + zipimport.zipimporter) + ++ @support.fails_in_fips_mode(ImportError) + def test_zipfile_compiled_checked_hash(self): + with support.temp_dir() as script_dir: + script_name = _make_test_script(script_dir, '__main__') +@@ -292,6 +293,7 @@ class CmdLineTest(unittest.TestCase): + self._check_script(zip_name, run_name, zip_name, zip_name, '', + zipimport.zipimporter) + ++ @support.fails_in_fips_mode(ImportError) + def test_zipfile_compiled_unchecked_hash(self): + with support.temp_dir() as script_dir: + script_name = _make_test_script(script_dir, '__main__') +diff --git a/Lib/test/test_compileall.py b/Lib/test/test_compileall.py +index 6e1f4b2..ec6b165 100644 +--- a/Lib/test/test_compileall.py ++++ b/Lib/test/test_compileall.py +@@ -773,14 +773,23 @@ class CommandLineTestsBase: + out = self.assertRunOK('badfilename') + self.assertRegex(out, b"Can't list 'badfilename'") + +- def test_pyc_invalidation_mode(self): ++ @support.fails_in_fips_mode(AssertionError) ++ def test_pyc_invalidation_mode_checked(self): + script_helper.make_script(self.pkgdir, 'f1', '') + pyc = importlib.util.cache_from_source( + os.path.join(self.pkgdir, 'f1.py')) ++ + self.assertRunOK('--invalidation-mode=checked-hash', self.pkgdir) + with open(pyc, 'rb') as fp: + data = fp.read() + self.assertEqual(int.from_bytes(data[4:8], 'little'), 0b11) ++ ++ @support.fails_in_fips_mode(AssertionError) ++ def test_pyc_invalidation_mode_unchecked(self): ++ script_helper.make_script(self.pkgdir, 'f1', '') ++ pyc = importlib.util.cache_from_source( ++ os.path.join(self.pkgdir, 'f1.py')) ++ + self.assertRunOK('--invalidation-mode=unchecked-hash', self.pkgdir) + with open(pyc, 'rb') as fp: + data = fp.read() +diff --git a/Lib/test/test_imp.py b/Lib/test/test_imp.py +index fe394dc..802f0e8 100644 +--- a/Lib/test/test_imp.py ++++ b/Lib/test/test_imp.py +@@ -343,6 +343,7 @@ class ImportTests(unittest.TestCase): + import _frozen_importlib + self.assertEqual(_frozen_importlib.__spec__.origin, "frozen") + ++ @support.fails_in_fips_mode(ImportError) + def test_source_hash(self): + self.assertEqual(_imp.source_hash(42, b'hi'), b'\xc6\xe7Z\r\x03:}\xab') + self.assertEqual(_imp.source_hash(43, b'hi'), b'\x85\x9765\xf8\x9a\x8b9') +@@ -362,6 +363,7 @@ class ImportTests(unittest.TestCase): + res = script_helper.assert_python_ok(*args) + self.assertEqual(res.out.strip().decode('utf-8'), expected) + ++ @support.fails_in_fips_mode(ImportError) + def test_find_and_load_checked_pyc(self): + # issue 34056 + with support.temp_cwd(): +diff --git a/Lib/test/test_importlib/source/test_file_loader.py b/Lib/test/test_importlib/source/test_file_loader.py +index ab44722..480cc81 100644 +--- a/Lib/test/test_importlib/source/test_file_loader.py ++++ b/Lib/test/test_importlib/source/test_file_loader.py +@@ -17,6 +17,7 @@ import types + import unittest + import warnings + ++from test import support + from test.support import make_legacy_pyc, unload + + from test.test_py_compile import without_source_date_epoch +@@ -239,6 +240,7 @@ class SimpleTest(abc.LoaderTests): + loader.load_module('bad name') + + @util.writes_bytecode_files ++ @support.fails_in_fips_mode(ImportError) + def test_checked_hash_based_pyc(self): + with util.create_modules('_temp') as mapping: + source = mapping['_temp'] +@@ -270,6 +272,7 @@ class SimpleTest(abc.LoaderTests): + ) + + @util.writes_bytecode_files ++ @support.fails_in_fips_mode(ImportError) + def test_overridden_checked_hash_based_pyc(self): + with util.create_modules('_temp') as mapping, \ + unittest.mock.patch('_imp.check_hash_based_pycs', 'never'): +@@ -295,6 +298,7 @@ class SimpleTest(abc.LoaderTests): + self.assertEqual(mod.state, 'old') + + @util.writes_bytecode_files ++ @support.fails_in_fips_mode(ImportError) + def test_unchecked_hash_based_pyc(self): + with util.create_modules('_temp') as mapping: + source = mapping['_temp'] +@@ -325,6 +329,7 @@ class SimpleTest(abc.LoaderTests): + ) + + @util.writes_bytecode_files ++ @support.fails_in_fips_mode(ImportError) + def test_overridden_unchecked_hash_based_pyc(self): + with util.create_modules('_temp') as mapping, \ + unittest.mock.patch('_imp.check_hash_based_pycs', 'always'): +@@ -434,6 +439,7 @@ class BadBytecodeTest: + del_source=del_source) + test('_temp', mapping, bc_path) + ++ @support.fails_in_fips_mode(ImportError) + def _test_partial_hash(self, test, *, del_source=False): + with util.create_modules('_temp') as mapping: + bc_path = self.manipulate_bytecode( +diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py +index b2d3dcf..7e4b0c5 100644 +--- a/Lib/test/test_py_compile.py ++++ b/Lib/test/test_py_compile.py +@@ -141,13 +141,16 @@ class PyCompileTestsBase: + importlib.util.cache_from_source(bad_coding))) + + def test_source_date_epoch(self): ++ import _hashlib + py_compile.compile(self.source_path, self.pyc_path) + self.assertTrue(os.path.exists(self.pyc_path)) + self.assertFalse(os.path.exists(self.cache_path)) + with open(self.pyc_path, 'rb') as fp: + flags = importlib._bootstrap_external._classify_pyc( + fp.read(), 'test', {}) +- if os.environ.get('SOURCE_DATE_EPOCH'): ++ if _hashlib.get_fips_mode(): ++ expected_flags = 0b00 ++ elif os.environ.get('SOURCE_DATE_EPOCH'): + expected_flags = 0b11 + else: + expected_flags = 0b00 +@@ -178,7 +181,8 @@ class PyCompileTestsBase: + # Specifying optimized bytecode should lead to a path reflecting that. + self.assertIn('opt-2', py_compile.compile(self.source_path, optimize=2)) + +- def test_invalidation_mode(self): ++ @support.fails_in_fips_mode(ImportError) ++ def test_invalidation_mode_checked(self): + py_compile.compile( + self.source_path, + invalidation_mode=py_compile.PycInvalidationMode.CHECKED_HASH, +@@ -187,6 +191,9 @@ class PyCompileTestsBase: + flags = importlib._bootstrap_external._classify_pyc( + fp.read(), 'test', {}) + self.assertEqual(flags, 0b11) ++ ++ @support.fails_in_fips_mode(ImportError) ++ def test_invalidation_mode_unchecked(self): + py_compile.compile( + self.source_path, + invalidation_mode=py_compile.PycInvalidationMode.UNCHECKED_HASH, +diff --git a/Lib/test/test_zipimport.py b/Lib/test/test_zipimport.py +index 2e24388..11e7978 100644 +--- a/Lib/test/test_zipimport.py ++++ b/Lib/test/test_zipimport.py +@@ -186,6 +186,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): + TESTMOD + pyc_ext: (NOW, test_pyc)} + self.doTest(pyc_ext, files, TESTMOD) + ++ @support.fails_in_fips_mode(ImportError) + def testUncheckedHashBasedPyc(self): + source = b"state = 'old'" + source_hash = importlib.util.source_hash(source) +@@ -200,6 +201,7 @@ class UncompressedZipImportTestCase(ImportHooksBaseTestCase): + self.assertEqual(mod.state, 'old') + self.doTest(None, files, TESTMOD, call=check) + ++ @support.fails_in_fips_mode(ImportError) + @unittest.mock.patch('_imp.check_hash_based_pycs', 'always') + def test_checked_hash_based_change_pyc(self): + source = b"state = 'old'" +diff --git a/Python/import.c b/Python/import.c +index 8358d70..1b7fb85 100644 +--- a/Python/import.c ++++ b/Python/import.c +@@ -2354,6 +2354,26 @@ static PyObject * + _imp_source_hash_impl(PyObject *module, long key, Py_buffer *source) + /*[clinic end generated code: output=edb292448cf399ea input=9aaad1e590089789]*/ + { ++ PyObject *_hashlib = PyImport_ImportModule("_hashlib"); ++ if (_hashlib == NULL) { ++ return NULL; ++ } ++ PyObject *fips_mode_obj = PyObject_CallMethod(_hashlib, "get_fips_mode", NULL); ++ Py_DECREF(_hashlib); ++ if (fips_mode_obj == NULL) { ++ return NULL; ++ } ++ int fips_mode = PyObject_IsTrue(fips_mode_obj); ++ Py_DECREF(fips_mode_obj); ++ if (fips_mode < 0) { ++ return NULL; ++ } ++ if (fips_mode) { ++ PyErr_SetString( ++ PyExc_ImportError, ++ "hash-based PYC validation (siphash24) not available in FIPS mode"); ++ return NULL; ++ }; + union { + uint64_t x; + char data[sizeof(uint64_t)]; +-- +2.34.1 diff --git a/python3.9.spec b/python3.9.spec index ad0621f..e54cede 100644 --- a/python3.9.spec +++ b/python3.9.spec @@ -13,11 +13,11 @@ URL: https://www.python.org/ # WARNING When rebasing to a new Python version, # remember to update the python3-docs package as well -%global general_version %{pybasever}.9 +%global general_version %{pybasever}.10 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 4%{?dist} +Release: 1%{?dist} License: Python @@ -369,8 +369,9 @@ Patch328: 00328-pyc-timestamp-invalidation-mode.patch # - In FIPS mode, the blake2 hashes use OpenSSL wrappers # and do not offer extended functionality (keys, tree hashing, custom digest size) # -# - The patch, in its current state, is in a preliminary form to set the groundwork -# for refining Python's FIPS compatibility until OpenSSL is FIPS ready. +# - In FIPS mode, hmac.HMAC can only be instantiated with an OpenSSL wrapper +# or a string with OpenSSL hash name as the "digestmod" argument. +# The argument must be specified (instead of defaulting to ‘md5’). Patch329: 00329-fips.patch # 00353 # ab4cc97b643cfe99f567e3a03e5617b507183771 @@ -1799,6 +1800,11 @@ CheckPython optimized # ====================================================== %changelog +* Mon Jan 17 2022 Charalampos Stratakis - 3.9.10-1 +- Update to 3.9.10 +- Support OpenSSL FIPS mode +Resolves: rhbz#1942527, rhbz#1835169 + * Sat Jan 08 2022 Miro Hrončok - 3.9.9-4 - Instruct pip to use distutils - Instruct pypa/distutils to add /local/ addition to prefix diff --git a/sources b/sources index d7b5371..ae1b630 100644 --- a/sources +++ b/sources @@ -1,2 +1,2 @@ -SHA512 (Python-3.9.9.tar.xz) = 0ab29fb9a7ecb808bd08d84d28908d5a934e0f021853da0f7a9c94670eb30c8dbbc233d461afdb3995b0de59275ef7e1de43e82d7f848802cbd6e6e50b7b25a6 -SHA512 (Python-3.9.9.tar.xz.asc) = 84fb739c60216ab9a7a487a0ec4039a7e85c7684ef1f71d8b3bc415ff2ae284f79474103ae05385502bc2510401f8cfb33d84dccf5d0ea9a0dd62528519d935f +SHA512 (Python-3.9.10.tar.xz) = 09cb942f84bf362df88999ffa6faf89b4ad12302e67cda4a11547828ebe410c7c93a3dc96cd66fd9c5c7d9a1abe5b8e259e7ec47c10273b42d212270aca5ecba +SHA512 (Python-3.9.10.tar.xz.asc) = d9fed5b39100b81835085d712453516d309b0dddc1524d578cb63c95cd1e2392882702111dd3cb0ebf58c98b94ec04838c89c57f4a7a649c585f7ba5eef70e5e