Skip to content
Advertisement

Proper data encryption with a user-set password in python3

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
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement