50

What are the considerations when picking the best password hashing algorithm in .NET Core?

I read that not all hashing algorithms are compliant / unverified, so I am a hesitant on just getting various implementations from NuGet. Also, it is not recommended to create your own hashing algorithm as verifying it needs some processes / money involved.

Currently, I am using .NET Core Identity V3 password hashing because at least it is something official that Microsoft provides.

I read that the following algorithms are the best:

  1. Argon2
  2. bcrypt
  3. scrypt
  4. Catena
  5. PBKDF2 (what .NET Core Identity V3 uses)

I would like Argon2 or bcrypt ideally. However, I don't want to just use the first thing that Google turns up. Also, how do I know which ones are verified / recommended?

Peter Mortensen
  • 877
  • 5
  • 10
Water
  • 623
  • 1
  • 6
  • 6
  • scrypt is definitely better than bcrypt. bcrypt was made to be hard for ASICS (the main danger at the time), scrypt added memory hard. – Bruno Rohée Sep 02 '19 at 09:13
  • @BrunoRohée bcrypt is actually pretty good against GPUs as well due to use of contended memory. – forest Sep 03 '19 at 01:46
  • 1
    @BrunoRohée To echo what forest said but with a little more emphasis, scrypt is **not** definitely better than bcrypt for some purposes. – Xander Sep 03 '19 at 02:16
  • The best at what? If this is for quick value lookups, then these are all terrible choices. A prefilter for data deduplication? All rubbish! Part of a bloom filter? Worst options ever. These listed aren't even hash functions anyways; they're key stretching algorithms with tunable difficulties that make them great for preventing brute-force confirmation of a plaintext... but absolute trash at the things that hash functions should be used for. (Of course, hash functions are trash at protecting passwords and other plaintexts, so...) – Ghedipunk Sep 03 '19 at 22:26
  • If one of the answers have answered your question, please mark it as solved. :) – forest Jan 21 '21 at 06:58

3 Answers3

42

Argon2 is the best of those to use. It has been well-vetted and is the subject of intense research. It was chosen as the winner in the Password Hashing Competition (PHC) to replace scrypt, which has some nasty time-memory tradeoff (TMTO) attacks, and which is not nearly as flexible in configuration.


Argon2 won the PHC and is based on a thorough analysis of tradeoff attacks. It requires a configurable amount of memory to run, and an attacker will either need to use that much memory per brute force thread, or they will need to perform drastically more computations. Each memory pass reduces the flexibility an attacker has to trade memory requirements for time requirements. There are two primary Argon2 modes, called Argon2i and Argon2d. The former is designed to resist side-channel attacks, whereas the latter is designed to maximize security against offline attacks. A hybrid, Argon2id, which uses Argon2i for the first memory pass and Argon2d for subsequent passes, also exists.

bcrypt is an older KDF designed from the key schedule of Blowfish, which is slow. It requires 4 KiB of fast memory to compute, which makes it inefficient on GPU-based crackers due to their use of contended memory. This is because GPUs have many cores, but each core has to share access to the main VRAM. Because bcrypt reads and modifies a 4 KiB state as it runs, multiple parallel GPU cores will fight over who gets access to the main memory bus, and the result is that most cores are idling, waiting until they have access to the memory they need to perform bcrypt evaluations.

scrypt is a memory-hard KDF, but it is subject to more severe TMTO attacks than Argon2. That is, if an attacker doesn't have enough memory, they can run scrypt over the same input with less memory than required by performing more operations. Additionally, scrypt's internal memory and time requirements are not independent, so it is impossible to increase memory usage without increasing processing time, and vice versa. This may become an issue for servers that might want to spend only a few tens of milliseconds on each computation, but which want to use several megabytes of memory.

Catena was one of the PHC candidates, but it did not win the competition. I haven't read the paper so I can't really say much about it, but because it did not win, it won't have nearly as much research going into it, so if there are any issues, they may not be discovered. It is not, however, a bad choice, as it was still the subject of significant research and has a solid theoretical foundation. I still wouldn't use it.

PBKDF2 is one of the oldest KDFs, and is likely the most common. It is not memory hard, which means an attacker can perform massively parallel attacks against it without running into memory issues. PBKDF2 works by taking any keyed hash, typically HMAC, and running it multiple times such that each hash evaluation depends on all the previous hash evaluations. Unfortunately, typical hashes used in HMAC require next to no memory to compute, so an attacker can massively parallelize brute force searches on GPUs and ASICs without running into the issues that a memory-hard KDF would cause.

