2
$\begingroup$

I'm writing an AES file encryption program, and I'd like to put in a way to tell whether or not the user has entered the correct password without decrypting the entire file and GCM telling me the tag is invalid.

My process is as follows:

  • Get the user to enter a password ($p$), generate a salt/nonce/IV ($n$), and use Scrypt to generate 2 keys (The first and second half of the generated key); $$k_1, k_2 = Scrypt(salt:n, keylen:32, n:2^{16}, r:8, p:1).derive(p)$$

  • Encrypt the data of a file with $\text{AES-GCM-128(}k_1, \text{iv/nonce=n})$ and empty associated data.

  • Write encrypted data to the file so that the contents of the file is $n || gcmtag || data$

Would it be secure if I instead wrote the following to the file: $$n || gcmtag || k_2 || data$$

That means I can load $n$ from the file, take the user-inputted password, derive the keys, and check if the value of $k_2$ is equivalent to the $k_2$ loaded from the file.

$\endgroup$
2
$\begingroup$

Your $k_2$ value is functioning effectively the same way as conventional password verification methods, where you store a salted password hash of the users' passwords. So it allows for an adversary to test password guesses, but—

  1. So does the authenticated GCM $(c, tag)$ pair;
  2. The memory hard scrypt function is your main line of defense against this attack anyway.

Alternative to very strongly consider: instead of encrypting the whole file in one GCM encryption call, split it into chunks to be encrypted separately with some construction that protects against reordering, deletion and truncation. Study these examples:

The main reason for this is that way you can encrypt/decrypt very large files with a fixed memory footprint, and yet abort decryption as soon as you hit an inauthentic chunk. And secondarily to this, it also indirectly tackles your problem: if the user enters the wrong password, then decryption will fail on the first block.

Potential downsides are:

  • Encryption produces larger files by some percentage of the plaintext size (and not just a fixed overhead like your proposal);
  • If an encrypted file is adulterated later than the first block you may output a prefix of the plaintext before you abort, which could be a problem in some applications (or not).
| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks but I'm already doing it in chunks. I just didn't mention it in the Q. $\endgroup$ – LittleBlueDragon Jan 29 at 10:37
2
$\begingroup$

You can use a similar method like VeraCrypt. Also, you can use a better Key derivation alternative like Argon2id. Argon2id is the combined version of Argon2d, data-dependent, and Argon2i data-independent modes.

For the encryption

  1. Get/force a good password from user and use it with Argonid with application/usage dependent domain separation $$k = \operatorname{Argon2i}(passwd\|app)$$
  2. Use $k$ for $\operatorname{AES-GCM}$ encryption of the file with a random or counter-based generated nonce;

    $$(c,tag)\operatorname{AES-GCM-128}(k, nonce)$$

  3. Store the $(nonce\mathbin\|c\mathbin\|tag)$
  4. Also encrypt a small text with the $k$ $$check = \operatorname{AES-ENC}(k,\text{"myData"})$$
  5. Store this $check$, too.
  6. Delete the $k$ from the memory.

For the decryption;

  1. Get the password from user and use $k' = Argon2i(passwd\|app)$
  2. Decrypt the $check$ with the $k'$ $$check' = \operatorname{AES-DEC}(k',\text{"myData"})$$
  3. If the plaintext is $\text{"myData"}$ then $k'=k$ ans start to use it, else warn the user.

Other methods;

  • you can test the password like the usual password-based login systems.

  • you can generate a random key for the message and encrypt this key with the key derived from the user's passwords. Prepend the message to check the validity of the password.

Is this correct/incorrect password for file encryption scheme secure?

There is no need to derive $k_2$ since it will be public. And, though it will be secure, you are giving direct testing for the attackers.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Regarding Argon2 being better than scrypt, what's that based on? I know Argon2 won the Password Hashing competition a few years back, but I've also seen a tweet from someone that works on Hashcat who said that in some cases bcrypt is better than Argon2 concluding "So 'Use Argon2' is not a good one-size-fits-all answer". twitter.com/jmgosney/status/1111865772656246786 $\endgroup$ – Swashbuckler Jan 23 at 17:15
  • $\begingroup$ @kelalaka Good point about the direct testing for the attackers, and thanks for the tip about Argon2, but I think I'll stick to Scrypt mainly because the crypto library I'm using doesn't have argon2 implemented - it's a wrapper to openssl. Would, instead of the $k_2$ scheme, encrypting password with CTR, the derived key, and the same nonce-iv and putting that in be better? $\endgroup$ – LittleBlueDragon Jan 23 at 22:12
  • $\begingroup$ @LittleBlueDragon yes, unfortunately, some libraries do not have support yet. Scrypt is still good. Don't use the nonce again. Remember nonce is number used once. Veracrypt actually uses XTS, however, you should be fine with CTR. $\endgroup$ – kelalaka Jan 23 at 22:32
  • $\begingroup$ @Swashbuckler as said in later in the tweets, increasing the iteration will solve the issue. Argon2 is more flexible. Argon2 vs Scrypt $\endgroup$ – kelalaka Jan 23 at 22:37
  • $\begingroup$ @kelalaka Of course... Would there be a security trade-off if I used $k_2$ as a nonce? $\endgroup$ – LittleBlueDragon Jan 24 at 7:38

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.