I'm creating a password vault, and I plan to use Argon2id to derive the master key from the master password. For encryption, I plan to use XChaCha20 with Poly1305.
To be clear: a set of multiple passwords and usernames will be encrypted using XChaCha20 and authenticated using Poly1305, using (a random 192-bit IV and) a 256-bit key derived from a master password using Argon2id (the version that tries to both resist GPU cracking attacks and side-channel attacks) (using 256-bit secure random salt; although I might actually use a 128-bit salt because that should also be plenty). (If there are any issues with this setup, such as using the id version of Argon2 or using XChaCha20, also tell me, but I think these should be suitable.) Eventually, I'd like to make the password vault usable on both desktop(/laptop) and mobile devices.
Now I want to determine good secure-but-not-overkill default parameters to use for Argon2id that are suited for both modern mobile phones and standard computers. The parameters to choose are the following:
- Time cost (number of iterations)
- Memory cost (number of KiBs to use)
- Parallelism (number of threads to use; also influences the output)
The Argon2 draft RFC v12 tells us first to choose the maximum parallelism and memory cost you can afford and increase the time cost until the function takes longer than you can afford. (EDIT 2021: apparently, v13 now recommends parallelism of 4.)
Another page references multiple sources, such as the libsodium documentation, which has multiple recommendations:
- "For interactive, online operations,
crypto_pwhash_OPSLIMIT_INTERACTIVE
andcrypto_pwhash_MEMLIMIT_INTERACTIVE
provide base line for these two parameters"- Currently this means 2 iterations and 64 MiB RAM (see crypto_pwhash_argon2id.h)
- "Alternatively,
crypto_pwhash_OPSLIMIT_MODERATE
andcrypto_pwhash_MEMLIMIT_MODERATE
can be used"- Currently 3 iterations and 256 MiB RAM
- "For highly sensitive data and non-interactive operations,
crypto_pwhash_OPSLIMIT_SENSITIVE
andcrypto_pwhash_MEMLIMIT_SENSITIVE
can be used"- Currently 4 iterations and 1 GiB RAM
(In all cases just one thread is used.)
A quick benchmark on my laptop tells me that the function just takes 3.9 seconds with the last set of parameters (using the original Argon2id implementation), which I think is more than acceptable because I prefer having a secure system. However, on my phone this takes 8 seconds (using argon2kt), which is a bit longer than I'd prefer, not to mention that 1 GiB is quite a lot of memory.
For reference I took a look at the source code of password vault KeePass, and it seems they use Argon2d (so less side-channel protection) with 2 iterations with just 1 MiB of memory (but 2 threads) by default: [excerpt from KeePassLib.Cryptography.KeyDerivation.Argon2Kdf
:]
internal const ulong DefaultIterations = 2;
internal const ulong DefaultMemory = 1024 * 1024; // 1 MB
internal const uint DefaultParallelism = 2;
Is it just me, or is that a bit on the insecure side?
Also, what role does the parallelism parameter play in countering attacks? Is this not as important, as libsodium just keeps it at 1?
In conclusion, I think this is a bit vague, and I'd like to have some minimum secure values for the 3 parameters because what if I take small parameters since I have a slow phone while the attacker has a bunch of GPUs? Then my vault would be utterly useless. Also, I imagine one can go overboard and choose values way larger than necessary. Is there some limit after which directly cracking the symmetric key is faster than guessing passwords?