Skip to content

Commit

Permalink
bpo-9216: Add usedforsecurity to hashlib constructors (GH-16044)
Browse files Browse the repository at this point in the history
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 [email protected]
  • Loading branch information
tiran authored and gpshead committed Sep 13, 2019
1 parent 3a4f667 commit 7cad53e
Show file tree
Hide file tree
Showing 20 changed files with 495 additions and 165 deletions.
17 changes: 13 additions & 4 deletions Doc/library/hashlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ Constructors for hash algorithms that are always present in this module are
:func:`sha1`, :func:`sha224`, :func:`sha256`, :func:`sha384`,
:func:`sha512`, :func:`blake2b`, and :func:`blake2s`.
:func:`md5` is normally available as well, though it
may be missing if you are using a rare "FIPS compliant" build of Python.
may be missing or blocked if you are using a rare "FIPS compliant" build of Python.
Additional algorithms may also be available depending upon the OpenSSL
library that Python uses on your platform. On most platforms the
:func:`sha3_224`, :func:`sha3_256`, :func:`sha3_384`, :func:`sha3_512`,
Expand All @@ -80,6 +80,13 @@ library that Python uses on your platform. On most platforms the
.. versionadded:: 3.6
:func:`blake2b` and :func:`blake2s` were added.

.. versionchanged:: 3.9
All hashlib constructors take a keyword-only argument *usedforsecurity*
with default value *True*. A false value allows the use of insecure and
blocked hashing algorithms in restricted environments. *False* indicates
that the hashing algorithm is not used in a security context, e.g. as a
non-cryptographic one-way compression function.

For example, to obtain the digest of the byte string ``b'Nobody inspects the
spammish repetition'``::

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

.. function:: new(name[, data])
.. function:: new(name[, data], *, usedforsecurity=True)

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

.. function:: blake2b(data=b'', *, digest_size=64, key=b'', salt=b'', \
person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \
node_depth=0, inner_size=0, last_node=False)
node_depth=0, inner_size=0, last_node=False, \
usedforsecurity=True)

.. function:: blake2s(data=b'', *, digest_size=32, key=b'', salt=b'', \
person=b'', fanout=1, depth=1, leaf_size=0, node_offset=0, \
node_depth=0, inner_size=0, last_node=False)
node_depth=0, inner_size=0, last_node=False, \
usedforsecurity=True)


These functions return the corresponding hash objects for calculating
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_hashlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ def test_algorithms_available(self):
self.assertTrue(set(hashlib.algorithms_guaranteed).
issubset(hashlib.algorithms_available))

def test_usedforsecurity(self):
for cons in self.hash_constructors:
cons(usedforsecurity=True)
cons(usedforsecurity=False)
cons(b'', usedforsecurity=True)
cons(b'', usedforsecurity=False)
hashlib.new("sha256", usedforsecurity=True)
hashlib.new("sha256", usedforsecurity=False)

def test_unknown_hash(self):
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
self.assertRaises(TypeError, hashlib.new, 1)
Expand Down
7 changes: 5 additions & 2 deletions Lib/uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -772,8 +772,11 @@ def uuid1(node=None, clock_seq=None):
def uuid3(namespace, name):
"""Generate a UUID from the MD5 hash of a namespace UUID and a name."""
from hashlib import md5
hash = md5(namespace.bytes + bytes(name, "utf-8")).digest()
return UUID(bytes=hash[:16], version=3)
digest = md5(
namespace.bytes + bytes(name, "utf-8"),
usedforsecurity=False
).digest()
return UUID(bytes=digest[:16], version=3)

def uuid4():
"""Generate a random UUID."""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
hashlib constructors now support usedforsecurity flag to signal that a
hashing algorithm is not used in a security context.
5 changes: 3 additions & 2 deletions Modules/_blake2/blake2b_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ _blake2.blake2b.__new__ as py_blake2b_new
node_depth: int = 0
inner_size: int = 0
last_node: bool = False
usedforsecurity: bool = True
Return a new BLAKE2b hash object.
[clinic start generated code]*/
Expand All @@ -90,8 +91,8 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
int fanout, int depth, unsigned long leaf_size,
unsigned long long node_offset, int node_depth,
int inner_size, int last_node)
/*[clinic end generated code: output=65e732c66c2297a0 input=82be35a4e6a9daa2]*/
int inner_size, int last_node, int usedforsecurity)
/*[clinic end generated code: output=32bfd8f043c6896f input=b947312abff46977]*/
{
BLAKE2bObject *self = NULL;
Py_buffer buf;
Expand Down
5 changes: 3 additions & 2 deletions Modules/_blake2/blake2s_impl.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ _blake2.blake2s.__new__ as py_blake2s_new
node_depth: int = 0
inner_size: int = 0
last_node: bool = False
usedforsecurity: bool = True
Return a new BLAKE2s hash object.
[clinic start generated code]*/
Expand All @@ -90,8 +91,8 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
int fanout, int depth, unsigned long leaf_size,
unsigned long long node_offset, int node_depth,
int inner_size, int last_node)
/*[clinic end generated code: output=b95806be0514dcf7 input=641c0509debf714d]*/
int inner_size, int last_node, int usedforsecurity)
/*[clinic end generated code: output=556181f73905c686 input=4dda87723f23abb0]*/
{
BLAKE2sObject *self = NULL;
Py_buffer buf;
Expand Down
27 changes: 19 additions & 8 deletions Modules/_blake2/clinic/blake2b_impl.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ preserve
PyDoc_STRVAR(py_blake2b_new__doc__,
"blake2b(data=b\'\', /, *, digest_size=_blake2.blake2b.MAX_DIGEST_SIZE,\n"
" key=b\'\', salt=b\'\', person=b\'\', fanout=1, depth=1, leaf_size=0,\n"
" node_offset=0, node_depth=0, inner_size=0, last_node=False)\n"
" node_offset=0, node_depth=0, inner_size=0, last_node=False,\n"
" usedforsecurity=True)\n"
"--\n"
"\n"
"Return a new BLAKE2b hash object.");
Expand All @@ -15,15 +16,15 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
int fanout, int depth, unsigned long leaf_size,
unsigned long long node_offset, int node_depth,
int inner_size, int last_node);
int inner_size, int last_node, int usedforsecurity);

