Skip to content

Commit 7cad53e

Browse files
tirangpshead
authored andcommitted
bpo-9216: Add usedforsecurity to hashlib constructors (GH-16044)
The usedforsecurity keyword only argument added to the hash constructors is useful for FIPS builds and similar restrictive environment with non-technical requirements that legacy algorithms be forbidden by their implementations without being explicitly annotated as not being used for any security related purposes. Linux distros with FIPS support benefit from this being standard rather than making up their own way(s) to do it. Contributed and Signed-off-by: Christian Heimes christian@python.org
1 parent 3a4f667 commit 7cad53e

20 files changed

+495
-165
lines changed

Doc/library/hashlib.rst

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ Constructors for hash algorithms that are always present in this module are
6767
:func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`,
6868
:func:`sha512`, :func:`blake2b`, and :func:`blake2s`.
6969
:func:`md5` is normally available as well, though it
70-
may be missing if you are using a rare "FIPS compliant" build of Python.
70+
may be missing or blocked if you are using a rare "FIPS compliant" build of Python.
7171
Additional algorithms may also be available depending upon the OpenSSL
7272
library that Python uses on your platform. On most platforms the
7373
:func:`sha3_224`, :func:`sha3_256`, :func:`sha3_384`, :func:`sha3_512`,
@@ -80,6 +80,13 @@ library that Python uses on your platform. On most platforms the
8080
.. versionadded:: 3.6
8181
:func:`blake2b` and :func:`blake2s` were added.
8282

83+
.. versionchanged:: 3.9
84+
All hashlib constructors take a keyword-only argument *usedforsecurity*
85+
with default value *True*. A false value allows the use of insecure and
86+
blocked hashing algorithms in restricted environments. *False* indicates
87+
that the hashing algorithm is not used in a security context, e.g. as a
88+
non-cryptographic one-way compression function.
89+
8390
For example, to obtain the digest of the byte string ``b'Nobody inspects the
8491
spammish repetition'``::
8592

@@ -99,7 +106,7 @@ More condensed:
99106
>>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest()
100107
'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2'
101108

102-
.. function:: new(name[, data])
109+
.. function:: new(name[, data], *, usedforsecurity=True)
103110

104111
Is a generic constructor that takes the string *name* of the desired
105112
algorithm as its first parameter. It also exists to allow access to the
@@ -308,11 +315,13 @@ New hash objects are created by calling constructor functions:
308315

309316
.. function:: blake2b(data=b'', *, digest_size=64, key=b'', salt=b'', \
310317
person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \
311-
node_depth=0, inner_size=0, last_node=False)
318+
node_depth=0, inner_size=0, last_node=False, \
319+
usedforsecurity=True)
312320

313321
.. function:: blake2s(data=b'', *, digest_size=32, key=b'', salt=b'', \
314322
person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \
315-
node_depth=0, inner_size=0, last_node=False)
323+
node_depth=0, inner_size=0, last_node=False, \
324+
usedforsecurity=True)
316325

317326

318327
These functions return the corresponding hash objects for calculating

Lib/test/test_hashlib.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,15 @@ def test_algorithms_available(self):
190190
self.assertTrue(set(hashlib.algorithms_guaranteed).
191191
issubset(hashlib.algorithms_available))
192192

193+
def test_usedforsecurity(self):
194+
for cons in self.hash_constructors:
195+
cons(usedforsecurity=True)
196+
cons(usedforsecurity=False)
197+
cons(b'', usedforsecurity=True)
198+
cons(b'', usedforsecurity=False)
199+
hashlib.new("sha256", usedforsecurity=True)
200+
hashlib.new("sha256", usedforsecurity=False)
201+
193202
def test_unknown_hash(self):
194203
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
195204
self.assertRaises(TypeError, hashlib.new, 1)

Lib/uuid.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -772,8 +772,11 @@ def uuid1(node=None, clock_seq=None):
772772
def uuid3(namespace, name):
773773
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
774774
from hashlib import md5
775-
hash = md5(namespace.bytes + bytes(name, "utf-8")).digest()
776-
return UUID(bytes=hash[:16], version=3)
775+
digest = md5(
776+
namespace.bytes + bytes(name, "utf-8"),
777+
usedforsecurity=False
778+
).digest()
779+
return UUID(bytes=digest[:16], version=3)
777780

778781
def uuid4():
779782
"""Generate a random UUID."""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
hashlib constructors now support usedforsecurity flag to signal that a
2+
hashing algorithm is not used in a security context.

Modules/_blake2/blake2b_impl.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ _blake2.blake2b.__new__ as py_blake2b_new
8181
node_depth: int = 0
8282
inner_size: int = 0
8383
last_node: bool = False
84+
usedforsecurity: bool = True
8485
8586
Return a new BLAKE2b hash object.
8687
[clinic start generated code]*/
@@ -90,8 +91,8 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
9091
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
9192
int fanout, int depth, unsigned long leaf_size,
9293
unsigned long long node_offset, int node_depth,
93-
int inner_size, int last_node)
94-
/*[clinic end generated code: output=65e732c66c2297a0 input=82be35a4e6a9daa2]*/
94+
int inner_size, int last_node, int usedforsecurity)
95+
/*[clinic end generated code: output=32bfd8f043c6896f input=b947312abff46977]*/
9596
{
9697
BLAKE2bObject *self = NULL;
9798
Py_buffer buf;

Modules/_blake2/blake2s_impl.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ _blake2.blake2s.__new__ as py_blake2s_new
8181
node_depth: int = 0
8282
inner_size: int = 0
8383
last_node: bool = False
84+
usedforsecurity: bool = True
8485
8586
Return a new BLAKE2s hash object.
8687
[clinic start generated code]*/
@@ -90,8 +91,8 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
9091
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
9192
int fanout, int depth, unsigned long leaf_size,
9293
unsigned long long node_offset, int node_depth,
93-
int inner_size, int last_node)
94-
/*[clinic end generated code: output=b95806be0514dcf7 input=641c0509debf714d]*/
94+
int inner_size, int last_node, int usedforsecurity)
95+
/*[clinic end generated code: output=556181f73905c686 input=4dda87723f23abb0]*/
9596
{
9697
BLAKE2sObject *self = NULL;
9798
Py_buffer buf;

Modules/_blake2/clinic/blake2b_impl.c.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ preserve
55
PyDoc_STRVAR(py_blake2b_new__doc__,
66
"blake2b(data=b\'\', /, *, digest_size=_blake2.blake2b.MAX_DIGEST_SIZE,\n"
77
" key=b\'\', salt=b\'\', person=b\'\', fanout=1, depth=1, leaf_size=0,\n"
8-
" node_offset=0, node_depth=0, inner_size=0, last_node=False)\n"
8+
" node_offset=0, node_depth=0, inner_size=0, last_node=False,\n"
9+
" usedforsecurity=True)\n"
910
"--\n"
1011
"\n"
1112
"Return a new BLAKE2b hash object.");
@@ -15,15 +16,15 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
1516
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
1617
int fanout, int depth, unsigned long leaf_size,
1718
unsigned long long node_offset, int node_depth,
18-
int inner_size, int last_node);
19+
int inner_size, int last_node, int usedforsecurity);
1920

2021
static PyObject *
2122
py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
2223
{
2324
PyObject *return_value = NULL;
24-
static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", NULL};
25+
static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", "usedforsecurity", NULL};
2526
static _PyArg_Parser _parser = {NULL, _keywords, "blake2b", 0};
26-
PyObject *argsbuf[12];
27+
PyObject *argsbuf[13];
2728
PyObject * const *fastargs;
2829
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
2930
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0;
@@ -39,6 +40,7 @@ py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
3940
int node_depth = 0;
4041
int inner_size = 0;
4142
int last_node = 0;
43+
int usedforsecurity = 1;
4244

4345
fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf);
4446
if (!fastargs) {
@@ -175,12 +177,21 @@ py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
175177
goto skip_optional_kwonly;
176178
}
177179
}
178-
last_node = PyObject_IsTrue(fastargs[11]);
179-
if (last_node < 0) {
180+
if (fastargs[11]) {
181+
last_node = PyObject_IsTrue(fastargs[11]);
182+
if (last_node < 0) {
183+
goto exit;
184+
}
185+
if (!--noptargs) {
186+
goto skip_optional_kwonly;
187+
}
188+
}
189+
usedforsecurity = PyObject_IsTrue(fastargs[12]);
190+
if (usedforsecurity < 0) {
180191
goto exit;
181192
}
182193
skip_optional_kwonly:
183-
return_value = py_blake2b_new_impl(type, data, digest_size, &key, &salt, &person, fanout, depth, leaf_size, node_offset, node_depth, inner_size, last_node);
194+
return_value = py_blake2b_new_impl(type, data, digest_size, &key, &salt, &person, fanout, depth, leaf_size, node_offset, node_depth, inner_size, last_node, usedforsecurity);
184195

185196
exit:
186197
/* Cleanup for key */
@@ -261,4 +272,4 @@ _blake2_blake2b_hexdigest(BLAKE2bObject *self, PyObject *Py_UNUSED(ignored))
261272
{
262273
return _blake2_blake2b_hexdigest_impl(self);
263274
}
264-
/*[clinic end generated code: output=cbb625d7f60c288c input=a9049054013a1b77]*/
275+
/*[clinic end generated code: output=2d6d0fe9aa42a42a input=a9049054013a1b77]*/

Modules/_blake2/clinic/blake2s_impl.c.h

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ preserve
55
PyDoc_STRVAR(py_blake2s_new__doc__,
66
"blake2s(data=b\'\', /, *, digest_size=_blake2.blake2s.MAX_DIGEST_SIZE,\n"
77
" key=b\'\', salt=b\'\', person=b\'\', fanout=1, depth=1, leaf_size=0,\n"
8-
" node_offset=0, node_depth=0, inner_size=0, last_node=False)\n"
8+
" node_offset=0, node_depth=0, inner_size=0, last_node=False,\n"
9+
" usedforsecurity=True)\n"
910
"--\n"
1011
"\n"
1112
"Return a new BLAKE2s hash object.");
@@ -15,15 +16,15 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
1516
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
1617
int fanout, int depth, unsigned long leaf_size,
1718
unsigned long long node_offset, int node_depth,
18-
int inner_size, int last_node);
19+
int inner_size, int last_node, int usedforsecurity);
1920

2021
static PyObject *
2122
py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
2223
{
2324
PyObject *return_value = NULL;
24-
static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", NULL};
25+
static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", "usedforsecurity", NULL};
2526
static _PyArg_Parser _parser = {NULL, _keywords, "blake2s", 0};
26-
PyObject *argsbuf[12];
27+
PyObject *argsbuf[13];
2728
PyObject * const *fastargs;
2829
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
2930
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0;
@@ -39,6 +40,7 @@ py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
3940
int node_depth = 0;
4041
int inner_size = 0;
4142
int last_node = 0;
43+
int usedforsecurity = 1;
4244

4345
fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf);
4446
if (!fastargs) {
@@ -175,12 +177,21 @@ py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
175177
goto skip_optional_kwonly;
176178
}
177179
}
178-
last_node = PyObject_IsTrue(fastargs[11]);
179-
if (last_node < 0) {
180+
if (fastargs[11]) {
181+
last_node = PyObject_IsTrue(fastargs[11]);
182+
if (last_node < 0) {
183+
goto exit;
184+
}
185+
if (!--noptargs) {
186+
goto skip_optional_kwonly;
187+
}
188+
}
189+
usedforsecurity = PyObject_IsTrue(fastargs[12]);
190+
if (usedforsecurity < 0) {
180191
goto exit;
181192
}
182193
skip_optional_kwonly:
183-
return_value = py_blake2s_new_impl(type, data, digest_size, &key, &salt, &person, fanout, depth, leaf_size, node_offset, node_depth, inner_size, last_node);
194+
return_value = py_blake2s_new_impl(type, data, digest_size, &key, &salt, &person, fanout, depth, leaf_size, node_offset, node_depth, inner_size, last_node, usedforsecurity);
184195

185196
exit:
186197
/* Cleanup for key */
@@ -261,4 +272,4 @@ _blake2_blake2s_hexdigest(BLAKE2sObject *self, PyObject *Py_UNUSED(ignored))
261272
{
262273
return _blake2_blake2s_hexdigest_impl(self);
263274
}
264-
/*[clinic end generated code: output=39af5a74c8805b36 input=a9049054013a1b77]*/
275+
/*[clinic end generated code: output=c80d8d06ce40a192 input=a9049054013a1b77]*/

0 commit comments

Comments
 (0)