From f8c20760d5463a8a2a1ca544ac531042c16f4f45 Mon Sep 17 00:00:00 2001 From: Michele Comitini Date: Wed, 17 Jul 2013 02:58:47 +0200 Subject: [PATCH] optimizations. Switch to PyCrypto support in pbkdf2 when possible. --- gluon/contrib/pbkdf2.py | 35 ++++++++++++++++++++++++++--------- gluon/custom_import.py | 2 +- gluon/utils.py | 39 ++++++++++++++++++++++++--------------- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/gluon/contrib/pbkdf2.py b/gluon/contrib/pbkdf2.py index b7a7dd42..a67bf94e 100644 --- a/gluon/contrib/pbkdf2.py +++ b/gluon/contrib/pbkdf2.py @@ -40,15 +40,32 @@ :copyright: (c) Copyright 2011 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ -import hmac -import hashlib +#import hmac +#import hashlib +try: + # Use PyCrypto (if available). + from Crypto.Hash import HMAC as hmac, SHA as sha1 +except ImportError: + # PyCrypto not available. Use the Python standard library. + import hmac + try: + from hashlib import sha1 + except ImportError: + # hashlib not available. Use the old sha module. + import sha as sha1 + from struct import Struct from operator import xor from itertools import izip, starmap - +from collections import deque _pack_int = Struct('>I').pack +try: + from Crypto.Util.strxor import strxor +except ImportError: + def strxor(a, b): + return ''.join(chr(xor(ord(x), ord(y))) for x,y in izip(a, b)) def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None): """Like :func:`pbkdf2_bin` but returns a hex encoded string.""" @@ -61,20 +78,20 @@ def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None): key of `keylen` bytes. By default SHA-1 is used as hash function, a different hashlib `hashfunc` can be provided. """ - hashfunc = hashfunc or hashlib.sha1 + hashfunc = hashfunc or sha1 mac = hmac.new(data, None, hashfunc) def _pseudorandom(x, mac=mac): h = mac.copy() h.update(x) - return map(ord, h.digest()) - buf = [] + return h.digest() + buf = deque() for block in xrange(1, -(-keylen // mac.digest_size) + 1): rv = u = _pseudorandom(salt + _pack_int(block)) for i in xrange(iterations - 1): - u = _pseudorandom(''.join(map(chr, u))) - rv = starmap(xor, izip(rv, u)) + u = _pseudorandom(u) + rv = strxor(rv, u) buf.extend(rv) - return ''.join(map(chr, buf))[:keylen] + return ''.join(buf)[:keylen] def test(): diff --git a/gluon/custom_import.py b/gluon/custom_import.py index b1c67e0c..5e06d7dd 100644 --- a/gluon/custom_import.py +++ b/gluon/custom_import.py @@ -57,7 +57,7 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1): # if not relative and not from applications: if hasattr(current, 'request') \ and level <= 0 \ - and not name.split('.')[0] in INVALID_MODULES \ + and not name.partition('.')[0] in INVALID_MODULES \ and isinstance(globals, dict): import_tb = None try: diff --git a/gluon/utils.py b/gluon/utils.py index 3edc280a..7cb3106d 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -24,6 +24,8 @@ import socket import base64 import zlib +_struct_2_long_long = struct.Struct('=QQ') + python_version = sys.version_info[0] if python_version == 2: @@ -31,6 +33,15 @@ if python_version == 2: else: import pickle +try: + from Crypto.Hash import MD5 as md5, \ + SHA as sha1, \ + SHA224 as sha224, \ + SHA256 as sha256, \ + SHA384 as sha384, \ + SHA512 as sha512 +except ImportError: + from hashlib import md5, sha1, sha224, sha256, sha384, sha512 try: from Crypto.Cipher import AES @@ -69,7 +80,7 @@ def compare(a, b): def md5_hash(text): """ Generate a md5 hash with the given text """ - return hashlib.md5(text).hexdigest() + return md5(text).hexdigest() def simple_hash(text, key='', salt='', digest_alg='md5'): @@ -102,17 +113,17 @@ def get_digest(value): return value value = value.lower() if value == "md5": - return hashlib.md5 + return md5 elif value == "sha1": - return hashlib.sha1 + return sha1 elif value == "sha224": - return hashlib.sha224 + return sha224 elif value == "sha256": - return hashlib.sha256 + return sha256 elif value == "sha384": - return hashlib.sha384 + return sha384 elif value == "sha512": - return hashlib.sha512 + return sha512 else: raise ValueError("Invalid digest algorithm: %s" % value) @@ -212,7 +223,7 @@ This is not specific to web2py; consider deploying on a different operating syst packed = ''.join(chr(x) for x in ctokens) # python 2 else: packed = bytes([]).join(bytes([x]) for x in ctokens) # python 3 - unpacked_ctokens = struct.unpack('=QQ', packed) + unpacked_ctokens = _struct_2_long_long.unpack(packed) return unpacked_ctokens, have_urandom UNPACKED_CTOKENS, HAVE_URANDOM = initialize_urandom() @@ -244,14 +255,12 @@ def web2py_uuid(ctokens=UNPACKED_CTOKENS): """ rand_longs = (random.getrandbits(64), random.getrandbits(64)) if HAVE_URANDOM: - urand_longs = struct.unpack('=QQ', fast_urandom16()) - byte_s = struct.pack('=QQ', - rand_longs[0] ^ urand_longs[0] ^ ctokens[0], - rand_longs[1] ^ urand_longs[1] ^ ctokens[1]) + urand_longs = _struct_2_long_long.unpack(fast_urandom16()) + byte_s = _struct_2_long_long.pack(rand_longs[0] ^ urand_longs[0] ^ ctokens[0], + rand_longs[1] ^ urand_longs[1] ^ ctokens[1]) else: - byte_s = struct.pack('=QQ', - rand_longs[0] ^ ctokens[0], - rand_longs[1] ^ ctokens[1]) + byte_s = _struct_2_long_long.pack(rand_longs[0] ^ ctokens[0], + rand_longs[1] ^ ctokens[1]) return str(uuid.UUID(bytes=byte_s, version=4)) REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)')