Blame SOURCES/00217-pep466-backport-hashlib-algorithm-consts.patch

925e6b
925e6b
# HG changeset patch
925e6b
# User Benjamin Peterson <benjamin@python.org>
925e6b
# Date 1409233289 14400
925e6b
# Node ID 3f73c44b1fd1d442d6841493328e9756fb5e7ef5
925e6b
# Parent  97081a80f487841d81aeed55d398a1dba1faca00
925e6b
PEP 466: backport hashlib algorithm constants (closes #21307)
925e6b
925e6b
diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst
925e6b
--- a/Doc/library/hashlib.rst
925e6b
+++ b/Doc/library/hashlib.rst
925e6b
@@ -88,6 +88,24 @@ This module provides the following const
925e6b
 
925e6b
    .. versionadded:: 2.7
925e6b
 
925e6b
+.. data:: algorithms_guaranteed
925e6b
+
925e6b
+   A set containing the names of the hash algorithms guaranteed to be supported
925e6b
+   by this module on all platforms.
925e6b
+
925e6b
+   .. versionadded:: 2.7.9
925e6b
+
925e6b
+.. data:: algorithms_available
925e6b
+
925e6b
+   A set containing the names of the hash algorithms that are available in the
925e6b
+   running Python interpreter.  These names will be recognized when passed to
925e6b
+   :func:`new`.  :attr:`algorithms_guaranteed` will always be a subset.  The
925e6b
+   same algorithm may appear multiple times in this set under different names
925e6b
+   (thanks to OpenSSL).
925e6b
+
925e6b
+   .. versionadded:: 2.7.9
925e6b
+
925e6b
+
925e6b
 The following values are provided as constant attributes of the hash objects
925e6b
 returned by the constructors:
925e6b
 
925e6b
diff -up Python-2.7.5/Lib/hashlib.py.hash Python-2.7.5/Lib/hashlib.py
925e6b
--- Python-2.7.5/Lib/hashlib.py.hash	2015-03-04 17:05:57.496598686 +0100
925e6b
+++ Python-2.7.5/Lib/hashlib.py	2015-03-04 17:11:34.872739103 +0100
925e6b
@@ -18,8 +18,9 @@ than using new():
925e6b
 
925e6b
 md5(), sha1(), sha224(), sha256(), sha384(), and sha512()
925e6b
 
925e6b
-More algorithms may be available on your platform but the above are
925e6b
-guaranteed to exist.
925e6b
+More algorithms may be available on your platform but the above are guaranteed
925e6b
+to exist.  See the algorithms_guaranteed and algorithms_available attributes
925e6b
+to find out what algorithm names can be passed to new().
925e6b
 
925e6b
 NOTE: If you want the adler32 or crc32 hash functions they are available in
925e6b
 the zlib module.
925e6b
@@ -75,9 +76,14 @@ More condensed:
925e6b
 # always available algorithm is added.
925e6b
 __always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512')
925e6b
 
925e6b
+algorithms_guaranteed = set(__always_supported)
925e6b
+algorithms_available = set(__always_supported)
925e6b
+
925e6b
 algorithms = __always_supported
925e6b
 
925e6b
-__all__ = __always_supported + ('new', 'algorithms', 'pbkdf2_hmac')
925e6b
+__all__ = __always_supported + ('new', 'algorithms_guaranteed',
925e6b
+                                'algorithms_available', 'algorithms',
925e6b
+                                'pbkdf2_hmac')
925e6b
 
925e6b
 
925e6b
 def __get_openssl_constructor(name):
925e6b
@@ -110,6 +116,8 @@ try:
925e6b
     import _hashlib
925e6b
     new = __hash_new
925e6b
     __get_hash = __get_openssl_constructor
925e6b
+    algorithms_available = algorithms_available.union(
925e6b
+        _hashlib.openssl_md_meth_names)
925e6b
 except ImportError:
925e6b
     # We don't build the legacy modules
925e6b
     raise
925e6b
diff -up Python-2.7.5/Modules/_hashopenssl.c.hash Python-2.7.5/Modules/_hashopenssl.c
925e6b
--- Python-2.7.5/Modules/_hashopenssl.c.hash	2015-03-04 17:06:18.246791837 +0100
925e6b
+++ Python-2.7.5/Modules/_hashopenssl.c	2015-03-04 17:16:17.696369000 +0100
925e6b
@@ -784,6 +784,61 @@ pbkdf2_hmac(PyObject *self, PyObject *ar
925e6b
 
925e6b
 #endif
925e6b
 
925e6b
+/* State for our callback function so that it can accumulate a result. */
925e6b
+typedef struct _internal_name_mapper_state {
925e6b
+    PyObject *set;
925e6b
+    int error;
925e6b
+} _InternalNameMapperState;
925e6b
+
925e6b
+
925e6b
+/* A callback function to pass to OpenSSL's OBJ_NAME_do_all(...) */
925e6b
+static void
925e6b
+_openssl_hash_name_mapper(const OBJ_NAME *openssl_obj_name, void *arg)
925e6b
+{
925e6b
+    _InternalNameMapperState *state = (_InternalNameMapperState *)arg;
925e6b
+    PyObject *py_name;
925e6b
+
925e6b
+    assert(state != NULL);
925e6b
+    if (openssl_obj_name == NULL)
925e6b
+        return;
925e6b
+    /* Ignore aliased names, they pollute the list and OpenSSL appears to
925e6b
+     * have a its own definition of alias as the resulting list still
925e6b
+     * contains duplicate and alternate names for several algorithms.     */
925e6b
+    if (openssl_obj_name->alias)
925e6b
+        return;
925e6b
+
925e6b
+    py_name = PyString_FromString(openssl_obj_name->name);
925e6b
+    if (py_name == NULL) {
925e6b
+        state->error = 1;
925e6b
+    } else {
925e6b
+        if (PySet_Add(state->set, py_name) != 0) {
925e6b
+            state->error = 1;
925e6b
+        }
925e6b
+        Py_DECREF(py_name);
925e6b
+    }
925e6b
+}
925e6b
+
925e6b
+
925e6b
+/* Ask OpenSSL for a list of supported ciphers, filling in a Python set. */
925e6b
+static PyObject*
925e6b
+generate_hash_name_list(void)
925e6b
+{
925e6b
+    _InternalNameMapperState state;
925e6b
+    state.set = PyFrozenSet_New(NULL);
925e6b
+    if (state.set == NULL)
925e6b
+        return NULL;
925e6b
+    state.error = 0;
925e6b
+
925e6b
+    OBJ_NAME_do_all(OBJ_NAME_TYPE_MD_METH, &_openssl_hash_name_mapper, &state);
925e6b
+
925e6b
+    if (state.error) {
925e6b
+        Py_DECREF(state.set);
925e6b
+        return NULL;
925e6b
+    }
925e6b
+    return state.set;
925e6b
+}
925e6b
+
925e6b
+
925e6b
 /*
925e6b
  *  This macro and function generates a family of constructor function
925e6b
  *  definitions for specific hash algorithms.  These constructors are much
925e6b
@@ -924,11 +979,11 @@ static struct PyMethodDef EVP_functions[
925e6b
 PyMODINIT_FUNC
925e6b
 init_hashlib(void)
925e6b
 {
925e6b
-    PyObject *m;
925e6b
+    PyObject *m, *openssl_md_meth_names;
925e6b
 
925e6b
     SSL_load_error_strings();
925e6b
     SSL_library_init();
925e6b
-    OpenSSL_add_all_digests();
925e6b
+    ERR_load_crypto_strings();
925e6b
 
925e6b
     Py_TYPE(&EVPtype) = &PyType_Type;
925e6b
     if (PyType_Ready(&EVPtype) < 0)
925e6b
@@ -938,6 +993,14 @@ init_hashlib(void)
925e6b
     if (m == NULL)
925e6b
         return;
925e6b
 
925e6b
+    openssl_md_meth_names = generate_hash_name_list();
925e6b
+    if (openssl_md_meth_names == NULL) {
925e6b
+        return;
925e6b
+    }
925e6b
+    if (PyModule_AddObject(m, "openssl_md_meth_names", openssl_md_meth_names)) {
925e6b
+        return;
925e6b
+    }
925e6b
+
925e6b
 #if HASH_OBJ_CONSTRUCTOR
925e6b
     Py_INCREF(&EVPtype);
925e6b
     PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype);
925e6b
diff -up Python-2.7.5/Lib/test/test_hashlib.py.hash Python-2.7.5/Lib/test/test_hashlib.py
925e6b
--- Python-2.7.5/Lib/test/test_hashlib.py.hash	2015-03-04 18:04:57.823553474 +0100
925e6b
+++ Python-2.7.5/Lib/test/test_hashlib.py	2015-03-04 18:06:39.395499123 +0100
925e6b
@@ -107,6 +107,15 @@ class HashLibTestCase(unittest.TestCase)
925e6b
             tuple([_algo for _algo in self.supported_hash_names if
925e6b
                                                 _algo.islower()]))
925e6b
 
925e6b
+    def test_algorithms_guaranteed(self):
925e6b
+        self.assertEqual(hashlib.algorithms_guaranteed,
925e6b
+            set(_algo for _algo in self.supported_hash_names
925e6b
+                  if _algo.islower()))
925e6b
+
925e6b
+    def test_algorithms_available(self):
925e6b
+        self.assertTrue(set(hashlib.algorithms_guaranteed).
925e6b
+                            issubset(hashlib.algorithms_available))
925e6b
+
925e6b
     def test_unknown_hash(self):
925e6b
         self.assertRaises(ValueError, hashlib.new, 'spam spam spam spam spam')
925e6b
         self.assertRaises(TypeError, hashlib.new, 1)