import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES
from Crypto.Protocol import KDF
from Crypto.Util.Padding import pad, unpad
### AES-256-CBC w/ PKCS7 padding ###
# 0x28, 0x7c, 0x6a, 0xa2
# 0x2e, 0xa6, 0x46, 0x4b
# 0x68, 0xef, 0x91, 0xec
# 0x0e, 0x8c, 0x3e, 0x50
salt_bytes = [0x28, 0x7c, 0x6a, 0xa2, 0x2e, 0xa6, 0x46, 0x4b, 0x68, 0xef, 0x91, 0xec, 0x0e, 0x8c, 0x3e, 0x50]
salt = "".join(map(chr, salt_bytes))
# for debugging purposes
def print_bytes(bytes):
count = len(bytes)
for i in range(count):
byte = bytes[i]
last = i == count - 1
print(hex(byte), end = "")
print("\n" if last else ", ", end = "")
key_size = 32 # 256 bits
def encrypt_string(plain, key):
# use PBKDF2 key derivation
# RFC 2898: https://www.ietf.org/rfc/rfc2898.txt
key = KDF.PBKDF2(key, salt, key_size)
# pad text with PKCS7
# RFC 2315: https://tools.ietf.org/html/rfc2315
padded = pad(plain.encode(), AES.block_size, 'pkcs7')
# generate random iv
iv = Random.new().read(AES.block_size)
# create cipher and encrypt padded data
cipher = AES.new(key, AES.MODE_CBC, iv)
encrypted = cipher.encrypt(padded)
# return encoded cipher text
# first 16 bytes = iv, rest is encrypted bytes
return base64.b64encode(iv + encrypted)
def decrypt_string(enc, key):
# use PBKDF2 key derivation
# RFC 2898: https://www.ietf.org/rfc/rfc2898.txt
key = KDF.PBKDF2(key, salt, key_size)
# decode cipher text and determine iv (first 16 bytes)
enc = base64.b64decode(enc)
iv = enc[:AES.block_size]
# create cipher and decrypt encrypted bytes (skip first 16 bytes)
cipher = AES.new(key, AES.MODE_CBC, iv)
decrypted = cipher.decrypt(enc[AES.block_size:])
# unpad decrypted bytes
unpadded = unpad(decrypted, AES.block_size, 'pkcs7')
# convert bytes to UTF-8 encoded string and return
return unpadded.decode('utf-8')
Comments