aboutsummaryrefslogtreecommitdiff
path: root/lib/Hashing.Portable
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Hashing.Portable')
-rw-r--r--lib/Hashing.Portable/src/HashAlg.cs22
-rw-r--r--lib/Hashing.Portable/src/ManagedHash.cs349
-rw-r--r--lib/Hashing.Portable/src/ManagedHashAlgImpl.cs113
-rw-r--r--lib/Hashing.Portable/tests/ManagedHashTests.cs29
4 files changed, 232 insertions, 281 deletions
diff --git a/lib/Hashing.Portable/src/HashAlg.cs b/lib/Hashing.Portable/src/HashAlg.cs
index 030e7e6..4535b08 100644
--- a/lib/Hashing.Portable/src/HashAlg.cs
+++ b/lib/Hashing.Portable/src/HashAlg.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Hashing.Portable
@@ -58,6 +58,26 @@ namespace VNLib.Hashing
/// </summary>
MD5 = 16,
+#pragma warning disable CA1707 // Identifiers should not contain underscores
+
+ /// <summary>
+ /// Defines the SHA3-512 hashing algorithm. NOTE: May not be supported on all platforms.
+ /// Inspect the value of <see cref="ManagedHash.SupportsSha3"/>
+ /// </summary>
+ SHA3_512 = 364,
+ /// <summary>
+ /// Defines the SHA3-384 hashing algorithm. NOTE: May not be supported on all platforms.
+ /// Inspect the value of <see cref="ManagedHash.SupportsSha3"/>
+ /// </summary>
+ SHA3_384 = 348,
+ /// <summary>
+ /// Defines the SHA3-256 hashing algorithm. NOTE: May not be supported on all platforms.
+ /// Inspect the value of <see cref="ManagedHash.SupportsSha3"/>
+ /// </summary>
+ SHA3_256 = 332,
+
+#pragma warning restore CA1707 // Identifiers should not contain underscores
+
/*
* The blake2 value is negative because the hash size is variable and the enum value
* and cannot be used to determine the hash buffer size.
diff --git a/lib/Hashing.Portable/src/ManagedHash.cs b/lib/Hashing.Portable/src/ManagedHash.cs
index 5933804..49a2f09 100644
--- a/lib/Hashing.Portable/src/ManagedHash.cs
+++ b/lib/Hashing.Portable/src/ManagedHash.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Hashing.Portable
@@ -24,12 +24,12 @@
using System;
using System.Text;
+using System.Diagnostics;
using System.Security.Cryptography;
using VNLib.Utils;
using VNLib.Utils.Memory;
using VNLib.Hashing.Native.MonoCypher;
-using VNLib.Utils.Extensions;
namespace VNLib.Hashing
{
@@ -76,34 +76,47 @@ namespace VNLib.Hashing
private static readonly Md5 _md5Alg;
private static readonly Blake2b _blake2bAlg;
+ private static readonly Sha3_256 _3_sha256;
+ private static readonly Sha3_384 _3_sha384;
+ private static readonly Sha3_512 _3_sha512;
+
/// <summary>
/// Gets a value that indicates whether the current runtime has the required libraries
/// available to support the Blake2b hashing algorithm
/// </summary>
- public static bool SupportsBlake2b => MonoCypherLibrary.CanLoadDefaultLibrary();
+ public static bool SupportsBlake2b => MonoCypherLibrary.CanLoadDefaultLibrary();
+
+ /// <summary>
+ /// Gets a value that indicates whether the current platform supports the SHA3
+ /// hashing algorithm.
+ /// </summary>
+ public static bool SupportsSha3
+ {
+ get
+ {
+ if (Sha3_512.IsSupported)
+ {
+ //Assume others are supported too
+ Debug.Assert(Sha3_384.IsSupported);
+ Debug.Assert(Sha3_256.IsSupported);
+ return true;
+ }
+
+ return false;
+ }
+ }
/// <summary>
/// Uses the UTF8 character encoding to encode the string, then
/// attempts to compute the hash and store the results into the output buffer
/// </summary>
/// <param name="data">String to hash</param>
- /// <param name="buffer">The hash output buffer</param>
+ /// <param name="output">The hash output buffer</param>
/// <param name="type">The hash algorithm to use</param>
/// <returns>The number of bytes written to the buffer, false if the hash could not be computed</returns>
/// <exception cref="ArgumentException"></exception>
- public static ERRNO ComputeHash(ReadOnlySpan<char> data, Span<byte> buffer, HashAlg type)
- {
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, buffer),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, buffer),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, buffer),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, buffer),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, buffer),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, buffer),
- _ => throw new ArgumentException("Invalid hash algorithm", nameof(type))
- };
- }
+ public static ERRNO ComputeHash(ReadOnlySpan<char> data, Span<byte> output, HashAlg type)
+ => ComputeHashInternal(type, data, output);
/// <summary>
/// Uses the UTF8 character encoding to encode the string, then
@@ -113,19 +126,8 @@ namespace VNLib.Hashing
/// <param name="type">The hash algorithm to use</param>
/// <returns>The number of bytes written to the buffer, false if the hash could not be computed</returns>
/// <exception cref="ArgumentException"></exception>
- public static byte[] ComputeHash(ReadOnlySpan<char> data, HashAlg type)
- {
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data),
- _ => throw new ArgumentException("Invalid hash algorithm", nameof(type))
- };
- }
+ public static byte[] ComputeHash(ReadOnlySpan<char> data, HashAlg type)
+ => ComputeHashInternal(type, data);
/// <summary>
/// Hashes the data parameter to the output buffer using the specified algorithm type
@@ -135,20 +137,8 @@ namespace VNLib.Hashing
/// <param name="type">The hash algorithm to use</param>
/// <returns>The number of bytes written to the buffer, <see cref="ERRNO.E_FAIL"/> if the hash could not be computed</returns>
/// <exception cref="ArgumentException"></exception>
- public static ERRNO ComputeHash(ReadOnlySpan<byte> data, Span<byte> output, HashAlg type)
- {
- //hash the buffer
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, output),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, output),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, output),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, output),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, output),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, output),
- _ => throw new ArgumentException("Hash algorithm is not supported"),
- };
- }
+ public static ERRNO ComputeHash(ReadOnlySpan<byte> data, Span<byte> output, HashAlg type)
+ => ComputeHashInternal(type, data, output);
/// <summary>
/// Hashes the data parameter to the output buffer using the specified algorithm type
@@ -157,20 +147,8 @@ namespace VNLib.Hashing
/// <param name="type">The hash algorithm to use</param>
/// <returns>A byte array that contains the hash of the data buffer</returns>
/// <exception cref="ArgumentException"></exception>
- public static byte[] ComputeHash(ReadOnlySpan<byte> data, HashAlg type)
- {
- //hash the buffer
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data),
- _ => throw new ArgumentException("Invalid hash algorithm", nameof(type))
- };
- }
+ public static byte[] ComputeHash(ReadOnlySpan<byte> data, HashAlg type)
+ => ComputeHashInternal(type, data);
/// <summary>
/// Hashes the data parameter to the output buffer using the specified algorithm type
@@ -183,18 +161,7 @@ namespace VNLib.Hashing
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="CryptographicException"></exception>
public static string ComputeHash(ReadOnlySpan<byte> data, HashAlg type, HashEncodingMode mode)
- {
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, mode),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, mode),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, mode),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, mode),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, mode),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, mode),
- _ => throw new ArgumentException("Invalid hash algorithm", nameof(type))
- };
- }
+ => ComputeHashInternal(type, data, mode);
/// <summary>
/// Uses the UTF8 character encoding to encode the string, then computes the hash and encodes
@@ -208,18 +175,7 @@ namespace VNLib.Hashing
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="CryptographicException"></exception>
public static string ComputeHash(ReadOnlySpan<char> data, HashAlg type, HashEncodingMode mode)
- {
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, mode),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, mode),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, mode),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, mode),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, mode),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, mode),
- _ => throw new ArgumentException("Invalid hash algorithm", nameof(type))
- };
- }
+ => ComputeHashInternal(type, data, mode);
/// <summary>
/// Computes the HMAC of the specified character buffer using the specified key and
@@ -231,19 +187,8 @@ namespace VNLib.Hashing
/// <param name="type">The <see cref="HashAlg"/> type used to compute the HMAC</param>
/// <returns>The number of bytes written to the ouput buffer or <see cref="ERRNO.E_FAIL"/> if the operation failed</returns>
/// <exception cref="ArgumentException"></exception>
- public static ERRNO ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<char> data, Span<byte> output, HashAlg type)
- {
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, output, key),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, output, key),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, output, key),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, output, key),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, output, key),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, output, key),
- _ => ERRNO.E_FAIL
- };
- }
+ public static ERRNO ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<char> data, Span<byte> output, HashAlg type)
+ => ComputeHashInternal(type, data, output, key);
/// <summary>
/// Computes the HMAC of the specified character buffer using the specified key and
@@ -254,19 +199,8 @@ namespace VNLib.Hashing
/// <param name="type">The <see cref="HashAlg"/> type used to compute the HMAC</param>
/// <returns>A buffer containg the computed HMAC</returns>
/// <exception cref="ArgumentException"></exception>
- public static byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<char> data, HashAlg type)
- {
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, key),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, key),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, key),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, key),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, key),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, key),
- _ => throw new ArgumentException("Hash algorithm is not supported"),
- };
- }
+ public static byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<char> data, HashAlg type)
+ => ComputeHashInternal(type, data, key);
/// <summary>
/// Computes the HMAC of the specified data buffer using the specified key and
@@ -279,19 +213,7 @@ namespace VNLib.Hashing
/// <returns>The number of bytes written to the ouput buffer or <see cref="ERRNO.E_FAIL"/> if the operation failed</returns>
/// <exception cref="ArgumentException"></exception>
public static ERRNO ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, HashAlg type)
- {
- //hash the buffer
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, output, key),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, output, key),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, output, key),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, output, key),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, output, key),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, output, key),
- _ => throw new ArgumentException("Hash algorithm is not supported"),
- };
- }
+ => ComputeHashInternal(type, data, output, key);
/// <summary>
/// Computes the HMAC of the specified data buffer using the specified key and
@@ -302,20 +224,8 @@ namespace VNLib.Hashing
/// <param name="type">The <see cref="HashAlg"/> type used to compute the HMAC</param>
/// <returns>A buffer containg the computed HMAC</returns>
/// <exception cref="ArgumentException"></exception>
- public static byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, HashAlg type)
- {
- //hash the buffer
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, key),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, key),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, key),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, key),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, key),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, key),
- _ => throw new ArgumentException("Hash algorithm is not supported"),
- };
- }
+ public static byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, HashAlg type)
+ => ComputeHashInternal(type, data, key);
/// <summary>
/// Computes the HMAC of the specified data buffer and encodes the result in
@@ -328,18 +238,7 @@ namespace VNLib.Hashing
/// <returns>The encoded string of the result</returns>
/// <exception cref="ArgumentException"></exception>
public static string ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, HashAlg type, HashEncodingMode mode)
- {
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, mode, key),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, mode, key),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, mode, key),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, mode, key),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, mode, key),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, mode, key),
- _ => throw new ArgumentException("Invalid hash algorithm", nameof(type))
- };
- }
+ => ComputeHashInternal(type, data, mode, key);
/// <summary>
/// Computes the HMAC of the specified data buffer and encodes the result in
@@ -351,115 +250,137 @@ namespace VNLib.Hashing
/// <param name="mode">The encoding type for the output data</param>
/// <returns>The encoded string of the result</returns>
/// <exception cref="ArgumentException"></exception>
- public static string ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<char> data, HashAlg type, HashEncodingMode mode)
- {
- return type switch
- {
- HashAlg.BlAKE2B => ComputeHashInternal(in _blake2bAlg, data, mode, key),
- HashAlg.SHA512 => ComputeHashInternal(in _sha512Alg, data, mode, key),
- HashAlg.SHA384 => ComputeHashInternal(in _sha384Alg, data, mode, key),
- HashAlg.SHA256 => ComputeHashInternal(in _sha256Alg, data, mode, key),
- HashAlg.SHA1 => ComputeHashInternal(in _sha1Alg, data, mode, key),
- HashAlg.MD5 => ComputeHashInternal(in _md5Alg, data, mode, key),
- _ => throw new ArgumentException("Invalid hash algorithm", nameof(type))
- };
- }
+ public static string ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<char> data, HashAlg type, HashEncodingMode mode)
+ => ComputeHashInternal(type, data, mode, key);
#region internal
- private static byte[] ComputeHashInternal<T>(in T algorithm, ReadOnlySpan<char> data, ReadOnlySpan<byte> key = default) where T : IHashAlgorithm
+
+ private static byte[] ComputeHashInternal(HashAlg alg, ReadOnlySpan<char> data, ReadOnlySpan<byte> key = default)
+ {
+ //Alloc output buffer
+ byte[] output = new byte[HashSize(alg)];
+
+ //Hash data
+ ERRNO result = ComputeHashInternal(alg, data, output, key);
+ Debug.Assert(result == HashSize(alg), $"Failed to compute hash using {alg} of size {output.Length}");
+
+ return output;
+ }
+
+ private static ERRNO ComputeHashInternal(HashAlg alg, ReadOnlySpan<char> data, Span<byte> output, ReadOnlySpan<byte> key = default)
{
int byteCount = CharEncoding.GetByteCount(data);
//Alloc buffer
using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc(byteCount, true);
//Encode data
byteCount = CharEncoding.GetBytes(data, binbuf.Span);
- //hash the buffer
- return ComputeHashInternal(in algorithm, binbuf.AsSpan(0, byteCount), key);
+ //hash the buffer or hmac if key is not empty
+ return ComputeHashInternal(alg, binbuf.Span[..byteCount], output, key);
}
- private static string ComputeHashInternal<T>(in T algorithm, ReadOnlySpan<char> data, HashEncodingMode mode, ReadOnlySpan<byte> key = default) where T : IHashAlgorithm
+ private static string ComputeHashInternal(HashAlg alg, ReadOnlySpan<char> data, HashEncodingMode mode, ReadOnlySpan<byte> key = default)
{
//Alloc stack buffer to store hash output
- Span<byte> hashBuffer = stackalloc byte[algorithm.HashSize];
+ Span<byte> hashBuffer = stackalloc byte[HashSize(alg)];
+
//hash the buffer
- ERRNO count = ComputeHashInternal(in algorithm, data, hashBuffer, key);
- if (!count)
- {
- throw new CryptographicException("Failed to compute the hash of the data");
- }
+ ERRNO count = ComputeHashInternal(alg, data, hashBuffer, key);
+
+ //Count should always be the same as the hash size, this should never fail
+ Debug.Assert(count == HashSize(alg), $"Failed to compute hash using {alg} of size {hashBuffer.Length}");
//Convert to encoded string
return mode switch
{
- HashEncodingMode.Hexadecimal => Convert.ToHexString(hashBuffer.Slice(0, count)),
- HashEncodingMode.Base64 => Convert.ToBase64String(hashBuffer.Slice(0, count)),
- HashEncodingMode.Base32 => VnEncoding.ToBase32String(hashBuffer.Slice(0, count)),
- HashEncodingMode.Base64Url => VnEncoding.ToBase64UrlSafeString(hashBuffer.Slice(0, count), true),
+ HashEncodingMode.Hexadecimal => Convert.ToHexString(hashBuffer),
+ HashEncodingMode.Base64 => Convert.ToBase64String(hashBuffer),
+ HashEncodingMode.Base32 => VnEncoding.ToBase32String(hashBuffer),
+ HashEncodingMode.Base64Url => VnEncoding.ToBase64UrlSafeString(hashBuffer, true),
_ => throw new ArgumentException("Encoding mode is not supported"),
};
}
- private static string ComputeHashInternal<T>(in T algorithm, ReadOnlySpan<byte> data, HashEncodingMode mode, ReadOnlySpan<byte> key = default) where T : IHashAlgorithm
+ private static string ComputeHashInternal(HashAlg alg, ReadOnlySpan<byte> data, HashEncodingMode mode, ReadOnlySpan<byte> key = default)
{
//Alloc stack buffer to store hash output
- Span<byte> hashBuffer = stackalloc byte[algorithm.HashSize];
+ Span<byte> hashBuffer = stackalloc byte[HashSize(alg)];
//hash the buffer
- ERRNO count = ComputeHashInternal(in algorithm, data, hashBuffer, key);
- if (!count)
- {
- throw new CryptographicException("Failed to compute the hash of the data");
- }
+ ERRNO count = ComputeHashInternal(alg, data, hashBuffer, key);
+
+ //Count should always be the same as the hash size, this should never fail
+ Debug.Assert(count == HashSize(alg), $"Failed to compute hash using {alg} of size {hashBuffer.Length}");
//Convert to encoded string
return mode switch
{
- HashEncodingMode.Hexadecimal => Convert.ToHexString(hashBuffer.Slice(0, count)),
- HashEncodingMode.Base64 => Convert.ToBase64String(hashBuffer.Slice(0, count)),
- HashEncodingMode.Base32 => VnEncoding.ToBase32String(hashBuffer.Slice(0, count)),
- HashEncodingMode.Base64Url => VnEncoding.ToBase64UrlSafeString(hashBuffer.Slice(0, count), true),
+ HashEncodingMode.Hexadecimal => Convert.ToHexString(hashBuffer),
+ HashEncodingMode.Base64 => Convert.ToBase64String(hashBuffer),
+ HashEncodingMode.Base32 => VnEncoding.ToBase32String(hashBuffer),
+ HashEncodingMode.Base64Url => VnEncoding.ToBase64UrlSafeString(hashBuffer, true),
_ => throw new ArgumentException("Encoding mode is not supported"),
};
}
- private static ERRNO ComputeHashInternal<T>(in T algorithm, ReadOnlySpan<char> data, Span<byte> output, ReadOnlySpan<byte> key = default) where T : IHashAlgorithm
+
+ private static byte[] ComputeHashInternal(HashAlg alg, ReadOnlySpan<byte> data, ReadOnlySpan<byte> key = default)
{
- int byteCount = CharEncoding.GetByteCount(data);
- //Alloc buffer
- using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc(byteCount, true);
- //Encode data
- byteCount = CharEncoding.GetBytes(data, binbuf.Span);
- //hash the buffer or hmac if key is not empty
- return ComputeHashInternal(in algorithm, binbuf.Span[..byteCount], output, key);
+ //Alloc output buffer
+ byte[] output = new byte[HashSize(alg)];
+
+ //Hash data
+ ERRNO result = ComputeHashInternal(alg, data, output, key);
+ Debug.Assert(result == HashSize(alg), $"Failed to compute hash using {alg} of size {output.Length}");
+
+ return output;
}
-
- private static ERRNO ComputeHashInternal<T>(in T algorithm, ReadOnlySpan<byte> data, Span<byte> buffer, ReadOnlySpan<byte> key = default) where T : IHashAlgorithm
+ private static int HashSize(HashAlg alg) => alg switch
{
- //hash the buffer or hmac if key is not empty
- if (key.IsEmpty)
- {
- return algorithm.TryComputeHash(data, buffer, out int written) ? written : ERRNO.E_FAIL;
- }
- else
- {
- return algorithm.TryComputeHmac(key, data, buffer, out int written) ? written : ERRNO.E_FAIL;
- }
- }
-
- private static byte[] ComputeHashInternal<T>(in T algorithm, ReadOnlySpan<byte> data, ReadOnlySpan<byte> key = default) where T : IHashAlgorithm
+ HashAlg.SHA3_512 => _3_sha512.HashSize,
+ HashAlg.SHA3_384 => _3_sha384.HashSize,
+ HashAlg.SHA3_256 => _3_sha256.HashSize,
+ HashAlg.BlAKE2B => _blake2bAlg.HashSize,
+ HashAlg.SHA512 => _sha512Alg.HashSize,
+ HashAlg.SHA384 => _sha384Alg.HashSize,
+ HashAlg.SHA256 => _sha256Alg.HashSize,
+ HashAlg.SHA1 => _sha1Alg.HashSize,
+ HashAlg.MD5 => _md5Alg.HashSize,
+ _ => throw new ArgumentException("Invalid hash algorithm", nameof(alg))
+ };
+
+
+ private static ERRNO ComputeHashInternal(HashAlg alg, ReadOnlySpan<byte> data, Span<byte> buffer, ReadOnlySpan<byte> key = default)
{
- //hash the buffer or hmac if key is not empty
- if (key.IsEmpty)
+ return alg switch
{
- return algorithm.ComputeHash(data);
- }
- else
+ HashAlg.SHA3_512 => computeHashInternal(in _3_sha512, data, buffer, key),
+ HashAlg.SHA3_384 => computeHashInternal(in _3_sha384, data, buffer, key),
+ HashAlg.SHA3_256 => computeHashInternal(in _3_sha256, data, buffer, key),
+ HashAlg.BlAKE2B => computeHashInternal(in _blake2bAlg, data, buffer, key),
+ HashAlg.SHA512 => computeHashInternal(in _sha512Alg, data, buffer, key),
+ HashAlg.SHA384 => computeHashInternal(in _sha384Alg, data, buffer, key),
+ HashAlg.SHA256 => computeHashInternal(in _sha256Alg, data, buffer, key),
+ HashAlg.SHA1 => computeHashInternal(in _sha1Alg, data, buffer, key),
+ HashAlg.MD5 => computeHashInternal(in _md5Alg, data, buffer, key),
+ _ => throw new ArgumentException("Invalid hash algorithm", nameof(alg))
+ };
+
+ static ERRNO computeHashInternal<T>(in T algorithm, ReadOnlySpan<byte> data, Span<byte> buffer, ReadOnlySpan<byte> key = default)
+ where T : IHashAlgorithm
{
- return algorithm.ComputeHmac(key, data);
+ //hash the buffer or hmac if key is not empty
+ if (key.IsEmpty)
+ {
+ return algorithm.TryComputeHash(data, buffer, out int written) ? written : ERRNO.E_FAIL;
+ }
+ else
+ {
+ return algorithm.TryComputeHmac(key, data, buffer, out int written) ? written : ERRNO.E_FAIL;
+ }
}
- }
+ }
#endregion
}
diff --git a/lib/Hashing.Portable/src/ManagedHashAlgImpl.cs b/lib/Hashing.Portable/src/ManagedHashAlgImpl.cs
index 7e2262e..d53152e 100644
--- a/lib/Hashing.Portable/src/ManagedHashAlgImpl.cs
+++ b/lib/Hashing.Portable/src/ManagedHashAlgImpl.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Hashing.Portable
@@ -23,7 +23,6 @@
*/
using System;
-using System.Diagnostics;
using System.Security.Cryptography;
using VNLib.Utils;
@@ -37,12 +36,8 @@ namespace VNLib.Hashing
{
int HashSize { get; }
- byte[] ComputeHash(ReadOnlySpan<byte> data);
-
bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count);
- byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data);
-
bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count);
}
@@ -50,13 +45,10 @@ namespace VNLib.Hashing
{
///<inheritdoc/>
public readonly int HashSize => (int)HashAlg.SHA1;
- ///<inheritdoc/>
- public readonly byte[] ComputeHash(ReadOnlySpan<byte> data) => SHA1.HashData(data);
+
///<inheritdoc/>
public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count) => SHA1.TryHashData(data, output, out count);
-
- ///<inheritdoc/>
- public readonly byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data) => HMACSHA1.HashData(key, data);
+
///<inheritdoc/>
public readonly bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count) => HMACSHA1.TryHashData(key, data, output, out count);
}
@@ -67,15 +59,9 @@ namespace VNLib.Hashing
public readonly int HashSize => (int)HashAlg.SHA256;
///<inheritdoc/>
- public readonly byte[] ComputeHash(ReadOnlySpan<byte> data) => SHA256.HashData(data);
-
- ///<inheritdoc/>
public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count) => SHA256.TryHashData(data, output, out count);
///<inheritdoc/>
- public readonly byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data) => HMACSHA256.HashData(key, data);
-
- ///<inheritdoc/>
public readonly bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count) => HMACSHA256.TryHashData(key, data, output, out count);
}
@@ -83,15 +69,11 @@ namespace VNLib.Hashing
{
///<inheritdoc/>
public readonly int HashSize => (int)HashAlg.SHA384;
- ///<inheritdoc/>
- public readonly byte[] ComputeHash(ReadOnlySpan<byte> data) => SHA384.HashData(data);
+
///<inheritdoc/>
public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count) => SHA384.TryHashData(data, output, out count);
///<inheritdoc/>
- public readonly byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data) => HMACSHA384.HashData(key, data);
-
- ///<inheritdoc/>
public readonly bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count) => HMACSHA384.TryHashData(key, data, output, out count);
}
@@ -99,13 +81,10 @@ namespace VNLib.Hashing
{
///<inheritdoc/>
public readonly int HashSize => (int)HashAlg.SHA512;
- ///<inheritdoc/>
- public readonly byte[] ComputeHash(ReadOnlySpan<byte> data) => SHA512.HashData(data);
+
///<inheritdoc/>
public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count) => SHA512.TryHashData(data, output, out count);
-
- ///<inheritdoc/>
- public readonly byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data) => HMACSHA512.HashData(key, data);
+
///<inheritdoc/>
public readonly bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count) => HMACSHA512.TryHashData(key, data, output, out count);
}
@@ -114,63 +93,65 @@ namespace VNLib.Hashing
{
///<inheritdoc/>
public readonly int HashSize => (int)HashAlg.MD5;
- ///<inheritdoc/>
- public readonly byte[] ComputeHash(ReadOnlySpan<byte> data) => MD5.HashData(data);
+
///<inheritdoc/>
public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count) => MD5.TryHashData(data, output, out count);
-
- ///<inheritdoc/>
- public readonly byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data) => HMACMD5.HashData(key, data);
+
///<inheritdoc/>
public readonly bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count) => HMACMD5.TryHashData(key, data, output, out count);
}
- private readonly struct Blake2b : IHashAlgorithm
+ private readonly struct Sha3_256 : IHashAlgorithm
{
- const byte DefaultBlake2HashSize = 64;
-
- internal static int MaxHashSize => MCBlake2Module.MaxHashSize;
- internal static int MaxKeySize => MCBlake2Module.MaxKeySize;
+ public static bool IsSupported => SHA3_256.IsSupported;
///<inheritdoc/>
- public readonly int HashSize => DefaultBlake2HashSize;
-
+ public readonly int HashSize => (int)HashAlg.SHA256;
+
///<inheritdoc/>
- public readonly byte[] ComputeHash(ReadOnlySpan<byte> data)
- {
- //Stack buffer for output hash
- byte[] output = new byte[DefaultBlake2HashSize];
+ public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count) => SHA3_256.TryHashData(data, output, out count);
+
+ ///<inheritdoc/>
+ public readonly bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count) => HMACSHA3_256.TryHashData(key, data, output, out count);
+ }
- if (!TryComputeHash(data, output, out int count))
- {
- throw new ArgumentException("Failed to compute Blake2 hash of desired data");
- }
+ private readonly struct Sha3_384 : IHashAlgorithm
+ {
+ public static bool IsSupported => SHA3_384.IsSupported;
- //Count must be exact same (sanity check)
- Debug.Assert(count == DefaultBlake2HashSize);
+ ///<inheritdoc/>
+ public readonly int HashSize => (int)HashAlg.SHA384;
+
+ ///<inheritdoc/>
+ public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count) => SHA3_384.TryHashData(data, output, out count);
+
+ ///<inheritdoc/>
+ public readonly bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count) => HMACSHA3_384.TryHashData(key, data, output, out count);
+ }
- //Return the hash as a new array
- return output;
- }
+ private readonly struct Sha3_512 : IHashAlgorithm
+ {
+ public static bool IsSupported => SHA3_512.IsSupported;
///<inheritdoc/>
- public readonly byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data)
- {
- //Alloc output buffer
- byte[] output = new byte[DefaultBlake2HashSize];
-
- if (!TryComputeHmac(key, data, output, out int count))
- {
- throw new ArgumentException("Failed to compute Blake2 hash of desired data");
- }
+ public readonly int HashSize => (int)HashAlg.SHA512;
+
+ ///<inheritdoc/>
+ public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count) => SHA3_512.TryHashData(data, output, out count);
+
+ ///<inheritdoc/>
+ public readonly bool TryComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> output, out int count) => HMACSHA3_512.TryHashData(key, data, output, out count);
+ }
- //Count must be exact same (sanity check)
- Debug.Assert(count == DefaultBlake2HashSize);
+ private readonly struct Blake2b : IHashAlgorithm
+ {
+ const byte DefaultBlake2HashSize = 64;
- //Return the hash as a new array
- return output;
+ internal static int MaxHashSize => MCBlake2Module.MaxHashSize;
+ internal static int MaxKeySize => MCBlake2Module.MaxKeySize;
- }
+ ///<inheritdoc/>
+ public readonly int HashSize => DefaultBlake2HashSize;
///<inheritdoc/>
public readonly bool TryComputeHash(ReadOnlySpan<byte> data, Span<byte> output, out int count)
diff --git a/lib/Hashing.Portable/tests/ManagedHashTests.cs b/lib/Hashing.Portable/tests/ManagedHashTests.cs
index 01b3336..8b2a3c3 100644
--- a/lib/Hashing.Portable/tests/ManagedHashTests.cs
+++ b/lib/Hashing.Portable/tests/ManagedHashTests.cs
@@ -4,6 +4,7 @@ using System.Text;
using VNLib.Utils.Memory;
using VNLib.Utils;
+using System.Diagnostics;
namespace VNLib.Hashing.Tests
@@ -17,6 +18,9 @@ namespace VNLib.Hashing.Tests
byte[] testData = Encoding.UTF8.GetBytes("Hello World!");
using UnsafeMemoryHandle<byte> heapBuffer = MemoryUtil.UnsafeAlloc(64, false);
+ Trace.WriteLineIf(ManagedHash.SupportsSha3, "SHA3 is supported");
+ Trace.WriteLineIf(ManagedHash.SupportsBlake2b, "Blake2b is supported");
+
//Test all supported algorithms
foreach (HashAlg alg in Enum.GetValues<HashAlg>())
{
@@ -25,6 +29,17 @@ namespace VNLib.Hashing.Tests
//Skip unsupported algorithms
if (alg == HashAlg.BlAKE2B && !ManagedHash.SupportsBlake2b) continue;
+ if (!ManagedHash.SupportsSha3)
+ {
+ switch (alg)
+ {
+ case HashAlg.SHA3_256:
+ case HashAlg.SHA3_384:
+ case HashAlg.SHA3_512:
+ continue;
+ }
+ }
+
//Compute hash
ERRNO hashSize = ManagedHash.ComputeHash(testData, heapBuffer.Span, alg);
Assert.IsTrue(hashSize == Math.Abs(hashSize));
@@ -58,6 +73,9 @@ namespace VNLib.Hashing.Tests
byte[] testKey = RandomHash.GetRandomBytes(32);
using UnsafeMemoryHandle<byte> heapBuffer = MemoryUtil.UnsafeAlloc(64, false);
+ Trace.WriteLineIf(ManagedHash.SupportsSha3, "SHA3 is supported");
+ Trace.WriteLineIf(ManagedHash.SupportsBlake2b, "Blake2b is supported");
+
//Test all supported algorithms
foreach (HashAlg alg in Enum.GetValues<HashAlg>())
{
@@ -66,6 +84,17 @@ namespace VNLib.Hashing.Tests
//Skip unsupported algorithms
if (alg == HashAlg.BlAKE2B && !ManagedHash.SupportsBlake2b) continue;
+ if (!ManagedHash.SupportsSha3)
+ {
+ switch (alg)
+ {
+ case HashAlg.SHA3_256:
+ case HashAlg.SHA3_384:
+ case HashAlg.SHA3_512:
+ continue;
+ }
+ }
+
//Compute hash
ERRNO hashSize = ManagedHash.ComputeHmac(testKey, testData, heapBuffer.Span, alg);
Assert.IsTrue(hashSize == Math.Abs(hashSize));