0

I'm building a small command line tool (mostly for fun) that is meant to store data (such as passwords or files) encrypted into a local database, with a user supplied password, pretty much the same functionallity as KeePass.

I'm looking for some review to what I'm doing to confirm I'm not leaving any security hole and I'm using known algorithms correctly or if there is too much redundancy.

It's also important how resillient the data is against brute force.

The process is as follows:

Encryption:

  • User provides a password and data

  • Two 32 bytes key are derived from the user password (call it d_key and s_key), using PBKDF2 with a 16 bytes RNG (os urandom) salt (different salt for each key), and 10000 iterations (configurable)

  • Data is encrypted using AES CTR with d_key, and a signature is calculated from the ciphertext using HMAC sha512 with s_key

  • Both ciphertext and hmac signatures are stored in the database, along with the used salts and iteration count.

Decryption:

  • User enters his password and id of the data he want's to retrieve

  • d_key and s_key are generated using the existing stored salts and iteration count.

  • Data is loaded decrypted using d_key

  • HMAC is re-calculated from the encrypted data, using s_key and compared with the one stored to confirm data wasn't modified and key is valid.

  • All good, data returned to the user.

Now my main question is, HMAC seems redundant, but seems like the only good way to check that the key is valid, otherwise a hash of the plain text data would need to be compared which would be very easy to reverse engineer / brute force.

Also, do I really need two separate keys for AES and HMAC? Even if using a salt?

Lastly, does PBKDF2 really help? Or would it be the same to simply use a hash function to derive the key? Since the key is not stored anywhere, it seems redudant as well, although I would guess that if the key is not salted and iterated brute could be easier.

  • I don't see a real need for anything except the PBKDF cycle. a derived hash is not "very easy" to break. The rest of the setup is just obscurity. just compare the stored hash to a re-calc'd hash upon user input. there's not even a need for salt with just one user... – dandavis Feb 6 '18 at 4:09
  • Since I'm not encrypting the whole database but rather just the sensitive fields, if the user stores passwords, wouldn't using a simple hash (ie sha256) be "easy" to break? Pretty much defeats the purpose of encryption since someone with access to the database would just use a precomputed table in order to find out what passwords are stored, he wouldn't even need to break the AES algorithm, this is the main reason for using HMAC with the encrypted data (could be over the plain text data aswell) – Cristiano Coelho Feb 6 '18 at 13:07
  • The salt is mainly to get two different keys for AES and HMAC. Also if each row of the database is encrypted with a different key, wouldn't it help as well against pre-computed tables? Again, all the above is because each row is encrypted individually so it actually looks like the "more than 1 user storing password hashes" scenario – Cristiano Coelho Feb 6 '18 at 13:21
  • sha256 != pbkdf/sha256. no need to worry about rainbows with derivation. you can use a static salt, you just don't need a unique salt per password with a small number (1?) of passwords. – dandavis Feb 6 '18 at 20:08
  • Sorry, the idea is to store more than 1 password, perhaps hundreds. – Cristiano Coelho Feb 6 '18 at 23:49
1

Now my main question is, HMAC seems redundant, but seems like the only good way to check that the key is valid, otherwise a hash of the plain text data would need to be compared which would be very easy to reverse engineer / brute force.

HMAC is important. AES CTR encryption gives you confidentiality. But that is not enough. You also need an integrity check, which HMAC can provide. Look up Authenticated Encryption for details.

Getting the implementation right can be tricky so I recommend using AES GCM, which has the integrity check built-in.

But since you're rolling your own integrity check...

Also, do I really need two separate keys for AES and HMAC?

Yes, but easy to do. Churn the same password through PBKDF2 but with a different iteration count (say 20000). What's interesting about this approach is you could use the key derived for HMAC to authenticate the user, i.e. if the HMAC check fails, don't try decrypting the ciphertext - just return an error stating password was wrong. Although a simplification, this is how password managers work in a nutshell - same password is used for authentication as well as encryption.

Lastly, does PBKDF2 really help?

Yes, to mitigate against brute force attacks by:

  1. Intentionally making PBKDF2 an expensive operation, especially with higher iterations.
  2. Expanding the password to take up the entire 128 or 256 bit space of AES. Otherwise you'd need ridiculously long passwords.
  • Sadly the AES library I'm using (a pure python implementation with no C dependencies) doesn't support AES GCM – Cristiano Coelho Feb 15 '18 at 0:47
  • " Churn the same password through PBKDF2 but with a different iteration count (say 20000)". Is it the same to use 2 different salts instead? Are there any differences at all? – Cristiano Coelho Mar 21 '18 at 0:41
  • The intention of using PBKDF2 with different iteration count is two fold: (1) to generate different key - one for authentication, one for encryption, and (2) to ensure the key generated for authentication is intentionally an expensive operation. Using two different salts will get you two different keys only, but not make the operation for authentication more expensive. – HTLee Mar 21 '18 at 1:18

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.