I am currently writing a password manager application. As usual, the passwords are put in a file encrypted using a user-entered password. I'm using the NaCl library but actually it's not really important.

Here is what I do to encrypt the file content with a user-entered password P:

  • First I generate a random salt S of 32 bytes (256 bits) and store it at the start of the encrypted file.
  • Second I derive a secret encryption key K from the password P and salt S using Argon2i.
  • I then compute a hash H of the file content using SHA-512.
  • Then I encrypt and authenticate the hash H with the XSalsa20 and Poly1305 primitive of the NaCl library (crypto_secretbox functions) using key K and nonce 0 and I store the encrypted and authenticated hash in the encrypted file after the salt.
  • Finally, I split the file content into blocks of 1024 bytes, except for the last block which may be less than 1024 bytes. The blocks are encrypted/authenticated and stored in the encrypted file in order, one by one, using the XSalsa20 and Poly1305 primitive. The first block is encrypted with key K and nonce 1, second block with nonce 2, third block with nonce 3, etc...

The file content length is not hidden since the last block is not padded to 1024 bytes but I don't believe it's a problem for a file holding logins and passwords.

Each time a file is encrypted or re-encrypted, for instance after having been modified, a new salt S is generated to prevent the same key K from being used twice.

To decrypt the file, the plaintext salt is used to derive the key again from the password entered by the user, then the hash H and file content are decrypted and authenticated and a hash of the decrypted file content is computed and checked to see if it match the initial hash H.

The reason I'm computing a hash H and encrypting and authenticating it is to prevent the individual encrypted blocks of the file from being added, removed or swapped. If an attacker try to do this, the hash H will not match at decryption. I'm also using block size of 1024 bytes because the NaCl website warns that primitives are only tested with message lengths up to 4096 bytes (https://nacl.cr.yp.to/valid.html). 1024 is completely arbitrary, I could have chosen 2048, 3159 or 4096 but I thought "Better take some margin" and I feel power of 2 are neater.


It's obvious that in a given file, a combination of K and a given nonce is never used twice, since the nonce is incremented each time the key K is used. The same key K is also never used twice since it depends on the salt which is random and changed each time and is therefore never the same. So unless I'm overlooking something, it seems to me that my scheme is not vulnerable to a reused key attack.

However, the NaCl website also warns that "NaCl does not make any promises regarding the resistance of crypto_stream to related-key attacks". Since crypto_stream is XSalsa20, I believe this warning also applies to XSalsa20 + Poly1305. It makes me fear that two secret keys K1 and K2 derived from the same password P using Argon2i and two different salts S1 and S2 are somewhat related, since they come from the same password.

On the other hand, I'm thinking that even if a mathematical relation exists between K1 and K2 it's not a simple direct one. Otherwise we could compute, say Argon2i(P, S1) from Argon2i(P, S2) and that would mean that Argon2 was flawed since the salt would be ineffective.

Does two keys K1 and K2 derived from the same password P but from two different salts using Argon2i are vulnerable to a related key attack ? By the way, do you see any other flaws in my file encryption scheme? My main goal is confidentiality but it's a plus if my application can detect files which were tampered with, that is authenticity.

  • $\begingroup$ For clarification: You are generating a new salt everytime you re-save your file? $\endgroup$
    – mat
    Aug 9, 2019 at 10:26
  • $\begingroup$ @mat, Yes that what I wanted to say by "Each time a file is encrypted [...] key K from being used twice". I'm not sure about what is unclear in the sentence and how I can clarify it, though. Now that I think about it, it's not necessary if the file content hasn't changed but in this case it's not even necessary to save the file again since it's the same. $\endgroup$
    – Zaphod
    Aug 9, 2019 at 14:12

1 Answer 1


Unless the key derivation function you use is specifically designed to exploit related-key attacks, then no, it will not result in a vulnerable related-key pair. It is true that XSalsa20 is not designed with related-key attack resistance in mind, but a key put through any decent KDF will not lead to exploitable weakness.

This answer explains the kind of effort that can go into creating a pair of related keys. Suffice it to say, there is absolutely no reason to worry that a KDF will produce such keys. The only other kind of attacks occur for certain ciphers when keys are extremely similar. An example of this is RC4, where encrypting known-plaintext with multiple keys that differ in only a few bits can lead to complete key recovery.

Note: I have not analyzed your overall password management scheme, just your direct question about related keys.

  • $\begingroup$ Just to be sure, is Argon2i a "decent" KDF for my purpose ? $\endgroup$
    – Zaphod
    Sep 12, 2019 at 15:32
  • 1
    $\begingroup$ @Zaphod I would use Argon2id (the hybrid version). But Argon2, yes. $\endgroup$
    – forest
    Sep 13, 2019 at 11:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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