diff options
author | vnugent <public@vaughnnugent.com> | 2023-01-14 16:24:28 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-01-14 16:24:28 -0500 |
commit | 52b8e30437e235817ed534dec860e781bb0468c0 (patch) | |
tree | b279418c23daec838fab6bf8a0b29c3091d2e300 /lib/Hashing.Portable/src | |
parent | 0dcecff0f51bdb51b070c05255fbcd9338ab11f2 (diff) |
MemoryUtil native integer size update + tests
Diffstat (limited to 'lib/Hashing.Portable/src')
-rw-r--r-- | lib/Hashing.Portable/src/Argon2/VnArgon2.cs | 24 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs | 57 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs | 5 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs | 61 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/ManagedHash.cs | 23 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/RandomHash.cs | 143 |
6 files changed, 245 insertions, 68 deletions
diff --git a/lib/Hashing.Portable/src/Argon2/VnArgon2.cs b/lib/Hashing.Portable/src/Argon2/VnArgon2.cs index 01cfe74..7b467ba 100644 --- a/lib/Hashing.Portable/src/Argon2/VnArgon2.cs +++ b/lib/Hashing.Portable/src/Argon2/VnArgon2.cs @@ -51,7 +51,7 @@ namespace VNLib.Hashing public const string ARGON2_DEFUALT_LIB_NAME = "Argon2"; private static readonly Encoding LocEncoding = Encoding.Unicode; - private static readonly Lazy<IUnmangedHeap> _heap = new (Memory.InitializeNewHeapForProcess, LazyThreadSafetyMode.PublicationOnly); + private static readonly Lazy<IUnmangedHeap> _heap = new (MemoryUtil.InitializeNewHeapForProcess, LazyThreadSafetyMode.PublicationOnly); private static readonly Lazy<Argon2NativeLibary> _nativeLibrary = new(LoadNativeLib, LazyThreadSafetyMode.PublicationOnly); @@ -137,16 +137,22 @@ namespace VNLib.Hashing { //Get bytes count int saltbytes = LocEncoding.GetByteCount(salt); + //Get bytes count for password int passBytes = LocEncoding.GetByteCount(password); + //Alloc memory for salt using MemoryHandle<byte> buffer = PwHeap.Alloc<byte>(saltbytes + passBytes, true); + Span<byte> saltBuffer = buffer.AsSpan(0, saltbytes); Span<byte> passBuffer = buffer.AsSpan(passBytes); + //Encode salt with span the same size of the salt _ = LocEncoding.GetBytes(salt, saltBuffer); + //Encode password, create a new span to make sure its proper size _ = LocEncoding.GetBytes(password, passBuffer); + //Hash return Hash2id(passBuffer, saltBuffer, secret, timeCost, memCost, parallelism, hashLen); } @@ -170,10 +176,13 @@ namespace VNLib.Hashing { //Get bytes count int passBytes = LocEncoding.GetByteCount(password); + //Alloc memory for password using MemoryHandle<byte> pwdHandle = PwHeap.Alloc<byte>(passBytes, true); + //Encode password, create a new span to make sure its proper size _ = LocEncoding.GetBytes(password, pwdHandle); + //Hash return Hash2id(pwdHandle.Span, salt, secret, timeCost, memCost, parallelism, hashLen); } @@ -197,12 +206,16 @@ namespace VNLib.Hashing string hash, salts; //Alloc data for hash output using MemoryHandle<byte> hashHandle = PwHeap.Alloc<byte>(hashLen, true); + //hash the password Hash2id(password, salt, secret, hashHandle.Span, timeCost, memCost, parallelism); + //Encode hash hash = Convert.ToBase64String(hashHandle.Span); + //encode salt salts = Convert.ToBase64String(salt); + //Encode salt in base64 return $"${ID_MODE},v={(int)Argon2_version.VERSION_13},m={memCost},t={timeCost},p={parallelism},s={salts}${hash}"; } @@ -277,6 +290,7 @@ namespace VNLib.Hashing { //Alloc data for hash output using MemoryHandle<byte> outputHandle = PwHeap.Alloc<byte>(hashBytes.Length, true); + //Get pointers fixed (byte* secretptr = secret, pwd = rawPass, slptr = salt) { @@ -333,6 +347,7 @@ namespace VNLib.Hashing { throw new VnArgon2PasswordFormatException("The hash argument supplied is not a valid format and cannot be decoded"); } + Argon2PasswordEntry entry; try { @@ -343,12 +358,15 @@ namespace VNLib.Hashing { throw new VnArgon2PasswordFormatException("Password format was not recoverable", ex); } + //Calculate base64 buffer sizes int passBase64BufSize = Base64.GetMaxDecodedFromUtf8Length(entry.Hash.Length); int saltBase64BufSize = Base64.GetMaxDecodedFromUtf8Length(entry.Salt.Length); int rawPassLen = LocEncoding.GetByteCount(rawPass); + //Alloc buffer for decoded data - using MemoryHandle<byte> rawBufferHandle = Memory.Shared.Alloc<byte>(passBase64BufSize + saltBase64BufSize + rawPassLen, true); + using MemoryHandle<byte> rawBufferHandle = MemoryUtil.Shared.Alloc<byte>(passBase64BufSize + saltBase64BufSize + rawPassLen, true); + //Split buffers Span<byte> saltBuf = rawBufferHandle.Span[..saltBase64BufSize]; Span<byte> passBuf = rawBufferHandle.AsSpan(saltBase64BufSize, passBase64BufSize); @@ -362,6 +380,7 @@ namespace VNLib.Hashing //Resize pass buff passBuf = passBuf[..actualHashLen]; } + //Decode salt { if (!Convert.TryFromBase64Chars(entry.Salt, saltBuf, out int actualSaltLen)) @@ -371,6 +390,7 @@ namespace VNLib.Hashing //Resize salt buff saltBuf = saltBuf[..actualSaltLen]; } + //encode password bytes rawPassLen = LocEncoding.GetBytes(rawPass, rawPassBuf); //Verify password diff --git a/lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs b/lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs index f36b151..5ff37e8 100644 --- a/lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs +++ b/lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs @@ -45,27 +45,39 @@ namespace VNLib.Hashing.IdentityUtility /// <param name="data">The data to compute the hash of</param> /// <param name="encoding">The <see cref="Encoding"/> used to encode the character buffer</param> /// <returns>The base64 UTF8 string of the computed hash of the specified data</returns> - public static string ComputeBase64Hash(this HMAC hmac, ReadOnlySpan<char> data, Encoding encoding = null) + /// <exception cref="OutOfMemoryException"></exception> + /// <exception cref="ArgumentNullException"></exception> + public static string ComputeBase64Hash(this HMAC hmac, ReadOnlySpan<char> data, Encoding? encoding = null) { + _ = hmac ?? throw new ArgumentNullException(nameof(hmac)); + encoding ??= Encoding.UTF8; + //Calc hashsize to alloc buffer int hashBufSize = (hmac.HashSize / 8); + //Calc buffer size int encBufSize = encoding.GetByteCount(data); + //Alloc buffer for encoding data - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(encBufSize + hashBufSize); + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(encBufSize + hashBufSize); + Span<byte> encBuffer = buffer.Span[0..encBufSize]; Span<byte> hashBuffer = buffer.Span[encBufSize..]; + //Encode data _ = encoding.GetBytes(data, encBuffer); + //compute hash if (!hmac.TryComputeHash(encBuffer, hashBuffer, out int hashBytesWritten)) { - throw new OutOfMemoryException("Hash buffer size was too small"); + throw new InternalBufferTooSmallException("Hash buffer size was too small"); } + //Convert to base64 string return Convert.ToBase64String(hashBuffer[..hashBytesWritten]); } + /// <summary> /// Computes the hash of the raw data and compares the computed hash against /// the specified base64hash @@ -77,36 +89,48 @@ namespace VNLib.Hashing.IdentityUtility /// <returns>A value indicating if the hash values match</returns> /// <exception cref="ArgumentException"></exception> /// <exception cref="OutOfMemoryException"></exception> - public static bool VerifyBase64Hash(this HMAC hmac, ReadOnlySpan<char> base64Hmac, ReadOnlySpan<char> raw, Encoding encoding = null) + /// <exception cref="ArgumentNullException"></exception> + public static bool VerifyBase64Hash(this HMAC hmac, ReadOnlySpan<char> base64Hmac, ReadOnlySpan<char> raw, Encoding? encoding = null) { _ = hmac ?? throw new ArgumentNullException(nameof(hmac)); + if (raw.IsEmpty) { throw new ArgumentException("Raw data buffer must not be empty", nameof(raw)); } + if (base64Hmac.IsEmpty) { throw new ArgumentException("Hmac buffer must not be empty", nameof(base64Hmac)); } + encoding ??= Encoding.UTF8; + //Calc buffer size int rawDataBufSize = encoding.GetByteCount(raw); + //Calc base64 buffer size int base64BufSize = base64Hmac.Length; + //Alloc buffer for encoding and raw data - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(rawDataBufSize + base64BufSize, true); + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(rawDataBufSize + base64BufSize, true); + Span<byte> rawDataBuf = buffer.Span[0..rawDataBufSize]; Span<byte> base64Buf = buffer.Span[rawDataBufSize..]; + //encode _ = encoding.GetBytes(raw, rawDataBuf); + //Convert to binary if(!Convert.TryFromBase64Chars(base64Hmac, base64Buf, out int base64Converted)) { - throw new OutOfMemoryException("Base64 buffer too small"); + throw new InternalBufferTooSmallException("Base64 buffer too small"); } + //Compare hash buffers return hmac.VerifyHash(base64Buf[0..base64Converted], rawDataBuf); } + /// <summary> /// Computes the hash of the raw data and compares the computed hash against /// the specified hash @@ -117,25 +141,33 @@ namespace VNLib.Hashing.IdentityUtility /// <returns>A value indicating if the hash values match</returns> /// <exception cref="ArgumentException"></exception> /// <exception cref="OutOfMemoryException"></exception> + /// <exception cref="ArgumentNullException"></exception> public static bool VerifyHash(this HMAC hmac, ReadOnlySpan<byte> hash, ReadOnlySpan<byte> raw) { + _ = hmac ?? throw new ArgumentNullException(nameof(hmac)); + if (raw.IsEmpty) { throw new ArgumentException("Raw data buffer must not be empty", nameof(raw)); } + if (hash.IsEmpty) { throw new ArgumentException("Hash buffer must not be empty", nameof(hash)); } + //Calc hashsize to alloc buffer int hashBufSize = hmac.HashSize / 8; + //Alloc buffer for hash - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(hashBufSize); + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(hashBufSize); + //compute hash if (!hmac.TryComputeHash(raw, buffer, out int hashBytesWritten)) { - throw new OutOfMemoryException("Hash buffer size was too small"); + throw new InternalBufferTooSmallException("Hash buffer size was too small"); } + //Compare hash buffers return CryptographicOperations.FixedTimeEquals(buffer.Span[0..hashBytesWritten], hash); } @@ -153,17 +185,22 @@ namespace VNLib.Hashing.IdentityUtility /// <exception cref="ArgumentNullException"></exception> /// <exception cref="CryptographicException"></exception> /// <exception cref="ObjectDisposedException"></exception> - public static ERRNO TryEncrypt(this RSA alg, ReadOnlySpan<char> data, in Span<byte> output, RSAEncryptionPadding padding, Encoding enc = null) + public static ERRNO TryEncrypt(this RSA alg, ReadOnlySpan<char> data, in Span<byte> output, RSAEncryptionPadding padding, Encoding? enc = null) { _ = alg ?? throw new ArgumentNullException(nameof(alg)); + //Default to UTF8 encoding enc ??= Encoding.UTF8; + //Alloc decode buffer int buffSize = enc.GetByteCount(data); + //Alloc buffer - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(buffSize, true); + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(buffSize, true); + //Encode data int converted = enc.GetBytes(data, buffer); + //Try encrypt return !alg.TryEncrypt(buffer.Span, output, padding, out int bytesWritten) ? ERRNO.E_FAIL : (ERRNO)bytesWritten; } diff --git a/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs b/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs index 54098c2..9076e5b 100644 --- a/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs +++ b/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs @@ -449,10 +449,13 @@ namespace VNLib.Hashing.IdentityUtility { return null; } + //bin buffer for temp decoding - using UnsafeMemoryHandle<byte> binBuffer = Memory.UnsafeAlloc<byte>(base64.Length + 16, false); + using UnsafeMemoryHandle<byte> binBuffer = MemoryUtil.UnsafeAlloc<byte>(base64.Length + 16, false); + //base64url decode ERRNO count = VnEncoding.Base64UrlDecode(base64, binBuffer.Span); + //Return buffer or null if failed return count ? binBuffer.AsSpan(0, count).ToArray() : null; } diff --git a/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs b/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs index 716dd4c..e3822d0 100644 --- a/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs +++ b/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs @@ -39,7 +39,7 @@ namespace VNLib.Hashing.IdentityUtility /// Provides a dynamic JSON Web Token class that will store and /// compute Base64Url encoded WebTokens /// </summary> - public class JsonWebToken : VnDisposeable, IStringSerializeable, IDisposable + public class JsonWebToken : VnDisposeable, IStringSerializeable { internal const byte SAEF_PERIOD = 0x2e; internal const byte PADDING_BYTES = 0x3d; @@ -53,15 +53,19 @@ namespace VNLib.Hashing.IdentityUtility /// <exception cref="FormatException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="OutOfMemoryException"></exception> - public static JsonWebToken Parse(ReadOnlySpan<char> urlEncJwtString, IUnmangedHeap heap = null) + public static JsonWebToken Parse(ReadOnlySpan<char> urlEncJwtString, IUnmangedHeap? heap = null) { - heap ??= Memory.Shared; + heap ??= MemoryUtil.Shared; + //Calculate the decoded size of the characters to alloc a buffer int utf8Size = Encoding.UTF8.GetByteCount(urlEncJwtString); + //Alloc bin buffer to store decode data using MemoryHandle<byte> binBuffer = heap.Alloc<byte>(utf8Size, true); + //Decode to utf8 utf8Size = Encoding.UTF8.GetBytes(urlEncJwtString, binBuffer); + //Parse and return the jwt return ParseRaw(binBuffer.Span[..utf8Size], heap); } @@ -75,14 +79,16 @@ namespace VNLib.Hashing.IdentityUtility /// <exception cref="FormatException"></exception> /// <exception cref="ArgumentException"></exception> /// <exception cref="OutOfMemoryException"></exception> - public static JsonWebToken ParseRaw(ReadOnlySpan<byte> utf8JWTData, IUnmangedHeap heap = null) + public static JsonWebToken ParseRaw(ReadOnlySpan<byte> utf8JWTData, IUnmangedHeap? heap = null) { if (utf8JWTData.IsEmpty) { throw new ArgumentException("JWT data may not be empty", nameof(utf8JWTData)); } + //Set default heap of non was specified - heap ??= Memory.Shared; + heap ??= MemoryUtil.Shared; + //Alloc the token and copy the supplied data to a new mem stream JsonWebToken jwt = new(heap, new (heap, utf8JWTData)); try @@ -144,17 +150,19 @@ namespace VNLib.Hashing.IdentityUtility { Heap = heap; DataStream = initialData; + + //Update position to the end of the initial data initialData.Position = initialData.Length; } /// <summary> /// Creates a new empty JWT instance, with an optional heap to alloc - /// buffers from. (<see cref="Memory.Shared"/> is used as default) + /// buffers from. (<see cref="MemoryUtil.Shared"/> is used as default) /// </summary> /// <param name="heap">The <see cref="IUnmangedHeap"/> to alloc buffers from</param> - public JsonWebToken(IUnmangedHeap heap = null) + public JsonWebToken(IUnmangedHeap? heap = null) { - Heap = heap ?? Memory.Shared; + Heap = heap ?? MemoryUtil.Shared; DataStream = new(Heap, 100, true); } @@ -186,8 +194,10 @@ namespace VNLib.Hashing.IdentityUtility #endregion #region Payload + private int PayloadStart => HeaderEnd + 1; private int PayloadEnd; + /// <summary> /// The Base64URL encoded UTF8 bytes of the payload portion of the current JWT /// </summary> @@ -218,6 +228,7 @@ namespace VNLib.Hashing.IdentityUtility //Store final position PayloadEnd = ByteSize; } + /// <summary> /// Encodes the specified value and writes it to the /// internal buffer @@ -233,7 +244,7 @@ namespace VNLib.Hashing.IdentityUtility //Slice off the begiing of the buffer for the base64 encoding if(Base64.EncodeToUtf8(value, binBuffer.Span, out _, out int written) != OperationStatus.Done) { - throw new OutOfMemoryException(); + throw new InternalBufferTooSmallException("Failed to encode the specified value to base64"); } //Base64 encoded Span<byte> base64Data = binBuffer.Span[..written].Trim(PADDING_BYTES); @@ -245,8 +256,10 @@ namespace VNLib.Hashing.IdentityUtility #endregion #region Signature + private int SignatureStart => PayloadEnd + 1; private int SignatureEnd => ByteSize; + /// <summary> /// The Base64URL encoded UTF8 bytes of the signature portion of the current JWT /// </summary> @@ -266,23 +279,31 @@ namespace VNLib.Hashing.IdentityUtility public virtual void Sign(HashAlgorithm signatureAlgorithm) { Check(); + _ = signatureAlgorithm ?? throw new ArgumentNullException(nameof(signatureAlgorithm)); + //Calculate the size of the buffer to use for the current algorithm int bufferSize = signatureAlgorithm.HashSize / 8; + //Alloc buffer for signature output Span<byte> signatureBuffer = stackalloc byte[bufferSize]; + //Compute the hash of the current payload if(!signatureAlgorithm.TryComputeHash(DataBuffer, signatureBuffer, out int bytesWritten)) { - throw new OutOfMemoryException(); + throw new InternalBufferTooSmallException(); } + //Reset the stream position to the end of the payload DataStream.SetLength(PayloadEnd); + //Write leading period DataStream.WriteByte(SAEF_PERIOD); + //Write the signature data to the buffer WriteValue(signatureBuffer[..bytesWritten]); } + /// <summary> /// Use an RSA algorithm to sign the JWT message /// </summary> @@ -296,20 +317,27 @@ namespace VNLib.Hashing.IdentityUtility public virtual void Sign(RSA rsa, in HashAlgorithmName hashAlg, RSASignaturePadding padding, int hashSize) { Check(); + _ = rsa ?? throw new ArgumentNullException(nameof(rsa)); + //Calculate the size of the buffer to use for the current algorithm using UnsafeMemoryHandle<byte> sigBuffer = Heap.UnsafeAlloc<byte>(hashSize); + if(!rsa.TrySignData(HeaderAndPayload, sigBuffer.Span, hashAlg, padding, out int hashBytesWritten)) { - throw new OutOfMemoryException("Signature buffer is not large enough to store the hash"); + throw new InternalBufferTooSmallException("Signature buffer is not large enough to store the hash"); } + //Reset the stream position to the end of the payload DataStream.SetLength(PayloadEnd); + //Write leading period DataStream.WriteByte(SAEF_PERIOD); + //Write the signature data to the buffer WriteValue(sigBuffer.Span[..hashBytesWritten]); } + /// <summary> /// Use an RSA algorithm to sign the JWT message /// </summary> @@ -322,17 +350,23 @@ namespace VNLib.Hashing.IdentityUtility public virtual void Sign(ECDsa alg, in HashAlgorithmName hashAlg, int hashSize) { Check(); + _ = alg ?? throw new ArgumentNullException(nameof(alg)); + //Calculate the size of the buffer to use for the current algorithm using UnsafeMemoryHandle<byte> sigBuffer = Heap.UnsafeAlloc<byte>(hashSize); + if (!alg.TrySignData(HeaderAndPayload, sigBuffer.Span, hashAlg, out int hashBytesWritten)) { - throw new OutOfMemoryException("Signature buffer is not large enough to store the hash"); + throw new InternalBufferTooSmallException("Signature buffer is not large enough to store the hash"); } + //Reset the stream position to the end of the payload DataStream.SetLength(PayloadEnd); + //Write leading period DataStream.WriteByte(SAEF_PERIOD); + //Write the signature data to the buffer WriteValue(sigBuffer.Span[..hashBytesWritten]); } @@ -379,7 +413,6 @@ namespace VNLib.Hashing.IdentityUtility //Clear pointers, so buffer get operations just return empty instead of throwing Reset(); DataStream.Dispose(); - } - + } } } diff --git a/lib/Hashing.Portable/src/ManagedHash.cs b/lib/Hashing.Portable/src/ManagedHash.cs index 46a8cb8..b4e5f09 100644 --- a/lib/Hashing.Portable/src/ManagedHash.cs +++ b/lib/Hashing.Portable/src/ManagedHash.cs @@ -79,10 +79,13 @@ namespace VNLib.Hashing public static ERRNO ComputeHash(ReadOnlySpan<char> data, Span<byte> buffer, HashAlg type) { int byteCount = CharEncoding.GetByteCount(data); + //Alloc buffer - using UnsafeMemoryHandle<byte> binbuf = Memory.UnsafeAlloc<byte>(byteCount, true); + using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc<byte>(byteCount, true); + //Encode data byteCount = CharEncoding.GetBytes(data, binbuf); + //hash the buffer return ComputeHash(binbuf.Span[..byteCount], buffer, type); } @@ -99,7 +102,7 @@ namespace VNLib.Hashing { int byteCount = CharEncoding.GetByteCount(data); //Alloc buffer - using UnsafeMemoryHandle<byte> binbuf = Memory.UnsafeAlloc<byte>(byteCount, true); + using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc<byte>(byteCount, true); //Encode data byteCount = CharEncoding.GetBytes(data, binbuf); //hash the buffer @@ -229,10 +232,13 @@ namespace VNLib.Hashing public static ERRNO ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<char> data, Span<byte> output, HashAlg type) { int byteCount = CharEncoding.GetByteCount(data); + //Alloc buffer - using UnsafeMemoryHandle<byte> binbuf = Memory.UnsafeAlloc<byte>(byteCount, true); + using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc<byte>(byteCount, true); + //Encode data byteCount = CharEncoding.GetBytes(data, binbuf); + //hash the buffer return ComputeHmac(key, binbuf.Span[..byteCount], output, type); } @@ -249,10 +255,13 @@ namespace VNLib.Hashing public static byte[] ComputeHmac(ReadOnlySpan<byte> key, ReadOnlySpan<char> data, HashAlg type) { int byteCount = CharEncoding.GetByteCount(data); + //Alloc buffer - using UnsafeMemoryHandle<byte> binbuf = Memory.UnsafeAlloc<byte>(byteCount, true); + using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc<byte>(byteCount, true); + //Encode data byteCount = CharEncoding.GetBytes(data, binbuf); + //hash the buffer return ComputeHmac(key, binbuf.Span[..byteCount], type); } @@ -317,12 +326,15 @@ namespace VNLib.Hashing { //Alloc hash buffer Span<byte> hashBuffer = stackalloc byte[(int)type]; + //hash the buffer ERRNO count = ComputeHmac(key, data, hashBuffer, type); + if (!count) { throw new InternalBufferTooSmallException("Failed to compute the hash of the data"); } + //Convert to hex string return mode switch { @@ -347,12 +359,15 @@ namespace VNLib.Hashing { //Alloc hash buffer Span<byte> hashBuffer = stackalloc byte[(int)type]; + //hash the buffer ERRNO count = ComputeHmac(key, data, hashBuffer, type); + if (!count) { throw new InternalBufferTooSmallException("Failed to compute the hash of the data"); } + //Convert to hex string return mode switch { diff --git a/lib/Hashing.Portable/src/RandomHash.cs b/lib/Hashing.Portable/src/RandomHash.cs index 5a4fc66..67518ad 100644 --- a/lib/Hashing.Portable/src/RandomHash.cs +++ b/lib/Hashing.Portable/src/RandomHash.cs @@ -23,6 +23,7 @@ */ using System; +using System.Runtime.CompilerServices; using System.Security.Cryptography; using VNLib.Utils; @@ -36,6 +37,8 @@ namespace VNLib.Hashing public static class RandomHash { + private const int MAX_STACK_ALLOC = 128; + /// <summary> /// Generates a cryptographic random number, computes the hash, and encodes the hash as a string. /// </summary> @@ -45,12 +48,28 @@ namespace VNLib.Hashing /// <returns>String containing hash of the random number</returns> public static string GetRandomHash(HashAlg alg, int size = 64, HashEncodingMode encoding = HashEncodingMode.Base64) { - //Get temporary buffer for storing random keys - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(size); - //Fill with random non-zero bytes - GetRandomBytes(buffer.Span); - //Compute hash - return ManagedHash.ComputeHash(buffer.Span, alg, encoding); + if(size > MAX_STACK_ALLOC) + { + //Get temporary buffer for storing random keys + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(size); + + //Fill with random non-zero bytes + GetRandomBytes(buffer.Span); + + //Compute hash + return ManagedHash.ComputeHash(buffer.Span, alg, encoding); + } + else + { + //Get temporary buffer for storing random keys + Span<byte> buffer = stackalloc byte[size]; + + //Fill with random non-zero bytes + GetRandomBytes(buffer); + + //Compute hash + return ManagedHash.ComputeHash(buffer, alg, encoding); + } } /// <summary> @@ -60,25 +79,27 @@ namespace VNLib.Hashing /// <exception cref="FormatException"></exception> public static string GetGuidHash(HashAlg alg, HashEncodingMode encoding = HashEncodingMode.Base64) { - //Get temp buffer - Span<byte> buffer = stackalloc byte[16]; + //Get temp buffer, the size of the guid + Span<byte> buffer = stackalloc byte[Unsafe.SizeOf<Guid>()]; + //Get a new GUID and write bytes to - if (!Guid.NewGuid().TryWriteBytes(buffer)) - { - throw new FormatException("Failed to get a guid hash"); - } - return ManagedHash.ComputeHash(buffer, alg, encoding); + return Guid.NewGuid().TryWriteBytes(buffer) + ? ManagedHash.ComputeHash(buffer, alg, encoding) + : throw new FormatException("Failed to get a guid hash"); } + /// <summary> /// Generates a secure random number and seeds a GUID object, then returns the string GUID /// </summary> /// <returns>Guid string</returns> public static Guid GetSecureGuid() { - //Get temp buffer - Span<byte> buffer = stackalloc byte[16]; + //Get temp buffer size of Guid + Span<byte> buffer = stackalloc byte[Unsafe.SizeOf<Guid>()]; + //Generate non zero bytes GetRandomBytes(buffer); + //Get a GUID initialized with the key data and return the string represendation return new Guid(buffer); } @@ -90,13 +111,30 @@ namespace VNLib.Hashing /// <returns>Base64 string of the random number</returns> public static string GetRandomBase64(int size = 64) { - //Get temp buffer - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(size); - //Generate non zero bytes - GetRandomBytes(buffer.Span); - //Convert to base 64 - return Convert.ToBase64String(buffer.Span, Base64FormattingOptions.None); + if (size > MAX_STACK_ALLOC) + { + //Get temp buffer + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(size); + + //Generate non zero bytes + GetRandomBytes(buffer.Span); + + //Convert to base 64 + return Convert.ToBase64String(buffer.Span, Base64FormattingOptions.None); + } + else + { + //Get temp buffer + Span<byte> buffer = stackalloc byte[size]; + + //Generate non zero bytes + GetRandomBytes(buffer); + + //Convert to base 64 + return Convert.ToBase64String(buffer, Base64FormattingOptions.None); + } } + /// <summary> /// Generates a cryptographic random number and returns the hex string of that number /// </summary> @@ -104,13 +142,30 @@ namespace VNLib.Hashing /// <returns>Hex string of the random number</returns> public static string GetRandomHex(int size = 64) { - //Get temp buffer - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(size); - //Generate non zero bytes - GetRandomBytes(buffer.Span); - //Convert to hex - return Convert.ToHexString(buffer.Span); + if (size > MAX_STACK_ALLOC) + { + //Get temp buffer + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(size); + + //Generate non zero bytes + GetRandomBytes(buffer.Span); + + //Convert to hex + return Convert.ToHexString(buffer.Span); + } + else + { + //Get temp buffer + Span<byte> buffer = stackalloc byte[size]; + + //Generate non zero bytes + GetRandomBytes(buffer); + + //Convert to hex + return Convert.ToHexString(buffer); + } } + /// <summary> /// Generates a cryptographic random number and returns the Base32 encoded string of that number /// </summary> @@ -118,12 +173,28 @@ namespace VNLib.Hashing /// <returns>Base32 string of the random number</returns> public static string GetRandomBase32(int size = 64) { - //Get temporary buffer for storing random keys - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(size); - //Fill with random non-zero bytes - GetRandomBytes(buffer.Span); - //Return string of encoded data - return VnEncoding.ToBase32String(buffer.Span); + if (size > MAX_STACK_ALLOC) + { + //Get temp buffer + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(size); + + //Generate non zero bytes + GetRandomBytes(buffer.Span); + + //Convert to hex + return VnEncoding.ToBase32String(buffer.Span); + } + else + { + //Get temp buffer + Span<byte> buffer = stackalloc byte[size]; + + //Generate non zero bytes + GetRandomBytes(buffer); + + //Convert to hex + return VnEncoding.ToBase32String(buffer); + } } /// <summary> @@ -137,13 +208,11 @@ namespace VNLib.Hashing GetRandomBytes(rand); return rand; } + /// <summary> /// Fill the buffer with non-zero bytes /// </summary> /// <param name="data">Buffer to fill</param> - public static void GetRandomBytes(Span<byte> data) - { - RandomNumberGenerator.Fill(data); - } + public static void GetRandomBytes(Span<byte> data) => RandomNumberGenerator.Fill(data); } }
\ No newline at end of file |