aboutsummaryrefslogtreecommitdiff
path: root/lib/Hashing.Portable/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Hashing.Portable/src')
-rw-r--r--lib/Hashing.Portable/src/Argon2/VnArgon2.cs24
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs57
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs5
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs61
-rw-r--r--lib/Hashing.Portable/src/ManagedHash.cs23
-rw-r--r--lib/Hashing.Portable/src/RandomHash.cs143
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