Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Questions: Does the hashing time scale by RAM size? #235

Open
smiled0g opened this issue Dec 10, 2017 · 12 comments
Open

Questions: Does the hashing time scale by RAM size? #235

smiled0g opened this issue Dec 10, 2017 · 12 comments

Comments

@smiled0g
Copy link

For example: does hashing on 64 GB ram take 4 times faster comparing on 16 GB ram?

Thanks :)

@WOnder93
Copy link
Contributor

It scales more-or-less linearly, the hashing time is (very) roughly C * t_cost * m_cost / threads, where C is some constant (so it is not faster, instead it takes longer).

Also, note that when you allocate a large chunk of memory, the OS usually only maps the first couple of pages to actual physical memory. When you start writing beyond those pages, the CPU will generate page faults and trigger the kernel's page fault handler. This handler then assigns more physical pages to the region you were trying to write and switches back to your application. This happens many times in a row when you first write to a big freshly allocated chunk of memory. For Argon2, it causes an additional slowdown in the first pass, so the actual scaling is not really linear when you try to measure it.

I tried once to curve-fit the dependency of hashing time on memory cost, but it didn't fit even to simple polynomial curves, so I guess it is really not easy to predict precisely (and it will heavily depend on the system and configuration used).

@Leont
Copy link
Contributor

Leont commented Dec 11, 2017

Also, note that when you allocate a large chunk of memory, the OS usually only maps the first couple of pages to actual physical memory. When you start writing beyond those pages, the CPU will generate page faults and trigger the kernel's page fault handler. This handler then assigns more physical pages to the region you were trying to write and switches back to your application. This happens many times in a row when you first write to a big freshly allocated chunk of memory. For Argon2, it causes an additional slowdown in the first pass, so the actual scaling is not really linear when you try to measure it.

Using madvice might help here.

@jedisct1
Copy link
Contributor

Using madvice might help here.

It won't. But MAP_POPULATE will.

@My1
Copy link

My1 commented Jan 9, 2018

on this issue a question: what would be better? more time by using a higher time parameter or just go for more ram (and have it take longer as side effect).

Libsodium apparently makes argon2i have a minimum time factor of 3, where I am honestly not sure whether that's a sensible choice when one could instead lower the time down to one and for for much more RAM instead.

@SparkDustJoe
Copy link

SparkDustJoe commented Jan 9, 2018 via email

@My1
Copy link

My1 commented Jan 10, 2018

okay, this is kinda sad. would have been awesome if the time/iterations would be only helpful (for example on a system where you have more CPU than RAM) but not needed, so that on systems with an overabundance of RAM you use a lot of RAM which makes the time naturally grow enough already and not cope with any artificial time increases.

for example 1GB of RAM takes on a iterations of 1 (via PHP) already 1,5sec on my dev machine. while a server may be more powerful, it also has to cope with multiple sign ins at once, so to avoid an accidential DoS I usually tune around 0,1-0,5 sec for one single login, so that the chance of way too many people logging in right in that time space goes down and even if, that there wont be too many issues.

@jedisct1
Copy link
Contributor

If you are using PHP's password_hash() function, I'd suggest using the sodium_pwhash() function instead, that is likely to be faster.

@SparkDustJoe
Copy link

SparkDustJoe commented Jan 10, 2018 via email

@My1
Copy link

My1 commented Jan 10, 2018

no client involved, the server does everything, and since the hash is stored in a format where all factors are known this is not a problem for verifying

@waywardgeek
Copy link

Here's my advice:

  1. Use Argon2id, not Argon2i*, and never Argon2d.
  2. Use t_cost = 1.
  3. Use as many threads as your application can afford to allocate to Argon2id.
  4. Use as much memory as you have time to fill.
  5. Keep the salt secret, just like the password hash.
  • Caveat: Use Argon2i if your hash runs on a system where the attacker has an oscilloscope on the power rails and an RF detector hovering over your CPU, yet cannot simply read data from your DRAM. This is rare.

tl; dr;

IMO, Argon2i is too dangerous for most applications. It has had a couple of bad compromises already, and I suspect there will be more. If you want confidence your password hash will be viable two years from now, use Argon2id. The 'd' part means password-dependent hashing, like Scrypt uses, which runs in the second half of the algorithm. The attacks we're seeing against Argon2i keep on coming, but they don't work against Argon2id. For some reason academics have a strong preference for the ideal rather than the practical, and Argon2i is more "ideal" in that it has far less chance of leaking information about your password through side-channels like cache-timing. However, from a practical point of view, Argon2i is still an academic exercise: it is not ready for prime time. You take a big risk by using it.

Also, if you do choose Argon2i, choose t_cost >= 3 (reducing ASIC attacker's cost by >= 3X vs defender.

The only downside to Argon2id is, like Scrypt, in the second half of execution password-dependent side channels may be visible. If you keep the salt secret, cache-timing can only leak meta-data such as "the same person just logged in again,

So, just use Argon2id, use t_cost = 1, and let it run for as long as your application can stand. It really is simple to choose these parameters.

@josephlr
Copy link
Contributor

For an example of @waywardgeek's advice, fscrypt has to search for optimal Argon2id hashing costs when a user initially creates /etc/fscrypt.conf.

Search function: getHashingCosts
Timing function: timeHashingCosts
Hashing function: PassphraseHash

@My1
Copy link

My1 commented Jan 11, 2018

@waywardgeek
Not bad. I just hope the guys at php will include a2id in the password_hash function. I opened a request long ago with no response so far.

But a2i already has that much problems? Iirc the main issue of a2i was time memory tradeoffs, where one could use less memory by instead calculating the needed stuff as needed, which instead raises the calculations by a lot.

But if a2i should really use a time parameter of 3 i guess they should go and change the default (which iirc was 2)

I know the basic differences between i, d and id, d is dependent, which axes down tmto but shows side channels, i is independent, meaning no side channels but instead tmto and id is a nice mix of both lowering the risk of both problems.

@josephlr
I also wrote myself a tool where i enter the maximum memory i can afford, the threadcount and of course the target time (including a tolerance so it wont just bounce up and down all the time because it is either too slow or too fast) all of course for php and password hashing for websites.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants