Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I correctly adjust the Argon2 parameters in Go to consume less memory?

Argon2 by design is memory hungry. In the semi-official Go implementation, the following parameters are recommended when using IDKey:

key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)

where 1 is the time parameter and 64*1024 is the memory parameter. This means the library will create a 64MB buffer when hashing a value. In scenarios where many hashing procedures might run at the same time this creates high pressure on the host memory.

In cases where this is too much memory consumption it is advised to decrease the memory parameter and increase the time factor:

The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number. If using that amount of memory (64 MB) is not possible in some contexts then the time parameter can be increased to compensate.


So, assuming I would like to limit memory consumption to 16MB (1/4 of the recommended 64MB), it is still unclear to me how I should be adjusting the time parameter: is this supposed to be times 4 so that the product of memory and time stays the same? Or is there some other logic behind the correlation of time and memory at play?

like image 491
m90 Avatar asked Jul 10 '20 12:07

m90


People also ask

How do you choose Argon2 parameters?

Choosing ParametersIncrease the number of iterations until you reach your maximum runtime limit (for example, 500ms). If you're already exceeding the your maximum runtime limit with the number of iterations = 1, then you should reduce the memory parameter.

Does Argon2 need salt?

The salt is technically not required, but neither is Argon2, so yeah...

How long is an Argon2 hash?

The hash size is 32 bytes. The default parameters for the algorithm are a memory_cost of 1024 Kb (1 Mb), a time_cost of 2, and two threads to be used for parallelism.

Why is Argon2 good?

​Argon2 is modern ASIC-resistant and GPU-resistant secure key derivation function. It has better password cracking resistance (when configured correctly) than PBKDF2, Bcrypt and Scrypt (for similar configuration parameters for CPU and RAM usage).


2 Answers

The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number. If using that amount of memory (64 MB) is not possible in some contexts then the time parameter can be increased to compensate.

I think the key here is the word "to compensate", so in this context it is trying to say: to achieve similar hashing complexity as IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32), you can try IDKey([]byte("some password"), salt, 4, 16*1024, 4, 32).
But if you want to decrease hashing result complexity (and decreasing performance overhead), you can decrease the size of memory uint32 disregarding the time parameter.

is this supposed to be times 4 so that the product of memory and time stays the same?

I dont think so, i believe the memory here means the length of result hash, but time parameter could mean "how many times the hashing result needs to be re-hash-ed until i get the end result".

So these 2 parameters are independent of each other. These are just controlling how much "brute- force cost savings due to time-memory tradeoffs" you want to achieve

like image 160
Nikko Khresna Avatar answered Oct 09 '22 01:10

Nikko Khresna


Difficulty is roughly equal to time_cost * memory_cost (and possibly / parallelism). So if you 0.25x the memory cost, you should 4x the time cost. See also this answer.

// The time parameter specifies the number of passes over the memory and the
// memory parameter specifies the size of the memory in KiB.

Check out the Argon2 API itself. I'm going to cross-reference a little bit and use the argon2-cffi documentation. It looks like the go interface uses the C-FFI (foreign function interface) under the hood, so the protoype should be the same.

Parameters
time_cost (int) – Defines the amount of computation realized and therefore the execution time, given in number of iterations.

memory_cost (int) – Defines the memory usage, given in kibibytes.

parallelism (int) – Defines the number of parallel threads (changes the resulting hash value).

hash_len (int) – Length of the hash in bytes.

salt_len (int) – Length of random salt to be generated for each password.

encoding (str) – The Argon2 C library expects bytes. So if hash() or verify() are passed an unicode string, it will be encoded using this encoding.

type (Type) – Argon2 type to use. Only change for interoperability with legacy systems.

Indeed, if we look at the Go docs:

// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
// If using that amount of memory (64 MB) is not possible in some contexts then
// the time parameter can be increased to compensate.
//
// The time parameter specifies the number of passes over the memory and the
// memory parameter specifies the size of the memory in KiB. For example
// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
// adjusted to the numbers of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.

I'm not 100% clear on the impact of thread count, but I believe it does parallelize the hashing, and like any multithreaded job, this reduces the total amount of time taken by approximately 1/N, for N cores. Apparently, you should essentially set the parallelism to cpu count.

like image 1
DeusXMachina Avatar answered Oct 09 '22 02:10

DeusXMachina