mirror of
https://github.com/python/cpython
synced 2026-09-29 12:10:30 +03:00
Issue #18582: provide a faster C implementation of pbkdf2_hmac that works with OpenSSL < 1.0
This commit is contained in:
parent
fcd8de2d86
commit
e723622775
2 changed files with 97 additions and 11 deletions
|
|
@ -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::
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue