When it comes to writing code that portably encrypts and decrypts without concern for the platform, you’ll have to overcome a couple of small factors with Python:
- You’ll have to install a cryptography library
- You’ll need code that doesn’t need a build environment (no C-code)
I personally prefer Rijndael (also called “AES” when you constrain yourself to certain key-sizes and/or block-sizes) as an affordable, ubiquitous algorithm. There was a remarkable amount of difficulty in finding a pure-Python algorithm that worked in both Python 2 and Python 3.
By mentioning “proper” encryption in the title, I was referring to “key derivation” (also called “key expansion”). It’s not enough to have a password and an algorithm. Unless you already have a sufficiently random key of exactly the right number of bits, you’ll need to submit the password to a key-expansion algorithm in order to generate a key of the right length. This is often done using the PBKDF2 algorithm.
It was difficult to find a Python 2 and 3 version of PBKDF2 as well.
I’ve posted the pprp package, which packages Python 2 and Python 3 versions of Rijndael and PBKDF2 and automatically chooses the right version during module-load. As I implement PKCS7 for padding, I also include a utility function to trim the padding.
The main documentation provides the following as an example:
import io
import os.path
import hashlib
import pprp
def trans(text):
return text.encode('ASCII') if sys.version_info[0] >= 3 else text
passphrase = trans('password')
salt = trans('salt')
block_size = 16
key_size = 32
data = "this is a test" * 100
key = pprp.pbkdf2(passphrase, salt, key_size)
def source_gen():
for i in range(0, len(data), block_size):
block = data[i:i + block_size]
len_ = len(block)
if len_ > 0:
yield block.encode('ASCII')
if len_ < block_size:
break
# Pump the encrypter output into the decrypter.
encrypted_gen = pprp.rjindael_encrypt_gen(key, source_gen(), block_size)
# Run, and sink the output into an IO stream. Trim the padding off the last
# block.
s = io.BytesIO()
ends_at = 0
for block in pprp.rjindael_decrypt_gen(key, encrypted_gen, block_size):
ends_at += block_size
if ends_at >= len(data):
block = pprp.trim_pkcs7_padding(block)
s.write(block)
decrypted = s.getvalue()
assert data == decrypted.decode('ASCII')