summaryrefslogtreecommitdiffstats
path: root/Modules/_hashopenssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'Modules/_hashopenssl.c')
-rw-r--r--Modules/_hashopenssl.c256
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);