summaryrefslogtreecommitdiffstats
path: root/Lib/test/_test_multiprocessing.py
diff options
context:
space:
mode:
authorChristian Heimes <christian@python.org>2023-05-20 23:33:09 (GMT)
committerGitHub <noreply@github.com>2023-05-20 23:33:09 (GMT)
commit3ed57e4995d9f8583083483f397ddc3131720953 (patch)
treecea152a7d52840c5a958db40c9f543d3fa5d0d8c /Lib/test/_test_multiprocessing.py
parent12f1581b0c43f40a792c188e17d2dea2357debb3 (diff)
downloadcpython-3ed57e4995d9f8583083483f397ddc3131720953.zip
cpython-3ed57e4995d9f8583083483f397ddc3131720953.tar.gz
cpython-3ed57e4995d9f8583083483f397ddc3131720953.tar.bz2
gh-61460: Stronger HMAC in multiprocessing (#20380)
bpo-17258: `multiprocessing` now supports stronger HMAC algorithms for inter-process connection authentication rather than only HMAC-MD5. Signed-off-by: Christian Heimes <christian@python.org> gpshead: I Reworked to be more robust while keeping the idea. The protocol modification idea remains, but we now take advantage of the message length as an indicator of legacy vs modern protocol version. No more regular expression usage. We now default to HMAC-SHA256, but do so in a way that will be compatible when communicating with older clients or older servers. No protocol transition period is needed. More integration tests to verify these claims remain true are required. I'm unaware of anyone depending on multiprocessing connections between different Python versions. --------- Signed-off-by: Christian Heimes <christian@python.org> Co-authored-by: Gregory P. Smith [Google] <greg@krypto.org>
Diffstat (limited to 'Lib/test/_test_multiprocessing.py')
-rw-r--r--Lib/test/_test_multiprocessing.py57
1 files changed, 47 insertions, 10 deletions
diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py
index 9a2db24..767f049 100644
--- a/Lib/test/_test_multiprocessing.py
+++ b/Lib/test/_test_multiprocessing.py
@@ -48,6 +48,7 @@ import multiprocessing.heap
import multiprocessing.managers
import multiprocessing.pool
import multiprocessing.queues
+from multiprocessing.connection import wait, AuthenticationError
from multiprocessing import util
@@ -131,8 +132,6 @@ HAVE_GETVALUE = not getattr(_multiprocessing,
WIN32 = (sys.platform == "win32")
-from multiprocessing.connection import wait
-
def wait_for_handle(handle, timeout):
if timeout is not None and timeout < 0.0:
timeout = None
@@ -3042,7 +3041,7 @@ class _TestRemoteManager(BaseTestCase):
del queue
-@hashlib_helper.requires_hashdigest('md5')
+@hashlib_helper.requires_hashdigest('sha256')
class _TestManagerRestart(BaseTestCase):
@classmethod
@@ -3531,7 +3530,7 @@ class _TestPoll(BaseTestCase):
#
@unittest.skipUnless(HAS_REDUCTION, "test needs multiprocessing.reduction")
-@hashlib_helper.requires_hashdigest('md5')
+@hashlib_helper.requires_hashdigest('sha256')
class _TestPicklingConnections(BaseTestCase):
ALLOWED_TYPES = ('processes',)
@@ -3834,7 +3833,7 @@ class _TestSharedCTypes(BaseTestCase):
@unittest.skipUnless(HAS_SHMEM, "requires multiprocessing.shared_memory")
-@hashlib_helper.requires_hashdigest('md5')
+@hashlib_helper.requires_hashdigest('sha256')
class _TestSharedMemory(BaseTestCase):
ALLOWED_TYPES = ('processes',)
@@ -4636,7 +4635,7 @@ class TestInvalidHandle(unittest.TestCase):
-@hashlib_helper.requires_hashdigest('md5')
+@hashlib_helper.requires_hashdigest('sha256')
class OtherTest(unittest.TestCase):
# TODO: add more tests for deliver/answer challenge.
def test_deliver_challenge_auth_failure(self):
@@ -4656,7 +4655,7 @@ class OtherTest(unittest.TestCase):
def recv_bytes(self, size):
self.count += 1
if self.count == 1:
- return multiprocessing.connection.CHALLENGE
+ return multiprocessing.connection._CHALLENGE
elif self.count == 2:
return b'something bogus'
return b''
@@ -4666,6 +4665,44 @@ class OtherTest(unittest.TestCase):
multiprocessing.connection.answer_challenge,
_FakeConnection(), b'abc')
+
+@hashlib_helper.requires_hashdigest('md5')
+@hashlib_helper.requires_hashdigest('sha256')
+class ChallengeResponseTest(unittest.TestCase):
+ authkey = b'supadupasecretkey'
+
+ def create_response(self, message):
+ return multiprocessing.connection._create_response(
+ self.authkey, message
+ )
+
+ def verify_challenge(self, message, response):
+ return multiprocessing.connection._verify_challenge(
+ self.authkey, message, response
+ )
+
+ def test_challengeresponse(self):
+ for algo in [None, "md5", "sha256"]:
+ with self.subTest(f"{algo=}"):
+ msg = b'is-twenty-bytes-long' # The length of a legacy message.
+ if algo:
+ prefix = b'{%s}' % algo.encode("ascii")
+ else:
+ prefix = b''
+ msg = prefix + msg
+ response = self.create_response(msg)
+ if not response.startswith(prefix):
+ self.fail(response)
+ self.verify_challenge(msg, response)
+
+ # TODO(gpshead): We need integration tests for handshakes between modern
+ # deliver_challenge() and verify_response() code and connections running a
+ # test-local copy of the legacy Python <=3.11 implementations.
+
+ # TODO(gpshead): properly annotate tests for requires_hashdigest rather than
+ # only running these on a platform supporting everything. otherwise logic
+ # issues preventing it from working on FIPS mode setups will be hidden.
+
#
# Test Manager.start()/Pool.__init__() initializer feature - see issue 5585
#
@@ -4673,7 +4710,7 @@ class OtherTest(unittest.TestCase):
def initializer(ns):
ns.test += 1
-@hashlib_helper.requires_hashdigest('md5')
+@hashlib_helper.requires_hashdigest('sha256')
class TestInitializers(unittest.TestCase):
def setUp(self):
self.mgr = multiprocessing.Manager()
@@ -5537,7 +5574,7 @@ class TestPoolNotLeakOnFailure(unittest.TestCase):
any(process.is_alive() for process in forked_processes))
-@hashlib_helper.requires_hashdigest('md5')
+@hashlib_helper.requires_hashdigest('sha256')
class TestSyncManagerTypes(unittest.TestCase):
"""Test all the types which can be shared between a parent and a
child process by using a manager which acts as an intermediary
@@ -5969,7 +6006,7 @@ def install_tests_in_module_dict(remote_globs, start_method):
class Temp(base, Mixin, unittest.TestCase):
pass
if type_ == 'manager':
- Temp = hashlib_helper.requires_hashdigest('md5')(Temp)
+ Temp = hashlib_helper.requires_hashdigest('sha256')(Temp)
Temp.__name__ = Temp.__qualname__ = newname
Temp.__module__ = __module__
remote_globs[newname] = Temp