summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Heimes <christian@cheimes.de>2013-10-19 12:24:44 (GMT)
committerChristian Heimes <christian@cheimes.de>2013-10-19 12:24:44 (GMT)
commite723622775172a2516f43721d998aae95f32e59d (patch)
treed8c027abf13a9145ad6636e9cef34dfa4a5a4151
parentfcd8de2d86f15797e6952551b5baa0beb7a7c668 (diff)
downloadcpython-e723622775172a2516f43721d998aae95f32e59d.zip
cpython-e723622775172a2516f43721d998aae95f32e59d.tar.gz
cpython-e723622775172a2516f43721d998aae95f32e59d.tar.bz2
Issue #18582: provide a faster C implementation of pbkdf2_hmac that works with OpenSSL < 1.0
-rw-r--r--Doc/library/hashlib.rst8
-rw-r--r--Modules/_hashopenssl.c100
2 files changed, 97 insertions, 11 deletions
diff --git a/Doc/library/hashlib.rst b/Doc/library/hashlib.rst
index 677d530..b1daba1 100644
--- a/Doc/library/hashlib.rst
+++ b/Doc/library/hashlib.rst
@@ -212,11 +212,9 @@ slow and include a salt.
.. versionadded:: 3.4
- .. note:: A fast implementation of *pbkdf2_hmac* is only available with
- OpenSSL 1.0 and newer. The Python implementation uses an inline
- version of :mod:`hmac` and is about three times slower. Contrary to
- OpenSSL's current code the length of the password has only a minimal
- impact on the runtime of the Python implementation.
+ .. note:: A fast implementation of *pbkdf2_hmac* is available with OpenSSL.
+ The Python implementation uses an inline version of :mod:`hmac`. It is
+ about three times slower and doesn't release the GIL.
.. seealso::
diff --git a/Modules/_hashopenssl.c b/Modules/_hashopenssl.c
index bec84ce..9dba5c7 100644
--- a/Modules/_hashopenssl.c
+++ b/Modules/_hashopenssl.c
@@ -20,6 +20,7 @@
/* 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"
@@ -495,10 +496,97 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
return ret_obj;
}
-#if (OPENSSL_VERSION_NUMBER >= 0x10000000 && !defined(OPENSSL_NO_HMAC) \
- && !defined(OPENSSL_NO_SHA))
+
+
+#if (!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.
+ */
+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 (!pass)
+ passlen = 0;
+ else if(passlen == -1)
+ passlen = strlen(pass);
+ 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;
+ }
+ 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;
+}
+
+
PyDoc_STRVAR(pbkdf2_hmac__doc__,
"pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None) -> key\n\
\n\
@@ -579,10 +667,10 @@ pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict)
key = PyBytes_AS_STRING(key_obj);
Py_BEGIN_ALLOW_THREADS
- retval = PKCS5_PBKDF2_HMAC((char*)password.buf, password.len,
- (unsigned char *)salt.buf, salt.len,
- iterations, digest, dklen,
- (unsigned char *)key);
+ retval = PKCS5_PBKDF2_HMAC_fast((char*)password.buf, password.len,
+ (unsigned char *)salt.buf, salt.len,
+ iterations, digest, dklen,
+ (unsigned char *)key);
Py_END_ALLOW_THREADS
if (!retval) {