From 58740dc73d53102c2a0571095d6d894379a1601a Mon Sep 17 00:00:00 2001 From: MSVSphere Packaging Team Date: Mon, 18 Dec 2023 22:02:26 +0300 Subject: [PATCH] import python39-3.9.2-1.module+el8.4.0+10237+bdc77aac --- .gitignore | 2 +- .python39.metadata | 2 +- SOURCES/00189-use-rpm-wheels.patch | 28 +- SOURCES/00329-fips.patch | 3849 +++++++++++++---------- SOURCES/00378-support-expat-2-4-5.patch | 47 - SOURCES/Python-3.9.16.tar.xz.asc | 16 - SOURCES/Python-3.9.2.tar.xz.asc | 16 + SPECS/python39.spec | 129 +- 8 files changed, 2169 insertions(+), 1920 deletions(-) delete mode 100644 SOURCES/00378-support-expat-2-4-5.patch delete mode 100644 SOURCES/Python-3.9.16.tar.xz.asc create mode 100644 SOURCES/Python-3.9.2.tar.xz.asc diff --git a/.gitignore b/.gitignore index 071ce6d..df4a92f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/Python-3.9.16.tar.xz +SOURCES/Python-3.9.2.tar.xz diff --git a/.python39.metadata b/.python39.metadata index bc9973c..2ffe7aa 100644 --- a/.python39.metadata +++ b/.python39.metadata @@ -1 +1 @@ -19acd6a341e4f2d7ff97c10c2eada258e9898624 SOURCES/Python-3.9.16.tar.xz +110ca5bca7989f9558a54ee6762e6774a4b9644a SOURCES/Python-3.9.2.tar.xz diff --git a/SOURCES/00189-use-rpm-wheels.patch b/SOURCES/00189-use-rpm-wheels.patch index c7edc79..24eafad 100644 --- a/SOURCES/00189-use-rpm-wheels.patch +++ b/SOURCES/00189-use-rpm-wheels.patch @@ -1,4 +1,4 @@ -From 2c91575950d4de95d308e30cc4ab20d032b1aceb Mon Sep 17 00:00:00 2001 +From 0000000000000000000000000000000000000000 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 | 37 ++++++++++++++++++++++++++----------- - 1 file changed, 26 insertions(+), 11 deletions(-) + Lib/ensurepip/__init__.py | 33 ++++++++++++++++++++++----------- + 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/Lib/ensurepip/__init__.py b/Lib/ensurepip/__init__.py -index e510cc7..8de2e55 100644 +index 97dfa7ea71..984e587ea0 100644 --- a/Lib/ensurepip/__init__.py +++ b/Lib/ensurepip/__init__.py @@ -1,3 +1,5 @@ @@ -21,7 +21,7 @@ index e510cc7..8de2e55 100644 import os import os.path import sys -@@ -6,13 +8,29 @@ import tempfile +@@ -6,16 +8,28 @@ import tempfile import subprocess from importlib import resources @@ -30,13 +30,13 @@ index e510cc7..8de2e55 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,11 +51,10 @@ index e510cc7..8de2e55 100644 +_SETUPTOOLS_VERSION = _get_most_recent_wheel_version("setuptools") + +_PIP_VERSION = _get_most_recent_wheel_version("pip") -+ + _PROJECTS = [ ("setuptools", _SETUPTOOLS_VERSION, "py3"), - ("pip", _PIP_VERSION, "py3"), -@@ -101,13 +119,10 @@ def _bootstrap(*, root=None, upgrade=False, user=False, +@@ -105,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: @@ -73,6 +72,3 @@ index e510cc7..8de2e55 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 cec8740..5fdad74 100644 --- a/SOURCES/00329-fips.patch +++ b/SOURCES/00329-fips.patch @@ -1,970 +1,715 @@ -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 +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 -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). --- - Include/modsupport.h | 10 ++++++++++ - Python/modsupport.c | 13 +++++++++++-- - 2 files changed, 21 insertions(+), 2 deletions(-) + Lib/hashlib.py | 194 +++++++++++++++------------------------ + Lib/test/test_hashlib.py | 1 + + 2 files changed, 76 insertions(+), 119 deletions(-) -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; +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', } -+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) ++try: ++ from _hashlib import get_fips_mode ++except ImportError: ++ def get_fips_mode(): ++ return 0 + - 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; - } ++if not get_fips_mode(): ++ __builtin_constructor_cache = {} ++ ++ 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) --#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; -+ } - } + 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) - 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: + +@@ -173,72 +185,14 @@ try: algorithms_available = algorithms_available.union( _hashlib.openssl_md_meth_names) except ImportError: -+ _hashlib = None ++ if get_fips_mode: ++ raise new = __py_new __get_hash = __get_builtin_constructor -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 - +-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) - - 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() +- if not isinstance(password, (bytes, bytearray)): +- password = bytes(memoryview(password)) +- if not isinstance(salt, (bytes, bytearray)): +- salt = bytes(memoryview(salt)) - - 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 +- # 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)) - -- @property -- def digest_cons(self): -- return self._digest_cons +- 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() - -- @property -- def inner(self): -- return self._inner +- if iterations < 1: +- raise ValueError(iterations) +- if dklen is None: +- dklen = outer.digest_size +- if dklen < 1: +- raise ValueError(dklen) - -- @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 +- 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] ++# OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA ++from _hashlib import pbkdf2_hmac try: -+ 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 + # 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]) -+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) -- ) ++ @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 + -+ 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 -+ ) ++#include "Python.h" ++#include ++#include + -+ h = hmac.HMAC.__new__(hmac.HMAC) -+ h._init_hmac(key, data, digestmod=hashname) -+ 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; + - @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") ++ errcode = ERR_peek_last_error(); ++ if (!errcode) { ++ PyErr_SetString(exc, "unknown reasons"); ++ return NULL; ++ } ++ ERR_clear_error(); + -+ digest = hmac.digest(b"key", b"hash this!", sha256_module.sha256) -+ self.assertEqual(digest, binascii.unhexlify(self.expected)) ++ lib = ERR_lib_error_string(errcode); ++ func = ERR_func_error_string(errcode); ++ reason = ERR_reason_error_string(errcode); + - - 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); ++ if (lib && func) { ++ PyErr_Format(exc, "[%s: %s] %s", lib, func, 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; ++ else if (lib) { ++ PyErr_Format(exc, "[%s] %s", lib, reason); + } -+ -+ name = PyUnicode_AsUTF8(name_obj); -+ if (name == NULL) { -+ return NULL; ++ else { ++ PyErr_SetString(exc, reason); + } ++ return NULL; ++} ++/* LCOV_EXCL_STOP */ + -+ evp = py_digest_by_name(module, name, py_ht); -+ if (evp == NULL) { -+ return NULL; -+ } + -+ return evp; ++__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; ++ } ++ PyErr_Format(PyExc_ValueError, "%s is not available in FIPS mode", ++ name); ++ return 1; +} + - static EVPobject * - newEVPobject(PyTypeObject *type) - { -@@ -1238,7 +1282,6 @@ pbkdf2_hmac_impl(PyObject *module, const char *hash_name, - - PY_EVP_MD *digest = py_digest_by_name(module, hash_name, Py_ht_pbkdf2); - if (digest == NULL) { -- PyErr_SetString(PyExc_ValueError, "unsupported hash type"); - goto end; - } - -@@ -1443,25 +1486,21 @@ _hashlib.hmac_digest as _hashlib_hmac_singleshot ++#define FAIL_RETURN_IN_FIPS_MODE(name) do { \ ++ if (_Py_hashlib_fips_error(name)) return NULL; \ ++} while (0) ++ ++#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 @@ + */ - key: Py_buffer - msg: Py_buffer -- digest: str -+ digest: object + #include "Python.h" ++#include "_hashopenssl.h" + #include "pystrhex.h" - Single-shot HMAC. - [clinic start generated code]*/ + #include "../hashlib.h" +@@ -96,6 +97,8 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size, + BLAKE2bObject *self = NULL; + Py_buffer buf; - 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]*/ ++ FAIL_RETURN_IN_FIPS_MODE("_blake2"); ++ + self = new_BLAKE2bObject(type); + if (self == NULL) { + goto error; +@@ -274,6 +277,8 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data) { - 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 = 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 - - key: Py_buffer - msg as msg_obj: object(c_default="NULL") = b'' -- digestmod: str(c_default="NULL") = None -+ digestmod: object(c_default="NULL") = None + Py_buffer buf; - Return a new hmac object. - [clinic start generated code]*/ ++ FAIL_RETURN_IN_FIPS_MODE("_blake2"); ++ + GET_BUFFER_VIEW_OR_ERROUT(data, &buf); - static PyObject * - _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; - } + 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 @@ + */ -- if ((digestmod == NULL) || !strlen(digestmod)) { -+ if (digestmod == NULL) { - PyErr_SetString( - PyExc_TypeError, "Missing required parameter 'digestmod'."); - return NULL; - } + #include "Python.h" ++#include "_hashopenssl.h" -- 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; - } + #include "impl/blake2.h" -@@ -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; - } +@@ -57,6 +58,8 @@ PyInit__blake2(void) + PyObject *m; + PyObject *d; -@@ -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; - } ++ FAIL_RETURN_IN_FIPS_MODE("blake2"); + - return 0; - } + 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 @@ + */ -@@ -2227,6 +2272,79 @@ hashlib_init_hmactype(PyObject *module) - return 0; - } + #include "Python.h" ++#include "_hashopenssl.h" + #include "pystrhex.h" -+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; -+ } -+ -+ 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; -+ } -+ } -+ -+ proxy = PyDictProxy_New(state->constructs); -+ if (proxy == NULL) { -+ return -1; -+ } -+ -+ int rc = _PyModule_AddObjectRef(module, "_constructors", proxy); -+ Py_DECREF(proxy); -+ if (rc < 0) { -+ return -1; -+ } -+ return 0; -+} -+ -+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; -+} + #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) + { + Py_buffer buf; + ++ FAIL_RETURN_IN_FIPS_MODE("_blake2"); + - 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} - }; + GET_BUFFER_VIEW_OR_ERROUT(data, &buf); -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__, + 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" - 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); + /* 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() - exit: -@@ -1176,7 +1164,7 @@ PyDoc_STRVAR(_hashlib_hmac_new__doc__, + #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]*/ - 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; +-/* 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); - } -- Py_ssize_t digestmod_length; -- digestmod = PyUnicode_AsUTF8AndSize(args[2], &digestmod_length); -- if (digestmod == NULL) { -- goto exit; +- else if (lib) { +- PyErr_Format(exc, "[%s] %s", lib, reason); - } -- if (strlen(digestmod) != (size_t)digestmod_length) { -- PyErr_SetString(PyExc_ValueError, "embedded null character"); -- goto exit; +- else { +- PyErr_SetString(exc, reason); - } -+ digestmod = args[2]; - skip_optional_pos: - return_value = _hashlib_hmac_new_impl(module, &key, msg_obj, digestmod); +- 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)) + +- 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() + +@@ -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') + + # 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') + + # 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) + ++ ++ ssl_args = { ++ 'include_dirs': openssl_includes, ++ 'library_dirs': openssl_libdirs, ++ 'libraries': ['ssl', 'crypto'], ++ } ++ ++ return ssl_args ++ ++ def detect_openssl_hashlib(self): ++ ++ config_vars = sysconfig.get_config_vars() ++ + if config_vars.get("HAVE_X509_VERIFY_PARAM_SET1_HOST"): + self.add(Extension( + '_ssl', ['_ssl.c'], +- include_dirs=openssl_includes, +- library_dirs=openssl_libdirs, +- libraries=openssl_libs, ++ **self.detect_openssl_args(), + depends=['socketmodule.h', '_ssl/debughelpers.c']) + ) + else: +@@ -2358,9 +2369,7 @@ class PyBuildExt(build_ext): + + self.add(Extension('_hashlib', ['_hashopenssl.c'], + depends=['hashlib.h'], +- include_dirs=openssl_includes, +- library_dirs=openssl_libdirs, +- libraries=openssl_libs)) ++ **self.detect_openssl_args()) ) + + def detect_hash_builtins(self): + # By default we always compile these even when OpenSSL is available +@@ -2417,6 +2426,7 @@ class PyBuildExt(build_ext): + '_blake2/blake2b_impl.c', + '_blake2/blake2s_impl.c' + ], ++ **self.detect_openssl_args(), + depends=blake2_deps + )) -@@ -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.37.2 +2.26.2 -From b63e3fbd7c0506b5a6c00c1bb0d255054e38bbe8 Mon Sep 17 00:00:00 2001 +From 56171083467bd5798adcb1946cfc0b1d68403755 Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Thu, 12 Dec 2019 16:58:31 +0100 -Subject: [PATCH 04/10] Expose blake2b and blake2s hashes from OpenSSL +Subject: [PATCH 03/13] Expose all hashes available to OpenSSL -These aren't as powerful as Python's own implementation, but they can be -used under FIPS. --- - Lib/test/test_hashlib.py | 6 ++ - Modules/_hashopenssl.c | 37 +++++++++++ + 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 +++++++++++++ Modules/clinic/_hashopenssl.c.h | 106 +++++++++++++++++++++++++++++++- - 3 files changed, 148 insertions(+), 1 deletion(-) + 6 files changed, 158 insertions(+), 12 deletions(-) -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/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/Modules/_hashopenssl.c b/Modules/_hashopenssl.c -index ca9fea9..9d98d20 100644 +index deecc077ef8..a805183721b 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c -@@ -1138,6 +1138,41 @@ _hashlib_openssl_sha512_impl(PyObject *module, PyObject *data_obj, +@@ -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, } @@ -982,7 +727,7 @@ index ca9fea9..9d98d20 100644 +/*[clinic end generated code: output=7a838b1643cde13e input=4ad7fd54268f3689]*/ + +{ -+ return py_evp_fromname(module, Py_hash_blake2b, data_obj, usedforsecurity); ++ return EVP_fast_new(module, data_obj, EVP_blake2b512(), usedforsecurity); +} + +/*[clinic input] @@ -999,14 +744,14 @@ index ca9fea9..9d98d20 100644 +/*[clinic end generated code: output=4eda6b40757471da input=1ed39481ffa4e26a]*/ + +{ -+ return py_evp_fromname(module, Py_hash_blake2s, data_obj, usedforsecurity); ++ return EVP_fast_new(module, data_obj, EVP_blake2s256(), usedforsecurity); +} + + #ifdef PY_OPENSSL_HAS_SHA3 /*[clinic input] -@@ -2135,6 +2170,8 @@ static struct PyMethodDef EVP_functions[] = { +@@ -1931,6 +1972,8 @@ static struct PyMethodDef EVP_functions[] = { _HASHLIB_OPENSSL_SHA256_METHODDEF _HASHLIB_OPENSSL_SHA384_METHODDEF _HASHLIB_OPENSSL_SHA512_METHODDEF @@ -1016,7 +761,7 @@ index ca9fea9..9d98d20 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 4466ec4..54c22b2 100644 +index 68aa765e529..2957ae2e135 100644 --- a/Modules/clinic/_hashopenssl.c.h +++ b/Modules/clinic/_hashopenssl.c.h @@ -540,6 +540,110 @@ exit: @@ -1130,518 +875,874 @@ index 4466ec4..54c22b2 100644 #if defined(PY_OPENSSL_HAS_SHA3) PyDoc_STRVAR(_hashlib_openssl_sha3_224__doc__, -@@ -1418,4 +1522,4 @@ exit: +@@ -1442,4 +1546,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=7ff9aad0bd53e7ce input=a9049054013a1b77]*/ -+/*[clinic end generated code: output=fab05055e982f112 input=a9049054013a1b77]*/ +-/*[clinic end generated code: output=b6b280e46bf0b139 input=a9049054013a1b77]*/ ++/*[clinic end generated code: output=4f8cc45bf0337f8e input=a9049054013a1b77]*/ -- -2.37.2 +2.26.2 -From dc8ad7b98d6d9bf14cae439acb3a99fa8f4f5020 Mon Sep 17 00:00:00 2001 +From e024cae691bffa2d093a63f8e2058331fce94d2a Mon Sep 17 00:00:00 2001 From: Petr Viktorin -Date: Thu, 1 Aug 2019 17:57:05 +0200 -Subject: [PATCH 05/10] Use a stronger hash in multiprocessing handshake +Date: Thu, 25 Jul 2019 18:13:45 +0200 +Subject: [PATCH 04/13] Fix tests -Adapted from patch by David Malcolm, -https://bugs.python.org/issue17258 --- - Lib/multiprocessing/connection.py | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) + Lib/test/test_hashlib.py | 5 +++++ + 1 file changed, 5 insertions(+) -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: +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.37.2 +2.26.2 -From af0c88c9d5bc4f9c127e49ed80d14e25d18813f2 Mon Sep 17 00:00:00 2001 +From 3e87bf1c3d32c09a50385d8576b1164cafce4158 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 +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 + +This removes the _hmacopenssl.new function. --- - 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(-) + 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 -diff --git a/Lib/hashlib.py b/Lib/hashlib.py -index ffa3be0..3e3f4dd 100644 ---- a/Lib/hashlib.py -+++ b/Lib/hashlib.py -@@ -70,14 +70,17 @@ __all__ = __always_supported + ('new', 'algorithms_guaranteed', - - __builtin_constructor_cache = {} - --# Prefer our blake2 implementation -+# Prefer our blake2 implementation (unless in FIPS mode) - # OpenSSL 1.1.0 comes with a limited implementation of blake2b/s. The OpenSSL - # implementations neither support keyed blake2 (blake2 MAC) nor advanced - # features like salt, personalization, or tree hashing. OpenSSL hash-only - # variants are available as 'blake2b512' and 'blake2s256', though. --__block_openssl_constructor = { -- 'blake2b', 'blake2s', --} -+import _hashlib -+if _hashlib.get_fips_mode(): -+ __block_openssl_constructor = set() -+else: -+ __block_openssl_constructor = {'blake2b', 'blake2s'} -+ +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 - 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(",") - } + 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().' ++ ) --# 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') + 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: - try: - from _hashlib import HASH, HASHXOF, openssl_md_meth_names, get_fips_mode -@@ -118,6 +119,12 @@ class HashLibTestCase(unittest.TestCase): - except ModuleNotFoundError as error: - if self._warn_on_extension_import and module_name in builtin_hashes: - warnings.warn('Did a C extension fail to compile? %s' % error) -+ except ImportError: -+ if get_fips_mode() and module_name == '_blake2': -+ # blake2b & blake2s disabled under FIPS -+ return None -+ else: -+ raise - return None + def 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 __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; + def copy(self): +@@ -150,6 +159,34 @@ class HMAC: + h = self._current() + return h.hexdigest() -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); ++def _get_openssl_name(digestmod): ++ if isinstance(digestmod, str): ++ return digestmod.lower() ++ elif callable(digestmod): ++ digestmod = digestmod(b'') + - self = new_BLAKE2bObject(type); - if (self == NULL) { - goto error; -@@ -274,6 +276,8 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data) - { - Py_buffer buf; - -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); ++ if not isinstance(digestmod, _hashlibopenssl.HASH): ++ raise TypeError( ++ 'Only OpenSSL hashlib hashes are accepted in FIPS mode.') + - GET_BUFFER_VIEW_OR_ERROUT(data, &buf); ++ 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. - 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 @@ - */ +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) - #include "Python.h" -+#include "../hashlib.h" ++ @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.") - #include "impl/blake2.h" ++ 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.") ++ + @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): -@@ -57,6 +58,8 @@ PyInit__blake2(void) - PyObject *m; - PyObject *d; + class CopyTestCase(unittest.TestCase): -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ImportError, "blake2"); -+ - m = PyModule_Create(&blake2_module); - if (m == NULL) - return NULL; -diff --git a/Modules/_blake2/blake2s_impl.c b/Modules/_blake2/blake2s_impl.c -index e3e90d0..e45f8f6 100644 ---- a/Modules/_blake2/blake2s_impl.c -+++ b/Modules/_blake2/blake2s_impl.c -@@ -96,6 +96,8 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size, - BLAKE2sObject *self = NULL; - Py_buffer buf; ++ @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.") -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); + - self = new_BLAKE2sObject(type); - if (self == NULL) { - goto error; -@@ -274,6 +276,8 @@ _blake2_blake2s_update(BLAKE2sObject *self, PyObject *data) - { - Py_buffer buf; ++ @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) -+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2"); ++ @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 + - 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 + @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 */ + - /* - * 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; ++/* 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; + } -+ return 0; -+} + -+#define FAIL_RETURN_IN_FIPS_MODE(exc, name) do { \ -+ if (_Py_hashlib_fips_error(exc, name)) return NULL; \ -+} while (0) -diff --git a/setup.py b/setup.py -index 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, ++ 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; + } ++ } + -+ def detect_openssl_hashlib(self): ++ const EVP_MD *digest = EVP_get_digestbyname(digestmod); ++ if (!digest) { ++ PyErr_SetString(PyExc_ValueError, "unknown hash function"); ++ return NULL; ++ } + -+ config_vars = sysconfig.get_config_vars() ++ PyObject *name = NULL; ++ HMAC_CTX *ctx = NULL; ++ HmacObject *retval = NULL; + - 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'], -- include_dirs=openssl_includes, -- library_dirs=openssl_libdirs, -- libraries=openssl_libs)) -+ **self.detect_openssl_args()) ) - - def detect_hash_builtins(self): - # By default we always compile these even when OpenSSL is available -@@ -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.37.2 - - -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 ++ name = PyUnicode_FromFormat("hmac-%s", digestmod); ++ if (name == NULL) { ++ goto error; ++ } ++ ++ ctx = HMAC_CTX_new(); ++ if (ctx == NULL) { ++ _setException(PyExc_ValueError); ++ goto error; ++ } ++ ++ int r = HMAC_Init_ex( ++ ctx, ++ (const char*)key.buf, ++ key.len, ++ digest, ++ NULL /*impl*/); ++ if (r == 0) { ++ _setException(PyExc_ValueError); ++ goto error; ++ } ++ ++ PyBuffer_Release(&key); ++ key.buf = NULL; ++ ++ retval = (HmacObject *)subtype->tp_alloc(subtype, 0); ++ if (retval == NULL) { ++ goto error; ++ } ++ ++ 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; ++} ++ ++/*[clinic input] ++_hmacopenssl.HMAC.copy ++ ++Return a copy (“clone”) of the HMAC object. ++[clinic start generated code]*/ ++ ++static PyObject * ++_hmacopenssl_HMAC_copy_impl(HmacObject *self) ++/*[clinic end generated code: output=fe5ee41faf30dcf0 input=f5ed20feec42d8d0]*/ ++{ ++ HmacObject *retval; ++ ++ HMAC_CTX *ctx = HMAC_CTX_new(); ++ if (ctx == NULL) { ++ return _setException(PyExc_ValueError); ++ } ++ ++ 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): + depends=['hashlib.h'], + **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 +-- +2.26.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 +Adapted from patch by David Malcolm, +https://bugs.python.org/issue17258 --- - Lib/hashlib.py | 69 +++------------------------------------- - Lib/test/test_hashlib.py | 23 +++++++++++++- - 2 files changed, 27 insertions(+), 65 deletions(-) + Lib/multiprocessing/connection.py | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) -diff --git a/Lib/hashlib.py b/Lib/hashlib.py -index 3e3f4dd..b842f5f 100644 ---- a/Lib/hashlib.py -+++ b/Lib/hashlib.py -@@ -67,7 +67,6 @@ algorithms_available = set(__always_supported) - __all__ = __always_supported + ('new', 'algorithms_guaranteed', - 'algorithms_available', 'pbkdf2_hmac') - -- - __builtin_constructor_cache = {} - - # Prefer our blake2 implementation (unless in FIPS mode) -@@ -83,6 +82,8 @@ else: - - - def __get_builtin_constructor(name): -+ if _hashlib.get_fips_mode(): -+ raise ValueError('unsupported hash type ' + name + '(in FIPS mode)') - cache = __builtin_constructor_cache - constructor = cache.get(name) - if constructor is not None: -@@ -176,79 +177,19 @@ try: - algorithms_available = algorithms_available.union( - _hashlib.openssl_md_meth_names) - except ImportError: -- _hashlib = None -- new = __py_new -- __get_hash = __get_builtin_constructor -+ raise # importing _hashlib should never fail on RHEL - - try: - # OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA - from _hashlib import pbkdf2_hmac - except ImportError: -- _trans_5C = bytes((x ^ 0x5C) for x in range(256)) -- _trans_36 = bytes((x ^ 0x36) for x in range(256)) -- -- def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None): -- """Password based key derivation function 2 (PKCS #5 v2.0) -- -- This Python implementations based on the hmac module about as fast -- as OpenSSL's PKCS5_PBKDF2_HMAC for short passwords and much faster -- for long passwords. -- """ -- if not isinstance(hash_name, str): -- raise TypeError(hash_name) -- -- if not isinstance(password, (bytes, bytearray)): -- password = bytes(memoryview(password)) -- if not isinstance(salt, (bytes, bytearray)): -- salt = bytes(memoryview(salt)) -- -- # Fast inline HMAC implementation -- inner = new(hash_name) -- outer = new(hash_name) -- blocksize = getattr(inner, 'block_size', 64) -- if len(password) > blocksize: -- password = new(hash_name, password).digest() -- password = password + b'\x00' * (blocksize - len(password)) -- inner.update(password.translate(_trans_36)) -- outer.update(password.translate(_trans_5C)) -- -- def prf(msg, inner=inner, outer=outer): -- # PBKDF2_HMAC uses the password as key. We can re-use the same -- # digest objects and just update copies to skip initialization. -- icpy = inner.copy() -- ocpy = outer.copy() -- icpy.update(msg) -- ocpy.update(icpy.digest()) -- return ocpy.digest() -- -- if iterations < 1: -- raise ValueError(iterations) -- if dklen is None: -- dklen = outer.digest_size -- if dklen < 1: -- raise ValueError(dklen) -- -- dkey = b'' -- loop = 1 -- from_bytes = int.from_bytes -- while len(dkey) < dklen: -- prev = prf(salt + loop.to_bytes(4, 'big')) -- # endianness doesn't matter here as long to / from use the same -- rkey = int.from_bytes(prev, 'big') -- for i in range(iterations - 1): -- prev = prf(prev) -- # rkey = rkey ^ prev -- rkey ^= from_bytes(prev, 'big') -- loop += 1 -- dkey += rkey.to_bytes(inner.digest_size, 'big') -- -- return dkey[:dklen] -+ raise # importing _hashlib should never fail on RHEL - - try: - # OpenSSL's scrypt requires OpenSSL 1.1+ - from _hashlib import scrypt - except ImportError: -- pass -+ raise # importing _hashlib should never fail on RHEL - - - for __func_name in __always_supported: -diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py -index 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") +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. -+ @unittest.skipUnless(get_fips_mode(), "Builtin constructor only usable in FIPS mode") -+ def test_get_builtin_constructor_fips(self): -+ get_builtin_constructor = getattr(hashlib, -+ '__get_builtin_constructor') -+ with self.assertRaises(ValueError): -+ get_builtin_constructor('md5') -+ with self.assertRaises(ValueError): -+ get_builtin_constructor('sha256') -+ with self.assertRaises(ValueError): -+ get_builtin_constructor('blake2s') -+ with self.assertRaises(ValueError): -+ get_builtin_constructor('test') -+ -+ @unittest.skipIf(get_fips_mode(), "No builtin constructors in FIPS mode") - def test_get_builtin_constructor(self): - get_builtin_constructor = getattr(hashlib, - '__get_builtin_constructor') -@@ -1061,6 +1081,7 @@ class KDFTests(unittest.TestCase): - iterations=1, dklen=None) - self.assertEqual(out, self.pbkdf2_results['sha1'][0][0]) ++# 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() -+ @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) + 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 +2.26.2 -From 331c0d39cbc9c4df266c375bae8c1a0d27dd78d9 Mon Sep 17 00:00:00 2001 +From 86868ca46c47112f771d54a54ee89e2d6c00f56f Mon Sep 17 00:00:00 2001 From: Charalampos Stratakis Date: Wed, 31 Jul 2019 15:43:43 +0200 -Subject: [PATCH 08/10] Test equivalence of hashes for the various digests with - usedforsecurity=True/False +Subject: [PATCH 07/13] Add initial tests for various hashes under FIPS mode --- - Lib/test/test_fips.py | 24 +++++++++++++++++++++ - Lib/test/test_hashlib.py | 46 ++++++++++++++++++++++++++++++---------- - 2 files changed, 59 insertions(+), 11 deletions(-) + Lib/test/test_fips.py | 31 +++++++++++++++++++++++++++++++ + 1 file changed, 31 insertions(+) create mode 100644 Lib/test/test_fips.py diff --git a/Lib/test/test_fips.py b/Lib/test/test_fips.py new file mode 100644 -index 0000000..1f99dd7 +index 00000000000..fe4ea72296e --- /dev/null +++ b/Lib/test/test_fips.py -@@ -0,0 +1,24 @@ +@@ -0,0 +1,31 @@ +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 + """ @@ -1650,16 +1751,147 @@ index 0000000..1f99dd7 + 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 ec6c883..0fd036f 100644 +index 6f60ad4b8fb..f306ba33b20 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -20,6 +20,7 @@ import warnings @@ -1670,32 +1902,46 @@ index ec6c883..0fd036f 100644 # Were we compiled --with-pydebug or with #define Py_DEBUG? COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount') -@@ -55,6 +56,11 @@ except ImportError: - def get_fips_mode(): - return 0 +@@ -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() -+if get_fips_mode(): -+ FIPS_DISABLED = {'md5'} -+else: -+ FIPS_DISABLED = set() -+ try: - import _blake2 - except ImportError: -@@ -98,6 +104,11 @@ def read_vectors(hash_name): +@@ -98,6 +101,14 @@ def read_vectors(hash_name): parts[0] = bytes.fromhex(parts[0]) yield parts -+def _is_blake2_constructor(constructor): ++def _is_openssl_constructor(constructor): ++ if getattr(constructor, '__name__', '').startswith('openssl_'): ++ return True + if isinstance(constructor, partial): -+ constructor = constructor.func -+ return getattr(constructor, '__name__', '').startswith('openssl_blake2') ++ if constructor.func.__name__.startswith('openssl_'): ++ return True ++ return False + class HashLibTestCase(unittest.TestCase): supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1', -@@ -142,15 +153,21 @@ class HashLibTestCase(unittest.TestCase): - continue +@@ -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: self.constructors_to_test[algorithm] = set() + def _add_constructor(algorithm, constructor): @@ -1718,7 +1964,7 @@ index ec6c883..0fd036f 100644 _hashlib = self._conditional_import_module('_hashlib') self._hashlib = _hashlib -@@ -162,13 +179,7 @@ class HashLibTestCase(unittest.TestCase): +@@ -158,13 +175,7 @@ class HashLibTestCase(unittest.TestCase): for algorithm, constructors in self.constructors_to_test.items(): constructor = getattr(_hashlib, 'openssl_'+algorithm, None) if constructor: @@ -1732,34 +1978,86 @@ index ec6c883..0fd036f 100644 + _add_constructor(algorithm, constructor) def add_builtin_constructor(name): - try: -@@ -346,6 +357,8 @@ class HashLibTestCase(unittest.TestCase): + 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): self.assertIn(h.name, self.supported_hash_names) else: self.assertNotIn(h.name, self.supported_hash_names) -+ if not h.name.startswith('blake2') and h.name not in FIPS_DISABLED: +- self.assertEqual( +- h.name, +- hashlib.new(h.name, usedforsecurity=False).name +- ) ++ if h.name not in FIPS_DISABLED: + self.assertEqual(h.name, hashlib.new(h.name).name) - self.assertEqual( - h.name, - hashlib.new(h.name, usedforsecurity=False).name -@@ -392,8 +405,10 @@ class HashLibTestCase(unittest.TestCase): - for hash_object_constructor in constructors: ++ self.assertEqual(h.name, hashlib.new(h.name, usedforsecurity=False).name) - # 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) -+ ): + def test_large_update(self): + aas = b'a' * 128 +@@ -371,9 +381,11 @@ class HashLibTestCase(unittest.TestCase): + self.assertGreaterEqual(len(constructors), 2) + 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) -@@ -974,6 +989,15 @@ class HashLibTestCase(unittest.TestCase): + 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 + ) + +- @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): ): HASHXOF() -+ @unittest.skipUnless(get_fips_mode(), 'Needs FIPS mode.') ++ @unittest.skipUnless(hashlib.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): @@ -1772,68 +2070,225 @@ index ec6c883..0fd036f 100644 class KDFTests(unittest.TestCase): -- -2.37.2 +2.26.2 -From 1a3df28f95710925bc80018bcf22b7f37bbb1e17 Mon Sep 17 00:00:00 2001 +From 5feafbf68d297e3f4fcafe4cbeff97817c592c53 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Mon, 26 Aug 2019 19:39:48 +0200 -Subject: [PATCH 09/10] Guard against Python HMAC in FIPS mode +Subject: [PATCH 10/13] Don't re-export get_fips_mode from hashlib +Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1745685 --- - Lib/hmac.py | 13 +++++++++---- - Lib/test/test_hmac.py | 10 ++++++++++ - 2 files changed, 19 insertions(+), 4 deletions(-) + 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(-) +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 8b4f920..20ef96c 100644 +index 482e443bfe4..ff466322d7b 100644 --- a/Lib/hmac.py +++ b/Lib/hmac.py -@@ -16,8 +16,9 @@ else: +@@ -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: - import hashlib as _hashlib + 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) --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)) +@@ -183,7 +183,7 @@ class HMAC_openssl(_hmacopenssl.HMAC): + return result - # The size of the digests returned by HMAC depends on the underlying - # hashing module used. Use digest_size from the instance of HMAC instead. -@@ -48,17 +49,18 @@ class HMAC: - msg argument. Passing it as a keyword argument is - recommended, though not required for legacy API reasons. + +-if _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 + + 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 """ -- - if not isinstance(key, (bytes, bytearray)): - raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__) +@@ -15,7 +15,7 @@ class HashlibFipsTests(unittest.TestCase): + with self.assertRaises(ValueError, msg='blake2b not available in FIPS'): + m = hashlib.blake2b() - 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): +- @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() + +- @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): diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py -index adf52ad..41e6a14 100644 +index 544ec7cb411..2d4484911c2 100644 --- a/Lib/test/test_hmac.py +++ b/Lib/test/test_hmac.py @@ -5,6 +5,7 @@ import hashlib @@ -1844,335 +2299,275 @@ index adf52ad..41e6a14 100644 from test.support import hashlib_helper -@@ -339,6 +340,7 @@ class TestVectorsTestCase(unittest.TestCase): +@@ -322,7 +323,7 @@ class TestVectorsTestCase(unittest.TestCase): def test_sha512_rfc4231(self): self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128) -+ #unittest.skipIf(get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.') +- @unittest.skipIf(hashlib.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): -@@ -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): +@@ -455,7 +456,7 @@ class SanityTestCase(unittest.TestCase): class CopyTestCase(unittest.TestCase): -+ @unittest.skipIf(get_fips_mode(), "_init_old unavailable in FIPS mode") +- @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_attributes_old(self): + def test_attributes(self): # Testing if attributes are of same type. -@@ -483,6 +492,7 @@ class CopyTestCase(unittest.TestCase): - self.assertEqual(type(h1._outer), type(h2._outer), +@@ -469,7 +470,7 @@ class CopyTestCase(unittest.TestCase): "Types of outer don't match.") -+ @unittest.skipIf(get_fips_mode(), "_init_old unavailable in FIPS mode") + +- @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_realcopy_old(self): + def test_realcopy(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.37.2 - - -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 +2.26.2 -If FIPS mode is on, we can't use siphash-based HMAC -(_Py_KeyedHash), so: -- Unchecked hash PYCs can be imported, but not created -- Checked hash PYCs can not be imported nor created -- The default mode is timestamp-based PYCs, even if - SOURCE_DATE_EPOCH is set. +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 -If FIPS mode is off, there are no changes in behavior. +Kernel's getrandom() source is not yet FIPS compliant. Use OpenSSL's +DRBG in FIPS mode and disable os.getrandom() function. -Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1835169 +Signed-off-by: Christian Heimes --- - 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(-) + 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(-) -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 +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 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 -+from test import support - from test.support import make_legacy_pyc, unload - - from test.test_py_compile import without_source_date_epoch -@@ -239,6 +240,7 @@ class SimpleTest(abc.LoaderTests): - loader.load_module('bad name') - - @util.writes_bytecode_files -+ @support.fails_in_fips_mode(ImportError) - def test_checked_hash_based_pyc(self): - with util.create_modules('_temp') as mapping: - source = mapping['_temp'] -@@ -270,6 +272,7 @@ class SimpleTest(abc.LoaderTests): - ) + 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 - @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): - ) ++/* 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(); + } - @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) { ++ if (FIPS_mode()) { ++ PyErr_SetString(PyExc_ValueError, "getrandom is not FIPS compliant"); + return NULL; + } -+ PyObject *fips_mode_obj = PyObject_CallMethod(_hashlib, "get_fips_mode", NULL); -+ Py_DECREF(_hashlib); -+ if (fips_mode_obj == NULL) { -+ 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 */ + ++#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; ++ } + } -+ int fips_mode = PyObject_IsTrue(fips_mode_obj); -+ Py_DECREF(fips_mode_obj); -+ if (fips_mode < 0) { -+ return NULL; ++ ++ if (size > INT_MAX) { ++ if (raise) { ++ PyErr_Format(PyExc_OverflowError, ++ "RAND_bytes() size is limited to 2GB."); ++ } ++ return -1; + } -+ 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)]; ++ ++ res = RAND_bytes((unsigned char*)buffer, (int)size); ++ ++ if (res == 1) { ++ return 1; ++ } else { ++ if (raise) { ++ _setException(PyExc_RuntimeError); ++ } ++ return 0; ++ } ++} ++ + + /* 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); ++ } ++ + #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): + -- -2.37.2 +2.26.2 diff --git a/SOURCES/00378-support-expat-2-4-5.patch b/SOURCES/00378-support-expat-2-4-5.patch deleted file mode 100644 index a69e111..0000000 --- a/SOURCES/00378-support-expat-2-4-5.patch +++ /dev/null @@ -1,47 +0,0 @@ -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/Python-3.9.16.tar.xz.asc b/SOURCES/Python-3.9.16.tar.xz.asc deleted file mode 100644 index 5a093c6..0000000 --- a/SOURCES/Python-3.9.16.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------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 new file mode 100644 index 0000000..23a63c9 --- /dev/null +++ b/SOURCES/Python-3.9.2.tar.xz.asc @@ -0,0 +1,16 @@ +-----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 10af63f..27af78e 100644 --- a/SPECS/python39.spec +++ b/SPECS/python39.spec @@ -13,7 +13,7 @@ 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}.16 +%global general_version %{pybasever}.2 #global prerel ... %global upstream_version %{general_version}%{?prerel} Version: %{general_version}%{?prerel:~%{prerel}} @@ -164,13 +164,6 @@ 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' @@ -307,7 +300,7 @@ Patch1: 00001-rpath.patch # See https://bugzilla.redhat.com/show_bug.cgi?id=556092 Patch111: 00111-no-static-lib.patch -# 00189 # 4242864a6a12f1f4cf9fd63a6699a73f35261aa3 +# 00189 # 7c07eec60735bd65bda7d8e821d34718497cba27 # Instead of bundled wheels, use our RPM packaged wheels # # We keep them in /usr/share/python-wheels @@ -319,8 +312,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 21.2.3 -%global setuptools_version 57.4.0 +%global pip_version 20.2.3 +%global setuptools_version 49.2.1 # 00251 # 2eabd04356402d488060bc8fe316ad13fc8a3356 # Change user install location @@ -390,31 +383,6 @@ 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 - # (New patches go here ^^^) # # When adding new patches to "python" and "python3" in Fedora, EL, etc., @@ -433,10 +401,10 @@ Patch378: 00378-support-expat-2-4-5.patch # Descriptions, and metadata for subpackages # ========================================== -# 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 +# Runtime require alternatives +Requires: %{_sbindir}/alternatives +Requires(post): %{_sbindir}/alternatives +Requires(postun): %{_sbindir}/alternatives # When the user tries to `yum install python`, yum will list this package among # the possible alternatives @@ -552,11 +520,6 @@ 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. @@ -574,10 +537,6 @@ 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. @@ -609,8 +568,6 @@ 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} @@ -659,8 +616,6 @@ 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} @@ -720,8 +675,6 @@ 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} @@ -767,10 +720,6 @@ 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 @@ -808,9 +757,6 @@ 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} @@ -825,7 +771,6 @@ rm Lib/ensurepip/_bundled/*.whl %apply_patch -q %{PATCH328} %apply_patch -q %{PATCH329} %apply_patch -q %{PATCH353} -%apply_patch -q %{PATCH378} # Remove all exe files to ensure we are not shipping prebuilt binaries # note that those are only used to create Microsoft Windows installers @@ -1299,11 +1244,6 @@ 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 @@ -1404,15 +1344,15 @@ fi %postun # Do this only during uninstall process (not during update) if [ $1 -eq 0 ]; then - alternatives --keep-foreign --remove python \ + alternatives --remove python \ %{_bindir}/python3.9 - alternatives --keep-foreign --remove python3 \ + alternatives --remove python3 \ %{_bindir}/python3.9 # Remove link python → python3 if no other python3.* exists if ! alternatives --display python3 > /dev/null; then - alternatives --keep-foreign --remove python \ + alternatives --remove python \ %{_bindir}/python3 fi fi @@ -1427,7 +1367,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 --keep-foreign --remove-slave python3 %{_bindir}/python3.9 \ + alternatives --remove-slave python3 %{_bindir}/python3.9 \ python3-config fi @@ -1445,9 +1385,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 --keep-foreign --remove-slave python3 %{_bindir}/python3.9 \ + alternatives --remove-slave python3 %{_bindir}/python3.9 \ python3-debug - alternatives --keep-foreign --remove-slave python3 %{_bindir}/python3.9 \ + alternatives --remove-slave python3 %{_bindir}/python3.9 \ python3-debug-config fi @@ -1461,7 +1401,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 --keep-foreign --remove-slave python3 %{_bindir}/python3.9 \ + alternatives --remove-slave python3 %{_bindir}/python3.9 \ idle3 fi @@ -1570,6 +1510,7 @@ 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 @@ -1862,6 +1803,7 @@ 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 @@ -1992,43 +1934,6 @@ fi # ====================================================== %changelog -* 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