Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / db / aes_encrypt.py @ 8d5795b4

History | View | Annotate | Download (2 kB)

1
from binascii import b2a_base64, a2b_base64
2
from Crypto.Cipher import AES
3
from Crypto import Random
4
from random import choice
5
from string import letters, digits
6
from synnefo.settings import SECRET_ENCRYPTION_KEY
7

    
8

    
9
DB_ENCRYPTED_FIELD_PREFIX = 'encrypted'
10
SALT_LEN = 8
11

    
12

    
13
def _pad_secret(secret, blocksize=32, padding='}'):
14
    len_secret = len(secret)
15
    if len_secret > 32:
16
        raise ValueError('Encryption key must be smaller than 32 bytes')
17
    if not len_secret in (16, 24, 32):
18
        return secret + (blocksize - len(secret)) * padding
19
    return secret
20

    
21

    
22
def encrypt(s, iv):
23
    obj = AES.new(_pad_secret(SECRET_ENCRYPTION_KEY), AES.MODE_CFB, iv)
24
    return obj.encrypt(s)
25

    
26

    
27
def decrypt(s, iv):
28
    obj = AES.new(_pad_secret(SECRET_ENCRYPTION_KEY), AES.MODE_CFB, iv)
29
    return obj.decrypt(s)
30

    
31

    
32
def encrypt_db_charfield(plaintext):
33
    if not plaintext:
34
        return plaintext
35
    salt = "".join([choice(letters + digits) for i in xrange(SALT_LEN)])
36

    
37
    iv = Random.get_random_bytes(16)
38
    plaintext = "%s%s" % (salt, plaintext)
39
    # Encrypt and convert to binary
40
    ciphertext = b2a_base64(encrypt(plaintext, iv))
41
    iv = b2a_base64(iv)
42
    # Append prefix,salt and return encoded value
43
    final = '%s:%s:%s$%s' % (DB_ENCRYPTED_FIELD_PREFIX, iv, salt, ciphertext)
44
    return final.encode('utf8')
45

    
46

    
47
def decrypt_db_charfield(ciphertext):
48
    if not ciphertext:
49
        return ciphertext
50
    has_prefix = ciphertext.startswith(DB_ENCRYPTED_FIELD_PREFIX + ':')
51
    if not has_prefix:  # Non-encoded value
52
        return ciphertext
53
    else:
54
        _, iv, ciphertext = ciphertext.split(':')
55

    
56
    pure_salt, encrypted = ciphertext.split('$')
57
    iv = a2b_base64(iv)
58

    
59
    plaintext = decrypt(a2b_base64(encrypted), iv)
60

    
61
    salt = plaintext[:SALT_LEN]
62
    plaintext = plaintext[SALT_LEN:]
63

    
64
    if salt != pure_salt:
65
        # Cannot decrtypt password
66
        raise CorruptedPassword("Cannot decrypt password. Check the key")
67
    else:
68
        return plaintext
69

    
70

    
71
class CorruptedPassword(Exception):
72
    pass