diff --git a/gluon/contrib/pbkdf2_ctypes.py b/gluon/contrib/pbkdf2_ctypes.py index 70c260de..8cad65dc 100644 --- a/gluon/contrib/pbkdf2_ctypes.py +++ b/gluon/contrib/pbkdf2_ctypes.py @@ -3,13 +3,15 @@ pbkdf2_ctypes ~~~~~~ + Fast pbkdf2. + This module implements pbkdf2 for Python using crypto lib from - openssl. + openssl or commoncrypto. Note: This module is intended as a plugin replacement of pbkdf2.py by Armin Ronacher. - Git repository: + Git repository: $ git clone https://github.com/michele-comitini/pbkdf2_ctypes.git :copyright: Copyright (c) 2013: Michele Comitini @@ -22,8 +24,12 @@ import ctypes.util import hashlib import platform import os.path +import binascii -def commoncrypto_hashlib_to_crypto_map_get(hashfunc): +__all__ = ['pkcs5_pbkdf2_hmac', 'pbkdf2_bin', 'pbkdf2_hex'] +__version__ = '0.99.3' + +def _commoncrypto_hashlib_to_crypto_map_get(hashfunc): hashlib_to_crypto_map = {hashlib.sha1: 1, hashlib.sha224: 2, hashlib.sha256: 3, @@ -34,10 +40,10 @@ def commoncrypto_hashlib_to_crypto_map_get(hashfunc): raise ValueError('Unkwnown digest %s' % hashfunc) return crypto_hashfunc -def commoncrypto_pbkdf2(data, salt, iterations, digest, keylen): +def _commoncrypto_pbkdf2(data, salt, iterations, digest, keylen): """Common Crypto compatibile wrapper """ - c_hashfunc = ctypes.c_uint32(commoncrypto_hashlib_to_crypto_map_get(digest)) + c_hashfunc = ctypes.c_uint32(_commoncrypto_hashlib_to_crypto_map_get(digest)) c_pass = ctypes.c_char_p(data) c_passlen = ctypes.c_size_t(len(data)) c_salt = ctypes.c_char_p(salt) @@ -66,7 +72,7 @@ def commoncrypto_pbkdf2(data, salt, iterations, digest, keylen): return (1 - ret, c_buff) -def openssl_hashlib_to_crypto_map_get(hashfunc): +def _openssl_hashlib_to_crypto_map_get(hashfunc): hashlib_to_crypto_map = {hashlib.md5: crypto.EVP_md5, hashlib.sha1: crypto.EVP_sha1, hashlib.sha256: crypto.EVP_sha256, @@ -79,11 +85,11 @@ def openssl_hashlib_to_crypto_map_get(hashfunc): crypto_hashfunc.restype = ctypes.c_void_p return crypto_hashfunc() - -def openssl_pbkdf2(data, salt, iterations, digest, keylen): + +def _openssl_pbkdf2(data, salt, iterations, digest, keylen): """OpenSSL compatibile wrapper """ - c_hashfunc = ctypes.c_void_p(openssl_hashlib_to_crypto_map_get(digest)) + c_hashfunc = ctypes.c_void_p(_openssl_hashlib_to_crypto_map_get(digest)) c_pass = ctypes.c_char_p(data) c_passlen = ctypes.c_int(len(data)) @@ -92,7 +98,7 @@ def openssl_pbkdf2(data, salt, iterations, digest, keylen): c_iter = ctypes.c_int(iterations) c_keylen = ctypes.c_int(keylen) c_buff = ctypes.create_string_buffer(keylen) - + # PKCS5_PBKDF2_HMAC(const char *pass, int passlen, # const unsigned char *salt, int saltlen, int iter, # const EVP_MD *digest, @@ -102,7 +108,7 @@ def openssl_pbkdf2(data, salt, iterations, digest, keylen): ctypes.c_char_p, ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p] - + crypto.PKCS5_PBKDF2_HMAC.restype = ctypes.c_int err = crypto.PKCS5_PBKDF2_HMAC(c_pass, c_passlen, c_salt, c_saltlen, @@ -119,36 +125,40 @@ try: # check that we have proper OpenSSL or Common Crypto on the system. libname = ctypes.util.find_library('libeay64') if not libname: raise OSError('Library not found') - crypto = ctypes.CDLL(os.path.basename(libname)) + crypto = ctypes.CDLL(libname) else: libname = ctypes.util.find_library('libeay32') if not libname: - raise OSError('Library not found') + raise OSError('Library libeay32 not found.') - crypto = ctypes.CDLL(os.path.basename(libname)) - _pbkdf2_hmac = openssl_pbkdf2 + crypto = ctypes.CDLL(libname) + _pbkdf2_hmac = _openssl_pbkdf2 crypto.PKCS5_PBKDF2_HMAC # test compatibility elif system == 'Darwin': # think different(TM)! i.e. break things! + if [int(x) for x in platform.mac_ver()[0].split('.')] < [10, 7, 0]: + raise OSError('OS X Version too old %s < 10.7.0' % platform.mac_ver()[0]) libname = ctypes.util.find_library('System') if not libname: raise OSError('Library not found') crypto = ctypes.CDLL(os.path.basename(libname)) - _pbkdf2_hmac = commoncrypto_pbkdf2 + _pbkdf2_hmac = _commoncrypto_pbkdf2 else: libname = ctypes.util.find_library('crypto') if not libname: - raise OSError('not found') + raise OSError('Library crypto not found.') crypto = ctypes.CDLL(os.path.basename(libname)) - _pbkdf2_hmac = openssl_pbkdf2 + _pbkdf2_hmac = _openssl_pbkdf2 crypto.PKCS5_PBKDF2_HMAC # test compatibility -except (OSError, AttributeError), e: +except (OSError, AttributeError) as e: raise ImportError('Cannot find a compatible cryptographic library ' - 'on your system') + 'on your system. %s' % e) def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): + if hashfunc is None: + hashfunc = hashlib.sha1 err, c_buff = _pbkdf2_hmac(data, salt, iterations, hashfunc, keylen) if err == 0: @@ -157,8 +167,7 @@ def pkcs5_pbkdf2_hmac(data, salt, iterations=1000, keylen=24, hashfunc=None): def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): - return pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc).\ - encode('hex') + return binascii.hexlify(pkcs5_pbkdf2_hmac(data, salt, iterations, keylen, hashfunc)) def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): @@ -167,10 +176,11 @@ def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): if __name__ == '__main__': try: crypto.SSLeay_version.restype = ctypes.c_char_p - print crypto.SSLeay_version(0) + print(crypto.SSLeay_version(0)) except: pass for h in [hashlib.sha1, hashlib.sha224, hashlib.sha256, hashlib.sha384, hashlib.sha512]: - print pkcs5_pbkdf2_hmac('secret' * 11, 'salt', hashfunc=h).encode('hex') + print(binascii.hexlify(pkcs5_pbkdf2_hmac(b'secret' * 11, b'salt', + hashfunc=h)))