forest
  • 64,616
  • 20
  • 206
  • 257
  • 1
    Thanks forest, Argon2 seems good. Although is it safe to assume that ports of it (ex. Libsodium or any other implementation) really follow its algorithm? Is there like a list of verified implementations? – Water Sep 02 '19 at 08:49
  • 3
    @Water This can be done by yourself quite easily. All you had to do was create a hash using the implementation of your choice, then verify it using the reference implementation and vice versa. –  Sep 02 '19 at 09:00
  • 1
    So does .NET core support all Argon2 modes then? – Andrew Savinykh Sep 03 '19 at 01:41
  • @AndrewSavinykh I have no idea; I've never used .NET before. [I guess it does?](https://www.twelve21.io/how-to-use-argon2-for-password-hashing-in-csharp/) – forest Sep 03 '19 at 01:46
  • @forest It does not, natively. There are third party implementations (like those you referenced) that do. – Xander Sep 03 '19 at 02:14
  • @Water Not verified implementation, but KeePass does implement it, and if IIRC KeePass code is not only opensource but also well tested. – bradbury9 Sep 03 '19 at 08:02
  • @MechMK1 that's not sufficient - you'd have to check every possible input (which is infeasible) to confirm whether a black box is a correct implementation of Argon2. – OrangeDog Sep 03 '19 at 17:39
  • @OrangeDog That's not true. You can formally verify a cryptographic implementation using SMT solvers. Also, test vectors work pretty well too for 99.999% of cases (at least for symmetric cryptography). – forest Sep 03 '19 at 23:50
  • 1
    @OrangeDog: having all output of an implementation match the reference implementation is necessary, but it isn't sufficient to consider an alternate crypto implementation secure. Even if the output matches, an implementation may still be subject to side channel attacks (e.g. timing, electromagnetic emission). Many side channel attacks are somewhat a bit in the realm of science fiction, but some have the potential to become practical in the future or under certain limited circumstances. – Lie Ryan Sep 04 '19 at 04:26
  • @LieRyan That's why test vectors (and even formal verification) are only useful to ensure that the _algorithm_ is implemented correctly. You still need someone to write the code who understands how to make it constant time. – forest Sep 04 '19 at 04:56
13

Even though Argon2 is my personal favorite among them, all of them are solid choices and you will likely not get weird looks for choosing one over the other. What matters the most is that you properly benchmark your system, so that you choose sane parameters.

With Argon2, you additionally have to select a mode-of-operation. There is Argon2i, Argon2d and Argon2id. Unless you know you have a good reason to pick one of the first two, you should pick Argon2id.

  • 1
    Well PBKDF2 I'd be a little iffy with, but at least it's not _bad_ (just not memory hard). – forest Sep 02 '19 at 08:08
  • 3
    @forest Yes, it's not, but PBKDF2 with like 1000000 rounds is still probably more secure than your regular run-of-the-mill `SHA1(password + salt)` implementation –  Sep 02 '19 at 08:11
  • 1
    do Argon2i, Argon2d, and Argon2id available in .Net Core? – kelalaka Sep 02 '19 at 09:53
  • There is no "pure" Argon2. When you see something advertised as "Argon2", it's always some mode. An implementation may choose to only implement e.g. "Argon2i", but it always has to implement some mode. As for availability, it depends on your choice of implementation. –  Sep 02 '19 at 09:59
  • Actually, I was asking which one is implemented. I looked for and found [this](https://www.scottbrady91.com/ASPNET-Identity/Improving-the-ASPNET-Core-Identity-Password-Hasher). It seems that there is no official one around. Type is not [clear](https://github.com/scottbrady91/ScottBrady91.AspNetCore.Identity.Argon2PasswordHasher) – kelalaka Sep 02 '19 at 21:54
  • @kelalaka No, implementations take a while to do. [Cryptography is hard](http://heartbleed.com/), and developing good cryptography code takes its time. –  Sep 03 '19 at 08:16
  • I can think of hearbleed as intentional, anyway. I was looking for an official implementation. – kelalaka Sep 03 '19 at 08:39
  • 4
    Who do you consider "official"? Implemented by Microsoft? Implemented by the authors of Argon2? –  Sep 03 '19 at 08:46
  • 3
    **Do not** use a .NET *implementation* of Argon2. Use a *binding* of the official library. I know that's probably what everyone meant, but it would be a big problem to use a sketchy re-implementation of the algorithm. The number one issue would be potential bugs. Secondary to that, you want to use an efficient implementation of any so called "slow hash" function. It does no good to be wasteful on the defender's side. It's not enough to just run slowly on your own machine. – Future Security Sep 03 '19 at 15:29
  • 1
    @FutureSecurity I drilled down through the half dozen layers of wrappers from the .net core package kelalaka found to what i believe is the actual crypto code (caveat, i followed the "library X wraps library Y" comments in github, but didn't do any deeper checking). It's a 2015 dated C implementation that says it was written by Daniel Dinu and Dmitry Khovratovich - two of the Argon 2 designers. A more detailed audit might be desirable; but on that front it looks good. 1/2 – Dan Is Fiddling By Firelight Sep 03 '19 at 19:35
  • 1
    Scott Brady's net core identity plugin uses [libsodium-core](https://github.com/tabrath/libsodium-core) which is a .netcore compatable fork of [libsodium-net](https://github.com/adamcaudill/libsodium-net). libsodium-net, is a .netframework wrapper of [lib-sodium](https://github.com/jedisct1/libsodium), which appears to incorporate the original argon2 implementation by its creators. (for anyone not famliar with.net; .net-core is the newer-cross os version of the platform and not quite 100% back compatible with the older mostly Windows only .net framework) 2/2 – Dan Is Fiddling By Firelight Sep 03 '19 at 19:35
  • @FutureSecurity Good advice. When I say implementation, I refer to a binding of course, even though it's not the correct term to use. –  Sep 04 '19 at 08:08
13

While the other responses debate the effectiveness of each algorithm from the list, I will attempt answer from the .NET Core perspective. Unless things have changed recently, the only algorithm from your list that is natively supported (ie authored by Microsoft) in .NET Core is PBKDF2. For some companies, this means that this is your only choice. If you have the luxury of being able to use any third party NuGet package then you will find numerous implementations of many of the other algorithms in you list, but you will have to decide if you trust them. Personally, from the low numbers of downloads, I would be uncomfortable using anything other than perhaps BCrypt-Official but even then, I might want to research it further. It is important to consider that anyone can author a NuGet package (and call it 'official' if they like) and that most packages are unlikely to go through any review process so it is up to you to do your homework.

Paul Hiles
  • 231
  • 1
  • 3