8000 [3.13] gh-134696: align OpenSSL and HACL*-based hash functions constr… · python/cpython@ad4c913 · GitHub
[go: up one dir, main page]

Skip to content

Commit ad4c913

Browse files
committed
[3.13] gh-134696: align OpenSSL and HACL*-based hash functions constructors AC signatures (GH-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) (cherry picked from commit 379d0bc) Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
1 parent 19b31d1 commit ad4c913

18 files changed

+868
-439
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: 74 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,75 @@ def test_usedforsecurity_false(self):
251251
self._hashlib.new("md5", usedforsecurity=False)
252252
self._hashlib.openssl_md5(usedforsecurity=False)
253253

254+
@unittest.skipIf(get_fips_mode(), "skip in FIPS mode")
255+
def test_clinic_signature(self):
256+
for constructor in self.hash_constructors:
257+
with self.subTest(constructor.__name__):
258+
constructor(b'')
259+
constructor(data=b'')
260+
constructor(string=b'') # should be deprecated in the future
261+
262+
digest_name = constructor(b'').name
263+
with self.subTest(digest_name):
264+
hashlib.new(digest_name, b'')
265+
hashlib.new(digest_name, data=b'')
266+
hashlib.new(digest_name, string=b'')
267+
if self._hashlib:
268+
self._hashlib.new(digest_name, b'')
269+
self._hashlib.new(digest_name, data=b'')
270+
self._hashlib.new(digest_name, string=b'')
271+
272+
@unittest.skipIf(get_fips_mode(), "skip in FIPS mode")
273+
def test_clinic_signature_errors(self):
274+
nomsg = b''
275+
mymsg = b'msg'
276+
conflicting_call = re.escape(
277+
"'data' and 'string' are mutually exclusive "
278+
"and support for 'string' keyword parameter "
279+
"is slated for removal in a future version."
280+
)
281+
duplicated_param = re.escape("given by name ('data') and position")
282+
unexpected_param = re.escape("got an unexpected keyword argument '_'")
283+
for args, kwds, errmsg in [
284+
# Reject duplicated arguments before unknown keyword arguments.
285+
((nomsg,), dict(data=nomsg, _=nomsg), duplicated_param),
286+
((mymsg,), dict(data=nomsg, _=nomsg), duplicated_param),
287+
# Reject duplicated arguments before conflicting ones.
288+
*itertools.product(
289+
[[nomsg], [mymsg]],
290+
[dict(data=nomsg), dict(data=nomsg, string=nomsg)],
291+
[duplicated_param]
292+
),
293+
# Reject unknown keyword arguments before conflicting ones.
294+
*itertools.product(
295+
[()],
296+
[
297+
dict(_=None),
298+
dict(data=nomsg, _=None),
299+
dict(string=nomsg, _=None),
300+
dict(string=nomsg, data=nomsg, _=None),
301+
],
302+
[unexpected_param]
303+
),
304+
((nomsg,), dict(_=None), unexpected_param),
305+
((mymsg,), dict(_=None), unexpected_param),
306+
# Reject conflicting arguments.
307+
[(nomsg,), dict(string=nomsg), conflicting_call],
308+
[(mymsg,), dict(string=nomsg), conflicting_call],
309+
[(), dict(data=nomsg, string=nomsg), conflicting_call],
310+
]:
311+
for constructor in self.hash_constructors:
312+
digest_name = constructor(b'').name
313+
with self.subTest(constructor.__name__, args=args, kwds=kwds):
314+
with self.assertRaisesRegex(TypeError, errmsg):
315+
constructor(*args, **kwds)
316+
with self.subTest(digest_name, args=args, kwds=kwds):
317+
with self.assertRaisesRegex(TypeError, errmsg):
318+
hashlib.new(digest_name, *args, **kwds)
319+
if self._hashlib:
320+
with self.assertRaisesRegex(TypeError, errmsg):
321+
self._hashlib.new(digest_name, *args, **kwds)
322+
254323
def test_unknown_hash(self):
255324
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
256325
self.assertRaises(TypeError, hashlib.new, 1)
@@ -711,8 +780,6 @@ def check_blake2(self, constructor, salt_size, person_size, key_size,
711780
self.assertRaises(ValueError, constructor, node_offset=-1)
712781
self.assertRaises(OverflowError, constructor, node_offset=max_offset+1)
713782

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

718785
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.

Modules/_blake2/blake2b_impl.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ new_BLAKE2bObject(PyTypeObject *type)
7373
/*[clinic input]
7474
@classmethod
7575
_blake2.blake2b.__new__ as py_blake2b_new
76-
data: object(c_default="NULL") = b''
77-
/
76+
data as data_obj: object(c_default="NULL") = b''
7877
*
7978
digest_size: int(c_default="BLAKE2B_OUTBYTES") = _blake2.blake2b.MAX_DIGEST_SIZE
8079
key: Py_buffer(c_default="NULL", py_default="b''") = None
@@ -88,20 +87,26 @@ _blake2.blake2b.__new__ as py_blake2b_new
8887
inner_size: int = 0
8988
last_node: bool = False
9089
usedforsecurity: bool = True
90+
string: object(c_default="NULL") = None
9191
9292
Return a new BLAKE2b hash object.
9393
[clinic start generated code]*/
9494

9595
static PyObject *
96-
py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
96+
py_blake2b_new_impl(PyTypeObject *type, PyObject *data_obj, int digest_size,
9797
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
9898
int fanout, int depth, unsigned long leaf_size,
9999
unsigned long long node_offset, int node_depth,
100-
int inner_size, int last_node, int usedforsecurity)
101-
/*[clinic end generated code: output=32bfd8f043c6896f input=b947312abff46977]*/
100+
int inner_size, int last_node, int usedforsecurity,
101+
PyObject *string)
102+
/*[clinic end generated code: output=de64bd850606b6a0 input=a876354eae7e3c39]*/
102103
{
103104
BLAKE2bObject *self = NULL;
105+
PyObject *data;
104106
Py_buffer buf;
107+
if (_Py_hashlib_data_argument(&data, data_obj, string) < 0) {
108+
return NULL;
109+
}
105110

106111
self = new_BLAKE2bObject(type);
107112
if (self == NULL) {

Modules/_blake2/blake2s_impl.c

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ new_BLAKE2sObject(PyTypeObject *type)
7373
/*[clinic input]
7474
@classmethod
7575
_blake2.blake2s.__new__ as py_blake2s_new
76-
data: object(c_default="NULL") = b''
77-
/
76+
data as data_obj: object(c_default="NULL") = b''
7877
*
7978
digest_size: int(c_default="BLAKE2S_OUTBYTES") = _blake2.blake2s.MAX_DIGEST_SIZE
8079
key: Py_buffer(c_default="NULL", py_default="b''") = None
@@ -88,19 +87,25 @@ _blake2.blake2s.__new__ as py_blake2s_new
8887
inner_size: int = 0
8988
last_node: bool = False
9089
usedforsecurity: bool = True
90+
string: object(c_default="NULL") = None
9191
9292
Return a new BLAKE2s hash object.
9393
[clinic start generated code]*/
9494

9595
static PyObject *
96-
py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
96+
py_blake2s_new_impl(PyTypeObject *type, PyObject *data_obj, int digest_size,
9797
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
9898
int fanout, int depth, unsigned long leaf_size,
9999
unsigned long long node_offset, int node_depth,
100-
int inner_size, int last_node, int usedforsecurity)
101-
/*[clinic end generated code: output=556181f73905c686 input=4dda87723f23abb0]*/
100+
int inner_size, int last_node, int usedforsecurity,
101+
PyObject *string)
102+
/*[clinic end generated code: output=582a0c4295cc3a3c input=308c3421c9c57f03]*/
102103
{
103104
BLAKE2sObject *self = NULL;
105+
PyObject *data;
106+
if (_Py_hashlib_data_argument(&data, data_obj, string) < 0) {
107+
return NULL;
108+
}
104109
Py_buffer buf;
105110

106111
self = new_BLAKE2sObject(type);

Modules/_blake2/clinic/blake2b_impl.c.h

Lines changed: 31 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)
0