-1
$\begingroup$

I want to encrypt some small text files so I can put them online, in non-restricted access (not the best idea, but it's cheap and simple). I know I should use something like Truecrypt instead of this, but I'm looking for something lighter and simple.

import base64
import os
from cryptography.fernet import Fernet
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

from sys import argv


if len(argv)<3:
    exit('please provide a password')
    exit()
# password = b"password"
password = argv[2].encode()
salt = os.urandom(16)
salt = b'\x03>\x8d\xd9x\x8d\xdcR\xc9\x1b-\x9c\x86\xc7\x83\x8c'
kdf = PBKDF2HMAC(
    algorithm=hashes.SHA256(),
    length=32,
    salt=salt,
    iterations=100000,
    backend=default_backend()
)
key = base64.urlsafe_b64encode(kdf.derive(password))
# print(key)
f = Fernet(key)
# print(token)
# print(f.decrypt(token))
# print(salt)

if "encrypt" in argv:
    plain_src = open("plain.nogit.txt", encoding='utf8')
    crypted = open("crypted.nogit.txt",'w')

    token = f.encrypt(plain_src.read().encode())
    plain_src.read().encode()
    # print(repr(token))
    crypted.write(repr(token))
elif "decrypt" in argv:
    plain_src = open("plain.decrypted.nogit.txt",'w', encoding='utf8')
    crypted = open("crypted.nogit.txt")
    token = eval(crypted.read())
    plain_src.write(f.decrypt(token).decode())

    if open("plain.decrypted.nogit.txt", encoding='utf8').read() == open("plain.nogit.txt", encoding='utf8').read():
        print('same content')
    else:
        print('different content')
else:
    exit('provide either 1 or 2 args')

How secure is this? I'm aware that this would leave the password in the shell history (which can be deleted and cleared), but I just want to ask if this the right way to encrypt some simple text data. My goal here is to have best ratio security/simplicity. What would you change? Any advice?

$\endgroup$
  • 2
    $\begingroup$ This code can be made shorter. No need to decryption to identify a weakness. And more importantly, we are not interested in how much secure your code. Rather we are interested in the algorithm itself. In cryptography.Se a small code can be presented to show the algorithm, nothing more. Fernet uses CBC with HMAC. Why not libsodium? $\endgroup$ – kelalaka Jun 25 at 15:20
  • $\begingroup$ I've heard there are many way to misuse cryptography, that's what my question is about $\endgroup$ – jokoon Jun 25 at 16:01
3
$\begingroup$

I see the following security issues:

  • The essential purposely-slow password-to-key transform is PBKDF2-HMAC. That's better than nothing, but among the better-than-nothing that's one of the worse from the standpoint of resistance to hardware-assisted password cracking (using GPUs, FPGAs, or ASICs). Nothing lesser than scrypt or Argon2 is recommendable nowadays.
  • The random salt for PBKDF2 is obliterated by a fixed value. It should be left random, stored in the ciphertext on encryption and taken from that on decryption. Random salt is there for a good reason: prevent password-cracking adversaries from amortizing work among multiple users.
  • The workfactor parameter iterations=100000 is fixed and not stored in the ciphertext. That's not future-open. Also it seems a tad low.

I have no opinion on the security of the implementation of Fernet, which I did not check. However I have some mixed feelings about what I see in the doc:

  • The example for password-to-key transform is awfully dated (and the root cause of much of the above critic). On the other hand, on second read, I notice they do link to and have built-in support for scrypt, a rather satisfactory alternative, +1 for that.
  • The specification states "Signing-key, 128 bits" for what really is a MAC key¹.
  • It is documented a 256-bit (H)MAC, which is needlessly large given the key size.
  • If quantum computers usable for cryptanalysis materialize, choosing 128-bit keys for encryption and MAC will look like a poor choice; OTOH 128-bit is still quite safe in the foreseeable future.
  • Handcrafting authenticated encryption with AES-CBC and HMAC is dated; there are better modes nowadays, like AES-GCM-SIV, or ChaCha20-Poly1305.

Other issues:

  • The program opens the plaintext with encoding='utf8' and thus can't be used for e.g. a jpeg file.
  • Command line parsing is uh, substandard.

¹ I have made / let a similar terminology error slip review long ago, and it still burns me often.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks for the reply. I don't know why I used a fixed salt after using os.urandom(16). Could you comment on os.urandom()? That's one thing I learn what salt is: it's required to be the same at decryption. That's probably the reason I tried a fixed salt... $\endgroup$ – jokoon Jun 25 at 17:35
  • 1
    $\begingroup$ I'll also add that if you're typing cryptography.hazmat you're doing it wrong. Nothing in there should be necessary, either use a better library (like pysodium) or avoid the hazardous parts of it. $\endgroup$ – SAI Peregrinus Jun 25 at 18:54
  • $\begingroup$ os.urandom() is good if the OS is. The question gives no info on the OS, thus there's no more to say. Plus OS changes all the time, and that kills my motivation in analyzing one. $\endgroup$ – fgrieu Jun 25 at 19:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.