## October 27, 2013

### AES encryption with Python

For a current project I had to encrypt arbitary data with AES. AES is a symmetric-key algorithm that is even used by the U.S. government. That cipher is approved to be used for top secret information at the National Secret Agency (NSA). It offers key sizes of 128, 192 and 256 bits.

To encrypt a message with AES, you'll first have to install PyCrypto. Its considered as the "standard" cryptography library for Python and is widely used. The easiest way to install PyCrypto is using pip:
# pip install pycrypto

### Encryption of a message

You first have to decide which key size you'd like to use. The higher the key size, the more secure is the encryption, but it takes more computing resources to encrypt your message. While this is almost irrelevant for small usage, it might have a big impact if you plan to encrypt large amount of data with AES. In the following example we'll use AES-256 with a key size of 256 bits.
```from Crypto import Random
from Crypto.Cipher import AES

def encrypt(message, key=None, key_size=256):
x = AES.block_size - len(s) % AES.block_size
return s + (bytes([x]) * x)

if key is None:

cipher = AES.new(key, AES.MODE_CBC, iv)

The encrypt function expects the message as parameter, which is a bytes object. If your message is a string, you can convert it to a bytes object by using 'my string'.encode('utf-8'). The second parameter is optional: The key you'd like to use for encryption. It must be a bytes object as well; if no key is specified, a random one will be generated. The third and last parameter specifies the key size in bits.

Since we're using the CBC mode for AES, the message must be a multiple of key size / 8 . If your message is of length 3 and the key size is 256 bits, it must be padded with 29 bytes (256 bits / 8 = 32 bytes - 3 bytes of the message length). The padding is performed by appending the integer representation of the number of bytes that have to be appended to the message. In case if the message is 3 bytes long, we'll append 29 times the byte \x1d (hexadecimal representation of 29) to the message to get the padded message.

If no key has been specified in the function arguments, a random one will be generated. The key must be of size key size / 8  (32 bytes for AES-256, 16 bytes for AES-128). If you have a key that is shorter or longer than this, you could hash your key using SHA to get the appropriate number of bytes you need.

Next, we need an initialization vector (IV). We'll use a random IV here. The IV must be block_size  bytes long (which is always 16 bytes). Once we've got the IV, we can create an AES object with the key, mode and IV. That AES object must be created later again when you want to decipher the message.

In the last step, we'll encrypt the padded message. The encrypt function above will return a tuple that contains two items:

1. The ciphertext of the padded message. The IV has been prepended to the ciphertext as we need the IV to decipher the message again

2. The key that was used to encrypt the message

### Decryption of a previously encrypted message

To decrypt AES encrypted ciphertext, you need to know how it was encrypted (which AES mode was being used, the IV and of course the key). For messages that were encrypted using the above function, this is easy, since we know all these parameters. For AES ciphertext that was generated by other programs, its rather hard to find out how it was encrypted to get the original message from it - unless you know the encryption parameters.
```def decrypt(ciphertext, key):
iv = ciphertext[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)

return plaintext```

Start decrypting the ciphertext by passing the ciphertext (from encrypt()'s first return parameter)  and the key that was used for encryption (from encrypt()'s second return parameter).

We first define the unpad lambda function: It will basically remove the padding bytes from the original message. The length of bytes to remove from end has been specified in the padding function when the message was encrypted.

The IV has to be extracted from the ciphertext, as its a required parameter for the AES object initialized when using the CBC mode. The IV is the first 16 bytes of the ciphertext. Now we can reconstruct the AES object that was used to encrypt the original message.

To get the original message (also called plaintext in the code above), we have to decrypt the ciphertext with the reconstructed AES object. Then, the message is un-padded to remove the bytes that have been appended when the message was encrypted. Finally, only use bytes after the IV to get the original message.

### A complete example

```#!/usr/bin/env python3.3

from Crypto import Random
from Crypto.Cipher import AES

def encrypt(message, key=None, key_size=256):
x = AES.block_size - len(s) % AES.block_size
return s + (bytes([x]) * x)

if key is None:

cipher = AES.new(key, AES.MODE_CBC, iv)

def decrypt(ciphertext, key):
iv = ciphertext[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)

return plaintext

if __name__ == '__main__':
message = b'my secret message'

encrypted = encrypt(message)
decrypted = decrypt(*encrypted)

assert decrypted == message```

The example will encrypt a message and validate whether the decrypted version of the encrypted message will match the original message.

### Python 2.x

Please note that the code will only run on Python 3. To use it with Python 2, you'll have to use other (un)padding functions to achieve that. Below is the full program example as from above that will use with Python 2.x.
```#!/usr/bin/env python2.7

from Crypto import Random
from Crypto.Cipher import AES

def encrypt(message, key=None, key_size=256):
x = AES.block_size - len(s) % AES.block_size
return s + (chr(x) * x)

if key is None:

cipher = AES.new(key, AES.MODE_CBC, iv)

def decrypt(ciphertext, key):
iv = ciphertext[:AES.block_size]
cipher = AES.new(key, AES.MODE_CBC, iv)

return plaintext

if __name__ == '__main__':
message = b'my secret message'

encrypted = encrypt(message)
decrypted = decrypt(*encrypted)

assert decrypted == message```