diff options
Diffstat (limited to 'Modules/_hashopenssl.c')
-rw-r--r-- | Modules/_hashopenssl.c | 256 |
1 files changed, 229 insertions, 27 deletions
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c index 3ab1c0c..5b0a7be 100644 --- a/Modules/_hashopenssl.c +++ b/Modules/_hashopenssl.c @@ -17,36 +17,16 @@ #include "structmember.h" #include "hashlib.h" -#ifdef WITH_THREAD -#include "pythread.h" - #define ENTER_HASHLIB(obj) \ - if ((obj)->lock) { \ - if (!PyThread_acquire_lock((obj)->lock, 0)) { \ - Py_BEGIN_ALLOW_THREADS \ - PyThread_acquire_lock((obj)->lock, 1); \ - Py_END_ALLOW_THREADS \ - } \ - } - #define LEAVE_HASHLIB(obj) \ - if ((obj)->lock) { \ - PyThread_release_lock((obj)->lock); \ - } -#else - #define ENTER_HASHLIB(obj) - #define LEAVE_HASHLIB(obj) -#endif /* EVP is the preferred interface to hashing in OpenSSL */ #include <openssl/evp.h> +#include <openssl/hmac.h> /* We use the object interface to discover what hashes OpenSSL supports. */ #include <openssl/objects.h> +#include "openssl/err.h" #define MUNCH_SIZE INT_MAX -/* TODO(gps): We should probably make this a module or EVPobject attribute - * to allow the user to optimize based on the platform they're using. */ -#define HASHLIB_GIL_MINSIZE 2048 - #ifndef HASH_OBJ_CONSTRUCTOR #define HASH_OBJ_CONSTRUCTOR 0 #endif @@ -489,6 +469,225 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) } + +#if (OPENSSL_VERSION_NUMBER >= 0x10000000 && !defined(OPENSSL_NO_HMAC) \ + && !defined(OPENSSL_NO_SHA)) + +#define PY_PBKDF2_HMAC 1 + +/* Improved implementation of PKCS5_PBKDF2_HMAC() + * + * PKCS5_PBKDF2_HMAC_fast() hashes the password exactly one time instead of + * `iter` times. Today (2013) the iteration count is typically 100,000 or + * more. The improved algorithm is not subject to a Denial-of-Service + * vulnerability with overly large passwords. + * + * Also OpenSSL < 1.0 don't provide PKCS5_PBKDF2_HMAC(), only + * PKCS5_PBKDF2_SHA1. + */ +static int +PKCS5_PBKDF2_HMAC_fast(const char *pass, int passlen, + const unsigned char *salt, int saltlen, + int iter, const EVP_MD *digest, + int keylen, unsigned char *out) +{ + unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4]; + int cplen, j, k, tkeylen, mdlen; + unsigned long i = 1; + HMAC_CTX hctx_tpl, hctx; + + mdlen = EVP_MD_size(digest); + if (mdlen < 0) + return 0; + + HMAC_CTX_init(&hctx_tpl); + HMAC_CTX_init(&hctx); + p = out; + tkeylen = keylen; + if (!HMAC_Init_ex(&hctx_tpl, pass, passlen, digest, NULL)) { + HMAC_CTX_cleanup(&hctx_tpl); + return 0; + } + while (tkeylen) { + if (tkeylen > mdlen) + cplen = mdlen; + else + cplen = tkeylen; + /* We are unlikely to ever use more than 256 blocks (5120 bits!) + * but just in case... + */ + itmp[0] = (unsigned char)((i >> 24) & 0xff); + itmp[1] = (unsigned char)((i >> 16) & 0xff); + itmp[2] = (unsigned char)((i >> 8) & 0xff); + itmp[3] = (unsigned char)(i & 0xff); + if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) { + HMAC_CTX_cleanup(&hctx_tpl); + return 0; + } + if (!HMAC_Update(&hctx, salt, saltlen) + || !HMAC_Update(&hctx, itmp, 4) + || !HMAC_Final(&hctx, digtmp, NULL)) { + HMAC_CTX_cleanup(&hctx_tpl); + HMAC_CTX_cleanup(&hctx); + return 0; + } + HMAC_CTX_cleanup(&hctx); + memcpy(p, digtmp, cplen); + for (j = 1; j < iter; j++) { + if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) { + HMAC_CTX_cleanup(&hctx_tpl); + return 0; + } + if (!HMAC_Update(&hctx, digtmp, mdlen) + || !HMAC_Final(&hctx, digtmp, NULL)) { + HMAC_CTX_cleanup(&hctx_tpl); + HMAC_CTX_cleanup(&hctx); + return 0; + } + HMAC_CTX_cleanup(&hctx); + for (k = 0; k < cplen; k++) { + p[k] ^= digtmp[k]; + } + } + tkeylen-= cplen; + i++; + p+= cplen; + } + HMAC_CTX_cleanup(&hctx_tpl); + return 1; +} + +/* LCOV_EXCL_START */ +static PyObject * +_setException(PyObject *exc) +{ + unsigned long errcode; + const char *lib, *func, *reason; + + errcode = ERR_peek_last_error(); + if (!errcode) { + PyErr_SetString(exc, "unknown reasons"); + return NULL; + } + ERR_clear_error(); + + lib = ERR_lib_error_string(errcode); + func = ERR_func_error_string(errcode); + reason = ERR_reason_error_string(errcode); + + if (lib && func) { + PyErr_Format(exc, "[%s: %s] %s", lib, func, reason); + } + else if (lib) { + PyErr_Format(exc, "[%s] %s", lib, reason); + } + else { + PyErr_SetString(exc, reason); + } + return NULL; +} +/* LCOV_EXCL_STOP */ + +PyDoc_STRVAR(pbkdf2_hmac__doc__, +"pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None) -> key\n\ +\n\ +Password based key derivation function 2 (PKCS #5 v2.0) with HMAC as\n\ +pseudorandom function."); + +static PyObject * +pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"hash_name", "password", "salt", "iterations", + "dklen", NULL}; + PyObject *key_obj = NULL, *dklen_obj = Py_None; + char *name, *key; + Py_buffer password, salt; + long iterations, dklen; + int retval; + const EVP_MD *digest; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "sy*y*l|O:pbkdf2_hmac", + kwlist, &name, &password, &salt, + &iterations, &dklen_obj)) { + return NULL; + } + + digest = EVP_get_digestbyname(name); + if (digest == NULL) { + PyErr_SetString(PyExc_ValueError, "unsupported hash type"); + goto end; + } + + if (password.len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "password is too long."); + goto end; + } + + if (salt.len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "salt is too long."); + goto end; + } + + if (iterations < 1) { + PyErr_SetString(PyExc_ValueError, + "iteration value must be greater than 0."); + goto end; + } + if (iterations > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "iteration value is too great."); + goto end; + } + + if (dklen_obj == Py_None) { + dklen = EVP_MD_size(digest); + } else { + dklen = PyLong_AsLong(dklen_obj); + if ((dklen == -1) && PyErr_Occurred()) { + goto end; + } + } + if (dklen < 1) { + PyErr_SetString(PyExc_ValueError, + "key length must be greater than 0."); + goto end; + } + if (dklen > INT_MAX) { + /* INT_MAX is always smaller than dkLen max (2^32 - 1) * hLen */ + PyErr_SetString(PyExc_OverflowError, + "key length is too great."); + goto end; + } + + key_obj = PyBytes_FromStringAndSize(NULL, dklen); + if (key_obj == NULL) { + goto end; + } + key = PyBytes_AS_STRING(key_obj); + + Py_BEGIN_ALLOW_THREADS + retval = PKCS5_PBKDF2_HMAC_fast((char*)password.buf, (int)password.len, + (unsigned char *)salt.buf, (int)salt.len, + iterations, digest, dklen, + (unsigned char *)key); + Py_END_ALLOW_THREADS + + if (!retval) { + Py_CLEAR(key_obj); + _setException(PyExc_ValueError); + goto end; + } + + end: + PyBuffer_Release(&password); + PyBuffer_Release(&salt); + return key_obj; +} + +#endif + /* State for our callback function so that it can accumulate a result. */ typedef struct _internal_name_mapper_state { PyObject *set; @@ -507,7 +706,7 @@ _openssl_hash_name_mapper(const OBJ_NAME *openssl_obj_name, void *arg) 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 + * have its own definition of alias as the resulting list still * contains duplicate and alternate names for several algorithms. */ if (openssl_obj_name->alias) return; @@ -517,9 +716,9 @@ _openssl_hash_name_mapper(const OBJ_NAME *openssl_obj_name, void *arg) state->error = 1; } else { if (PySet_Add(state->set, py_name) != 0) { - Py_DECREF(py_name); state->error = 1; } + Py_DECREF(py_name); } } @@ -610,6 +809,10 @@ GEN_CONSTRUCTOR(sha512) static struct PyMethodDef EVP_functions[] = { {"new", (PyCFunction)EVP_new, METH_VARARGS|METH_KEYWORDS, EVP_new__doc__}, +#ifdef PY_PBKDF2_HMAC + {"pbkdf2_hmac", (PyCFunction)pbkdf2_hmac, METH_VARARGS|METH_KEYWORDS, + pbkdf2_hmac__doc__}, +#endif CONSTRUCTOR_METH_DEF(md5), CONSTRUCTOR_METH_DEF(sha1), #ifdef _OPENSSL_SUPPORTS_SHA2 @@ -643,6 +846,7 @@ PyInit__hashlib(void) 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 @@ -667,10 +871,8 @@ PyInit__hashlib(void) return NULL; } -#if HASH_OBJ_CONSTRUCTOR - Py_INCREF(&EVPtype); + Py_INCREF((PyObject *)&EVPtype); PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); -#endif /* these constants are used by the convenience constructors */ INIT_CONSTRUCTOR_CONSTANTS(md5); |