3

I have some data in a python program that I'd like to encrypt before writing to a file with a password, and then read it and decrypt it before using it. I'm looking for some secure symmetric algorithm that can encrypt and decrypt against a password.

This question shows a non-secure way and suggests using libsodium. Since I'm using Python, I found pysodium. It seems to have tons of functions mapped from libsodium, but I don't know how to simply encrypt/decrypt data against password.

My problem is that it looks like all encryption algorithms use keys. I don't want to use keys. I want to only use a password. Just like what I do in the terminal:

To encrypt:

$ cat data | openssl aes-256-cbc -salt | dd of=output.des3 

To decrypt:

$ dd if=output.des3 | openssl aes-256-cbc -d -salt 

Is it possible to do this with pysodium (in a cross-platform way, so please don't suggest using a system call)?

11
  • 1. Encryption is data based, that means 8-bit bytes, not characters and the keys need to be the correct length. 2. If you want to use a password you need to derive a key from not the usual method is a cryptographic hash and to be secure that should consume ~100ms of CPU time, the method of choice is PBKDF2. The CPU burn is needed because in general passwords are not secure, they are brute forced against lists of frequently used passwords. 3. 3DES, not AES, really? Commented Mar 24, 2018 at 14:54
  • @zaph Thank you for the response. You're right about AES. I'm switching to that. I'm currently researching PBKDF2, too. But meanwhile, why are you making the distinction between characters and bytes? Isn't ascii all we need to solve that problem? Also about rainbow tables... well... isn't that why we use salt? Why are passwords not secure even with salt (and pepper if necessary)? Excuse my ignorance on the matter if I'm asking obvious questions. Am I missing something obvious? Commented Mar 24, 2018 at 15:13
  • 1
    @JamesKPolk Thanks for trying to help... for some reason I keep getting "unsupported version" errors when I run crypto_pwhash... no idea why or how and the lack of documentation is really irritating. I'm doing exactly the opposite of what I wanted to do... I was hoping I could find some one-liner to encrypt my data and a one-liner to decrypt it, but for some reason that doesn't exist. Now I'm tackling the cryptography package of python... maybe that'll do it. Commented Mar 24, 2018 at 16:06
  • 1
    Please post your answer if you solve your problem Commented Mar 24, 2018 at 16:10
  • 1
    @IamTheWalrus I'm considering now using Argon2 to hash my password, then use that as a key. I'm working on it. Commented Mar 24, 2018 at 17:31

1 Answer 1

5

So my question reduced to: "How can I encrypt data against a password in Python". I gave up on pysodium due to the lack of documentation. I used cryptography and argon2 packages to write my own encryption algorithm (it's not my own crypto algorithm, I know Rule No. 1 in crypto; it's just the procedure to utilize what's already there). So here are my functions:

import cryptography.fernet import argon2 import base64 def encrypt_data(data_bytes, password, salt): password_hash = argon2.argon2_hash(password=password, salt=salt) encoded_hash = base64.urlsafe_b64encode(password_hash[:32]) encryptor = cryptography.fernet.Fernet(encoded_hash) return encryptor.encrypt(data_bytes) def decrypt_data(cipher_bytes, password, salt): password_hash = argon2.argon2_hash(password=password, salt=salt) encoded_hash = base64.urlsafe_b64encode(password_hash[:32]) decryptor = cryptography.fernet.Fernet(encoded_hash) return decryptor.decrypt(cipher_bytes) 

And here's an example on how to use them:

cipher = encrypt_data("Hi Dude, Don't tell anyone I said Hi!".encode(), "SecretPassword", "SaltySaltySalt") decrypted = decrypt_data(cipher, "SecretPassword", "SaltySaltySalt") print(cipher) print(decrypted.decode()) 

Remember that encryption is for bytes only; not for strings. This is why I'm using encode/decode.

Why argon2? Because it's a memory hard algorithm that's very hard to break with GPUs and ASICs (yes, I'm a cryptocurrency fan).

Why Fernet? Because it uses AES CBC, which seems to be secure enough; besides, it's really easy to use (which is exactly what I need... I'm not a cryptographer, so I need a black-box to use).

Disclaimer: Please be aware that I'm not a cryptographer. I'm just a programmer. Please feel free to critique my way of encrypting and decrypting, and please feel free to add your contribution to make this better.

Sign up to request clarification or add additional context in comments.

7 Comments

I'm not sure I understand why you are base64 encoding the result from Argon2. I assume the parameter to Fernet is likely to be the AES key, yes? If so, you are passing too many bytes.
@LukeJoshuaPark Fernet doesn't take more than 32 bytes in that function, so I simply cropped that to 32. Fernet takes normally a result of its own generate_key() static method as its key, which I checked and found that it uses b64encode to encode a random number. So all I did, is that I used their same way, except that the input is an Argon2 hash, to ensure reproducibility of the hash instead of generating a random key.
@LukeJoshuaPark I tried this before. It gives errors: Fernet key must be 32 url-safe base64-encoded bytes. ValueError: Fernet key must be 32 url-safe base64-encoded bytes.. I don't know how else to do it.
Ahhh okay. That is odd. Crypto nomrally expects raw bytes... It's odd that this expects a base-64 string. I guess that's Python for you!
@IamTheWalrus trimming hashes is a normal practice in cryptography and has no bad side effects whatsoever, except for the obvious and negligible fact of increasing collision probability from a mathematical point of view. I see this being done all the time.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.