diff options
Diffstat (limited to 'lib/Hashing.Portable')
-rw-r--r-- | lib/Hashing.Portable/src/HashAlg.cs | 22 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/ManagedHash.cs | 349 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/ManagedHashAlgImpl.cs | 113 | ||||
-rw-r--r-- | lib/Hashing.Portable/tests/ManagedHashTests.cs | 29 |
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)); |