Isopoh.Cryptography.Argon2

Argon2 Password Hasher written in C#. Uses Isopoh.Cryptography.Blake2 for hashing and Isopoh.Cryptography.SecureArray to protect sensitive data.


Keywords
argon2, hash, password, crypto, cryptography, hashing, password-hash, sensitive-data
License
Other
Install
Install-Package Isopoh.Cryptography.Argon2 -Version 1.1.10

Documentation

Isopoh

FULLY MANAGED .NET CORE ARGON2 IMPLEMENTATION

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 or .NET Framework applications.

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...

MOTIVATION

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 application without added complexity.

GETTING STARTED

This requires a .NET environment and runs on Windows, Linux, and MacOS.

INSTALLATION

NUGET

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.

CLONE

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>

USAGE

Using the defaults:

var password = "password1";
var passwordHash = Argon2.Hash(password);
if (Argon2.Verify(passwordHash, password))
{
    // do stuff
}

Setting everything:

var password = "password1";
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
byte[] salt = new byte[16];

// somewhere in the class definition:
//   private static readonly RandomNumberGenerator Rng =
//       System.Security.Cryptography.RandomNumberGenerator.Create();
Rng.GetBytes(salt);

var config = new Argon2Config
{
    Type = Argon2Type.DataIndependentAddressing,
    Version = Argon2Version.Nineteen,
    TimeCost = 10,
    MemoryCost = 32768,
    Lanes = 5,
    Threads = Environment.ProcessorCount,
    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 effect 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(var hashToVerify = argon2ToVerify.Hash())
        {
            if (Argon2.FixedTimeEquals(hashB, hashToVerify))
            {
                // verified
            }
        }
    }
}
finally
{
    hashB?.Dispose();
}

//
// Or, more simply (but this doesn't allow setting "Threads")
//
if (Argon2.Verify(hashString, passwordBytes))
{
    // verified
}

API

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.

JUST WHAT IS THIS "SecureArray"?

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.

Always dispose of your SecureArrays.

BLAKE2B PEDIGREE

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>...

API GENERATION

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.

AUTHOR

Michael Heyman

ACKNOWLEDGMENTS

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

LICENSE

Creative Commons License
Isopoh.Cryptography.Argon2 by Michael Heyman is licensed under a Creative Commons Attribution 4.0 International License.

PRODUCTION STATUS & SUPPORT

You should be aware that this project is supported solely by me and provided as is.

Go back to the project description