summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBenjamin Peterson <benjamin@python.org>2014-08-28 13:41:29 (GMT)
committerBenjamin Peterson <benjamin@python.org>2014-08-28 13:41:29 (GMT)
commit0062d1e7f42b3036aa02701c723ff63d1d3d6028 (patch)
treed646a256590212ddb56342a6021a61d7c356a472
parent876473eba3e7fab7d91446b66bd58c43d8c96647 (diff)
downloadcpython-0062d1e7f42b3036aa02701c723ff63d1d3d6028.zip
cpython-0062d1e7f42b3036aa02701c723ff63d1d3d6028.tar.gz
cpython-0062d1e7f42b3036aa02701c723ff63d1d3d6028.tar.bz2
PEP 466: backport hashlib algorithm constants (closes #21307)
-rw-r--r--Doc/library/hashlib.rst18
-rw-r--r--Lib/hashlib.py14
-rw-r--r--Lib/test/test_hashlib.py9
-rw-r--r--Misc/NEWS3
-rw-r--r--Modules/_hashopenssl.c66
5 files changed, 106 insertions, 4 deletions
diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst
index e3b5ebb..e33ef50 100644
--- a/Doc/library/hashlib.rst
+++ b/Doc/library/hashlib.rst
@@ -88,6 +88,24 @@ This module provides the following constant attribute:
.. versionadded:: 2.7
+.. data:: algorithms_guaranteed
+
+ A set containing the names of the hash algorithms guaranteed to be supported
+ by this module on all platforms.
+
+ .. versionadded:: 2.7.9
+
+.. data:: algorithms_available
+
+ A set containing the names of the hash algorithms that are available in the
+ running Python interpreter. These names will be recognized when passed to
+ :func:`new`. :attr:`algorithms_guaranteed` will always be a subset. The
+ same algorithm may appear multiple times in this set under different names
+ (thanks to OpenSSL).
+
+ .. versionadded:: 2.7.9
+
+
The following values are provided as constant attributes of the hash objects
returned by the constructors:
diff --git a/Lib/hashlib.py b/Lib/hashlib.py
index 6d69ad2..4a411bc 100644
--- a/Lib/hashlib.py
+++ b/Lib/hashlib.py
@@ -15,8 +15,9 @@ than using new():
md5(), sha1(), sha224(), sha256(), sha384(), and sha512()
-More algorithms may be available on your platform but the above are
-guaranteed to exist.
+More algorithms may be available on your platform but the above are guaranteed
+to exist. See the algorithms_guaranteed and algorithms_available attributes
+to find out what algorithm names can be passed to new().
NOTE: If you want the adler32 or crc32 hash functions they are available in
the zlib module.
@@ -58,9 +59,14 @@ More condensed:
# always available algorithm is added.
__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
+algorithms_guaranteed = set(__always_supported)
+algorithms_available = set(__always_supported)
+
algorithms = __always_supported
-__all__ = __always_supported + ('new', 'algorithms', 'pbkdf2_hmac')
+__all__ = __always_supported + ('new', 'algorithms_guaranteed',
+ 'algorithms_available', 'algorithms',
+ 'pbkdf2_hmac')
def __get_builtin_constructor(name):
@@ -128,6 +134,8 @@ try:
import _hashlib
new = __hash_new
__get_hash = __get_openssl_constructor
+ algorithms_available = algorithms_available.union(
+ _hashlib.openssl_md_meth_names)
except ImportError:
new = __py_new
__get_hash = __get_builtin_constructor
diff --git a/Lib/test/test_hashlib.py b/Lib/test/test_hashlib.py
index 3fc172f..76be461 100644
--- a/Lib/test/test_hashlib.py
+++ b/Lib/test/test_hashlib.py
@@ -109,6 +109,15 @@ class HashLibTestCase(unittest.TestCase):
tuple([_algo for _algo in self.supported_hash_names if
_algo.islower()]))
+ def test_algorithms_guaranteed(self):
+ self.assertEqual(hashlib.algorithms_guaranteed,
+ set(_algo for _algo in self.supported_hash_names
+ if _algo.islower()))
+
+ def test_algorithms_available(self):
+ self.assertTrue(set(hashlib.algorithms_guaranteed).
+ issubset(hashlib.algorithms_available))
+
def test_unknown_hash(self):
self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
self.assertRaises(TypeError, hashlib.new, 1)
diff --git a/Misc/NEWS b/Misc/NEWS
index 2ad993b..88af5a4 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -19,6 +19,9 @@ Core and Builtins
Library
-------
+- Issue #21307: As part of PEP 466, backport hashlib.algorithms_guaranteed and
+ hashlib.algorithms_available.
+
- Issue #22259: Fix segfault when attempting to fopen a file descriptor
corresponding to a directory.
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index 7116eeb..df53e96 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -712,6 +712,61 @@ pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict)
#endif
+/* State for our callback function so that it can accumulate a result. */
+typedef struct _internal_name_mapper_state {
+ PyObject *set;
+ int error;
+} _InternalNameMapperState;
+
+
+/* A callback function to pass to OpenSSL's OBJ_NAME_do_all(...) */
+static void
+_openssl_hash_name_mapper(const OBJ_NAME *openssl_obj_name, void *arg)
+{
+ _InternalNameMapperState *state = (_InternalNameMapperState *)arg;
+ PyObject *py_name;
+
+ assert(state != NULL);
+ if (openssl_obj_name == NULL)
+ return;
+ /* Ignore aliased names, they pollute the list and OpenSSL appears to
+ * have a its own definition of alias as the resulting list still
+ * contains duplicate and alternate names for several algorithms. */
+ if (openssl_obj_name->alias)
+ return;
+
+ py_name = PyString_FromString(openssl_obj_name->name);
+ if (py_name == NULL) {
+ state->error = 1;
+ } else {
+ if (PySet_Add(state->set, py_name) != 0) {
+ state->error = 1;
+ }
+ Py_DECREF(py_name);
+ }
+}
+
+
+/* Ask OpenSSL for a list of supported ciphers, filling in a Python set. */
+static PyObject*
+generate_hash_name_list(void)
+{
+ _InternalNameMapperState state;
+ state.set = PyFrozenSet_New(NULL);
+ if (state.set == NULL)
+ return NULL;
+ state.error = 0;
+
+ OBJ_NAME_do_all(OBJ_NAME_TYPE_MD_METH, &_openssl_hash_name_mapper, &state);
+
+ if (state.error) {
+ Py_DECREF(state.set);
+ return NULL;
+ }
+ return state.set;
+}
+
+
/*
* This macro generates constructor function definitions for specific
* hash algorithms. These constructors are much faster than calling
@@ -792,9 +847,10 @@ static struct PyMethodDef EVP_functions[] = {
PyMODINIT_FUNC
init_hashlib(void)
{
- PyObject *m;
+ PyObject *m, *openssl_md_meth_names;
OpenSSL_add_all_digests();
+ ERR_load_crypto_strings();
/* TODO build EVP_functions openssl_* entries dynamically based
* on what hashes are supported rather than listing many
@@ -809,6 +865,14 @@ init_hashlib(void)
if (m == NULL)
return;
+ openssl_md_meth_names = generate_hash_name_list();
+ if (openssl_md_meth_names == NULL) {
+ return;
+ }
+ if (PyModule_AddObject(m, "openssl_md_meth_names", openssl_md_meth_names)) {
+ return;
+ }
+
#if HASH_OBJ_CONSTRUCTOR
Py_INCREF(&EVPtype);
PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype);