AES-256 Cipher – Python Cryptography Examples
Tags: cryptography, programming, python, security
Published: 2020-02-06 15:46:22

---
title: AES-256 Cipher – Python Cryptography Examples
published: true
date: 2020-02-06 15:45:04 UTC
tags: cryptography,programming,python,security
canonical_url: https://qvault.io/2020/02/06/aes-256-cipher-python-cryptography-examples/?utm_source=rss&utm_medium=rss&utm_campaign=aes-256-cipher-python-cryptography-examples
---

By Lane Wagner – [@wagslane](https://twitter.com/wagslane) on Twitter

![](https://qvault.io/wp-content/uploads/2020/02/photo-1507680225127-6450260913c0-1024x685.jpeg)

Need to encrypt some text with a password or private key in Python? You came to the right place. AES-256 is a solid symmetric cipher that is commonly used to encrypt data for oneself. In other words, the same person who is encrypting the data is typically decrypting it as well (think [password manager](https://qvault.io))

## Dependencies

For this tutorial, we will be using Python 3. Make sure you install [pycryptodome](https://www.pycryptodome.org/en/latest/src/introduction.html), which will give us access to an implementation of AES-256:

```
pip3 install pycryptodomex
```

## Padding – Handled by GCM

AES-256 typically requires that the data to be encrypted is supplied in 16-byte blocks, and you may have seen that on other sites or tutorials. AES-256 in GCM mode, however, doesn’t require an special padding to be done by us manually.

## Encrypting

Now we create a simple _encrypt(plain\_text, password)_ function. This function uses the password to encrypt the plain text. Anyone with access to the encrypted text and the password will be able to decrypt it.

```
def encrypt(plain_text, password):
    # generate a random salt
    salt = get_random_bytes(AES.block_size)

    # use the Scrypt KDF to get a private key from the password
    private_key = hashlib.scrypt(
        password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)

    # create cipher config
    cipher_config = AES.new(private_key, AES.MODE_GCM)

    # return a dictionary with the encrypted text
    cipher_text, tag = cipher_config.encrypt_and_digest(bytes(plain_text, 'utf-8'))
    return {
        'cipher_text': b64encode(cipher_text).decode('utf-8'),
        'salt': b64encode(salt).decode('utf-8'),
        'nonce': b64encode(cipher_config.nonce).decode('utf-8'),
        'tag': b64encode(tag).decode('utf-8')
    }
```

## Notes on encrypt() function

1. [Nonce](https://en.wikipedia.org/wiki/Cryptographic_nonce): A random nonce (arbitrary value) must be a random and unique value for each time our encryption function is used with the same key. Think of it as a random salt for a cipher. The library supplies us with a secure nonce.
2. [Scrypt](https://qvault.io/2019/06/26/very-basic-intro-to-the-scrypt-hash/): Scrypt is used to generate a secure private key from the password. This will make it harder for an attacker to brute-force our encryption.
3. Salt: A new random salt is used for each run of our encryption. This makes it impossible for an attacker to use precomputed hashes in an attempt to crack the cipher. (see [rainbow table](https://en.wikipedia.org/wiki/Rainbow_table))
4. Scrypt [parameters](https://docs.python.org/3/library/hashlib.html#hashlib.scrypt):
  1. N is the cost factor. It must be a power of two, and the higher it is the more secure the key, but the more resources it requires to run.
  2. R is the block size.
  3. P is the parallelization factor, useful for running on multiple cores.
5. [Base64](https://en.wikipedia.org/wiki/Base64): We encode all of our bytes-type data into base64 for convenient string representation
6. [Tag](https://en.wikipedia.org/wiki/Message_authentication_code): The tag is used to authenticate the data when using AES in GCM mode. This ensures no one can change our data without us knowing about it when we decrypt.

## Decrypting

```
def decrypt(enc_dict, password):
    # decode the dictionary entries from base64
    salt = b64decode(enc_dict['salt'])
    cipher_text = b64decode(enc_dict['cipher_text'])
    nonce = b64decode(enc_dict['nonce'])
    tag = b64decode(enc_dict['tag'])
    

    # generate the private key from the password and salt
    private_key = hashlib.scrypt(
        password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)

    # create the cipher config
    cipher = AES.new(private_key, AES.MODE_GCM, nonce=nonce)

    # decrypt the cipher text
    decrypted = cipher.decrypt_and_verify(cipher_text, tag)

    return decrypted
```

## Notes on decrypt() function

1. The decrypt() function needs the same salt and iv that were used for encryption. We used a dictionary for convenience in parsing, but if we instead wanted one string of ciphertext we could have used a scheme like _salt.iv.tag.cipher\_text_
2. The configuration parameters on the Scrypt and AES functions need to be the same as the encrypt function.

## Give Me The Full Code!

You probably want to see it all work in an example script. Look no further!

```
# AES 256 encryption/decryption using pycryptodome library

from base64 import b64encode, b64decode
import hashlib
from Cryptodome.Cipher import AES
import os
from Cryptodome.Random import get_random_bytes


# pad with spaces at the end of the text
# beacuse AES needs 16 byte blocks
def pad(s):
    block_size = 16
    remainder = len(s) % block_size
    padding_needed = block_size - remainder
    return s + padding_needed * ' '

# remove the extra spaces at the end


def unpad(s):
    return s.rstrip()


def encrypt(plain_text, password):
    # generate a random salt
    salt = get_random_bytes(AES.block_size)

    # use the Scrypt KDF to get a private key from the password
    private_key = hashlib.scrypt(
        password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)

    # create cipher config
    cipher_config = AES.new(private_key, AES.MODE_GCM)

    # return a dictionary with the encrypted text
    cipher_text, tag = cipher_config.encrypt_and_digest(bytes(plain_text, 'utf-8'))
    return {
        'cipher_text': b64encode(cipher_text).decode('utf-8'),
        'salt': b64encode(salt).decode('utf-8'),
        'nonce': b64encode(cipher_config.nonce).decode('utf-8'),
        'tag': b64encode(tag).decode('utf-8')
    }


def decrypt(enc_dict, password):
    # decode the dictionary entries from base64
    salt = b64decode(enc_dict['salt'])
    cipher_text = b64decode(enc_dict['cipher_text'])
    nonce = b64decode(enc_dict['nonce'])
    tag = b64decode(enc_dict['tag'])
    

    # generate the private key from the password and salt
    private_key = hashlib.scrypt(
        password.encode(), salt=salt, n=2**14, r=8, p=1, dklen=32)

    # create the cipher config
    cipher = AES.new(private_key, AES.MODE_GCM, nonce=nonce)

    # decrypt the cipher text
    decrypted = cipher.decrypt_and_verify(cipher_text, tag)

    return decrypted


def main():
    password = input("Password: ")

    # First let us encrypt secret message
    encrypted = encrypt("The secretest message here", password)
    print(encrypted)

    # Let us decrypt using our original password
    decrypted = decrypt(encrypted, password)
    print(bytes.decode(decrypted))

main()
```

## Thanks For Reading

Lane on Twitter: [@wagslane](https://twitter.com/wagslane)

Lane on Dev.to: [wagslane](https://dev.to/wagslane)

Download Qvault: [https://qvault.io](https://qvault.io/)

The post [AES-256 Cipher – Python Cryptography Examples](https://qvault.io/2020/02/06/aes-256-cipher-python-cryptography-examples/) appeared first on [Qvault](https://qvault.io).