Issue #18582: provide a faster C implementation of pbkdf2_hmac that works with OpenSSL < 1.0
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 @@
     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 @@
     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) {