2

I'm working on a desktop application where we handle sensitive data and we want to encrypt local files with a password that we ask the user when he opens the application. On most similar project, we see that people store a hash of the password so they can verify that the password is valid before trying to decrypt everything. That exposes a security problem, because the hash can easily be extracted and brute-forced externally.

What we want to do is putting a static text at the beginning of the file to decrypt, And when the user enters the password, the application tries to decrypt that stamp and if it can, it would mean the password is valid and then he could proceed to decrypt the rest of the file.

Is it safe? Since I haven't seen this anywhere, I'm not sure if I overlooked something that could make this system vulnerable.

EDIT:
I wanted to revisit this question since I learned a lot about cryptography since then.
Obviously, the right answer is "Don't do it yourself, use a library for that made from someone that knows what they're doing."

Basically, the question was about how to verify if password was good before trying to decrypt an big file with it. The basic scheme I would recommend today for this is to generate a random key, use it to encrypt the file, and then key-wrap it using a key generated with a strong KDF(Like Argon2, Scrypt, BCrypt or PBKDF2) and an AEAD (like ChaCha20Poly1305, AES-GCM, or if you trust more recent algorithms you can use something like Deoxys-II or Ascon). This way, the password can be changed without re-encrypting the file and your code can check somewhat easily is the password is valid. This is somewhat similar to how KeePass and other password managers do, except they generally use a "regular" cipher + an HMAC instead of an AEAD.

TL; DR: Crypto is complicated, use a library.

