Merge pull request #136 from michele-comitini/speed_triks1

optimizations. Switch to PyCrypto support in pbkdf2 when possible.
This commit is contained in:
mdipierro
2013-07-17 05:24:19 -07:00
3 changed files with 51 additions and 25 deletions
+26 -9
View File
@@ -40,15 +40,32 @@
:copyright: (c) Copyright 2011 by Armin Ronacher. :copyright: (c) Copyright 2011 by Armin Ronacher.
:license: BSD, see LICENSE for more details. :license: BSD, see LICENSE for more details.
""" """
import hmac #import hmac
import hashlib #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 struct import Struct
from operator import xor from operator import xor
from itertools import izip, starmap from itertools import izip, starmap
from collections import deque
_pack_int = Struct('>I').pack _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): def pbkdf2_hex(data, salt, iterations=1000, keylen=24, hashfunc=None):
"""Like :func:`pbkdf2_bin` but returns a hex encoded string.""" """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, key of `keylen` bytes. By default SHA-1 is used as hash function,
a different hashlib `hashfunc` can be provided. a different hashlib `hashfunc` can be provided.
""" """
hashfunc = hashfunc or hashlib.sha1 hashfunc = hashfunc or sha1
mac = hmac.new(data, None, hashfunc) mac = hmac.new(data, None, hashfunc)
def _pseudorandom(x, mac=mac): def _pseudorandom(x, mac=mac):
h = mac.copy() h = mac.copy()
h.update(x) h.update(x)
return map(ord, h.digest()) return h.digest()
buf = [] buf = deque()
for block in xrange(1, -(-keylen // mac.digest_size) + 1): for block in xrange(1, -(-keylen // mac.digest_size) + 1):
rv = u = _pseudorandom(salt + _pack_int(block)) rv = u = _pseudorandom(salt + _pack_int(block))
for i in xrange(iterations - 1): for i in xrange(iterations - 1):
u = _pseudorandom(''.join(map(chr, u))) u = _pseudorandom(u)
rv = starmap(xor, izip(rv, u)) rv = strxor(rv, u)
buf.extend(rv) buf.extend(rv)
return ''.join(map(chr, buf))[:keylen] return ''.join(buf)[:keylen]
def test(): def test():
+1 -1
View File
@@ -57,7 +57,7 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
# if not relative and not from applications: # if not relative and not from applications:
if hasattr(current, 'request') \ if hasattr(current, 'request') \
and level <= 0 \ and level <= 0 \
and not name.split('.')[0] in INVALID_MODULES \ and not name.partition('.')[0] in INVALID_MODULES \
and isinstance(globals, dict): and isinstance(globals, dict):
import_tb = None import_tb = None
try: try:
+24 -15
View File
@@ -24,6 +24,8 @@ import socket
import base64 import base64
import zlib import zlib
_struct_2_long_long = struct.Struct('=QQ')
python_version = sys.version_info[0] python_version = sys.version_info[0]
if python_version == 2: if python_version == 2:
@@ -31,6 +33,15 @@ if python_version == 2:
else: else:
import pickle 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: try:
from Crypto.Cipher import AES from Crypto.Cipher import AES
@@ -69,7 +80,7 @@ def compare(a, b):
def md5_hash(text): def md5_hash(text):
""" Generate a md5 hash with the given 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'): def simple_hash(text, key='', salt='', digest_alg='md5'):
@@ -102,17 +113,17 @@ def get_digest(value):
return value return value
value = value.lower() value = value.lower()
if value == "md5": if value == "md5":
return hashlib.md5 return md5
elif value == "sha1": elif value == "sha1":
return hashlib.sha1 return sha1
elif value == "sha224": elif value == "sha224":
return hashlib.sha224 return sha224
elif value == "sha256": elif value == "sha256":
return hashlib.sha256 return sha256
elif value == "sha384": elif value == "sha384":
return hashlib.sha384 return sha384
elif value == "sha512": elif value == "sha512":
return hashlib.sha512 return sha512
else: else:
raise ValueError("Invalid digest algorithm: %s" % value) 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 packed = ''.join(chr(x) for x in ctokens) # python 2
else: else:
packed = bytes([]).join(bytes([x]) for x in ctokens) # python 3 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 return unpacked_ctokens, have_urandom
UNPACKED_CTOKENS, HAVE_URANDOM = initialize_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)) rand_longs = (random.getrandbits(64), random.getrandbits(64))
if HAVE_URANDOM: if HAVE_URANDOM:
urand_longs = struct.unpack('=QQ', fast_urandom16()) urand_longs = _struct_2_long_long.unpack(fast_urandom16())
byte_s = struct.pack('=QQ', byte_s = _struct_2_long_long.pack(rand_longs[0] ^ urand_longs[0] ^ ctokens[0],
rand_longs[0] ^ urand_longs[0] ^ ctokens[0], rand_longs[1] ^ urand_longs[1] ^ ctokens[1])
rand_longs[1] ^ urand_longs[1] ^ ctokens[1])
else: else:
byte_s = struct.pack('=QQ', byte_s = _struct_2_long_long.pack(rand_longs[0] ^ ctokens[0],
rand_longs[0] ^ ctokens[0], rand_longs[1] ^ ctokens[1])
rand_longs[1] ^ ctokens[1])
return str(uuid.UUID(bytes=byte_s, version=4)) return str(uuid.UUID(bytes=byte_s, version=4))
REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)') REGEX_IPv4 = re.compile('(\d+)\.(\d+)\.(\d+)\.(\d+)')