Skip to content

Commit 4ba757a

Browse files
committed
[3.13] pythongh-134696: align OpenSSL and HACL*-based hash functions constructors AC signatures (pythonGH-134713)
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 c6e63d9) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent 19b31d1 commit 4ba757a

15 files changed

+1841
-391
lines changed

Lib/hashlib.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,29 +141,29 @@ def __get_openssl_constructor(name):
141141
return __get_builtin_constructor(name)
142142

143143

144-
def __py_new(name, data=b'', **kwargs):
144+
def __py_new(name, *args, **kwargs):
145145
"""new(name, data=b'', **kwargs) - Return a new hashing object using the
146146
named algorithm; optionally initialized with data (which must be
147147
a bytes-like object).
148148
"""
149-
return __get_builtin_constructor(name)(data, **kwargs)
149+
return __get_builtin_constructor(name)(*args, **kwargs)
150150

151151

152-
def __hash_new(name, data=b'', **kwargs):
152+
def __hash_new(name, *args, **kwargs):
153153
"""new(name, data=b'') - Return a new hashing object using the named algorithm;
154154
optionally initialized with data (which must be a bytes-like object).
155155
"""
156156
if name in __block_openssl_constructor:
157157
# Prefer our builtin blake2 implementation.
158-
return __get_builtin_constructor(name)(data, **kwargs)
158+
return __get_builtin_constructor(name)(*args, **kwargs)
159159
try:
160-
return _hashlib.new(name, data, **kwargs)
160+
return _hashlib.new(name, *args, **kwargs)
161161
except ValueError:
162162
# If the _hashlib module (OpenSSL) doesn't support the named
163163
# hash, try using our builtin implementations.
164164
# This allows for SHA224/256 and SHA384/512 support even though
165165
# the OpenSSL library prior to 0.9.8 doesn't provide them.
166-
return __get_builtin_constructor(name)(data)
166+
return __get_builtin_constructor(name)(*args, **kwargs)
167167

168168

169169
try:

Lib/test/test_hashlib.py

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import itertools
1313
import logging
1414
import os
15+
import re
1516
import sys
1617
import sysconfig
1718
import threading
@@ -140,11 +141,10 @@ def __init__(self, *args, **kwargs):
140141
# of hashlib.new given the algorithm name.
141142
for algorithm, constructors in self.constructors_to_test.items():
142143
constructors.add(getattr(hashlib, algorithm))
143-
def _test_algorithm_via_hashlib_new(data=None, _alg=algorithm, **kwargs):
144-
if data is None:
145-
return hashlib.new(_alg, **kwargs)
146-
return hashlib.new(_alg, data, **kwargs)
147-
constructors.add(_test_algorithm_via_hashlib_new)
144+
def c(*args, __algorithm_name=algorithm, **kwargs):
145+
return hashlib.new(__algorithm_name, *args, **kwargs)
146+
c.__name__ = f'do_test_algorithm_via_hashlib_new_{algorithm}'
147+
constructors.add(c)
148148

149149
_hashlib = self._conditional_import_module('_hashlib')
150150
self._hashlib = _hashlib
@@ -251,6 +251,56 @@ def test_usedforsecurity_false(self):
251251
self._hashlib.new("md5", usedforsecurity=False)
252252
self._hashlib.openssl_md5(usedforsecurity=False)
253253

254+
def test_clinic_signature(self):
255+
for constructor in self.hash_constructors:
256+
with self.subTest(constructor.__name__):
257+
constructor(b'')
258+
constructor(data=b'')
259+
constructor(string=b'') # should be deprecated in the future
260+
261+
def test_clinic_signature_errors(self):
262+
nomsg = b''
263+
mymsg = b'msg'
264+
conflicting_call = re.escape(
265+
"'data' and 'string' are mutually exclusive "
266+
"and support for 'string' keyword parameter "
267+
"is slated for removal in a future version."
268+
)
269+
duplicated_param = re.escape("given by name ('data') and position")
270+
unexpected_param = re.escape("got an unexpected keyword argument '_'")
271+
for args, kwds, errmsg in [
272+
# Reject duplicated arguments before unknown keyword arguments.
273+
((nomsg,), dict(data=nomsg, _=nomsg), duplicated_param),
274+
((mymsg,), dict(data=nomsg, _=nomsg), duplicated_param),
275+
# Reject duplicated arguments before conflicting ones.
276+
*itertools.product(
277+
[[nomsg], [mymsg]],
278+
[dict(data=nomsg), dict(data=nomsg, string=nomsg)],
279+
[duplicated_param]
280+
),
281+
# Reject unknown keyword arguments before conflicting ones.
282+
*itertools.product(
283+
[()],
284+
[
285+
dict(_=None),
286+
dict(data=nomsg, _=None),
287+
dict(string=nomsg, _=None),
288+
dict(string=nomsg, data=nomsg, _=None),
289+
],
290+
[unexpected_param]
291+
),
292+
((nomsg,), dict(_=None), unexpected_param),
293+
((mymsg,), dict(_=None), unexpected_param),
294+
# Reject conflicting arguments.
295+
[(nomsg,), dict(string=nomsg), conflicting_call],
296+
[(mymsg,), dict(string=nomsg), conflicting_call],
297+
[(), dict(data=nomsg, string=nomsg), conflicting_call],
298+
]:
299+
for constructor in self.hash_constructors:
300+
with self.subTest(constructor.__name__, args=args, kwds=kwds):
301+
with self.assertRaisesRegex(TypeError, errmsg):
302+
constructor(*args, **kwds)
303+
254304
def test_unknown_hash(self):
255305
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
256306
self.assertRaises(TypeError, hashlib.new, 1)
@@ -711,8 +761,6 @@ def check_blake2(self, constructor, salt_size, person_size, key_size,
711761
self.assertRaises(ValueError, constructor, node_offset=-1)
712762
self.assertRaises(OverflowError, constructor, node_offset=max_offset+1)
713763

714-
self.assertRaises(TypeError, constructor, data=b'')
715-
self.assertRaises(TypeError, constructor, string=b'')
716764
self.assertRaises(TypeError, constructor, '')
717765

718766
constructor(
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Built-in HACL* and OpenSSL implementations of hash function constructors
2+
now correctly accept the same *documented* named arguments. For instance,
3+
:func:`~hashlib.md5` could be previously invoked as ``md5(data=data)``
4+
or ``md5(string=string)`` depending on the underlying implementation
5+
but these calls were not compatible. Patch by Bénédikt Tran.

0 commit comments

Comments
 (0)