static PyObject *
py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", NULL};
static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", "usedforsecurity", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "blake2b", 0};
PyObject *argsbuf[12];
PyObject *argsbuf[13];
PyObject * const *fastargs;
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0;
Expand All @@ -39,6 +40,7 @@ py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
int node_depth = 0;
int inner_size = 0;
int last_node = 0;
int usedforsecurity = 1;

fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf);
if (!fastargs) {
Expand Down Expand Up @@ -175,12 +177,21 @@ py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto skip_optional_kwonly;
}
}
last_node = PyObject_IsTrue(fastargs[11]);
if (last_node < 0) {
if (fastargs[11]) {
last_node = PyObject_IsTrue(fastargs[11]);
if (last_node < 0) {
goto exit;
}
if (!--noptargs) {
goto skip_optional_kwonly;
}
}
usedforsecurity = PyObject_IsTrue(fastargs[12]);
if (usedforsecurity < 0) {
goto exit;
}
skip_optional_kwonly:
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);
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);

exit:
/* Cleanup for key */
Expand Down Expand Up @@ -261,4 +272,4 @@ _blake2_blake2b_hexdigest(BLAKE2bObject *self, PyObject *Py_UNUSED(ignored))
{
return _blake2_blake2b_hexdigest_impl(self);
}
/*[clinic end generated code: output=cbb625d7f60c288c input=a9049054013a1b77]*/
/*[clinic end generated code: output=2d6d0fe9aa42a42a input=a9049054013a1b77]*/
27 changes: 19 additions & 8 deletions Modules/_blake2/clinic/blake2s_impl.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ preserve
PyDoc_STRVAR(py_blake2s_new__doc__,
"blake2s(data=b\'\', /, *, digest_size=_blake2.blake2s.MAX_DIGEST_SIZE,\n"
" key=b\'\', salt=b\'\', person=b\'\', fanout=1, depth=1, leaf_size=0,\n"
" node_offset=0, node_depth=0, inner_size=0, last_node=False)\n"
" node_offset=0, node_depth=0, inner_size=0, last_node=False,\n"
" usedforsecurity=True)\n"
"--\n"
"\n"
"Return a new BLAKE2s hash object.");
Expand All @@ -15,15 +16,15 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data, int digest_size,
Py_buffer *key, Py_buffer *salt, Py_buffer *person,
int fanout, int depth, unsigned long leaf_size,
unsigned long long node_offset, int node_depth,
int inner_size, int last_node);
int inner_size, int last_node, int usedforsecurity);

static PyObject *
py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", NULL};
static const char * const _keywords[] = {"", "digest_size", "key", "salt", "person", "fanout", "depth", "leaf_size", "node_offset", "node_depth", "inner_size", "last_node", "usedforsecurity", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "blake2s", 0};
PyObject *argsbuf[12];
PyObject *argsbuf[13];
PyObject * const *fastargs;
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0;
Expand All @@ -39,6 +40,7 @@ py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
int node_depth = 0;
int inner_size = 0;
int last_node = 0;
int usedforsecurity = 1;

fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 1, 0, argsbuf);
if (!fastargs) {
Expand Down Expand Up @@ -175,12 +177,21 @@ py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto skip_optional_kwonly;
}
}
last_node = PyObject_IsTrue(fastargs[11]);
if (last_node < 0) {
if (fastargs[11]) {
last_node = PyObject_IsTrue(fastargs[11]);
if (last_node < 0) {
goto exit;
}
if (!--noptargs) {
goto skip_optional_kwonly;
}
}
usedforsecurity = PyObject_IsTrue(fastargs[12]);
if (usedforsecurity < 0) {
goto exit;
}
skip_optional_kwonly:
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);
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);

exit:
/* Cleanup for key */
Expand Down Expand Up @@ -261,4 +272,4 @@ _blake2_blake2s_hexdigest(BLAKE2sObject *self, PyObject *Py_UNUSED(ignored))
{
return _blake2_blake2s_hexdigest_impl(self);
}
/*[clinic end generated code: output=39af5a74c8805b36 input=a9049054013a1b77]*/
/*[clinic end generated code: output=c80d8d06ce40a192 input=a9049054013a1b77]*/
Loading

0 comments on commit 7cad53e

Please sign in to comment.