< Summary

Information
Class: ArturRios.Util.Hashing.Hash
Assembly: ArturRios.Util
File(s): D:\Repositories\dotnet-util\src\Hashing\Hash.cs
Line coverage
100%
Covered lines: 33
Uncovered lines: 0
Coverable lines: 33
Total lines: 90
Line coverage: 100%
Branch coverage
100%
Covered branches: 4
Total branches: 4
Branch coverage: 100%
Method coverage
100%
Covered methods: 4
Fully covered methods: 0
Total methods: 4
Method coverage: 100%
Full method coverage: 0%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
EncodeWithSalt(...)100%22100%
EncodeWithRandomSalt(...)100%22100%
TextMatches(...)100%11100%
CreateSalt()100%11100%

File(s)

D:\Repositories\dotnet-util\src\Hashing\Hash.cs

#LineLine coverage
 1using System.Security.Cryptography;
 2using System.Text;
 3using Konscious.Security.Cryptography;
 4
 5namespace ArturRios.Util.Hashing;
 6
 7/// <summary>
 8/// Provides hashing helpers based on Argon2id for secure password / secret derivation.
 9/// </summary>
 10/// <remarks>
 11/// All methods use a key length of 128 bytes and allow optional configuration overrides via <see cref="HashConfiguratio
 12/// </remarks>
 13public static class Hash
 14{
 15    private const int Argon2IdKeyBytes = 128;
 16    private const int SaltByteSize = 16;
 17
 18    /// <summary>
 19    /// Hashes <paramref name="text"/> using Argon2id with a provided salt and optional configuration.
 20    /// </summary>
 21    /// <param name="text">The input text to hash (e.g. password).</param>
 22    /// <param name="salt">A cryptographically strong random salt.</param>
 23    /// <param name="configuration">Optional hashing configuration; defaults are used if null.</param>
 24    /// <returns>The derived hash bytes.</returns>
 25    public static byte[] EncodeWithSalt(string text, byte[] salt, HashConfiguration? configuration = null)
 326    {
 327        configuration ??= new HashConfiguration();
 28
 329        Argon2id argon2Id = new(Encoding.UTF8.GetBytes(text))
 330        {
 331            Salt = salt,
 332            DegreeOfParallelism = configuration.DegreeOfParallelism,
 333            Iterations = configuration.NumberOfIterations,
 334            MemorySize = configuration.MemoryToUseInKb
 335        };
 36
 337        return argon2Id.GetBytes(Argon2IdKeyBytes);
 338    }
 39
 40    /// <summary>
 41    /// Hashes <paramref name="text"/> using Argon2id and a newly generated random 16-byte salt.
 42    /// </summary>
 43    /// <param name="text">The input text to hash.</param>
 44    /// <param name="salt">Outputs the generated salt used for hashing.</param>
 45    /// <param name="configuration">Optional hashing configuration.</param>
 46    /// <returns>The derived hash bytes.</returns>
 47    public static byte[] EncodeWithRandomSalt(string text, out byte[] salt, HashConfiguration? configuration = null)
 548    {
 549        configuration ??= new HashConfiguration();
 550        salt = CreateSalt();
 51
 552        Argon2id argon2Id = new(Encoding.UTF8.GetBytes(text))
 553        {
 554            Salt = salt,
 555            DegreeOfParallelism = configuration.DegreeOfParallelism,
 556            Iterations = configuration.NumberOfIterations,
 557            MemorySize = configuration.MemoryToUseInKb
 558        };
 59
 560        return argon2Id.GetBytes(Argon2IdKeyBytes);
 561    }
 62
 63    /// <summary>
 64    /// Verifies if the provided <paramref name="hash"/> matches hashing <paramref name="text"/> with <paramref name="sa
 65    /// </summary>
 66    /// <param name="text">Plain text to verify.</param>
 67    /// <param name="hash">Expected hash value.</param>
 68    /// <param name="salt">Salt associated with the stored hash.</param>
 69    /// <returns><c>true</c> if the hashes match; otherwise <c>false</c>.</returns>
 70    public static bool TextMatches(string text, byte[] hash, byte[] salt)
 271    {
 272        var hashToMatch = EncodeWithSalt(text, salt);
 73
 274        return hash.SequenceEqual(hashToMatch);
 275    }
 76
 77    /// <summary>
 78    /// Creates a cryptographically strong random salt.
 79    /// </summary>
 80    /// <returns>A 16-byte salt.</returns>
 81    private static byte[] CreateSalt()
 582    {
 583        var buffer = new byte[SaltByteSize];
 84
 585        using var rng = RandomNumberGenerator.Create();
 586        rng.GetBytes(buffer);
 87
 588        return buffer;
 589    }
 90}