aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs31
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs30
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs53
-rw-r--r--lib/Plugins.Runtime/src/PluginController.cs89
-rw-r--r--lib/Plugins.Runtime/src/RuntimePluginLoader.cs6
5 files changed, 162 insertions, 47 deletions
diff --git a/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs b/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs
index e8bd13f..77874ba 100644
--- a/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs
+++ b/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs
@@ -410,15 +410,30 @@ namespace VNLib.Hashing.IdentityUtility
{
return null;
}
-
- //bin buffer for temp decoding
- 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;
+ //Use stack buffer
+ if(base64.Length <= 64)
+ {
+ //Use stack buffer
+ Span<byte> buffer = stackalloc byte[84];
+
+ //base64url decode
+ ERRNO count = VnEncoding.Base64UrlDecode(base64, buffer);
+
+ //Return buffer or null if failed
+ return count ? buffer[0.. (int)count].ToArray() : null;
+ }
+ else
+ {
+ //bin buffer for temp decoding
+ 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 55e31fc..2bc4f24 100644
--- a/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs
+++ b/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs
@@ -301,7 +301,7 @@ namespace VNLib.Hashing.IdentityUtility
Span<byte> signatureBuffer = stackalloc byte[bufferSize];
//Compute the hash of the current payload
- if(!signatureAlgorithm.TryComputeHash(DataBuffer, signatureBuffer, out int bytesWritten))
+ if(!signatureAlgorithm.TryComputeHash(HeaderAndPayload, signatureBuffer, out int bytesWritten))
{
throw new InternalBufferTooSmallException();
}
@@ -326,7 +326,7 @@ namespace VNLib.Hashing.IdentityUtility
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
- public virtual void Sign(RSA rsa, in HashAlgorithmName hashAlg, RSASignaturePadding padding, int hashSize)
+ public virtual void Sign(RSA rsa, HashAlgorithmName hashAlg, RSASignaturePadding padding, int hashSize)
{
Check();
@@ -359,7 +359,7 @@ namespace VNLib.Hashing.IdentityUtility
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
- public virtual void Sign(ECDsa alg, in HashAlgorithmName hashAlg, int hashSize)
+ public virtual void Sign(ECDsa alg, HashAlgorithmName hashAlg, int hashSize)
{
Check();
@@ -383,6 +383,30 @@ namespace VNLib.Hashing.IdentityUtility
WriteValue(sigBuffer.Span[..hashBytesWritten]);
}
+ /// <summary>
+ /// Signs the JWT data using HMAC without allocating a <see cref="HashAlgorithm"/>
+ /// instance.
+ /// </summary>
+ /// <param name="alg">The algorithm used to sign</param>
+ /// <param name="key">The key data</param>
+ /// <exception cref="InternalBufferTooSmallException"></exception>
+ public virtual void Sign(ReadOnlySpan<byte> key, HashAlg alg)
+ {
+ //Stack hash output buffer, will be the size of the alg
+ Span<byte> sigOut = stackalloc byte[(int)alg];
+
+ //Compute
+ ERRNO count = ManagedHash.ComputeHmac(key, HeaderAndPayload, sigOut, alg);
+
+ if (!count)
+ {
+ throw new InternalBufferTooSmallException("Failed to compute the hmac signature because the internal buffer was mis-sized");
+ }
+
+ //write
+ WriteValue(sigOut[..(int)count]);
+ }
+
#endregion
///<inheritdoc/>
diff --git a/lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs b/lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs
index fca2e75..3331738 100644
--- a/lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs
+++ b/lib/Hashing.Portable/src/IdentityUtility/JwtExtensions.cs
@@ -204,6 +204,55 @@ namespace VNLib.Hashing.IdentityUtility
//Verify the signatures and return results
return CryptographicOperations.FixedTimeEquals(jwt.SignatureData, base64);
}
+
+ /// <summary>
+ /// Verifies the current JWT body-segements against the parsed signature segment.
+ /// </summary>
+ /// <param name="jwt"></param>
+ /// <param name="alg">
+ /// The HMAC algorithm to use when calculating the hash of the JWT
+ /// </param>
+ /// <param name="key">The HMAC shared symetric key</param>
+ /// <returns>
+ /// True if the signature field of the current JWT matches the re-computed signature of the header and data-fields
+ /// signature
+ /// </returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ObjectDisposedException"></exception>
+ /// <exception cref="InternalBufferTooSmallException"></exception>
+ public static bool Verify(this JsonWebToken jwt, ReadOnlySpan<byte> key, HashAlg alg)
+ {
+ _ = jwt ?? throw new ArgumentNullException(nameof(jwt));
+
+ //Get base64 buffer size for in-place conversion
+ int bufferSize = Base64.GetMaxEncodedToUtf8Length((int)alg);
+
+ //Alloc buffer for signature output
+ Span<byte> signatureBuffer = stackalloc byte[bufferSize];
+
+ //Compute the hash of the current payload
+ ERRNO count = ManagedHash.ComputeHmac(key, jwt.HeaderAndPayload, signatureBuffer, alg);
+ if (!count)
+ {
+ throw new InternalBufferTooSmallException("Failed to compute the hash of the JWT data");
+ }
+
+ //Do an in-place base64 conversion of the signature to base64
+ if (Base64.EncodeToUtf8InPlace(signatureBuffer, count, out int base64BytesWritten) != OperationStatus.Done)
+ {
+ throw new InternalBufferTooSmallException("Failed to convert the signature buffer to its base64 because the buffer was too small");
+ }
+
+ //Trim padding
+ Span<byte> base64 = signatureBuffer[..base64BytesWritten].Trim(JsonWebToken.PADDING_BYTES);
+
+ //Urlencode
+ VnEncoding.Base64ToUrlSafeInPlace(base64);
+
+ //Verify the signatures and return results
+ return CryptographicOperations.FixedTimeEquals(jwt.SignatureData, base64);
+ }
+
/// <summary>
/// Verifies the signature of the data using the specified <see cref="RSA"/> and hash parameters
/// </summary>
@@ -217,7 +266,7 @@ namespace VNLib.Hashing.IdentityUtility
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public static bool Verify(this JsonWebToken jwt, RSA alg, in HashAlgorithmName hashAlg, RSASignaturePadding padding)
+ public static bool Verify(this JsonWebToken jwt, RSA alg, HashAlgorithmName hashAlg, RSASignaturePadding padding)
{
_ = jwt ?? throw new ArgumentNullException(nameof(jwt));
_ = alg ?? throw new ArgumentNullException(nameof(alg));
@@ -243,7 +292,7 @@ namespace VNLib.Hashing.IdentityUtility
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public static bool Verify(this JsonWebToken jwt, ECDsa alg, in HashAlgorithmName hashAlg)
+ public static bool Verify(this JsonWebToken jwt, ECDsa alg, HashAlgorithmName hashAlg)
{
_ = alg ?? throw new ArgumentNullException(nameof(alg));
//Decode the signature
diff --git a/lib/Plugins.Runtime/src/PluginController.cs b/lib/Plugins.Runtime/src/PluginController.cs
index 14ea7f0..f602492 100644
--- a/lib/Plugins.Runtime/src/PluginController.cs
+++ b/lib/Plugins.Runtime/src/PluginController.cs
@@ -25,6 +25,7 @@
using System;
using System.Linq;
using System.Text.Json;
+using System.Threading;
using System.Reflection;
using System.Collections.Generic;
@@ -38,6 +39,18 @@ namespace VNLib.Plugins.Runtime
/// </summary>
public sealed class PluginController : IPluginEventRegistrar
{
+ /*
+ * Lock must be held any time the internals lists are read/written
+ * to avoid read/write enumeration issues.
+ *
+ * This can happen when a manual unload is called duiring an automatic
+ * reload, or a runtime is tearing down the plugin environment
+ * when an automatic reload is happening.
+ *
+ * This also allows thread safe register/unregister event listeners
+ */
+ private readonly object _stateLock = new();
+
private readonly List<LivePlugin> _plugins;
private readonly List<KeyValuePair<IPluginEventListener, object?>> _listeners;
@@ -58,61 +71,80 @@ namespace VNLib.Plugins.Runtime
{
_ = listener ?? throw new ArgumentNullException(nameof(listener));
- _listeners.Add(new(listener, state));
+ lock (_stateLock)
+ {
+ _listeners.Add(new(listener, state));
+ }
}
///<inheritdoc/>
public bool Unregister(IPluginEventListener listener)
{
- //Remove listener
- return _listeners.RemoveAll(p => p.Key == listener) > 0;
+ lock(_stateLock)
+ {
+ //Remove listener
+ return _listeners.RemoveAll(p => p.Key == listener) > 0;
+ }
}
internal void InitializePlugins(Assembly asm)
{
- //get all Iplugin types
- Type[] types = asm.GetTypes().Where(static type => !type.IsAbstract && typeof(IPlugin).IsAssignableFrom(type)).ToArray();
+ lock (_stateLock)
+ {
+ //get all Iplugin types
+ Type[] types = asm.GetTypes().Where(static type => !type.IsAbstract && typeof(IPlugin).IsAssignableFrom(type)).ToArray();
- //Initialize the new plugin instances
- IPlugin[] plugins = types.Select(static t => (IPlugin)Activator.CreateInstance(t)!).ToArray();
+ //Initialize the new plugin instances
+ IPlugin[] plugins = types.Select(static t => (IPlugin)Activator.CreateInstance(t)!).ToArray();
- //Crate new containers
- LivePlugin[] lps = plugins.Select(p => new LivePlugin(p, asm)).ToArray();
+ //Crate new containers
+ LivePlugin[] lps = plugins.Select(p => new LivePlugin(p, asm)).ToArray();
- //Store containers
- _plugins.AddRange(lps);
+ //Store containers
+ _plugins.AddRange(lps);
+ }
}
internal void ConfigurePlugins(JsonDocument hostDom, JsonDocument pluginDom, string[] cliArgs)
{
- _plugins.TryForeach(lp => lp.InitConfig(hostDom, pluginDom));
- _plugins.TryForeach(lp => lp.InitLog(cliArgs));
+ lock (_stateLock)
+ {
+ _plugins.TryForeach(lp => lp.InitConfig(hostDom, pluginDom));
+ _plugins.TryForeach(lp => lp.InitLog(cliArgs));
+ }
}
internal void LoadPlugins()
{
- //Load all plugins
- _plugins.TryForeach(static p => p.LoadPlugin());
+ lock( _stateLock)
+ {
+ //Load all plugins
+ _plugins.TryForeach(static p => p.LoadPlugin());
- //Notify event handlers
- _listeners.TryForeach(l => l.Key.OnPluginLoaded(this, l.Value));
+ //Notify event handlers
+ _listeners.TryForeach(l => l.Key.OnPluginLoaded(this, l.Value));
+ }
}
internal void UnloadPlugins()
{
- try
- {
- //Notify event handlers
- _listeners.TryForeach(l => l.Key.OnPluginUnloaded(this, l.Value));
-
- //Unload plugin instances
- _plugins.TryForeach(static p => p.UnloadPlugin());
- }
- finally
+ lock (_stateLock)
{
- //Always
- _plugins.Clear();
+ try
+ {
+ //Notify event handlers
+ _listeners.TryForeach(l => l.Key.OnPluginUnloaded(this, l.Value));
+
+ //Unload plugin instances
+ _plugins.TryForeach(static p => p.UnloadPlugin());
+ }
+ finally
+ {
+ //Always
+ _plugins.Clear();
+
+ }
}
}
@@ -121,7 +153,6 @@ namespace VNLib.Plugins.Runtime
_plugins.Clear();
_listeners.Clear();
}
-
}
}
diff --git a/lib/Plugins.Runtime/src/RuntimePluginLoader.cs b/lib/Plugins.Runtime/src/RuntimePluginLoader.cs
index d79def3..698ce56 100644
--- a/lib/Plugins.Runtime/src/RuntimePluginLoader.cs
+++ b/lib/Plugins.Runtime/src/RuntimePluginLoader.cs
@@ -184,11 +184,7 @@ namespace VNLib.Plugins.Runtime
/// to block individual loading.
/// </summary>
/// <exception cref="AggregateException"></exception>
- public void LoadPlugins()
- {
- //Load all plugins
- Controller.LoadPlugins();
- }
+ public void LoadPlugins() => Controller.LoadPlugins();
/// <summary>
/// Manually reload the internal <see cref="PluginLoader"/>