[3.14] gh-134696: align OpenSSL and HACL*-based hash functions constructors AC signatures (GH-134713) (#134961)

OpenSSL and HACL*-based hash functions constructors now support both `data` and `string` parameters.
Previously these constructor functions inconsistently supported sometimes `data` and sometimes `string`,
while the documentation expected `data` to be given in all cases.

(cherry picked from commit c6e63d9d35)
(cherry picked from commit 379d0bc956)
This commit is contained in:
Bénédikt Tran 2025-06-01 10:26:56 +02:00 committed by GitHub
parent 5d07d16d45
commit 777fd4979c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 937 additions and 502 deletions

View file

@ -12,6 +12,7 @@ import io
import itertools
import logging
import os
import re
import sys
import sysconfig
import tempfile
@ -141,11 +142,10 @@ class HashLibTestCase(unittest.TestCase):
# of hashlib.new given the algorithm name.
for algorithm, constructors in self.constructors_to_test.items():
constructors.add(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)
def c(*args, __algorithm_name=algorithm, **kwargs):
return hashlib.new(__algorithm_name, *args, **kwargs)
c.__name__ = f'do_test_algorithm_via_hashlib_new_{algorithm}'
constructors.add(c)
_hashlib = self._conditional_import_module('_hashlib')
self._hashlib = _hashlib
@ -250,6 +250,75 @@ class HashLibTestCase(unittest.TestCase):
self._hashlib.new("md5", usedforsecurity=False)
self._hashlib.openssl_md5(usedforsecurity=False)
@unittest.skipIf(get_fips_mode(), "skip in FIPS mode")
def test_clinic_signature(self):
for constructor in self.hash_constructors:
with self.subTest(constructor.__name__):
constructor(b'')
constructor(data=b'')
constructor(string=b'') # should be deprecated in the future
digest_name = constructor(b'').name
with self.subTest(digest_name):
hashlib.new(digest_name, b'')
hashlib.new(digest_name, data=b'')
hashlib.new(digest_name, string=b'')
if self._hashlib:
self._hashlib.new(digest_name, b'')
self._hashlib.new(digest_name, data=b'')
self._hashlib.new(digest_name, string=b'')
@unittest.skipIf(get_fips_mode(), "skip in FIPS mode")
def test_clinic_signature_errors(self):
nomsg = b''
mymsg = b'msg'
conflicting_call = re.escape(
"'data' and 'string' are mutually exclusive "
"and support for 'string' keyword parameter "
"is slated for removal in a future version."
)
duplicated_param = re.escape("given by name ('data') and position")
unexpected_param = re.escape("got an unexpected keyword argument '_'")
for args, kwds, errmsg in [
# Reject duplicated arguments before unknown keyword arguments.
((nomsg,), dict(data=nomsg, _=nomsg), duplicated_param),
((mymsg,), dict(data=nomsg, _=nomsg), duplicated_param),
# Reject duplicated arguments before conflicting ones.
*itertools.product(
[[nomsg], [mymsg]],
[dict(data=nomsg), dict(data=nomsg, string=nomsg)],
[duplicated_param]
),
# Reject unknown keyword arguments before conflicting ones.
*itertools.product(
[()],
[
dict(_=None),
dict(data=nomsg, _=None),
dict(string=nomsg, _=None),
dict(string=nomsg, data=nomsg, _=None),
],
[unexpected_param]
),
((nomsg,), dict(_=None), unexpected_param),
((mymsg,), dict(_=None), unexpected_param),
# Reject conflicting arguments.
[(nomsg,), dict(string=nomsg), conflicting_call],
[(mymsg,), dict(string=nomsg), conflicting_call],
[(), dict(data=nomsg, string=nomsg), conflicting_call],
]:
for constructor in self.hash_constructors:
digest_name = constructor(b'').name
with self.subTest(constructor.__name__, args=args, kwds=kwds):
with self.assertRaisesRegex(TypeError, errmsg):
constructor(*args, **kwds)
with self.subTest(digest_name, args=args, kwds=kwds):
with self.assertRaisesRegex(TypeError, errmsg):
hashlib.new(digest_name, *args, **kwds)
if self._hashlib:
with self.assertRaisesRegex(TypeError, errmsg):
self._hashlib.new(digest_name, *args, **kwds)
def test_unknown_hash(self):
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
self.assertRaises(TypeError, hashlib.new, 1)
@ -719,8 +788,6 @@ class HashLibTestCase(unittest.TestCase):
self.assertRaises(ValueError, constructor, node_offset=-1)
self.assertRaises(OverflowError, constructor, node_offset=max_offset+1)
self.assertRaises(TypeError, constructor, data=b'')
self.assertRaises(TypeError, constructor, string=b'')
self.assertRaises(TypeError, constructor, '')
constructor(