zer0x64
  • 23
  • 4
  • Relevant: [How to securely hash passwords?](https://security.stackexchange.com/q/211/2138) – user Jun 29 '17 at 11:06

5 Answers5

2

Try using something like PBKDF2 to turn the password into a key, with which you encrypt the file. PBKDF2 provides salting and iteration configuration to make it more difficult to brute force.

Swashbuckler
  • 2,115
  • 8
  • 9
  • We're already using PKDF, the question was more about validating if the password is right without trying to decrypt the entire file. – zer0x64 Jun 28 '17 at 12:25
2

On most similar project, we see that people store a hash of the password so they can verify that the password is valid before trying to decrypt everything. That exposes a security problem, because the hash can easily be extracted and brute-forced externally.

That is an incorrect solution, but not for the reason you give. The truth is that any encryption scheme where a password is the only secret allows an attacker to make and test password guesses. This issue can only be avoided by requiring something else in addition to a password.

What we want to do is putting a static text at the beginning of the file to decrypt, And when the user enters the password, the application tries to decrypt that stamp and if it can, it would mean the password is valid and then he could proceed to decrypt the rest of the file.

This is flawed because you're trying to use encryption as a check of the validity of the password, but encryption doesn't provide such a guarantee.

The right solution here is fairly complicated, and very easy to get wrong. What can I say, crypto is hard. To encrypt a file, here's an incomplete outline of steps:

  1. Every time you encrypt some data anew, generate a fresh random salt with a cryptographically secure random number generator.
  2. Use a password-based key derivation function like PBKDF2, scrypt or Argon2id to derive encryption keys from the random salt and a password entered by the user.
  3. Use the derived key to encrypt the data with an authenticated encryption algorithm (a.k.a. an AEAD), with the salt as associated data. The AEAD will output an authenticated ciphertext—one that contains an authentication tag that will be checked during decryption.
  4. Write the salt and ciphertext to the file.

To decrypt a file:

  1. Read the salt from the encrypted file.
  2. Ask the user for the password, and use the salt an supplied password to reconstruct the key.
  3. Try to decrypt the ciphertext. Since you're using an AEAD, if the password is incorrect the decryption will fail with an authenticity error.

One way to improve security a bit is to store the salts somewhere else other than the file, where an attacker is unlikely to find. For example the 1Password password manager calls the salt a "secret key" that it stores in the devices separately from the encrypted password vault file. The online sync transfers the ciphertext but the secret key must be copied by hand between devices by the user.

Luis Casillas
  • 10,181
  • 2
  • 27
  • 42
  • Sadly, AEAD is not possible in my specific case, except if I write the algorithm myself. But thanks for the info, I didn't know about them and they're pretty interesting! – zer0x64 Jun 28 '17 at 12:24
1

What you are proposing is the exact thing you wish to avoid.

You are proposing to include a short encrypted passage or static plaintext at the beginning of the text. But... that's exactly what a password hash is for! Whatever process you planned to use to decode the block of text, could be bruteforced in the exact same way as the hash.

But you'd be doing something worse. You would be providing the attacker with a known-plaintext attack, with a very large number of permutations, one for every message encrypted on your system. Why would you do that?

You've just made a very broken poor-man's password hash.

Instead, follow best practices:

  • Pick a secure, slow password hashing system with a far too high number of bits to be crackable in feasible time, or rainbow-table-able.
  • Pick good random salts to prevent identical passwords having identical hashes.
  • Generate a good static pepper and secure your code well, so that simply throwing a dictionary at your password hash won't be enough to crack even the simplest "aaa" password unless they first discover the pepper.

Then, sure. Make that hash be the first thing you encrypt with your block cypher, if it makes you feel safer. Some will argue that this actually weakens it; that's probably not true in most cases, it just doesn't strengthen it much, and wastes a few CPU cycles.

Dewi Morgan
  • 1,340
  • 7
  • 14
  • 1
    The question is about password-based encryption, but your answer is about password storage. Rainbow tables are inapplicable, for example. – Luis Casillas Jun 28 '17 at 02:00
  • 1
    Thank you for the answer. Didn't though about the risk of having a known plaintext/ciphertext pair. – zer0x64 Jun 28 '17 at 12:27
  • @LuisCasillas Read the first paragraph of the question details more carefully. The question is about password verification before decryption, and is asking for an alternative to password hashes. – Dewi Morgan Jun 28 '17 at 14:13
  • 1
    Known plaintext attacks are not an issue for modern ciphers. If it weakened the system at all, that would mean the cipher was broken, not that the system was bad. – Xander Jul 01 '17 at 00:27
1

TL;DR: Use a proper key derivation function instead of some homegrown scheme.


the hash can easily be extracted and brute-forced externally.

I think you are very much underestimating the difficulty of a preimage attack on a modern password hash function.

Is it safe? Since I haven't seen this anywhere, I'm not sure if I overlooked something that could make this system vulnerable.

Done right, what you propose might not be actually vulnerable, but it also probably wouldn't buy you much. Done wrong, as discussed in previous answers, it can become horribly insecure.

Basically, when you think of hashing passwords, don't think just a single round of your average cryptographic hash function such as MD5 or SHA512. Instead, your first choice should be to use a properly salted password-based key derivation function. There are many such available, but the more popular choices currently seem to be PBKDF2 and bcrypt, with scrypt growing in popularity.

Unlike plain hash primitives, key derivation functions are specifically designed to be slow as well as try to give an attacker as little advantage as possible, even where the attacker is able to use custom hardware to attack the password entered by the user, even if that password has relatively low entropy.

You can still store something that indicates whether the password is correct. For example, LUKS does this by storing a salted cryptographic hash of the master key; if the passphrase provided by the user doesn't correctly decrypt any of the key slots, then the decryption of the master key data in the key slot will yield an incorrect result, resulting in a different master key hash, which is detected and acted upon as an "incorrect passphrase" result. Notice in this case that the hash of the passphrase itself is not stored. Compare the LUKS on-disk format, figure 1 PHDR layout.

user
  • 7,670
  • 2
  • 30
  • 54
  • From what I've seen everywhere, what seems to be the most secure is to: 1. Generate a random masterkey. 2. Derive a key from the password and encrypt the masterkey. 3. Put a hash of the masterkey in the header(plain or encrypted?) 4. To verify the password, derive a key from it, decrypt the masterkey, hash it and verify if the hash is equal to the one in the header. 5. All the data is encrypted with the masterkey. That way, the masterkey is random and is pretty much impossible to bruteforce if the blocksize is large enough. Am I missing something? – zer0x64 Jun 28 '17 at 12:22
-1

It depends on how many times you hash the password value. Sure, if you do it once then it will definitely be easier to crack the password.

My recommendation is to hash the password 100,000 times, instead, and prepend that to the encrypted file.

Not only will you be utilizing a much simpler method of authentication, but it can still prove to be secure if the password is hashed many many times.

Alpha
  • 1
  • 2