Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e723622

Browse files
committed
Issue #18582: provide a faster C implementation of pbkdf2_hmac that works with OpenSSL < 1.0
1 parent fcd8de2 commit e723622

2 files changed

Lines changed: 97 additions & 11 deletions

File tree

Doc/library/hashlib.rst

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,9 @@ slow and include a salt.
212212

213213
.. versionadded:: 3.4
214214

215-
.. note:: A fast implementation of *pbkdf2_hmac* is only available with
216-
OpenSSL 1.0 and newer. The Python implementation uses an inline
217-
version of :mod:`hmac` and is about three times slower. Contrary to
218-
OpenSSL's current code the length of the password has only a minimal
219-
impact on the runtime of the Python implementation.
215+
.. note:: A fast implementation of *pbkdf2_hmac* is available with OpenSSL.
216+
The Python implementation uses an inline version of :mod:`hmac`. It is
217+
about three times slower and doesn't release the GIL.
220218

221219

222220
.. seealso::

Modules/_hashopenssl.c

Lines changed: 94 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
/* EVP is the preferred interface to hashing in OpenSSL */
2222
#include <openssl/evp.h>
23+
#include <openssl/hmac.h>
2324
/* We use the object interface to discover what hashes OpenSSL supports. */
2425
#include <openssl/objects.h>
2526
#include "openssl/err.h"
@@ -495,10 +496,97 @@ EVP_new(PyObject *self, PyObject *args, PyObject *kwdict)
495496
return ret_obj;
496497
}
497498

498-
#if (OPENSSL_VERSION_NUMBER >= 0x10000000 && !defined(OPENSSL_NO_HMAC) \
499-
&& !defined(OPENSSL_NO_SHA))
499+
500+
501+
#if (!defined(OPENSSL_NO_HMAC) && !defined(OPENSSL_NO_SHA))
502+
500503
#define PY_PBKDF2_HMAC 1
501504

505+
/* Improved implementation of PKCS5_PBKDF2_HMAC()
506+
*
507+
* PKCS5_PBKDF2_HMAC_fast() hashes the password exactly one time instead of
508+
* `iter` times. Today (2013) the iteration count is typically 100,000 or
509+
* more. The improved algorithm is not subject to a Denial-of-Service
510+
* vulnerability with overly large passwords.
511+
*
512+
* Also OpenSSL < 1.0 don't provide PKCS5_PBKDF2_HMAC(), only
513+
* PKCS5_PBKDF2_SHA1.
514+
*/
515+
int PKCS5_PBKDF2_HMAC_fast(const char *pass, int passlen,
516+
const unsigned char *salt, int saltlen,
517+
int iter, const EVP_MD *digest,
518+
int keylen, unsigned char *out)
519+
{
520+
unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4];
521+
int cplen, j, k, tkeylen, mdlen;
522+
unsigned long i = 1;
523+
HMAC_CTX hctx_tpl, hctx;
524+
525+
mdlen = EVP_MD_size(digest);
526+
if (mdlen < 0)
527+
return 0;
528+
529+
HMAC_CTX_init(&hctx_tpl);
530+
HMAC_CTX_init(&hctx);
531+
p = out;
532+
tkeylen = keylen;
533+
if (!pass)
534+
passlen = 0;
535+
else if(passlen == -1)
536+
passlen = strlen(pass);
537+
if (!HMAC_Init_ex(&hctx_tpl, pass, passlen, digest, NULL)) {
538+
HMAC_CTX_cleanup(&hctx_tpl);
539+
return 0;
540+
}
541+
while(tkeylen) {
542+
if(tkeylen > mdlen)
543+
cplen = mdlen;
544+
else
545+
cplen = tkeylen;
546+
/* We are unlikely to ever use more than 256 blocks (5120 bits!)
547+
* but just in case...
548+
*/
549+
itmp[0] = (unsigned char)((i >> 24) & 0xff);
550+
itmp[1] = (unsigned char)((i >> 16) & 0xff);
551+
itmp[2] = (unsigned char)((i >> 8) & 0xff);
552+
itmp[3] = (unsigned char)(i & 0xff);
553+
if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) {
554+
HMAC_CTX_cleanup(&hctx_tpl);
555+
return 0;
556+
}
557+
if (!HMAC_Update(&hctx, salt, saltlen)
558+
|| !HMAC_Update(&hctx, itmp, 4)
559+
|| !HMAC_Final(&hctx, digtmp, NULL)) {
560+
HMAC_CTX_cleanup(&hctx_tpl);
561+
HMAC_CTX_cleanup(&hctx);
562+
return 0;
563+
}
564+
memcpy(p, digtmp, cplen);
565+
for (j = 1; j < iter; j++) {
566+
if (!HMAC_CTX_copy(&hctx, &hctx_tpl)) {
567+
HMAC_CTX_cleanup(&hctx_tpl);
568+
return 0;
569+
}
570+
if (!HMAC_Update(&hctx, digtmp, mdlen)
571+
|| !HMAC_Final(&hctx, digtmp, NULL)) {
572+
HMAC_CTX_cleanup(&hctx_tpl);
573+
HMAC_CTX_cleanup(&hctx);
574+
return 0;
575+
}
576+
HMAC_CTX_cleanup(&hctx);
577+
for (k = 0; k < cplen; k++) {
578+
p[k] ^= digtmp[k];
579+
}
580+
}
581+
tkeylen-= cplen;
582+
i++;
583+
p+= cplen;
584+
}
585+
HMAC_CTX_cleanup(&hctx_tpl);
586+
return 1;
587+
}
588+
589+
502590
PyDoc_STRVAR(pbkdf2_hmac__doc__,
503591
"pbkdf2_hmac(hash_name, password, salt, iterations, dklen=None) -> key\n\
504592
\n\
@@ -579,10 +667,10 @@ pbkdf2_hmac(PyObject *self, PyObject *args, PyObject *kwdict)
579667
key = PyBytes_AS_STRING(key_obj);
580668

581669
Py_BEGIN_ALLOW_THREADS
582-
retval = PKCS5_PBKDF2_HMAC((char*)password.buf, password.len,
583-
(unsigned char *)salt.buf, salt.len,
584-
iterations, digest, dklen,
585-
(unsigned char *)key);
670+
retval = PKCS5_PBKDF2_HMAC_fast((char*)password.buf, password.len,
671+
(unsigned char *)salt.buf, salt.len,
672+
iterations, digest, dklen,
673+
(unsigned char *)key);
586674
Py_END_ALLOW_THREADS
587675

588676
if (!retval) {

0 commit comments

Comments
 (0)