From 5eca15f8addd011e8a7f6e45bcfa22b317c34783 Mon Sep 17 00:00:00 2001 From: MSVSphere Packaging Team Date: Mon, 18 Dec 2023 22:34:54 +0300 Subject: [PATCH] import python39-3.9.16-1.module+el8.8.0+18968+3d7b19f0.1 --- .gitignore | 2 +- .python39.metadata | 2 +- SOURCES/00189-use-rpm-wheels.patch | 28 +- SOURCES/00329-fips.patch | 3835 ++++++++++------------- SOURCES/00378-support-expat-2-4-5.patch | 47 + SOURCES/00399-cve-2023-24329.patch | 229 ++ SOURCES/Python-3.9.16.tar.xz.asc | 16 + SOURCES/Python-3.9.2.tar.xz.asc | 16 - SPECS/python39.spec | 148 +- 9 files changed, 2160 insertions(+), 2163 deletions(-) create mode 100644 SOURCES/00378-support-expat-2-4-5.patch create mode 100644 SOURCES/00399-cve-2023-24329.patch create mode 100644 SOURCES/Python-3.9.16.tar.xz.asc delete mode 100644 SOURCES/Python-3.9.2.tar.xz.asc diff --git a/.gitignore b/.gitignore index df4a92f..071ce6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/Python-3.9.2.tar.xz +SOURCES/Python-3.9.16.tar.xz diff --git a/.python39.metadata b/.python39.metadata index 2ffe7aa..bc9973c 100644 --- a/.python39.metadata +++ b/.python39.metadata @@ -1 +1 @@ -110ca5bca7989f9558a54ee6762e6774a4b9644a SOURCES/Python-3.9.2.tar.xz +19acd6a341e4f2d7ff97c10c2eada258e9898624 SOURCES/Python-3.9.16.tar.xz diff --git a/SOURCES/00189-use-rpm-wheels.patch b/SOURCES/00189-use-rpm-wheels.patch index 24eafad..c7edc79 100644 --- a/SOURCES/00189-use-rpm-wheels.patch +++ b/SOURCES/00189-use-rpm-wheels.patch @@ -1,4 +1,4 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From 2c91575950d4de95d308e30cc4ab20d032b1aceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 15 Aug 2018 15:36:29 +0200 Subject: [PATCH] 00189: Instead of bundled wheels, use our RPM packaged wheels @@ -8,11 +8,11 @@ We keep them in /usr/share/python-wheels Downstream only: upstream bundles We might eventually pursuit upstream support, but it's low prio --- - Lib/ensurepip/__init__.py | 33 ++++++++++++++++++++++----------- - 1 file changed, 22 insertions(+), 11 deletions(-) + Lib/ensurepip/__init__.py | 37 ++++++++++++++++++++++++++----------- + 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py -index 97dfa7ea71..984e587ea0 100644 +index e510cc7..8de2e55 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -1,3 +1,5 @@ @@ -21,7 +21,7 @@ index 97dfa7ea71..984e587ea0 100644 import os import os.path import sys -@@ -6,16 +8,28 @@ import tempfile +@@ -6,13 +8,29 @@ import tempfile import subprocess from importlib import resources @@ -30,13 +30,13 @@ index 97dfa7ea71..984e587ea0 100644 __all__ = ["version", "bootstrap"] - +-_SETUPTOOLS_VERSION = "58.1.0" +-_PIP_VERSION = "22.0.4" ++ +_WHEEL_DIR = "/usr/share/python39-wheels/" - --_SETUPTOOLS_VERSION = "49.2.1" ++ +_wheels = {} - --_PIP_VERSION = "20.2.3" ++ +def _get_most_recent_wheel_version(pkg): + prefix = os.path.join(_WHEEL_DIR, "{}-".format(pkg)) + _wheels[pkg] = {} @@ -51,10 +51,11 @@ index 97dfa7ea71..984e587ea0 100644 +_SETUPTOOLS_VERSION = _get_most_recent_wheel_version("setuptools") + +_PIP_VERSION = _get_most_recent_wheel_version("pip") - ++ _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), -@@ -105,13 +119,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False, + ("pip", _PIP_VERSION, "py3"), +@@ -101,13 +119,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False, # additional paths that need added to sys.path additional_paths = [] for project, version, py_tag in _PROJECTS: @@ -72,3 +73,6 @@ index 97dfa7ea71..984e587ea0 100644 additional_paths.append(os.path.join(tmpdir, wheel_name)) +-- +2.35.3 + diff --git a/SOURCES/00329-fips.patch b/SOURCES/00329-fips.patch index 5fdad74..cec8740 100644 --- a/SOURCES/00329-fips.patch +++ b/SOURCES/00329-fips.patch @@ -1,715 +1,970 @@ -From 0d4515001c99025c024d773f34d3eb97833d0b5d Mon Sep 17 00:00:00 2001 -From: Charalampos Stratakis -Date: Fri, 29 Jan 2021 14:16:21 +0100 -Subject: [PATCH 01/13] Use python's fall backs for the crypto it implements - only if we are not in FIPS mode +From ccb2659fa0ec259d4161ed84345553bf3f216531 Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Wed, 11 Aug 2021 16:51:03 +0200 +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. +The backport adds an underscore so that we don't break extension +modules that define PyModule_AddObjectRef themselves on Python<=3.9 +(which would be a virtuous thing to do). --- - Lib/hashlib.py | 194 +++++++++++++++------------------------ - Lib/test/test_hashlib.py | 1 + - 2 files changed, 76 insertions(+), 119 deletions(-) + Include/modsupport.h | 10 ++++++++++ + Python/modsupport.c | 13 +++++++++++-- + 2 files changed, 21 insertions(+), 2 deletions(-) -diff --git a/Lib/hashlib.py b/Lib/hashlib.py -index 58c340d56e3..1fd80c7d4fd 100644 ---- a/Lib/hashlib.py -+++ b/Lib/hashlib.py -@@ -68,8 +68,6 @@ __all__ = __always_supported + ('new', 'algorithms_guaranteed', - 'algorithms_available', 'pbkdf2_hmac') - - --__builtin_constructor_cache = {} -- - # Prefer our blake2 implementation - # OpenSSL 1.1.0 comes with a limited implementation of blake2b/s. The OpenSSL - # implementations neither support keyed blake2 (blake2 MAC) nor advanced -@@ -79,54 +77,64 @@ __block_openssl_constructor = { - 'blake2b', 'blake2s', +diff --git a/Include/modsupport.h b/Include/modsupport.h +index 4c4aab6..d9fac52 100644 +--- a/Include/modsupport.h ++++ b/Include/modsupport.h +@@ -136,7 +136,17 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords( + void _PyArg_Fini(void); + #endif /* Py_LIMITED_API */ + ++// Add an attribute with name 'name' and value 'obj' to the module 'mod. ++// On success, return 0 on success. ++// On error, raise an exception and return -1. ++// Backported from Python 3.10, where it's available without the underscore ++// in the name, to ease porting patches to RHEL ++PyAPI_FUNC(int) _PyModule_AddObjectRef(PyObject *mod, const char *name, PyObject *value); ++ ++// Similar to PyModule_AddObjectRef() but steal a reference to 'obj' ++// (Py_DECREF(obj)) on success (if it returns 0). + PyAPI_FUNC(int) PyModule_AddObject(PyObject *, const char *, PyObject *); ++ + PyAPI_FUNC(int) PyModule_AddIntConstant(PyObject *, const char *, long); + 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 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, + + + int +-PyModule_AddObject(PyObject *m, const char *name, PyObject *o) ++_PyModule_AddObjectRef(PyObject *m, const char *name, PyObject *o) + { + PyObject *dict; + if (!PyModule_Check(m)) { +@@ -655,10 +655,19 @@ PyModule_AddObject(PyObject *m, const char *name, PyObject *o) + } + if (PyDict_SetItemString(dict, name, o)) + return -1; +- Py_DECREF(o); + return 0; } --def __get_builtin_constructor(name): -- cache = __builtin_constructor_cache -- constructor = cache.get(name) -- if constructor is not None: -- return constructor -- try: -- if name in {'SHA1', 'sha1'}: -- import _sha1 -- cache['SHA1'] = cache['sha1'] = _sha1.sha1 -- elif name in {'MD5', 'md5'}: -- import _md5 -- cache['MD5'] = cache['md5'] = _md5.md5 -- elif name in {'SHA256', 'sha256', 'SHA224', 'sha224'}: -- import _sha256 -- cache['SHA224'] = cache['sha224'] = _sha256.sha224 -- cache['SHA256'] = cache['sha256'] = _sha256.sha256 -- elif name in {'SHA512', 'sha512', 'SHA384', 'sha384'}: -- import _sha512 -- cache['SHA384'] = cache['sha384'] = _sha512.sha384 -- cache['SHA512'] = cache['sha512'] = _sha512.sha512 -- elif name in {'blake2b', 'blake2s'}: -- import _blake2 -- cache['blake2b'] = _blake2.blake2b -- cache['blake2s'] = _blake2.blake2s -- elif name in {'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512'}: -- import _sha3 -- cache['sha3_224'] = _sha3.sha3_224 -- cache['sha3_256'] = _sha3.sha3_256 -- cache['sha3_384'] = _sha3.sha3_384 -- cache['sha3_512'] = _sha3.sha3_512 -- elif name in {'shake_128', 'shake_256'}: -- import _sha3 -- cache['shake_128'] = _sha3.shake_128 -- cache['shake_256'] = _sha3.shake_256 -- except ImportError: -- pass # no extension module, this hash is unsupported. -- -- constructor = cache.get(name) -- if constructor is not None: -- return constructor -- -- raise ValueError('unsupported hash type ' + name) -+try: -+ from _hashlib import get_fips_mode -+except ImportError: -+ def get_fips_mode(): -+ return 0 -+ -+if not get_fips_mode(): -+ __builtin_constructor_cache = {} ++int ++PyModule_AddObject(PyObject *mod, const char *name, PyObject *value) ++{ ++ int res = _PyModule_AddObjectRef(mod, name, value); ++ if (res == 0) { ++ Py_DECREF(value); ++ } ++ return res; ++} + -+ def __get_builtin_constructor(name): -+ cache = __builtin_constructor_cache -+ constructor = cache.get(name) -+ if constructor is not None: -+ return constructor -+ try: -+ if name in {'SHA1', 'sha1'}: -+ import _sha1 -+ cache['SHA1'] = cache['sha1'] = _sha1.sha1 -+ elif name in {'MD5', 'md5'}: -+ import _md5 -+ cache['MD5'] = cache['md5'] = _md5.md5 -+ elif name in {'SHA256', 'sha256', 'SHA224', 'sha224'}: -+ import _sha256 -+ cache['SHA224'] = cache['sha224'] = _sha256.sha224 -+ cache['SHA256'] = cache['sha256'] = _sha256.sha256 -+ elif name in {'SHA512', 'sha512', 'SHA384', 'sha384'}: -+ import _sha512 -+ cache['SHA384'] = cache['sha384'] = _sha512.sha384 -+ cache['SHA512'] = cache['sha512'] = _sha512.sha512 -+ elif name in {'blake2b', 'blake2s'}: -+ import _blake2 -+ cache['blake2b'] = _blake2.blake2b -+ cache['blake2s'] = _blake2.blake2s -+ elif name in {'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512'}: -+ import _sha3 -+ cache['sha3_224'] = _sha3.sha3_224 -+ cache['sha3_256'] = _sha3.sha3_256 -+ cache['sha3_384'] = _sha3.sha3_384 -+ cache['sha3_512'] = _sha3.sha3_512 -+ elif name in {'shake_128', 'shake_256'}: -+ import _sha3 -+ cache['shake_128'] = _sha3.shake_128 -+ cache['shake_256'] = _sha3.shake_256 -+ except ImportError: -+ pass # no extension module, this hash is unsupported. -+ -+ constructor = cache.get(name) -+ if constructor is not None: -+ return constructor -+ -+ raise ValueError('unsupported hash type ' + name) - - - def __get_openssl_constructor(name): -- if name in __block_openssl_constructor: -- # Prefer our builtin blake2 implementation. -- return __get_builtin_constructor(name) -+ if not get_fips_mode(): -+ if name in __block_openssl_constructor: -+ # Prefer our builtin blake2 implementation. -+ return __get_builtin_constructor(name) - try: - # MD5, SHA1, and SHA2 are in all supported OpenSSL versions - # SHA3/shake are available in OpenSSL 1.1.1+ -@@ -141,21 +149,23 @@ def __get_openssl_constructor(name): - return __get_builtin_constructor(name) - - --def __py_new(name, data=b'', **kwargs): -- """new(name, data=b'', **kwargs) - Return a new hashing object using the -- named algorithm; optionally initialized with data (which must be -- a bytes-like object). -- """ -- return __get_builtin_constructor(name)(data, **kwargs) -+if not get_fips_mode(): -+ def __py_new(name, data=b'', **kwargs): -+ """new(name, data=b'', **kwargs) - Return a new hashing object using the -+ named algorithm; optionally initialized with data (which must be -+ a bytes-like object). -+ """ -+ return __get_builtin_constructor(name)(data, **kwargs) - + int + PyModule_AddIntConstant(PyObject *m, const char *name, long value) + { +-- +2.37.2 + + +From 794c37495d91823bd820b96382b999d84dcad58d Mon Sep 17 00:00:00 2001 +From: Petr Viktorin +Date: Fri, 13 Aug 2021 13:16:43 +0200 +Subject: [PATCH 02/10] _hashopenssl: Uncomment and use initialization function + list + +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 | 30 +++++------------------------- + 1 file changed, 5 insertions(+), 25 deletions(-) + +diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c +index 4db058c..56dfff9 100644 +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -2227,7 +2227,6 @@ hashlib_init_hmactype(PyObject *module) + return 0; + } - def __hash_new(name, data=b'', **kwargs): - """new(name, data=b'') - Return a new hashing object using the named algorithm; - optionally initialized with data (which must be a bytes-like object). - """ -- if name in __block_openssl_constructor: -- # Prefer our builtin blake2 implementation. -- return __get_builtin_constructor(name)(data, **kwargs) -+ if not get_fips_mode(): -+ if name in __block_openssl_constructor: -+ # Prefer our builtin blake2 implementation. -+ return __get_builtin_constructor(name)(data, **kwargs) - try: - return _hashlib.new(name, data, **kwargs) - except ValueError: -@@ -163,6 +173,8 @@ def __hash_new(name, data=b'', **kwargs): - # hash, try using our builtin implementations. - # This allows for SHA224/256 and SHA384/512 support even though - # the OpenSSL library prior to 0.9.8 doesn't provide them. -+ if get_fips_mode(): -+ raise - return __get_builtin_constructor(name)(data) +-#if 0 + static PyModuleDef_Slot hashlib_slots[] = { + /* OpenSSL 1.0.2 and LibreSSL */ + {Py_mod_exec, hashlib_openssl_legacy_init}, +@@ -2238,7 +2237,6 @@ static PyModuleDef_Slot hashlib_slots[] = { + {Py_mod_exec, hashlib_md_meth_names}, + {0, NULL} + }; +-#endif + + static struct PyModuleDef _hashlibmodule = { + PyModuleDef_HEAD_INIT, +@@ -2266,29 +2264,11 @@ PyInit__hashlib(void) + return NULL; + } +- if (hashlib_openssl_legacy_init(m) < 0) { +- 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; +- } +- if (hashlib_init_evpxoftype(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_init_hmactype(m) < 0) { +- Py_DECREF(m); +- return NULL; +- } +- if (hashlib_md_meth_names(m) == -1) { +- Py_DECREF(m); +- return NULL; ++ for (int i=0; hashlib_slots[i].slot; i++) { ++ if (((int (*)(PyObject*))hashlib_slots[i].value)(m) < 0) { ++ Py_DECREF(m); ++ return NULL; ++ } + } -@@ -173,72 +185,14 @@ try: + return m; +-- +2.37.2 + + +From 94b56c82b459474c3e0f9e5421fa7becbf5a1c70 Mon Sep 17 00:00:00 2001 +From: Christian Heimes +Date: Sat, 27 Mar 2021 14:55:03 +0100 +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 +from: https://bugs.python.org/issue40645 + +Signed-off-by: Christian Heimes +Co-authored-by: Erlend Egeberg Aasland +Co-Authored-By: Pablo Galindo +--- + Lib/hashlib.py | 1 + + Lib/hmac.py | 86 ++++++---- + Lib/test/test_hmac.py | 114 +++++++------ + .../2021-03-19-10-22-17.bpo-40645.5pXhb-.rst | 2 + + Modules/_hashopenssl.c | 150 ++++++++++++++++-- + Modules/clinic/_hashopenssl.c.h | 38 +---- + 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 58c340d..ffa3be0 100644 +--- a/Lib/hashlib.py ++++ b/Lib/hashlib.py +@@ -173,6 +173,7 @@ try: algorithms_available = algorithms_available.union( _hashlib.openssl_md_meth_names) except ImportError: -+ if get_fips_mode: -+ raise ++ _hashlib = None new = __py_new __get_hash = __get_builtin_constructor --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) +diff --git a/Lib/hmac.py b/Lib/hmac.py +index 180bc37..8b4f920 100644 +--- a/Lib/hmac.py ++++ b/Lib/hmac.py +@@ -8,11 +8,12 @@ try: + import _hashlib as _hashopenssl + except ImportError: + _hashopenssl = None +- _openssl_md_meths = None ++ _functype = None + from _operator import _compare_digest as compare_digest + else: +- _openssl_md_meths = frozenset(_hashopenssl.openssl_md_meth_names) + compare_digest = _hashopenssl.compare_digest ++ _functype = type(_hashopenssl.openssl_sha256) # builtin type ++ + import hashlib as _hashlib + + trans_5C = bytes((x ^ 0x5C) for x in range(256)) +@@ -23,7 +24,6 @@ trans_36 = bytes((x ^ 0x36) for x in range(256)) + digest_size = None + -- 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)) + class HMAC: + """RFC 2104 HMAC class. Also complies with RFC 4231. + +@@ -32,7 +32,7 @@ class HMAC: + blocksize = 64 # 512-bit HMAC; can be changed in subclasses. + + __slots__ = ( +- "_digest_cons", "_inner", "_outer", "block_size", "digest_size" ++ "_hmac", "_inner", "_outer", "block_size", "digest_size" + ) + + def __init__(self, key, msg=None, digestmod=''): +@@ -55,15 +55,30 @@ class HMAC: + if not digestmod: + raise TypeError("Missing required parameter 'digestmod'.") + ++ if _hashopenssl and isinstance(digestmod, (str, _functype)): ++ try: ++ self._init_hmac(key, msg, digestmod) ++ except _hashopenssl.UnsupportedDigestmodError: ++ self._init_old(key, msg, digestmod) ++ else: ++ self._init_old(key, msg, digestmod) ++ ++ def _init_hmac(self, key, msg, digestmod): ++ self._hmac = _hashopenssl.hmac_new(key, msg, digestmod=digestmod) ++ self.digest_size = self._hmac.digest_size ++ self.block_size = self._hmac.block_size ++ ++ def _init_old(self, key, msg, digestmod): + if callable(digestmod): +- self._digest_cons = digestmod ++ digest_cons = digestmod + elif isinstance(digestmod, str): +- self._digest_cons = lambda d=b'': _hashlib.new(digestmod, d) ++ digest_cons = lambda d=b'': _hashlib.new(digestmod, d) + else: +- self._digest_cons = lambda d=b'': digestmod.new(d) ++ digest_cons = lambda d=b'': digestmod.new(d) + +- self._outer = self._digest_cons() +- self._inner = self._digest_cons() ++ self._hmac = None ++ self._outer = digest_cons() ++ self._inner = digest_cons() + self.digest_size = self._inner.digest_size + + if hasattr(self._inner, 'block_size'): +@@ -79,13 +94,13 @@ class HMAC: + RuntimeWarning, 2) + blocksize = self.blocksize + ++ if len(key) > blocksize: ++ key = digest_cons(key).digest() ++ + # self.blocksize is the default blocksize. self.block_size is + # effective block size as well as the public API attribute. + self.block_size = blocksize + +- if len(key) > blocksize: +- key = self._digest_cons(key).digest() - -- 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() + key = key.ljust(blocksize, b'\0') + self._outer.update(key.translate(trans_5C)) + self._inner.update(key.translate(trans_36)) +@@ -94,23 +109,15 @@ class HMAC: + + @property + def name(self): +- return "hmac-" + self._inner.name - -- if iterations < 1: -- raise ValueError(iterations) -- if dklen is None: -- dklen = outer.digest_size -- if dklen < 1: -- raise ValueError(dklen) +- @property +- def digest_cons(self): +- return self._digest_cons - -- 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') +- @property +- def inner(self): +- return self._inner - -- return dkey[:dklen] -+# OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA -+from _hashlib import pbkdf2_hmac +- @property +- def outer(self): +- return self._outer ++ if self._hmac: ++ return self._hmac.name ++ else: ++ return f"hmac-{self._inner.name}" + + def update(self, msg): + """Feed data from msg into this hashing object.""" +- self._inner.update(msg) ++ inst = self._hmac or self._inner ++ inst.update(msg) + + def copy(self): + """Return a separate copy of this hashing object. +@@ -119,10 +126,14 @@ class HMAC: + """ + # Call __new__ directly to avoid the expensive __init__. + other = self.__class__.__new__(self.__class__) +- other._digest_cons = self._digest_cons + other.digest_size = self.digest_size +- other._inner = self._inner.copy() +- other._outer = self._outer.copy() ++ if self._hmac: ++ other._hmac = self._hmac.copy() ++ other._inner = other._outer = None ++ else: ++ other._hmac = None ++ other._inner = self._inner.copy() ++ other._outer = self._outer.copy() + return other + + def _current(self): +@@ -130,9 +141,12 @@ class HMAC: + + To be used only internally with digest() and hexdigest(). + """ +- h = self._outer.copy() +- h.update(self._inner.digest()) +- return h ++ if self._hmac: ++ return self._hmac ++ else: ++ h = self._outer.copy() ++ h.update(self._inner.digest()) ++ return h + + def digest(self): + """Return the hash value of this hashing object. +@@ -179,9 +193,11 @@ def digest(key, msg, digest): + A hashlib constructor returning a new hash object. *OR* + A module supporting PEP 247. + """ +- if (_hashopenssl is not None and +- isinstance(digest, str) and digest in _openssl_md_meths): +- return _hashopenssl.hmac_digest(key, msg, digest) ++ if _hashopenssl is not None and isinstance(digest, (str, _functype)): ++ try: ++ return _hashopenssl.hmac_digest(key, msg, digest) ++ except _hashopenssl.UnsupportedDigestmodError: ++ pass + + if callable(digest): + digest_cons = digest +diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py +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 + from _operator import _compare_digest as operator_compare_digest try: - # OpenSSL's scrypt requires OpenSSL 1.1+ -@@ -259,4 +213,6 @@ for __func_name in __always_supported: - - # Cleanup locals() - del __always_supported, __func_name, __get_hash --del __py_new, __hash_new, __get_openssl_constructor -+del __hash_new, __get_openssl_constructor -+if not get_fips_mode(): -+ del __py_new -diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py -index 86f31a55878..8235505092b 100644 ---- a/Lib/test/test_hashlib.py -+++ b/Lib/test/test_hashlib.py -@@ -1039,6 +1039,7 @@ class KDFTests(unittest.TestCase): - iterations=1, dklen=None) - self.assertEqual(out, self.pbkdf2_results['sha1'][0][0]) ++ import _hashlib as _hashopenssl + from _hashlib import HMAC as C_HMAC + from _hashlib import hmac_new as c_hmac_new + from _hashlib import compare_digest as openssl_compare_digest + except ImportError: ++ _hashopenssl = None + C_HMAC = None + c_hmac_new = None + openssl_compare_digest = None -+ @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.26.2 - - -From 8a174c9a8d4180a5a7b19f4419b98c63b91b13ab Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Thu, 25 Jul 2019 17:19:06 +0200 -Subject: [PATCH 02/13] Disable Python's hash implementations in FIPS mode, - forcing OpenSSL - ---- - Include/_hashopenssl.h | 66 ++++++++++++++++++++++++++++++++++ - Modules/_blake2/blake2b_impl.c | 5 +++ - Modules/_blake2/blake2module.c | 3 ++ - Modules/_blake2/blake2s_impl.c | 5 +++ - Modules/_hashopenssl.c | 35 +----------------- - setup.py | 28 ++++++++++----- - 6 files changed, 99 insertions(+), 43 deletions(-) - create mode 100644 Include/_hashopenssl.h - -diff --git a/Include/_hashopenssl.h b/Include/_hashopenssl.h -new file mode 100644 -index 00000000000..a726c0d3fbf ---- /dev/null -+++ b/Include/_hashopenssl.h -@@ -0,0 +1,66 @@ -+#ifndef Py_HASHOPENSSL_H -+#define Py_HASHOPENSSL_H ++try: ++ import _sha256 as sha256_module ++except ImportError: ++ sha256_module = None ++ + + def ignore_warning(func): + @functools.wraps(func) +@@ -32,22 +39,27 @@ def ignore_warning(func): + + class TestVectorsTestCase(unittest.TestCase): + +- def asssert_hmac( +- self, key, data, digest, hashfunc, hashname, digest_size, block_size ++ def assert_hmac_internals( ++ self, h, digest, hashname, digest_size, block_size + ): +- h = hmac.HMAC(key, data, digestmod=hashfunc) + self.assertEqual(h.hexdigest().upper(), digest.upper()) + self.assertEqual(h.digest(), binascii.unhexlify(digest)) + self.assertEqual(h.name, f"hmac-{hashname}") + self.assertEqual(h.digest_size, digest_size) + self.assertEqual(h.block_size, block_size) + ++ def assert_hmac( ++ self, key, data, digest, hashfunc, hashname, digest_size, block_size ++ ): ++ h = hmac.HMAC(key, data, digestmod=hashfunc) ++ self.assert_hmac_internals( ++ h, digest, hashname, digest_size, block_size ++ ) ++ + h = hmac.HMAC(key, data, digestmod=hashname) +- self.assertEqual(h.hexdigest().upper(), digest.upper()) +- self.assertEqual(h.digest(), binascii.unhexlify(digest)) +- self.assertEqual(h.name, f"hmac-{hashname}") +- self.assertEqual(h.digest_size, digest_size) +- self.assertEqual(h.block_size, block_size) ++ self.assert_hmac_internals( ++ h, digest, hashname, digest_size, block_size ++ ) + + h = hmac.HMAC(key, digestmod=hashname) + h2 = h.copy() +@@ -56,11 +68,9 @@ class TestVectorsTestCase(unittest.TestCase): + self.assertEqual(h.hexdigest().upper(), digest.upper()) + + h = hmac.new(key, data, digestmod=hashname) +- self.assertEqual(h.hexdigest().upper(), digest.upper()) +- self.assertEqual(h.digest(), binascii.unhexlify(digest)) +- self.assertEqual(h.name, f"hmac-{hashname}") +- self.assertEqual(h.digest_size, digest_size) +- self.assertEqual(h.block_size, block_size) ++ self.assert_hmac_internals( ++ h, digest, hashname, digest_size, block_size ++ ) + + h = hmac.new(key, None, digestmod=hashname) + h.update(data) +@@ -81,23 +91,18 @@ class TestVectorsTestCase(unittest.TestCase): + hmac.digest(key, data, digest=hashfunc), + binascii.unhexlify(digest) + ) +- with unittest.mock.patch('hmac._openssl_md_meths', {}): +- self.assertEqual( +- hmac.digest(key, data, digest=hashname), +- binascii.unhexlify(digest) +- ) +- self.assertEqual( +- hmac.digest(key, data, digest=hashfunc), +- binascii.unhexlify(digest) +- ) + -+#include "Python.h" -+#include -+#include ++ h = hmac.HMAC.__new__(hmac.HMAC) ++ h._init_old(key, data, digestmod=hashname) ++ self.assert_hmac_internals( ++ h, digest, hashname, digest_size, block_size ++ ) + + if c_hmac_new is not None: + h = c_hmac_new(key, data, digestmod=hashname) +- self.assertEqual(h.hexdigest().upper(), digest.upper()) +- self.assertEqual(h.digest(), binascii.unhexlify(digest)) +- self.assertEqual(h.name, f"hmac-{hashname}") +- self.assertEqual(h.digest_size, digest_size) +- self.assertEqual(h.block_size, block_size) ++ self.assert_hmac_internals( ++ h, digest, hashname, digest_size, block_size ++ ) + + h = c_hmac_new(key, digestmod=hashname) + h2 = h.copy() +@@ -105,12 +110,24 @@ class TestVectorsTestCase(unittest.TestCase): + h.update(data) + self.assertEqual(h.hexdigest().upper(), digest.upper()) + ++ func = getattr(_hashopenssl, f"openssl_{hashname}") ++ h = c_hmac_new(key, data, digestmod=func) ++ self.assert_hmac_internals( ++ h, digest, hashname, digest_size, block_size ++ ) + -+/* LCOV_EXCL_START */ -+static PyObject * -+_setException(PyObject *exc) -+{ -+ unsigned long errcode; -+ const char *lib, *func, *reason; ++ h = hmac.HMAC.__new__(hmac.HMAC) ++ h._init_hmac(key, data, digestmod=hashname) ++ self.assert_hmac_internals( ++ h, digest, hashname, digest_size, block_size ++ ) + -+ errcode = ERR_peek_last_error(); -+ if (!errcode) { -+ PyErr_SetString(exc, "unknown reasons"); -+ return NULL; -+ } -+ ERR_clear_error(); + @hashlib_helper.requires_hashdigest('md5', openssl=True) + def test_md5_vectors(self): + # Test the HMAC module against test vectors from the RFC. + + def md5test(key, data, digest): +- self.asssert_hmac( ++ self.assert_hmac( + key, data, digest, + hashfunc=hashlib.md5, + hashname="md5", +@@ -150,7 +167,7 @@ class TestVectorsTestCase(unittest.TestCase): + @hashlib_helper.requires_hashdigest('sha1', openssl=True) + def test_sha_vectors(self): + def shatest(key, data, digest): +- self.asssert_hmac( ++ self.assert_hmac( + key, data, digest, + hashfunc=hashlib.sha1, + hashname="sha1", +@@ -191,7 +208,7 @@ class TestVectorsTestCase(unittest.TestCase): + def hmactest(key, data, hexdigests): + digest = hexdigests[hashfunc] + +- self.asssert_hmac( ++ self.assert_hmac( + key, data, digest, + hashfunc=hashfunc, + hashname=hash_name, +@@ -427,6 +444,15 @@ class ConstructorTestCase(unittest.TestCase): + ): + C_HMAC() + ++ @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) ++ self.assertEqual(h.hexdigest(), self.expected) ++ self.assertEqual(h.name, "hmac-sha256") + -+ lib = ERR_lib_error_string(errcode); -+ func = ERR_func_error_string(errcode); -+ reason = ERR_reason_error_string(errcode); ++ digest = hmac.digest(b"key", b"hash this!", sha256_module.sha256) ++ self.assertEqual(digest, binascii.unhexlify(self.expected)) + -+ if (lib && func) { -+ PyErr_Format(exc, "[%s: %s] %s", lib, func, reason); -+ } -+ else if (lib) { -+ PyErr_Format(exc, "[%s] %s", lib, reason); + + class SanityTestCase(unittest.TestCase): + +@@ -447,21 +473,21 @@ class SanityTestCase(unittest.TestCase): + class CopyTestCase(unittest.TestCase): + + @hashlib_helper.requires_hashdigest('sha256') +- def test_attributes(self): ++ def test_attributes_old(self): + # Testing if attributes are of same type. +- h1 = hmac.HMAC(b"key", digestmod="sha256") ++ h1 = hmac.HMAC.__new__(hmac.HMAC) ++ h1._init_old(b"key", b"msg", digestmod="sha256") + h2 = h1.copy() +- self.assertTrue(h1._digest_cons == h2._digest_cons, +- "digest constructors don't match.") + self.assertEqual(type(h1._inner), type(h2._inner), + "Types of inner don't match.") + self.assertEqual(type(h1._outer), type(h2._outer), + "Types of outer don't match.") + + @hashlib_helper.requires_hashdigest('sha256') +- def test_realcopy(self): ++ def test_realcopy_old(self): + # Testing if the copy method created a real copy. +- h1 = hmac.HMAC(b"key", digestmod="sha256") ++ h1 = hmac.HMAC.__new__(hmac.HMAC) ++ h1._init_old(b"key", b"msg", digestmod="sha256") + h2 = h1.copy() + # Using id() in case somebody has overridden __eq__/__ne__. + self.assertTrue(id(h1) != id(h2), "No real copy of the HMAC instance.") +@@ -469,17 +495,15 @@ class CopyTestCase(unittest.TestCase): + "No real copy of the attribute 'inner'.") + self.assertTrue(id(h1._outer) != id(h2._outer), + "No real copy of the attribute 'outer'.") +- self.assertEqual(h1._inner, h1.inner) +- self.assertEqual(h1._outer, h1.outer) +- self.assertEqual(h1._digest_cons, h1.digest_cons) ++ self.assertIs(h1._hmac, None) + ++ @unittest.skipIf(_hashopenssl is None, "test requires _hashopenssl") + @hashlib_helper.requires_hashdigest('sha256') +- def test_properties(self): +- # deprecated properties +- h1 = hmac.HMAC(b"key", digestmod="sha256") +- self.assertEqual(h1._inner, h1.inner) +- self.assertEqual(h1._outer, h1.outer) +- self.assertEqual(h1._digest_cons, h1.digest_cons) ++ def test_realcopy_hmac(self): ++ h1 = hmac.HMAC.__new__(hmac.HMAC) ++ h1._init_hmac(b"key", b"msg", digestmod="sha256") ++ h2 = h1.copy() ++ self.assertTrue(id(h1._hmac) != id(h2._hmac)) + + @hashlib_helper.requires_hashdigest('sha256') + def test_equality(self): +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 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 56dfff9..ca9fea9 100644 +--- a/Modules/_hashopenssl.c ++++ b/Modules/_hashopenssl.c +@@ -260,6 +260,8 @@ typedef struct { + PyTypeObject *EVPXOFtype; + #endif + _Py_hashtable_t *hashtable; ++ PyObject *constructs; ++ PyObject *unsupported_digestmod_error; + } _hashlibstate; + + static inline _hashlibstate* +@@ -420,6 +422,48 @@ py_digest_by_name(PyObject *module, const char *name, enum Py_hash_type py_ht) + return digest; + } + ++/* Get digest EVP from object ++ * ++ * * string ++ * * _hashopenssl builtin function ++ * ++ * on error returns NULL with exception set. ++ */ ++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; ++ ++ if (PyUnicode_Check(digestmod)) { ++ name_obj = digestmod; ++ } else { ++ _hashlibstate *state = get_hashlib_state(module); ++ // borrowed ref ++ name_obj = PyDict_GetItem(state->constructs, digestmod); + } -+ else { -+ PyErr_SetString(exc, reason); ++ if (name_obj == NULL) { ++ _hashlibstate *state = get_hashlib_state(module); ++ PyErr_Clear(); ++ PyErr_Format( ++ state->unsupported_digestmod_error, ++ "Unsupported digestmod %R", digestmod); ++ return NULL; + } -+ return NULL; -+} -+/* LCOV_EXCL_STOP */ -+ + -+__attribute__((__unused__)) -+static int -+_Py_hashlib_fips_error(char *name) { -+ int result = FIPS_mode(); -+ if (result == 0) { -+ // "If the library was built without support of the FIPS Object Module, -+ // then the function will return 0 with an error code of -+ // CRYPTO_R_FIPS_MODE_NOT_SUPPORTED (0x0f06d065)." -+ // But 0 is also a valid result value. -+ -+ unsigned long errcode = ERR_peek_last_error(); -+ if (errcode) { -+ _setException(PyExc_ValueError); -+ return 1; -+ } -+ return 0; ++ name = PyUnicode_AsUTF8(name_obj); ++ if (name == NULL) { ++ return NULL; + } -+ PyErr_Format(PyExc_ValueError, "%s is not available in FIPS mode", -+ name); -+ return 1; -+} + -+#define FAIL_RETURN_IN_FIPS_MODE(name) do { \ -+ if (_Py_hashlib_fips_error(name)) return NULL; \ -+} while (0) ++ evp = py_digest_by_name(module, name, py_ht); ++ if (evp == NULL) { ++ return NULL; ++ } + -+#endif // !Py_HASHOPENSSL_H -diff --git a/Modules/_blake2/blake2b_impl.c b/Modules/_blake2/blake2b_impl.c -index 7fb1296f8b2..67620afcad2 100644 ---- a/Modules/_blake2/blake2b_impl.c -+++ b/Modules/_blake2/blake2b_impl.c -@@ -14,6 +14,7 @@ - */ - - #include "Python.h" -+#include "_hashopenssl.h" - #include "pystrhex.h" - - #include "../hashlib.h" -@@ -96,6 +97,8 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size, - BLAKE2bObject *self = NULL; - Py_buffer buf; - -+ FAIL_RETURN_IN_FIPS_MODE("_blake2"); ++ return evp; ++} + - self = new_BLAKE2bObject(type); - if (self == NULL) { - goto error; -@@ -274,6 +277,8 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data) + static EVPobject * + newEVPobject(PyTypeObject *type) { - Py_buffer buf; +@@ -1238,7 +1282,6 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name, -+ FAIL_RETURN_IN_FIPS_MODE("_blake2"); -+ - GET_BUFFER_VIEW_OR_ERROUT(data, &buf); + 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; + } - if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) -diff --git a/Modules/_blake2/blake2module.c b/Modules/_blake2/blake2module.c -index ff142c9f3ed..bc67529cb5e 100644 ---- a/Modules/_blake2/blake2module.c -+++ b/Modules/_blake2/blake2module.c -@@ -9,6 +9,7 @@ - */ +@@ -1443,25 +1486,21 @@ _hashlib.hmac_digest as _hashlib_hmac_singleshot - #include "Python.h" -+#include "_hashopenssl.h" + key: Py_buffer + msg: Py_buffer +- digest: str ++ digest: object - #include "impl/blake2.h" + Single-shot HMAC. + [clinic start generated code]*/ -@@ -57,6 +58,8 @@ PyInit__blake2(void) - PyObject *m; - PyObject *d; - -+ FAIL_RETURN_IN_FIPS_MODE("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 e3e90d0587b..57c0f3fcbd7 100644 ---- a/Modules/_blake2/blake2s_impl.c -+++ b/Modules/_blake2/blake2s_impl.c -@@ -14,6 +14,7 @@ - */ - - #include "Python.h" -+#include "_hashopenssl.h" - #include "pystrhex.h" - - #include "../hashlib.h" -@@ -96,6 +97,8 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size, - BLAKE2sObject *self = NULL; - Py_buffer buf; - -+ FAIL_RETURN_IN_FIPS_MODE("_blake2"); -+ - self = new_BLAKE2sObject(type); - if (self == NULL) { - goto error; -@@ -274,6 +277,8 @@ _blake2_blake2s_update(BLAKE2sObject *self, PyObject *data) + static PyObject * + _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, +- Py_buffer *msg, const char *digest) +-/*[clinic end generated code: output=15658ede5ab98185 input=019dffc571909a46]*/ ++ Py_buffer *msg, PyObject *digest) ++/*[clinic end generated code: output=82f19965d12706ac input=0a0790cc3db45c2e]*/ { - Py_buffer buf; - -+ FAIL_RETURN_IN_FIPS_MODE("_blake2"); -+ - GET_BUFFER_VIEW_OR_ERROUT(data, &buf); - - if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) -diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c -index adc86537732..deecc077ef8 100644 ---- a/Modules/_hashopenssl.c -+++ b/Modules/_hashopenssl.c -@@ -16,6 +16,7 @@ - #include "Python.h" - #include "hashlib.h" - #include "pystrhex.h" -+#include "_hashopenssl.h" + unsigned char md[EVP_MAX_MD_SIZE] = {0}; + unsigned int md_len = 0; + unsigned char *result; + PY_EVP_MD *evp; +- 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."); +@@ -1473,7 +1512,7 @@ _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, + return NULL; + } - /* EVP is the preferred interface to hashing in OpenSSL */ -@@ -24,9 +25,6 @@ - #include - /* We use the object interface to discover what hashes OpenSSL supports. */ - #include --#include "openssl/err.h" -- --#include // FIPS_mode() +- 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; + } +@@ -1505,15 +1544,15 @@ _hashlib.hmac_new - #ifndef OPENSSL_THREADS - # error "OPENSSL_THREADS is not defined, Python requires thread-safe OpenSSL" -@@ -118,37 +116,6 @@ class _hashlib.HMAC "HMACobject *" "((_hashlibstate *)PyModule_GetState(module)) - /*[clinic end generated code: output=da39a3ee5e6b4b0d input=7df1bcf6f75cb8ef]*/ + key: Py_buffer + msg as msg_obj: object(c_default="NULL") = b'' +- digestmod: str(c_default="NULL") = None ++ digestmod: object(c_default="NULL") = None + Return a new hmac object. + [clinic start generated code]*/ --/* LCOV_EXCL_START */ --static PyObject * --_setException(PyObject *exc) --{ -- unsigned long errcode; -- const char *lib, *func, *reason; -- -- errcode = ERR_peek_last_error(); -- if (!errcode) { -- PyErr_SetString(exc, "unknown reasons"); -- return NULL; -- } -- ERR_clear_error(); -- -- lib = ERR_lib_error_string(errcode); -- func = ERR_func_error_string(errcode); -- reason = ERR_reason_error_string(errcode); -- -- if (lib && func) { -- PyErr_Format(exc, "[%s: %s] %s", lib, func, reason); -- } -- else if (lib) { -- PyErr_Format(exc, "[%s] %s", lib, reason); -- } -- else { -- PyErr_SetString(exc, reason); -- } -- return NULL; --} --/* LCOV_EXCL_STOP */ -- - /* {Py_tp_new, NULL} doesn't block __new__ */ static PyObject * - _disabled_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) -diff --git a/setup.py b/setup.py -index bd5f7369244..89edbb627fa 100644 ---- a/setup.py -+++ b/setup.py -@@ -2306,7 +2306,7 @@ class PyBuildExt(build_ext): - sources=sources, - depends=depends)) + _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, +- const char *digestmod) +-/*[clinic end generated code: output=9a35673be0cbea1b input=a0878868eb190134]*/ ++ PyObject *digestmod) ++/*[clinic end generated code: output=c20d9e4d9ed6d219 input=5f4071dcc7f34362]*/ + { + PyTypeObject *type = get_hashlib_state(module)->HMACtype; + PY_EVP_MD *digest; +@@ -1527,14 +1566,14 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, + return NULL; + } -- 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() +- if ((digestmod == NULL) || !strlen(digestmod)) { ++ if (digestmod == NULL) { + PyErr_SetString( + PyExc_TypeError, "Missing required parameter 'digestmod'."); + return NULL; + } -@@ -2327,7 +2327,7 @@ class PyBuildExt(build_ext): - 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') +- digest = py_digest_by_name(module, digestmod, Py_ht_mac); +- if (!digest) { ++ digest = py_digest_by_digestmod(module, digestmod, Py_ht_mac); ++ if (digest == NULL) { + return NULL; + } - # Find OpenSSL includes - ssl_incs = find_file( -@@ -2335,7 +2335,7 @@ class PyBuildExt(build_ext): - ) - if ssl_incs is None: - self.missing.extend(['_ssl', '_hashlib']) -- return None, None -+ raise ValueError('Cannot build for RHEL without OpenSSL') +@@ -2117,6 +2156,8 @@ hashlib_traverse(PyObject *m, visitproc visit, void *arg) + #ifdef PY_OPENSSL_HAS_SHAKE + Py_VISIT(state->EVPXOFtype); + #endif ++ Py_VISIT(state->constructs); ++ Py_VISIT(state->unsupported_digestmod_error); + return 0; + } - # OpenSSL 1.0.2 uses Kerberos for KRB5 ciphers - krb5_h = find_file( -@@ -2345,12 +2345,23 @@ class PyBuildExt(build_ext): - if krb5_h: - ssl_incs.extend(krb5_h) +@@ -2129,10 +2170,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; + } + +@@ -2227,6 +2272,79 @@ hashlib_init_hmactype(PyObject *module) + return 0; + } ++static int ++hashlib_init_constructors(PyObject *module) ++{ ++ /* Create dict from builtin openssl_hash functions to name ++ * {_hashlib.openssl_sha256: "sha256", ...} ++ */ ++ PyModuleDef *mdef; ++ PyMethodDef *fdef; ++ PyObject *proxy; ++ PyObject *func, *name_obj; ++ _hashlibstate *state = get_hashlib_state(module); ++ ++ mdef = PyModule_GetDef(module); ++ if (mdef == NULL) { ++ return -1; ++ } + -+ ssl_args = { -+ 'include_dirs': openssl_includes, -+ 'library_dirs': openssl_libdirs, -+ 'libraries': ['ssl', 'crypto'], ++ state->constructs = PyDict_New(); ++ if (state->constructs == NULL) { ++ return -1; ++ } ++ ++ for (fdef = mdef->m_methods; fdef->ml_name != NULL; fdef++) { ++ if (strncmp(fdef->ml_name, "openssl_", 8)) { ++ continue; + } ++ name_obj = PyUnicode_FromString(fdef->ml_name + 8); ++ if (name_obj == NULL) { ++ return -1; ++ } ++ func = PyObject_GetAttrString(module, fdef->ml_name); ++ if (func == NULL) { ++ Py_DECREF(name_obj); ++ return -1; ++ } ++ int rc = PyDict_SetItem(state->constructs, func, name_obj); ++ Py_DECREF(func); ++ Py_DECREF(name_obj); ++ if (rc < 0) { ++ return -1; ++ } ++ } + -+ return ssl_args ++ proxy = PyDictProxy_New(state->constructs); ++ if (proxy == NULL) { ++ return -1; ++ } + -+ def detect_openssl_hashlib(self): ++ int rc = _PyModule_AddObjectRef(module, "_constructors", proxy); ++ Py_DECREF(proxy); ++ if (rc < 0) { ++ return -1; ++ } ++ return 0; ++} + -+ config_vars = sysconfig.get_config_vars() ++static int ++hashlib_exception(PyObject *module) ++{ ++ _hashlibstate *state = get_hashlib_state(module); ++ state->unsupported_digestmod_error = PyErr_NewException( ++ "_hashlib.UnsupportedDigestmodError", PyExc_ValueError, NULL); ++ if (state->unsupported_digestmod_error == NULL) { ++ return -1; ++ } ++ if (_PyModule_AddObjectRef(module, "UnsupportedDigestmodError", ++ state->unsupported_digestmod_error) < 0) { ++ return -1; ++ } ++ return 0; ++} + - 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']) - ) - else: -@@ -2358,9 +2369,7 @@ class PyBuildExt(build_ext): ++ + static PyModuleDef_Slot hashlib_slots[] = { + /* OpenSSL 1.0.2 and LibreSSL */ + {Py_mod_exec, hashlib_openssl_legacy_init}, +@@ -2235,6 +2353,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}, ++ {Py_mod_exec, hashlib_init_constructors}, ++ {Py_mod_exec, hashlib_exception}, + {0, NULL} + }; - self.add(Extension('_hashlib', ['_hashopenssl.c'], - depends=['hashlib.h'], -- include_dirs=openssl_includes, -- library_dirs=openssl_libdirs, -- libraries=openssl_libs)) -+ **self.detect_openssl_args()) ) +diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h +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__, - def detect_hash_builtins(self): - # By default we always compile these even when OpenSSL is available -@@ -2417,6 +2426,7 @@ class PyBuildExt(build_ext): - '_blake2/blake2b_impl.c', - '_blake2/blake2s_impl.c' - ], -+ **self.detect_openssl_args(), - depends=blake2_deps - )) + static PyObject * + _hashlib_hmac_singleshot_impl(PyObject *module, Py_buffer *key, +- Py_buffer *msg, const char *digest); ++ Py_buffer *msg, PyObject *digest); + + static PyObject * + _hashlib_hmac_singleshot(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +@@ -1117,7 +1117,7 @@ _hashlib_hmac_singleshot(PyObject *module, PyObject *const *args, Py_ssize_t nar + PyObject *argsbuf[3]; + Py_buffer key = {NULL, NULL}; + Py_buffer msg = {NULL, NULL}; +- const char *digest; ++ PyObject *digest; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 3, 3, 0, argsbuf); + if (!args) { +@@ -1137,19 +1137,7 @@ _hashlib_hmac_singleshot(PyObject *module, PyObject *const *args, Py_ssize_t nar + _PyArg_BadArgument("hmac_digest", "argument 'msg'", "contiguous buffer", args[1]); + goto exit; + } +- if (!PyUnicode_Check(args[2])) { +- _PyArg_BadArgument("hmac_digest", "argument 'digest'", "str", args[2]); +- goto exit; +- } +- Py_ssize_t digest_length; +- digest = PyUnicode_AsUTF8AndSize(args[2], &digest_length); +- if (digest == NULL) { +- goto exit; +- } +- if (strlen(digest) != (size_t)digest_length) { +- PyErr_SetString(PyExc_ValueError, "embedded null character"); +- goto exit; +- } ++ digest = args[2]; + return_value = _hashlib_hmac_singleshot_impl(module, &key, &msg, digest); + + exit: +@@ -1176,7 +1164,7 @@ PyDoc_STRVAR(_hashlib_hmac_new__doc__, + + static PyObject * + _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj, +- const char *digestmod); ++ PyObject *digestmod); + + static PyObject * + _hashlib_hmac_new(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +@@ -1188,7 +1176,7 @@ _hashlib_hmac_new(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + Py_buffer key = {NULL, NULL}; + PyObject *msg_obj = NULL; +- const char *digestmod = NULL; ++ PyObject *digestmod = NULL; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); + if (!args) { +@@ -1210,19 +1198,7 @@ _hashlib_hmac_new(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyO + goto skip_optional_pos; + } + } +- if (!PyUnicode_Check(args[2])) { +- _PyArg_BadArgument("hmac_new", "argument 'digestmod'", "str", args[2]); +- goto exit; +- } +- Py_ssize_t digestmod_length; +- digestmod = PyUnicode_AsUTF8AndSize(args[2], &digestmod_length); +- if (digestmod == NULL) { +- goto exit; +- } +- if (strlen(digestmod) != (size_t)digestmod_length) { +- PyErr_SetString(PyExc_ValueError, "embedded null character"); +- goto exit; +- } ++ digestmod = args[2]; + skip_optional_pos: + return_value = _hashlib_hmac_new_impl(module, &key, msg_obj, digestmod); +@@ -1442,4 +1418,4 @@ exit: + #ifndef _HASHLIB_GET_FIPS_MODE_METHODDEF + #define _HASHLIB_GET_FIPS_MODE_METHODDEF + #endif /* !defined(_HASHLIB_GET_FIPS_MODE_METHODDEF) */ +-/*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/ ++/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/ -- -2.26.2 +2.37.2 -From 56171083467bd5798adcb1946cfc0b1d68403755 Mon Sep 17 00:00:00 2001 +From b63e3fbd7c0506b5a6c00c1bb0d255054e38bbe8 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Thu, 12 Dec 2019 16:58:31 +0100 -Subject: [PATCH 03/13] Expose all hashes available to 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. --- - Include/_hashopenssl.h | 11 ++-- - Modules/_blake2/blake2b_impl.c | 4 +- - Modules/_blake2/blake2module.c | 2 +- - Modules/_blake2/blake2s_impl.c | 4 +- - Modules/_hashopenssl.c | 43 +++++++++++++ + Lib/test/test_hashlib.py | 6 ++ + Modules/_hashopenssl.c | 37 +++++++++++ Modules/clinic/_hashopenssl.c.h | 106 +++++++++++++++++++++++++++++++- - 6 files changed, 158 insertions(+), 12 deletions(-) + 3 files changed, 148 insertions(+), 1 deletion(-) -diff --git a/Include/_hashopenssl.h b/Include/_hashopenssl.h -index a726c0d3fbf..47ed0030422 100644 ---- a/Include/_hashopenssl.h -+++ b/Include/_hashopenssl.h -@@ -39,7 +39,7 @@ _setException(PyObject *exc) - - __attribute__((__unused__)) - static int --_Py_hashlib_fips_error(char *name) { -+_Py_hashlib_fips_error(PyObject *exc, char *name) { - int result = FIPS_mode(); - if (result == 0) { - // "If the library was built without support of the FIPS Object Module, -@@ -49,18 +49,17 @@ _Py_hashlib_fips_error(char *name) { - - unsigned long errcode = ERR_peek_last_error(); - if (errcode) { -- _setException(PyExc_ValueError); -+ _setException(exc); - return 1; - } - return 0; - } -- PyErr_Format(PyExc_ValueError, "%s is not available in FIPS mode", -- name); -+ PyErr_Format(exc, "%s is not available in FIPS mode", name); - return 1; - } - --#define FAIL_RETURN_IN_FIPS_MODE(name) do { \ -- if (_Py_hashlib_fips_error(name)) return NULL; \ -+#define FAIL_RETURN_IN_FIPS_MODE(exc, name) do { \ -+ if (_Py_hashlib_fips_error(exc, name)) return NULL; \ - } while (0) - - #endif // !Py_HASHOPENSSL_H -diff --git a/Modules/_blake2/blake2b_impl.c b/Modules/_blake2/blake2b_impl.c -index 67620afcad2..9e125dcbf43 100644 ---- a/Modules/_blake2/blake2b_impl.c -+++ b/Modules/_blake2/blake2b_impl.c -@@ -97,7 +97,7 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size, - BLAKE2bObject *self = NULL; - Py_buffer buf; - -- FAIL_RETURN_IN_FIPS_MODE("_blake2"); -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); - - self = new_BLAKE2bObject(type); - if (self == NULL) { -@@ -277,7 +277,7 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data) - { - Py_buffer buf; - -- FAIL_RETURN_IN_FIPS_MODE("_blake2"); -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); - - GET_BUFFER_VIEW_OR_ERROUT(data, &buf); - -diff --git a/Modules/_blake2/blake2module.c b/Modules/_blake2/blake2module.c -index bc67529cb5e..79a9eed5c13 100644 ---- a/Modules/_blake2/blake2module.c -+++ b/Modules/_blake2/blake2module.c -@@ -58,7 +58,7 @@ PyInit__blake2(void) - PyObject *m; - PyObject *d; - -- FAIL_RETURN_IN_FIPS_MODE("blake2"); -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ImportError, "blake2"); - - m = PyModule_Create(&blake2_module); - if (m == NULL) -diff --git a/Modules/_blake2/blake2s_impl.c b/Modules/_blake2/blake2s_impl.c -index 57c0f3fcbd7..b59624d7d98 100644 ---- a/Modules/_blake2/blake2s_impl.c -+++ b/Modules/_blake2/blake2s_impl.c -@@ -97,7 +97,7 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size, - BLAKE2sObject *self = NULL; - Py_buffer buf; - -- FAIL_RETURN_IN_FIPS_MODE("_blake2"); -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); - - self = new_BLAKE2sObject(type); - if (self == NULL) { -@@ -277,7 +277,7 @@ _blake2_blake2s_update(BLAKE2sObject *self, PyObject *data) - { - Py_buffer buf; - -- FAIL_RETURN_IN_FIPS_MODE("_blake2"); -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); - - GET_BUFFER_VIEW_OR_ERROUT(data, &buf); - +diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py +index f845c7a..7aaeb76 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 deecc077ef8..a805183721b 100644 +index ca9fea9..9d98d20 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c -@@ -253,6 +253,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 - } - -@@ -946,6 +952,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj, +@@ -1138,6 +1138,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj, } @@ -727,7 +982,7 @@ index deecc077ef8..a805183721b 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] @@ -744,14 +999,14 @@ index deecc077ef8..a805183721b 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] -@@ -1931,6 +1972,8 @@ static struct PyMethodDef EVP_functions[] = { +@@ -2135,6 +2170,8 @@ static struct PyMethodDef EVP_functions[] = { _HASHLIB_OPENSSL_SHA256_METHODDEF _HASHLIB_OPENSSL_SHA384_METHODDEF _HASHLIB_OPENSSL_SHA512_METHODDEF @@ -761,7 +1016,7 @@ index deecc077ef8..a805183721b 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 68aa765e529..2957ae2e135 100644 +index 4466ec4..54c22b2 100644 --- a/Modules/clinic/_hashopenssl.c.h +++ b/Modules/clinic/_hashopenssl.c.h @@ -540,6 +540,110 @@ exit: @@ -875,874 +1130,518 @@ index 68aa765e529..2957ae2e135 100644 #if defined(PY_OPENSSL_HAS_SHA3) PyDoc_STRVAR(_hashlib_openssl_sha3_224__doc__, -@@ -1442,4 +1546,4 @@ exit: +@@ -1418,4 +1522,4 @@ exit: #ifndef _HASHLIB_GET_FIPS_MODE_METHODDEF #define _HASHLIB_GET_FIPS_MODE_METHODDEF #endif /* !defined(_HASHLIB_GET_FIPS_MODE_METHODDEF) */ --/*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/ -+/*[clinic end generated code: output=4f8cc45bf0337f8e input=a9049054013a1b77]*/ +-/*[clinic end generated code: output=7ff9aad0bd53e7ce input=a9049054013a1b77]*/ ++/*[clinic end generated code: output=fab05055e982f112 input=a9049054013a1b77]*/ -- -2.26.2 +2.37.2 -From e024cae691bffa2d093a63f8e2058331fce94d2a Mon Sep 17 00:00:00 2001 +From dc8ad7b98d6d9bf14cae439acb3a99fa8f4f5020 Mon Sep 17 00:00:00 2001 From: Petr Viktorin -Date: Thu, 25 Jul 2019 18:13:45 +0200 -Subject: [PATCH 04/13] Fix tests +Date: Thu, 1 Aug 2019 17:57:05 +0200 +Subject: [PATCH 05/10] Use a stronger hash in multiprocessing handshake +Adapted from patch by David Malcolm, +https://bugs.python.org/issue17258 --- - Lib/test/test_hashlib.py | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py -index 8235505092b..a838bcee2a8 100644 ---- a/Lib/test/test_hashlib.py -+++ b/Lib/test/test_hashlib.py -@@ -354,6 +354,11 @@ class HashLibTestCase(unittest.TestCase): - # 2 is for hashlib.name(...) and hashlib.new(name, ...) - self.assertGreaterEqual(len(constructors), 2) - for hash_object_constructor in constructors: -+ if ( -+ kwargs -+ and hash_object_constructor.__name__.startswith('openssl_') -+ ): -+ return - m = hash_object_constructor(data, **kwargs) - computed = m.hexdigest() if not shake else m.hexdigest(length) - self.assertEqual( --- -2.26.2 - + 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 510e4b5..b68f2fb 100644 +--- a/Lib/multiprocessing/connection.py ++++ b/Lib/multiprocessing/connection.py +@@ -42,6 +42,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' +@@ -741,7 +745,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) +@@ -757,7 +761,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: +-- +2.37.2 -From 3e87bf1c3d32c09a50385d8576b1164cafce4158 Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Fri, 26 Jul 2019 15:41:10 +0200 -Subject: [PATCH 05/13] Implement hmac.new using new built-in module, - _hmacopenssl -Make _hmacopenssl.HMAC subclassable; subclass it as hmac.HMAC under FIPS +From af0c88c9d5bc4f9c127e49ed80d14e25d18813f2 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 -This removes the _hmacopenssl.new function. --- - Lib/hmac.py | 37 +++ - Lib/test/test_hmac.py | 28 ++ - Modules/_hmacopenssl.c | 459 ++++++++++++++++++++++++++++++++ - Modules/clinic/_hmacopenssl.c.h | 104 ++++++++ - setup.py | 4 + - 5 files changed, 632 insertions(+) - create mode 100644 Modules/_hmacopenssl.c - create mode 100644 Modules/clinic/_hmacopenssl.c.h + 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/hmac.py b/Lib/hmac.py -index 180bc378b52..482e443bfe4 100644 ---- a/Lib/hmac.py -+++ b/Lib/hmac.py -@@ -14,6 +14,8 @@ else: - _openssl_md_meths = frozenset(_hashopenssl.openssl_md_meth_names) - compare_digest = _hashopenssl.compare_digest - import hashlib as _hashlib -+import _hashlib as _hashlibopenssl -+import _hmacopenssl +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', - trans_5C = bytes((x ^ 0x5C) for x in range(256)) - trans_36 = bytes((x ^ 0x36) for x in range(256)) -@@ -48,6 +50,11 @@ class HMAC: - msg argument. Passing it as a keyword argument is - recommended, though not required for legacy API reasons. - """ -+ if _hashlib.get_fips_mode(): -+ raise ValueError( -+ 'This class is not available in FIPS mode. ' -+ + 'Use hmac.new().' -+ ) + __builtin_constructor_cache = {} - if not isinstance(key, (bytes, bytearray)): - raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) -@@ -110,6 +117,8 @@ class HMAC: +-# 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 update(self, msg): - """Feed data from msg into this hashing object.""" -+ if _hashlib.get_fips_mode(): -+ raise ValueError('hmac.HMAC is not available in FIPS mode') - self._inner.update(msg) + def __get_builtin_constructor(name): + cache = __builtin_constructor_cache +diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py +index 7aaeb76..fa4a8d7 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(",") + } - def copy(self): -@@ -150,6 +159,34 @@ class HMAC: - h = self._current() - return h.hexdigest() +-# 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') -+def _get_openssl_name(digestmod): -+ if isinstance(digestmod, str): -+ return digestmod.lower() -+ elif callable(digestmod): -+ digestmod = digestmod(b'') -+ -+ if not isinstance(digestmod, _hashlibopenssl.HASH): -+ raise TypeError( -+ 'Only OpenSSL hashlib hashes are accepted in FIPS mode.') -+ -+ return digestmod.name.lower().replace('_', '-') -+ -+class HMAC_openssl(_hmacopenssl.HMAC): -+ def __new__(cls, key, msg = None, digestmod = None): -+ if not isinstance(key, (bytes, bytearray)): -+ raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) -+ -+ name = _get_openssl_name(digestmod) -+ result = _hmacopenssl.HMAC.__new__(cls, key, digestmod=name) -+ if msg: -+ result.update(msg) -+ return result -+ -+ -+if _hashlib.get_fips_mode(): -+ HMAC = HMAC_openssl -+ -+ - def new(key, msg=None, digestmod=''): - """Create a new hashing object and return it. + 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 -diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py -index 6daf22ca06f..544ec7cb411 100644 ---- a/Lib/test/test_hmac.py -+++ b/Lib/test/test_hmac.py -@@ -322,6 +322,7 @@ class TestVectorsTestCase(unittest.TestCase): - def test_sha512_rfc4231(self): - self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128) + 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; -+ @unittest.skipIf(hashlib.get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.') - @hashlib_helper.requires_hashdigest('sha256') - def test_legacy_block_size_warnings(self): - class MockCrazyHash(object): -@@ -371,6 +372,14 @@ class ConstructorTestCase(unittest.TestCase): - except Exception: - self.fail("Standard constructor call raised exception.") ++ 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; -+ def test_normal_digestmod(self): -+ # Standard constructor call. -+ failed = 0 -+ try: -+ h = hmac.HMAC(b"key", digestmod='sha1') -+ except Exception: -+ self.fail("Standard constructor call raised exception.") ++ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); + - @hashlib_helper.requires_hashdigest('sha256') - def test_with_str_key(self): - # Pass a key of type str, which is an error, because it expects a key -@@ -446,6 +455,7 @@ class SanityTestCase(unittest.TestCase): + GET_BUFFER_VIEW_OR_ERROUT(data, &buf); - class CopyTestCase(unittest.TestCase): + 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 @@ + */ -+ @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode") - @hashlib_helper.requires_hashdigest('sha256') - def test_attributes(self): - # Testing if attributes are of same type. -@@ -458,6 +468,8 @@ class CopyTestCase(unittest.TestCase): - self.assertEqual(type(h1._outer), type(h2._outer), - "Types of outer don't match.") + #include "Python.h" ++#include "../hashlib.h" -+ -+ @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode") - @hashlib_helper.requires_hashdigest('sha256') - def test_realcopy(self): - # Testing if the copy method created a real copy. -@@ -473,6 +485,7 @@ class CopyTestCase(unittest.TestCase): - self.assertEqual(h1._outer, h1.outer) - self.assertEqual(h1._digest_cons, h1.digest_cons) + #include "impl/blake2.h" -+ @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode") - @hashlib_helper.requires_hashdigest('sha256') - def test_properties(self): - # deprecated properties -@@ -481,6 +494,21 @@ class CopyTestCase(unittest.TestCase): - self.assertEqual(h1._outer, h1.outer) - self.assertEqual(h1._digest_cons, h1.digest_cons) - -+ def test_realcopy_by_digest(self): -+ # Testing if the copy method created a real copy. -+ h1 = hmac.HMAC(b"key", digestmod="sha1") -+ h2 = h1.copy() -+ # Using id() in case somebody has overridden __eq__/__ne__. -+ self.assertTrue(id(h1) != id(h2), "No real copy of the HMAC instance.") -+ old_digest = h1.digest() -+ assert h1.digest() == h2.digest() -+ h1.update(b'hi') -+ assert h1.digest() != h2.digest() -+ assert h2.digest() == old_digest -+ new_digest = h1.digest() -+ h2.update(b'hi') -+ assert h1.digest() == h2.digest() == new_digest -+ - @hashlib_helper.requires_hashdigest('sha256') - def test_equality(self): - # Testing if the copy has the same digests. -diff --git a/Modules/_hmacopenssl.c b/Modules/_hmacopenssl.c -new file mode 100644 -index 00000000000..c31d233fbe4 ---- /dev/null -+++ b/Modules/_hmacopenssl.c -@@ -0,0 +1,459 @@ -+/* Module that wraps all OpenSSL MHAC algorithm */ -+ -+/* Copyright (C) 2019 Red Hat, Inc. Red Hat, Inc. and/or its affiliates -+ * -+ * Based on _hashopenssl.c, which is: -+ * Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org) -+ * Licensed to PSF under a Contributor Agreement. -+ * -+ * Derived from a skeleton of shamodule.c containing work performed by: -+ * -+ * Andrew Kuchling (amk@amk.ca) -+ * Greg Stein (gstein@lyra.org) -+ * -+ */ -+ -+#define PY_SSIZE_T_CLEAN -+ -+#include "Python.h" -+#include "structmember.h" -+#include "hashlib.h" -+#include "pystrhex.h" -+#include "_hashopenssl.h" -+ -+ -+ -+typedef struct hmacopenssl_state { -+ PyTypeObject *HmacType; -+} hmacopenssl_state; -+ -+#include -+ -+typedef struct { -+ PyObject_HEAD -+ PyObject *name; /* name of the hash algorithm */ -+ HMAC_CTX *ctx; /* OpenSSL hmac context */ -+ PyThread_type_lock lock; /* HMAC context lock */ -+} HmacObject; -+ -+#include "clinic/_hmacopenssl.c.h" -+/*[clinic input] -+module _hmacopenssl -+class _hmacopenssl.HMAC "HmacObject *" "((hmacopenssl_state *)PyModule_GetState(module))->HmacType" -+[clinic start generated code]*/ -+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9fe07a087adc2cf9]*/ -+ -+ -+static PyObject * -+Hmac_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds) -+{ -+ static char *kwarg_names[] = {"key", "digestmod", NULL}; -+ Py_buffer key = {NULL, NULL}; -+ char *digestmod = NULL; -+ -+ int ret = PyArg_ParseTupleAndKeywords( -+ args, kwds, "y*|$s:_hmacopenssl.HMAC", kwarg_names, -+ &key, &digestmod); -+ if (ret == 0) { -+ return NULL; -+ } -+ -+ if (digestmod == NULL) { -+ PyErr_SetString(PyExc_ValueError, "digestmod must be specified"); -+ return NULL; -+ } -+ -+ /* name must be lowercase */ -+ for (int i=0; digestmod[i]; i++) { -+ if ( -+ ((digestmod[i] < 'a') || (digestmod[i] > 'z')) -+ && ((digestmod[i] < '0') || (digestmod[i] > '9')) -+ && digestmod[i] != '-' -+ ) { -+ PyErr_SetString(PyExc_ValueError, "digestmod must be lowercase"); -+ return NULL; -+ } -+ } -+ -+ const EVP_MD *digest = EVP_get_digestbyname(digestmod); -+ if (!digest) { -+ PyErr_SetString(PyExc_ValueError, "unknown hash function"); -+ return NULL; -+ } -+ -+ PyObject *name = NULL; -+ HMAC_CTX *ctx = NULL; -+ HmacObject *retval = NULL; -+ -+ name = PyUnicode_FromFormat("hmac-%s", digestmod); -+ if (name == NULL) { -+ goto error; -+ } +@@ -57,6 +58,8 @@ PyInit__blake2(void) + PyObject *m; + PyObject *d; + ++ FAIL_RETURN_IN_FIPS_MODE(PyExc_ImportError, "blake2"); + -+ ctx = HMAC_CTX_new(); -+ if (ctx == NULL) { -+ _setException(PyExc_ValueError); -+ goto error; -+ } + 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"); + -+ int r = HMAC_Init_ex( -+ ctx, -+ (const char*)key.buf, -+ key.len, -+ digest, -+ NULL /*impl*/); -+ if (r == 0) { -+ _setException(PyExc_ValueError); -+ goto error; -+ } + 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"); + -+ PyBuffer_Release(&key); -+ key.buf = NULL; + 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 + -+ retval = (HmacObject *)subtype->tp_alloc(subtype, 0); -+ if (retval == NULL) { -+ goto error; + /* + * 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; + } -+ -+ retval->name = name; -+ retval->ctx = ctx; -+ retval->lock = NULL; -+ -+ return (PyObject*)retval; -+ -+error: -+ if (ctx) HMAC_CTX_free(ctx); -+ if (name) Py_DECREF(name); -+ if (retval) PyObject_Del(name); -+ if (key.buf) PyBuffer_Release(&key); -+ return NULL; ++ return 0; +} + -+/*[clinic input] -+_hmacopenssl.HMAC.copy -+ -+Return a copy (“clone”) of the HMAC object. -+[clinic start generated code]*/ ++#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 0bec170..479f4b5 100644 +--- a/setup.py ++++ b/setup.py +@@ -2315,7 +2315,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() + +@@ -2335,16 +2335,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( +@@ -2354,12 +2352,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, ++ } + -+static PyObject * -+_hmacopenssl_HMAC_copy_impl(HmacObject *self) -+/*[clinic end generated code: output=fe5ee41faf30dcf0 input=f5ed20feec42d8d0]*/ -+{ -+ HmacObject *retval; ++ def detect_openssl_hashlib(self): + -+ HMAC_CTX *ctx = HMAC_CTX_new(); -+ if (ctx == NULL) { -+ return _setException(PyExc_ValueError); -+ } ++ config_vars = sysconfig.get_config_vars() + -+ int r = HMAC_CTX_copy(ctx, self->ctx); -+ if (r == 0) { -+ HMAC_CTX_free(ctx); -+ return _setException(PyExc_ValueError); -+ } -+ -+ retval = (HmacObject *)Py_TYPE(self)->tp_alloc(Py_TYPE(self), 0); -+ if (retval == NULL) { -+ HMAC_CTX_free(ctx); -+ return NULL; -+ } -+ retval->ctx = ctx; -+ Py_INCREF(self->name); -+ retval->name = self->name; -+ -+ retval->lock = NULL; -+ -+ return (PyObject *)retval; -+} -+ -+static void -+_hmac_dealloc(HmacObject *self) -+{ -+ if (self->lock != NULL) { -+ PyThread_free_lock(self->lock); -+ } -+ HMAC_CTX_free(self->ctx); -+ Py_CLEAR(self->name); -+ Py_TYPE(self)->tp_free(self); -+} -+ -+static PyObject * -+_hmac_repr(HmacObject *self) -+{ -+ return PyUnicode_FromFormat("<%U HMAC object @ %p>", self->name, self); -+} -+ -+/*[clinic input] -+_hmacopenssl.HMAC.update -+ -+ msg: Py_buffer -+ -+Update the HMAC object with msg. -+[clinic start generated code]*/ -+ -+static PyObject * -+_hmacopenssl_HMAC_update_impl(HmacObject *self, Py_buffer *msg) -+/*[clinic end generated code: output=0efeee663a98cee5 input=0683d64f35808cb9]*/ -+{ -+ if (self->lock == NULL && msg->len >= HASHLIB_GIL_MINSIZE) { -+ self->lock = PyThread_allocate_lock(); -+ /* fail? lock = NULL and we fail over to non-threaded code. */ -+ } -+ -+ int r; -+ -+ if (self->lock != NULL) { -+ Py_BEGIN_ALLOW_THREADS -+ PyThread_acquire_lock(self->lock, 1); -+ r = HMAC_Update(self->ctx, (const unsigned char*)msg->buf, msg->len); -+ PyThread_release_lock(self->lock); -+ Py_END_ALLOW_THREADS -+ } else { -+ r = HMAC_Update(self->ctx, (const unsigned char*)msg->buf, msg->len); -+ } -+ -+ if (r == 0) { -+ _setException(PyExc_ValueError); -+ return NULL; -+ } -+ Py_RETURN_NONE; -+} -+ -+static unsigned int -+_digest_size(HmacObject *self) -+{ -+ const EVP_MD *md = HMAC_CTX_get_md(self->ctx); -+ if (md == NULL) { -+ _setException(PyExc_ValueError); -+ return 0; -+ } -+ return EVP_MD_size(md); -+} -+ -+static int -+_digest(HmacObject *self, unsigned char *buf, unsigned int len) -+{ -+ HMAC_CTX *temp_ctx = HMAC_CTX_new(); -+ if (temp_ctx == NULL) { -+ PyErr_NoMemory(); -+ return 0; -+ } -+ int r = HMAC_CTX_copy(temp_ctx, self->ctx); -+ if (r == 0) { -+ _setException(PyExc_ValueError); -+ return 0; -+ } -+ r = HMAC_Final(temp_ctx, buf, &len); -+ HMAC_CTX_free(temp_ctx); -+ if (r == 0) { -+ _setException(PyExc_ValueError); -+ return 0; -+ } -+ return 1; -+} -+ -+/*[clinic input] -+_hmacopenssl.HMAC.digest -+ -+Return the digest of the bytes passed to the update() method so far. -+[clinic start generated code]*/ -+ -+static PyObject * -+_hmacopenssl_HMAC_digest_impl(HmacObject *self) -+/*[clinic end generated code: output=3aa6dbfc46ec4957 input=bf769a10b1d9edd9]*/ -+{ -+ unsigned int digest_size = _digest_size(self); -+ if (digest_size == 0) { -+ return _setException(PyExc_ValueError); -+ } -+ unsigned char buf[digest_size]; /* FIXME: C99 feature */ -+ int r = _digest(self, buf, digest_size); -+ if (r == 0) { -+ return NULL; -+ } -+ return PyBytes_FromStringAndSize((const char *)buf, digest_size); -+} -+ -+/*[clinic input] -+_hmacopenssl.HMAC.hexdigest -+ -+Return hexadecimal digest of the bytes passed to the update() method so far. -+ -+This may be used to exchange the value safely in email or other non-binary -+environments. -+[clinic start generated code]*/ -+ -+static PyObject * -+_hmacopenssl_HMAC_hexdigest_impl(HmacObject *self) -+/*[clinic end generated code: output=630f6fa89f9f1e48 input=b8e60ec8b811c4cd]*/ -+{ -+ unsigned int digest_size = _digest_size(self); -+ if (digest_size == 0) { -+ return _setException(PyExc_ValueError); -+ } -+ unsigned char buf[digest_size]; /* FIXME: C99 feature */ -+ int r = _digest(self, buf, digest_size); -+ if (r == 0) { -+ return NULL; -+ } -+ return _Py_strhex((const char *)buf, digest_size); -+} -+ -+ -+ -+static PyObject * -+_hmacopenssl_get_digest_size(HmacObject *self, void *closure) -+{ -+ unsigned int digest_size = _digest_size(self); -+ if (digest_size == 0) { -+ return _setException(PyExc_ValueError); -+ } -+ return PyLong_FromLong(digest_size); -+} -+ -+static PyObject * -+_hmacopenssl_get_block_size(HmacObject *self, void *closure) -+{ -+ const EVP_MD *md = HMAC_CTX_get_md(self->ctx); -+ if (md == NULL) { -+ return _setException(PyExc_ValueError); -+ } -+ return PyLong_FromLong(EVP_MD_block_size(md)); -+} -+ -+static PyMethodDef Hmac_methods[] = { -+ _HMACOPENSSL_HMAC_UPDATE_METHODDEF -+ _HMACOPENSSL_HMAC_DIGEST_METHODDEF -+ _HMACOPENSSL_HMAC_HEXDIGEST_METHODDEF -+ _HMACOPENSSL_HMAC_COPY_METHODDEF -+ {NULL, NULL} /* sentinel */ -+}; -+ -+static PyGetSetDef Hmac_getset[] = { -+ {"digest_size", (getter)_hmacopenssl_get_digest_size, NULL, NULL, NULL}, -+ {"block_size", (getter)_hmacopenssl_get_block_size, NULL, NULL, NULL}, -+ {NULL} /* Sentinel */ -+}; -+ -+static PyMemberDef Hmac_members[] = { -+ {"name", T_OBJECT, offsetof(HmacObject, name), READONLY, PyDoc_STR("HMAC name")}, -+ {NULL} /* Sentinel */ -+}; -+ -+PyDoc_STRVAR(hmactype_doc, -+"The object used to calculate HMAC of a message.\n\ -+\n\ -+Methods:\n\ -+\n\ -+update() -- updates the current digest with an additional string\n\ -+digest() -- return the current digest value\n\ -+hexdigest() -- return the current digest as a string of hexadecimal digits\n\ -+copy() -- return a copy of the current hash object\n\ -+\n\ -+Attributes:\n\ -+\n\ -+name -- the name, including the hash algorithm used by this object\n\ -+digest_size -- number of bytes in digest() output\n"); -+ -+static PyType_Slot HmacType_slots[] = { -+ {Py_tp_doc, (char*)hmactype_doc}, -+ {Py_tp_repr, (reprfunc)_hmac_repr}, -+ {Py_tp_dealloc,(destructor)_hmac_dealloc}, -+ {Py_tp_methods, Hmac_methods}, -+ {Py_tp_getset, Hmac_getset}, -+ {Py_tp_members, Hmac_members}, -+ {Py_tp_new, Hmac_new}, -+ {0, NULL} -+}; -+ -+PyType_Spec HmacType_spec = { -+ "_hmacopenssl.HMAC", /* name */ -+ sizeof(HmacObject), /* basicsize */ -+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, -+ .slots = HmacType_slots, -+}; -+ -+ -+static int -+hmacopenssl_traverse(PyObject *self, visitproc visit, void *arg) -+{ -+ hmacopenssl_state *state; -+ -+ state = PyModule_GetState(self); -+ -+ if (state) { -+ Py_VISIT(state->HmacType); -+ } -+ -+ return 0; -+} -+ -+static int -+hmacopenssl_clear(PyObject *self) -+{ -+ hmacopenssl_state *state; -+ -+ state = PyModule_GetState(self); -+ -+ if (state) { -+ Py_CLEAR(state->HmacType); -+ } -+ -+ return 0; -+} -+ -+ -+ -+/* Initialize this module. */ -+ -+static int -+hmacopenssl_exec(PyObject *m) { -+ /* TODO build EVP_functions openssl_* entries dynamically based -+ * on what hashes are supported rather than listing many -+ * and having some unsupported. Only init appropriate -+ * constants. */ -+ PyObject *temp = NULL; -+ hmacopenssl_state *state; -+ -+ temp = PyType_FromSpec(&HmacType_spec); -+ if (temp == NULL) { -+ goto fail; -+ } -+ -+ if (PyModule_AddObject(m, "HMAC", temp) == -1) { -+ goto fail; -+ } -+ -+ state = PyModule_GetState(m); -+ -+ state->HmacType = (PyTypeObject *)temp; -+ Py_INCREF(temp); -+ -+ -+ return 0; -+ -+fail: -+ Py_XDECREF(temp); -+ return -1; -+} -+ -+static PyModuleDef_Slot hmacopenssl_slots[] = { -+ {Py_mod_exec, hmacopenssl_exec}, -+ {0, NULL}, -+}; -+ -+static struct PyModuleDef _hmacopenssl_def = { -+ PyModuleDef_HEAD_INIT, /* m_base */ -+ .m_name = "_hmacopenssl", -+ .m_slots = hmacopenssl_slots, -+ .m_size = sizeof(hmacopenssl_state), -+ .m_traverse = hmacopenssl_traverse, -+ .m_clear = hmacopenssl_clear -+}; -+ -+ -+PyMODINIT_FUNC -+PyInit__hmacopenssl(void) -+{ -+ return PyModuleDef_Init(&_hmacopenssl_def); -+} -diff --git a/Modules/clinic/_hmacopenssl.c.h b/Modules/clinic/_hmacopenssl.c.h -new file mode 100644 -index 00000000000..a2af550838a ---- /dev/null -+++ b/Modules/clinic/_hmacopenssl.c.h -@@ -0,0 +1,104 @@ -+/*[clinic input] -+preserve -+[clinic start generated code]*/ -+ -+PyDoc_STRVAR(_hmacopenssl_HMAC_copy__doc__, -+"copy($self, /)\n" -+"--\n" -+"\n" -+"Return a copy (“clone”) of the HMAC object."); -+ -+#define _HMACOPENSSL_HMAC_COPY_METHODDEF \ -+ {"copy", (PyCFunction)_hmacopenssl_HMAC_copy, METH_NOARGS, _hmacopenssl_HMAC_copy__doc__}, -+ -+static PyObject * -+_hmacopenssl_HMAC_copy_impl(HmacObject *self); -+ -+static PyObject * -+_hmacopenssl_HMAC_copy(HmacObject *self, PyObject *Py_UNUSED(ignored)) -+{ -+ return _hmacopenssl_HMAC_copy_impl(self); -+} -+ -+PyDoc_STRVAR(_hmacopenssl_HMAC_update__doc__, -+"update($self, /, msg)\n" -+"--\n" -+"\n" -+"Update the HMAC object with msg."); -+ -+#define _HMACOPENSSL_HMAC_UPDATE_METHODDEF \ -+ {"update", (PyCFunction)(void(*)(void))_hmacopenssl_HMAC_update, METH_FASTCALL|METH_KEYWORDS, _hmacopenssl_HMAC_update__doc__}, -+ -+static PyObject * -+_hmacopenssl_HMAC_update_impl(HmacObject *self, Py_buffer *msg); -+ -+static PyObject * -+_hmacopenssl_HMAC_update(HmacObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) -+{ -+ PyObject *return_value = NULL; -+ static const char * const _keywords[] = {"msg", NULL}; -+ static _PyArg_Parser _parser = {NULL, _keywords, "update", 0}; -+ PyObject *argsbuf[1]; -+ Py_buffer msg = {NULL, NULL}; -+ -+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); -+ if (!args) { -+ goto exit; -+ } -+ if (PyObject_GetBuffer(args[0], &msg, PyBUF_SIMPLE) != 0) { -+ goto exit; -+ } -+ if (!PyBuffer_IsContiguous(&msg, 'C')) { -+ _PyArg_BadArgument("update", "argument 'msg'", "contiguous buffer", args[0]); -+ goto exit; -+ } -+ return_value = _hmacopenssl_HMAC_update_impl(self, &msg); -+ -+exit: -+ /* Cleanup for msg */ -+ if (msg.obj) { -+ PyBuffer_Release(&msg); -+ } -+ -+ return return_value; -+} -+ -+PyDoc_STRVAR(_hmacopenssl_HMAC_digest__doc__, -+"digest($self, /)\n" -+"--\n" -+"\n" -+"Return the digest of the bytes passed to the update() method so far."); -+ -+#define _HMACOPENSSL_HMAC_DIGEST_METHODDEF \ -+ {"digest", (PyCFunction)_hmacopenssl_HMAC_digest, METH_NOARGS, _hmacopenssl_HMAC_digest__doc__}, -+ -+static PyObject * -+_hmacopenssl_HMAC_digest_impl(HmacObject *self); -+ -+static PyObject * -+_hmacopenssl_HMAC_digest(HmacObject *self, PyObject *Py_UNUSED(ignored)) -+{ -+ return _hmacopenssl_HMAC_digest_impl(self); -+} -+ -+PyDoc_STRVAR(_hmacopenssl_HMAC_hexdigest__doc__, -+"hexdigest($self, /)\n" -+"--\n" -+"\n" -+"Return hexadecimal digest of the bytes passed to the update() method so far.\n" -+"\n" -+"This may be used to exchange the value safely in email or other non-binary\n" -+"environments."); -+ -+#define _HMACOPENSSL_HMAC_HEXDIGEST_METHODDEF \ -+ {"hexdigest", (PyCFunction)_hmacopenssl_HMAC_hexdigest, METH_NOARGS, _hmacopenssl_HMAC_hexdigest__doc__}, -+ -+static PyObject * -+_hmacopenssl_HMAC_hexdigest_impl(HmacObject *self); -+ -+static PyObject * -+_hmacopenssl_HMAC_hexdigest(HmacObject *self, PyObject *Py_UNUSED(ignored)) -+{ -+ return _hmacopenssl_HMAC_hexdigest_impl(self); -+} -+/*[clinic end generated code: output=e0c910f3c9ed523e input=a9049054013a1b77]*/ -diff --git a/setup.py b/setup.py -index 89edbb627fa..5c2cbd665af 100644 ---- a/setup.py -+++ b/setup.py -@@ -2371,6 +2371,10 @@ class PyBuildExt(build_ext): + 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', +@@ -2372,9 +2378,7 @@ class PyBuildExt(build_ext): + + self.add(Extension('_hashlib', ['_hashopenssl.c'], depends=['hashlib.h'], - **self.detect_openssl_args()) ) +- include_dirs=openssl_includes, +- library_dirs=openssl_libdirs, +- libraries=openssl_libs)) ++ **self.detect_openssl_args()) ) -+ self.add(Extension('_hmacopenssl', ['_hmacopenssl.c'], -+ depends = ['hashlib.h'], -+ **self.detect_openssl_args()) ) -+ def detect_hash_builtins(self): # By default we always compile these even when OpenSSL is available - # (issue #14693). It's harmless and the object code is tiny +@@ -2431,6 +2435,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.26.2 +2.37.2 -From a6d7c4268a6e305b1178b633e59dde7b5c8a1069 Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Thu, 1 Aug 2019 17:57:05 +0200 -Subject: [PATCH 06/13] Use a stronger hash in multiprocessing handshake +From 9bc3d493a3508fb82df7d24cc62315c072d9eca8 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 -Adapted from patch by David Malcolm, -https://bugs.python.org/issue17258 --- - Lib/multiprocessing/connection.py | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) + Lib/hashlib.py | 69 +++------------------------------------- + Lib/test/test_hashlib.py | 23 +++++++++++++- + 2 files changed, 27 insertions(+), 65 deletions(-) -diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py -index 510e4b5aba4..b68f2fb837a 100644 ---- a/Lib/multiprocessing/connection.py -+++ b/Lib/multiprocessing/connection.py -@@ -42,6 +42,10 @@ BUFSIZE = 8192 - # A very generous timeout when it comes to local connections... - CONNECTION_TIMEOUT = 20. +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') -+# The hmac module implicitly defaults to using MD5. -+# Support using a stronger algorithm for the challenge/response code: -+HMAC_DIGEST_NAME='sha256' +- + __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 fa4a8d7..ec6c883 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') + - _mmap_counter = itertools.count() ++ @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') +@@ -1061,6 +1081,7 @@ class KDFTests(unittest.TestCase): + iterations=1, dklen=None) + self.assertEqual(out, self.pbkdf2_results['sha1'][0][0]) - default_family = 'AF_INET' -@@ -741,7 +745,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) -@@ -757,7 +761,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: ++ @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.26.2 +2.37.2 -From 86868ca46c47112f771d54a54ee89e2d6c00f56f Mon Sep 17 00:00:00 2001 +From 331c0d39cbc9c4df266c375bae8c1a0d27dd78d9 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Wed, 31 Jul 2019 15:43:43 +0200 -Subject: [PATCH 07/13] Add initial tests for various hashes under FIPS mode +Subject: [PATCH 08/10] Test equivalence of hashes for the various digests with + usedforsecurity=True/False --- - Lib/test/test_fips.py | 31 +++++++++++++++++++++++++++++++ - 1 file changed, 31 insertions(+) + 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 00000000000..fe4ea72296e +index 0000000..1f99dd7 --- /dev/null +++ b/Lib/test/test_fips.py -@@ -0,0 +1,31 @@ +@@ -0,0 +1,24 @@ +import unittest -+import hmac, _hmacopenssl +import hashlib, _hashlib + + + +class HashlibFipsTests(unittest.TestCase): + -+ @unittest.skipUnless(hashlib.get_fips_mode(), "Test only when FIPS is enabled") ++ @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 + """ @@ -1751,147 +1650,16 @@ index 00000000000..fe4ea72296e + 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") ++ @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()) + -+ def test_hmac_digests(self): -+ self.assertEqual(_hmacopenssl.HMAC(b'My hovercraft is full of eels', digestmod='sha384').hexdigest(), -+ hmac.new(b'My hovercraft is full of eels', digestmod='sha384').hexdigest()) -+ -+ -+ + +if __name__ == "__main__": + unittest.main() --- -2.26.2 - - -From 5badab85d3fc725b56a19658f1e9b16aeb0ed663 Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Mon, 5 Aug 2019 18:23:57 +0200 -Subject: [PATCH 08/13] Make hashlib tests pass in FIPS mode - ---- - Lib/test/test_hashlib.py | 27 ++++++++++++++++++++++++--- - 1 file changed, 24 insertions(+), 3 deletions(-) - -diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py -index a838bcee2a8..6f60ad4b8fb 100644 ---- a/Lib/test/test_hashlib.py -+++ b/Lib/test/test_hashlib.py -@@ -44,6 +44,12 @@ if builtin_hashes == default_builtin_hashes: - else: - builtin_hashlib = None - -+ -+if hashlib.get_fips_mode(): -+ FIPS_DISABLED = {'md5', 'MD5', 'blake2b', 'blake2s'} -+else: -+ FIPS_DISABLED = set() -+ - try: - from _hashlib import HASH, HASHXOF, openssl_md_meth_names - except ImportError: -@@ -106,6 +112,7 @@ class HashLibTestCase(unittest.TestCase): - # Issue #14693: fallback modules are always compiled under POSIX - _warn_on_extension_import = os.name == 'posix' or COMPILED_WITH_PYDEBUG - -+ - def _conditional_import_module(self, module_name): - """Import a module and return a reference to it or None on failure.""" - try: -@@ -113,6 +120,9 @@ 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 as error: -+ if not hashlib.get_fips_mode(): -+ raise - return None - - def __init__(self, *args, **kwargs): -@@ -211,7 +221,7 @@ class HashLibTestCase(unittest.TestCase): - c.hexdigest() - - def test_algorithms_guaranteed(self): -- self.assertEqual(hashlib.algorithms_guaranteed, -+ self.assertEqual(hashlib.algorithms_guaranteed - FIPS_DISABLED, - set(_algo for _algo in self.supported_hash_names - if _algo.islower())) - -@@ -250,6 +260,12 @@ class HashLibTestCase(unittest.TestCase): - def test_new_upper_to_lower(self): - self.assertEqual(hashlib.new("SHA256").name, "sha256") - -+ @unittest.skipUnless(hashlib.get_fips_mode(), "Builtin constructor only unavailable in FIPS mode") -+ def test_get_builtin_constructor_fips(self): -+ with self.assertRaises(AttributeError): -+ hashlib.__get_builtin_constructor -+ -+ @unittest.skipIf(hashlib.get_fips_mode(), "No builtin constructors in FIPS mode") - def test_get_builtin_constructor(self): - get_builtin_constructor = getattr(hashlib, - '__get_builtin_constructor') -@@ -380,7 +396,8 @@ class HashLibTestCase(unittest.TestCase): - self.assertRaises(TypeError, hash_object_constructor, 'spam') - - def test_no_unicode(self): -- self.check_no_unicode('md5') -+ if not hashlib.get_fips_mode(): -+ self.check_no_unicode('md5') - self.check_no_unicode('sha1') - self.check_no_unicode('sha224') - self.check_no_unicode('sha256') -@@ -421,7 +438,8 @@ class HashLibTestCase(unittest.TestCase): - self.assertIn(name.split("_")[0], repr(m)) - - def test_blocksize_name(self): -- self.check_blocksize_name('md5', 64, 16) -+ if not hashlib.get_fips_mode(): -+ self.check_blocksize_name('md5', 64, 16) - self.check_blocksize_name('sha1', 64, 20) - self.check_blocksize_name('sha224', 64, 28) - self.check_blocksize_name('sha256', 64, 32) -@@ -463,18 +481,21 @@ class HashLibTestCase(unittest.TestCase): - self.check_blocksize_name('blake2b', 128, 64) - self.check_blocksize_name('blake2s', 64, 32) - -+ @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode") - def test_case_md5_0(self): - self.check( - 'md5', b'', 'd41d8cd98f00b204e9800998ecf8427e', - usedforsecurity=False - ) - -+ @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode") - def test_case_md5_1(self): - self.check( - 'md5', b'abc', '900150983cd24fb0d6963f7d28e17f72', - usedforsecurity=False - ) - -+ @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode") - def test_case_md5_2(self): - self.check( - 'md5', --- -2.26.2 - - -From 0f7a3094bc4cf691ae0dd093567ceea149e14e8a Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Mon, 26 Aug 2019 19:09:39 +0200 -Subject: [PATCH 09/13] Test the usedforsecurity flag - ---- - Lib/test/test_hashlib.py | 66 +++++++++++++++++++++++++--------------- - 1 file changed, 42 insertions(+), 24 deletions(-) - diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py -index 6f60ad4b8fb..f306ba33b20 100644 +index ec6c883..0fd036f 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -20,6 +20,7 @@ import warnings @@ -1902,46 +1670,32 @@ index 6f60ad4b8fb..f306ba33b20 100644 # Were we compiled --with-pydebug or with #define Py_DEBUG? COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') -@@ -46,8 +47,10 @@ else: - - - if hashlib.get_fips_mode(): -- FIPS_DISABLED = {'md5', 'MD5', 'blake2b', 'blake2s'} -+ FIPS_UNAVAILABLE = {'blake2b', 'blake2s'} -+ FIPS_DISABLED = {'md5', 'MD5', *FIPS_UNAVAILABLE} - else: -+ FIPS_UNAVAILABLE = set() - FIPS_DISABLED = set() +@@ -55,6 +56,11 @@ except ImportError: + def get_fips_mode(): + return 0 ++if get_fips_mode(): ++ FIPS_DISABLED = {'md5'} ++else: ++ FIPS_DISABLED = set() ++ try: -@@ -98,6 +101,14 @@ def read_vectors(hash_name): + import _blake2 + except ImportError: +@@ -98,6 +104,11 @@ def read_vectors(hash_name): parts[0] = bytes.fromhex(parts[0]) yield parts -+def _is_openssl_constructor(constructor): -+ if getattr(constructor, '__name__', '').startswith('openssl_'): -+ return True ++def _is_blake2_constructor(constructor): + if isinstance(constructor, partial): -+ if constructor.func.__name__.startswith('openssl_'): -+ return True -+ return False ++ constructor = constructor.func ++ return getattr(constructor, '__name__', '').startswith('openssl_blake2') + class HashLibTestCase(unittest.TestCase): supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1', -@@ -128,8 +139,8 @@ class HashLibTestCase(unittest.TestCase): - def __init__(self, *args, **kwargs): - algorithms = set() - for algorithm in self.supported_hash_names: -- algorithms.add(algorithm.lower()) -- -+ if algorithm not in FIPS_UNAVAILABLE: -+ algorithms.add(algorithm.lower()) - _blake2 = self._conditional_import_module('_blake2') - if _blake2: - algorithms.update({'blake2b', 'blake2s'}) -@@ -138,15 +149,21 @@ class HashLibTestCase(unittest.TestCase): - for algorithm in algorithms: +@@ -142,15 +153,21 @@ class HashLibTestCase(unittest.TestCase): + continue self.constructors_to_test[algorithm] = set() + def _add_constructor(algorithm, constructor): @@ -1964,7 +1718,7 @@ index 6f60ad4b8fb..f306ba33b20 100644 _hashlib = self._conditional_import_module('_hashlib') self._hashlib = _hashlib -@@ -158,13 +175,7 @@ class HashLibTestCase(unittest.TestCase): +@@ -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: @@ -1978,86 +1732,34 @@ index 6f60ad4b8fb..f306ba33b20 100644 + _add_constructor(algorithm, constructor) def add_builtin_constructor(name): - constructor = getattr(hashlib, "__get_builtin_constructor")(name) -@@ -221,7 +232,7 @@ class HashLibTestCase(unittest.TestCase): - c.hexdigest() - - def test_algorithms_guaranteed(self): -- self.assertEqual(hashlib.algorithms_guaranteed - FIPS_DISABLED, -+ self.assertEqual(hashlib.algorithms_guaranteed, - set(_algo for _algo in self.supported_hash_names - if _algo.islower())) - -@@ -326,10 +337,9 @@ class HashLibTestCase(unittest.TestCase): + 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) -- self.assertEqual( -- h.name, -- hashlib.new(h.name, usedforsecurity=False).name -- ) -+ if h.name not in FIPS_DISABLED: ++ 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) - - def test_large_update(self): - aas = b'a' * 128 -@@ -371,9 +381,11 @@ class HashLibTestCase(unittest.TestCase): - self.assertGreaterEqual(len(constructors), 2) + 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: - if ( -- kwargs -- and hash_object_constructor.__name__.startswith('openssl_') -+ (kwargs.keys() - {'usedforsecurity'}) -+ and _is_openssl_constructor(hash_object_constructor) - ): -+ # Don't check openssl constructors with -+ # any extra keys (except usedforsecurity) - return - m = hash_object_constructor(data, **kwargs) - computed = m.hexdigest() if not shake else m.hexdigest(length) -@@ -481,21 +493,18 @@ class HashLibTestCase(unittest.TestCase): - self.check_blocksize_name('blake2b', 128, 64) - self.check_blocksize_name('blake2s', 64, 32) - -- @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode") - def test_case_md5_0(self): - self.check( - 'md5', b'', 'd41d8cd98f00b204e9800998ecf8427e', - usedforsecurity=False - ) -- @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode") - def test_case_md5_1(self): - self.check( - 'md5', b'abc', '900150983cd24fb0d6963f7d28e17f72', - usedforsecurity=False - ) + # 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 -- @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode") - def test_case_md5_2(self): - self.check( - 'md5', -@@ -507,12 +516,12 @@ class HashLibTestCase(unittest.TestCase): - @unittest.skipIf(sys.maxsize < _4G + 5, 'test cannot run on 32-bit systems') - @bigmemtest(size=_4G + 5, memuse=1, dry_run=False) - def test_case_md5_huge(self, size): -- self.check('md5', b'A'*size, 'c9af2dff37468ce5dfee8f2cfc0a9c6d') -+ self.check('md5', b'A'*size, 'c9af2dff37468ce5dfee8f2cfc0a9c6d', usedforsecurity=False) - - @unittest.skipIf(sys.maxsize < _4G - 1, 'test cannot run on 32-bit systems') - @bigmemtest(size=_4G - 1, memuse=1, dry_run=False) - def test_case_md5_uintmax(self, size): -- self.check('md5', b'A'*size, '28138d306ff1b8281f1a9067e1a1a2b3') -+ self.check('md5', b'A'*size, '28138d306ff1b8281f1a9067e1a1a2b3', usedforsecurity=False) - - # use the three examples from Federal Information Processing Standards - # Publication 180-1, Secure Hash Standard, 1995 April 17 -@@ -958,6 +967,15 @@ class HashLibTestCase(unittest.TestCase): + m = hash_object_constructor(data, **kwargs) +@@ -974,6 +989,15 @@ class HashLibTestCase(unittest.TestCase): ): HASHXOF() -+ @unittest.skipUnless(hashlib.get_fips_mode(), 'Needs FIPS mode.') ++ @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): @@ -2070,225 +1772,68 @@ index 6f60ad4b8fb..f306ba33b20 100644 class KDFTests(unittest.TestCase): -- -2.26.2 +2.37.2 -From 5feafbf68d297e3f4fcafe4cbeff97817c592c53 Mon Sep 17 00:00:00 2001 +From 1a3df28f95710925bc80018bcf22b7f37bbb1e17 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 26 Aug 2019 19:39:48 +0200 -Subject: [PATCH 10/13] Don't re-export get_fips_mode from hashlib +Subject: [PATCH 09/10] Guard against Python HMAC in FIPS mode -Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1745685 --- - Lib/hashlib.py | 22 +++++++++------------- - Lib/hmac.py | 6 +++--- - Lib/test/test_fips.py | 4 ++-- - Lib/test/test_hashlib.py | 16 +++++++++------- - Lib/test/test_hmac.py | 9 +++++---- - Lib/test/test_urllib2_localnet.py | 1 + - 6 files changed, 29 insertions(+), 29 deletions(-) + Lib/hmac.py | 13 +++++++++---- + Lib/test/test_hmac.py | 10 ++++++++++ + 2 files changed, 19 insertions(+), 4 deletions(-) -diff --git a/Lib/hashlib.py b/Lib/hashlib.py -index 1fd80c7d4fd..6121d251267 100644 ---- a/Lib/hashlib.py -+++ b/Lib/hashlib.py -@@ -53,6 +53,8 @@ More condensed: - - """ - -+import _hashlib -+ - # This tuple and __get_builtin_constructor() must be modified if a new - # always available algorithm is added. - __always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512', -@@ -77,13 +79,7 @@ __block_openssl_constructor = { - 'blake2b', 'blake2s', - } - --try: -- from _hashlib import get_fips_mode --except ImportError: -- def get_fips_mode(): -- return 0 -- --if not get_fips_mode(): -+if not _hashlib.get_fips_mode(): - __builtin_constructor_cache = {} - - def __get_builtin_constructor(name): -@@ -131,7 +127,7 @@ if not get_fips_mode(): - - - def __get_openssl_constructor(name): -- if not get_fips_mode(): -+ if not _hashlib.get_fips_mode(): - if name in __block_openssl_constructor: - # Prefer our builtin blake2 implementation. - return __get_builtin_constructor(name) -@@ -149,7 +145,7 @@ def __get_openssl_constructor(name): - return __get_builtin_constructor(name) - - --if not get_fips_mode(): -+if not _hashlib.get_fips_mode(): - def __py_new(name, data=b'', **kwargs): - """new(name, data=b'', **kwargs) - Return a new hashing object using the - named algorithm; optionally initialized with data (which must be -@@ -162,7 +158,7 @@ def __hash_new(name, data=b'', **kwargs): - """new(name, data=b'') - Return a new hashing object using the named algorithm; - optionally initialized with data (which must be a bytes-like object). - """ -- if not get_fips_mode(): -+ if not _hashlib.get_fips_mode(): - if name in __block_openssl_constructor: - # Prefer our builtin blake2 implementation. - return __get_builtin_constructor(name)(data, **kwargs) -@@ -173,7 +169,7 @@ def __hash_new(name, data=b'', **kwargs): - # hash, try using our builtin implementations. - # This allows for SHA224/256 and SHA384/512 support even though - # the OpenSSL library prior to 0.9.8 doesn't provide them. -- if get_fips_mode(): -+ if _hashlib.get_fips_mode(): - raise - return __get_builtin_constructor(name)(data) - -@@ -185,7 +181,7 @@ try: - algorithms_available = algorithms_available.union( - _hashlib.openssl_md_meth_names) - except ImportError: -- if get_fips_mode: -+ if _hashlib.get_fips_mode: - raise - new = __py_new - __get_hash = __get_builtin_constructor -@@ -214,5 +210,5 @@ for __func_name in __always_supported: - # Cleanup locals() - del __always_supported, __func_name, __get_hash - del __hash_new, __get_openssl_constructor --if not get_fips_mode(): -+if not _hashlib.get_fips_mode(): - del __py_new diff --git a/Lib/hmac.py b/Lib/hmac.py -index 482e443bfe4..ff466322d7b 100644 +index 8b4f920..20ef96c 100644 --- a/Lib/hmac.py +++ b/Lib/hmac.py -@@ -50,7 +50,7 @@ class HMAC: - msg argument. Passing it as a keyword argument is - recommended, though not required for legacy API reasons. - """ -- if _hashlib.get_fips_mode(): -+ if _hashlibopenssl.get_fips_mode(): - raise ValueError( - 'This class is not available in FIPS mode. ' - + 'Use hmac.new().' -@@ -117,7 +117,7 @@ class HMAC: - - def update(self, msg): - """Feed data from msg into this hashing object.""" -- if _hashlib.get_fips_mode(): -+ if _hashlibopenssl.get_fips_mode(): - raise ValueError('hmac.HMAC is not available in FIPS mode') - self._inner.update(msg) +@@ -16,8 +16,9 @@ else: -@@ -183,7 +183,7 @@ class HMAC_openssl(_hmacopenssl.HMAC): - return result - - --if _hashlib.get_fips_mode(): -+if _hashlibopenssl.get_fips_mode(): - HMAC = HMAC_openssl - - -diff --git a/Lib/test/test_fips.py b/Lib/test/test_fips.py -index fe4ea72296e..6b50f8b45d4 100644 ---- a/Lib/test/test_fips.py -+++ b/Lib/test/test_fips.py -@@ -6,7 +6,7 @@ import hashlib, _hashlib + import hashlib as _hashlib - class HashlibFipsTests(unittest.TestCase): +-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)) -- @unittest.skipUnless(hashlib.get_fips_mode(), "Test only when FIPS is enabled") -+ @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 + # 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. """ -@@ -15,7 +15,7 @@ class HashlibFipsTests(unittest.TestCase): - 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") -+ @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()) -diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py -index f306ba33b20..03cfb6b2fb4 100644 ---- a/Lib/test/test_hashlib.py -+++ b/Lib/test/test_hashlib.py -@@ -46,7 +46,9 @@ else: - builtin_hashlib = None - - --if hashlib.get_fips_mode(): -+from _hashlib import get_fips_mode as _get_fips_mode -+ -+if _get_fips_mode(): - FIPS_UNAVAILABLE = {'blake2b', 'blake2s'} - FIPS_DISABLED = {'md5', 'MD5', *FIPS_UNAVAILABLE} - else: -@@ -132,7 +134,7 @@ class HashLibTestCase(unittest.TestCase): - 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 as error: -- if not hashlib.get_fips_mode(): -+ if not _get_fips_mode(): - raise - return None - -@@ -271,12 +273,12 @@ class HashLibTestCase(unittest.TestCase): - def test_new_upper_to_lower(self): - self.assertEqual(hashlib.new("SHA256").name, "sha256") - -- @unittest.skipUnless(hashlib.get_fips_mode(), "Builtin constructor only unavailable in FIPS mode") -+ @unittest.skipUnless(_get_fips_mode(), "Builtin constructor only unavailable in FIPS mode") - def test_get_builtin_constructor_fips(self): - with self.assertRaises(AttributeError): - hashlib.__get_builtin_constructor - -- @unittest.skipIf(hashlib.get_fips_mode(), "No builtin constructors in FIPS mode") -+ @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') -@@ -408,7 +410,7 @@ class HashLibTestCase(unittest.TestCase): - self.assertRaises(TypeError, hash_object_constructor, 'spam') - - def test_no_unicode(self): -- if not hashlib.get_fips_mode(): -+ if not _get_fips_mode(): - self.check_no_unicode('md5') - self.check_no_unicode('sha1') - self.check_no_unicode('sha224') -@@ -450,7 +452,7 @@ class HashLibTestCase(unittest.TestCase): - self.assertIn(name.split("_")[0], repr(m)) - - def test_blocksize_name(self): -- if not hashlib.get_fips_mode(): -+ if not _get_fips_mode(): - self.check_blocksize_name('md5', 64, 16) - self.check_blocksize_name('sha1', 64, 20) - self.check_blocksize_name('sha224', 64, 28) -@@ -967,7 +969,7 @@ class HashLibTestCase(unittest.TestCase): - ): - HASHXOF() +- + if not isinstance(key, (bytes, bytearray)): + raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) -- @unittest.skipUnless(hashlib.get_fips_mode(), 'Needs FIPS mode.') -+ @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): + 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 544ec7cb411..2d4484911c2 100644 +index adf52ad..41e6a14 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -5,6 +5,7 @@ import hashlib @@ -2299,275 +1844,335 @@ index 544ec7cb411..2d4484911c2 100644 from test.support import hashlib_helper -@@ -322,7 +323,7 @@ class TestVectorsTestCase(unittest.TestCase): +@@ -339,6 +340,7 @@ class TestVectorsTestCase(unittest.TestCase): def test_sha512_rfc4231(self): self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128) -- @unittest.skipIf(hashlib.get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.') -+ @unittest.skipIf(get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.') ++ #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): -@@ -455,7 +456,7 @@ class SanityTestCase(unittest.TestCase): +@@ -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(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode") -+ @unittest.skipIf(get_fips_mode(), "Internal attributes unavailable in FIPS mode") ++ @unittest.skipIf(get_fips_mode(), "_init_old unavailable in FIPS mode") @hashlib_helper.requires_hashdigest('sha256') - def test_attributes(self): + def test_attributes_old(self): # Testing if attributes are of same type. -@@ -469,7 +470,7 @@ class CopyTestCase(unittest.TestCase): +@@ -483,6 +492,7 @@ class CopyTestCase(unittest.TestCase): + self.assertEqual(type(h1._outer), type(h2._outer), "Types of outer don't match.") - -- @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode") -+ @unittest.skipIf(get_fips_mode(), "Internal attributes unavailable in FIPS mode") ++ @unittest.skipIf(get_fips_mode(), "_init_old unavailable in FIPS mode") @hashlib_helper.requires_hashdigest('sha256') - def test_realcopy(self): + def test_realcopy_old(self): # Testing if the copy method created a real copy. -@@ -485,7 +486,7 @@ class CopyTestCase(unittest.TestCase): - self.assertEqual(h1._outer, h1.outer) - self.assertEqual(h1._digest_cons, h1.digest_cons) - -- @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode") -+ @unittest.skipIf(get_fips_mode(), "Internal attributes unavailable in FIPS mode") - @hashlib_helper.requires_hashdigest('sha256') - def test_properties(self): - # deprecated properties -diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py -index ed426b05a71..faec6844f9a 100644 ---- a/Lib/test/test_urllib2_localnet.py -+++ b/Lib/test/test_urllib2_localnet.py -@@ -7,6 +7,7 @@ import http.server - import threading - import unittest - import hashlib -+from _hashlib import get_fips_mode - - from test import support - from test.support import hashlib_helper -- -2.26.2 +2.37.2 -From 3e314b647bc316dda3cef1f611fbac9170ed1030 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Wed, 20 Nov 2019 10:59:25 +0100 -Subject: [PATCH 11/13] Use FIPS compliant CSPRNG +From dded0e09dd3e51998a2aa54d2ae8464d73987e51 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 -Kernel's getrandom() source is not yet FIPS compliant. Use OpenSSL's -DRBG in FIPS mode and disable os.getrandom() function. +If FIPS mode is on, we can't use siphash-based HMAC +(_Py_KeyedHash), so: -Signed-off-by: Christian Heimes +- 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/test/test_os.py | 6 ++++++ - Makefile.pre.in | 2 +- - Modules/posixmodule.c | 8 +++++++ - Python/bootstrap_hash.c | 48 +++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 63 insertions(+), 1 deletion(-) + 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/test/test_os.py b/Lib/test/test_os.py -index bf1cb5f5112..d3b1d9c8969 100644 ---- a/Lib/test/test_os.py -+++ b/Lib/test/test_os.py -@@ -29,6 +29,7 @@ import types +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 6dc0813..b9d5f9a 100644 +--- a/Lib/test/support/__init__.py ++++ b/Lib/test/support/__init__.py +@@ -3296,6 +3296,20 @@ def clear_ignored_deprecations(*tokens: object) -> None: + 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 ++ ++ + @contextlib.contextmanager + def adjust_int_max_str_digits(max_digits): + """Temporarily change the integer string conversion length limit.""" +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 ab647d6..7d50f07 100644 +--- a/Lib/test/test_compileall.py ++++ b/Lib/test/test_compileall.py +@@ -758,14 +758,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 uuid import warnings -+from _hashlib import get_fips_mode - from test import support - from test.support import socket_helper - from platform import win32_is_iot -@@ -1661,6 +1662,11 @@ class GetRandomTests(unittest.TestCase): - raise unittest.SkipTest("getrandom() syscall fails with ENOSYS") - else: - raise -+ except ValueError as exc: -+ if get_fips_mode() and exc.args[0] == "getrandom is not FIPS compliant": -+ raise unittest.SkipTest("Skip in FIPS mode") -+ else: -+ raise - def test_getrandom_type(self): - data = os.getrandom(16) -diff --git a/Makefile.pre.in b/Makefile.pre.in -index f128444b985..3ea348a5461 100644 ---- a/Makefile.pre.in -+++ b/Makefile.pre.in -@@ -116,7 +116,7 @@ PY_STDMODULE_CFLAGS= $(PY_CFLAGS) $(PY_CFLAGS_NODIST) $(PY_CPPFLAGS) $(CFLAGSFOR - PY_BUILTIN_MODULE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE_BUILTIN - PY_CORE_CFLAGS= $(PY_STDMODULE_CFLAGS) -DPy_BUILD_CORE - # Linker flags used for building the interpreter object files --PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST) -+PY_CORE_LDFLAGS=$(PY_LDFLAGS) $(PY_LDFLAGS_NODIST) -lcrypto - # Strict or non-strict aliasing flags used to compile dtoa.c, see above - CFLAGS_ALIASING=@CFLAGS_ALIASING@ - -diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c -index 12f72f525f7..d244d264d8a 100644 ---- a/Modules/posixmodule.c -+++ b/Modules/posixmodule.c -@@ -495,6 +495,9 @@ extern char *ctermid_r(char *); - # define MODNAME "posix" - #endif ++from test import support + from test.support import make_legacy_pyc, unload -+/* for FIPS check in os.getrandom() */ -+#include -+ - #if defined(__sun) - /* Something to implement in autoconf, not present in autoconf 2.69 */ - # define HAVE_STRUCT_STAT_ST_FSTYPE 1 -@@ -14171,6 +14174,11 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags) - return posix_error(); - } + from test.test_py_compile import without_source_date_epoch +@@ -239,6 +240,7 @@ class SimpleTest(abc.LoaderTests): + loader.load_module('bad name') -+ if (FIPS_mode()) { -+ PyErr_SetString(PyExc_ValueError, "getrandom is not FIPS compliant"); -+ return NULL; -+ } -+ - bytes = PyBytes_FromStringAndSize(NULL, size); - if (bytes == NULL) { - PyErr_NoMemory(); -diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c -index a212f69870e..6333cd446dc 100644 ---- a/Python/bootstrap_hash.c -+++ b/Python/bootstrap_hash.c -@@ -429,6 +429,50 @@ dev_urandom_close(void) - } - #endif /* !MS_WINDOWS */ + @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): + ) -+#include -+#include <_hashopenssl.h> -+ -+#if (OPENSSL_VERSION_NUMBER < 0x10101000L) || defined(LIBRESSL_VERSION_NUMBER) -+# error "py_openssl_drbg_urandom requires OpenSSL 1.1.1 for fork safety" -+#endif -+ -+static int -+py_openssl_drbg_urandom(char *buffer, Py_ssize_t size, int raise) -+{ -+ int res; -+ static int init = 0; -+ -+ if (!init) { -+ init = 1; -+ res = OPENSSL_init_crypto(OPENSSL_INIT_ATFORK, NULL); -+ if (res == 0) { -+ if (raise) { -+ _setException(PyExc_RuntimeError); -+ } -+ return 0; -+ } -+ } -+ -+ if (size > INT_MAX) { -+ if (raise) { -+ PyErr_Format(PyExc_OverflowError, -+ "RAND_bytes() size is limited to 2GB."); -+ } -+ return -1; + @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 b7347a3..09ea990 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; + } -+ -+ res = RAND_bytes((unsigned char*)buffer, (int)size); -+ -+ if (res == 1) { -+ return 1; -+ } else { -+ if (raise) { -+ _setException(PyExc_RuntimeError); -+ } -+ return 0; ++ PyObject *fips_mode_obj = PyObject_CallMethod(_hashlib, "get_fips_mode", NULL); ++ Py_DECREF(_hashlib); ++ if (fips_mode_obj == NULL) { ++ return NULL; + } -+} -+ - - /* Fill buffer with pseudo-random bytes generated by a linear congruent - generator (LCG): -@@ -513,6 +557,10 @@ pyurandom(void *buffer, Py_ssize_t size, int blocking, int raise) - return 0; - } - -+ if (FIPS_mode()) { -+ return py_openssl_drbg_urandom(buffer, size, raise); ++ int fips_mode = PyObject_IsTrue(fips_mode_obj); ++ Py_DECREF(fips_mode_obj); ++ if (fips_mode < 0) { ++ return NULL; + } -+ - #ifdef MS_WINDOWS - return win32_urandom((unsigned char *)buffer, size, raise); - #else --- -2.26.2 - - -From 16b9a981697b94c348fdc0d73d2d12e12b1b4227 Mon Sep 17 00:00:00 2001 -From: Petr Viktorin -Date: Tue, 7 Apr 2020 15:16:45 +0200 -Subject: [PATCH 12/13] Pass kwargs (like usedforsecurity) through __hash_new - ---- - Lib/hashlib.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/Lib/hashlib.py b/Lib/hashlib.py -index 6121d251267..00794adffc1 100644 ---- a/Lib/hashlib.py -+++ b/Lib/hashlib.py -@@ -171,7 +171,7 @@ def __hash_new(name, data=b'', **kwargs): - # the OpenSSL library prior to 0.9.8 doesn't provide them. - if _hashlib.get_fips_mode(): - raise -- return __get_builtin_constructor(name)(data) -+ return __get_builtin_constructor(name)(data, **kwargs) - - - try: --- -2.26.2 - - -From 48fb6366a0fcb95c8565be35495b25a23dc03896 Mon Sep 17 00:00:00 2001 -From: Charalampos Stratakis -Date: Fri, 24 Apr 2020 19:57:16 +0200 -Subject: [PATCH 13/13] Skip the test_with_digestmod_no_default under FIPS - -Also add a new test for testing the error values of -the digestmod parameter misuse under FIPS mode. ---- - Lib/test/test_hmac.py | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py -index 2d4484911c2..e0a5b6a053b 100644 ---- a/Lib/test/test_hmac.py -+++ b/Lib/test/test_hmac.py -@@ -347,6 +347,7 @@ class TestVectorsTestCase(unittest.TestCase): - hmac.HMAC(b'a', b'b', digestmod=MockCrazyHash) - self.fail('Expected warning about small block_size') - -+ @unittest.skipIf(get_fips_mode(), "digestmod misuse raises different errors under FIPS mode") - def test_with_digestmod_no_default(self): - """The digestmod parameter is required as of Python 3.8.""" - with self.assertRaisesRegex(TypeError, r'required.*digestmod'): -@@ -358,6 +359,18 @@ class TestVectorsTestCase(unittest.TestCase): - with self.assertRaisesRegex(TypeError, r'required.*digestmod'): - hmac.HMAC(key, msg=data, digestmod='') - -+ @unittest.skipIf(not get_fips_mode(), "test is run only under FIPS mode") -+ def test_with_digestmod_no_default_under_fips(self): -+ """Test the error values of digestmod misuse under FIPS mode.""" -+ with self.assertRaises(TypeError): -+ key = b"\x0b" * 16 -+ data = b"Hi There" -+ hmac.HMAC(key, data, digestmod=None) -+ with self.assertRaises(ValueError): -+ hmac.new(key, data) -+ with self.assertRaises(ValueError): -+ hmac.HMAC(key, msg=data, digestmod='') -+ - - class ConstructorTestCase(unittest.TestCase): - ++ 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.26.2 +2.37.2 diff --git a/SOURCES/00378-support-expat-2-4-5.patch b/SOURCES/00378-support-expat-2-4-5.patch new file mode 100644 index 0000000..a69e111 --- /dev/null +++ b/SOURCES/00378-support-expat-2-4-5.patch @@ -0,0 +1,47 @@ +From db083095e3bdb93e4f8170d814664c482b1e94da Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Tue, 14 Jun 2022 06:38:43 +0200 +Subject: [PATCH] Fix test suite for Expat >= 2.4.5 + +--- + Lib/test/test_minidom.py | 17 +++++------------ + 1 file changed, 5 insertions(+), 12 deletions(-) + +diff --git a/Lib/test/test_minidom.py b/Lib/test/test_minidom.py +index 9762025..5f52ed1 100644 +--- a/Lib/test/test_minidom.py ++++ b/Lib/test/test_minidom.py +@@ -1149,14 +1149,10 @@ class MinidomTest(unittest.TestCase): + + # Verify that character decoding errors raise exceptions instead + # of crashing +- if pyexpat.version_info >= (2, 4, 5): +- self.assertRaises(ExpatError, parseString, +- b'') +- self.assertRaises(ExpatError, parseString, +- b'Comment \xe7a va ? Tr\xe8s bien ?') +- else: +- self.assertRaises(UnicodeDecodeError, parseString, +- b'Comment \xe7a va ? Tr\xe8s bien ?') ++ self.assertRaises(ExpatError, parseString, ++ b'') ++ self.assertRaises(ExpatError, parseString, ++ b'Comment \xe7a va ? Tr\xe8s bien ?') + + doc.unlink() + +@@ -1617,10 +1613,7 @@ class MinidomTest(unittest.TestCase): + self.confirm(doc2.namespaceURI == xml.dom.EMPTY_NAMESPACE) + + def testExceptionOnSpacesInXMLNSValue(self): +- if pyexpat.version_info >= (2, 4, 5): +- context = self.assertRaisesRegex(ExpatError, 'syntax error') +- else: +- context = self.assertRaisesRegex(ValueError, 'Unsupported syntax') ++ context = self.assertRaisesRegex(ExpatError, 'syntax error') + + with context: + parseString('') +-- +2.35.3 + diff --git a/SOURCES/00399-cve-2023-24329.patch b/SOURCES/00399-cve-2023-24329.patch new file mode 100644 index 0000000..6e01787 --- /dev/null +++ b/SOURCES/00399-cve-2023-24329.patch @@ -0,0 +1,229 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: "Miss Islington (bot)" + <31488909+miss-islington@users.noreply.github.com> +Date: Wed, 17 May 2023 14:41:25 -0700 +Subject: [PATCH] 00399: CVE-2023-24329 + +* gh-102153: Start stripping C0 control and space chars in `urlsplit` (GH-102508) + +`urllib.parse.urlsplit` has already been respecting the WHATWG spec a bit GH-25595. + +This adds more sanitizing to respect the "Remove any leading C0 control or space from input" [rule](https://url.spec.whatwg.org/GH-url-parsing:~:text=Remove%20any%20leading%20and%20trailing%20C0%20control%20or%20space%20from%20input.) in response to [CVE-2023-24329](https://nvd.nist.gov/vuln/detail/CVE-2023-24329). + +--------- + +(cherry picked from commit 2f630e1ce18ad2e07428296532a68b11dc66ad10) + +Co-authored-by: Illia Volochii +Co-authored-by: Gregory P. Smith [Google] +--- + Doc/library/urllib.parse.rst | 46 +++++++++++++- + Lib/test/test_urlparse.py | 61 ++++++++++++++++++- + Lib/urllib/parse.py | 12 ++++ + ...-03-07-20-59-17.gh-issue-102153.14CLSZ.rst | 3 + + 4 files changed, 119 insertions(+), 3 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst + +diff --git a/Doc/library/urllib.parse.rst b/Doc/library/urllib.parse.rst +index f0f8605128..c76b5879ea 100644 +--- a/Doc/library/urllib.parse.rst ++++ b/Doc/library/urllib.parse.rst +@@ -159,6 +159,10 @@ or on combining URL components into a URL string. + ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', + params='', query='', fragment='') + ++ .. warning:: ++ ++ :func:`urlparse` does not perform validation. See :ref:`URL parsing ++ security ` for details. + + .. versionchanged:: 3.2 + Added IPv6 URL parsing capabilities. +@@ -323,8 +327,14 @@ or on combining URL components into a URL string. + ``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is + decomposed before parsing, no error will be raised. + +- Following the `WHATWG spec`_ that updates RFC 3986, ASCII newline +- ``\n``, ``\r`` and tab ``\t`` characters are stripped from the URL. ++ Following some of the `WHATWG spec`_ that updates RFC 3986, leading C0 ++ control and space characters are stripped from the URL. ``\n``, ++ ``\r`` and tab ``\t`` characters are removed from the URL at any position. ++ ++ .. warning:: ++ ++ :func:`urlsplit` does not perform validation. See :ref:`URL parsing ++ security ` for details. + + .. versionchanged:: 3.6 + Out-of-range port numbers now raise :exc:`ValueError`, instead of +@@ -337,6 +347,9 @@ or on combining URL components into a URL string. + .. versionchanged:: 3.9.5 + ASCII newline and tab characters are stripped from the URL. + ++ .. versionchanged:: 3.9.17 ++ Leading WHATWG C0 control and space characters are stripped from the URL. ++ + .. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser + + .. function:: urlunsplit(parts) +@@ -413,6 +426,35 @@ or on combining URL components into a URL string. + or ``scheme://host/path``). If *url* is not a wrapped URL, it is returned + without changes. + ++.. _url-parsing-security: ++ ++URL parsing security ++-------------------- ++ ++The :func:`urlsplit` and :func:`urlparse` APIs do not perform **validation** of ++inputs. They may not raise errors on inputs that other applications consider ++invalid. They may also succeed on some inputs that might not be considered ++URLs elsewhere. Their purpose is for practical functionality rather than ++purity. ++ ++Instead of raising an exception on unusual input, they may instead return some ++component parts as empty strings. Or components may contain more than perhaps ++they should. ++ ++We recommend that users of these APIs where the values may be used anywhere ++with security implications code defensively. Do some verification within your ++code before trusting a returned component part. Does that ``scheme`` make ++sense? Is that a sensible ``path``? Is there anything strange about that ++``hostname``? etc. ++ ++What constitutes a URL is not universally well defined. Different applications ++have different needs and desired constraints. For instance the living `WHATWG ++spec`_ describes what user facing web clients such as a web browser require. ++While :rfc:`3986` is more general. These functions incorporate some aspects of ++both, but cannot be claimed compliant with either. The APIs and existing user ++code with expectations on specific behaviors predate both standards leading us ++to be very cautious about making API behavior changes. ++ + .. _parsing-ascii-encoded-bytes: + + Parsing ASCII Encoded Bytes +diff --git a/Lib/test/test_urlparse.py b/Lib/test/test_urlparse.py +index 31943f357f..574da5bd69 100644 +--- a/Lib/test/test_urlparse.py ++++ b/Lib/test/test_urlparse.py +@@ -649,6 +649,65 @@ class UrlParseTestCase(unittest.TestCase): + self.assertEqual(p.scheme, "http") + self.assertEqual(p.geturl(), "http://www.python.org/javascript:alert('msg')/?query=something#fragment") + ++ def test_urlsplit_strip_url(self): ++ noise = bytes(range(0, 0x20 + 1)) ++ base_url = "http://User:Pass@www.python.org:080/doc/?query=yes#frag" ++ ++ url = noise.decode("utf-8") + base_url ++ p = urllib.parse.urlsplit(url) ++ self.assertEqual(p.scheme, "http") ++ self.assertEqual(p.netloc, "User:Pass@www.python.org:080") ++ self.assertEqual(p.path, "/doc/") ++ self.assertEqual(p.query, "query=yes") ++ self.assertEqual(p.fragment, "frag") ++ self.assertEqual(p.username, "User") ++ self.assertEqual(p.password, "Pass") ++ self.assertEqual(p.hostname, "www.python.org") ++ self.assertEqual(p.port, 80) ++ self.assertEqual(p.geturl(), base_url) ++ ++ url = noise + base_url.encode("utf-8") ++ p = urllib.parse.urlsplit(url) ++ self.assertEqual(p.scheme, b"http") ++ self.assertEqual(p.netloc, b"User:Pass@www.python.org:080") ++ self.assertEqual(p.path, b"/doc/") ++ self.assertEqual(p.query, b"query=yes") ++ self.assertEqual(p.fragment, b"frag") ++ self.assertEqual(p.username, b"User") ++ self.assertEqual(p.password, b"Pass") ++ self.assertEqual(p.hostname, b"www.python.org") ++ self.assertEqual(p.port, 80) ++ self.assertEqual(p.geturl(), base_url.encode("utf-8")) ++ ++ # Test that trailing space is preserved as some applications rely on ++ # this within query strings. ++ query_spaces_url = "https://www.python.org:88/doc/?query= " ++ p = urllib.parse.urlsplit(noise.decode("utf-8") + query_spaces_url) ++ self.assertEqual(p.scheme, "https") ++ self.assertEqual(p.netloc, "www.python.org:88") ++ self.assertEqual(p.path, "/doc/") ++ self.assertEqual(p.query, "query= ") ++ self.assertEqual(p.port, 88) ++ self.assertEqual(p.geturl(), query_spaces_url) ++ ++ p = urllib.parse.urlsplit("www.pypi.org ") ++ # That "hostname" gets considered a "path" due to the ++ # trailing space and our existing logic... YUCK... ++ # and re-assembles via geturl aka unurlsplit into the original. ++ # django.core.validators.URLValidator (at least through v3.2) relies on ++ # this, for better or worse, to catch it in a ValidationError via its ++ # regular expressions. ++ # Here we test the basic round trip concept of such a trailing space. ++ self.assertEqual(urllib.parse.urlunsplit(p), "www.pypi.org ") ++ ++ # with scheme as cache-key ++ url = "//www.python.org/" ++ scheme = noise.decode("utf-8") + "https" + noise.decode("utf-8") ++ for _ in range(2): ++ p = urllib.parse.urlsplit(url, scheme=scheme) ++ self.assertEqual(p.scheme, "https") ++ self.assertEqual(p.geturl(), "https://www.python.org/") ++ + def test_attributes_bad_port(self): + """Check handling of invalid ports.""" + for bytes in (False, True): +@@ -656,7 +715,7 @@ class UrlParseTestCase(unittest.TestCase): + for port in ("foo", "1.5", "-1", "0x10"): + with self.subTest(bytes=bytes, parse=parse, port=port): + netloc = "www.example.net:" + port +- url = "http://" + netloc ++ url = "http://" + netloc + "/" + if bytes: + netloc = netloc.encode("ascii") + url = url.encode("ascii") +diff --git a/Lib/urllib/parse.py b/Lib/urllib/parse.py +index b7965fe3d2..5b7193f67c 100644 +--- a/Lib/urllib/parse.py ++++ b/Lib/urllib/parse.py +@@ -25,6 +25,10 @@ currently not entirely compliant with this RFC due to defacto + scenarios for parsing, and for backward compatibility purposes, some + parsing quirks from older RFCs are retained. The testcases in + test_urlparse.py provides a good indicator of parsing behavior. ++ ++The WHATWG URL Parser spec should also be considered. We are not compliant with ++it either due to existing user code API behavior expectations (Hyrum's Law). ++It serves as a useful guide when making changes. + """ + + import re +@@ -78,6 +82,10 @@ scheme_chars = ('abcdefghijklmnopqrstuvwxyz' + '0123456789' + '+-.') + ++# Leading and trailing C0 control and space to be stripped per WHATWG spec. ++# == "".join([chr(i) for i in range(0, 0x20 + 1)]) ++_WHATWG_C0_CONTROL_OR_SPACE = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ' ++ + # Unsafe bytes to be removed per WHATWG spec + _UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n'] + +@@ -456,6 +464,10 @@ def urlsplit(url, scheme='', allow_fragments=True): + """ + + url, scheme, _coerce_result = _coerce_args(url, scheme) ++ # Only lstrip url as some applications rely on preserving trailing space. ++ # (https://url.spec.whatwg.org/#concept-basic-url-parser would strip both) ++ url = url.lstrip(_WHATWG_C0_CONTROL_OR_SPACE) ++ scheme = scheme.strip(_WHATWG_C0_CONTROL_OR_SPACE) + + for b in _UNSAFE_URL_BYTES_TO_REMOVE: + url = url.replace(b, "") +diff --git a/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst b/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst +new file mode 100644 +index 0000000000..e57ac4ed3a +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2023-03-07-20-59-17.gh-issue-102153.14CLSZ.rst +@@ -0,0 +1,3 @@ ++:func:`urllib.parse.urlsplit` now strips leading C0 control and space ++characters following the specification for URLs defined by WHATWG in ++response to CVE-2023-24329. Patch by Illia Volochii. diff --git a/SOURCES/Python-3.9.16.tar.xz.asc b/SOURCES/Python-3.9.16.tar.xz.asc new file mode 100644 index 0000000..5a093c6 --- /dev/null +++ b/SOURCES/Python-3.9.16.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCgAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmOPjAQACgkQsmmV4xAl +BWjzjQ//TQ9AtAs3RwRfGfJigHl3TG5lfYYdzAZIwEtt6NUw8tVKriCBMSvsJDjD +rlFX64SPWaDlTggnatU88sj1y4AtGpf517GbYKwJ1oLQjcCSIs6WSxD7CZAfb4CL +257KMANkT/n46luovTraqhAyLXp8fVWIEoSt3+6RgNYshjv00V6+L0HoE6jkzBRV +si6KHDUCyIydOJEtAt79w5Ze/pFxJjIlGZ6WxyRVEy77cyQKh0g4dSdQ15HZAsfr +fvv8rOmd8VXwIMi4xaUaHMddQxNrydDldDpKR4L1Lay/nY3OvSLI1AMw0D7n/FVO +HxgYvxwkRqHPgbDIBLoHe7nsou0621ELS+j6M7cRoqAjsSfEOwpHOBw7k4+zOoa3 +4FHvru6TmT1p2iT6GSRllp/XspAzSelJeaFWA0Rs57MQ14gtXrw5hQHyZ1NgMzZi +TMpnj0tGHufQYn2ZQqGUIySvtH3S5eIZgZGdPETJ5k09mcRVEKcdujTbkrIcOYtC +GoPCw+3Qe7feVZLzElnsela9bDZi3uWfZh2kVyhZPAvxXJ0VNVCLvPlCKpr0R7t5 +JJ7jMpblsA05FT6ZanbqWNFZtCHMjlkK1259oST3BMbBSHTFgY/KGJEHQTkYU3M2 +U5OSn4za47qFBTVIXQsqkLGEBU/wrxtNmerJel8YW3ZIrkoTv2E= +=dXB5 +-----END PGP SIGNATURE----- diff --git a/SOURCES/Python-3.9.2.tar.xz.asc b/SOURCES/Python-3.9.2.tar.xz.asc deleted file mode 100644 index 23a63c9..0000000 --- a/SOURCES/Python-3.9.2.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCgAdFiEE4/8oOcBIslwITevpsmmV4xAlBWgFAmAvyCoACgkQsmmV4xAl -BWhP7g//XBDQxNrjEaLSBfGy8tGbNPqlrBAOWsuH02JzvRXnr2gBX2m8TfaUSAnq -8Kzafrpsfw0+7LFoPfrp+YwUO5k2WprovW9Iw+LoUM0d5DABL/gXKwVb0j9i8nRj -uaPLzX9SRnCQQPfYQW/5wRFIm+/aqz4fx93k3Gw0AfeYh9Ka1pUJOCxCvihS47+E -dUeoC6S8SUDrm5lPLj8t1uVVtp8W7GpGMwF5Zn31ThrlUA4V/dTMmqSUXCaAI9Ii -zXditd26EfySKSxps+VQgL7GB778XcIYxlnMYzoqd6SD/pCQgagpFP2nZ1zdZ0/g -qpwgeGE2SK++w8iiOs2Q59tisREU7PHNVtpdILhw9Me892mwxIjl8wDMTZHY8vvU -6OZRI9G8UktpkEcT9FeFgwna2T3T16rEVbrzpteeDLFgqUpt84yXD+pd5W/Oozaj -sfbd7lCFBcdzCQIKa+DGDuJKFPExu8oqGg7Zq25wxLvkNosmHXny9NylE1VIJ5ad -WHadwBeFSFCR7faplO8s+hO/BmT5PcEwIXrz/xVqwf28o/0im63llkE6WUCRW4MU -x/S5uWjB/HSDw2NHLRRc0bLabl30mMCf7J/EkVmm9dsIpmXhn6SLC9YCYjJtIjC7 -ChSQs+U8MgEnwk/un/DELIRUtu+rQZ1GkQnJ4tooaYJlYr/m7Ww= -=s/wm ------END PGP SIGNATURE----- diff --git a/SPECS/python39.spec b/SPECS/python39.spec index 27af78e..033c2b5 100644 --- a/SPECS/python39.spec +++ b/SPECS/python39.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}.2 +%global general_version %{pybasever}.16 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} -Release: 1%{?dist} +Release: 1%{?dist}.1 License: Python # Exclude i686 arch. Due to a modularity issue it's being added to the @@ -164,6 +164,13 @@ ExcludeArch: i686 # foo/__pycache__/bar.cpython-%%{pyshortver}.opt-2.pyc %global bytecode_suffixes .cpython-%{pyshortver}*.pyc +# libmpdec (mpdecimal package in Fedora) is tightly coupled with the +# decimal module. We keep it bundled as to avoid incompatibilities +# with the packaged version. +# The version information can be found at Modules/_decimal/libmpdec/mpdecimal.h +# defined as MPD_VERSION. +%global libmpdec_version 2.5.0 + # Python's configure script defines SOVERSION, and this is used in the Makefile # to determine INSTSONAME, the name of the libpython DSO: # LDLIBRARY='libpython$(VERSION).so' @@ -300,7 +307,7 @@ Patch1: 00001-rpath.patch # See https://bugzilla.redhat.com/show_bug.cgi?id=556092 Patch111: 00111-no-static-lib.patch -# 00189 # 7c07eec60735bd65bda7d8e821d34718497cba27 +# 00189 # 4242864a6a12f1f4cf9fd63a6699a73f35261aa3 # Instead of bundled wheels, use our RPM packaged wheels # # We keep them in /usr/share/python-wheels @@ -312,8 +319,8 @@ Patch189: 00189-use-rpm-wheels.patch # The versions are written in Lib/ensurepip/__init__.py, this patch removes them. # When the bundled setuptools/pip wheel is updated, the patch no longer applies cleanly. # In such cases, the patch needs to be amended and the versions updated here: -%global pip_version 20.2.3 -%global setuptools_version 49.2.1 +%global pip_version 21.2.3 +%global setuptools_version 57.4.0 # 00251 # 2eabd04356402d488060bc8fe316ad13fc8a3356 # Change user install location @@ -383,6 +390,43 @@ Patch329: 00329-fips.patch # a nightmare because it's basically a binary file. Patch353: 00353-architecture-names-upstream-downstream.patch +# 00378 # +# Support expat 2.4.5 +# +# Curly brackets were never allowed in namespace URIs +# according to RFC 3986, and so-called namespace-validating +# XML parsers have the right to reject them a invalid URIs. +# +# libexpat >=2.4.5 has become strcter in that regard due to +# related security issues; with ET.XML instantiating a +# namespace-aware parser under the hood, this test has no +# future in CPython. +# +# References: +# - https://datatracker.ietf.org/doc/html/rfc3968 +# - https://www.w3.org/TR/xml-names/ +# +# Also, test_minidom.py: Support Expat >=2.4.5 +# +# The patch has diverged from upstream as the python test +# suite was relying on checking the expat version, whereas +# in RHEL fixes get backported instead of rebasing packages. +# +# Upstream: https://bugs.python.org/issue46811 +Patch378: 00378-support-expat-2-4-5.patch + +# 00399 # c32eff86eb80f6a6bdcbf4b1b6535fbc627b51a2 +# CVE-2023-24329 +# +# * gh-102153: Start stripping C0 control and space chars in `urlsplit` (GH-102508) +# +# `urllib.parse.urlsplit` has already been respecting the WHATWG spec a bit GH-25595. +# +# This adds more sanitizing to respect the "Remove any leading C0 control or space from input" [rule](https://url.spec.whatwg.org/GH-url-parsing:~:text=Remove%%20any%%20leading%%20and%%20trailing%%20C0%%20control%%20or%%20space%%20from%%20input.) in response to [CVE-2023-24329](https://nvd.nist.gov/vuln/detail/CVE-2023-24329). +# +# --------- +Patch399: 00399-cve-2023-24329.patch + # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -401,10 +445,10 @@ Patch353: 00353-architecture-names-upstream-downstream.patch # Descriptions, and metadata for subpackages # ========================================== -# Runtime require alternatives -Requires: %{_sbindir}/alternatives -Requires(post): %{_sbindir}/alternatives -Requires(postun): %{_sbindir}/alternatives +# Require alternatives version that implements the --keep-foreign flag +Requires: alternatives >= 1.19.1-1 +Requires(post): alternatives >= 1.19.1-1 +Requires(postun): alternatives >= 1.19.1-1 # When the user tries to `yum install python`, yum will list this package among # the possible alternatives @@ -520,6 +564,11 @@ Requires: python3 == %{version}-%{release} Provides: python = %{version}-%{release} # This also save us an explicit conflict for older python3 builds +# Also provide the name of the Ubuntu package with the same function, +# to be nice to people who temporarily forgot which distro they're on. +# C.f. https://packages.ubuntu.com/hirsute/all/python-is-python3/filelist +Provides: python-is-python3 = %{version}-%{release} + %description -n python-unversioned-command This package contains /usr/bin/python - the "python" command that runs Python 3. @@ -537,6 +586,10 @@ Provides: bundled(python3dist(pip)) = %{pip_version} Provides: bundled(python3dist(setuptools)) = %{setuptools_version} %endif +# Provides for the bundled libmpdec +Provides: bundled(mpdecimal) = %{libmpdec_version} +Provides: bundled(libmpdec) = %{libmpdec_version} + # There are files in the standard library that have python shebang. # We've filtered the automatic requirement out so libs are installable without # the main package. This however makes it pulled in by default. @@ -568,6 +621,8 @@ Requires: %{pkgname}-libs%{?_isa} = %{version}-%{release} Requires: (python-rpm-macros if rpm-build) Requires: (python3-rpm-macros if rpm-build) +# Require alternatives version that implements the --keep-foreign flag +Requires(postun): alternatives >= 1.19.1-1 # python39 installs the alternatives master symlink to which we attach a slave Requires(post): %{pkgname} Requires(postun): %{pkgname} @@ -616,6 +671,8 @@ Provides: %{pkgname}-tools = %{version}-%{release} Provides: %{pkgname}-tools%{?_isa} = %{version}-%{release} Obsoletes: %{pkgname}-tools < %{version}-%{release} +# Require alternatives version that implements the --keep-foreign flag +Requires(postun): alternatives >= 1.19.1-1 # python39 installs the alternatives master symlink to which we attach a slave Requires(post): %{pkgname} Requires(postun): %{pkgname} @@ -675,6 +732,8 @@ Provides: platform-python-debug%{?_isa} = %{version}-%{release} Obsoletes: platform-python-debug < %{pybasever} %endif +# Require alternatives version that implements the --keep-foreign flag +Requires(postun): alternatives >= 1.19.1-1 # python39 installs the alternatives master symlink to which we attach a slave Requires(post): %{pkgname} Requires(postun): %{pkgname} @@ -720,6 +779,10 @@ Provides: bundled(python3dist(pip)) = %{pip_version} Provides: bundled(python3dist(setuptools)) = %{setuptools_version} %endif +# Provides for the bundled libmpdec +Provides: bundled(mpdecimal) = %{libmpdec_version} +Provides: bundled(libmpdec) = %{libmpdec_version} + # The zoneinfo module needs tzdata Requires: tzdata @@ -757,6 +820,9 @@ If you want to build an RPM against the python%{pyshortver} module, you need to %prep %autosetup -S git_am -N -n Python-%{upstream_version} +# Temporary workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1954999 +%{?!apply_patch:%define apply_patch(qp:m:) {%__apply_patch %**}} + # Apply patches up to 188 %apply_patch -q %{PATCH1} %apply_patch -q %{PATCH111} @@ -771,6 +837,8 @@ rm Lib/ensurepip/_bundled/*.whl %apply_patch -q %{PATCH328} %apply_patch -q %{PATCH329} %apply_patch -q %{PATCH353} +%apply_patch -q %{PATCH378} +%apply_patch -q %{PATCH399} # Remove all exe files to ensure we are not shipping prebuilt binaries # note that those are only used to create Microsoft Windows installers @@ -1244,6 +1312,11 @@ for Module in %{buildroot}/%{dynload_dir}/*.so ; do esac done +# Verify that the bundled libmpdec version python was compiled with, is the same version we have virtual +# provides for in the SPEC. +test "$(LD_LIBRARY_PATH=$(pwd)/build/optimized $(pwd)/build/optimized/python -c 'import decimal; print(decimal.__libmpdec_version__)')" = \ + "%{libmpdec_version}" + # ====================================================== # Running the upstream test suite @@ -1344,15 +1417,15 @@ fi %postun # Do this only during uninstall process (not during update) if [ $1 -eq 0 ]; then - alternatives --remove python \ + alternatives --keep-foreign --remove python \ %{_bindir}/python3.9 - alternatives --remove python3 \ + alternatives --keep-foreign --remove python3 \ %{_bindir}/python3.9 # Remove link python → python3 if no other python3.* exists if ! alternatives --display python3 > /dev/null; then - alternatives --remove python \ + alternatives --keep-foreign --remove python \ %{_bindir}/python3 fi fi @@ -1367,7 +1440,7 @@ alternatives --add-slave python3 %{_bindir}/python3.9 \ %postun devel # Do this only during uninstall process (not during update) if [ $1 -eq 0 ]; then - alternatives --remove-slave python3 %{_bindir}/python3.9 \ + alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.9 \ python3-config fi @@ -1385,9 +1458,9 @@ alternatives --add-slave python3 %{_bindir}/python3.9 \ %postun debug # Do this only during uninstall process (not during update) if [ $1 -eq 0 ]; then - alternatives --remove-slave python3 %{_bindir}/python3.9 \ + alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.9 \ python3-debug - alternatives --remove-slave python3 %{_bindir}/python3.9 \ + alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.9 \ python3-debug-config fi @@ -1401,7 +1474,7 @@ alternatives --add-slave python3 %{_bindir}/python3.9 \ %postun idle # Do this only during uninstall process (not during update) if [ $1 -eq 0 ]; then - alternatives --remove-slave python3 %{_bindir}/python3.9 \ + alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.9 \ idle3 fi @@ -1510,7 +1583,6 @@ fi %{pylibdir}/pydoc_data %{dynload_dir}/_blake2.%{SOABI_optimized}.so -%{dynload_dir}/_hmacopenssl.%{SOABI_optimized}.so %{dynload_dir}/_asyncio.%{SOABI_optimized}.so %{dynload_dir}/_bisect.%{SOABI_optimized}.so @@ -1803,7 +1875,6 @@ fi # ...with debug builds of the built-in "extension" modules: %{dynload_dir}/_blake2.%{SOABI_debug}.so -%{dynload_dir}/_hmacopenssl.%{SOABI_debug}.so %{dynload_dir}/_asyncio.%{SOABI_debug}.so %{dynload_dir}/_bisect.%{SOABI_debug}.so @@ -1934,6 +2005,47 @@ fi # ====================================================== %changelog +* Mon May 29 2023 Charalampos Stratakis - 3.9.16-1.1 +- Security fix for CVE-2023-24329 +Resolves: rhbz#2173917 + +* Tue Dec 13 2022 Charalampos Stratakis - 3.9.16-1 +- Update to 3.9.16 +- Security fix for CVE-2022-45061 +Resolves: rhbz#2144072 + +* Mon Nov 07 2022 Lumír Balhar - 3.9.14-2 +- Fix for CVE-2022-42919 +Resolves: rhbz#2138705 + +* Mon Sep 12 2022 Charalampos Stratakis - 3.9.14-1 +- Update to 3.9.14 +- Security fixes for CVE-2020-10735 and CVE-2021-28861 +Resolves: rhbz#1834423, rhbz#2120642 + +* Tue Jun 14 2022 Charalampos Stratakis - 3.9.13-1 +- Update to 3.9.13 +- Security fix for CVE-2015-20107 +- Fix the test suite support for Expat >= 2.4.5 +Resolves: rhbz#2075390 + +* Tue Sep 07 2021 Charalampos Stratakis - 3.9.7-1 +- Update to 3.9.7 +Resolves: rhbz#2003102 + +* Thu Aug 05 2021 Tomas Orsava - 3.9.6-2 +- Adjusted the postun scriptlets to enable upgrading to RHEL 9 +- Resolves: rhbz#1933055 + +* Tue Jul 27 2021 Charalampos Stratakis - 3.9.6-1 +- Update to 3.9.6 +- Fix CVE-2021-29921: Improper input validation of octal strings in the ipaddress module +Resolves: rhbz#1957458 + +* Fri Apr 30 2021 Charalampos Stratakis - 3.9.2-2 +- Security fix for CVE-2021-3426: information disclosure via pydoc +Resolves: rhbz#1935913 + * Wed Mar 03 2021 Lumír Balhar - 3.9.2-1 - Update to 3.9.2 to fix CVE-2021-23336 Resolves: rhbz#1928904