From c4c331d6450591877c5753060ed53b802bde56f4 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Sat, 27 Jul 2013 16:27:12 +0200 Subject: [PATCH 1/2] pbkdf2 implemented ctypes wrapper around OpenSSL native implementation: 6x speedup --- gluon/contrib/pbkdf2_ctypes.py | 86 ++++++++++++++++++++++++++++++++++ gluon/utils.py | 5 +- 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 gluon/contrib/pbkdf2_ctypes.py diff --git a/gluon/contrib/pbkdf2_ctypes.py b/gluon/contrib/pbkdf2_ctypes.py new file mode 100644 index 00000000..4f55c5b9 --- /dev/null +++ b/gluon/contrib/pbkdf2_ctypes.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +""" + pbkdf2_ctypes + ~~~~~~ + + This module implements pbkdf2 for Python using crypto lib from + openssl. + + Note: This module is intended as a plugin replacement of pbkdf2.py + by Armin Ronacher. + + + :copyright: Copyright (c) 2013: Michele Comitini + :license: LGPLv3 + +""" + +import ctypes +import hashlib +import platform + + +try: # check that we have proper OpenSSL on the system. + if platform.system()=='Windows': + if platform.architecture()[0] == '64bit': + crypto = ctypes.CDLL('libeay64.dll') + else: + crypto = ctypes.CDLL('libeay32.dll') + else: # should work on most unix os'. + crypto = ctypes.CDLL('libcrypto.so') + + PKCS5_PBKDF2_HMAC = crypto.PKCS5_PBKDF2_HMAC + + hashlib_to_crypto_map = {hashlib.md5: crypto.EVP_md5, + hashlib.sha1: crypto.EVP_sha1, + hashlib.sha256: crypto.EVP_sha256, + hashlib.sha224: crypto.EVP_sha224, + hashlib.sha384: crypto.EVP_sha384, + hashlib.sha512: crypto.EVP_sha512 + } +except OSError, AttributeError: + raise ImportError('Cannot find a compatible OpenSSL installation on your system') + + +def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): + c_pass = ctypes.c_char_p(data) + c_passlen = ctypes.c_int(len(data)) + c_salt = ctypes.c_char_p(salt) + c_saltlen = ctypes.c_int(len(salt)) + c_iter = ctypes.c_int(iterations) + c_keylen = ctypes.c_int(keylen) + if hashfunc: + crypto_hashfunc = hashlib_to_crypto_map.get(hashfunc) + crypto_hashfunc.restype = ctypes.c_void_p + if crypto_hashfunc is None: + raise ValueError('Unknown digest' + str(hashfunc)) + c_digest = ctypes.c_void_p(crypto_hashfunc()) + else: + crypto.EVP_sha1.restype = ctypes.c_void_p + c_digest = ctypes.c_void_p(crypto.EVP_sha1()) + c_buff = ctypes.create_string_buffer('\000' * (keylen + 1)) + err = PKCS5_PBKDF2_HMAC(c_pass, c_passlen, + c_salt, c_saltlen, + c_iter, + c_digest, + c_keylen, + c_buff) + + if err == 0: + raise ValueError('wrong parameters') + return c_buff.raw[:keylen] + +def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): + return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc).encode('hex') + +def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): + return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc) + + + + +if __name__ == '__main__': + crypto.SSLeay_version.restype = ctypes.c_char_p + print crypto.SSLeay_version(0) + for h in hashlib_to_crypto_map: + pkcs5_pbkdf2_hmac('secret' * 11, 'salt', hashfunc=h) diff --git a/gluon/utils.py b/gluon/utils.py index e8f1d294..848b80ad 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -44,7 +44,10 @@ except ImportError: import hmac try: - from contrib.pbkdf2 import pbkdf2_hex + try: + from contrib.pbkdf2_ctypes import pbkdf2_hex + except ImportError: + from contrib.pbkdf2 import pbkdf2_hex HAVE_PBKDF2 = True except ImportError: try: From 60792d943fa67f65646ebccee6fd31fd7decb698 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Mon, 29 Jul 2013 18:59:40 +0200 Subject: [PATCH 2/2] pbkdf2_ctypes updated from upstream --- gluon/contrib/pbkdf2_ctypes.py | 51 +++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/gluon/contrib/pbkdf2_ctypes.py b/gluon/contrib/pbkdf2_ctypes.py index 4f55c5b9..3627abed 100644 --- a/gluon/contrib/pbkdf2_ctypes.py +++ b/gluon/contrib/pbkdf2_ctypes.py @@ -1,11 +1,11 @@ # -*- coding: utf-8 -*- -""" +""" pbkdf2_ctypes ~~~~~~ This module implements pbkdf2 for Python using crypto lib from - openssl. - + openssl. + Note: This module is intended as a plugin replacement of pbkdf2.py by Armin Ronacher. @@ -16,30 +16,35 @@ """ import ctypes +import ctypes.util import hashlib import platform +import os.path - -try: # check that we have proper OpenSSL on the system. - if platform.system()=='Windows': +try: # check that we have proper OpenSSL on the system. + system = platform.system() + if system == 'Windows': if platform.architecture()[0] == '64bit': - crypto = ctypes.CDLL('libeay64.dll') + crypto = ctypes.CDLL(os.path.basename( + ctypes.util.find_library('libeay64'))) else: - crypto = ctypes.CDLL('libeay32.dll') - else: # should work on most unix os'. - crypto = ctypes.CDLL('libcrypto.so') + crypto = ctypes.CDLL(os.path.basename( + ctypes.util.find_library('libeay32'))) + else: + crypto = ctypes.CDLL(os.path.basename( + ctypes.util.find_library('crypto'))) PKCS5_PBKDF2_HMAC = crypto.PKCS5_PBKDF2_HMAC - hashlib_to_crypto_map = {hashlib.md5: crypto.EVP_md5, - hashlib.sha1: crypto.EVP_sha1, - hashlib.sha256: crypto.EVP_sha256, - hashlib.sha224: crypto.EVP_sha224, - hashlib.sha384: crypto.EVP_sha384, - hashlib.sha512: crypto.EVP_sha512 - } + hashlib_to_crypto_map = {hashlib.md5: crypto.EVP_md5, + hashlib.sha1: crypto.EVP_sha1, + hashlib.sha256: crypto.EVP_sha256, + hashlib.sha224: crypto.EVP_sha224, + hashlib.sha384: crypto.EVP_sha384, + hashlib.sha512: crypto.EVP_sha512} except OSError, AttributeError: - raise ImportError('Cannot find a compatible OpenSSL installation on your system') + raise ImportError('Cannot find a compatible OpenSSL installation ' + 'on your system') def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): @@ -58,7 +63,7 @@ def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): else: crypto.EVP_sha1.restype = ctypes.c_void_p c_digest = ctypes.c_void_p(crypto.EVP_sha1()) - c_buff = ctypes.create_string_buffer('\000' * (keylen + 1)) + c_buff = ctypes.create_string_buffer('\000' * keylen) err = PKCS5_PBKDF2_HMAC(c_pass, c_passlen, c_salt, c_saltlen, c_iter, @@ -70,15 +75,15 @@ def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): raise ValueError('wrong parameters') return c_buff.raw[:keylen] + def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): - return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc).encode('hex') + return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc).\ + encode('hex') + def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc) - - - if __name__ == '__main__': crypto.SSLeay_version.restype = ctypes.c_char_p print crypto.SSLeay_version(0)