diff options
-rw-r--r-- | Doc/library/hashlib.rst | 29 | ||||
-rw-r--r-- | Lib/hashlib.py | 17 | ||||
-rw-r--r-- | Lib/test/test_hashlib.py | 8 | ||||
-rw-r--r-- | Misc/NEWS | 5 | ||||
-rw-r--r-- | Modules/_hashopenssl.c | 70 |
5 files changed, 111 insertions, 18 deletions
diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst index 6fa01be..fa95821 100644 --- a/Doc/library/hashlib.rst +++ b/Doc/library/hashlib.rst @@ -70,10 +70,13 @@ More condensed: >>> hashlib.sha224(b"Nobody inspects the spammish repetition").hexdigest() 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2' -A generic :func:`new` constructor that takes the string name of the desired -algorithm as its first parameter also exists to allow access to the above listed -hashes as well as any other algorithms that your OpenSSL library may offer. The -named constructors are much faster than :func:`new` and should be preferred. +.. function:: new(name[, data]) + + 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 + above listed hashes as well as any other algorithms that your OpenSSL + library may offer. The named constructors are much faster than :func:`new` + and should be preferred. Using :func:`new` with an algorithm provided by OpenSSL: @@ -82,12 +85,22 @@ Using :func:`new` with an algorithm provided by OpenSSL: >>> h.hexdigest() 'cc4a5ce1b3df48aec5d22d1f16b894a0b894eccc' -This module provides the following constant attribute: +Hashlib provides the following constant attributes: + +.. data:: algorithms_guaranteed + + Contains the names of the hash algorithms guaranteed to be supported + by this module on all platforms. + + .. versionadded:: 3.2 -.. data:: hashlib.algorithms +.. data:: algorithms_available - A tuple providing the names of the hash algorithms guaranteed to be - supported by this module. + Contains 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. Duplicate algorithms with different + name formats may appear in this set (thanks to OpenSSL). .. versionadded:: 3.2 diff --git a/Lib/hashlib.py b/Lib/hashlib.py index aacb268..9d860c4 100644 --- a/Lib/hashlib.py +++ b/Lib/hashlib.py @@ -1,6 +1,4 @@ -# $Id$ -# -# Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org) +# Copyright (C) 2005-2010 Gregory P. Smith (greg@krypto.org) # Licensed to PSF under a Contributor Agreement. # @@ -15,8 +13,9 @@ than using new(name): 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. @@ -57,9 +56,11 @@ More condensed: # always available algorithm is added. __always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') -algorithms = __always_supported +algorithms_guaranteed = __always_supported +algorithms_available = frozenset(__always_supported) -__all__ = __always_supported + ('new', 'algorithms') +__all__ = __always_supported + ('new', 'algorithms_guaranteed', + 'algorithms_available') def __get_builtin_constructor(name): @@ -124,6 +125,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 4fb693e..c8db6a8 100644 --- a/Lib/test/test_hashlib.py +++ b/Lib/test/test_hashlib.py @@ -101,11 +101,15 @@ class HashLibTestCase(unittest.TestCase): c = cons(a) c.hexdigest() - def test_algorithms_attribute(self): - self.assertEqual(hashlib.algorithms, + def test_algorithms_guaranteed(self): + self.assertEqual(hashlib.algorithms_guaranteed, tuple(_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): try: hashlib.new('spam spam spam spam spam') @@ -17,6 +17,11 @@ Library unsupported operation is attempted (for example, writing to a file open only for reading). +- hashlib has two new constant attributes: algorithms_guaranteed and + algorithms_avaiable that respectively list the names of hash algorithms + guaranteed to exist in all Python implementations and the names of hash + algorithms available in the current process. + What's New in Python 3.2 Alpha 2? ================================= diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 9f550a6..dd4317f 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -38,6 +38,8 @@ /* EVP is the preferred interface to hashing in OpenSSL */ #include <openssl/evp.h> +/* We use the object interface to discover what hashes OpenSSL supports. */ +#include <openssl/objects.h> #define MUNCH_SIZE INT_MAX @@ -488,6 +490,62 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) return ret_obj; } + +/* 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 = PyUnicode_FromString(openssl_obj_name->name); + if (py_name == NULL) { + state->error = 1; + } else { + if (PySet_Add(state->set, py_name) != 0) { + Py_DECREF(py_name); + state->error = 1; + } + } +} + + +/* 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 @@ -581,7 +639,7 @@ static struct PyModuleDef _hashlibmodule = { PyMODINIT_FUNC PyInit__hashlib(void) { - PyObject *m; + PyObject *m, *openssl_md_meth_names; OpenSSL_add_all_digests(); @@ -598,6 +656,16 @@ PyInit__hashlib(void) if (m == NULL) return NULL; + openssl_md_meth_names = generate_hash_name_list(); + if (openssl_md_meth_names == NULL) { + Py_DECREF(m); + return NULL; + } + if (PyModule_AddObject(m, "openssl_md_meth_names", openssl_md_meth_names)) { + Py_DECREF(m); + return NULL; + } + #if HASH_OBJ_CONSTRUCTOR Py_INCREF(&EVPtype); PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); |