]> granicus.if.org Git - python/commitdiff
bpo-38270: More fixes for strict crypto policy (GH-16418)
authorChristian Heimes <christian@python.org>
Fri, 27 Sep 2019 13:03:53 +0000 (15:03 +0200)
committerMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Fri, 27 Sep 2019 13:03:53 +0000 (06:03 -0700)
test_hmac and test_hashlib test built-in hashing implementations and
OpenSSL-based hashing implementations. Add more checks to skip OpenSSL
implementations when a strict crypto policy is active.

Use EVP_DigestInit_ex() instead of EVP_DigestInit() to initialize the
EVP context. The EVP_DigestInit() function clears alls flags and breaks
usedforsecurity flag again.

Signed-off-by: Christian Heimes <christian@python.org>
https://bugs.python.org/issue38270

Lib/test/support/__init__.py
Lib/test/test_hashlib.py
Lib/test/test_hmac.py
Modules/_hashopenssl.c

index e401090c2146817922fc36e86d55723b07d9ffc2..d593fc18d96e1745e4c33a96b7a0bc6454918c96 100644 (file)
@@ -69,6 +69,11 @@ try:
 except ImportError:
     resource = None
 
+try:
+    import _hashlib
+except ImportError:
+    _hashlib = None
+
 __all__ = [
     # globals
     "PIPE_MAX_SIZE", "verbose", "max_memuse", "use_resources", "failfast",
@@ -86,8 +91,8 @@ __all__ = [
     "create_empty_file", "can_symlink", "fs_is_case_insensitive",
     # unittest
     "is_resource_enabled", "requires", "requires_freebsd_version",
-    "requires_linux_version", "requires_mac_ver", "check_syntax_error",
-    "check_syntax_warning",
+    "requires_linux_version", "requires_mac_ver", "requires_hashdigest",
+    "check_syntax_error", "check_syntax_warning",
     "TransientResource", "time_out", "socket_peer_reset", "ioerror_peer_reset",
     "transient_internet", "BasicTestRunner", "run_unittest", "run_doctest",
     "skip_unless_symlink", "requires_gzip", "requires_bz2", "requires_lzma",
@@ -649,12 +654,16 @@ def requires_mac_ver(*min_version):
     return decorator
 
 
-def requires_hashdigest(digestname):
+def requires_hashdigest(digestname, openssl=None, usedforsecurity=True):
     """Decorator raising SkipTest if a hashing algorithm is not available
 
     The hashing algorithm could be missing or blocked by a strict crypto
     policy.
 
+    If 'openssl' is True, then the decorator checks that OpenSSL provides
+    the algorithm. Otherwise the check falls back to built-in
+    implementations. The usedforsecurity flag is passed to the constructor.
+
     ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS
     ValueError: unsupported hash type md4
     """
@@ -662,7 +671,10 @@ def requires_hashdigest(digestname):
         @functools.wraps(func)
         def wrapper(*args, **kwargs):
             try:
-                hashlib.new(digestname)
+                if openssl and _hashlib is not None:
+                    _hashlib.new(digestname, usedforsecurity=usedforsecurity)
+                else:
+                    hashlib.new(digestname, usedforsecurity=usedforsecurity)
             except ValueError:
                 raise unittest.SkipTest(
                     f"hash digest '{digestname}' is not available."
index d55de02f91645deadba1321b37b9c2b416cf66e8..0e30b2fb11f29c2267bc2e09f1e77c294cd49e96 100644 (file)
@@ -8,6 +8,7 @@
 
 import array
 from binascii import unhexlify
+import functools
 import hashlib
 import importlib
 import itertools
@@ -18,6 +19,7 @@ import unittest
 import warnings
 from test import support
 from test.support import _4G, bigmemtest, import_fresh_module
+from test.support import requires_hashdigest
 from http.client import HTTPException
 
 # Were we compiled --with-pydebug or with #define Py_DEBUG?
@@ -119,6 +121,7 @@ class HashLibTestCase(unittest.TestCase):
             constructors.add(_test_algorithm_via_hashlib_new)
 
         _hashlib = self._conditional_import_module('_hashlib')
+        self._hashlib = _hashlib
         if _hashlib:
             # These two algorithms should always be present when this module
             # is compiled.  If not, something was compiled wrong.
@@ -127,7 +130,13 @@ class HashLibTestCase(unittest.TestCase):
             for algorithm, constructors in self.constructors_to_test.items():
                 constructor = getattr(_hashlib, 'openssl_'+algorithm, None)
                 if constructor:
-                    constructors.add(constructor)
+                    try:
+                        constructor()
+                    except ValueError:
+                        # default constructor blocked by crypto policy
+                        pass
+                    else:
+                        constructors.add(constructor)
 
         def add_builtin_constructor(name):
             constructor = getattr(hashlib, "__get_builtin_constructor")(name)
@@ -193,6 +202,9 @@ class HashLibTestCase(unittest.TestCase):
             cons(b'', usedforsecurity=False)
         hashlib.new("sha256", usedforsecurity=True)
         hashlib.new("sha256", usedforsecurity=False)
+        if self._hashlib is not None:
+            self._hashlib.new("md5", usedforsecurity=False)
+            self._hashlib.openssl_md5(usedforsecurity=False)
 
     def test_unknown_hash(self):
         self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
index 2c09de8b26fa07aa6e3ed96abdbba132d91fcc1f..1bbf201727d7a844db7b09112270ee029fc0f97d 100644 (file)
@@ -21,7 +21,7 @@ def ignore_warning(func):
 
 class TestVectorsTestCase(unittest.TestCase):
 
-    @requires_hashdigest('md5')
+    @requires_hashdigest('md5', openssl=True)
     def test_md5_vectors(self):
         # Test the HMAC module against test vectors from the RFC.
 
@@ -79,7 +79,7 @@ class TestVectorsTestCase(unittest.TestCase):
                  b"and Larger Than One Block-Size Data"),
                 "6f630fad67cda0ee1fb1f562db3aa53e")
 
-    @requires_hashdigest('sha1')
+    @requires_hashdigest('sha1', openssl=True)
     def test_sha_vectors(self):
         def shatest(key, data, digest):
             h = hmac.HMAC(key, data, digestmod=hashlib.sha1)
@@ -272,19 +272,19 @@ class TestVectorsTestCase(unittest.TestCase):
                                    '134676fb6de0446065c97440fa8c6a58',
                  })
 
