I have been looking for a proper data encryption library in python for a long while, today I needed it once again, cannot find anything, so is there any way to encrypt data using a user-set password, if I find something it’s usually insecure, if I find a good solution it has no support for user-set passwords, meaning I’m stuck, any way to do it?
Here’s some pseudocode:
import encryption encryptor: encryption.Crypt = encryption.Crypt("my secret password") encryptor.encrypt("hello this is my very secret string") # => 9oe gyu yp9q*(Y 28j encryptor.decrypt("9oe gyu yp9q*(Y 28j") # => hello this is my very secret string
I don’t care if it’s an object, for all I care it can also be a function which accepts the password:
import encryption encryption.encrypt("hello this is my very secret string", "my secret password") # => 9oe gyu yp9q*(Y 28j encryption.decrypt("9oe gyu yp9q*(Y 28j", "my secret password") # => hello this is my very secret string
I don’t mind the way it’s encrypted or decrypted, I just want to have a way to do it :), I also don’t care abt it’s output, it can be binary, an object, a string, anything
Advertisement
Answer
Building on the answer from Sam Hartzog, below is an example which follows the logic described for PBES2 (Password Based Encryption Scheme 2) defined in RFC8018, Section 6.2. However, it stops short of encoding algorithm choices and parameters.
#!/usr/bin/python import base64 import secrets from cryptography.fernet import Fernet from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC KDF_ALGORITHM = hashes.SHA256() KDF_LENGTH = 32 KDF_ITERATIONS = 120000 def encrypt(plaintext: str, password: str) -> (bytes, bytes): # Derive a symmetric key using the passsword and a fresh random salt. salt = secrets.token_bytes(16) kdf = PBKDF2HMAC( algorithm=KDF_ALGORITHM, length=KDF_LENGTH, salt=salt, iterations=KDF_ITERATIONS) key = kdf.derive(password.encode("utf-8")) # Encrypt the message. f = Fernet(base64.urlsafe_b64encode(key)) ciphertext = f.encrypt(plaintext.encode("utf-8")) return ciphertext, salt def decrypt(ciphertext: bytes, password: str, salt: bytes) -> str: # Derive the symmetric key using the password and provided salt. kdf = PBKDF2HMAC( algorithm=KDF_ALGORITHM, length=KDF_LENGTH, salt=salt, iterations=KDF_ITERATIONS) key = kdf.derive(password.encode("utf-8")) # Decrypt the message f = Fernet(base64.urlsafe_b64encode(key)) plaintext = f.decrypt(ciphertext) return plaintext.decode("utf-8") def main(): password = "aStrongPassword" message = "a secret message" encrypted, salt = encrypt(message, password) decrypted = decrypt(encrypted, password, salt) print(f"message: {message}") print(f"encrypted: {encrypted}") print(f"decrypted: {decrypted}")
Output:
message: a secret message encrypted: b'gAAAAABjDlH2eaRZmB4rduBdNHUOITV5q4oelpnLRUgI_uyQyNpUyW8h3c2lZYS1MwMpRWIZposcZvag9si1pc4IEK83_CzyBdXF27Aop9WWS6ybxTg9BSo=' decrypted: a secret message