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 >= 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')