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)?

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • 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? – zaph Mar 24 '18 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? – The Quantum Physicist Mar 24 '18 at 15:13
  • Salt protects against precomputation, i.e. rainbow tables. High iteration counts protect against brute forcing. PBKDF2 is designed to provide protection against both these attacks by including a salt and a tunable iteration count. – President James K. Polk Mar 24 '18 at 15:34
  • @JamesKPolk I see... Thanks for explaining. Digging through this rabbit hole I see that Argon2 is even better. It's very disappointing there are absolutely no examples out there! If you could provide an example, I'd be grateful. – The Quantum Physicist Mar 24 '18 at 15:55
  • Yes, and scrypt is also in that family and I see from the pysodium page you linked that there are methods named `crypto_pwhash` and `crypto_pwhash_scryptsalsa208sha256` which are probably something close to what you want, but I'm not really familiar with them, sorry. – President James K. Polk Mar 24 '18 at 16:02
  • 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. – The Quantum Physicist Mar 24 '18 at 16:06
  • 1
    Please post your answer if you solve your problem – President James K. Polk Mar 24 '18 at 16:10
  • @JamesKPolk Will do. – The Quantum Physicist Mar 24 '18 at 16:17
  • What about https://github.com/dlitz/pycrypto ? The examples there seem easy enough. However as commented above, using a simple password (some of the examples include just that..) instead of a properly generated key will not be as secure. – IamTheWalrus Mar 24 '18 at 16:46
  • 1
    @IamTheWalrus I'm considering now using Argon2 to hash my password, then use that as a key. I'm working on it. – The Quantum Physicist Mar 24 '18 at 17:31
  • @TheQuantumPhysicist 1. The distinction between characters and bytes is because not all byte values can be reperesentred as a character, consider a byte with the value 2. The salt means that each password needs to be tried and that with random salts the same password will have a different value. So the art of brute force is to try different passwords from a [frequent password list](https://github.com/danielmiessler/SecLists/tree/master/Passwords) and the cost of a hash call is very small so we iterate to increase the time a brute force attack takes. – zaph Mar 24 '18 at 20:54

1 Answers1

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.

The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • 1
    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. – Luke Joshua Park Mar 24 '18 at 20:02
  • @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. – The Quantum Physicist Mar 24 '18 at 20:06
  • You are passing more than 32 bytes to `Fernet` because you are base64 encoding the 32 bytes from `password_hash`. I'm pretty sure you should be passing those 32 bytes *without* base64 encoding them first. – Luke Joshua Park Mar 24 '18 at 20:08
  • 1
    @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. – The Quantum Physicist Mar 24 '18 at 20:13
  • 1
    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! – Luke Joshua Park Mar 24 '18 at 20:16
  • I'm not a cryptographer either, but I would feel very uncomfortable to just trim the hash received from argon2. – IamTheWalrus Mar 25 '18 at 04:00
  • 1
    @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. – The Quantum Physicist Mar 25 '18 at 10:01