INSTALL | USAGE | API | AUTHOR | LICENSE
Argon2 is a hash generator optimized to produce hashes suitable for credential storage, key derivation, or other situations requiring a cryptographically secure password hash. Argon2 was the winner of the 2015 Password Hashing Competition.
This fully managed implementation of Argon2 runs in .NET Core, .NET Framework, or WebAssembly (via Blazor or Uno Platform).
Standard Argon2 Hashing:
var password = "password1";
var passwordHash = Argon2.Hash(password);
Argon2 Verification:
if (Argon2.Verify(passwordHash, password))
{
// do stuff
}
All Argon2 options available for your hashing needs...
The Argon2 reference implementation is available from https://github.com/p-h-c/phc-winner-argon2 and, indeed, the C# code in this repository was based upon that implementation but that implementation is in C. Building a C# wrapper around the C implementation is possible but adds complexity.
This 100% managed-code library allows you to use the Argon2 hash in any .NET (including Blazor) application without added complexity.
This requires a .NET environment and runs on Windows, Linux, MacOS, and WebAssembly (via Blazor).
The recommended way to get started is by use the NuGet package:
Install-Package Isopoh.Cryptography.Argon2
from https://www.nuget.org/packages/Isopoh.Cryptography.Argon2.
This project uses SourceLink so you should be able to step into the source code for debugging even when just adding the NuGet package as a dependency.
You can also, of course, go old-school and clone the repository and link use the .csproj files directly:
git clone https://github.com/mheyman/Isopoh.Cryptography.Argon2.git
then add the ProjectReference
lines to your .csproj to reference
...Isopoh.Cryptography.SecureArray\Isopoh.Cryptography.SecureArray.csproj
,
...Isopoh.Cryptography.Blake2b\Isopoh.Cryptography.Blake2b.csproj
, and
...Isopoh.Cryptography.Argon2\Isopoh.Cryptography.Argon2.csproj
. For example:
<ItemGroup>
<ProjectReference Include="..\..\..\Isopoh.Cryptography.Argon2\Isopoh.Cryptography.SecureArray\Isopoh.Cryptography.SecureArray.csproj" />
</ItemGroup>
Using the defaults:
using Isopoh.Cryptography.Argon2;
var password = "password1";
var passwordHash = Argon2.Hash(password);
if (Argon2.Verify(passwordHash, password))
{
// do stuff
}
Setting everything:
using Isopoh.Cryptography.Argon2;
using Isopoh.Cryptography.SecureArray;
byte[] passwordBytes = "password1"u8.ToArray();
byte[] secret = { 0x6b, 0xc0, 0x19, 0x4c, 0x11, 0x46, 0x35, 0x69, 0x89, 0xd6, 0x50, 0x19, 0x56, 0xac, 0x69, 0x0e };
byte[] salt = new byte[16];
byte[] associatedData = "My Associated Data"u8.ToArray();
// somewhere in the class definition:
// private static readonly RandomNumberGenerator Rng =
// System.Security.Cryptography.RandomNumberGenerator.Create();
new Random().NextBytes(salt);
var config = new Argon2Config
{
Type = Argon2Type.DataIndependentAddressing,
Version = Argon2Version.Nineteen,
TimeCost = 10,
MemoryCost = 32768,
Lanes = 5,
Threads = Environment.ProcessorCount, // higher than "Lanes" doesn't help (or hurt)
Password = passwordBytes,
Salt = salt, // >= 8 bytes if not null
Secret = secret, // from somewhere
AssociatedData = associatedData, // from somewhere
HashLength = 20, // >= 4
};
var argon2A = new Argon2(config);
string hashString;
using (SecureArray<byte> hashA = argon2A.Hash())
{
hashString = config.EncodeString(hashA.Buffer);
}
//
// Now pretend "passwordBytes" is what just came in and that it must be
// verified against the known "hashString".
//
// Note setting "Threads" to different values doesn't affect the result,
// just the time it takes to get the result.
//
var configOfPasswordToVerify = new Argon2Config { Password = passwordBytes, Threads = 1 };
SecureArray<byte>? hashB = null;
try
{
if (configOfPasswordToVerify.DecodeString(hashString, out hashB) && hashB != null)
{
var argon2ToVerify = new Argon2(configOfPasswordToVerify);
using SecureArray<byte>? hashToVerify = argon2ToVerify.Hash();
if (Argon2.FixedTimeEquals(hashB, hashToVerify))
{
// verified
}
}
}
finally
{
hashB?.Dispose();
}
//
// Or, more simply (setting "Threads" to "5")
//
if (Argon2.Verify(hashString, passwordBytes, 5))
{
// verified
}
The full API is at https://mheyman.github.io/Isopoh.Cryptography.Argon2.
In particular, the various options for Argon2 hashing can be found in
Argon2Config
and used with Argon2.Hash().
There are other Argon2.Hash()
convenience calls available there as well.
If you are only interested in Blake2b, the underlying hash used in Argon2, you can go to the Blake2b.ComputeHash() calls.
Also, there is SecureArray<T>. The SecureArray
takes a SecureArrayCall
which is a class that has three Func<>
properties, one to
LockMemory,
one to
UnlockMemory,
and one to ZeroMemory.
You can easily create your own SecureArrayCall
to lock/unlock/zero or perhaps
to log secure memory actions.
You can think of the SecureArray
sort of like you would think of
SecureString
except that SecureString
does crypto (usually -
encryption isn't supported everywhere)
to protect its sensitive data and has windows of vulnerability when it
decrypts the string for use. SecureArray
protects its data by locking the
data into RAM to keep it from swapping to disk and also zeroing the buffer when
disposed. So, unlike SecureString
, any process with access to your process's
memory will be able to read the data in your SecureArray
, but you do not
have to worry about your data persisting anywhere or multiple copies of your
data floating around RAM due to C#'s memory management.
Because it locks the memory into RAM (and at a non-movable-by-the-garbage-collector location), you need to use it as infrequently as possible and for as short a time as possible. RAM secured this way puts stress on the computer as a whole by denying physical RAM for other processes and puts stress on your particular executable by denying freedom to the garbage collector to reduce fragmentation as needed for best performance.
Note: when using SecureArray in the browser (for example, under Blazor or UnoPlatform), the memory cannot be locked into RAM so SecureArray does its best effort to protect the data by zeroing the buffer when it is disposed.
Note similarly: when using SecureArray in a Universal Windows Platform (UWP)
application, I have yet to figure out how to use the supposedly available
VirtualAllocFromApp()
system call to lock memory into RAM so SecureArray does
its best effort to protect the data by zeroing the buffer when it is disposed.
Always dispose of your SecureArray
s.
Argon2 uses Blake2b as a cryptographic building block. This code uses the
C# implementation of Blake2 modified from https://github.com/BLAKE2.
The main modification is that the Blake2 here uses SecureArray<T>. The SecureArray
takes a SecureArrayCall
to protect potentially sensitive data. Most other modifications are
strictly cosmetic.
As part of this Blake2b port, an effort was made to speed Blake2b by using techniques like unrolling and using raw buffers in unsafe code. It turns out the CLR optimizes plain code better than unrolled/unsafe code and the original always ran faster. At some point I may try a port to System.Numerics.Vector<T>...
The API Documentation at https://mheyman.github.io/Isopoh.Cryptography.Argon2 gets generated automatically upon build. This happens via a dummy C# "Doc" project that uses the DocFx NuGet package to produce the API documentation.
List of people and project that inspired creation of this one:
- The many contributers of the Argon2 repository
- and the cryptographers responsible for creating and testing that algorithm
- @CodesInChaos for the fully managed Blake2b implementation here
- @PurpleBooth for his readme template posted here
To the extent possible under law,
Michael Heyman
has waived all copyright and related or neighboring rights to
Isopoh.Cryptography.ARgon2.
You should be aware that this project is supported solely by me and provided as is.
Go back to the project description