From 52b8e30437e235817ed534dec860e781bb0468c0 Mon Sep 17 00:00:00 2001 From: vnugent Date: Sat, 14 Jan 2023 16:24:28 -0500 Subject: MemoryUtil native integer size update + tests --- lib/Hashing.Portable/src/Argon2/VnArgon2.cs | 24 +++- .../src/IdentityUtility/HashingExtensions.cs | 57 ++++++-- .../src/IdentityUtility/JsonWebKey.cs | 5 +- .../src/IdentityUtility/JsonWebToken.cs | 61 +++++++-- lib/Hashing.Portable/src/ManagedHash.cs | 23 +++- lib/Hashing.Portable/src/RandomHash.cs | 143 +++++++++++++++------ 6 files changed, 245 insertions(+), 68 deletions(-) (limited to 'lib/Hashing.Portable/src') 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 _heap = new (Memory.InitializeNewHeapForProcess, LazyThreadSafetyMode.PublicationOnly); + private static readonly Lazy _heap = new (MemoryUtil.InitializeNewHeapForProcess, LazyThreadSafetyMode.PublicationOnly); private static readonly Lazy _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 buffer = PwHeap.Alloc(saltbytes + passBytes, true); + Span saltBuffer = buffer.AsSpan(0, saltbytes); Span 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 pwdHandle = PwHeap.Alloc(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 hashHandle = PwHeap.Alloc(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 outputHandle = PwHeap.Alloc(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 rawBufferHandle = Memory.Shared.Alloc(passBase64BufSize + saltBase64BufSize + rawPassLen, true); + using MemoryHandle rawBufferHandle = MemoryUtil.Shared.Alloc(passBase64BufSize + saltBase64BufSize + rawPassLen, true); + //Split buffers Span saltBuf = rawBufferHandle.Span[..saltBase64BufSize]; Span 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 /// The data to compute the hash of /// The used to encode the character buffer /// The base64 UTF8 string of the computed hash of the specified data - public static string ComputeBase64Hash(this HMAC hmac, ReadOnlySpan data, Encoding encoding = null) + /// + /// + public static string ComputeBase64Hash(this HMAC hmac, ReadOnlySpan 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 buffer = Memory.UnsafeAlloc(encBufSize + hashBufSize); + using UnsafeMemoryHandle buffer = MemoryUtil.UnsafeAlloc(encBufSize + hashBufSize); + Span encBuffer = buffer.Span[0..encBufSize]; Span 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]); } + /// /// Computes the hash of the raw data and compares the computed hash against /// the specified base64hash @@ -77,36 +89,48 @@ namespace VNLib.Hashing.IdentityUtility /// A value indicating if the hash values match /// /// - public static bool VerifyBase64Hash(this HMAC hmac, ReadOnlySpan base64Hmac, ReadOnlySpan raw, Encoding encoding = null) + /// + public static bool VerifyBase64Hash(this HMAC hmac, ReadOnlySpan base64Hmac, ReadOnlySpan 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 buffer = Memory.UnsafeAlloc(rawDataBufSize + base64BufSize, true); + using UnsafeMemoryHandle buffer = MemoryUtil.UnsafeAlloc(rawDataBufSize + base64BufSize, true); + Span rawDataBuf = buffer.Span[0..rawDataBufSize]; Span 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); } + /// /// Computes the hash of the raw data and compares the computed hash against /// the specified hash @@ -117,25 +141,33 @@ namespace VNLib.Hashing.IdentityUtility /// A value indicating if the hash values match /// /// + /// public static bool VerifyHash(this HMAC hmac, ReadOnlySpan hash, ReadOnlySpan 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 buffer = Memory.UnsafeAlloc(hashBufSize); + using UnsafeMemoryHandle buffer = MemoryUtil.UnsafeAlloc(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 /// /// /// - public static ERRNO TryEncrypt(this RSA alg, ReadOnlySpan data, in Span output, RSAEncryptionPadding padding, Encoding enc = null) + public static ERRNO TryEncrypt(this RSA alg, ReadOnlySpan data, in Span 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 buffer = Memory.UnsafeAlloc(buffSize, true); + using UnsafeMemoryHandle buffer = MemoryUtil.UnsafeAlloc(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 binBuffer = Memory.UnsafeAlloc(base64.Length + 16, false); + using UnsafeMemoryHandle binBuffer = MemoryUtil.UnsafeAlloc(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 /// - 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 /// /// /// - public static JsonWebToken Parse(ReadOnlySpan urlEncJwtString, IUnmangedHeap heap = null) + public static JsonWebToken Parse(ReadOnlySpan 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 binBuffer = heap.Alloc(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 /// /// /// - public static JsonWebToken ParseRaw(ReadOnlySpan utf8JWTData, IUnmangedHeap heap = null) + public static JsonWebToken ParseRaw(ReadOnlySpan 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; } /// /// Creates a new empty JWT instance, with an optional heap to alloc - /// buffers from. ( is used as default) + /// buffers from. ( is used as default) /// /// The to alloc buffers from - 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; + /// /// The Base64URL encoded UTF8 bytes of the payload portion of the current JWT /// @@ -218,6 +228,7 @@ namespace VNLib.Hashing.IdentityUtility //Store final position PayloadEnd = ByteSize; } + /// /// 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 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; + /// /// The Base64URL encoded UTF8 bytes of the signature portion of the current JWT /// @@ -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 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]); } + /// /// Use an RSA algorithm to sign the JWT message /// @@ -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 sigBuffer = Heap.UnsafeAlloc(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]); } + /// /// Use an RSA algorithm to sign the JWT message /// @@ -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 sigBuffer = Heap.UnsafeAlloc(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 data, Span buffer, HashAlg type) { int byteCount = CharEncoding.GetByteCount(data); + //Alloc buffer - using UnsafeMemoryHandle binbuf = Memory.UnsafeAlloc(byteCount, true); + using UnsafeMemoryHandle binbuf = MemoryUtil.UnsafeAlloc(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 binbuf = Memory.UnsafeAlloc(byteCount, true); + using UnsafeMemoryHandle binbuf = MemoryUtil.UnsafeAlloc(byteCount, true); //Encode data byteCount = CharEncoding.GetBytes(data, binbuf); //hash the buffer @@ -229,10 +232,13 @@ namespace VNLib.Hashing public static ERRNO ComputeHmac(ReadOnlySpan key, ReadOnlySpan data, Span output, HashAlg type) { int byteCount = CharEncoding.GetByteCount(data); + //Alloc buffer - using UnsafeMemoryHandle binbuf = Memory.UnsafeAlloc(byteCount, true); + using UnsafeMemoryHandle binbuf = MemoryUtil.UnsafeAlloc(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 key, ReadOnlySpan data, HashAlg type) { int byteCount = CharEncoding.GetByteCount(data); + //Alloc buffer - using UnsafeMemoryHandle binbuf = Memory.UnsafeAlloc(byteCount, true); + using UnsafeMemoryHandle binbuf = MemoryUtil.UnsafeAlloc(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 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 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; + /// /// Generates a cryptographic random number, computes the hash, and encodes the hash as a string. /// @@ -45,12 +48,28 @@ namespace VNLib.Hashing /// String containing hash of the random number public static string GetRandomHash(HashAlg alg, int size = 64, HashEncodingMode encoding = HashEncodingMode.Base64) { - //Get temporary buffer for storing random keys - using UnsafeMemoryHandle buffer = Memory.UnsafeAlloc(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 buffer = MemoryUtil.UnsafeAlloc(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 buffer = stackalloc byte[size]; + + //Fill with random non-zero bytes + GetRandomBytes(buffer); + + //Compute hash + return ManagedHash.ComputeHash(buffer, alg, encoding); + } } /// @@ -60,25 +79,27 @@ namespace VNLib.Hashing /// public static string GetGuidHash(HashAlg alg, HashEncodingMode encoding = HashEncodingMode.Base64) { - //Get temp buffer - Span buffer = stackalloc byte[16]; + //Get temp buffer, the size of the guid + Span buffer = stackalloc byte[Unsafe.SizeOf()]; + //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"); } + /// /// Generates a secure random number and seeds a GUID object, then returns the string GUID /// /// Guid string public static Guid GetSecureGuid() { - //Get temp buffer - Span buffer = stackalloc byte[16]; + //Get temp buffer size of Guid + Span buffer = stackalloc byte[Unsafe.SizeOf()]; + //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 /// Base64 string of the random number public static string GetRandomBase64(int size = 64) { - //Get temp buffer - using UnsafeMemoryHandle buffer = Memory.UnsafeAlloc(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 buffer = MemoryUtil.UnsafeAlloc(size); + + //Generate non zero bytes + GetRandomBytes(buffer.Span); + + //Convert to base 64 + return Convert.ToBase64String(buffer.Span, Base64FormattingOptions.None); + } + else + { + //Get temp buffer + Span buffer = stackalloc byte[size]; + + //Generate non zero bytes + GetRandomBytes(buffer); + + //Convert to base 64 + return Convert.ToBase64String(buffer, Base64FormattingOptions.None); + } } + /// /// Generates a cryptographic random number and returns the hex string of that number /// @@ -104,13 +142,30 @@ namespace VNLib.Hashing /// Hex string of the random number public static string GetRandomHex(int size = 64) { - //Get temp buffer - using UnsafeMemoryHandle buffer = Memory.UnsafeAlloc(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 buffer = MemoryUtil.UnsafeAlloc(size); + + //Generate non zero bytes + GetRandomBytes(buffer.Span); + + //Convert to hex + return Convert.ToHexString(buffer.Span); + } + else + { + //Get temp buffer + Span buffer = stackalloc byte[size]; + + //Generate non zero bytes + GetRandomBytes(buffer); + + //Convert to hex + return Convert.ToHexString(buffer); + } } + /// /// Generates a cryptographic random number and returns the Base32 encoded string of that number /// @@ -118,12 +173,28 @@ namespace VNLib.Hashing /// Base32 string of the random number public static string GetRandomBase32(int size = 64) { - //Get temporary buffer for storing random keys - using UnsafeMemoryHandle buffer = Memory.UnsafeAlloc(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 buffer = MemoryUtil.UnsafeAlloc(size); + + //Generate non zero bytes + GetRandomBytes(buffer.Span); + + //Convert to hex + return VnEncoding.ToBase32String(buffer.Span); + } + else + { + //Get temp buffer + Span buffer = stackalloc byte[size]; + + //Generate non zero bytes + GetRandomBytes(buffer); + + //Convert to hex + return VnEncoding.ToBase32String(buffer); + } } /// @@ -137,13 +208,11 @@ namespace VNLib.Hashing GetRandomBytes(rand); return rand; } + /// /// Fill the buffer with non-zero bytes /// /// Buffer to fill - public static void GetRandomBytes(Span data) - { - RandomNumberGenerator.Fill(data); - } + public static void GetRandomBytes(Span data) => RandomNumberGenerator.Fill(data); } } \ No newline at end of file -- cgit