Boot.dev
Published in

Boot.dev

AES-256 Cipher — Python Cryptography Examples

By Lane Wagner — @wagslane on Twitter

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)

Dependencies

For this tutorial, we will be using Python 3. Make sure you install pycrypto, which will give us access to an implementation of AES-256:

pip install pycrypto

Padding

AES-256 requires that the data to be encrypted is supplied in 16-byte blocks. We will naively add spaces to the end of our ciphertext to satisfy that requirement:

# 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 * ' '

We will also create an unpad() function that strips the extra spaces off after decryption:

# remove the extra spaces at the end
def unpad(s):
return s.rstrip()

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 = os.urandom(AES.block_size)

# generate a random iv
iv = Random.new().read(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)

# pad text with spaces to be valid for AES CBC mode
padded_text = pad(plain_text)

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

# return a dictionary with the encrypted text
return {
'cipher_text': base64.b64encode(cipher_config.encrypt(padded_text)),
'salt': base64.b64encode(salt),
'iv': base64.b64encode(iv)
}

Notes on encrypt() function

  1. IV: Initialization vector. The initialization vector must be random and new for each time our encryption function is used. Think of it as a random salt for a cipher.
  2. Scrypt: 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)
  4. Scrypt parameters:
  5. 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.
  6. R is the block size.
  7. P is the parallelization factor, useful for running on multiple cores.
  8. Base64: We encode all of our bytes-type data into base64 for convenient string representation

Decrypting

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

# 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_CBC, iv)

# decrypt the cipher text
decrypted = cipher.decrypt(enc)

# unpad the text to remove the added spaces
original = unpad(decrypted)

return original

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.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 pycrypto library

import base64
import hashlib
from Crypto.Cipher import AES
from Crypto import Random
import os


# 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 = os.urandom(AES.block_size)

# generate a random iv
iv = Random.new().read(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)

# pad text with spaces to be valid for AES CBC mode
padded_text = pad(plain_text)

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

# return a dictionary with the encrypted text
return {
'cipher_text': base64.b64encode(cipher_config.encrypt(padded_text)),
'salt': base64.b64encode(salt),
'iv': base64.b64encode(iv)
}


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

# 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_CBC, iv)

# decrypt the cipher text
decrypted = cipher.decrypt(enc)

# unpad the text to remove the added spaces
original = unpad(decrypted)

return original

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

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

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

Thanks For Reading

Lane on Twitter: @wagslane

Lane on Dev.to: wagslane

Download Qvault: https://qvault.io

The post AES-256 Cipher — Python Cryptography Examples appeared first on Qvault.

--

--

--

New computer science and coding articles every week.

Recommended from Medium

Privacy is about context not information

How AEC & Design Firms can protect their CAD drawings from Ransomware Attacks

HyperPay Weekly Report 03/29–04/04

Daily links of Fernand0 — Enlaces diarios de Fernand0 — Issue #441

HyperPay Creative Video Contest, Win New Year Gifts

LetsDefend- SOC 173- Follina 0-Day detected // Microsoft Windows Support Diagnostic Tool (MSDT)…

How to visit the pub while retaining your privacy

Automated SFTP with PGP encryption and decryption

cloud managed file transfer pgp

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Lane Wagner

Lane Wagner

I love Go and Rust, and I like JavaScript and Python. I’m indiehacking on https://boot.dev when I’m not with my wife and daughter.

More from Medium

A walkthrough to django models — part 1

[Python] pysftp.Connection() “Authentication failed.” with private key

1-Introduction to Python

Creating a basic Discord bot in Python