From 76b035b80026f1f080ae932ef27014a5c181c74a Mon Sep 17 00:00:00 2001 From: Marcin Wielgoszewski Date: Sun, 27 Jan 2013 12:23:42 -0500 Subject: [PATCH 1/2] actually use the constant-time compare function in secure_loads --- gluon/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gluon/utils.py b/gluon/utils.py index 8e55108b..77ac7aa7 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -144,7 +144,7 @@ def secure_loads(data, encryption_key, hash_key=None, compression_level=None): hash_key = hashlib.sha1(encryption_key).hexdigest() signature, encrypted_data = data.split(':', 1) actual_signature = hmac.new(hash_key, encrypted_data).hexdigest() - if signature != actual_signature: + if not compare(signature, actual_signature): return None key = pad(encryption_key[:32]) cipher = AES_new(key) From d16b5899e81b1e764a0a27027fdcdd256dc935a0 Mon Sep 17 00:00:00 2001 From: Marcin Wielgoszewski Date: Sun, 27 Jan 2013 12:47:19 -0500 Subject: [PATCH 2/2] never use secret key as an initialization vector --- gluon/utils.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/gluon/utils.py b/gluon/utils.py index 77ac7aa7..79e9fdd2 100644 --- a/gluon/utils.py +++ b/gluon/utils.py @@ -49,7 +49,13 @@ except ImportError: logger = logging.getLogger("web2py") -AES_new = lambda key: AES.new(key, AES.MODE_CBC, IV=key[:16]) +def AES_new(key, IV=None): + """ Returns an AES cipher object and random IV if None specified """ + if IV is None: + IV = fast_urandom16() + + return AES.new(key, AES.MODE_CBC, IV), IV + def compare(a, b): """ compares two strings and not vulnerable to timing attacks """ @@ -131,8 +137,8 @@ def secure_dumps(data, encryption_key, hash_key=None, compression_level=None): if compression_level: dump = zlib.compress(dump, compression_level) key = pad(encryption_key[:32]) - cipher = AES_new(key) - encrypted_data = base64.urlsafe_b64encode(cipher.encrypt(pad(dump))) + cipher, IV = AES_new(key) + encrypted_data = base64.urlsafe_b64encode(IV + cipher.encrypt(pad(dump))) signature = hmac.new(hash_key, encrypted_data).hexdigest() return signature + ':' + encrypted_data @@ -147,9 +153,11 @@ def secure_loads(data, encryption_key, hash_key=None, compression_level=None): if not compare(signature, actual_signature): return None key = pad(encryption_key[:32]) - cipher = AES_new(key) + encrypted_data = base64.urlsafe_b64decode(encrypted_data) + IV, encrypted_data = encrypted_data[:16], encrypted_data[16:] + cipher, _ = AES_new(key, IV=IV) try: - data = cipher.decrypt(base64.urlsafe_b64decode(encrypted_data)) + data = cipher.decrypt(encrypted_data) data = data.rstrip(' ') if compression_level: data = zlib.decompress(data)