3

For the purposes of learning, I'm writing a small utility that allows encrypting a list of passwords using a single master key (using AES-256 and PBKDF2)

Before encrypting or decrypting any password user has to enter the master key (which will be used to derive a key for AES).

I would like to know what is the best option to know if a user has entered a valid master key :

  1. To salt and then hash the master key (once). The resulting salt and hash are stored somewhere - for instance in a DB. Later on, when the user has entered his master key, I add the stored salt and compare the computed hash with hash stored in DB.

  2. To generate a salt. The salt is encrypted using the master key. I store both the salt and the encrypted salt in a DB. Later on, when the user enters the master key, I try to decrypt the encrypted salt value with the master key and compare it with the salt stored in DB.

Maarten Bodewes
  • 92,551
  • 13
  • 161
  • 313
tigrou
  • 155
  • 5
  • For AES key : I generate a salt (which will be included in the encrypted password) and use PBKDF2 / HMAC SHA1 with 1000 iterations : https://nodejs.org/api/crypto.html#crypto_crypto_pbkdf2_password_salt_iterations_keylen_digest_callback – tigrou Dec 19 '18 at 18:42
  • I was thinking of a hash created using SHA-256 directly not PBKDF2. AFAIK it's probably more secure to use PBKDF2 with a given number of iterations (eg : 1000) as it's slower – tigrou Dec 19 '18 at 18:54
  • 2
    Am I understanding things right, that you essentially want a definitive and secure way to check whether the user has entered the correct password before trying to decrypt the data? – SEJPM Dec 19 '18 at 19:18
  • @SEJPM : no only for decryption but also for encryption. If you encrypt a password with the wrong master password (by mistyping), you will never know (no error) and you will never be able to retrieve that encrypted password (I mean to decrypt it) later. Decryption is usually fine since if you try to decrypt with wrong AES key you will usually get an error during unpadding (eg : using PKCS#7) – tigrou Dec 19 '18 at 20:05
  • 1
    It would be much easier if you could formalize the cryptographic scheme you are proposing, like kelalaka tried to do in his answer using the $\TeX$ formulas. Given just text, we cannot be sure if you confuse passwords and keys and such, to name just one issue. – Maarten Bodewes Dec 19 '18 at 23:45
  • 1
    Note that 1000 iterations was the suggested number of iterations for PBKDF2 when it was introduced. You would expect a lot more iterations nowadays, say 40K to 100K. – Maarten Bodewes Dec 19 '18 at 23:47

1 Answers1

3

Let call a user's Master Key for AES as $\mathit{MK}_u$ and generated as;

$$\mathit{MK}_u = \operatorname{PBKDF2}(\mathit{passwd}_u, \mathit{salt}, \mathit{Iteration})$$

where $\mathit{Iteration}$ between 40K to 100K

1. To salt and then hash the master key (once). The resulting salt and hash are stored somewhere - for instance in a DB. Later on, when the user has entered his master key, I add the stored salt and compare the computed hash with hash stored in DB.

$$\mathit{hashed} = \operatorname{SHA-256}(\mathit{salt} \mathbin\| \mathit{MK}_u)$$

A passive attacker can look at the DB and;

  • Password search on $\mathit{MK}_u$ with hashcat.

A better solution is also using;

  • a pepper which is stored in the application server: this totally prevents the passive DB attacker $$\mathit{hashed} = \operatorname{SHA-256}(\mathit{salt} \mathbin\| \mathit{MK}_u \mathbin\| \mathit{pepper})$$
  • Key-Stretching as PBKDF2 or Argon2: this also prevents and make it harder even pepper is accessed from the applications server.

$$\mathit{hashed} = \operatorname{PBKDF2}(\mathit{MK}_u, \mathit{salt} \mathbin\| \mathit{pepper}, \mathit{Iteration})$$

2. To generate a salt. The salt is encrypted using the master key. I store both the salt and the encrypted salt in a DB. Later on, when the user enters the master key, I try to decrypt the encrypted salt value with the master key and compare it with the salt stored in DB.

$$\mathit{salt}_e = E_{\mathit{MK}_u}(\mathit{salt}),$$ and store $\mathit{salt}$ and $\mathit{salt}_e$ on DB.

For the second approach, the attacker can execute a password search for $\mathit{MK}_u$, this is almost the same as the attack shown against the #1 case since the attacker only deals with one AES encryption. To increase the iteration, one can modify this as in bcrypt.

The modified #2 approach is the winner due to the pepper in the passive DB attacker model.

Squeamish Ossifrage
  • 48,392
  • 3
  • 116
  • 223
kelalaka
  • 48,443
  • 11
  • 116
  • 196
  • 2
    Actually you don't need the additional stretching on $MK_u$ and you want to use the computational resources there up-front on the initial derivation. And actually using HKDF instead of this ad-hoc SHA-256 based construction would probably be "more standard" these days... – SEJPM Dec 29 '18 at 23:09