diff options
Diffstat (limited to 'lib/VNLib.Plugins.Extensions.Loading')
5 files changed, 109 insertions, 40 deletions
diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/AssemblyLoader.cs b/lib/VNLib.Plugins.Extensions.Loading/src/AssemblyLoader.cs index f3e03dd..2827587 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/AssemblyLoader.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/AssemblyLoader.cs @@ -153,7 +153,7 @@ namespace VNLib.Plugins.Extensions.Loading conf.PreferSharedTypes = true; //Share utils asm - conf.SharedAssemblies.Add(typeof(Utils.Memory.Memory).Assembly.GetName()); + conf.SharedAssemblies.Add(typeof(Utils.Memory.MemoryUtil).Assembly.GetName()); }); return new(loader, in unloadToken); diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs index fc88612..46d9585 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/LoadingExtensions.cs @@ -35,6 +35,7 @@ using VNLib.Utils; using VNLib.Utils.Logging; using VNLib.Utils.Extensions; using VNLib.Plugins.Essentials.Accounts; +using VNLib.Utils.Memory; namespace VNLib.Plugins.Extensions.Loading { @@ -112,18 +113,12 @@ namespace VNLib.Plugins.Extensions.Loading private static PasswordHashing LoadPasswords(PluginBase plugin) { PasswordHashing Passwords; - //Get the global password system secret (pepper) - byte[] pepper = plugin.TryGetSecretAsync(PASSWORD_HASHING_KEY) - .ToBase64Bytes().Result ?? throw new KeyNotFoundException($"Missing required key '{PASSWORD_HASHING_KEY}' in secrets"); - ERRNO cb(Span<byte> buffer) - { - //No longer valid peper if plugin is unloaded as its set to zero, so we need to protect it - plugin.ThrowIfUnloaded(); + //Create new session provider + SecretProvider secrets = new(); - pepper.CopyTo(buffer); - return pepper.Length; - } + //Load the secret in the background + secrets.LoadSecret(plugin); //See hashing params are defined IReadOnlyDictionary<string, JsonElement>? hashingArgs = plugin.TryGetConfig(PASSWORD_HASHING_KEY); @@ -136,20 +131,13 @@ namespace VNLib.Plugins.Extensions.Loading uint memoryCost = hashingArgs["memory_cost"].GetUInt32(); uint parallelism = hashingArgs["parallelism"].GetUInt32(); //Load passwords - Passwords = new(cb, pepper.Length, (int)saltLen, timeCost, memoryCost, parallelism, hashLen); + Passwords = new(secrets, (int)saltLen, timeCost, memoryCost, parallelism, hashLen); } else { //Init default password hashing - Passwords = new(cb, pepper.Length); + Passwords = new(secrets); } - - //Register event to cleanup the password class - _ = plugin.RegisterForUnload(() => - { - //Zero the pepper - CryptographicOperations.ZeroMemory(pepper); - }); //return return Passwords; } @@ -184,7 +172,7 @@ namespace VNLib.Plugins.Extensions.Loading * assembly, otherwise search all plugins directories */ - string? assetDir = config["assets"].GetString(); + string? assetDir = config.GetPropString("assets"); assetDir ??= config["path"].GetString(); /* @@ -346,5 +334,56 @@ namespace VNLib.Plugins.Extensions.Loading return lazyFactory; } } + + private sealed class SecretProvider : ISecretProvider + { + private byte[]? _pepper; + private Exception? _error; + + ///<inheritdoc/> + public int BufferSize => _error != null ? throw _error : _pepper?.Length ?? 0; + + public ERRNO GetSecret(Span<byte> buffer) + { + if(_error != null) + { + throw _error; + } + //Coppy pepper to buffer + _pepper.CopyTo(buffer); + //Return pepper length + return _pepper!.Length; + } + + public void LoadSecret(PluginBase pbase) + { + _ = pbase.DeferTask(() => LoadSecretInternal(pbase)); + } + + private async Task LoadSecretInternal(PluginBase pbase) + { + try + { + //Get the pepper from secret storage + _pepper = await pbase.TryGetSecretAsync(PASSWORD_HASHING_KEY).ToBase64Bytes(); + + //Regsiter cleanup + _ = pbase.RegisterForUnload(Clear); + } + catch(Exception ex) + { + //Store exception for re-propagation + _error = ex; + } + } + + public void Clear() + { + //Clear the pepper if set + MemoryUtil.InitializeBlock(_pepper.AsSpan()); + } + + + } } } diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/PrivateKey.cs b/lib/VNLib.Plugins.Extensions.Loading/src/PrivateKey.cs index b854419..2e5fb7f 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/PrivateKey.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/PrivateKey.cs @@ -49,14 +49,19 @@ namespace VNLib.Plugins.Extensions.Loading public ECDsa GetECDsa() { //Alloc buffer - using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(_utf8RawData.Length); + using IMemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(_utf8RawData.Length); + //Get base64 bytes from utf8 ERRNO count = VnEncoding.Base64UrlDecode(_utf8RawData, buffer.Span); + //Parse the private key ECDsa alg = ECDsa.Create(); + alg.ImportPkcs8PrivateKey(buffer.Span[..(int)count], out _); + //Wipe the buffer - Memory.InitializeBlock(buffer.Span); + MemoryUtil.InitializeBlock(buffer.Span); + return alg; } @@ -69,14 +74,19 @@ namespace VNLib.Plugins.Extensions.Loading public RSA GetRSA() { //Alloc buffer - using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(_utf8RawData.Length); + using IMemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(_utf8RawData.Length); + //Get base64 bytes from utf8 ERRNO count = VnEncoding.Base64UrlDecode(_utf8RawData, buffer.Span); + //Parse the private key RSA alg = RSA.Create(); + alg.ImportPkcs8PrivateKey(buffer.Span[..(int)count], out _); + //Wipe the buffer - Memory.InitializeBlock(buffer.Span); + MemoryUtil.InitializeBlock(buffer.Span); + return alg; } @@ -84,19 +94,22 @@ namespace VNLib.Plugins.Extensions.Loading { //Alloc and get utf8 byte[] buffer = new byte[secret.Result.Length]; + int count = Encoding.UTF8.GetBytes(secret.Result, buffer); + //Verify length if(count != buffer.Length) { throw new FormatException("UTF8 deocde failed"); } + //Store _utf8RawData = buffer; } protected override void Free() { - Memory.InitializeBlock(_utf8RawData.AsSpan()); + MemoryUtil.InitializeBlock(_utf8RawData.AsSpan()); } } } diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/SecretResult.cs b/lib/VNLib.Plugins.Extensions.Loading/src/SecretResult.cs index 817ef7a..6c1c5f8 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/SecretResult.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/SecretResult.cs @@ -48,13 +48,13 @@ namespace VNLib.Plugins.Extensions.Loading ///<inheritdoc/> protected override void Free() { - Memory.InitializeBlock(_secretChars.AsSpan()); + MemoryUtil.InitializeBlock(_secretChars.AsSpan()); } internal static SecretResult ToSecret(string? result) { SecretResult res = new(result.AsSpan()); - Memory.UnsafeZeroMemory<char>(result); + MemoryUtil.UnsafeZeroMemory<char>(result); return res; } } diff --git a/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs b/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs index 2b6bfd8..d3bdf42 100644 --- a/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs +++ b/lib/VNLib.Plugins.Extensions.Loading/src/VaultSecrets.cs @@ -328,20 +328,21 @@ namespace VNLib.Plugins.Extensions.Loading _ = secret ?? throw new ArgumentNullException(nameof(secret)); //Temp buffer - using UnsafeMemoryHandle<byte> buffer = Memory.UnsafeAlloc<byte>(secret.Result.Length); + using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc<byte>(secret.Result.Length); //Get base64 - if(Convert.TryFromBase64Chars(secret.Result, buffer, out int count)) + if(!Convert.TryFromBase64Chars(secret.Result, buffer, out int count)) { - //Copy to array - byte[] value = buffer.Span[..count].ToArray(); - //Clear block before returning - Memory.InitializeBlock<byte>(buffer); - - return value; + throw new InternalBufferTooSmallException("internal buffer too small"); } - throw new InternalBufferTooSmallException("internal buffer too small"); + //Copy to array + byte[] value = buffer.Span[..count].ToArray(); + + //Clear block before returning + MemoryUtil.InitializeBlock<byte>(buffer); + + return value; } /// <summary> @@ -354,7 +355,9 @@ namespace VNLib.Plugins.Extensions.Loading public static async Task<byte[]?> ToBase64Bytes(this Task<SecretResult?> secret) { _ = secret ?? throw new ArgumentNullException(nameof(secret)); + using SecretResult? sec = await secret.ConfigureAwait(false); + return sec?.GetFromBase64(); } @@ -378,12 +381,16 @@ namespace VNLib.Plugins.Extensions.Loading public static JsonDocument GetJsonDocument(this SecretResult secret) { _ = secret ?? throw new ArgumentNullException(nameof(secret)); + //Alloc buffer, utf8 so 1 byte per char - using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(secret.Result.Length); + using IMemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(secret.Result.Length); + //Get utf8 bytes int count = Encoding.UTF8.GetBytes(secret.Result, buffer.Span); + //Reader and parse Utf8JsonReader reader = new(buffer.Span[..count]); + return JsonDocument.ParseValue(ref reader); } @@ -396,10 +403,13 @@ namespace VNLib.Plugins.Extensions.Loading public static PublicKey GetPublicKey(this SecretResult secret) { _ = secret ?? throw new ArgumentNullException(nameof(secret)); + //Alloc buffer, base64 is larger than binary value so char len is large enough - using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(secret.Result.Length); + using IMemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(secret.Result.Length); + //Get base64 bytes ERRNO count = VnEncoding.TryFromBase64Chars(secret.Result, buffer.Span); + //Parse the SPKI from base64 return PublicKey.CreateFromSubjectPublicKeyInfo(buffer.Span[..(int)count], out _); } @@ -429,10 +439,13 @@ namespace VNLib.Plugins.Extensions.Loading public static ReadOnlyJsonWebKey GetJsonWebKey(this SecretResult secret) { _ = secret ?? throw new ArgumentNullException(nameof(secret)); + //Alloc buffer, utf8 so 1 byte per char - using IMemoryHandle<byte> buffer = Memory.SafeAlloc<byte>(secret.Result.Length); + using IMemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(secret.Result.Length); + //Get utf8 bytes int count = Encoding.UTF8.GetBytes(secret.Result, buffer.Span); + return new ReadOnlyJsonWebKey(buffer.Span[..count]); } @@ -446,7 +459,9 @@ namespace VNLib.Plugins.Extensions.Loading public static async Task<ReadOnlyJsonWebKey?> ToJsonWebKey(this Task<SecretResult?> secret) { _ = secret ?? throw new ArgumentNullException(nameof(secret)); + using SecretResult? sec = await secret.ConfigureAwait(false); + return sec?.GetJsonWebKey(); } @@ -464,7 +479,9 @@ namespace VNLib.Plugins.Extensions.Loading public static async Task<ReadOnlyJsonWebKey> ToJsonWebKey(this Task<SecretResult?> secret, bool required) { _ = secret ?? throw new ArgumentNullException(nameof(secret)); + using SecretResult? sec = await secret.ConfigureAwait(false); + //If required is true and result is null, raise an exception return required && sec == null ? throw new KeyNotFoundException("A required secret was missing") : (sec?.GetJsonWebKey()!); } |