diff options
Diffstat (limited to 'lib')
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"/> |