Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Doc/deprecations/pending-removal-in-3.21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ Pending removal in Python 3.21
parameter or pass the dispatch type explicitly instead.
(Contributed by Bartosz Sławecki in :gh:`143465`.)

* :mod:`hashlib`:

* Passing *salt* or *person* to :func:`~hashlib.blake2b` and
:func:`~hashlib.blake2s` with an empty or omitted *key* is deprecated
since Python 3.16 and slated for removal in Python 3.21. Keyed hashing
still accepts these parameters.
(Contributed by Bénédikt Tran in :gh:`160333`.)

* :mod:`struct`:

* Soft-deprecated since Python 3.15, using ``'F'`` and ``'D'`` type codes are now
Expand Down
4 changes: 4 additions & 0 deletions Doc/library/hashlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,10 @@ BLAKE2s 32 32 8 8
example, ``b'salt'`` and ``b'salt\x00'`` is the same value. (This is not
the case for *key*.)

.. deprecated-removed:: next 3.21
Passing *salt* or *person* with an empty or omitted *key* is deprecated.


These sizes are available as module `constants`_ described below.

Constructor functions also accept the following tree hashing parameters:
Expand Down
8 changes: 8 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1154,6 +1154,14 @@ New deprecations
instead.
(Contributed by Bartosz Sławecki in :gh:`143465`.)

* :mod:`hashlib`:

* Passing *salt* or *person* to :func:`~hashlib.blake2b` and
:func:`~hashlib.blake2s` with an empty or omitted *key* is deprecated
since Python 3.16 and slated for removal in Python 3.21. Keyed hashing
still accepts these parameters.
(Contributed by Bénédikt Tran in :gh:`160333`.)

* :mod:`struct`:

* The ``'F'`` and ``'D'`` type codes, soft-deprecated since Python 3.15,
Expand Down
102 changes: 84 additions & 18 deletions Lib/test/test_hashlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from test.support.import_helper import import_fresh_module
from test.support import requires_resource
from test.support import threading_helper
from test.support import warnings_helper
from http.client import HTTPException


Expand Down Expand Up @@ -125,6 +126,14 @@ def read_vectors(hash_name):
)


def deprecated_blake2_unkeyed_parameter(name):
return re.escape(
f"the {name!r} keyword parameter is deprecated for unkeyed "
"hashing since Python 3.16 and slated for removal in Python 3.21; "
f"use a non-empty 'key' or omit {name!r}"
)


