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

Skip to content
  • Commit 2c325e2

    Browse files
    authored
    [3.13] gh-134696: align OpenSSL and HACL*-based hash functions constructors AC signatures (GH-134713) (#134962)
    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)
    1 parent e69ecfe commit 2c325e2

    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") = 4F6 b''
    7877
    * 106D2
    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