-    @requires_hashdigest('sha224')
+    @requires_hashdigest('sha224', openssl=True)
     def test_sha224_rfc4231(self):
         self._rfc4231_test_cases(hashlib.sha224, 'sha224', 28, 64)
 
-    @requires_hashdigest('sha256')
+    @requires_hashdigest('sha256', openssl=True)
     def test_sha256_rfc4231(self):
         self._rfc4231_test_cases(hashlib.sha256, 'sha256', 32, 64)
 
-    @requires_hashdigest('sha384')
+    @requires_hashdigest('sha384', openssl=True)
     def test_sha384_rfc4231(self):
         self._rfc4231_test_cases(hashlib.sha384, 'sha384', 48, 128)
 
-    @requires_hashdigest('sha512')
+    @requires_hashdigest('sha512', openssl=True)
     def test_sha512_rfc4231(self):
         self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)
 
index 48511f72bc635002861d999d01040060ee96ed76..b147dbe8b3c55da1332b6750c5963d95ceeb8eec 100644 (file)
@@ -552,7 +552,7 @@ EVPnew(const EVP_MD *digest,
     }
 
 
-    if (!EVP_DigestInit(self->ctx, digest)) {
+    if (!EVP_DigestInit_ex(self->ctx, digest, NULL)) {
         _setException(PyExc_ValueError);
         Py_DECREF(self);
         return NULL;