The entire point of a key-derivation function like Argon2 is to increase the time (difficulty) it takes to create a key, and as a side effect, increase the resources required to attack the key.
The problem with other key functions like PBKDF2 is that you can only set the total iterations required, while this is fine for many applications, it isn't ideal for password hashing. PBKDF2 can be easily hashed quicker by using specific equipment. A graphics card is designed to be able to perform repetitive actions relatively fast. This makes hashing an easy task to allow a GPU to quickly power through. However, while GPUs have access to many logical processors, they are quite limited on access to fast memory.
Argon2 is designed with this in mind, allowing three different parameters for it to be enabled best with your hardware. As such it comes with three different adjustable factors (called cost):
- Iterations: A total time cost, requiring Argon2 to run a certain amount of time before the output is received, allowing you to adjust how long it takes for an output to generate.
- Memory: A system resource cost. Memory is specified as a set amount required for each running instance of the hash function. Unlike iterations, memory is not something that can be optimised for, it's a physical limit that needs a certain sum of physical memory (often RAM) available, and free for hashing. Unless a high-speed storage density revolution occurs in the future, this is going to be one of your hardest hitting costs
- Parallelism: The total sum of threads which will run at once in order to generate an output. If you have a large quantity of logical processors available, increasing this cost requires an attacker to either process less congruent attacks at a time, or purchase more expensive equipment.
(It is also important to note that there are several different inputs other than those listed, but those are mostly for low-level API usage of the Argon2lib and can be found here)
A quick guide for configuring Argon2 costs:
- Set your Memory cost as high as you can comfortably support. If you at most expect to have 10 users log in at the same time on a dedicated server and have a total of 20 GBs of memory available, issue a total of 1.5 GBs per function run. Also, always remember to be scaling friendly: if you have 20Gbs of memory, and only 10 users login at any given time currently, but you expect to have over a hundred at a time in the future, set your parameters to support such an event.
- Set your parallelism in accordance to what your hardware can support. I have a 4 core computer, as such, I set KeePass's parallelism to 4 threads, your needs will vary depending on total logins per second and what your hardware can do.
- Set your iterations to a reasonable time, depending on your application. Start with a low value like 10, run your function, and see how long it takes to receive your output, if it feels too fast, increase your iterations. Continue to repeat this process until well-tuned. You will want to set your iterations dependent on your application's needs. If you are performing password hashes for clients on your web service, you may want to aim for around 0.2 seconds per hash. However, if you are doing client-side hashing for local storage encryption, you may want to aim for a couple of seconds as you will likely only ever expect to run one function at a time and run it very few times a day.
Hope this helps and I explained things well for you!