5572 lines
194 KiB
Diff
5572 lines
194 KiB
Diff
From 53d108c5103c99079f1f231b0e731f3f47a142fc Mon Sep 17 00:00:00 2001
|
|
From: "Miss Islington (bot)"
|
|
<31488909+miss-islington@users.noreply.github.com>
|
|
Date: Wed, 25 Sep 2019 08:50:31 -0700
|
|
Subject: [PATCH 01/36] [3.8] bpo-38270: Check for hash digest algorithms and
|
|
avoid MD5 (GH-16382) (GH-16393)
|
|
|
|
Make it easier to run and test Python on systems with restrict crypto policies:
|
|
|
|
* add requires_hashdigest to test.support to check if a hash digest algorithm is available and working
|
|
* avoid MD5 in test_hmac
|
|
* replace MD5 with SHA256 in test_tarfile
|
|
* mark network tests that require MD5 for MD5-based digest auth or CRAM-MD5
|
|
|
|
https://bugs.python.org/issue38270
|
|
(cherry picked from commit c64a1a61e6fc542cada40eb069a239317e1af36e)
|
|
|
|
Co-authored-by: Christian Heimes <christian@python.org>
|
|
|
|
https://bugs.python.org/issue38270
|
|
|
|
Automerge-Triggered-By: @tiran
|
|
---
|
|
Lib/test/support/__init__.py | 22 ++++++++++
|
|
Lib/test/test_hmac.py | 67 +++++++++++++++++++------------
|
|
Lib/test/test_imaplib.py | 6 ++-
|
|
Lib/test/test_poplib.py | 2 +
|
|
Lib/test/test_smtplib.py | 11 ++++-
|
|
Lib/test/test_tarfile.py | 56 ++++++++++++++------------
|
|
Lib/test/test_urllib2_localnet.py | 1 +
|
|
7 files changed, 112 insertions(+), 53 deletions(-)
|
|
|
|
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
|
|
index 66c0fed8411..a43819904e0 100644
|
|
--- a/Lib/test/support/__init__.py
|
|
+++ b/Lib/test/support/__init__.py
|
|
@@ -11,6 +11,7 @@ import faulthandler
|
|
import fnmatch
|
|
import functools
|
|
import gc
|
|
+import hashlib
|
|
import importlib
|
|
import importlib.util
|
|
import io
|
|
@@ -627,6 +628,27 @@ def requires_mac_ver(*min_version):
|
|
return wrapper
|
|
return decorator
|
|
|
|
+def requires_hashdigest(digestname):
|
|
+ """Decorator raising SkipTest if a hashing algorithm is not available
|
|
+
|
|
+ The hashing algorithm could be missing or blocked by a strict crypto
|
|
+ policy.
|
|
+
|
|
+ ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS
|
|
+ ValueError: unsupported hash type md4
|
|
+ """
|
|
+ def decorator(func):
|
|
+ @functools.wraps(func)
|
|
+ def wrapper(*args, **kwargs):
|
|
+ try:
|
|
+ hashlib.new(digestname)
|
|
+ except ValueError:
|
|
+ raise unittest.SkipTest(
|
|
+ f"hash digest '{digestname}' is not available."
|
|
+ )
|
|
+ return func(*args, **kwargs)
|
|
+ return wrapper
|
|
+ return decorator
|
|
|
|
# Don't use "localhost", since resolving it uses the DNS under recent
|
|
# Windows versions (see issue #18792).
|
|
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
|
|
index 067e13f1079..81c3485f761 100644
|
|
--- a/Lib/test/test_hmac.py
|
|
+++ b/Lib/test/test_hmac.py
|
|
@@ -4,6 +4,8 @@ import hashlib
|
|
import unittest
|
|
import warnings
|
|
|
|
+from test.support import requires_hashdigest
|
|
+
|
|
|
|
def ignore_warning(func):
|
|
@functools.wraps(func)
|
|
@@ -17,6 +19,7 @@ def ignore_warning(func):
|
|
|
|
class TestVectorsTestCase(unittest.TestCase):
|
|
|
|
+ @requires_hashdigest('md5')
|
|
def test_md5_vectors(self):
|
|
# Test the HMAC module against test vectors from the RFC.
|
|
|
|
@@ -63,6 +66,7 @@ class TestVectorsTestCase(unittest.TestCase):
|
|
b"and Larger Than One Block-Size Data"),
|
|
"6f630fad67cda0ee1fb1f562db3aa53e")
|
|
|
|
+ @requires_hashdigest('sha1')
|
|
def test_sha_vectors(self):
|
|
def shatest(key, data, digest):
|
|
h = hmac.HMAC(key, data, digestmod=hashlib.sha1)
|
|
@@ -230,23 +234,28 @@ class TestVectorsTestCase(unittest.TestCase):
|
|
'134676fb6de0446065c97440fa8c6a58',
|
|
})
|
|
|
|
+ @requires_hashdigest('sha224')
|
|
def test_sha224_rfc4231(self):
|
|
self._rfc4231_test_cases(hashlib.sha224, 'sha224', 28, 64)
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_sha256_rfc4231(self):
|
|
self._rfc4231_test_cases(hashlib.sha256, 'sha256', 32, 64)
|
|
|
|
+ @requires_hashdigest('sha384')
|
|
def test_sha384_rfc4231(self):
|
|
self._rfc4231_test_cases(hashlib.sha384, 'sha384', 48, 128)
|
|
|
|
+ @requires_hashdigest('sha512')
|
|
def test_sha512_rfc4231(self):
|
|
self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_legacy_block_size_warnings(self):
|
|
class MockCrazyHash(object):
|
|
"""Ain't no block_size attribute here."""
|
|
def __init__(self, *args):
|
|
- self._x = hashlib.sha1(*args)
|
|
+ self._x = hashlib.sha256(*args)
|
|
self.digest_size = self._x.digest_size
|
|
def update(self, v):
|
|
self._x.update(v)
|
|
@@ -273,76 +282,80 @@ class TestVectorsTestCase(unittest.TestCase):
|
|
self.assertEqual(h.hexdigest().upper(), digest)
|
|
|
|
|
|
+
|
|
class ConstructorTestCase(unittest.TestCase):
|
|
|
|
+ expected = (
|
|
+ "6c845b47f52b3b47f6590c502db7825aad757bf4fadc8fa972f7cd2e76a5bdeb"
|
|
+ )
|
|
@ignore_warning
|
|
+ @requires_hashdigest('sha256')
|
|
def test_normal(self):
|
|
# Standard constructor call.
|
|
- failed = 0
|
|
try:
|
|
- h = hmac.HMAC(b"key")
|
|
+ hmac.HMAC(b"key", digestmod='sha256')
|
|
except Exception:
|
|
self.fail("Standard constructor call raised exception.")
|
|
|
|
@ignore_warning
|
|
+ @requires_hashdigest('sha256')
|
|
def test_with_str_key(self):
|
|
# Pass a key of type str, which is an error, because it expects a key
|
|
# of type bytes
|
|
with self.assertRaises(TypeError):
|
|
- h = hmac.HMAC("key")
|
|
+ h = hmac.HMAC("key", digestmod='sha256')
|
|
|
|
- @ignore_warning
|
|
+ @requires_hashdigest('sha256')
|
|
def test_dot_new_with_str_key(self):
|
|
# Pass a key of type str, which is an error, because it expects a key
|
|
# of type bytes
|
|
with self.assertRaises(TypeError):
|
|
- h = hmac.new("key")
|
|
+ h = hmac.HMAC("key", digestmod='sha256')
|
|
|
|
@ignore_warning
|
|
+ @requires_hashdigest('sha256')
|
|
def test_withtext(self):
|
|
# Constructor call with text.
|
|
try:
|
|
- h = hmac.HMAC(b"key", b"hash this!")
|
|
+ h = hmac.HMAC(b"key", b"hash this!", digestmod='sha256')
|
|
except Exception:
|
|
self.fail("Constructor call with text argument raised exception.")
|
|
- self.assertEqual(h.hexdigest(), '34325b639da4cfd95735b381e28cb864')
|
|
+ self.assertEqual(h.hexdigest(), self.expected)
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_with_bytearray(self):
|
|
try:
|
|
h = hmac.HMAC(bytearray(b"key"), bytearray(b"hash this!"),
|
|
- digestmod="md5")
|
|
+ digestmod="sha256")
|
|
except Exception:
|
|
self.fail("Constructor call with bytearray arguments raised exception.")
|
|
- self.assertEqual(h.hexdigest(), '34325b639da4cfd95735b381e28cb864')
|
|
+ self.assertEqual(h.hexdigest(), self.expected)
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_with_memoryview_msg(self):
|
|
try:
|
|
- h = hmac.HMAC(b"key", memoryview(b"hash this!"), digestmod="md5")
|
|
+ h = hmac.HMAC(b"key", memoryview(b"hash this!"), digestmod="sha256")
|
|
except Exception:
|
|
self.fail("Constructor call with memoryview msg raised exception.")
|
|
- self.assertEqual(h.hexdigest(), '34325b639da4cfd95735b381e28cb864')
|
|
+ self.assertEqual(h.hexdigest(), self.expected)
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_withmodule(self):
|
|
# Constructor call with text and digest module.
|
|
try:
|
|
- h = hmac.HMAC(b"key", b"", hashlib.sha1)
|
|
+ h = hmac.HMAC(b"key", b"", hashlib.sha256)
|
|
except Exception:
|
|
- self.fail("Constructor call with hashlib.sha1 raised exception.")
|
|
+ self.fail("Constructor call with hashlib.sha256 raised exception.")
|
|
|
|
-class SanityTestCase(unittest.TestCase):
|
|
|
|
- @ignore_warning
|
|
- def test_default_is_md5(self):
|
|
- # Testing if HMAC defaults to MD5 algorithm.
|
|
- # NOTE: this whitebox test depends on the hmac class internals
|
|
- h = hmac.HMAC(b"key")
|
|
- self.assertEqual(h.digest_cons, hashlib.md5)
|
|
+class SanityTestCase(unittest.TestCase):
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_exercise_all_methods(self):
|
|
# Exercising all methods once.
|
|
# This must not raise any exceptions
|
|
try:
|
|
- h = hmac.HMAC(b"my secret key", digestmod="md5")
|
|
+ h = hmac.HMAC(b"my secret key", digestmod="sha256")
|
|
h.update(b"compute the hash of this text!")
|
|
dig = h.digest()
|
|
dig = h.hexdigest()
|
|
@@ -350,11 +363,13 @@ class SanityTestCase(unittest.TestCase):
|
|
except Exception:
|
|
self.fail("Exception raised during normal usage of HMAC class.")
|
|
|
|
+
|
|
class CopyTestCase(unittest.TestCase):
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_attributes(self):
|
|
# Testing if attributes are of same type.
|
|
- h1 = hmac.HMAC(b"key", digestmod="md5")
|
|
+ h1 = hmac.HMAC(b"key", digestmod="sha256")
|
|
h2 = h1.copy()
|
|
self.assertTrue(h1.digest_cons == h2.digest_cons,
|
|
"digest constructors don't match.")
|
|
@@ -363,9 +378,10 @@ class CopyTestCase(unittest.TestCase):
|
|
self.assertEqual(type(h1.outer), type(h2.outer),
|
|
"Types of outer don't match.")
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_realcopy(self):
|
|
# Testing if the copy method created a real copy.
|
|
- h1 = hmac.HMAC(b"key", digestmod="md5")
|
|
+ h1 = hmac.HMAC(b"key", 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.")
|
|
@@ -374,9 +390,10 @@ class CopyTestCase(unittest.TestCase):
|
|
self.assertTrue(id(h1.outer) != id(h2.outer),
|
|
"No real copy of the attribute 'outer'.")
|
|
|
|
+ @requires_hashdigest('sha256')
|
|
def test_equality(self):
|
|
# Testing if the copy has the same digests.
|
|
- h1 = hmac.HMAC(b"key", digestmod="md5")
|
|
+ h1 = hmac.HMAC(b"key", digestmod="sha256")
|
|
h1.update(b"some random text")
|
|
h2 = h1.copy()
|
|
self.assertEqual(h1.digest(), h2.digest(),
|
|
diff --git a/Lib/test/test_imaplib.py b/Lib/test/test_imaplib.py
|
|
index 0593a3756b0..086cc0ac4e0 100644
|
|
--- a/Lib/test/test_imaplib.py
|
|
+++ b/Lib/test/test_imaplib.py
|
|
@@ -14,7 +14,8 @@ import calendar
|
|
import inspect
|
|
|
|
from test.support import (reap_threads, verbose, transient_internet,
|
|
- run_with_tz, run_with_locale, cpython_only)
|
|
+ run_with_tz, run_with_locale, cpython_only,
|
|
+ requires_hashdigest)
|
|
import unittest
|
|
from unittest import mock
|
|
from datetime import datetime, timezone, timedelta
|
|
@@ -369,6 +370,7 @@ class NewIMAPTestsMixin():
|
|
self.assertEqual(code, 'OK')
|
|
self.assertEqual(server.response, b'ZmFrZQ==\r\n') # b64 encoded 'fake'
|
|
|
|
+ @requires_hashdigest('md5')
|
|
def test_login_cram_md5_bytes(self):
|
|
class AuthHandler(SimpleIMAPHandler):
|
|
capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
|
|
@@ -386,6 +388,7 @@ class NewIMAPTestsMixin():
|
|
ret, _ = client.login_cram_md5("tim", b"tanstaaftanstaaf")
|
|
self.assertEqual(ret, "OK")
|
|
|
|
+ @requires_hashdigest('md5')
|
|
def test_login_cram_md5_plain_text(self):
|
|
class AuthHandler(SimpleIMAPHandler):
|
|
capabilities = 'LOGINDISABLED AUTH=CRAM-MD5'
|
|
@@ -797,6 +800,7 @@ class ThreadedNetworkedTests(unittest.TestCase):
|
|
b'ZmFrZQ==\r\n') # b64 encoded 'fake'
|
|
|
|
@reap_threads
|
|
+ @requires_hashdigest('md5')
|
|
def test_login_cram_md5(self):
|
|
|
|
class AuthHandler(SimpleIMAPHandler):
|
|
diff --git a/Lib/test/test_poplib.py b/Lib/test/test_poplib.py
|
|
index 234c855545c..b8146be3a8c 100644
|
|
--- a/Lib/test/test_poplib.py
|
|
+++ b/Lib/test/test_poplib.py
|
|
@@ -304,9 +304,11 @@ class TestPOP3Class(TestCase):
|
|
def test_rpop(self):
|
|
self.assertOK(self.client.rpop('foo'))
|
|
|
|
+ @test_support.requires_hashdigest('md5')
|
|
def test_apop_normal(self):
|
|
self.assertOK(self.client.apop('foo', 'dummypassword'))
|
|
|
|
+ @test_support.requires_hashdigest('md5')
|
|
def test_apop_REDOS(self):
|
|
# Replace welcome with very long evil welcome.
|
|
# NB The upper bound on welcome length is currently 2048.
|
|
diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py
|
|
index 87047514e7a..64b3201254a 100644
|
|
--- a/Lib/test/test_smtplib.py
|
|
+++ b/Lib/test/test_smtplib.py
|
|
@@ -4,6 +4,7 @@ import email.mime.text
|
|
from email.message import EmailMessage
|
|
from email.base64mime import body_encode as encode_base64
|
|
import email.utils
|
|
+import hashlib
|
|
import hmac
|
|
import socket
|
|
import smtpd
|
|
@@ -18,6 +19,7 @@ import textwrap
|
|
|
|
import unittest
|
|
from test import support, mock_socket
|
|
+from test.support import requires_hashdigest
|
|
from unittest.mock import Mock
|
|
|
|
HOST = "localhost"
|
|
@@ -968,6 +970,7 @@ class SMTPSimTests(unittest.TestCase):
|
|
self.assertEqual(resp, (235, b'Authentication Succeeded'))
|
|
smtp.close()
|
|
|
|
+ @requires_hashdigest('md5')
|
|
def testAUTH_CRAM_MD5(self):
|
|
self.serv.add_feature("AUTH CRAM-MD5")
|
|
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15)
|
|
@@ -984,7 +987,13 @@ class SMTPSimTests(unittest.TestCase):
|
|
smtp.close()
|
|
|
|
def test_auth_function(self):
|
|
- supported = {'CRAM-MD5', 'PLAIN', 'LOGIN'}
|
|
+ supported = {'PLAIN', 'LOGIN'}
|
|
+ try:
|
|
+ hashlib.md5()
|
|
+ except ValueError:
|
|
+ pass
|
|
+ else:
|
|
+ supported.add('CRAM-MD5')
|
|
for mechanism in supported:
|
|
self.serv.add_feature("AUTH {}".format(mechanism))
|
|
for mechanism in supported:
|
|
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
|
|
index 4cd7d5370f5..b5e855e0d7e 100644
|
|
--- a/Lib/test/test_tarfile.py
|
|
+++ b/Lib/test/test_tarfile.py
|
|
@@ -1,7 +1,7 @@
|
|
import sys
|
|
import os
|
|
import io
|
|
-from hashlib import md5
|
|
+from hashlib import sha256
|
|
from contextlib import contextmanager
|
|
from random import Random
|
|
import pathlib
|
|
@@ -11,7 +11,7 @@ import unittest.mock
|
|
import tarfile
|
|
|
|
from test import support
|
|
-from test.support import script_helper
|
|
+from test.support import script_helper, requires_hashdigest
|
|
|
|
# Check for our compression modules.
|
|
try:
|
|
@@ -27,8 +27,8 @@ try:
|
|
except ImportError:
|
|
lzma = None
|
|
|
|
-def md5sum(data):
|
|
- return md5(data).hexdigest()
|
|
+def sha256sum(data):
|
|
+ return sha256(data).hexdigest()
|
|
|
|
TEMPDIR = os.path.abspath(support.TESTFN) + "-tardir"
|
|
tarextdir = TEMPDIR + '-extract-test'
|
|
@@ -39,8 +39,12 @@ xzname = os.path.join(TEMPDIR, "testtar.tar.xz")
|
|
tmpname = os.path.join(TEMPDIR, "tmp.tar")
|
|
dotlessname = os.path.join(TEMPDIR, "testtar")
|
|
|
|
-md5_regtype = "65f477c818ad9e15f7feab0c6d37742f"
|
|
-md5_sparse = "a54fbc4ca4f4399a90e1b27164012fc6"
|
|
+sha256_regtype = (
|
|
+ "e09e4bc8b3c9d9177e77256353b36c159f5f040531bbd4b024a8f9b9196c71ce"
|
|
+)
|
|
+sha256_sparse = (
|
|
+ "4f05a776071146756345ceee937b33fc5644f5a96b9780d1c7d6a32cdf164d7b"
|
|
+)
|
|
|
|
|
|
class TarTest:
|
|
@@ -95,7 +99,7 @@ class UstarReadTest(ReadTest, unittest.TestCase):
|
|
data = fobj.read()
|
|
self.assertEqual(len(data), tarinfo.size,
|
|
"regular file extraction failed")
|
|
- self.assertEqual(md5sum(data), md5_regtype,
|
|
+ self.assertEqual(sha256sum(data), sha256_regtype,
|
|
"regular file extraction failed")
|
|
|
|
def test_fileobj_readlines(self):
|
|
@@ -180,7 +184,7 @@ class UstarReadTest(ReadTest, unittest.TestCase):
|
|
with self.tar.extractfile("ustar/regtype") as fobj:
|
|
fobj = io.TextIOWrapper(fobj)
|
|
data = fobj.read().encode("iso8859-1")
|
|
- self.assertEqual(md5sum(data), md5_regtype)
|
|
+ self.assertEqual(sha256sum(data), sha256_regtype)
|
|
try:
|
|
fobj.seek(100)
|
|
except AttributeError:
|
|
@@ -546,13 +550,13 @@ class MiscReadTestBase(CommonReadTest):
|
|
self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/lnktype"))
|
|
with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f:
|
|
data = f.read()
|
|
- self.assertEqual(md5sum(data), md5_regtype)
|
|
+ self.assertEqual(sha256sum(data), sha256_regtype)
|
|
|
|
tar.extract("ustar/symtype", TEMPDIR)
|
|
self.addCleanup(support.unlink, os.path.join(TEMPDIR, "ustar/symtype"))
|
|
with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f:
|
|
data = f.read()
|
|
- self.assertEqual(md5sum(data), md5_regtype)
|
|
+ self.assertEqual(sha256sum(data), sha256_regtype)
|
|
|
|
def test_extractall(self):
|
|
# Test if extractall() correctly restores directory permissions
|
|
@@ -687,7 +691,7 @@ class StreamReadTest(CommonReadTest, unittest.TestCase):
|
|
data = fobj.read()
|
|
self.assertEqual(len(data), tarinfo.size,
|
|
"regular file extraction failed")
|
|
- self.assertEqual(md5sum(data), md5_regtype,
|
|
+ self.assertEqual(sha256sum(data), sha256_regtype,
|
|
"regular file extraction failed")
|
|
|
|
def test_provoke_stream_error(self):
|
|
@@ -799,8 +803,8 @@ class MemberReadTest(ReadTest, unittest.TestCase):
|
|
def _test_member(self, tarinfo, chksum=None, **kwargs):
|
|
if chksum is not None:
|
|
with self.tar.extractfile(tarinfo) as f:
|
|
- self.assertEqual(md5sum(f.read()), chksum,
|
|
- "wrong md5sum for %s" % tarinfo.name)
|
|
+ self.assertEqual(sha256sum(f.read()), chksum,
|
|
+ "wrong sha256sum for %s" % tarinfo.name)
|
|
|
|
kwargs["mtime"] = 0o7606136617
|
|
kwargs["uid"] = 1000
|
|
@@ -815,11 +819,11 @@ class MemberReadTest(ReadTest, unittest.TestCase):
|
|
|
|
def test_find_regtype(self):
|
|
tarinfo = self.tar.getmember("ustar/regtype")
|
|
- self._test_member(tarinfo, size=7011, chksum=md5_regtype)
|
|
+ self._test_member(tarinfo, size=7011, chksum=sha256_regtype)
|
|
|
|
def test_find_conttype(self):
|
|
tarinfo = self.tar.getmember("ustar/conttype")
|
|
- self._test_member(tarinfo, size=7011, chksum=md5_regtype)
|
|
+ self._test_member(tarinfo, size=7011, chksum=sha256_regtype)
|
|
|
|
def test_find_dirtype(self):
|
|
tarinfo = self.tar.getmember("ustar/dirtype")
|
|
@@ -851,28 +855,28 @@ class MemberReadTest(ReadTest, unittest.TestCase):
|
|
|
|
def test_find_sparse(self):
|
|
tarinfo = self.tar.getmember("ustar/sparse")
|
|
- self._test_member(tarinfo, size=86016, chksum=md5_sparse)
|
|
+ self._test_member(tarinfo, size=86016, chksum=sha256_sparse)
|
|
|
|
def test_find_gnusparse(self):
|
|
tarinfo = self.tar.getmember("gnu/sparse")
|
|
- self._test_member(tarinfo, size=86016, chksum=md5_sparse)
|
|
+ self._test_member(tarinfo, size=86016, chksum=sha256_sparse)
|
|
|
|
def test_find_gnusparse_00(self):
|
|
tarinfo = self.tar.getmember("gnu/sparse-0.0")
|
|
- self._test_member(tarinfo, size=86016, chksum=md5_sparse)
|
|
+ self._test_member(tarinfo, size=86016, chksum=sha256_sparse)
|
|
|
|
def test_find_gnusparse_01(self):
|
|
tarinfo = self.tar.getmember("gnu/sparse-0.1")
|
|
- self._test_member(tarinfo, size=86016, chksum=md5_sparse)
|
|
+ self._test_member(tarinfo, size=86016, chksum=sha256_sparse)
|
|
|
|
def test_find_gnusparse_10(self):
|
|
tarinfo = self.tar.getmember("gnu/sparse-1.0")
|
|
- self._test_member(tarinfo, size=86016, chksum=md5_sparse)
|
|
+ self._test_member(tarinfo, size=86016, chksum=sha256_sparse)
|
|
|
|
def test_find_umlauts(self):
|
|
tarinfo = self.tar.getmember("ustar/umlauts-"
|
|
"\xc4\xd6\xdc\xe4\xf6\xfc\xdf")
|
|
- self._test_member(tarinfo, size=7011, chksum=md5_regtype)
|
|
+ self._test_member(tarinfo, size=7011, chksum=sha256_regtype)
|
|
|
|
def test_find_ustar_longname(self):
|
|
name = "ustar/" + "12345/" * 39 + "1234567/longname"
|
|
@@ -880,7 +884,7 @@ class MemberReadTest(ReadTest, unittest.TestCase):
|
|
|
|
def test_find_regtype_oldv7(self):
|
|
tarinfo = self.tar.getmember("misc/regtype-old-v7")
|
|
- self._test_member(tarinfo, size=7011, chksum=md5_regtype)
|
|
+ self._test_member(tarinfo, size=7011, chksum=sha256_regtype)
|
|
|
|
def test_find_pax_umlauts(self):
|
|
self.tar.close()
|
|
@@ -888,7 +892,7 @@ class MemberReadTest(ReadTest, unittest.TestCase):
|
|
encoding="iso8859-1")
|
|
tarinfo = self.tar.getmember("pax/umlauts-"
|
|
"\xc4\xd6\xdc\xe4\xf6\xfc\xdf")
|
|
- self._test_member(tarinfo, size=7011, chksum=md5_regtype)
|
|
+ self._test_member(tarinfo, size=7011, chksum=sha256_regtype)
|
|
|
|
|
|
class LongnameTest:
|
|
@@ -950,8 +954,8 @@ class GNUReadTest(LongnameTest, ReadTest, unittest.TestCase):
|
|
filename = os.path.join(TEMPDIR, name)
|
|
with open(filename, "rb") as fobj:
|
|
data = fobj.read()
|
|
- self.assertEqual(md5sum(data), md5_sparse,
|
|
- "wrong md5sum for %s" % name)
|
|
+ self.assertEqual(sha256sum(data), sha256_sparse,
|
|
+ "wrong sha256sum for %s" % name)
|
|
|
|
if self._fs_supports_holes():
|
|
s = os.stat(filename)
|
|
@@ -2431,7 +2435,7 @@ class LinkEmulationTest(ReadTest, unittest.TestCase):
|
|
self.tar.extract(name, TEMPDIR)
|
|
with open(os.path.join(TEMPDIR, name), "rb") as f:
|
|
data = f.read()
|
|
- self.assertEqual(md5sum(data), md5_regtype)
|
|
+ self.assertEqual(sha256sum(data), sha256_regtype)
|
|
|
|
# See issues #1578269, #8879, and #17689 for some history on these skips
|
|
@unittest.skipIf(hasattr(os.path, "islink"),
|
|
diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py
|
|
index ef0091c4930..895f97cc09a 100644
|
|
--- a/Lib/test/test_urllib2_localnet.py
|
|
+++ b/Lib/test/test_urllib2_localnet.py
|
|
@@ -325,6 +325,7 @@ class ProxyAuthTests(unittest.TestCase):
|
|
PASSWD = "test123"
|
|
REALM = "TestRealm"
|
|
|
|
+ @support.requires_hashdigest("md5")
|
|
def setUp(self):
|
|
super(ProxyAuthTests, self).setUp()
|
|
# Ignore proxy bypass settings in the environment.
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 75d9613294e1cbd5aab9363cf3c435871a25f065 Mon Sep 17 00:00:00 2001
|
|
From: Christian Heimes <christian@python.org>
|
|
Date: Mon, 30 Sep 2019 09:10:38 +0200
|
|
Subject: [PATCH 02/36] [3.8] bpo-38270: More fixes for strict crypto policy
|
|
(GH-16418) (#16437)
|
|
|
|
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.
|
|
(cherry picked from commit 90558158093c0ad893102158fd3c2dd9f864e82e)
|
|
|
|
Co-authored-by: Christian Heimes <christian@python.org>
|
|
---
|
|
Lib/test/support/__init__.py | 19 ++++++++++++++++---
|
|
Lib/test/test_hashlib.py | 3 +++
|
|
Lib/test/test_hmac.py | 12 ++++++------
|
|
3 files changed, 25 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
|
|
index a43819904e0..3342218cf0a 100644
|
|
--- a/Lib/test/support/__init__.py
|
|
+++ b/Lib/test/support/__init__.py
|
|
@@ -71,6 +71,11 @@ try:
|
|
except ImportError:
|
|
resource = None
|
|
|
|
+try:
|
|
+ import _hashlib
|
|
+except ImportError:
|
|
+ _hashlib = None
|
|
+
|
|
__all__ = [
|
|
# globals
|
|
"PIPE_MAX_SIZE", "verbose", "max_memuse", "use_resources", "failfast",
|
|
@@ -88,7 +93,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",
|
|
+ "requires_linux_version", "requires_mac_ver", "requires_hashdigest",
|
|
+ "check_syntax_error",
|
|
"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",
|
|
@@ -628,12 +634,16 @@ def requires_mac_ver(*min_version):
|
|
return wrapper
|
|
return decorator
|
|
|
|
-def requires_hashdigest(digestname):
|
|
+def requires_hashdigest(digestname, openssl=None):
|
|
"""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.
|
|
+
|
|
ValueError: [digital envelope routines: EVP_DigestInit_ex] disabled for FIPS
|
|
ValueError: unsupported hash type md4
|
|
"""
|
|
@@ -641,7 +651,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)
|
|
+ else:
|
|
+ hashlib.new(digestname)
|
|
except ValueError:
|
|
raise unittest.SkipTest(
|
|
f"hash digest '{digestname}' is not available."
|
|
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
|
|
index 9711856853d..1b1b4d54112 100644
|
|
--- a/Lib/test/test_hashlib.py
|
|
+++ b/Lib/test/test_hashlib.py
|
|
@@ -8,6 +8,7 @@
|
|
|
|
import array
|
|
from binascii import unhexlify
|
|
+import functools
|
|
import hashlib
|
|
import importlib
|
|
import itertools
|
|
@@ -21,6 +22,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?
|
|
@@ -117,6 +119,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.
|
|
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
|
|
index 81c3485f761..2a5a0d3d061 100644
|
|
--- a/Lib/test/test_hmac.py
|
|
+++ b/Lib/test/test_hmac.py
|
|
@@ -19,7 +19,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.
|
|
|
|
@@ -66,7 +66,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)
|
|
@@ -234,19 +234,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)
|
|
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 19a40f7e820bb991c03e03c05dc832539b676983 Mon Sep 17 00:00:00 2001
|
|
From: stratakis <cstratak@redhat.com>
|
|
Date: Tue, 3 Dec 2019 16:35:54 +0100
|
|
Subject: [PATCH 03/36] bpo-38270: Fix indentation of test_hmac assertions
|
|
(GH-17446)
|
|
|
|
Since https://github.com/python/cpython/commit/c64a1a61e6fc542cada40eb069a239317e1af36e two assertions were indented and thus ignored when running test_hmac.
|
|
|
|
This PR fixes it. As the change is quite trivial I didn't add a NEWS entry.
|
|
|
|
|
|
https://bugs.python.org/issue38270
|
|
---
|
|
Lib/test/test_hmac.py | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
|
|
index 2a5a0d3d061..338e0215a41 100644
|
|
--- a/Lib/test/test_hmac.py
|
|
+++ b/Lib/test/test_hmac.py
|
|
@@ -329,7 +329,7 @@ class ConstructorTestCase(unittest.TestCase):
|
|
digestmod="sha256")
|
|
except Exception:
|
|
self.fail("Constructor call with bytearray arguments raised exception.")
|
|
- self.assertEqual(h.hexdigest(), self.expected)
|
|
+ self.assertEqual(h.hexdigest(), self.expected)
|
|
|
|
@requires_hashdigest('sha256')
|
|
def test_with_memoryview_msg(self):
|
|
@@ -337,7 +337,7 @@ class ConstructorTestCase(unittest.TestCase):
|
|
h = hmac.HMAC(b"key", memoryview(b"hash this!"), digestmod="sha256")
|
|
except Exception:
|
|
self.fail("Constructor call with memoryview msg raised exception.")
|
|
- self.assertEqual(h.hexdigest(), self.expected)
|
|
+ self.assertEqual(h.hexdigest(), self.expected)
|
|
|
|
@requires_hashdigest('sha256')
|
|
def test_withmodule(self):
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 5fa96205b380f2cb555ac346e6b7039746cb51ca Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Thu, 25 Jul 2019 16:19:52 +0200
|
|
Subject: [PATCH 04/36] Expose OpenSSL FIPS_mode() as hashlib.get_fips_mode()
|
|
|
|
---
|
|
Lib/hashlib.py | 5 +++++
|
|
Modules/_hashopenssl.c | 36 +++++++++++++++++++++++++++++++++
|
|
Modules/clinic/_hashopenssl.c.h | 25 ++++++++++++++++++++++-
|
|
3 files changed, 65 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
|
index 98d2d7981a3..ae17c585110 100644
|
|
--- a/Lib/hashlib.py
|
|
+++ b/Lib/hashlib.py
|
|
@@ -236,6 +236,11 @@ try:
|
|
except ImportError:
|
|
pass
|
|
|
|
+try:
|
|
+ from _hashlib import get_fips_mode
|
|
+except ImportError:
|
|
+ pass
|
|
+
|
|
|
|
for __func_name in __always_supported:
|
|
# try them all, some may not work due to the OpenSSL
|
|
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
|
index 84edd72c85a..4876a7f7aa7 100644
|
|
--- a/Modules/_hashopenssl.c
|
|
+++ b/Modules/_hashopenssl.c
|
|
@@ -25,6 +25,9 @@
|
|
#include <openssl/objects.h>
|
|
#include "openssl/err.h"
|
|
|
|
+/* Expose FIPS_mode */
|
|
+#include <openssl/crypto.h>
|
|
+
|
|
#include "clinic/_hashopenssl.c.h"
|
|
/*[clinic input]
|
|
module _hashlib
|
|
@@ -987,6 +990,38 @@ GEN_CONSTRUCTOR(sha256)
|
|
GEN_CONSTRUCTOR(sha384)
|
|
GEN_CONSTRUCTOR(sha512)
|
|
|
|
+/*[clinic input]
|
|
+_hashlib.get_fips_mode
|
|
+
|
|
+Determine the OpenSSL FIPS mode of operation.
|
|
+
|
|
+Effectively any non-zero return value indicates FIPS mode;
|
|
+values other than 1 may have additional significance.
|
|
+
|
|
+See OpenSSL documentation for the FIPS_mode() function for details.
|
|
+[clinic start generated code]*/
|
|
+
|
|
+static PyObject *
|
|
+_hashlib_get_fips_mode_impl(PyObject *module)
|
|
+/*[clinic end generated code: output=ad8a7793310d3f98 input=f42a2135df2a5e11]*/
|
|
+{
|
|
+ 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 NULL;
|
|
+ }
|
|
+ }
|
|
+ return PyLong_FromLong(result);
|
|
+}
|
|
+
|
|
+
|
|
/* List of functions exported by this module */
|
|
|
|
static struct PyMethodDef EVP_functions[] = {
|
|
@@ -996,6 +1031,7 @@ static struct PyMethodDef EVP_functions[] = {
|
|
pbkdf2_hmac__doc__},
|
|
#endif
|
|
_HASHLIB_SCRYPT_METHODDEF
|
|
+ _HASHLIB_GET_FIPS_MODE_METHODDEF
|
|
CONSTRUCTOR_METH_DEF(md5),
|
|
CONSTRUCTOR_METH_DEF(sha1),
|
|
CONSTRUCTOR_METH_DEF(sha224),
|
|
diff --git a/Modules/clinic/_hashopenssl.c.h b/Modules/clinic/_hashopenssl.c.h
|
|
index 04453526040..8828e2776e9 100644
|
|
--- a/Modules/clinic/_hashopenssl.c.h
|
|
+++ b/Modules/clinic/_hashopenssl.c.h
|
|
@@ -54,7 +54,30 @@ exit:
|
|
|
|
#endif /* (OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_SCRYPT) && !defined(LIBRESSL_VERSION_NUMBER)) */
|
|
|
|
+PyDoc_STRVAR(_hashlib_get_fips_mode__doc__,
|
|
+"get_fips_mode($module, /)\n"
|
|
+"--\n"
|
|
+"\n"
|
|
+"Determine the OpenSSL FIPS mode of operation.\n"
|
|
+"\n"
|
|
+"Effectively any non-zero return value indicates FIPS mode;\n"
|
|
+"values other than 1 may have additional significance.\n"
|
|
+"\n"
|
|
+"See OpenSSL documentation for the FIPS_mode() function for details.");
|
|
+
|
|
+#define _HASHLIB_GET_FIPS_MODE_METHODDEF \
|
|
+ {"get_fips_mode", (PyCFunction)_hashlib_get_fips_mode, METH_NOARGS, _hashlib_get_fips_mode__doc__},
|
|
+
|
|
+static PyObject *
|
|
+_hashlib_get_fips_mode_impl(PyObject *module);
|
|
+
|
|
+static PyObject *
|
|
+_hashlib_get_fips_mode(PyObject *module, PyObject *Py_UNUSED(ignored))
|
|
+{
|
|
+ return _hashlib_get_fips_mode_impl(module);
|
|
+}
|
|
+
|
|
#ifndef _HASHLIB_SCRYPT_METHODDEF
|
|
#define _HASHLIB_SCRYPT_METHODDEF
|
|
#endif /* !defined(_HASHLIB_SCRYPT_METHODDEF) */
|
|
-/*[clinic end generated code: output=118cd7036fa0fb52 input=a9049054013a1b77]*/
|
|
+/*[clinic end generated code: output=7d683c930bbb7c36 input=a9049054013a1b77]*/
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From e60cc93037b913ca2a410d73b52d8301ab0d76cd Mon Sep 17 00:00:00 2001
|
|
From: Charalampos Stratakis <cstratak@redhat.com>
|
|
Date: Thu, 25 Jul 2019 17:04:06 +0200
|
|
Subject: [PATCH 05/36] Use python's fall backs for the crypto it implements
|
|
only if we are not in FIPS mode
|
|
|
|
---
|
|
Lib/hashlib.py | 209 +++++++++++++++------------------------
|
|
Lib/test/test_hashlib.py | 1 +
|
|
2 files changed, 81 insertions(+), 129 deletions(-)
|
|
|
|
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
|
index ae17c585110..7db1e02601f 100644
|
|
--- a/Lib/hashlib.py
|
|
+++ b/Lib/hashlib.py
|
|
@@ -67,56 +67,64 @@ algorithms_available = set(__always_supported)
|
|
__all__ = __always_supported + ('new', 'algorithms_guaranteed',
|
|
'algorithms_available', 'pbkdf2_hmac')
|
|
|
|
-
|
|
-__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',
|
|
- 'shake_128', 'shake_256'}:
|
|
- 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
|
|
- cache['shake_128'] = _sha3.shake_128
|
|
- cache['shake_256'] = _sha3.shake_256
|
|
- except ImportError:
|
|
- pass # no extension module, this hash is unsupported.
|
|
-
|
|
- constructor = cache.get(name)
|
|
- if constructor is not None:
|
|
- return constructor
|
|
-
|
|
- raise ValueError('unsupported hash type ' + name)
|
|
+try:
|
|
+ from _hashlib import get_fips_mode
|
|
+except ImportError:
|
|
+ def get_fips_mode():
|
|
+ return 0
|
|
+
|
|
+
|
|
+if not get_fips_mode():
|
|
+ __builtin_constructor_cache = {}
|
|
+
|
|
+ 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',
|
|
+ 'shake_128', 'shake_256'}:
|
|
+ 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
|
|
+ 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 {'blake2b', 'blake2s'}:
|
|
- # Prefer our blake2 implementation.
|
|
- return __get_builtin_constructor(name)
|
|
+ if not get_fips_mode():
|
|
+ if name in {'blake2b', 'blake2s'}:
|
|
+ # Prefer our blake2 implementation.
|
|
+ return __get_builtin_constructor(name)
|
|
try:
|
|
f = getattr(_hashlib, 'openssl_' + name)
|
|
# Allow the C module to raise ValueError. The function will be
|
|
@@ -125,27 +133,30 @@ def __get_openssl_constructor(name):
|
|
# Use the C function directly (very fast)
|
|
return f
|
|
except (AttributeError, ValueError):
|
|
+ if get_fips_mode():
|
|
+ raise
|
|
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)
|
|
|
|
|
|
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 {'blake2b', 'blake2s'}:
|
|
- # Prefer our blake2 implementation.
|
|
- # OpenSSL 1.1.0 comes with a limited implementation of blake2b/s.
|
|
- # It does neither support keyed blake2 nor advanced features like
|
|
- # salt, personal, tree hashing or SSE.
|
|
- return __get_builtin_constructor(name)(data, **kwargs)
|
|
+ if not get_fips_mode():
|
|
+ if name in {'blake2b', 'blake2s'}:
|
|
+ # Prefer our blake2 implementation.
|
|
+ # OpenSSL 1.1.0 comes with a limited implementation of blake2b/s.
|
|
+ # It does neither support keyed blake2 nor advanced features like
|
|
+ # salt, personal, tree hashing or SSE.
|
|
+ return __get_builtin_constructor(name)(data, **kwargs)
|
|
try:
|
|
return _hashlib.new(name, data)
|
|
except ValueError:
|
|
@@ -153,6 +164,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)
|
|
|
|
|
|
@@ -163,72 +176,14 @@ try:
|
|
algorithms_available = algorithms_available.union(
|
|
_hashlib.openssl_md_meth_names)
|
|
except ImportError:
|
|
+ if get_fips_mode():
|
|
+ raise
|
|
new = __py_new
|
|
__get_hash = __get_builtin_constructor
|
|
|
|
-try:
|
|
- # OpenSSL's PKCS5_PBKDF2_HMAC requires OpenSSL 1.0+ with HMAC and SHA
|
|
- from _hashlib import pbkdf2_hmac
|
|
-except ImportError:
|
|
- _trans_5C = bytes((x ^ 0x5C) for x in range(256))
|
|
- _trans_36 = bytes((x ^ 0x36) for x in range(256))
|
|
-
|
|
- def pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None):
|
|
- """Password based key derivation function 2 (PKCS #5 v2.0)
|
|
|
|
- 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'))
|
|
- # endianess 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:
|
|
# OpenSSL's scrypt requires OpenSSL 1.1+
|
|
@@ -236,12 +191,6 @@ try:
|
|
except ImportError:
|
|
pass
|
|
|
|
-try:
|
|
- from _hashlib import get_fips_mode
|
|
-except ImportError:
|
|
- pass
|
|
-
|
|
-
|
|
for __func_name in __always_supported:
|
|
# try them all, some may not work due to the OpenSSL
|
|
# version not supporting that algorithm.
|
|
@@ -254,4 +203,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 1b1b4d54112..4e6b5b607a9 100644
|
|
--- a/Lib/test/test_hashlib.py
|
|
+++ b/Lib/test/test_hashlib.py
|
|
@@ -933,6 +933,7 @@ class KDFTests(unittest.TestCase):
|
|
iterations=1, dklen=None)
|
|
self.assertEqual(out, self.pbkdf2_results['sha1'][0][0])
|
|
|
|
+ @unittest.skip("The python implementation of pbkdf2_hmac has been removed")
|
|
def test_pbkdf2_hmac_py(self):
|
|
self._test_pbkdf2_hmac(py_hashlib.pbkdf2_hmac)
|
|
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 4803386d980b9ad1a9bdde829e8642676acd8bf4 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Thu, 25 Jul 2019 17:19:06 +0200
|
|
Subject: [PATCH 06/36] 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 | 36 +------------------
|
|
Modules/_sha3/sha3module.c | 5 +++
|
|
setup.py | 31 ++++++++--------
|
|
7 files changed, 101 insertions(+), 50 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
|
|
+
|
|
+#include "Python.h"
|
|
+#include <openssl/crypto.h>
|
|
+#include <openssl/err.h>
|
|
+
|
|
+/* LCOV_EXCL_START */
|
|
+static PyObject *
|
|
+_setException(PyObject *exc)
|
|
+{
|
|
+ unsigned long errcode;
|
|
+ const char *lib, *func, *reason;
|
|
+
|
|
+ errcode = ERR_peek_last_error();
|
|
+ if (!errcode) {
|
|
+ PyErr_SetString(exc, "unknown reasons");
|
|
+ return NULL;
|
|
+ }
|
|
+ ERR_clear_error();
|
|
+
|
|
+ lib = ERR_lib_error_string(errcode);
|
|
+ func = ERR_func_error_string(errcode);
|
|
+ reason = ERR_reason_error_string(errcode);
|
|
+
|
|
+ if (lib && func) {
|
|
+ PyErr_Format(exc, "[%s: %s] %s", lib, func, reason);
|
|
+ }
|
|
+ else if (lib) {
|
|
+ PyErr_Format(exc, "[%s] %s", lib, reason);
|
|
+ }
|
|
+ else {
|
|
+ PyErr_SetString(exc, reason);
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+/* LCOV_EXCL_STOP */
|
|
+
|
|
+
|
|
+__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;
|
|
+}
|
|
+
|
|
+#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 418c0184006..341e67a8fcc 100644
|
|
--- a/Modules/_blake2/blake2b_impl.c
|
|
+++ b/Modules/_blake2/blake2b_impl.c
|
|
@@ -14,6 +14,7 @@
|
|
*/
|
|
|
|
#include "Python.h"
|
|
+#include "_hashopenssl.h"
|
|
#include "pystrhex.h"
|
|
#ifdef WITH_THREAD
|
|
#include "pythread.h"
|
|
@@ -103,6 +104,8 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
|
|
unsigned long leaf_size = 0;
|
|
unsigned long long node_offset = 0;
|
|
|
|
+ FAIL_RETURN_IN_FIPS_MODE("_blake2");
|
|
+
|
|
self = new_BLAKE2bObject(type);
|
|
if (self == NULL) {
|
|
goto error;
|
|
@@ -293,6 +296,8 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data)
|
|
{
|
|
Py_buffer buf;
|
|
|
|
+ FAIL_RETURN_IN_FIPS_MODE("_blake2");
|
|
+
|
|
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
|
|
|
|
#ifdef WITH_THREAD
|
|
diff --git a/Modules/_blake2/blake2module.c b/Modules/_blake2/blake2module.c
|
|
index e2a3d420d4e..817b7165684 100644
|
|
--- a/Modules/_blake2/blake2module.c
|
|
+++ b/Modules/_blake2/blake2module.c
|
|
@@ -9,6 +9,7 @@
|
|
*/
|
|
|
|
#include "Python.h"
|
|
+#include "_hashopenssl.h"
|
|
|
|
#include "impl/blake2.h"
|
|
|
|
@@ -57,6 +58,8 @@ PyInit__blake2(void)
|
|
PyObject *m;
|
|
PyObject *d;
|
|
|
|
+ FAIL_RETURN_IN_FIPS_MODE("blake2");
|
|
+
|
|
m = PyModule_Create(&blake2_module);
|
|
if (m == NULL)
|
|
return NULL;
|
|
diff --git a/Modules/_blake2/blake2s_impl.c b/Modules/_blake2/blake2s_impl.c
|
|
index 24e529b6596..0dfe0586b57 100644
|
|
--- a/Modules/_blake2/blake2s_impl.c
|
|
+++ b/Modules/_blake2/blake2s_impl.c
|
|
@@ -14,6 +14,7 @@
|
|
*/
|
|
|
|
#include "Python.h"
|
|
+#include "_hashopenssl.h"
|
|
#include "pystrhex.h"
|
|
#ifdef WITH_THREAD
|
|
#include "pythread.h"
|
|
@@ -103,6 +104,8 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
|
|
unsigned long leaf_size = 0;
|
|
unsigned long long node_offset = 0;
|
|
|
|
+ FAIL_RETURN_IN_FIPS_MODE("_blake2");
|
|
+
|
|
self = new_BLAKE2sObject(type);
|
|
if (self == NULL) {
|
|
goto error;
|
|
@@ -293,6 +296,8 @@ _blake2_blake2s_update(BLAKE2sObject *self, PyObject *data)
|
|
{
|
|
Py_buffer buf;
|
|
|
|
+ FAIL_RETURN_IN_FIPS_MODE("_blake2");
|
|
+
|
|
GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
|
|
|
|
#ifdef WITH_THREAD
|
|
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
|
index 4876a7f7aa7..02cf6087a2e 100644
|
|
--- a/Modules/_hashopenssl.c
|
|
+++ b/Modules/_hashopenssl.c
|
|
@@ -17,16 +17,13 @@
|
|
#include "structmember.h"
|
|
#include "hashlib.h"
|
|
#include "pystrhex.h"
|
|
+#include "_hashopenssl.h"
|
|
|
|
|
|
/* EVP is the preferred interface to hashing in OpenSSL */
|
|
#include <openssl/evp.h>
|
|
/* We use the object interface to discover what hashes OpenSSL supports. */
|
|
#include <openssl/objects.h>
|
|
-#include "openssl/err.h"
|
|
-
|
|
-/* Expose FIPS_mode */
|
|
-#include <openssl/crypto.h>
|
|
|
|
#include "clinic/_hashopenssl.c.h"
|
|
/*[clinic input]
|
|
@@ -77,37 +74,6 @@ DEFINE_CONSTS_FOR_NEW(sha384)
|
|
DEFINE_CONSTS_FOR_NEW(sha512)
|
|
|
|
|
|
-/* LCOV_EXCL_START */
|
|
-static PyObject *
|
|
-_setException(PyObject *exc)
|
|
-{
|
|
- unsigned long errcode;
|
|
- const char *lib, *func, *reason;
|
|
-
|
|
- errcode = ERR_peek_last_error();
|
|
- if (!errcode) {
|
|
- PyErr_SetString(exc, "unknown reasons");
|
|
- return NULL;
|
|
- }
|
|
- ERR_clear_error();
|
|
-
|
|
- lib = ERR_lib_error_string(errcode);
|
|
- func = ERR_func_error_string(errcode);
|
|
- reason = ERR_reason_error_string(errcode);
|
|
-
|
|
- if (lib && func) {
|
|
- PyErr_Format(exc, "[%s: %s] %s", lib, func, reason);
|
|
- }
|
|
- else if (lib) {
|
|
- PyErr_Format(exc, "[%s] %s", lib, reason);
|
|
- }
|
|
- else {
|
|
- PyErr_SetString(exc, reason);
|
|
- }
|
|
- return NULL;
|
|
-}
|
|
-/* LCOV_EXCL_STOP */
|
|
-
|
|
static EVPobject *
|
|
newEVPobject(PyObject *name)
|
|
{
|
|
diff --git a/Modules/_sha3/sha3module.c b/Modules/_sha3/sha3module.c
|
|
index 2c2b2dbc5c7..624f7f247d4 100644
|
|
--- a/Modules/_sha3/sha3module.c
|
|
+++ b/Modules/_sha3/sha3module.c
|
|
@@ -18,6 +18,7 @@
|
|
#include "Python.h"
|
|
#include "pystrhex.h"
|
|
#include "../hashlib.h"
|
|
+#include "_hashopenssl.h"
|
|
|
|
/* **************************************************************************
|
|
* SHA-3 (Keccak) and SHAKE
|
|
@@ -162,6 +163,7 @@ static PyTypeObject SHAKE256type;
|
|
static SHA3object *
|
|
newSHA3object(PyTypeObject *type)
|
|
{
|
|
+ FAIL_RETURN_IN_FIPS_MODE("_sha3");
|
|
SHA3object *newobj;
|
|
newobj = (SHA3object *)PyObject_New(SHA3object, type);
|
|
if (newobj == NULL) {
|
|
@@ -177,6 +179,7 @@ newSHA3object(PyTypeObject *type)
|
|
static PyObject *
|
|
py_sha3_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
{
|
|
+ FAIL_RETURN_IN_FIPS_MODE("_sha3");
|
|
SHA3object *self = NULL;
|
|
Py_buffer buf = {NULL, NULL};
|
|
HashReturn res;
|
|
@@ -724,6 +727,8 @@ PyInit__sha3(void)
|
|
{
|
|
PyObject *m = NULL;
|
|
|
|
+ FAIL_RETURN_IN_FIPS_MODE("_sha3");
|
|
+
|
|
if ((m = PyModule_Create(&_SHA3module)) == NULL) {
|
|
return NULL;
|
|
}
|
|
diff --git a/setup.py b/setup.py
|
|
index e2c18982532..e2e659ef795 100644
|
|
--- a/setup.py
|
|
+++ b/setup.py
|
|
@@ -901,31 +901,30 @@ class PyBuildExt(build_ext):
|
|
have_usable_openssl = (have_any_openssl and
|
|
openssl_ver >= min_openssl_ver)
|
|
|
|
+ if not have_usable_openssl:
|
|
+ raise ValueError('Cannot build for RHEL without OpenSSL')
|
|
+
|
|
+ ssl_args = {
|
|
+ 'include_dirs': ssl_incs,
|
|
+ 'library_dirs': ssl_libs,
|
|
+ 'libraries': ['ssl', 'crypto'],
|
|
+ }
|
|
+
|
|
if have_any_openssl:
|
|
if have_usable_openssl:
|
|
# The _hashlib module wraps optimized implementations
|
|
# of hash functions from the OpenSSL library.
|
|
exts.append( Extension('_hashlib', ['_hashopenssl.c'],
|
|
depends = ['hashlib.h'],
|
|
- include_dirs = ssl_incs,
|
|
- library_dirs = ssl_libs,
|
|
- libraries = ['ssl', 'crypto']) )
|
|
+ **ssl_args))
|
|
else:
|
|
print("warning: openssl 0x%08x is too old for _hashlib" %
|
|
openssl_ver)
|
|
missing.append('_hashlib')
|
|
|
|
- # We always compile these even when OpenSSL is available (issue #14693).
|
|
- # It's harmless and the object code is tiny (40-50 KB per module,
|
|
- # only loaded when actually used).
|
|
- exts.append( Extension('_sha256', ['sha256module.c'],
|
|
- depends=['hashlib.h']) )
|
|
- exts.append( Extension('_sha512', ['sha512module.c'],
|
|
- depends=['hashlib.h']) )
|
|
- exts.append( Extension('_md5', ['md5module.c'],
|
|
- depends=['hashlib.h']) )
|
|
- exts.append( Extension('_sha1', ['sha1module.c'],
|
|
- depends=['hashlib.h']) )
|
|
+ # RHEL: Always force OpenSSL for md5, sha1, sha256, sha512;
|
|
+ # don't build Python's implementations.
|
|
+ # sha3 and blake2 have extra functionality, so do build those:
|
|
|
|
blake2_deps = glob(os.path.join(os.getcwd(), srcdir,
|
|
'Modules/_blake2/impl/*'))
|
|
@@ -944,6 +943,7 @@ class PyBuildExt(build_ext):
|
|
'_blake2/blake2b_impl.c',
|
|
'_blake2/blake2s_impl.c'],
|
|
define_macros=blake2_macros,
|
|
+ **ssl_args,
|
|
depends=blake2_deps) )
|
|
|
|
sha3_deps = glob(os.path.join(os.getcwd(), srcdir,
|
|
@@ -951,7 +951,8 @@ class PyBuildExt(build_ext):
|
|
sha3_deps.append('hashlib.h')
|
|
exts.append( Extension('_sha3',
|
|
['_sha3/sha3module.c'],
|
|
- depends=sha3_deps))
|
|
+ **ssl_args,
|
|
+ depends=sha3_deps + ['hashlib.h']))
|
|
|
|
# Modules that provide persistent dictionary-like semantics. You will
|
|
# probably want to arrange for at least one of them to be available on
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 0f35f480e2134c7d5c4ea3d68d8c0af14f56afa3 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Thu, 25 Jul 2019 17:35:27 +0200
|
|
Subject: [PATCH 07/36] Expose all hashes available to OpenSSL, using a list
|
|
|
|
---
|
|
Modules/_hashopenssl.c | 44 ++++++++++++++-----------------------
|
|
Modules/_hashopenssl_list.h | 21 ++++++++++++++++++
|
|
2 files changed, 37 insertions(+), 28 deletions(-)
|
|
create mode 100644 Modules/_hashopenssl_list.h
|
|
|
|
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
|
index 02cf6087a2e..7dfd70822b9 100644
|
|
--- a/Modules/_hashopenssl.c
|
|
+++ b/Modules/_hashopenssl.c
|
|
@@ -66,12 +66,9 @@ static PyTypeObject EVPtype;
|
|
static PyObject *CONST_ ## Name ## _name_obj = NULL; \
|
|
static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL;
|
|
|
|
-DEFINE_CONSTS_FOR_NEW(md5)
|
|
-DEFINE_CONSTS_FOR_NEW(sha1)
|
|
-DEFINE_CONSTS_FOR_NEW(sha224)
|
|
-DEFINE_CONSTS_FOR_NEW(sha256)
|
|
-DEFINE_CONSTS_FOR_NEW(sha384)
|
|
-DEFINE_CONSTS_FOR_NEW(sha512)
|
|
+#define _HASH(py_name, openssl_name) DEFINE_CONSTS_FOR_NEW(py_name)
|
|
+#include "_hashopenssl_list.h"
|
|
+#undef _HASH
|
|
|
|
|
|
static EVPobject *
|
|
@@ -896,7 +893,7 @@ generate_hash_name_list(void)
|
|
* The first call will lazy-initialize, which reports an exception
|
|
* if initialization fails.
|
|
*/
|
|
-#define GEN_CONSTRUCTOR(NAME) \
|
|
+#define GEN_CONSTRUCTOR(NAME, SSL_NAME) \
|
|
static PyObject * \
|
|
EVP_new_ ## NAME (PyObject *self, PyObject *args) \
|
|
{ \
|
|
@@ -910,8 +907,8 @@ generate_hash_name_list(void)
|
|
\
|
|
if (CONST_new_ ## NAME ## _ctx_p == NULL) { \
|
|
EVP_MD_CTX *ctx_p = EVP_MD_CTX_new(); \
|
|
- if (!EVP_get_digestbyname(#NAME) || \
|
|
- !EVP_DigestInit(ctx_p, EVP_get_digestbyname(#NAME))) { \
|
|
+ if (!EVP_get_digestbyname(SSL_NAME) || \
|
|
+ !EVP_DigestInit(ctx_p, EVP_get_digestbyname(SSL_NAME))) { \
|
|
_setException(PyExc_ValueError); \
|
|
EVP_MD_CTX_free(ctx_p); \
|
|
return NULL; \
|
|
@@ -939,7 +936,7 @@ generate_hash_name_list(void)
|
|
{"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \
|
|
PyDoc_STR("Returns a " #NAME \
|
|
" hash object; optionally initialized with a string") \
|
|
- }
|
|
+ },
|
|
|
|
/* used in the init function to setup a constructor: initialize OpenSSL
|
|
constructor constants if they haven't been initialized already. */
|
|
@@ -949,12 +946,9 @@ generate_hash_name_list(void)
|
|
} \
|
|
} while (0);
|
|
|
|
-GEN_CONSTRUCTOR(md5)
|
|
-GEN_CONSTRUCTOR(sha1)
|
|
-GEN_CONSTRUCTOR(sha224)
|
|
-GEN_CONSTRUCTOR(sha256)
|
|
-GEN_CONSTRUCTOR(sha384)
|
|
-GEN_CONSTRUCTOR(sha512)
|
|
+#define _HASH(py_name, openssl_name) GEN_CONSTRUCTOR(py_name, openssl_name)
|
|
+#include "_hashopenssl_list.h"
|
|
+#undef _HASH
|
|
|
|
/*[clinic input]
|
|
_hashlib.get_fips_mode
|
|
@@ -998,12 +992,9 @@ static struct PyMethodDef EVP_functions[] = {
|
|
#endif
|
|
_HASHLIB_SCRYPT_METHODDEF
|
|
_HASHLIB_GET_FIPS_MODE_METHODDEF
|
|
- CONSTRUCTOR_METH_DEF(md5),
|
|
- CONSTRUCTOR_METH_DEF(sha1),
|
|
- CONSTRUCTOR_METH_DEF(sha224),
|
|
- CONSTRUCTOR_METH_DEF(sha256),
|
|
- CONSTRUCTOR_METH_DEF(sha384),
|
|
- CONSTRUCTOR_METH_DEF(sha512),
|
|
+#define _HASH(py_name, openssl_name) CONSTRUCTOR_METH_DEF(py_name)
|
|
+#include "_hashopenssl_list.h"
|
|
+#undef _HASH
|
|
{NULL, NULL} /* Sentinel */
|
|
};
|
|
|
|
@@ -1061,11 +1052,8 @@ PyInit__hashlib(void)
|
|
PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype);
|
|
|
|
/* these constants are used by the convenience constructors */
|
|
- INIT_CONSTRUCTOR_CONSTANTS(md5);
|
|
- INIT_CONSTRUCTOR_CONSTANTS(sha1);
|
|
- INIT_CONSTRUCTOR_CONSTANTS(sha224);
|
|
- INIT_CONSTRUCTOR_CONSTANTS(sha256);
|
|
- INIT_CONSTRUCTOR_CONSTANTS(sha384);
|
|
- INIT_CONSTRUCTOR_CONSTANTS(sha512);
|
|
+#define _HASH(py_name, openssl_name) INIT_CONSTRUCTOR_CONSTANTS(py_name)
|
|
+#include "_hashopenssl_list.h"
|
|
+#undef _HASH
|
|
return m;
|
|
}
|
|
diff --git a/Modules/_hashopenssl_list.h b/Modules/_hashopenssl_list.h
|
|
new file mode 100644
|
|
index 00000000000..3c11b2eb6c6
|
|
--- /dev/null
|
|
+++ b/Modules/_hashopenssl_list.h
|
|
@@ -0,0 +1,21 @@
|
|
+/* Call the _HASH macro with all the hashes exported by OpenSSL,
|
|
+ * at compile time.
|
|
+ *
|
|
+ * This file is meant to be included multiple times, with different values of
|
|
+ * _HASH.
|
|
+ */
|
|
+
|
|
+_HASH(md5, "md5")
|
|
+_HASH(sha1, "sha1")
|
|
+_HASH(sha224, "sha224")
|
|
+_HASH(sha256, "sha256")
|
|
+_HASH(sha384, "sha384")
|
|
+_HASH(sha512, "sha512")
|
|
+_HASH(blake2b, "blake2b512")
|
|
+_HASH(blake2s, "blake2s256")
|
|
+_HASH(sha3_224, "sha3-224")
|
|
+_HASH(sha3_256, "sha3-256")
|
|
+_HASH(sha3_384, "sha3-384")
|
|
+_HASH(sha3_512, "sha3-512")
|
|
+_HASH(shake_128, "shake128")
|
|
+_HASH(shake_256, "shake256")
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 7e5b74b7268e4fb7d473d24cd023493e4e87eb76 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Thu, 25 Jul 2019 18:13:45 +0200
|
|
Subject: [PATCH 08/36] Fix tests
|
|
|
|
---
|
|
Lib/hashlib.py | 5 +++-
|
|
Lib/test/test_hashlib.py | 58 +++++++++++++++++++++++++++++++---------
|
|
2 files changed, 49 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
|
index 7db1e02601f..2def0a310c6 100644
|
|
--- a/Lib/hashlib.py
|
|
+++ b/Lib/hashlib.py
|
|
@@ -122,7 +122,10 @@ if not get_fips_mode():
|
|
|
|
def __get_openssl_constructor(name):
|
|
if not get_fips_mode():
|
|
- if name in {'blake2b', 'blake2s'}:
|
|
+ if name in {
|
|
+ 'blake2b', 'blake2s', 'shake_256', 'shake_128',
|
|
+ #'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
|
|
+ }:
|
|
# Prefer our blake2 implementation.
|
|
return __get_builtin_constructor(name)
|
|
try:
|
|
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
|
|
index 4e6b5b607a9..e57c93b42f5 100644
|
|
--- a/Lib/test/test_hashlib.py
|
|
+++ b/Lib/test/test_hashlib.py
|
|
@@ -182,7 +182,9 @@ class HashLibTestCase(unittest.TestCase):
|
|
a = array.array("b", range(10))
|
|
for cons in self.hash_constructors:
|
|
c = cons(a)
|
|
- if c.name in self.shakes:
|
|
+ if (c.name in self.shakes
|
|
+ and not cons.__name__.startswith('openssl_')
|
|
+ ):
|
|
c.hexdigest(16)
|
|
else:
|
|
c.hexdigest()
|
|
@@ -229,7 +231,9 @@ class HashLibTestCase(unittest.TestCase):
|
|
def test_hexdigest(self):
|
|
for cons in self.hash_constructors:
|
|
h = cons()
|
|
- if h.name in self.shakes:
|
|
+ if (h.name in self.shakes
|
|
+ and not cons.__name__.startswith('openssl_')
|
|
+ ):
|
|
self.assertIsInstance(h.digest(16), bytes)
|
|
self.assertEqual(hexstr(h.digest(16)), h.hexdigest(16))
|
|
else:
|
|
@@ -243,6 +247,8 @@ class HashLibTestCase(unittest.TestCase):
|
|
h = cons()
|
|
if h.name not in self.shakes:
|
|
continue
|
|
+ if cons.__name__.startswith('openssl_'):
|
|
+ continue
|
|
for digest in h.digest, h.hexdigest:
|
|
with self.assertRaises((ValueError, OverflowError)):
|
|
digest(-10)
|
|
@@ -272,7 +278,9 @@ class HashLibTestCase(unittest.TestCase):
|
|
m1.update(bees)
|
|
m1.update(cees)
|
|
m1.update(dees)
|
|
- if m1.name in self.shakes:
|
|
+ if (m1.name in self.shakes
|
|
+ and not cons.__name__.startswith('openssl_')
|
|
+ ):
|
|
args = (16,)
|
|
else:
|
|
args = ()
|
|
@@ -299,15 +307,36 @@ 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)
|
|
+ if shake:
|
|
+ if hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ if length > m.digest_size:
|
|
+ # OpenSSL doesn't give long digests
|
|
+ return
|
|
+ computed = m.hexdigest()[:length*2]
|
|
+ hexdigest = hexdigest[:length*2]
|
|
+ else:
|
|
+ computed = m.hexdigest(length)
|
|
+ else:
|
|
+ computed = m.hexdigest()
|
|
self.assertEqual(
|
|
computed, hexdigest,
|
|
"Hash algorithm %s constructed using %s returned hexdigest"
|
|
" %r for %d byte input data that should have hashed to %r."
|
|
% (name, hash_object_constructor,
|
|
computed, len(data), hexdigest))
|
|
- computed = m.digest() if not shake else m.digest(length)
|
|
+ if shake:
|
|
+ if hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ computed = m.digest()[:length]
|
|
+ else:
|
|
+ computed = m.digest(length)
|
|
+ else:
|
|
+ computed = m.digest()
|
|
digest = bytes.fromhex(hexdigest)
|
|
self.assertEqual(computed, digest)
|
|
if not shake:
|
|
@@ -347,12 +376,14 @@ class HashLibTestCase(unittest.TestCase):
|
|
for hash_object_constructor in constructors:
|
|
m = hash_object_constructor()
|
|
self.assertEqual(m.block_size, block_size)
|
|
- self.assertEqual(m.digest_size, digest_size)
|
|
+ if not hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ self.assertEqual(m.digest_size, digest_size)
|
|
if digest_length:
|
|
- self.assertEqual(len(m.digest(digest_length)),
|
|
- digest_length)
|
|
- self.assertEqual(len(m.hexdigest(digest_length)),
|
|
- 2*digest_length)
|
|
+ if not hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ self.assertEqual(len(m.digest(digest_length)),
|
|
+ digest_length)
|
|
+ self.assertEqual(len(m.hexdigest(digest_length)),
|
|
+ 2*digest_length)
|
|
else:
|
|
self.assertEqual(len(m.digest()), digest_size)
|
|
self.assertEqual(len(m.hexdigest()), 2*digest_size)
|
|
@@ -382,9 +413,10 @@ class HashLibTestCase(unittest.TestCase):
|
|
for hash_object_constructor in constructors:
|
|
m = hash_object_constructor()
|
|
self.assertEqual(capacity + rate, 1600)
|
|
- self.assertEqual(m._capacity_bits, capacity)
|
|
- self.assertEqual(m._rate_bits, rate)
|
|
- self.assertEqual(m._suffix, suffix)
|
|
+ if not hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ self.assertEqual(m._capacity_bits, capacity)
|
|
+ self.assertEqual(m._rate_bits, rate)
|
|
+ self.assertEqual(m._suffix, suffix)
|
|
|
|
@requires_sha3
|
|
def test_extra_sha3(self):
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From d6618795dd079acd8585f830b129d7f2fc9a6c52 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Fri, 26 Jul 2019 11:27:57 +0200
|
|
Subject: [PATCH 09/36] Change FIPS exceptions from _blake2, _sha3 module init
|
|
to ImportError
|
|
|
|
---
|
|
Include/_hashopenssl.h | 11 +++++------
|
|
Modules/_blake2/blake2b_impl.c | 4 ++--
|
|
Modules/_blake2/blake2module.c | 2 +-
|
|
Modules/_blake2/blake2s_impl.c | 4 ++--
|
|
Modules/_sha3/sha3module.c | 6 +++---
|
|
5 files changed, 13 insertions(+), 14 deletions(-)
|
|
|
|
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 341e67a8fcc..f6bfce823b8 100644
|
|
--- a/Modules/_blake2/blake2b_impl.c
|
|
+++ b/Modules/_blake2/blake2b_impl.c
|
|
@@ -104,7 +104,7 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
|
|
unsigned long leaf_size = 0;
|
|
unsigned long long node_offset = 0;
|
|
|
|
- FAIL_RETURN_IN_FIPS_MODE("_blake2");
|
|
+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
|
|
|
|
self = new_BLAKE2bObject(type);
|
|
if (self == NULL) {
|
|
@@ -296,7 +296,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 817b7165684..a9c7cbc7ebe 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 0dfe0586b57..28ae5b65101 100644
|
|
--- a/Modules/_blake2/blake2s_impl.c
|
|
+++ b/Modules/_blake2/blake2s_impl.c
|
|
@@ -104,7 +104,7 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
|
|
unsigned long leaf_size = 0;
|
|
unsigned long long node_offset = 0;
|
|
|
|
- FAIL_RETURN_IN_FIPS_MODE("_blake2");
|
|
+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_blake2");
|
|
|
|
self = new_BLAKE2sObject(type);
|
|
if (self == NULL) {
|
|
@@ -296,7 +296,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/_sha3/sha3module.c b/Modules/_sha3/sha3module.c
|
|
index 624f7f247d4..2783a75644f 100644
|
|
--- a/Modules/_sha3/sha3module.c
|
|
+++ b/Modules/_sha3/sha3module.c
|
|
@@ -163,7 +163,7 @@ static PyTypeObject SHAKE256type;
|
|
static SHA3object *
|
|
newSHA3object(PyTypeObject *type)
|
|
{
|
|
- FAIL_RETURN_IN_FIPS_MODE("_sha3");
|
|
+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_sha3");
|
|
SHA3object *newobj;
|
|
newobj = (SHA3object *)PyObject_New(SHA3object, type);
|
|
if (newobj == NULL) {
|
|
@@ -179,7 +179,7 @@ newSHA3object(PyTypeObject *type)
|
|
static PyObject *
|
|
py_sha3_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
{
|
|
- FAIL_RETURN_IN_FIPS_MODE("_sha3");
|
|
+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ValueError, "_sha3");
|
|
SHA3object *self = NULL;
|
|
Py_buffer buf = {NULL, NULL};
|
|
HashReturn res;
|
|
@@ -727,7 +727,7 @@ PyInit__sha3(void)
|
|
{
|
|
PyObject *m = NULL;
|
|
|
|
- FAIL_RETURN_IN_FIPS_MODE("_sha3");
|
|
+ FAIL_RETURN_IN_FIPS_MODE(PyExc_ImportError, "_sha3");
|
|
|
|
if ((m = PyModule_Create(&_SHA3module)) == NULL) {
|
|
return NULL;
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From eb78e4b4179842185f53edba2073ab7644db7ad3 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Fri, 26 Jul 2019 11:24:09 +0200
|
|
Subject: [PATCH 10/36] Make hashlib importable under FIPS mode
|
|
|
|
---
|
|
Lib/hashlib.py | 8 +++++---
|
|
1 file changed, 5 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
|
index 2def0a310c6..ca1dd202251 100644
|
|
--- a/Lib/hashlib.py
|
|
+++ b/Lib/hashlib.py
|
|
@@ -132,12 +132,14 @@ def __get_openssl_constructor(name):
|
|
f = getattr(_hashlib, 'openssl_' + name)
|
|
# Allow the C module to raise ValueError. The function will be
|
|
# defined but the hash not actually available thanks to OpenSSL.
|
|
- f()
|
|
+ if not get_fips_mode():
|
|
+ # N.B. In "FIPS mode", there is no fallback.
|
|
+ # If this test fails, we want to export the broken hash
|
|
+ # constructor anyway.
|
|
+ f()
|
|
# Use the C function directly (very fast)
|
|
return f
|
|
except (AttributeError, ValueError):
|
|
- if get_fips_mode():
|
|
- raise
|
|
return __get_builtin_constructor(name)
|
|
|
|
if not get_fips_mode():
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 0f313f65457f1e7d0e7238a71935f5d4e0d197ee Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Fri, 26 Jul 2019 15:41:10 +0200
|
|
Subject: [PATCH 11/36] Implement hmac.new using new built-in module,
|
|
_hmacopenssl
|
|
|
|
---
|
|
Lib/hmac.py | 33 ++-
|
|
Modules/_hmacopenssl.c | 396 ++++++++++++++++++++++++++++++++
|
|
Modules/clinic/_hmacopenssl.c.h | 133 +++++++++++
|
|
setup.py | 4 +
|
|
4 files changed, 565 insertions(+), 1 deletion(-)
|
|
create mode 100644 Modules/_hmacopenssl.c
|
|
create mode 100644 Modules/clinic/_hmacopenssl.c.h
|
|
|
|
diff --git a/Lib/hmac.py b/Lib/hmac.py
|
|
index 121029aa670..ed98406bd2e 100644
|
|
--- a/Lib/hmac.py
|
|
+++ b/Lib/hmac.py
|
|
@@ -6,6 +6,8 @@ Implements the HMAC algorithm as described by RFC 2104.
|
|
import warnings as _warnings
|
|
from _operator import _compare_digest as compare_digest
|
|
import hashlib as _hashlib
|
|
+import _hashlib as _hashlibopenssl
|
|
+import _hmacopenssl
|
|
|
|
trans_5C = bytes((x ^ 0x5C) for x in range(256))
|
|
trans_36 = bytes((x ^ 0x36) for x in range(256))
|
|
@@ -37,6 +39,11 @@ class HMAC:
|
|
|
|
Note: key and msg must be a bytes or bytearray objects.
|
|
"""
|
|
+ if _hashlib.get_fips_mode():
|
|
+ raise ValueError(
|
|
+ 'hmac.HMAC is not available in FIPS mode. '
|
|
+ + 'Use hmac.new().'
|
|
+ )
|
|
|
|
if not isinstance(key, (bytes, bytearray)):
|
|
raise TypeError("key: expected bytes or bytearray, but got %r" % type(key).__name__)
|
|
@@ -90,6 +97,8 @@ class HMAC:
|
|
def update(self, msg):
|
|
"""Update this hashing object with the string msg.
|
|
"""
|
|
+ if _hashlib.get_fips_mode():
|
|
+ raise ValueError('hmac.HMAC is not available in FIPS mode')
|
|
self.inner.update(msg)
|
|
|
|
def copy(self):
|
|
@@ -130,6 +139,19 @@ class HMAC:
|
|
h = self._current()
|
|
return h.hexdigest()
|
|
|
|
+
|
|
+def _get_openssl_name(digestmod):
|
|
+ if isinstance(digestmod, str):
|
|
+ return digestmod.lower()
|
|
+ elif callable(digestmod):
|
|
+ digestmod = digestmod(b'')
|
|
+
|
|
+ if not isinstance(digestmod, _hashlibopenssl.HASH):
|
|
+ raise TypeError(
|
|
+ 'Only OpenSSL hashlib hashes are accepted in FIPS mode.')
|
|
+
|
|
+ return digestmod.name.lower().replace('_', '-')
|
|
+
|
|
def new(key, msg = None, digestmod = None):
|
|
"""Create a new hashing object and return it.
|
|
|
|
@@ -141,4 +163,13 @@ def new(key, msg = None, digestmod = None):
|
|
method, and can ask for the hash value at any time by calling its digest()
|
|
method.
|
|
"""
|
|
- return HMAC(key, msg, digestmod)
|
|
+ if _hashlib.get_fips_mode():
|
|
+ if digestmod is None:
|
|
+ digestmod = 'md5'
|
|
+ name = _get_openssl_name(digestmod)
|
|
+ result = _hmacopenssl.new(key, digestmod=name)
|
|
+ if msg:
|
|
+ result.update(msg)
|
|
+ return result
|
|
+ else:
|
|
+ return HMAC(key, msg, digestmod)
|
|
diff --git a/Modules/_hmacopenssl.c b/Modules/_hmacopenssl.c
|
|
new file mode 100644
|
|
index 00000000000..ca95d725f01
|
|
--- /dev/null
|
|
+++ b/Modules/_hmacopenssl.c
|
|
@@ -0,0 +1,396 @@
|
|
+/* Module that wraps all OpenSSL MHAC algorithm */
|
|
+
|
|
+/* Copyright (C) 2019 Red Hat, Inc. Red Hat, Inc. and/or its affiliates
|
|
+ *
|
|
+ * Based on _hashopenssl.c, which is:
|
|
+ * Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org)
|
|
+ * Licensed to PSF under a Contributor Agreement.
|
|
+ *
|
|
+ * Derived from a skeleton of shamodule.c containing work performed by:
|
|
+ *
|
|
+ * Andrew Kuchling (amk@amk.ca)
|
|
+ * Greg Stein (gstein@lyra.org)
|
|
+ *
|
|
+ */
|
|
+
|
|
+#define PY_SSIZE_T_CLEAN
|
|
+
|
|
+#include "Python.h"
|
|
+#include "structmember.h"
|
|
+#include "hashlib.h"
|
|
+#include "pystrhex.h"
|
|
+#include "_hashopenssl.h"
|
|
+
|
|
+
|
|
+#include <openssl/hmac.h>
|
|
+
|
|
+static PyTypeObject HmacType;
|
|
+
|
|
+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 *" "&HmacType"
|
|
+[clinic start generated code]*/
|
|
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c98d3f2af591c085]*/
|
|
+
|
|
+
|
|
+/*[clinic input]
|
|
+_hmacopenssl.new
|
|
+
|
|
+ key: Py_buffer
|
|
+ *
|
|
+ digestmod: str
|
|
+
|
|
+Return a new hmac object.
|
|
+[clinic start generated code]*/
|
|
+
|
|
+static PyObject *
|
|
+_hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
+ const char *digestmod)
|
|
+/*[clinic end generated code: output=46f1cb4e02921922 input=be8c0c2e4fad508c]*/
|
|
+{
|
|
+ if (digestmod == NULL) {
|
|
+ PyErr_SetString(PyExc_ValueError, "digestmod must be specified");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ /* name mut be lowercase */
|
|
+ for (int i=0; digestmod[i]; i++) {
|
|
+ if (
|
|
+ ((digestmod[i] < 'a') || (digestmod[i] > 'z'))
|
|
+ && ((digestmod[i] < '0') || (digestmod[i] > '9'))
|
|
+ && digestmod[i] != '-'
|
|
+ ) {
|
|
+ PyErr_SetString(PyExc_ValueError, "digestmod must be lowercase");
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ const EVP_MD *digest = EVP_get_digestbyname(digestmod);
|
|
+ if (!digest) {
|
|
+ PyErr_SetString(PyExc_ValueError, "unknown hash function");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ PyObject *name = NULL;
|
|
+ HMAC_CTX *ctx = NULL;
|
|
+ HmacObject *retval = NULL;
|
|
+
|
|
+ name = PyUnicode_FromFormat("hmac-%s", digestmod);
|
|
+ if (name == NULL) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ 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;
|
|
+ }
|
|
+
|
|
+ retval = (HmacObject *)PyObject_New(HmacObject, &HmacType);
|
|
+ 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);
|
|
+ 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 = (HmacObject *)PyObject_New(HmacObject, &HmacType);
|
|
+ if (retval == NULL) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ Py_INCREF(self->name);
|
|
+ retval->name = self->name;
|
|
+
|
|
+ int r = HMAC_CTX_copy(retval->ctx, self->ctx);
|
|
+ if (r == 0) {
|
|
+ PyObject_Del(retval);
|
|
+ return _setException(PyExc_ValueError);
|
|
+ }
|
|
+
|
|
+ return (PyObject*)retval;
|
|
+}
|
|
+
|
|
+static void
|
|
+_hmac_dealloc(HmacObject *self)
|
|
+{
|
|
+ if (self->lock != NULL) {
|
|
+ PyThread_free_lock(self->lock);
|
|
+ }
|
|
+ HMAC_CTX_free(self->ctx);
|
|
+ Py_XDECREF(self->name);
|
|
+ PyObject_Del(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_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")},
|
|
+};
|
|
+
|
|
+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 PyTypeObject HmacType = {
|
|
+ PyVarObject_HEAD_INIT(NULL, 0)
|
|
+ "_hmacopenssl.HMAC", /*tp_name*/
|
|
+ sizeof(HmacObject), /*tp_basicsize*/
|
|
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
+ .tp_doc = hmactype_doc,
|
|
+ .tp_repr = (reprfunc)_hmac_repr,
|
|
+ .tp_dealloc = (destructor)_hmac_dealloc,
|
|
+ .tp_methods = Hmac_methods,
|
|
+ .tp_getset = Hmac_getset,
|
|
+ .tp_members = Hmac_members,
|
|
+};
|
|
+
|
|
+static struct PyMethodDef hmacopenssl_functions[] = {
|
|
+ _HMACOPENSSL_NEW_METHODDEF
|
|
+ {NULL, NULL} /* Sentinel */
|
|
+};
|
|
+
|
|
+
|
|
+
|
|
+/* Initialize this module. */
|
|
+
|
|
+
|
|
+static struct PyModuleDef _hmacopenssl_module = {
|
|
+ PyModuleDef_HEAD_INIT,
|
|
+ "_hmacopenssl",
|
|
+ NULL,
|
|
+ -1,
|
|
+ hmacopenssl_functions,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+PyMODINIT_FUNC
|
|
+PyInit__hmacopenssl(void)
|
|
+{
|
|
+ /* TODO build EVP_functions openssl_* entries dynamically based
|
|
+ * on what hashes are supported rather than listing many
|
|
+ * but having some be unsupported. Only init appropriate
|
|
+ * constants. */
|
|
+
|
|
+ Py_TYPE(&HmacType) = &PyType_Type;
|
|
+ if (PyType_Ready(&HmacType) < 0)
|
|
+ return NULL;
|
|
+
|
|
+ PyObject *m = PyModule_Create(&_hmacopenssl_module);
|
|
+ if (m == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ Py_INCREF((PyObject *)&HmacType);
|
|
+ PyModule_AddObject(m, "HMAC", (PyObject *)&HmacType);
|
|
+
|
|
+ return m;
|
|
+}
|
|
diff --git a/Modules/clinic/_hmacopenssl.c.h b/Modules/clinic/_hmacopenssl.c.h
|
|
new file mode 100644
|
|
index 00000000000..b472a6eddd3
|
|
--- /dev/null
|
|
+++ b/Modules/clinic/_hmacopenssl.c.h
|
|
@@ -0,0 +1,133 @@
|
|
+/*[clinic input]
|
|
+preserve
|
|
+[clinic start generated code]*/
|
|
+
|
|
+PyDoc_STRVAR(_hmacopenssl_new__doc__,
|
|
+"new($module, /, key, *, digestmod)\n"
|
|
+"--\n"
|
|
+"\n"
|
|
+"Return a new hmac object.");
|
|
+
|
|
+#define _HMACOPENSSL_NEW_METHODDEF \
|
|
+ {"new", (PyCFunction)_hmacopenssl_new, METH_FASTCALL, _hmacopenssl_new__doc__},
|
|
+
|
|
+static PyObject *
|
|
+_hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
+ const char *digestmod);
|
|
+
|
|
+static PyObject *
|
|
+_hmacopenssl_new(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
|
|
+{
|
|
+ PyObject *return_value = NULL;
|
|
+ static const char * const _keywords[] = {"key", "digestmod", NULL};
|
|
+ static _PyArg_Parser _parser = {"y*$s:new", _keywords, 0};
|
|
+ Py_buffer key = {NULL, NULL};
|
|
+ const char *digestmod;
|
|
+
|
|
+ if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
|
|
+ &key, &digestmod)) {
|
|
+ goto exit;
|
|
+ }
|
|
+ return_value = _hmacopenssl_new_impl(module, &key, digestmod);
|
|
+
|
|
+exit:
|
|
+ /* Cleanup for key */
|
|
+ if (key.obj) {
|
|
+ PyBuffer_Release(&key);
|
|
+ }
|
|
+
|
|
+ return return_value;
|
|
+}
|
|
+
|
|
+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)_hmacopenssl_HMAC_update, METH_FASTCALL, _hmacopenssl_HMAC_update__doc__},
|
|
+
|
|
+static PyObject *
|
|
+_hmacopenssl_HMAC_update_impl(HmacObject *self, Py_buffer *msg);
|
|
+
|
|
+static PyObject *
|
|
+_hmacopenssl_HMAC_update(HmacObject *self, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
|
|
+{
|
|
+ PyObject *return_value = NULL;
|
|
+ static const char * const _keywords[] = {"msg", NULL};
|
|
+ static _PyArg_Parser _parser = {"y*:update", _keywords, 0};
|
|
+ Py_buffer msg = {NULL, NULL};
|
|
+
|
|
+ if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
|
|
+ &msg)) {
|
|
+ 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=10b6e8cac6d7a2c9 input=a9049054013a1b77]*/
|
|
diff --git a/setup.py b/setup.py
|
|
index e2e659ef795..282aa4178ed 100644
|
|
--- a/setup.py
|
|
+++ b/setup.py
|
|
@@ -922,6 +922,10 @@ class PyBuildExt(build_ext):
|
|
openssl_ver)
|
|
missing.append('_hashlib')
|
|
|
|
+ exts.append( Extension('_hmacopenssl', ['_hmacopenssl.c'],
|
|
+ depends = ['hashlib.h'],
|
|
+ **ssl_args))
|
|
+
|
|
# RHEL: Always force OpenSSL for md5, sha1, sha256, sha512;
|
|
# don't build Python's implementations.
|
|
# sha3 and blake2 have extra functionality, so do build those:
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 37797b4d89e7b4859dc4728da91cbebf5fdb2a52 Mon Sep 17 00:00:00 2001
|
|
From: Marcel Plch <mplch@redhat.com>
|
|
Date: Mon, 29 Jul 2019 12:45:11 +0200
|
|
Subject: [PATCH 12/36] FIPS review
|
|
|
|
* Port _hmacopenssl to multiphase init.
|
|
* Make _hmacopenssl.HMAC.copy create same type as self.
|
|
* hmac.py cosmetic nitpick
|
|
---
|
|
Lib/hmac.py | 2 +-
|
|
Modules/_hmacopenssl.c | 112 +++++++++++++++++++++++++----------------
|
|
2 files changed, 70 insertions(+), 44 deletions(-)
|
|
|
|
diff --git a/Lib/hmac.py b/Lib/hmac.py
|
|
index ed98406bd2e..b9bf16b84ca 100644
|
|
--- a/Lib/hmac.py
|
|
+++ b/Lib/hmac.py
|
|
@@ -42,7 +42,7 @@ class HMAC:
|
|
if _hashlib.get_fips_mode():
|
|
raise ValueError(
|
|
'hmac.HMAC is not available in FIPS mode. '
|
|
- + 'Use hmac.new().'
|
|
+ 'Use hmac.new().'
|
|
)
|
|
|
|
if not isinstance(key, (bytes, bytearray)):
|
|
diff --git a/Modules/_hmacopenssl.c b/Modules/_hmacopenssl.c
|
|
index ca95d725f01..216ed04f236 100644
|
|
--- a/Modules/_hmacopenssl.c
|
|
+++ b/Modules/_hmacopenssl.c
|
|
@@ -24,7 +24,10 @@
|
|
|
|
#include <openssl/hmac.h>
|
|
|
|
-static PyTypeObject HmacType;
|
|
+typedef struct hmacopenssl_state {
|
|
+ PyTypeObject *HmacType;
|
|
+} hmacopenssl_state;
|
|
+
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
@@ -36,9 +39,9 @@ typedef struct {
|
|
#include "clinic/_hmacopenssl.c.h"
|
|
/*[clinic input]
|
|
module _hmacopenssl
|
|
-class _hmacopenssl.HMAC "HmacObject *" "&HmacType"
|
|
+class _hmacopenssl.HMAC "HmacObject *" "PyModule_GetState(module)->HmacType"
|
|
[clinic start generated code]*/
|
|
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c98d3f2af591c085]*/
|
|
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=204b7f45847f57b4]*/
|
|
|
|
|
|
/*[clinic input]
|
|
@@ -56,11 +59,18 @@ _hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
const char *digestmod)
|
|
/*[clinic end generated code: output=46f1cb4e02921922 input=be8c0c2e4fad508c]*/
|
|
{
|
|
+ hmacopenssl_state *state;
|
|
+
|
|
if (digestmod == NULL) {
|
|
PyErr_SetString(PyExc_ValueError, "digestmod must be specified");
|
|
return NULL;
|
|
}
|
|
|
|
+ state = PyModule_GetState(module);
|
|
+ if (state == NULL) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
/* name mut be lowercase */
|
|
for (int i=0; digestmod[i]; i++) {
|
|
if (
|
|
@@ -105,7 +115,7 @@ _hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
goto error;
|
|
}
|
|
|
|
- retval = (HmacObject *)PyObject_New(HmacObject, &HmacType);
|
|
+ retval = (HmacObject *)PyObject_New(HmacObject, state->HmacType);
|
|
if (retval == NULL) {
|
|
goto error;
|
|
}
|
|
@@ -133,7 +143,9 @@ static PyObject *
|
|
_hmacopenssl_HMAC_copy_impl(HmacObject *self)
|
|
/*[clinic end generated code: output=fe5ee41faf30dcf0 input=f5ed20feec42d8d0]*/
|
|
{
|
|
- HmacObject *retval = (HmacObject *)PyObject_New(HmacObject, &HmacType);
|
|
+ HmacObject *retval;
|
|
+
|
|
+ retval = (HmacObject *)PyObject_New(HmacObject, (PyTypeObject *)PyObject_Type((PyObject *)self));
|
|
if (retval == NULL) {
|
|
return NULL;
|
|
}
|
|
@@ -147,7 +159,7 @@ _hmacopenssl_HMAC_copy_impl(HmacObject *self)
|
|
return _setException(PyExc_ValueError);
|
|
}
|
|
|
|
- return (PyObject*)retval;
|
|
+ return (PyObject *)retval;
|
|
}
|
|
|
|
static void
|
|
@@ -338,19 +350,24 @@ Attributes:\n\
|
|
name -- the name, including the hash algorithm used by this object\n\
|
|
digest_size -- number of bytes in digest() output\n");
|
|
|
|
-static PyTypeObject HmacType = {
|
|
- PyVarObject_HEAD_INIT(NULL, 0)
|
|
- "_hmacopenssl.HMAC", /*tp_name*/
|
|
- sizeof(HmacObject), /*tp_basicsize*/
|
|
- .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
- .tp_doc = hmactype_doc,
|
|
- .tp_repr = (reprfunc)_hmac_repr,
|
|
- .tp_dealloc = (destructor)_hmac_dealloc,
|
|
- .tp_methods = Hmac_methods,
|
|
- .tp_getset = Hmac_getset,
|
|
- .tp_members = Hmac_members,
|
|
+static PyType_Slot HmacType_slots[] = {
|
|
+ {Py_tp_doc, 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},
|
|
+ {0, NULL}
|
|
+};
|
|
+
|
|
+PyType_Spec HmacType_spec = {
|
|
+ "_hmacopenssl.HMAC", /* name */
|
|
+ sizeof(HmacObject), /* basicsize */
|
|
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
+ .slots = HmacType_slots,
|
|
};
|
|
|
|
+
|
|
static struct PyMethodDef hmacopenssl_functions[] = {
|
|
_HMACOPENSSL_NEW_METHODDEF
|
|
{NULL, NULL} /* Sentinel */
|
|
@@ -360,37 +377,46 @@ static struct PyMethodDef hmacopenssl_functions[] = {
|
|
|
|
/* Initialize this module. */
|
|
|
|
-
|
|
-static struct PyModuleDef _hmacopenssl_module = {
|
|
- PyModuleDef_HEAD_INIT,
|
|
- "_hmacopenssl",
|
|
- NULL,
|
|
- -1,
|
|
- hmacopenssl_functions,
|
|
- NULL,
|
|
- NULL,
|
|
- NULL,
|
|
- NULL
|
|
-};
|
|
-
|
|
-PyMODINIT_FUNC
|
|
-PyInit__hmacopenssl(void)
|
|
-{
|
|
+static int
|
|
+hmacopenssl_exec(PyObject *m) {
|
|
/* TODO build EVP_functions openssl_* entries dynamically based
|
|
* on what hashes are supported rather than listing many
|
|
- * but having some be unsupported. Only init appropriate
|
|
+ * and having some unsupported. Only init appropriate
|
|
* constants. */
|
|
+ PyObject *temp;
|
|
|
|
- Py_TYPE(&HmacType) = &PyType_Type;
|
|
- if (PyType_Ready(&HmacType) < 0)
|
|
- return NULL;
|
|
+ temp = PyType_FromSpec(&HmacType_spec);
|
|
+ if (temp == NULL) {
|
|
+ goto fail;
|
|
+ }
|
|
|
|
- PyObject *m = PyModule_Create(&_hmacopenssl_module);
|
|
- if (m == NULL)
|
|
- return NULL;
|
|
+ if (PyModule_AddObject(m, "HMAC", temp) == -1) {
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
|
|
- Py_INCREF((PyObject *)&HmacType);
|
|
- PyModule_AddObject(m, "HMAC", (PyObject *)&HmacType);
|
|
+fail:
|
|
+ Py_XDECREF(temp);
|
|
+ return -1;
|
|
+}
|
|
|
|
- return m;
|
|
+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_methods = hmacopenssl_functions,
|
|
+ .m_slots = hmacopenssl_slots,
|
|
+ .m_size = sizeof(hmacopenssl_state)
|
|
+};
|
|
+
|
|
+
|
|
+PyMODINIT_FUNC
|
|
+PyInit__hmacopenssl(void)
|
|
+{
|
|
+ return PyModuleDef_Init(&_hmacopenssl_def);
|
|
}
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 9a3bf73d382cda99a665cab8438f3cd0388256b0 Mon Sep 17 00:00:00 2001
|
|
From: Marcel Plch <mplch@redhat.com>
|
|
Date: Mon, 29 Jul 2019 13:05:04 +0200
|
|
Subject: [PATCH 13/36] revert cosmetic nitpick and remove trailing whitespace
|
|
|
|
---
|
|
Lib/hmac.py | 2 +-
|
|
Modules/_hmacopenssl.c | 4 ++--
|
|
2 files changed, 3 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/Lib/hmac.py b/Lib/hmac.py
|
|
index b9bf16b84ca..ed98406bd2e 100644
|
|
--- a/Lib/hmac.py
|
|
+++ b/Lib/hmac.py
|
|
@@ -42,7 +42,7 @@ class HMAC:
|
|
if _hashlib.get_fips_mode():
|
|
raise ValueError(
|
|
'hmac.HMAC is not available in FIPS mode. '
|
|
- 'Use hmac.new().'
|
|
+ + 'Use hmac.new().'
|
|
)
|
|
|
|
if not isinstance(key, (bytes, bytearray)):
|
|
diff --git a/Modules/_hmacopenssl.c b/Modules/_hmacopenssl.c
|
|
index 216ed04f236..221714ca434 100644
|
|
--- a/Modules/_hmacopenssl.c
|
|
+++ b/Modules/_hmacopenssl.c
|
|
@@ -363,7 +363,7 @@ static PyType_Slot HmacType_slots[] = {
|
|
PyType_Spec HmacType_spec = {
|
|
"_hmacopenssl.HMAC", /* name */
|
|
sizeof(HmacObject), /* basicsize */
|
|
- .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
|
.slots = HmacType_slots,
|
|
};
|
|
|
|
@@ -407,7 +407,7 @@ static PyModuleDef_Slot hmacopenssl_slots[] = {
|
|
};
|
|
|
|
static struct PyModuleDef _hmacopenssl_def = {
|
|
- PyModuleDef_HEAD_INIT, /* m_base */
|
|
+ PyModuleDef_HEAD_INIT, /* m_base */
|
|
.m_name = "_hmacopenssl",
|
|
.m_methods = hmacopenssl_functions,
|
|
.m_slots = hmacopenssl_slots,
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From b2853963d35c60595cec7e5cd40f31b1c9c59426 Mon Sep 17 00:00:00 2001
|
|
From: Charalampos Stratakis <cstratak@redhat.com>
|
|
Date: Wed, 31 Jul 2019 15:43:43 +0200
|
|
Subject: [PATCH 14/36] Add initial tests for various hashes under FIPS mode
|
|
|
|
---
|
|
Lib/test/test_fips.py | 64 +++++++++++++++++++++++++++++++++++++++++++
|
|
1 file changed, 64 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 00000000000..bee911ef405
|
|
--- /dev/null
|
|
+++ b/Lib/test/test_fips.py
|
|
@@ -0,0 +1,64 @@
|
|
+import unittest
|
|
+import hmac, _hmacopenssl
|
|
+import hashlib, _hashlib
|
|
+
|
|
+
|
|
+
|
|
+class HashlibFipsTests(unittest.TestCase):
|
|
+
|
|
+ @unittest.skipUnless(hashlib.get_fips_mode(), "Test only when FIPS is enabled")
|
|
+ def test_fips_imports(self):
|
|
+ """blake2s and blake2b should fail to import in FIPS mode
|
|
+ """
|
|
+ with self.assertRaises(ValueError, msg='blake2s not available in FIPS'):
|
|
+ m = hashlib.blake2s()
|
|
+ with self.assertRaises(ValueError, msg='blake2b not available in FIPS'):
|
|
+ m = hashlib.blake2b()
|
|
+
|
|
+ def compare_hashes(self, python_hash, openssl_hash):
|
|
+ """
|
|
+ Compare between the python implementation and the openssl one that the digests
|
|
+ are the same
|
|
+ """
|
|
+ if python_hash.name.startswith('shake_128'):
|
|
+ m = python_hash.hexdigest(16)
|
|
+ elif python_hash.name.startswith('shake_256'):
|
|
+ m = python_hash.hexdigest(32)
|
|
+ else:
|
|
+ m = python_hash.hexdigest()
|
|
+ h = openssl_hash.hexdigest()
|
|
+
|
|
+ self.assertEqual(m, h)
|
|
+
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), "blake2 hashes are not available under FIPS")
|
|
+ def test_blake2_hashes(self):
|
|
+ self.compare_hashes(hashlib.blake2b(b'abc'), _hashlib.openssl_blake2b(b'abc'))
|
|
+ self.compare_hashes(hashlib.blake2s(b'abc'), _hashlib.openssl_blake2s(b'abc'))
|
|
+
|
|
+ def test_sha3_hashes(self):
|
|
+ self.compare_hashes(hashlib.sha3_224(b'abc'), _hashlib.openssl_sha3_224(b'abc'))
|
|
+ self.compare_hashes(hashlib.sha3_256(b'abc'), _hashlib.openssl_sha3_256(b'abc'))
|
|
+ self.compare_hashes(hashlib.sha3_384(b'abc'), _hashlib.openssl_sha3_384(b'abc'))
|
|
+ self.compare_hashes(hashlib.sha3_512(b'abc'), _hashlib.openssl_sha3_512(b'abc'))
|
|
+
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), "shake hashes are not available under FIPS")
|
|
+ def test_shake_hashes(self):
|
|
+ self.compare_hashes(hashlib.shake_128(b'abc'), _hashlib.openssl_shake_128(b'abc'))
|
|
+ self.compare_hashes(hashlib.shake_256(b'abc'), _hashlib.openssl_shake_256(b'abc'))
|
|
+
|
|
+ def test_sha(self):
|
|
+ self.compare_hashes(hashlib.sha1(b'abc'), _hashlib.openssl_sha1(b'abc'))
|
|
+ self.compare_hashes(hashlib.sha224(b'abc'), _hashlib.openssl_sha224(b'abc'))
|
|
+ self.compare_hashes(hashlib.sha256(b'abc'), _hashlib.openssl_sha256(b'abc'))
|
|
+ self.compare_hashes(hashlib.sha384(b'abc'), _hashlib.openssl_sha384(b'abc'))
|
|
+ self.compare_hashes(hashlib.sha512(b'abc'), _hashlib.openssl_sha512(b'abc'))
|
|
+
|
|
+ def test_hmac_digests(self):
|
|
+ self.compare_hashes(_hmacopenssl.new(b'My hovercraft is full of eels', digestmod='sha384'),
|
|
+ hmac.new(b'My hovercraft is full of eels', digestmod='sha384'))
|
|
+
|
|
+
|
|
+
|
|
+
|
|
+if __name__ == "__main__":
|
|
+ unittest.main()
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From b3a8214b5afe4809983e6a5e5d339527e8238318 Mon Sep 17 00:00:00 2001
|
|
From: Marcel Plch <mplch@redhat.com>
|
|
Date: Thu, 1 Aug 2019 16:39:37 +0200
|
|
Subject: [PATCH 15/36] Initialize HMAC type.
|
|
|
|
---
|
|
Modules/_hmacopenssl.c | 12 ++++++++----
|
|
1 file changed, 8 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/Modules/_hmacopenssl.c b/Modules/_hmacopenssl.c
|
|
index 221714ca434..239445a0831 100644
|
|
--- a/Modules/_hmacopenssl.c
|
|
+++ b/Modules/_hmacopenssl.c
|
|
@@ -22,12 +22,12 @@
|
|
#include "_hashopenssl.h"
|
|
|
|
|
|
-#include <openssl/hmac.h>
|
|
|
|
typedef struct hmacopenssl_state {
|
|
PyTypeObject *HmacType;
|
|
} hmacopenssl_state;
|
|
|
|
+#include <openssl/hmac.h>
|
|
|
|
typedef struct {
|
|
PyObject_HEAD
|
|
@@ -39,7 +39,7 @@ typedef struct {
|
|
#include "clinic/_hmacopenssl.c.h"
|
|
/*[clinic input]
|
|
module _hmacopenssl
|
|
-class _hmacopenssl.HMAC "HmacObject *" "PyModule_GetState(module)->HmacType"
|
|
+class _hmacopenssl.HMAC "HmacObject *" "((hmacopenssl_state *)PyModule_GetState(module))->HmacType"
|
|
[clinic start generated code]*/
|
|
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=204b7f45847f57b4]*/
|
|
|
|
@@ -71,7 +71,7 @@ _hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
return NULL;
|
|
}
|
|
|
|
- /* name mut be lowercase */
|
|
+ /* name must be lowercase */
|
|
for (int i=0; digestmod[i]; i++) {
|
|
if (
|
|
((digestmod[i] < 'a') || (digestmod[i] > 'z'))
|
|
@@ -383,7 +383,8 @@ hmacopenssl_exec(PyObject *m) {
|
|
* on what hashes are supported rather than listing many
|
|
* and having some unsupported. Only init appropriate
|
|
* constants. */
|
|
- PyObject *temp;
|
|
+ PyObject *temp = NULL;
|
|
+ hmacopenssl_state *state;
|
|
|
|
temp = PyType_FromSpec(&HmacType_spec);
|
|
if (temp == NULL) {
|
|
@@ -394,6 +395,9 @@ hmacopenssl_exec(PyObject *m) {
|
|
goto fail;
|
|
}
|
|
|
|
+ state = PyModule_GetState(m);
|
|
+ state->HmacType = (PyTypeObject *)temp;
|
|
+
|
|
return 0;
|
|
|
|
fail:
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 5bd6eb14097fe4ff2bb639e5c50260b1379d0a69 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Thu, 1 Aug 2019 17:57:05 +0200
|
|
Subject: [PATCH 16/36] Use a stronger hash in multiprocessing handshake
|
|
|
|
Adapted from patch by David Malcolm,
|
|
https://bugs.python.org/issue17258
|
|
---
|
|
Lib/multiprocessing/connection.py | 8 ++++++--
|
|
1 file changed, 6 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/Lib/multiprocessing/connection.py b/Lib/multiprocessing/connection.py
|
|
index d3797503a75..a0b1538f88b 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'
|
|
@@ -718,7 +722,7 @@ def deliver_challenge(connection, authkey):
|
|
assert isinstance(authkey, bytes)
|
|
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)
|
|
@@ -732,7 +736,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.21.0
|
|
|
|
|
|
From 1dd4b2d6c4797b828ac38d8c62f1e69fce49f1e9 Mon Sep 17 00:00:00 2001
|
|
From: Marcel Plch <mplch@redhat.com>
|
|
Date: Fri, 2 Aug 2019 17:36:01 +0200
|
|
Subject: [PATCH 17/36] Fix refcounting
|
|
|
|
---
|
|
Modules/_hmacopenssl.c | 35 ++++++++++++++++++++++++++++++++++-
|
|
1 file changed, 34 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/Modules/_hmacopenssl.c b/Modules/_hmacopenssl.c
|
|
index 239445a0831..9c2882833d1 100644
|
|
--- a/Modules/_hmacopenssl.c
|
|
+++ b/Modules/_hmacopenssl.c
|
|
@@ -373,6 +373,34 @@ static struct PyMethodDef hmacopenssl_functions[] = {
|
|
{NULL, NULL} /* Sentinel */
|
|
};
|
|
|
|
+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. */
|
|
@@ -396,7 +424,10 @@ hmacopenssl_exec(PyObject *m) {
|
|
}
|
|
|
|
state = PyModule_GetState(m);
|
|
+
|
|
state->HmacType = (PyTypeObject *)temp;
|
|
+ Py_INCREF(temp);
|
|
+
|
|
|
|
return 0;
|
|
|
|
@@ -415,7 +446,9 @@ static struct PyModuleDef _hmacopenssl_def = {
|
|
.m_name = "_hmacopenssl",
|
|
.m_methods = hmacopenssl_functions,
|
|
.m_slots = hmacopenssl_slots,
|
|
- .m_size = sizeof(hmacopenssl_state)
|
|
+ .m_size = sizeof(hmacopenssl_state),
|
|
+ .m_traverse = hmacopenssl_traverse,
|
|
+ .m_clear = hmacopenssl_clear
|
|
};
|
|
|
|
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 4e1351cc2d704e8da7bb8125ad0ab194e8a02ec4 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 13:37:05 +0200
|
|
Subject: [PATCH 18/36] hmac: Don't default to md5 in FIPS mode
|
|
|
|
---
|
|
Lib/hmac.py | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/Lib/hmac.py b/Lib/hmac.py
|
|
index ed98406bd2e..7b8821edd58 100644
|
|
--- a/Lib/hmac.py
|
|
+++ b/Lib/hmac.py
|
|
@@ -165,7 +165,7 @@ def new(key, msg = None, digestmod = None):
|
|
"""
|
|
if _hashlib.get_fips_mode():
|
|
if digestmod is None:
|
|
- digestmod = 'md5'
|
|
+ raise ValueError("'digestmod' argument is mandatory in FIPS mode")
|
|
name = _get_openssl_name(digestmod)
|
|
result = _hmacopenssl.new(key, digestmod=name)
|
|
if msg:
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From ea44160c242101f14d22242a0722e730ef304b8b Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 14:20:58 +0200
|
|
Subject: [PATCH 19/36] Make _hmacopenssl.HMAC subclassable; subclass it as
|
|
hmac.HMAC under FIPS
|
|
|
|
This removes the _hmacopenssl.new function.
|
|
---
|
|
Lib/hmac.py | 27 +++++++-----
|
|
Lib/test/test_fips.py | 2 +-
|
|
Modules/_hmacopenssl.c | 75 ++++++++++++++++-----------------
|
|
Modules/clinic/_hmacopenssl.c.h | 39 +----------------
|
|
4 files changed, 56 insertions(+), 87 deletions(-)
|
|
|
|
diff --git a/Lib/hmac.py b/Lib/hmac.py
|
|
index 7b8821edd58..d479c5a4492 100644
|
|
--- a/Lib/hmac.py
|
|
+++ b/Lib/hmac.py
|
|
@@ -141,6 +141,8 @@ class HMAC:
|
|
|
|
|
|
def _get_openssl_name(digestmod):
|
|
+ if digestmod is None:
|
|
+ raise ValueError("'digestmod' argument is mandatory in FIPS mode")
|
|
if isinstance(digestmod, str):
|
|
return digestmod.lower()
|
|
elif callable(digestmod):
|
|
@@ -152,6 +154,20 @@ def _get_openssl_name(digestmod):
|
|
|
|
return digestmod.name.lower().replace('_', '-')
|
|
|
|
+
|
|
+class HMAC_openssl(_hmacopenssl.HMAC):
|
|
+ def __new__(cls, key, msg = None, digestmod = None):
|
|
+ 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 = None):
|
|
"""Create a new hashing object and return it.
|
|
|
|
@@ -163,13 +179,4 @@ def new(key, msg = None, digestmod = None):
|
|
method, and can ask for the hash value at any time by calling its digest()
|
|
method.
|
|
"""
|
|
- if _hashlib.get_fips_mode():
|
|
- if digestmod is None:
|
|
- raise ValueError("'digestmod' argument is mandatory in FIPS mode")
|
|
- name = _get_openssl_name(digestmod)
|
|
- result = _hmacopenssl.new(key, digestmod=name)
|
|
- if msg:
|
|
- result.update(msg)
|
|
- return result
|
|
- else:
|
|
- return HMAC(key, msg, digestmod)
|
|
+ return HMAC(key, msg, digestmod)
|
|
diff --git a/Lib/test/test_fips.py b/Lib/test/test_fips.py
|
|
index bee911ef405..34812e6098a 100644
|
|
--- a/Lib/test/test_fips.py
|
|
+++ b/Lib/test/test_fips.py
|
|
@@ -54,7 +54,7 @@ class HashlibFipsTests(unittest.TestCase):
|
|
self.compare_hashes(hashlib.sha512(b'abc'), _hashlib.openssl_sha512(b'abc'))
|
|
|
|
def test_hmac_digests(self):
|
|
- self.compare_hashes(_hmacopenssl.new(b'My hovercraft is full of eels', digestmod='sha384'),
|
|
+ self.compare_hashes(_hmacopenssl.HMAC(b'My hovercraft is full of eels', digestmod='sha384'),
|
|
hmac.new(b'My hovercraft is full of eels', digestmod='sha384'))
|
|
|
|
|
|
diff --git a/Modules/_hmacopenssl.c b/Modules/_hmacopenssl.c
|
|
index 9c2882833d1..7d3d9739f3a 100644
|
|
--- a/Modules/_hmacopenssl.c
|
|
+++ b/Modules/_hmacopenssl.c
|
|
@@ -41,33 +41,25 @@ typedef struct {
|
|
module _hmacopenssl
|
|
class _hmacopenssl.HMAC "HmacObject *" "((hmacopenssl_state *)PyModule_GetState(module))->HmacType"
|
|
[clinic start generated code]*/
|
|
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=204b7f45847f57b4]*/
|
|
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=9fe07a087adc2cf9]*/
|
|
|
|
|
|
-/*[clinic input]
|
|
-_hmacopenssl.new
|
|
-
|
|
- key: Py_buffer
|
|
- *
|
|
- digestmod: str
|
|
-
|
|
-Return a new hmac object.
|
|
-[clinic start generated code]*/
|
|
-
|
|
static PyObject *
|
|
-_hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
- const char *digestmod)
|
|
-/*[clinic end generated code: output=46f1cb4e02921922 input=be8c0c2e4fad508c]*/
|
|
+Hmac_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
|
|
{
|
|
- hmacopenssl_state *state;
|
|
-
|
|
- if (digestmod == NULL) {
|
|
- PyErr_SetString(PyExc_ValueError, "digestmod must be specified");
|
|
+ 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;
|
|
}
|
|
|
|
- state = PyModule_GetState(module);
|
|
- if (state == NULL) {
|
|
+ if (digestmod == NULL) {
|
|
+ PyErr_SetString(PyExc_ValueError, "digestmod must be specified");
|
|
return NULL;
|
|
}
|
|
|
|
@@ -106,8 +98,8 @@ _hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
|
|
int r = HMAC_Init_ex(
|
|
ctx,
|
|
- (const char*)key->buf,
|
|
- key->len,
|
|
+ (const char*)key.buf,
|
|
+ key.len,
|
|
digest,
|
|
NULL /*impl*/);
|
|
if (r == 0) {
|
|
@@ -115,7 +107,10 @@ _hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
goto error;
|
|
}
|
|
|
|
- retval = (HmacObject *)PyObject_New(HmacObject, state->HmacType);
|
|
+ PyBuffer_Release(&key);
|
|
+ key.buf = NULL;
|
|
+
|
|
+ retval = (HmacObject *)subtype->tp_alloc(subtype, 0);
|
|
if (retval == NULL) {
|
|
goto error;
|
|
}
|
|
@@ -130,6 +125,7 @@ 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;
|
|
}
|
|
|
|
@@ -145,19 +141,27 @@ _hmacopenssl_HMAC_copy_impl(HmacObject *self)
|
|
{
|
|
HmacObject *retval;
|
|
|
|
- retval = (HmacObject *)PyObject_New(HmacObject, (PyTypeObject *)PyObject_Type((PyObject *)self));
|
|
+ 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;
|
|
|
|
- int r = HMAC_CTX_copy(retval->ctx, self->ctx);
|
|
- if (r == 0) {
|
|
- PyObject_Del(retval);
|
|
- return _setException(PyExc_ValueError);
|
|
- }
|
|
+ retval->lock = NULL;
|
|
|
|
return (PyObject *)retval;
|
|
}
|
|
@@ -169,8 +173,8 @@ _hmac_dealloc(HmacObject *self)
|
|
PyThread_free_lock(self->lock);
|
|
}
|
|
HMAC_CTX_free(self->ctx);
|
|
- Py_XDECREF(self->name);
|
|
- PyObject_Del(self);
|
|
+ Py_CLEAR(self->name);
|
|
+ Py_TYPE(self)->tp_free(self);
|
|
}
|
|
|
|
static PyObject *
|
|
@@ -357,6 +361,7 @@ static PyType_Slot HmacType_slots[] = {
|
|
{Py_tp_methods, Hmac_methods},
|
|
{Py_tp_getset, Hmac_getset},
|
|
{Py_tp_members, Hmac_members},
|
|
+ {Py_tp_new, Hmac_new},
|
|
{0, NULL}
|
|
};
|
|
|
|
@@ -368,11 +373,6 @@ PyType_Spec HmacType_spec = {
|
|
};
|
|
|
|
|
|
-static struct PyMethodDef hmacopenssl_functions[] = {
|
|
- _HMACOPENSSL_NEW_METHODDEF
|
|
- {NULL, NULL} /* Sentinel */
|
|
-};
|
|
-
|
|
static int
|
|
hmacopenssl_traverse(PyObject *self, visitproc visit, void *arg)
|
|
{
|
|
@@ -444,7 +444,6 @@ static PyModuleDef_Slot hmacopenssl_slots[] = {
|
|
static struct PyModuleDef _hmacopenssl_def = {
|
|
PyModuleDef_HEAD_INIT, /* m_base */
|
|
.m_name = "_hmacopenssl",
|
|
- .m_methods = hmacopenssl_functions,
|
|
.m_slots = hmacopenssl_slots,
|
|
.m_size = sizeof(hmacopenssl_state),
|
|
.m_traverse = hmacopenssl_traverse,
|
|
diff --git a/Modules/clinic/_hmacopenssl.c.h b/Modules/clinic/_hmacopenssl.c.h
|
|
index b472a6eddd3..861acc11bfd 100644
|
|
--- a/Modules/clinic/_hmacopenssl.c.h
|
|
+++ b/Modules/clinic/_hmacopenssl.c.h
|
|
@@ -2,43 +2,6 @@
|
|
preserve
|
|
[clinic start generated code]*/
|
|
|
|
-PyDoc_STRVAR(_hmacopenssl_new__doc__,
|
|
-"new($module, /, key, *, digestmod)\n"
|
|
-"--\n"
|
|
-"\n"
|
|
-"Return a new hmac object.");
|
|
-
|
|
-#define _HMACOPENSSL_NEW_METHODDEF \
|
|
- {"new", (PyCFunction)_hmacopenssl_new, METH_FASTCALL, _hmacopenssl_new__doc__},
|
|
-
|
|
-static PyObject *
|
|
-_hmacopenssl_new_impl(PyObject *module, Py_buffer *key,
|
|
- const char *digestmod);
|
|
-
|
|
-static PyObject *
|
|
-_hmacopenssl_new(PyObject *module, PyObject **args, Py_ssize_t nargs, PyObject *kwnames)
|
|
-{
|
|
- PyObject *return_value = NULL;
|
|
- static const char * const _keywords[] = {"key", "digestmod", NULL};
|
|
- static _PyArg_Parser _parser = {"y*$s:new", _keywords, 0};
|
|
- Py_buffer key = {NULL, NULL};
|
|
- const char *digestmod;
|
|
-
|
|
- if (!_PyArg_ParseStack(args, nargs, kwnames, &_parser,
|
|
- &key, &digestmod)) {
|
|
- goto exit;
|
|
- }
|
|
- return_value = _hmacopenssl_new_impl(module, &key, digestmod);
|
|
-
|
|
-exit:
|
|
- /* Cleanup for key */
|
|
- if (key.obj) {
|
|
- PyBuffer_Release(&key);
|
|
- }
|
|
-
|
|
- return return_value;
|
|
-}
|
|
-
|
|
PyDoc_STRVAR(_hmacopenssl_HMAC_copy__doc__,
|
|
"copy($self, /)\n"
|
|
"--\n"
|
|
@@ -130,4 +93,4 @@ _hmacopenssl_HMAC_hexdigest(HmacObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
return _hmacopenssl_HMAC_hexdigest_impl(self);
|
|
}
|
|
-/*[clinic end generated code: output=10b6e8cac6d7a2c9 input=a9049054013a1b77]*/
|
|
+/*[clinic end generated code: output=d93ad460795d49b5 input=a9049054013a1b77]*/
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From e3d6a5adbe0032726dff0b1f6473ce3ff63fad51 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 16:10:36 +0200
|
|
Subject: [PATCH 20/36] Fix _hmacopenssl.HMAC.block_size
|
|
|
|
---
|
|
Modules/_hmacopenssl.c | 2 +-
|
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
|
diff --git a/Modules/_hmacopenssl.c b/Modules/_hmacopenssl.c
|
|
index 7d3d9739f3a..a24c8ba0229 100644
|
|
--- a/Modules/_hmacopenssl.c
|
|
+++ b/Modules/_hmacopenssl.c
|
|
@@ -318,7 +318,7 @@ _hmacopenssl_get_block_size(HmacObject *self, void *closure)
|
|
if (md == NULL) {
|
|
return _setException(PyExc_ValueError);
|
|
}
|
|
- return PyLong_FromLong(EVP_MD_size(md));
|
|
+ return PyLong_FromLong(EVP_MD_block_size(md));
|
|
}
|
|
|
|
static PyMethodDef Hmac_methods[] = {
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From f925a1165d7b07fb4d0bba216f7dbc387b94e94d Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 15:02:08 +0200
|
|
Subject: [PATCH 21/36] distutils upload: Skip md5 checksum in FIPS mode
|
|
|
|
---
|
|
Lib/distutils/command/upload.py | 11 ++++++++++-
|
|
Lib/distutils/tests/test_upload.py | 13 +++++++++++--
|
|
2 files changed, 21 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py
|
|
index 32dda359bad..0edb39efd4c 100644
|
|
--- a/Lib/distutils/command/upload.py
|
|
+++ b/Lib/distutils/command/upload.py
|
|
@@ -102,7 +102,6 @@ class upload(PyPIRCCommand):
|
|
'content': (os.path.basename(filename),content),
|
|
'filetype': command,
|
|
'pyversion': pyversion,
|
|
- 'md5_digest': hashlib.md5(content).hexdigest(),
|
|
|
|
# additional meta-data
|
|
'metadata_version': '1.0',
|
|
@@ -121,6 +120,16 @@ class upload(PyPIRCCommand):
|
|
'requires': meta.get_requires(),
|
|
'obsoletes': meta.get_obsoletes(),
|
|
}
|
|
+ try:
|
|
+ digest = hashlib.md5(content).hexdigest()
|
|
+ except ValueError as e:
|
|
+ msg = 'calculating md5 checksum failed: %s' % e
|
|
+ self.announce(msg, log.ERROR)
|
|
+ if not hashlib.get_fips_mode():
|
|
+ # this really shouldn't fail
|
|
+ raise
|
|
+ else:
|
|
+ data['md5_digest'] = digest
|
|
comment = ''
|
|
if command == 'bdist_rpm':
|
|
dist, version, id = platform.dist()
|
|
diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py
|
|
index c17d8e7d54e..b4b64e97737 100644
|
|
--- a/Lib/distutils/tests/test_upload.py
|
|
+++ b/Lib/distutils/tests/test_upload.py
|
|
@@ -3,6 +3,7 @@ import os
|
|
import unittest
|
|
import unittest.mock as mock
|
|
from urllib.request import HTTPError
|
|
+import hashlib
|
|
|
|
from test.support import run_unittest
|
|
|
|
@@ -130,7 +131,11 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
|
|
|
|
# what did we send ?
|
|
headers = dict(self.last_open.req.headers)
|
|
- self.assertEqual(headers['Content-length'], '2162')
|
|
+ if hashlib.get_fips_mode():
|
|
+ # md5 hash is omitted
|
|
+ self.assertEqual(headers['Content-length'], '2020')
|
|
+ else:
|
|
+ self.assertEqual(headers['Content-length'], '2162')
|
|
content_type = headers['Content-type']
|
|
self.assertTrue(content_type.startswith('multipart/form-data'))
|
|
self.assertEqual(self.last_open.req.get_method(), 'POST')
|
|
@@ -166,7 +171,11 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
|
|
cmd.run()
|
|
|
|
headers = dict(self.last_open.req.headers)
|
|
- self.assertEqual(headers['Content-length'], '2172')
|
|
+ if hashlib.get_fips_mode():
|
|
+ # md5 hash is omitted
|
|
+ self.assertEqual(headers['Content-length'], '2030')
|
|
+ else:
|
|
+ self.assertEqual(headers['Content-length'], '2172')
|
|
self.assertIn(b'long description\r', self.last_open.req.data)
|
|
|
|
def test_upload_fails(self):
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 936a7835a1873952eab8f7ec3b9b67f63786c001 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 15:32:25 +0200
|
|
Subject: [PATCH 22/36] Fix HMAC tests on FIPS mode
|
|
|
|
---
|
|
Lib/hmac.py | 3 +++
|
|
Lib/test/test_hmac.py | 26 ++++++++++++++++++++++++++
|
|
2 files changed, 29 insertions(+)
|
|
|
|
diff --git a/Lib/hmac.py b/Lib/hmac.py
|
|
index d479c5a4492..7af94c39ed6 100644
|
|
--- a/Lib/hmac.py
|
|
+++ b/Lib/hmac.py
|
|
@@ -157,6 +157,9 @@ def _get_openssl_name(digestmod):
|
|
|
|
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:
|
|
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
|
|
index 338e0215a41..5b090727ef6 100644
|
|
--- a/Lib/test/test_hmac.py
|
|
+++ b/Lib/test/test_hmac.py
|
|
@@ -250,6 +250,7 @@ class TestVectorsTestCase(unittest.TestCase):
|
|
def test_sha512_rfc4231(self):
|
|
self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)
|
|
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.')
|
|
@requires_hashdigest('sha256')
|
|
def test_legacy_block_size_warnings(self):
|
|
class MockCrazyHash(object):
|
|
@@ -298,6 +299,14 @@ class ConstructorTestCase(unittest.TestCase):
|
|
self.fail("Standard constructor call raised exception.")
|
|
|
|
@ignore_warning
|
|
+ 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.")
|
|
+
|
|
@requires_hashdigest('sha256')
|
|
def test_with_str_key(self):
|
|
# Pass a key of type str, which is an error, because it expects a key
|
|
@@ -366,6 +375,7 @@ class SanityTestCase(unittest.TestCase):
|
|
|
|
class CopyTestCase(unittest.TestCase):
|
|
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode")
|
|
@requires_hashdigest('sha256')
|
|
def test_attributes(self):
|
|
# Testing if attributes are of same type.
|
|
@@ -378,6 +388,7 @@ class CopyTestCase(unittest.TestCase):
|
|
self.assertEqual(type(h1.outer), type(h2.outer),
|
|
"Types of outer don't match.")
|
|
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode")
|
|
@requires_hashdigest('sha256')
|
|
def test_realcopy(self):
|
|
# Testing if the copy method created a real copy.
|
|
@@ -390,6 +401,21 @@ class CopyTestCase(unittest.TestCase):
|
|
self.assertTrue(id(h1.outer) != id(h2.outer),
|
|
"No real copy of the attribute 'outer'.")
|
|
|
|
+ def test_realcopy(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
|
|
+
|
|
@requires_hashdigest('sha256')
|
|
def test_equality(self):
|
|
# Testing if the copy has the same digests.
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 264c6eed5a5d060fe7bad7e4410d920cecbb083c Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 16:37:12 +0200
|
|
Subject: [PATCH 23/36] test_tools: Skip md5sum tests in FIPS mode
|
|
|
|
---
|
|
Lib/test/test_tools/test_md5sum.py | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/Lib/test/test_tools/test_md5sum.py b/Lib/test/test_tools/test_md5sum.py
|
|
index fb565b73778..7028a4dc214 100644
|
|
--- a/Lib/test/test_tools/test_md5sum.py
|
|
+++ b/Lib/test/test_tools/test_md5sum.py
|
|
@@ -4,11 +4,15 @@ import os
|
|
import unittest
|
|
from test import support
|
|
from test.support.script_helper import assert_python_ok, assert_python_failure
|
|
+import hashlib
|
|
|
|
from test.test_tools import scriptsdir, skip_if_missing
|
|
|
|
skip_if_missing()
|
|
|
|
+if hashlib.get_fips_mode():
|
|
+ raise unittest.SkipTest("md5sum won't work at all in FIPS mode")
|
|
+
|
|
class MD5SumTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From c5373f5e7ae4ecfe8337ee3c73b594b5f0ebb411 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 17:21:16 +0200
|
|
Subject: [PATCH 24/36] _hashopenssl: Include hash name in the error message
|
|
|
|
---
|
|
Modules/_hashopenssl.c | 4 ++--
|
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
|
index 7dfd70822b9..0563473c627 100644
|
|
--- a/Modules/_hashopenssl.c
|
|
+++ b/Modules/_hashopenssl.c
|
|
@@ -431,7 +431,7 @@ EVPnew(PyObject *name_obj,
|
|
EVPobject *self;
|
|
|
|
if (!digest && !initial_ctx) {
|
|
- PyErr_SetString(PyExc_ValueError, "unsupported hash type");
|
|
+ PyErr_Format(PyExc_ValueError, "unsupported hash type %U", name_obj);
|
|
return NULL;
|
|
}
|
|
|
|
@@ -622,7 +622,7 @@ pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict)
|
|
|
|
digest = EVP_get_digestbyname(name);
|
|
if (digest == NULL) {
|
|
- PyErr_SetString(PyExc_ValueError, "unsupported hash type");
|
|
+ PyErr_Format(PyExc_ValueError, "unsupported hash type %s", name);
|
|
goto end;
|
|
}
|
|
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From c3f9e194eb5f86ebec4db2820b8968d6896abc8b Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 18:23:57 +0200
|
|
Subject: [PATCH 25/36] Make hashlib tests pass in FIPS mode (with some hashlib
|
|
changes)
|
|
|
|
---
|
|
Lib/hashlib.py | 18 +++++++++--
|
|
Lib/test/test_hashlib.py | 67 ++++++++++++++++++++++++++++------------
|
|
Modules/_hashopenssl.c | 14 +++++++++
|
|
3 files changed, 78 insertions(+), 21 deletions(-)
|
|
|
|
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
|
index ca1dd202251..d3344f60b2e 100644
|
|
--- a/Lib/hashlib.py
|
|
+++ b/Lib/hashlib.py
|
|
@@ -155,7 +155,18 @@ 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 get_fips_mode():
|
|
+ # Use OpenSSL names for Python built-in hashes
|
|
+ orig_name = name
|
|
+ name = {
|
|
+ 'sha3_224': "sha3-224",
|
|
+ 'sha3_256': "sha3-256",
|
|
+ 'sha3_384': "sha3-384",
|
|
+ 'sha3_512': "sha3-512",
|
|
+ 'shake_128': "shake128",
|
|
+ 'shake_256': "shake256",
|
|
+ }.get(name, name)
|
|
+ else:
|
|
if name in {'blake2b', 'blake2s'}:
|
|
# Prefer our blake2 implementation.
|
|
# OpenSSL 1.1.0 comes with a limited implementation of blake2b/s.
|
|
@@ -163,7 +174,10 @@ def __hash_new(name, data=b'', **kwargs):
|
|
# salt, personal, tree hashing or SSE.
|
|
return __get_builtin_constructor(name)(data, **kwargs)
|
|
try:
|
|
- return _hashlib.new(name, data)
|
|
+ retval = _hashlib.new(name, data)
|
|
+ if get_fips_mode() and name != orig_name:
|
|
+ retval._set_name(orig_name)
|
|
+ return retval
|
|
except ValueError:
|
|
# If the _hashlib module (OpenSSL) doesn't support the named
|
|
# hash, try using our builtin implementations.
|
|
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
|
|
index e57c93b42f5..9745bfd6fa2 100644
|
|
--- a/Lib/test/test_hashlib.py
|
|
+++ b/Lib/test/test_hashlib.py
|
|
@@ -31,6 +31,11 @@ COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount')
|
|
c_hashlib = import_fresh_module('hashlib', fresh=['_hashlib'])
|
|
py_hashlib = import_fresh_module('hashlib', blocked=['_hashlib'])
|
|
|
|
+if hashlib.get_fips_mode():
|
|
+ FIPS_DISABLED = {'md5', 'MD5', 'blake2b', 'blake2s'}
|
|
+else:
|
|
+ FIPS_DISABLED = set()
|
|
+
|
|
try:
|
|
import _blake2
|
|
except ImportError:
|
|
@@ -86,6 +91,11 @@ class HashLibTestCase(unittest.TestCase):
|
|
# Issue #14693: fallback modules are always compiled under POSIX
|
|
_warn_on_extension_import = os.name == 'posix' or COMPILED_WITH_PYDEBUG
|
|
|
|
+ if hashlib.get_fips_mode():
|
|
+ shakes = set()
|
|
+ supported_hash_names = tuple(
|
|
+ n for n in supported_hash_names if n not in FIPS_DISABLED)
|
|
+
|
|
def _conditional_import_module(self, module_name):
|
|
"""Import a module and return a reference to it or None on failure."""
|
|
try:
|
|
@@ -93,8 +103,20 @@ class HashLibTestCase(unittest.TestCase):
|
|
except ModuleNotFoundError as error:
|
|
if self._warn_on_extension_import:
|
|
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 _has_shake_extras(self, hasher):
|
|
+ """Return true if the hasher should have "shake" API (digest length)"""
|
|
+ if hasher.name not in self.shakes:
|
|
+ return False
|
|
+ _hashlib = self._conditional_import_module('_hashlib')
|
|
+ if _hashlib and isinstance(hasher, _hashlib.HASH):
|
|
+ return False
|
|
+ return True
|
|
+
|
|
def __init__(self, *args, **kwargs):
|
|
algorithms = set()
|
|
for algorithm in self.supported_hash_names:
|
|
@@ -182,15 +204,13 @@ class HashLibTestCase(unittest.TestCase):
|
|
a = array.array("b", range(10))
|
|
for cons in self.hash_constructors:
|
|
c = cons(a)
|
|
- if (c.name in self.shakes
|
|
- and not cons.__name__.startswith('openssl_')
|
|
- ):
|
|
+ if self._has_shake_extras(c):
|
|
c.hexdigest(16)
|
|
else:
|
|
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()))
|
|
|
|
@@ -202,6 +222,12 @@ class HashLibTestCase(unittest.TestCase):
|
|
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
|
|
self.assertRaises(TypeError, hashlib.new, 1)
|
|
|
|
+ @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')
|
|
@@ -231,9 +257,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
def test_hexdigest(self):
|
|
for cons in self.hash_constructors:
|
|
h = cons()
|
|
- if (h.name in self.shakes
|
|
- and not cons.__name__.startswith('openssl_')
|
|
- ):
|
|
+ if self._has_shake_extras(h):
|
|
self.assertIsInstance(h.digest(16), bytes)
|
|
self.assertEqual(hexstr(h.digest(16)), h.hexdigest(16))
|
|
else:
|
|
@@ -245,9 +269,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
large_sizes = (2**29, 2**32-10, 2**32+10, 2**61, 2**64-10, 2**64+10)
|
|
for cons in self.hash_constructors:
|
|
h = cons()
|
|
- if h.name not in self.shakes:
|
|
- continue
|
|
- if cons.__name__.startswith('openssl_'):
|
|
+ if not self._has_shake_extras(h):
|
|
continue
|
|
for digest in h.digest, h.hexdigest:
|
|
with self.assertRaises((ValueError, OverflowError)):
|
|
@@ -278,9 +300,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
m1.update(bees)
|
|
m1.update(cees)
|
|
m1.update(dees)
|
|
- if (m1.name in self.shakes
|
|
- and not cons.__name__.startswith('openssl_')
|
|
- ):
|
|
+ if self._has_shake_extras(m1):
|
|
args = (16,)
|
|
else:
|
|
args = ()
|
|
@@ -349,7 +369,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')
|
|
@@ -392,7 +413,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)
|
|
@@ -432,22 +454,27 @@ 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')
|
|
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode")
|
|
def test_case_md5_1(self):
|
|
self.check('md5', b'abc', '900150983cd24fb0d6963f7d28e17f72')
|
|
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode")
|
|
def test_case_md5_2(self):
|
|
self.check('md5',
|
|
b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
|
'd174ab98d277d9f5a5611c2c9f419d9f')
|
|
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode")
|
|
@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')
|
|
|
|
+ @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode")
|
|
@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):
|
|
@@ -829,14 +856,16 @@ class HashLibTestCase(unittest.TestCase):
|
|
m = cons(b'x' * gil_minsize)
|
|
m.update(b'1')
|
|
|
|
- m = hashlib.md5()
|
|
+ m = hashlib.sha1()
|
|
m.update(b'1')
|
|
m.update(b'#' * gil_minsize)
|
|
m.update(b'1')
|
|
- self.assertEqual(m.hexdigest(), 'cb1e1a2cbc80be75e19935d621fb9b21')
|
|
+ self.assertEqual(m.hexdigest(),
|
|
+ 'c45f7445ca0ea087d7a1758fbea07935f267c46a')
|
|
|
|
- m = hashlib.md5(b'x' * gil_minsize)
|
|
- self.assertEqual(m.hexdigest(), 'cfb767f225d58469c5de3632a8803958')
|
|
+ m = hashlib.sha1(b'x' * gil_minsize)
|
|
+ self.assertEqual(m.hexdigest(),
|
|
+ '63fda1efde982ba1ffe9d53035bff5c9ce4758fb')
|
|
|
|
@unittest.skipUnless(threading, 'Threading required for this test.')
|
|
@support.reap_threads
|
|
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
|
index 0563473c627..e330423e266 100644
|
|
--- a/Modules/_hashopenssl.c
|
|
+++ b/Modules/_hashopenssl.c
|
|
@@ -256,11 +256,25 @@ EVP_update(EVPobject *self, PyObject *args)
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
+static PyObject *
|
|
+EVP_set_name(EVPobject *self, PyObject *new_name)
|
|
+{
|
|
+ if (!PyUnicode_CheckExact(new_name)) {
|
|
+ PyErr_SetString(PyExc_TypeError, "expected string");
|
|
+ return NULL;
|
|
+ }
|
|
+ Py_DECREF(self->name);
|
|
+ Py_INCREF(new_name);
|
|
+ self->name = new_name;
|
|
+ Py_RETURN_NONE;
|
|
+}
|
|
+
|
|
static PyMethodDef EVP_methods[] = {
|
|
{"update", (PyCFunction)EVP_update, METH_VARARGS, EVP_update__doc__},
|
|
{"digest", (PyCFunction)EVP_digest, METH_NOARGS, EVP_digest__doc__},
|
|
{"hexdigest", (PyCFunction)EVP_hexdigest, METH_NOARGS, EVP_hexdigest__doc__},
|
|
{"copy", (PyCFunction)EVP_copy, METH_NOARGS, EVP_copy__doc__},
|
|
+ {"_set_name", (PyCFunction)EVP_set_name, METH_O, EVP_copy__doc__},
|
|
{NULL, NULL} /* sentinel */
|
|
};
|
|
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From a40d1cb8672bc2c5403b6e9ff6eaa3324282de09 Mon Sep 17 00:00:00 2001
|
|
From: Lumir Balhar <lbalhar@redhat.com>
|
|
Date: Wed, 14 Aug 2019 14:43:07 +0200
|
|
Subject: [PATCH 26/36] distutils upload: only add md5 if available, but
|
|
*always* use sha256
|
|
|
|
---
|
|
Lib/distutils/command/upload.py | 3 ++-
|
|
Lib/distutils/tests/test_upload.py | 14 ++++++++------
|
|
2 files changed, 10 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py
|
|
index 0edb39efd4c..170acb3d6b6 100644
|
|
--- a/Lib/distutils/command/upload.py
|
|
+++ b/Lib/distutils/command/upload.py
|
|
@@ -102,6 +102,7 @@ class upload(PyPIRCCommand):
|
|
'content': (os.path.basename(filename),content),
|
|
'filetype': command,
|
|
'pyversion': pyversion,
|
|
+ 'sha256_digest': hashlib.sha256(content).hexdigest(),
|
|
|
|
# additional meta-data
|
|
'metadata_version': '1.0',
|
|
@@ -124,7 +125,7 @@ class upload(PyPIRCCommand):
|
|
digest = hashlib.md5(content).hexdigest()
|
|
except ValueError as e:
|
|
msg = 'calculating md5 checksum failed: %s' % e
|
|
- self.announce(msg, log.ERROR)
|
|
+ self.announce(msg, log.INFO)
|
|
if not hashlib.get_fips_mode():
|
|
# this really shouldn't fail
|
|
raise
|
|
diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py
|
|
index b4b64e97737..f720a7905dd 100644
|
|
--- a/Lib/distutils/tests/test_upload.py
|
|
+++ b/Lib/distutils/tests/test_upload.py
|
|
@@ -132,10 +132,11 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
|
|
# what did we send ?
|
|
headers = dict(self.last_open.req.headers)
|
|
if hashlib.get_fips_mode():
|
|
- # md5 hash is omitted
|
|
- self.assertEqual(headers['Content-length'], '2020')
|
|
+ # only sha256 hash is used
|
|
+ self.assertEqual(headers['Content-length'], '2197')
|
|
else:
|
|
- self.assertEqual(headers['Content-length'], '2162')
|
|
+ # both sha256 and md5 hashes are used
|
|
+ self.assertEqual(headers['Content-length'], '2339')
|
|
content_type = headers['Content-type']
|
|
self.assertTrue(content_type.startswith('multipart/form-data'))
|
|
self.assertEqual(self.last_open.req.get_method(), 'POST')
|
|
@@ -172,10 +173,11 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
|
|
|
|
headers = dict(self.last_open.req.headers)
|
|
if hashlib.get_fips_mode():
|
|
- # md5 hash is omitted
|
|
- self.assertEqual(headers['Content-length'], '2030')
|
|
+ # only sha256 hash is used
|
|
+ self.assertEqual(headers['Content-length'], '2207')
|
|
else:
|
|
- self.assertEqual(headers['Content-length'], '2172')
|
|
+ # both sha256 and md5 hashes are used
|
|
+ self.assertEqual(headers['Content-length'], '2349')
|
|
self.assertIn(b'long description\r', self.last_open.req.data)
|
|
|
|
def test_upload_fails(self):
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From e0d2cca338186bbc5cb9c67bb4402dec38fed5a7 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 26 Aug 2019 15:55:48 +0200
|
|
Subject: [PATCH 27/36] Add the usedforsecurity argument back
|
|
|
|
---
|
|
Lib/hashlib.py | 4 ++-
|
|
Modules/_hashopenssl.c | 82 +++++++++++++++++++++++++++++++-----------
|
|
2 files changed, 65 insertions(+), 21 deletions(-)
|
|
|
|
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
|
index d3344f60b2e..cd3b035b1d7 100644
|
|
--- a/Lib/hashlib.py
|
|
+++ b/Lib/hashlib.py
|
|
@@ -174,7 +174,9 @@ def __hash_new(name, data=b'', **kwargs):
|
|
# salt, personal, tree hashing or SSE.
|
|
return __get_builtin_constructor(name)(data, **kwargs)
|
|
try:
|
|
- retval = _hashlib.new(name, data)
|
|
+ usedforsecurity = kwargs.pop('usedforsecurity', True)
|
|
+ retval = _hashlib.new(
|
|
+ name, data, usedforsecurity=usedforsecurity)
|
|
if get_fips_mode() and name != orig_name:
|
|
retval._set_name(orig_name)
|
|
return retval
|
|
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
|
index e330423e266..b621c330c32 100644
|
|
--- a/Modules/_hashopenssl.c
|
|
+++ b/Modules/_hashopenssl.c
|
|
@@ -318,6 +318,25 @@ EVP_repr(EVPobject *self)
|
|
return PyUnicode_FromFormat("<%U HASH object @ %p>", self->name, self);
|
|
}
|
|
|
|
+
|
|
+static void
|
|
+mc_ctx_init(EVP_MD_CTX *ctx, int usedforsecurity)
|
|
+{
|
|
+ EVP_MD_CTX_init(ctx);
|
|
+ /*
|
|
+ If the user has declared that this digest is being used in a
|
|
+ non-security role (e.g. indexing into a data structure), set
|
|
+ the exception flag for openssl to allow it
|
|
+ */
|
|
+ if (!usedforsecurity) {
|
|
+#ifdef EVP_MD_CTX_FLAG_NON_FIPS_ALLOW
|
|
+ EVP_MD_CTX_set_flags(ctx,
|
|
+ EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
|
|
+#endif
|
|
+ }
|
|
+}
|
|
+
|
|
+
|
|
#if HASH_OBJ_CONSTRUCTOR
|
|
static int
|
|
EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
|
|
@@ -328,9 +347,10 @@ EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
|
|
Py_buffer view;
|
|
char *nameStr;
|
|
const EVP_MD *digest;
|
|
+ int usedforsecurity=1;
|
|
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:HASH", kwlist,
|
|
- &name_obj, &data_obj)) {
|
|
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O$d:HASH", kwlist,
|
|
+ &name_obj, &data_obj, &usedforsecurity)) {
|
|
return -1;
|
|
}
|
|
|
|
@@ -351,7 +371,8 @@ EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds)
|
|
PyBuffer_Release(&view);
|
|
return -1;
|
|
}
|
|
- if (!EVP_DigestInit(self->ctx, digest)) {
|
|
+ mc_ctx_init(&self->ctx, usedforsecurity);
|
|
+ if (!EVP_DigestInit_ex(self->ctx, digest, NULL)) {
|
|
_setException(PyExc_ValueError);
|
|
if (data_obj)
|
|
PyBuffer_Release(&view);
|
|
@@ -440,7 +461,7 @@ static PyTypeObject EVPtype = {
|
|
static PyObject *
|
|
EVPnew(PyObject *name_obj,
|
|
const EVP_MD *digest, const EVP_MD_CTX *initial_ctx,
|
|
- const unsigned char *cp, Py_ssize_t len)
|
|
+ const unsigned char *cp, Py_ssize_t len, int usedforsecurity)
|
|
{
|
|
EVPobject *self;
|
|
|
|
@@ -455,7 +476,8 @@ EVPnew(PyObject *name_obj,
|
|
if (initial_ctx) {
|
|
EVP_MD_CTX_copy(self->ctx, initial_ctx);
|
|
} else {
|
|
- if (!EVP_DigestInit(self->ctx, digest)) {
|
|
+ mc_ctx_init(self->ctx, usedforsecurity);
|
|
+ if (!EVP_DigestInit_ex(self->ctx, digest, NULL)) {
|
|
_setException(PyExc_ValueError);
|
|
Py_DECREF(self);
|
|
return NULL;
|
|
@@ -484,26 +506,35 @@ An optional string argument may be provided and will be\n\
|
|
automatically hashed.\n\
|
|
\n\
|
|
The MD5 and SHA1 algorithms are always supported.\n");
|
|
+static PyObject *_EVP_new_impl(PyObject *name_obj, char *name, PyObject *data_obj, int usedforsecurity);
|
|
|
|
static PyObject *
|
|
EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
|
|
{
|
|
- static char *kwlist[] = {"name", "string", NULL};
|
|
+ static char *kwlist[] = {"name", "string", "usedforsecurity", NULL};
|
|
PyObject *name_obj = NULL;
|
|
PyObject *data_obj = NULL;
|
|
- Py_buffer view = { 0 };
|
|
- PyObject *ret_obj;
|
|
- char *name;
|
|
- const EVP_MD *digest;
|
|
+ int usedforsecurity = 1;
|
|
|
|
- if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|O:new", kwlist,
|
|
- &name_obj, &data_obj)) {
|
|
+ if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|O$p:new", kwlist,
|
|
+ &name_obj, &data_obj, &usedforsecurity)) {
|
|
return NULL;
|
|
}
|
|
+ return _EVP_new_impl(name_obj, NULL, data_obj, usedforsecurity);
|
|
+}
|
|
|
|
- if (!PyArg_Parse(name_obj, "s", &name)) {
|
|
- PyErr_SetString(PyExc_TypeError, "name must be a string");
|
|
- return NULL;
|
|
+static PyObject *
|
|
+_EVP_new_impl(PyObject *name_obj, char *name, PyObject *data_obj, int usedforsecurity)
|
|
+{
|
|
+ Py_buffer view = { 0 };
|
|
+ PyObject *ret_obj;
|
|
+ const EVP_MD *digest;
|
|
+
|
|
+ if (!name) {
|
|
+ if (!PyArg_Parse(name_obj, "s", &name)) {
|
|
+ PyErr_SetString(PyExc_TypeError, "name must be a string");
|
|
+ return NULL;
|
|
+ }
|
|
}
|
|
|
|
if (data_obj)
|
|
@@ -511,7 +542,7 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
|
|
|
|
digest = EVP_get_digestbyname(name);
|
|
|
|
- ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf, view.len);
|
|
+ ret_obj = EVPnew(name_obj, digest, NULL, (unsigned char*)view.buf, view.len, usedforsecurity);
|
|
|
|
if (data_obj)
|
|
PyBuffer_Release(&view);
|
|
@@ -906,18 +937,27 @@ generate_hash_name_list(void)
|
|
* code that wants to make hashes of a bunch of small strings.
|
|
* The first call will lazy-initialize, which reports an exception
|
|
* if initialization fails.
|
|
+ *
|
|
+ * Note that for usedforsecurity=0, we fall back to new().
|
|
*/
|
|
#define GEN_CONSTRUCTOR(NAME, SSL_NAME) \
|
|
static PyObject * \
|
|
- EVP_new_ ## NAME (PyObject *self, PyObject *args) \
|
|
+ EVP_new_ ## NAME (PyObject *self, PyObject *args, PyObject *kw) \
|
|
{ \
|
|
PyObject *data_obj = NULL; \
|
|
Py_buffer view = { 0 }; \
|
|
PyObject *ret_obj; \
|
|
+ int usedforsecurity = 1; \
|
|
+ char *kwnames[] = {"", "usedforsecurity", NULL}; \
|
|
\
|
|
- if (!PyArg_ParseTuple(args, "|O:" #NAME , &data_obj)) { \
|
|
+ if (!PyArg_ParseTupleAndKeywords(args, kw, "|O$p:" #NAME, kwnames, &data_obj, &usedforsecurity)) { \
|
|
return NULL; \
|
|
} \
|
|
+ if (!usedforsecurity) { \
|
|
+ return _EVP_new_impl( \
|
|
+ CONST_ ## NAME ## _name_obj, SSL_NAME, \
|
|
+ data_obj, usedforsecurity); \
|
|
+ } \
|
|
\
|
|
if (CONST_new_ ## NAME ## _ctx_p == NULL) { \
|
|
EVP_MD_CTX *ctx_p = EVP_MD_CTX_new(); \
|
|
@@ -938,7 +978,8 @@ generate_hash_name_list(void)
|
|
NULL, \
|
|
CONST_new_ ## NAME ## _ctx_p, \
|
|
(unsigned char*)view.buf, \
|
|
- view.len); \
|
|
+ view.len, \
|
|
+ usedforsecurity); \
|
|
\
|
|
if (data_obj) \
|
|
PyBuffer_Release(&view); \
|
|
@@ -947,7 +988,8 @@ generate_hash_name_list(void)
|
|
|
|
/* a PyMethodDef structure for the constructor */
|
|
#define CONSTRUCTOR_METH_DEF(NAME) \
|
|
- {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \
|
|
+ {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, \
|
|
+ METH_VARARGS|METH_KEYWORDS, \
|
|
PyDoc_STR("Returns a " #NAME \
|
|
" hash object; optionally initialized with a string") \
|
|
},
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 0d169ad7d9aa48bdb3313b6d72682d097dc9dea5 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 26 Aug 2019 18:59:15 +0200
|
|
Subject: [PATCH 28/36] Add no-op usedforsecurity argument to internal hash
|
|
implementations
|
|
|
|
---
|
|
Modules/_blake2/blake2b_impl.c | 6 ++++--
|
|
Modules/_blake2/blake2s_impl.c | 6 ++++--
|
|
Modules/_blake2/clinic/blake2b_impl.c.h | 16 +++++++++-------
|
|
Modules/_blake2/clinic/blake2s_impl.c.h | 16 +++++++++-------
|
|
Modules/_sha3/sha3module.c | 5 +++++
|
|
5 files changed, 31 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/Modules/_blake2/blake2b_impl.c b/Modules/_blake2/blake2b_impl.c
|
|
index f6bfce823b8..ae9d244200a 100644
|
|
--- a/Modules/_blake2/blake2b_impl.c
|
|
+++ b/Modules/_blake2/blake2b_impl.c
|
|
@@ -87,6 +87,8 @@ _blake2.blake2b.__new__ as py_blake2b_new
|
|
inner_size: int = 0
|
|
last_node: bool = False
|
|
|
|
+ usedforsecurity: bool = True
|
|
+
|
|
Return a new BLAKE2b hash object.
|
|
[clinic start generated code]*/
|
|
|
|
@@ -95,8 +97,8 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
|
|
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
|
|
int fanout, int depth, PyObject *leaf_size_obj,
|
|
PyObject *node_offset_obj, int node_depth,
|
|
- int inner_size, int last_node)
|
|
-/*[clinic end generated code: output=7506d8d890e5f13b input=aca35b33c5612b4b]*/
|
|
+ int inner_size, int last_node, int usedforsecurity)
|
|
+/*[clinic end generated code: output=02dcc52ee784622b input=c5dfcb847f9065ac]*/
|
|
{
|
|
BLAKE2bObject *self = NULL;
|
|
Py_buffer buf;
|
|
diff --git a/Modules/_blake2/blake2s_impl.c b/Modules/_blake2/blake2s_impl.c
|
|
index 28ae5b65101..bf80d6b7428 100644
|
|
--- a/Modules/_blake2/blake2s_impl.c
|
|
+++ b/Modules/_blake2/blake2s_impl.c
|
|
@@ -87,6 +87,8 @@ _blake2.blake2s.__new__ as py_blake2s_new
|
|
inner_size: int = 0
|
|
last_node: bool = False
|
|
|
|
+ usedforsecurity: bool = True
|
|
+
|
|
Return a new BLAKE2s hash object.
|
|
[clinic start generated code]*/
|
|
|
|
@@ -95,8 +97,8 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
|
|
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
|
|
int fanout, int depth, PyObject *leaf_size_obj,
|
|
PyObject *node_offset_obj, int node_depth,
|
|
- int inner_size, int last_node)
|
|
-/*[clinic end generated code: output=fe060b258a8cbfc6 input=3abfaabe7f5f62cc]*/
|
|
+ int inner_size, int last_node, int usedforsecurity)
|
|
+/*[clinic end generated code: output=e32ea5e22d54db91 input=af5344c57efd870d]*/
|
|
{
|
|
BLAKE2sObject *self = NULL;
|
|
Py_buffer buf;
|
|
diff --git a/Modules/_blake2/clinic/blake2b_impl.c.h b/Modules/_blake2/clinic/blake2b_impl.c.h
|
|
index 9b2965eb6b3..9688c04dda8 100644
|
|
--- a/Modules/_blake2/clinic/blake2b_impl.c.h
|
|
+++ b/Modules/_blake2/clinic/blake2b_impl.c.h
|
|
@@ -5,7 +5,8 @@ preserve
|
|
PyDoc_STRVAR(py_blake2b_new__doc__,
|
|
"blake2b(data=b\'\', /, *, digest_size=_blake2.blake2b.MAX_DIGEST_SIZE,\n"
|
|
" key=b\'\', salt=b\'\', person=b\'\', fanout=1, depth=1, leaf_size=0,\n"
|
|
-" node_offset=0, node_depth=0, inner_size=0, last_node=False)\n"
|
|
+" node_offset=0, node_depth=0, inner_size=0, last_node=False,\n"
|
|
+" usedforsecurity=True)\n"
|
|
"--\n"
|
|
"\n"
|
|
"Return a new BLAKE2b hash object.");
|
|
@@ -15,14 +16,14 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
|
|
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
|
|
int fanout, int depth, PyObject *leaf_size_obj,
|
|
PyObject *node_offset_obj, int node_depth,
|
|
- int inner_size, int last_node);
|
|
+ int inner_size, int last_node, int usedforsecurity);
|
|
|
|
static PyObject *
|
|
py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
{
|
|
PyObject *return_value = NULL;
|
|
- static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", NULL};
|
|
- static _PyArg_Parser _parser = {"|O$iy*y*y*iiOOiip:blake2b", _keywords, 0};
|
|
+ static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", "usedforsecurity", NULL};
|
|
+ static _PyArg_Parser _parser = {"|O$iy*y*y*iiOOiipp:blake2b", _keywords, 0};
|
|
PyObject *data = NULL;
|
|
int digest_size = BLAKE2B_OUTBYTES;
|
|
Py_buffer key = {NULL, NULL};
|
|
@@ -35,12 +36,13 @@ py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
int node_depth = 0;
|
|
int inner_size = 0;
|
|
int last_node = 0;
|
|
+ int usedforsecurity = 1;
|
|
|
|
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
|
|
- &data, &digest_size, &key, &salt, &person, &fanout, &depth, &leaf_size_obj, &node_offset_obj, &node_depth, &inner_size, &last_node)) {
|
|
+ &data, &digest_size, &key, &salt, &person, &fanout, &depth, &leaf_size_obj, &node_offset_obj, &node_depth, &inner_size, &last_node, &usedforsecurity)) {
|
|
goto exit;
|
|
}
|
|
- return_value = py_blake2b_new_impl(type, data, digest_size, &key, &salt, &person, fanout, depth, leaf_size_obj, node_offset_obj, node_depth, inner_size, last_node);
|
|
+ return_value = py_blake2b_new_impl(type, data, digest_size, &key, &salt, &person, fanout, depth, leaf_size_obj, node_offset_obj, node_depth, inner_size, last_node, usedforsecurity);
|
|
|
|
exit:
|
|
/* Cleanup for key */
|
|
@@ -121,4 +123,4 @@ _blake2_blake2b_hexdigest(BLAKE2bObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
return _blake2_blake2b_hexdigest_impl(self);
|
|
}
|
|
-/*[clinic end generated code: output=0eb559f418fc0a21 input=a9049054013a1b77]*/
|
|
+/*[clinic end generated code: output=d5f214b052c75135 input=a9049054013a1b77]*/
|
|
diff --git a/Modules/_blake2/clinic/blake2s_impl.c.h b/Modules/_blake2/clinic/blake2s_impl.c.h
|
|
index 42b87b7099d..5653e93044d 100644
|
|
--- a/Modules/_blake2/clinic/blake2s_impl.c.h
|
|
+++ b/Modules/_blake2/clinic/blake2s_impl.c.h
|
|
@@ -5,7 +5,8 @@ preserve
|
|
PyDoc_STRVAR(py_blake2s_new__doc__,
|
|
"blake2s(data=b\'\', /, *, digest_size=_blake2.blake2s.MAX_DIGEST_SIZE,\n"
|
|
" key=b\'\', salt=b\'\', person=b\'\', fanout=1, depth=1, leaf_size=0,\n"
|
|
-" node_offset=0, node_depth=0, inner_size=0, last_node=False)\n"
|
|
+" node_offset=0, node_depth=0, inner_size=0, last_node=False,\n"
|
|
+" usedforsecurity=True)\n"
|
|
"--\n"
|
|
"\n"
|
|
"Return a new BLAKE2s hash object.");
|
|
@@ -15,14 +16,14 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
|
|
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
|
|
int fanout, int depth, PyObject *leaf_size_obj,
|
|
PyObject *node_offset_obj, int node_depth,
|
|
- int inner_size, int last_node);
|
|
+ int inner_size, int last_node, int usedforsecurity);
|
|
|
|
static PyObject *
|
|
py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
{
|
|
PyObject *return_value = NULL;
|
|
- static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", NULL};
|
|
- static _PyArg_Parser _parser = {"|O$iy*y*y*iiOOiip:blake2s", _keywords, 0};
|
|
+ static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", "usedforsecurity", NULL};
|
|
+ static _PyArg_Parser _parser = {"|O$iy*y*y*iiOOiipp:blake2s", _keywords, 0};
|
|
PyObject *data = NULL;
|
|
int digest_size = BLAKE2S_OUTBYTES;
|
|
Py_buffer key = {NULL, NULL};
|
|
@@ -35,12 +36,13 @@ py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
int node_depth = 0;
|
|
int inner_size = 0;
|
|
int last_node = 0;
|
|
+ int usedforsecurity = 1;
|
|
|
|
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
|
|
- &data, &digest_size, &key, &salt, &person, &fanout, &depth, &leaf_size_obj, &node_offset_obj, &node_depth, &inner_size, &last_node)) {
|
|
+ &data, &digest_size, &key, &salt, &person, &fanout, &depth, &leaf_size_obj, &node_offset_obj, &node_depth, &inner_size, &last_node, &usedforsecurity)) {
|
|
goto exit;
|
|
}
|
|
- return_value = py_blake2s_new_impl(type, data, digest_size, &key, &salt, &person, fanout, depth, leaf_size_obj, node_offset_obj, node_depth, inner_size, last_node);
|
|
+ return_value = py_blake2s_new_impl(type, data, digest_size, &key, &salt, &person, fanout, depth, leaf_size_obj, node_offset_obj, node_depth, inner_size, last_node, usedforsecurity);
|
|
|
|
exit:
|
|
/* Cleanup for key */
|
|
@@ -121,4 +123,4 @@ _blake2_blake2s_hexdigest(BLAKE2sObject *self, PyObject *Py_UNUSED(ignored))
|
|
{
|
|
return _blake2_blake2s_hexdigest_impl(self);
|
|
}
|
|
-/*[clinic end generated code: output=13d4b08ea9ee2d62 input=a9049054013a1b77]*/
|
|
+/*[clinic end generated code: output=2373a3b3fa542e89 input=a9049054013a1b77]*/
|
|
diff --git a/Modules/_sha3/sha3module.c b/Modules/_sha3/sha3module.c
|
|
index 2783a75644f..62db9cb5616 100644
|
|
--- a/Modules/_sha3/sha3module.c
|
|
+++ b/Modules/_sha3/sha3module.c
|
|
@@ -185,6 +185,11 @@ py_sha3_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
|
HashReturn res;
|
|
PyObject *data = NULL;
|
|
|
|
+ // Ignore "usedforsecurity"
|
|
+ if (PyMapping_DelItemString(kwargs, "usedforsecurity")) {
|
|
+ PyErr_Clear();
|
|
+ }
|
|
+
|
|
if (!_PyArg_NoKeywords(type->tp_name, kwargs)) {
|
|
return NULL;
|
|
}
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From c451744d82f78f42bfa947b782a24ff7c8c6ad9d Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 26 Aug 2019 19:09:39 +0200
|
|
Subject: [PATCH 29/36] Test the usedforsecurity flag
|
|
|
|
---
|
|
Lib/test/test_hashlib.py | 82 ++++++++++++++++++++++++++--------------
|
|
1 file changed, 54 insertions(+), 28 deletions(-)
|
|
|
|
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
|
|
index 9745bfd6fa2..19a2fbdf294 100644
|
|
--- a/Lib/test/test_hashlib.py
|
|
+++ b/Lib/test/test_hashlib.py
|
|
@@ -24,6 +24,7 @@ from test import support
|
|
from test.support import _4G, bigmemtest, import_fresh_module
|
|
from test.support import requires_hashdigest
|
|
from http.client import HTTPException
|
|
+from functools import partial
|
|
|
|
# Were we compiled --with-pydebug or with #define Py_DEBUG?
|
|
COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount')
|
|
@@ -32,8 +33,10 @@ c_hashlib = import_fresh_module('hashlib', fresh=['_hashlib'])
|
|
py_hashlib = import_fresh_module('hashlib', blocked=['_hashlib'])
|
|
|
|
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()
|
|
|
|
try:
|
|
@@ -78,6 +81,15 @@ def read_vectors(hash_name):
|
|
yield parts
|
|
|
|
|
|
+def _is_openssl_constructor(constructor):
|
|
+ if getattr(constructor, '__name__', '').startswith('openssl_'):
|
|
+ return True
|
|
+ if isinstance(constructor, partial):
|
|
+ if constructor.func.__name__.startswith('openssl_'):
|
|
+ return True
|
|
+ return False
|
|
+
|
|
+
|
|
class HashLibTestCase(unittest.TestCase):
|
|
supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1',
|
|
'sha224', 'SHA224', 'sha256', 'SHA256',
|
|
@@ -93,8 +105,6 @@ class HashLibTestCase(unittest.TestCase):
|
|
|
|
if hashlib.get_fips_mode():
|
|
shakes = set()
|
|
- supported_hash_names = tuple(
|
|
- n for n in supported_hash_names if n not in FIPS_DISABLED)
|
|
|
|
def _conditional_import_module(self, module_name):
|
|
"""Import a module and return a reference to it or None on failure."""
|
|
@@ -120,7 +130,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:
|
|
@@ -130,15 +141,21 @@ class HashLibTestCase(unittest.TestCase):
|
|
for algorithm in algorithms:
|
|
self.constructors_to_test[algorithm] = set()
|
|
|
|
+ def _add_constructor(algorithm, constructor):
|
|
+ constructors.add(partial(constructor, usedforsecurity=False))
|
|
+ if algorithm not in FIPS_DISABLED:
|
|
+ constructors.add(constructor)
|
|
+ constructors.add(partial(constructor, usedforsecurity=True))
|
|
+
|
|
# For each algorithm, test the direct constructor and the use
|
|
# of hashlib.new given the algorithm name.
|
|
for algorithm, constructors in self.constructors_to_test.items():
|
|
- constructors.add(getattr(hashlib, algorithm))
|
|
+ _add_constructor(algorithm, getattr(hashlib, algorithm))
|
|
def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs):
|
|
if data is None:
|
|
return hashlib.new(_alg, **kwargs)
|
|
return hashlib.new(_alg, data, **kwargs)
|
|
- constructors.add(_test_algorithm_via_hashlib_new)
|
|
+ _add_constructor(algorithm, _test_algorithm_via_hashlib_new)
|
|
|
|
_hashlib = self._conditional_import_module('_hashlib')
|
|
self._hashlib = _hashlib
|
|
@@ -150,7 +167,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
for algorithm, constructors in self.constructors_to_test.items():
|
|
constructor = getattr(_hashlib, 'openssl_'+algorithm, None)
|
|
if constructor:
|
|
- constructors.add(constructor)
|
|
+ _add_constructor(algorithm, constructor)
|
|
|
|
def add_builtin_constructor(name):
|
|
constructor = getattr(hashlib, "__get_builtin_constructor")(name)
|
|
@@ -210,7 +227,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()))
|
|
|
|
@@ -286,7 +303,9 @@ class HashLibTestCase(unittest.TestCase):
|
|
self.assertIn(h.name, self.supported_hash_names)
|
|
else:
|
|
self.assertNotIn(h.name, self.supported_hash_names)
|
|
- self.assertEqual(h.name, hashlib.new(h.name).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)
|
|
|
|
def test_large_update(self):
|
|
aas = b'a' * 128
|
|
@@ -328,13 +347,15 @@ 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)
|
|
if shake:
|
|
- if hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ if _is_openssl_constructor(hash_object_constructor):
|
|
if length > m.digest_size:
|
|
# OpenSSL doesn't give long digests
|
|
return
|
|
@@ -351,7 +372,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
% (name, hash_object_constructor,
|
|
computed, len(data), hexdigest))
|
|
if shake:
|
|
- if hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ if _is_openssl_constructor(hash_object_constructor):
|
|
computed = m.digest()[:length]
|
|
else:
|
|
computed = m.digest(length)
|
|
@@ -369,8 +390,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
self.assertRaises(TypeError, hash_object_constructor, 'spam')
|
|
|
|
def test_no_unicode(self):
|
|
- if not hashlib.get_fips_mode():
|
|
- self.check_no_unicode('md5')
|
|
+ self.check_no_unicode('md5')
|
|
self.check_no_unicode('sha1')
|
|
self.check_no_unicode('sha224')
|
|
self.check_no_unicode('sha256')
|
|
@@ -397,10 +417,10 @@ class HashLibTestCase(unittest.TestCase):
|
|
for hash_object_constructor in constructors:
|
|
m = hash_object_constructor()
|
|
self.assertEqual(m.block_size, block_size)
|
|
- if not hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ if not _is_openssl_constructor(hash_object_constructor):
|
|
self.assertEqual(m.digest_size, digest_size)
|
|
if digest_length:
|
|
- if not hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ if not _is_openssl_constructor(hash_object_constructor):
|
|
self.assertEqual(len(m.digest(digest_length)),
|
|
digest_length)
|
|
self.assertEqual(len(m.hexdigest(digest_length)),
|
|
@@ -435,7 +455,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
for hash_object_constructor in constructors:
|
|
m = hash_object_constructor()
|
|
self.assertEqual(capacity + rate, 1600)
|
|
- if not hash_object_constructor.__name__.startswith('openssl_'):
|
|
+ if not _is_openssl_constructor(hash_object_constructor):
|
|
self.assertEqual(m._capacity_bits, capacity)
|
|
self.assertEqual(m._rate_bits, rate)
|
|
self.assertEqual(m._suffix, suffix)
|
|
@@ -454,31 +474,27 @@ 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')
|
|
+ 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')
|
|
+ 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',
|
|
b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
|
|
- 'd174ab98d277d9f5a5611c2c9f419d9f')
|
|
+ 'd174ab98d277d9f5a5611c2c9f419d9f',
|
|
+ usedforsecurity=False)
|
|
|
|
- @unittest.skipIf(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode")
|
|
@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(hashlib.get_fips_mode(), "md5 unacceptable in FIPS mode")
|
|
@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
|
|
@@ -904,6 +920,16 @@ class HashLibTestCase(unittest.TestCase):
|
|
|
|
self.assertEqual(expected_hash, hasher.hexdigest())
|
|
|
|
+ @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):
|
|
+ for cons in hashlib.md5, partial(hashlib.new, 'md5'):
|
|
+ self.assertRaises(ValueError, cons)
|
|
+ self.assertRaises(ValueError, partial(cons, usedforsecurity=True))
|
|
+ self.assertEqual(cons(usedforsecurity=False).hexdigest(),
|
|
+ 'd41d8cd98f00b204e9800998ecf8427e')
|
|
+
|
|
|
|
class KDFTests(unittest.TestCase):
|
|
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 327707f185572cf13da66851e0af824a81d65aa9 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Thu, 29 Aug 2019 10:25:28 +0200
|
|
Subject: [PATCH 30/36] Skip error checking in _hashlib.get_fips_mode
|
|
|
|
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1745499
|
|
---
|
|
Modules/_hashopenssl.c | 30 ++++++++++++++++--------------
|
|
1 file changed, 16 insertions(+), 14 deletions(-)
|
|
|
|
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
|
|
index b621c330c32..6bfb12c5618 100644
|
|
--- a/Modules/_hashopenssl.c
|
|
+++ b/Modules/_hashopenssl.c
|
|
@@ -1021,20 +1021,22 @@ static PyObject *
|
|
_hashlib_get_fips_mode_impl(PyObject *module)
|
|
/*[clinic end generated code: output=ad8a7793310d3f98 input=f42a2135df2a5e11]*/
|
|
{
|
|
- 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 NULL;
|
|
- }
|
|
- }
|
|
- return PyLong_FromLong(result);
|
|
+ // XXX: This function skips error checking.
|
|
+ // This is only appropriate for RHEL.
|
|
+
|
|
+ // From the OpenSSL docs:
|
|
+ // "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)."
|
|
+ // In RHEL:
|
|
+ // * we do build with FIPS, so the function always succeeds
|
|
+ // * even if it didn't, people seem used to errors being left on the
|
|
+ // OpenSSL error stack.
|
|
+
|
|
+ // For more info, see:
|
|
+ // https://bugzilla.redhat.com/show_bug.cgi?id=1745499
|
|
+
|
|
+ return PyLong_FromLong(FIPS_mode());
|
|
}
|
|
|
|
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 695039674b78b19d9ff26d637c11acea99c44807 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Thu, 10 Oct 2019 13:04:50 +0200
|
|
Subject: [PATCH 31/36] Skip error checking in _Py_hashlib_fips_error
|
|
|
|
https://bugzilla.redhat.com/show_bug.cgi?id=1760106
|
|
---
|
|
Include/_hashopenssl.h | 12 +++---------
|
|
1 file changed, 3 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/Include/_hashopenssl.h b/Include/_hashopenssl.h
|
|
index 47ed0030422..d4cbdef984d 100644
|
|
--- a/Include/_hashopenssl.h
|
|
+++ b/Include/_hashopenssl.h
|
|
@@ -42,16 +42,10 @@ static int
|
|
_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,
|
|
- // 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.
|
|
+ // XXX: This function skips error checking.
|
|
+ // This is only appropriate for RHEL.
|
|
+ // See _hashlib.get_fips_mode for details.
|
|
|
|
- unsigned long errcode = ERR_peek_last_error();
|
|
- if (errcode) {
|
|
- _setException(exc);
|
|
- return 1;
|
|
- }
|
|
return 0;
|
|
}
|
|
PyErr_Format(exc, "%s is not available in FIPS mode", name);
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 0f9008e92b62f5f22e2bfd0a73d5325d16eedf6f Mon Sep 17 00:00:00 2001
|
|
From: Charalampos Stratakis <cstratak@redhat.com>
|
|
Date: Thu, 21 Nov 2019 00:20:09 +0100
|
|
Subject: [PATCH 32/36] Use usedforsecurity=False in uuid
|
|
|
|
Partially backport commit 7cad53e6b084435a220e6604010f1fa5778bd0b1
|
|
only for uuid
|
|
---
|
|
Lib/uuid.py | 7 +++++--
|
|
1 file changed, 5 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/Lib/uuid.py b/Lib/uuid.py
|
|
index db8b2ef94ed..7e680a24d06 100644
|
|
--- a/Lib/uuid.py
|
|
+++ b/Lib/uuid.py
|
|
@@ -615,8 +615,11 @@ def uuid1(node=None, clock_seq=None):
|
|
def uuid3(namespace, name):
|
|
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
|
|
from hashlib import md5
|
|
- hash = md5(namespace.bytes + bytes(name, "utf-8")).digest()
|
|
- return UUID(bytes=hash[:16], version=3)
|
|
+ digest = md5(
|
|
+ namespace.bytes + bytes(name, "utf-8"),
|
|
+ usedforsecurity=False
|
|
+ ).digest()
|
|
+ return UUID(bytes=digest[:16], version=3)
|
|
|
|
def uuid4():
|
|
"""Generate a random UUID."""
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 75829df72b65fb573f3b310033978e493ff700b4 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 5 Aug 2019 19:12:38 +0200
|
|
Subject: [PATCH 33/36] Fixups
|
|
|
|
- Adjust error message of the original hmac.HMAC class
|
|
- Don't duplicate a test name
|
|
---
|
|
Lib/hmac.py | 2 +-
|
|
Lib/test/test_hmac.py | 2 +-
|
|
2 files changed, 2 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/Lib/hmac.py b/Lib/hmac.py
|
|
index 7af94c39ed6..33b5be613d9 100644
|
|
--- a/Lib/hmac.py
|
|
+++ b/Lib/hmac.py
|
|
@@ -41,7 +41,7 @@ class HMAC:
|
|
"""
|
|
if _hashlib.get_fips_mode():
|
|
raise ValueError(
|
|
- 'hmac.HMAC is not available in FIPS mode. '
|
|
+ 'This class is not available in FIPS mode. '
|
|
+ 'Use hmac.new().'
|
|
)
|
|
|
|
diff --git a/Lib/test/test_hmac.py b/Lib/test/test_hmac.py
|
|
index 5b090727ef6..449b1366314 100644
|
|
--- a/Lib/test/test_hmac.py
|
|
+++ b/Lib/test/test_hmac.py
|
|
@@ -401,7 +401,7 @@ class CopyTestCase(unittest.TestCase):
|
|
self.assertTrue(id(h1.outer) != id(h2.outer),
|
|
"No real copy of the attribute 'outer'.")
|
|
|
|
- def test_realcopy(self):
|
|
+ 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()
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 86f1f161b5aab08975379dbae6ebac08a697e3d7 Mon Sep 17 00:00:00 2001
|
|
From: Petr Viktorin <pviktori@redhat.com>
|
|
Date: Mon, 26 Aug 2019 19:39:48 +0200
|
|
Subject: [PATCH 34/36] Don't re-export get_fips_mode from hashlib
|
|
|
|
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1745685
|
|
---
|
|
Lib/distutils/command/upload.py | 3 ++-
|
|
Lib/distutils/tests/test_upload.py | 5 +++--
|
|
Lib/hashlib.py | 23 ++++++++++++-----------
|
|
Lib/hmac.py | 6 +++---
|
|
Lib/test/test_fips.py | 6 +++---
|
|
Lib/test/test_hashlib.py | 16 +++++++++-------
|
|
Lib/test/test_hmac.py | 8 +++++---
|
|
Lib/test/test_logging.py | 1 +
|
|
Lib/test/test_smtplib.py | 4 +++-
|
|
Lib/test/test_tools/test_md5sum.py | 4 ++--
|
|
Lib/test/test_urllib2_localnet.py | 1 +
|
|
11 files changed, 44 insertions(+), 33 deletions(-)
|
|
|
|
diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py
|
|
index 170acb3d6b6..d0a4aee001e 100644
|
|
--- a/Lib/distutils/command/upload.py
|
|
+++ b/Lib/distutils/command/upload.py
|
|
@@ -126,7 +126,8 @@ class upload(PyPIRCCommand):
|
|
except ValueError as e:
|
|
msg = 'calculating md5 checksum failed: %s' % e
|
|
self.announce(msg, log.INFO)
|
|
- if not hashlib.get_fips_mode():
|
|
+ from _hashlib import get_fips_mode
|
|
+ if not get_fips_mode():
|
|
# this really shouldn't fail
|
|
raise
|
|
else:
|
|
diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py
|
|
index f720a7905dd..a198b213577 100644
|
|
--- a/Lib/distutils/tests/test_upload.py
|
|
+++ b/Lib/distutils/tests/test_upload.py
|
|
@@ -4,6 +4,7 @@ import unittest
|
|
import unittest.mock as mock
|
|
from urllib.request import HTTPError
|
|
import hashlib
|
|
+from _hashlib import get_fips_mode
|
|
|
|
from test.support import run_unittest
|
|
|
|
@@ -131,7 +132,7 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
|
|
|
|
# what did we send ?
|
|
headers = dict(self.last_open.req.headers)
|
|
- if hashlib.get_fips_mode():
|
|
+ if get_fips_mode():
|
|
# only sha256 hash is used
|
|
self.assertEqual(headers['Content-length'], '2197')
|
|
else:
|
|
@@ -172,7 +173,7 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
|
|
cmd.run()
|
|
|
|
headers = dict(self.last_open.req.headers)
|
|
- if hashlib.get_fips_mode():
|
|
+ if get_fips_mode():
|
|
# only sha256 hash is used
|
|
self.assertEqual(headers['Content-length'], '2207')
|
|
else:
|
|
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
|
|
index cd3b035b1d7..3e9a4aa27a7 100644
|
|
--- a/Lib/hashlib.py
|
|
+++ b/Lib/hashlib.py
|
|
@@ -68,13 +68,13 @@ __all__ = __always_supported + ('new', 'algorithms_guaranteed',
|
|
'algorithms_available', 'pbkdf2_hmac')
|
|
|
|
try:
|
|
- from _hashlib import get_fips_mode
|
|
+ from _hashlib import get_fips_mode as _hashlib_get_fips_mode
|
|
except ImportError:
|
|
- def get_fips_mode():
|
|
+ def _hashlib_get_fips_mode():
|
|
return 0
|
|
|
|
|
|
-if not get_fips_mode():
|
|
+if not _hashlib_get_fips_mode():
|
|
__builtin_constructor_cache = {}
|
|
|
|
def __get_builtin_constructor(name):
|
|
@@ -121,7 +121,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 {
|
|
'blake2b', 'blake2s', 'shake_256', 'shake_128',
|
|
#'sha3_224', 'sha3_256', 'sha3_384', 'sha3_512',
|
|
@@ -132,7 +132,7 @@ def __get_openssl_constructor(name):
|
|
f = getattr(_hashlib, 'openssl_' + name)
|
|
# Allow the C module to raise ValueError. The function will be
|
|
# defined but the hash not actually available thanks to OpenSSL.
|
|
- if not get_fips_mode():
|
|
+ if not _hashlib.get_fips_mode():
|
|
# N.B. In "FIPS mode", there is no fallback.
|
|
# If this test fails, we want to export the broken hash
|
|
# constructor anyway.
|
|
@@ -142,7 +142,7 @@ def __get_openssl_constructor(name):
|
|
except (AttributeError, ValueError):
|
|
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
|
|
@@ -155,7 +155,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 get_fips_mode():
|
|
+ if _hashlib.get_fips_mode():
|
|
# Use OpenSSL names for Python built-in hashes
|
|
orig_name = name
|
|
name = {
|
|
@@ -177,7 +177,7 @@ def __hash_new(name, data=b'', **kwargs):
|
|
usedforsecurity = kwargs.pop('usedforsecurity', True)
|
|
retval = _hashlib.new(
|
|
name, data, usedforsecurity=usedforsecurity)
|
|
- if get_fips_mode() and name != orig_name:
|
|
+ if _hashlib.get_fips_mode() and name != orig_name:
|
|
retval._set_name(orig_name)
|
|
return retval
|
|
except ValueError:
|
|
@@ -185,7 +185,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)
|
|
|
|
@@ -197,7 +197,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
|
|
@@ -225,5 +225,6 @@ 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
|
|
+del _hashlib_get_fips_mode
|
|
diff --git a/Lib/hmac.py b/Lib/hmac.py
|
|
index 33b5be613d9..ca83d9dc0d3 100644
|
|
--- a/Lib/hmac.py
|
|
+++ b/Lib/hmac.py
|
|
@@ -39,7 +39,7 @@ class HMAC:
|
|
|
|
Note: key and msg must be a bytes or bytearray objects.
|
|
"""
|
|
- if _hashlib.get_fips_mode():
|
|
+ if _hashlibopenssl.get_fips_mode():
|
|
raise ValueError(
|
|
'This class is not available in FIPS mode. '
|
|
+ 'Use hmac.new().'
|
|
@@ -97,7 +97,7 @@ class HMAC:
|
|
def update(self, msg):
|
|
"""Update this hashing object with the string msg.
|
|
"""
|
|
- if _hashlib.get_fips_mode():
|
|
+ if _hashlibopenssl.get_fips_mode():
|
|
raise ValueError('hmac.HMAC is not available in FIPS mode')
|
|
self.inner.update(msg)
|
|
|
|
@@ -167,7 +167,7 @@ class HMAC_openssl(_hmacopenssl.HMAC):
|
|
return result
|
|
|
|
|
|
-if _hashlib.get_fips_mode():
|
|
+if _hashlibopenssl.get_fips_mode():
|
|
HMAC = HMAC_openssl
|
|
|
|
|
|
diff --git a/Lib/test/test_fips.py b/Lib/test/test_fips.py
|
|
index 34812e6098a..86e61e29c0b 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
|
|
"""
|
|
@@ -30,7 +30,7 @@ class HashlibFipsTests(unittest.TestCase):
|
|
|
|
self.assertEqual(m, h)
|
|
|
|
- @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.compare_hashes(hashlib.blake2b(b'abc'), _hashlib.openssl_blake2b(b'abc'))
|
|
self.compare_hashes(hashlib.blake2s(b'abc'), _hashlib.openssl_blake2s(b'abc'))
|
|
@@ -41,7 +41,7 @@ class HashlibFipsTests(unittest.TestCase):
|
|
self.compare_hashes(hashlib.sha3_384(b'abc'), _hashlib.openssl_sha3_384(b'abc'))
|
|
self.compare_hashes(hashlib.sha3_512(b'abc'), _hashlib.openssl_sha3_512(b'abc'))
|
|
|
|
- @unittest.skipIf(hashlib.get_fips_mode(), "shake hashes are not available under FIPS")
|
|
+ @unittest.skipIf(_hashlib.get_fips_mode(), "shake hashes are not available under FIPS")
|
|
def test_shake_hashes(self):
|
|
self.compare_hashes(hashlib.shake_128(b'abc'), _hashlib.openssl_shake_128(b'abc'))
|
|
self.compare_hashes(hashlib.shake_256(b'abc'), _hashlib.openssl_shake_256(b'abc'))
|
|
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
|
|
index 19a2fbdf294..a6f9a353fd1 100644
|
|
--- a/Lib/test/test_hashlib.py
|
|
+++ b/Lib/test/test_hashlib.py
|
|
@@ -32,7 +32,9 @@ COMPILED_WITH_PYDEBUG = hasattr(sys, 'gettotalrefcount')
|
|
c_hashlib = import_fresh_module('hashlib', fresh=['_hashlib'])
|
|
py_hashlib = import_fresh_module('hashlib', blocked=['_hashlib'])
|
|
|
|
-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:
|
|
@@ -103,7 +105,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
|
|
|
|
- if hashlib.get_fips_mode():
|
|
+ if _get_fips_mode():
|
|
shakes = set()
|
|
|
|
def _conditional_import_module(self, module_name):
|
|
@@ -114,7 +116,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
if self._warn_on_extension_import:
|
|
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
|
|
|
|
@@ -239,12 +241,12 @@ class HashLibTestCase(unittest.TestCase):
|
|
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
|
|
self.assertRaises(TypeError, hashlib.new, 1)
|
|
|
|
- @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')
|
|
@@ -433,7 +435,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)
|
|
@@ -920,7 +922,7 @@ class HashLibTestCase(unittest.TestCase):
|
|
|
|
self.assertEqual(expected_hash, hasher.hexdigest())
|
|
|
|
- @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 449b1366314..35b893509cb 100644
|
|
--- a/Lib/test/test_hmac.py
|
|
+++ b/Lib/test/test_hmac.py
|
|
@@ -3,6 +3,7 @@ import hmac
|
|
import hashlib
|
|
import unittest
|
|
import warnings
|
|
+from _hashlib import get_fips_mode
|
|
|
|
from test.support import requires_hashdigest
|
|
|
|
@@ -250,7 +251,7 @@ class TestVectorsTestCase(unittest.TestCase):
|
|
def test_sha512_rfc4231(self):
|
|
self._rfc4231_test_cases(hashlib.sha512, 'sha512', 64, 128)
|
|
|
|
- @unittest.skipIf(hashlib.get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.')
|
|
+ @unittest.skipIf(get_fips_mode(), 'MockCrazyHash unacceptable in FIPS mode.')
|
|
@requires_hashdigest('sha256')
|
|
def test_legacy_block_size_warnings(self):
|
|
class MockCrazyHash(object):
|
|
@@ -274,6 +275,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(), 'md5 is not default in FIPS mode.')
|
|
def test_with_digestmod_warning(self):
|
|
with self.assertWarns(PendingDeprecationWarning):
|
|
key = b"\x0b" * 16
|
|
@@ -375,7 +377,7 @@ class SanityTestCase(unittest.TestCase):
|
|
|
|
class CopyTestCase(unittest.TestCase):
|
|
|
|
- @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode")
|
|
+ @unittest.skipIf(get_fips_mode(), "Internal attributes unavailable in FIPS mode")
|
|
@requires_hashdigest('sha256')
|
|
def test_attributes(self):
|
|
# Testing if attributes are of same type.
|
|
@@ -388,7 +390,7 @@ class CopyTestCase(unittest.TestCase):
|
|
self.assertEqual(type(h1.outer), type(h2.outer),
|
|
"Types of outer don't match.")
|
|
|
|
- @unittest.skipIf(hashlib.get_fips_mode(), "Internal attributes unavailable in FIPS mode")
|
|
+ @unittest.skipIf(get_fips_mode(), "Internal attributes unavailable in FIPS mode")
|
|
@requires_hashdigest('sha256')
|
|
def test_realcopy(self):
|
|
# Testing if the copy method created a real copy.
|
|
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
|
|
index d5c63b483ee..45b72e3bc7e 100644
|
|
--- a/Lib/test/test_logging.py
|
|
+++ b/Lib/test/test_logging.py
|
|
@@ -46,6 +46,7 @@ import time
|
|
import unittest
|
|
import warnings
|
|
import weakref
|
|
+
|
|
try:
|
|
import threading
|
|
# The following imports are needed only for tests which
|
|
diff --git a/Lib/test/test_smtplib.py b/Lib/test/test_smtplib.py
|
|
index 64b3201254a..704b2bc0d35 100644
|
|
--- a/Lib/test/test_smtplib.py
|
|
+++ b/Lib/test/test_smtplib.py
|
|
@@ -16,6 +16,8 @@ import time
|
|
import select
|
|
import errno
|
|
import textwrap
|
|
+import hashlib
|
|
+from _hashlib import get_fips_mode
|
|
|
|
import unittest
|
|
from test import support, mock_socket
|
|
@@ -980,7 +982,7 @@ class SMTPSimTests(unittest.TestCase):
|
|
|
|
def testAUTH_multiple(self):
|
|
# Test that multiple authentication methods are tried.
|
|
- self.serv.add_feature("AUTH BOGUS PLAIN LOGIN CRAM-MD5")
|
|
+ self.serv.add_feature("AUTH BOGUS PLAIN LOGIN")
|
|
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=15)
|
|
resp = smtp.login(sim_auth[0], sim_auth[1])
|
|
self.assertEqual(resp, (235, b'Authentication Succeeded'))
|
|
diff --git a/Lib/test/test_tools/test_md5sum.py b/Lib/test/test_tools/test_md5sum.py
|
|
index 7028a4dc214..3ba1ca0f146 100644
|
|
--- a/Lib/test/test_tools/test_md5sum.py
|
|
+++ b/Lib/test/test_tools/test_md5sum.py
|
|
@@ -4,13 +4,13 @@ import os
|
|
import unittest
|
|
from test import support
|
|
from test.support.script_helper import assert_python_ok, assert_python_failure
|
|
-import hashlib
|
|
+import _hashlib
|
|
|
|
from test.test_tools import scriptsdir, skip_if_missing
|
|
|
|
skip_if_missing()
|
|
|
|
-if hashlib.get_fips_mode():
|
|
+if _hashlib.get_fips_mode():
|
|
raise unittest.SkipTest("md5sum won't work at all in FIPS mode")
|
|
|
|
class MD5SumTests(unittest.TestCase):
|
|
diff --git a/Lib/test/test_urllib2_localnet.py b/Lib/test/test_urllib2_localnet.py
|
|
index 895f97cc09a..b4fb13ad092 100644
|
|
--- a/Lib/test/test_urllib2_localnet.py
|
|
+++ b/Lib/test/test_urllib2_localnet.py
|
|
@@ -6,6 +6,7 @@ import urllib.request
|
|
import http.server
|
|
import unittest
|
|
import hashlib
|
|
+from _hashlib import get_fips_mode
|
|
|
|
from test import support
|
|
|
|
--
|
|
2.21.0
|
|
|
|
|
|
From 3538f31cc15c633f1df2d6ed18471c1f771c4a52 Mon Sep 17 00:00:00 2001
|
|
From: Christian Heimes <christian@python.org>
|
|
Date: Wed, 20 Nov 2019 10:59:25 +0100
|
|
Subject: [PATCH 35/36] Use FIPS compliant CSPRNG
|
|
|
|
Kernel's getrandom() source is not yet FIPS compliant. Use OpenSSL's
|
|
DRBG in FIPS mode and disable os.getrandom() function.
|
|
|
|
Signed-off-by: Christian Heimes <christian@python.org>
|
|
---
|
|
Lib/test/test_os.py | 5 +++
|
|
Makefile.pre.in | 2 +-
|
|
Modules/posixmodule.c | 8 +++++
|
|
Python/random.c | 75 +++++++++++++++++++++++++++++++++++++++++++
|
|
4 files changed, 89 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
|
|
index 7a839c83fe4..91816afa47d 100644
|
|
--- a/Lib/test/test_os.py
|
|
+++ b/Lib/test/test_os.py
|
|
@@ -1362,6 +1362,11 @@ class GetRandomTests(unittest.TestCase):
|
|
raise unittest.SkipTest("getrandom() syscall fails with ENOSYS")
|
|
else:
|
|
raise
|
|
+ except ValueError as exc:
|
|
+ if exc.args[0] == "getrandom is not FIPS compliant":
|
|
+ raise unittest.SkipTest("Skip in FIPS mode")
|
|
+ else:
|
|
+ raise
|
|
|
|
def test_getrandom_type(self):
|
|
data = os.getrandom(16)
|
|
diff --git a/Makefile.pre.in b/Makefile.pre.in
|
|
index d15d93509d1..40502fc36d6 100644
|
|
--- a/Makefile.pre.in
|
|
+++ b/Makefile.pre.in
|
|
@@ -112,7 +112,7 @@ CFLAGSFORSHARED=@CFLAGSFORSHARED@
|
|
# C flags used for building the interpreter object files
|
|
PY_CORE_CFLAGS= $(PY_CFLAGS) $(PY_CFLAGS_NODIST) $(PY_CPPFLAGS) $(CFLAGSFORSHARED) -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 776a3d249a8..647dbd3b4d6 100644
|
|
--- a/Modules/posixmodule.c
|
|
+++ b/Modules/posixmodule.c
|
|
@@ -397,6 +397,9 @@ static int win32_can_symlink = 0;
|
|
#define MODNAME "posix"
|
|
#endif
|
|
|
|
+/* for FIPS check in os.getrandom() */
|
|
+#include <openssl/crypto.h>
|
|
+
|
|
#ifdef MS_WINDOWS
|
|
/* defined in fileutils.c */
|
|
PyAPI_FUNC(void) _Py_time_t_to_FILE_TIME(time_t, int, FILETIME *);
|
|
@@ -12196,6 +12199,11 @@ os_getrandom_impl(PyObject *module, Py_ssize_t size, int flags)
|
|
return posix_error();
|
|
}
|
|
|
|
+ if (FIPS_mode()) {
|
|
+ PyErr_SetString(PyExc_ValueError, "getrandom is not FIPS compliant");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
bytes = PyBytes_FromStringAndSize(NULL, size);
|
|
if (bytes == NULL) {
|
|
PyErr_NoMemory();
|
|
diff --git a/Python/random.c b/Python/random.c
|
|
index d4747d61eb8..ab3cee29a39 100644
|
|
--- a/Python/random.c
|
|
+++ b/Python/random.c
|
|
@@ -410,6 +410,77 @@ dev_urandom_close(void)
|
|
}
|
|
#endif /* !MS_WINDOWS */
|
|
|
|
+#include <openssl/crypto.h>
|
|
+#include <openssl/err.h>
|
|
+#include <openssl/rand.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 void
|
|
+py_openssl_set_exception(PyObject* exc) {
|
|
+ unsigned long errcode;
|
|
+ const char *lib, *func, *reason;
|
|
+
|
|
+ errcode = ERR_peek_last_error();
|
|
+ if (!errcode) {
|
|
+ PyErr_SetString(exc, "unknown reasons");
|
|
+ }
|
|
+ ERR_clear_error();
|
|
+
|
|
+ lib = ERR_lib_error_string(errcode);
|
|
+ func = ERR_func_error_string(errcode);
|
|
+ reason = ERR_reason_error_string(errcode);
|
|
+
|
|
+ if (lib && func) {
|
|
+ PyErr_Format(exc, "[%s: %s] %s", lib, func, reason);
|
|
+ }
|
|
+ else if (lib) {
|
|
+ PyErr_Format(exc, "[%s] %s", lib, reason);
|
|
+ }
|
|
+ else {
|
|
+ PyErr_SetString(exc, reason);
|
|
+ }
|
|
+}
|
|
+
|
|
+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) {
|
|
+ py_openssl_set_exception(PyExc_RuntimeError);
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (size > INT_MAX) {
|
|
+ if (raise) {
|
|
+ PyErr_Format(PyExc_OverflowError,
|
|
+ "RAND_bytes() size is limited to 2GB.");
|
|
+ }
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ res = RAND_bytes((unsigned char*)buffer, (int)size);
|
|
+
|
|
+ if (res == 1) {
|
|
+ return 1;
|
|
+ } else {
|
|
+ if (raise) {
|
|
+ py_openssl_set_exception(PyExc_RuntimeError);
|
|
+ }
|
|
+ return 0;
|
|
+ }
|
|
+}
|
|
+
|
|
|
|
/* Fill buffer with pseudo-random bytes generated by a linear congruent
|
|
generator (LCG):
|
|
@@ -494,6 +565,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.21.0
|
|
|
|
|
|
From ed4103bbd15a09bff4fb94a38d1b3f02df5e7c96 Mon Sep 17 00:00:00 2001
|
|
From: Charalampos Stratakis <cstratak@redhat.com>
|
|
Date: Thu, 28 Nov 2019 17:26:02 +0100
|
|
Subject: [PATCH 36/36] Fixups for FIPS compliant CSPRNG
|
|
|
|
---
|
|
Lib/test/test_os.py | 3 ++-
|
|
Python/random.c | 33 +++------------------------------
|
|
2 files changed, 5 insertions(+), 31 deletions(-)
|
|
|
|
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
|
|
index 91816afa47d..f2572bb6c44 100644
|
|
--- a/Lib/test/test_os.py
|
|
+++ b/Lib/test/test_os.py
|
|
@@ -27,6 +27,7 @@ import time
|
|
import unittest
|
|
import uuid
|
|
import warnings
|
|
+from _hashlib import get_fips_mode
|
|
from test import support
|
|
try:
|
|
import threading
|
|
@@ -1363,7 +1364,7 @@ class GetRandomTests(unittest.TestCase):
|
|
else:
|
|
raise
|
|
except ValueError as exc:
|
|
- if exc.args[0] == "getrandom is not FIPS compliant":
|
|
+ if get_fips_mode() and exc.args[0] == "getrandom is not FIPS compliant":
|
|
raise unittest.SkipTest("Skip in FIPS mode")
|
|
else:
|
|
raise
|
|
diff --git a/Python/random.c b/Python/random.c
|
|
index ab3cee29a39..222d651407c 100644
|
|
--- a/Python/random.c
|
|
+++ b/Python/random.c
|
|
@@ -410,40 +410,13 @@ dev_urandom_close(void)
|
|
}
|
|
#endif /* !MS_WINDOWS */
|
|
|
|
-#include <openssl/crypto.h>
|
|
-#include <openssl/err.h>
|
|
#include <openssl/rand.h>
|
|
+#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 void
|
|
-py_openssl_set_exception(PyObject* exc) {
|
|
- unsigned long errcode;
|
|
- const char *lib, *func, *reason;
|
|
-
|
|
- errcode = ERR_peek_last_error();
|
|
- if (!errcode) {
|
|
- PyErr_SetString(exc, "unknown reasons");
|
|
- }
|
|
- ERR_clear_error();
|
|
-
|
|
- lib = ERR_lib_error_string(errcode);
|
|
- func = ERR_func_error_string(errcode);
|
|
- reason = ERR_reason_error_string(errcode);
|
|
-
|
|
- if (lib && func) {
|
|
- PyErr_Format(exc, "[%s: %s] %s", lib, func, reason);
|
|
- }
|
|
- else if (lib) {
|
|
- PyErr_Format(exc, "[%s] %s", lib, reason);
|
|
- }
|
|
- else {
|
|
- PyErr_SetString(exc, reason);
|
|
- }
|
|
-}
|
|
-
|
|
static int
|
|
py_openssl_drbg_urandom(char *buffer, Py_ssize_t size, int raise)
|
|
{
|
|
@@ -455,7 +428,7 @@ py_openssl_drbg_urandom(char *buffer, Py_ssize_t size, int raise)
|
|
res = OPENSSL_init_crypto(OPENSSL_INIT_ATFORK, NULL);
|
|
if (res == 0) {
|
|
if (raise) {
|
|
- py_openssl_set_exception(PyExc_RuntimeError);
|
|
+ _setException(PyExc_RuntimeError);
|
|
}
|
|
return 0;
|
|
}
|
|
@@ -475,7 +448,7 @@ py_openssl_drbg_urandom(char *buffer, Py_ssize_t size, int raise)
|
|
return 1;
|
|
} else {
|
|
if (raise) {
|
|
- py_openssl_set_exception(PyExc_RuntimeError);
|
|
+ _setException(PyExc_RuntimeError);
|
|
}
|
|
return 0;
|
|
}
|
|
--
|
|
2.21.0
|
|
|