patternpythonMinor
Simple crypto library in Python
Viewed 0 times
cryptosimplepythonlibrary
Problem
Is this code (also available at github with tests, example, and description of algorithms) correct and secure? It follows the recommendations here as far as I can tell.
```
from Crypto.Cipher import AES
from Crypto.Hash import SHA256, HMAC
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Random.random import getrandbits
from Crypto.Util import Counter
EXPANSION_COUNT = 10000
AES_KEY_LEN = 256
SALT_LEN = 128
HASH = SHA256
HEADER = b'sc\x00\x00'
# lengths here are in bits, but pcrypto uses block size in bytes
HALF_BLOCK = AES.block_size*8//2
assert HALF_BLOCK 2**HALF_BLOCK:
raise EncryptionException('Message too long.')
def _assert_decrypt_length(data):
if len(data) < len(HEADER) + SALT_LEN//8 + HASH.digest_size:
raise DecryptionException('Missing data.')
def _assert_header_sc(data):
if len(data) < 2 or data[:2] != HEADER[:2]:
raise DecryptionException('Data passed to decrypt were not generated by simple-crypt (bad header).')
def _assert_header_version(data):
if len(data) < len(HEADER) or data[:len(HEADER)] != HEADER:
raise DecryptionException('The data appear to be encrypted with a more recent version of simple-crypt (bad header). ' +
'Please update the library and try again.')
def _assert_hmac(key ,hmac, hmac2):
# https://www.isecpartners.com/news-events/news/2011/february/double-hmac-verification.aspx
if _hmac(key, hmac) != _hmac(key, hmac2):
raise DecryptionException('Bad password or corrupt / modified data.')
def _expand_keys(password, salt):
if not salt: raise ValueError('Missing salt.')
if not password: raise ValueError('Missing password.')
key_len = AES_KEY_LEN // 8
# the form of the prf below is taken from the code for PBKDF2
keys = PBKDF2(_str_to_bytes(password), salt, dkLen=2*key_len,
count=EXPANSION_COUNT, prf=lambda p,s: HMAC.new(p,s,HASH).digest())
return keys[:key_len], keys[key_len:]
def _random_bytes(n):
return bytes(getrandbits(8) for _
```
from Crypto.Cipher import AES
from Crypto.Hash import SHA256, HMAC
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Random.random import getrandbits
from Crypto.Util import Counter
EXPANSION_COUNT = 10000
AES_KEY_LEN = 256
SALT_LEN = 128
HASH = SHA256
HEADER = b'sc\x00\x00'
# lengths here are in bits, but pcrypto uses block size in bytes
HALF_BLOCK = AES.block_size*8//2
assert HALF_BLOCK 2**HALF_BLOCK:
raise EncryptionException('Message too long.')
def _assert_decrypt_length(data):
if len(data) < len(HEADER) + SALT_LEN//8 + HASH.digest_size:
raise DecryptionException('Missing data.')
def _assert_header_sc(data):
if len(data) < 2 or data[:2] != HEADER[:2]:
raise DecryptionException('Data passed to decrypt were not generated by simple-crypt (bad header).')
def _assert_header_version(data):
if len(data) < len(HEADER) or data[:len(HEADER)] != HEADER:
raise DecryptionException('The data appear to be encrypted with a more recent version of simple-crypt (bad header). ' +
'Please update the library and try again.')
def _assert_hmac(key ,hmac, hmac2):
# https://www.isecpartners.com/news-events/news/2011/february/double-hmac-verification.aspx
if _hmac(key, hmac) != _hmac(key, hmac2):
raise DecryptionException('Bad password or corrupt / modified data.')
def _expand_keys(password, salt):
if not salt: raise ValueError('Missing salt.')
if not password: raise ValueError('Missing password.')
key_len = AES_KEY_LEN // 8
# the form of the prf below is taken from the code for PBKDF2
keys = PBKDF2(_str_to_bytes(password), salt, dkLen=2*key_len,
count=EXPANSION_COUNT, prf=lambda p,s: HMAC.new(p,s,HASH).digest())
return keys[:key_len], keys[key_len:]
def _random_bytes(n):
return bytes(getrandbits(8) for _
Solution
I'm not a crypto expert, but I see that at least your decryption code is vulnerable to verification timing attacks (see e.g. here: http://rdist.root.org/2009/05/28/timing-attack-in-google-keyczar-library/)
Context
StackExchange Code Review Q#19910, answer score: 5
Revisions (0)
No revisions yet.