class HashLibTestCase(unittest.TestCase):
supported_hash_names = ( 'md5', 'MD5', 'sha1', 'SHA1',
'sha224', 'SHA224', 'sha256', 'SHA256',
Expand Down Expand Up @@ -839,16 +848,26 @@ def skip_if_blake2_not_builtin(self, name, skip_reason):
def check_blake2(self, constructor, salt_size, person_size, key_size,
digest_size, max_offset):
self.assertEqual(constructor.SALT_SIZE, salt_size)
msg = deprecated_blake2_unkeyed_parameter('salt')
for i in range(salt_size + 1):
constructor(salt=b'a' * i)
with self.assertWarnsRegex(DeprecationWarning, msg):
constructor(salt=b'a' * i)
constructor(key=b'k', salt=b'a' * i)
salt = b'a' * (salt_size + 1)
self.assertRaises(ValueError, constructor, salt=salt)
with self.assertWarnsRegex(DeprecationWarning, msg):
self.assertRaises(ValueError, constructor, salt=salt)
self.assertRaises(ValueError, constructor, key=b'k', salt=salt)

self.assertEqual(constructor.PERSON_SIZE, person_size)
msg = deprecated_blake2_unkeyed_parameter('person')
for i in range(person_size+1):
constructor(person=b'a' * i)
with self.assertWarnsRegex(DeprecationWarning, msg):
constructor(person=b'a' * i)
constructor(key=b'k', person=b'a' * i)
person = b'a' * (person_size + 1)
self.assertRaises(ValueError, constructor, person=person)
with self.assertWarnsRegex(DeprecationWarning, msg):
self.assertRaises(ValueError, constructor, person=person)
self.assertRaises(ValueError, constructor, key=b'k', person=person)

self.assertEqual(constructor.MAX_DIGEST_SIZE, digest_size)
for i in range(1, digest_size + 1):
Expand Down Expand Up @@ -898,20 +917,26 @@ def check_blake2(self, constructor, salt_size, person_size, key_size,

self.assertRaises(TypeError, constructor, '')

constructor(
b'',
key=b'',
salt=b'',
person=b'',
digest_size=17,
fanout=1,
depth=1,
leaf_size=256,
node_offset=512,
node_depth=1,
inner_size=7,
last_node=True
)
salt_msg = deprecated_blake2_unkeyed_parameter('salt')
person_msg = deprecated_blake2_unkeyed_parameter('person')
with warnings_helper.check_warnings(
(salt_msg, DeprecationWarning),
(person_msg, DeprecationWarning),
):
constructor(
b'',
key=b'',
salt=b'',
person=b'',
digest_size=17,
fanout=1,
depth=1,
leaf_size=256,
node_offset=512,
node_depth=1,
inner_size=7,
last_node=True
)

def blake2_rfc7693(self, constructor, md_len, in_len):
def selftest_seq(length, seed):
Expand Down Expand Up @@ -1023,6 +1048,47 @@ def test_blake2s_vectors(self):
key = bytes.fromhex(key)
self.check('blake2s', msg, md, key=key)

@requires_blake2
@support.subTests("name", ["blake2b", "blake2s"])
@support.subTests("parameter", ["salt", "person"])
@support.subTests("value", [b'', b'x'])
@support.subTests("key", [None, b''])
def test_blake2_unkeyed_parameter_deprecated(self, name, parameter,
value, key):
constructor = getattr(hashlib, name)
msg = deprecated_blake2_unkeyed_parameter(parameter)
kwargs = {parameter: value}
if key is not None:
kwargs['key'] = key
with self.assertWarnsRegex(DeprecationWarning, msg):
constructor(**kwargs)
with self.assertWarnsRegex(DeprecationWarning, msg):
hashlib.new(name, **kwargs)

@requires_blake2
@support.subTests("name", ["blake2b", "blake2s"])
@support.subTests("parameter", ["salt", "person"])
@support.subTests("value", [b'', b'x'])
def test_blake2_keyed_parameter_not_deprecated(self, name, parameter,
value):
constructor = getattr(hashlib, name)
kwargs = {'key': b'key', parameter: value}
with warnings_helper.check_no_warnings(self,
category=DeprecationWarning):
constructor(**kwargs)
hashlib.new(name, **kwargs)

@requires_blake2
@support.subTests("name", ["blake2b", "blake2s"])
@support.subTests("key", [None, b''])
def test_blake2_unkeyed_parameters_omitted(self, name, key):
constructor = getattr(hashlib, name)
kwargs = {} if key is None else {'key': key}
with warnings_helper.check_no_warnings(self,
category=DeprecationWarning):
constructor(b'data', **kwargs)
hashlib.new(name, b'data', **kwargs)

@requires_sha3
def test_case_sha3_224_0(self):
self.check('sha3_224', b"",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:mod:`hashlib`: Passing *salt* or *person* to :func:`~hashlib.blake2b` and
:func:`~hashlib.blake2s` with an empty or omitted *key* is now deprecated
and slated for removal in Python 3.21. Patch by Bénédikt Tran.
42 changes: 42 additions & 0 deletions Modules/blake2module.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,44 @@ py_blake2_validate_params(Blake2Object *self,
return -1;
}

/*
* Emit a DeprecationWarning for a parameter given without a key.
*
* Return 0 on success; otherwise, set an exception and return -1.
*/
static int
blake2_warn_unkeyed_parameter(const char *name)
{
return PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"the '%s' keyword parameter is deprecated for "
"unkeyed hashing since Python 3.16 and slated "
"for removal in Python 3.21; use a non-empty "
"'key' or omit '%s'", name, name);
}

/*
* Check the salt and personalization parameters for unkeyed hashing.
*
* Omitting 'key' is not given or empty when using 'salt' or 'person'
* emits a deprecation warning.
*
* Return 0 on success; otherwise, set an exception and return -1.
*/
static int
blake2_check_unkeyed_parameters(Py_buffer *key, Py_buffer *salt,
Py_buffer *person)
{
if (key->obj != NULL && key->len > 0) {
return 0;
}
if (salt->obj != NULL && blake2_warn_unkeyed_parameter("salt") < 0) {
return -1;
}
if (person->obj != NULL && blake2_warn_unkeyed_parameter("person") < 0) {
return -1;
}
return 0;
}

static PyObject *
py_blake2_new(PyTypeObject *type, PyObject *data, int digest_size,
Expand All @@ -571,6 +609,10 @@ py_blake2_new(PyTypeObject *type, PyObject *data, int digest_size,
{
Blake2Object *self = NULL;

if (blake2_check_unkeyed_parameters(key, salt, person) < 0) {
return NULL;
}

self = new_Blake2Object(type);
if (self == NULL) {
goto error;
Expand Down
Loading