diff options
author | vnugent <public@vaughnnugent.com> | 2024-03-19 21:54:49 -0400 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2024-03-19 21:54:49 -0400 |
commit | 9c7b564911080ccd5cbbb9851a0757b05e1e9047 (patch) | |
tree | d29ceca64c1463d2cd738f08de5884e8c18ebe7e /lib | |
parent | 6d8c3444e09561e5957491b3cc1ae858e0abdd14 (diff) |
refactor: JWK overhaul & add length getter to FileUpload
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs | 38 | ||||
-rw-r--r-- | lib/Hashing.Portable/src/IdentityUtility/ReadOnlyJsonWebKey.cs | 120 | ||||
-rw-r--r-- | lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs | 6 | ||||
-rw-r--r-- | lib/Net.Http/src/FileUpload.cs | 7 | ||||
-rw-r--r-- | lib/Utils/src/ArgumentList.cs | 9 |
5 files changed, 122 insertions, 58 deletions
diff --git a/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs b/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs index 65f0837..a237db0 100644 --- a/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs +++ b/lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs @@ -292,7 +292,7 @@ namespace VNLib.Hashing.IdentityUtility } /// <summary> - /// Gets the RSA private key algorithm from the supplied Json Web Key <see cref="JsonElement"/> + /// Gets the RSA private key algorithm from the supplied Json Web Key /// </summary> /// <param name="jwk"></param> /// <returns>The <see cref="RSA"/> algorithm if found, or null if the element does not contain private key</returns> @@ -303,7 +303,23 @@ namespace VNLib.Hashing.IdentityUtility return rSAParameters.HasValue ? RSA.Create(rSAParameters.Value) : null; } - private static RSAParameters? GetRsaParameters<TKey>(in TKey jwk, bool includePrivateKey) where TKey : IJsonWebKey + /// <summary> + /// Gets the RSA key parameters from the current Json Web Key + /// </summary>> + /// <param name="jwk"></param> + /// <param name="includePrivateKey">A value that indicates that a private key should be parsed and included in the parameters</param> + /// <returns>A nullable structure that contains the parsed keys, or null if required properties were empty</returns> + public static RSAParameters? GetRsaParameters(this ReadOnlyJsonWebKey jwk, bool includePrivateKey) + => GetRsaParameters(in jwk, includePrivateKey); + + /// <summary> + /// Gets the RSA key parameters from the current Json Web Key + /// </summary> + /// <typeparam name="TKey"></typeparam> + /// <param name="jwk"></param> + /// <param name="includePrivateKey">A value that indicates that a private key should be parsed and included in the parameters</param> + /// <returns>A nullable structure that contains the parsed keys, or null if required properties were empty</returns> + public static RSAParameters? GetRsaParameters<TKey>(in TKey jwk, bool includePrivateKey) where TKey : IJsonWebKey { //Get the RSA public key credentials ReadOnlySpan<char> e = jwk.GetKeyProperty("e"); @@ -372,9 +388,23 @@ namespace VNLib.Hashing.IdentityUtility //Return new alg return ecParams.HasValue ? ECDsa.Create(ecParams.Value) : null; } - - private static ECParameters? GetECParameters<TKey>(in TKey jwk, bool includePrivate) where TKey : IJsonWebKey + /// <summary> + /// Gets the EC key parameters from the current Json Web Key + /// </summary> + /// <param name="jwk"></param> + /// <param name="includePrivate">A value that inidcates if private key parameters should be parsed and included </param> + /// <returns>The parsed key parameter structure, or null if the key parameters were empty or could not be parsed</returns> + public static ECParameters? GetECParameters(this ReadOnlyJsonWebKey jwk, bool includePrivate) => GetECParameters(in jwk, includePrivate); + + /// <summary> + /// Gets the EC key parameters from the current Json Web Key + /// </summary> + /// <typeparam name="TKey"></typeparam> + /// <param name="jwk"></param> + /// <param name="includePrivate">A value that inidcates if private key parameters should be parsed and included </param> + /// <returns>The parsed key parameter structure, or null if the key parameters were empty or could not be parsed</returns> + public static ECParameters? GetECParameters<TKey>(in TKey jwk, bool includePrivate) where TKey : IJsonWebKey { //Get the RSA public key credentials ReadOnlySpan<char> x = jwk.GetKeyProperty("x"); diff --git a/lib/Hashing.Portable/src/IdentityUtility/ReadOnlyJsonWebKey.cs b/lib/Hashing.Portable/src/IdentityUtility/ReadOnlyJsonWebKey.cs index 009d6bf..c97a023 100644 --- a/lib/Hashing.Portable/src/IdentityUtility/ReadOnlyJsonWebKey.cs +++ b/lib/Hashing.Portable/src/IdentityUtility/ReadOnlyJsonWebKey.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Hashing.Portable @@ -23,11 +23,12 @@ */ using System; +using System.Linq; using System.Text.Json; using System.Collections.Generic; +using System.Collections.Frozen; -using VNLib.Utils; -using VNLib.Utils.Extensions; +using VNLib.Utils.Memory; namespace VNLib.Hashing.IdentityUtility { @@ -35,24 +36,19 @@ namespace VNLib.Hashing.IdentityUtility /// A readonly Json Web Key (JWK) data structure that may be used for signing /// or verifying messages. /// </summary> - public sealed class ReadOnlyJsonWebKey : VnDisposeable, IJsonWebKey + public sealed class ReadOnlyJsonWebKey : IJsonWebKey { - private readonly JsonElement _jwk; - private readonly JsonDocument? _doc; + private readonly FrozenDictionary<string, string?> _properties; /// <summary> - /// Creates a new instance of <see cref="ReadOnlyJsonWebKey"/> from a <see cref="JsonElement"/>. - /// This will call <see cref="JsonElement.Clone"/> on the element and store an internal copy + /// Creates a new instance of <see cref="ReadOnlyJsonWebKey"/> from a dictionary of + /// JWK string properties /// </summary> - /// <param name="keyElement">The <see cref="JsonElement"/> to create the <see cref="ReadOnlyJsonWebKey"/> from</param> - public ReadOnlyJsonWebKey(ref readonly JsonElement keyElement) + /// <param name="properties">The frozen dictionary instance of parsed JWK properties</param> + public ReadOnlyJsonWebKey(FrozenDictionary<string, string?> properties) { - _jwk = keyElement.Clone(); - //Set initial values - KeyId = _jwk.GetPropString("kid"); - KeyType = _jwk.GetPropString("kty"); - Algorithm = _jwk.GetPropString("alg"); - Use = _jwk.GetPropString("use"); + ArgumentNullException.ThrowIfNull(properties); + _properties = properties; //Create a JWT header from the values JwtHeader = new Dictionary<string, string?>() @@ -71,58 +67,81 @@ namespace VNLib.Hashing.IdentityUtility } /// <summary> + /// Creates a new instance of <see cref="ReadOnlyJsonWebKey"/> from a <see cref="JsonElement"/>. + /// This will call <see cref="JsonElement.Clone"/> on the element and store an internal copy + /// </summary> + /// <param name="keyElement">The <see cref="JsonElement"/> to create the <see cref="ReadOnlyJsonWebKey"/> from</param> + public ReadOnlyJsonWebKey(ref readonly JsonElement keyElement) + :this( + //Get only top-level string properties and store them in a dictionary + keyElement.EnumerateObject() + .Where(static k => k.Value.ValueKind == JsonValueKind.String) + .ToDictionary(static k => k.Name, v => v.Value.GetString(), StringComparer.OrdinalIgnoreCase) + .ToFrozenDictionary() + ) + { } + + /// <summary> /// Creates a new instance of <see cref="ReadOnlyJsonWebKey"/> from a raw utf8 encoded json /// binary sequence /// </summary> /// <param name="rawValue">The utf8 encoded json binary sequence</param> /// <exception cref="ArgumentException"></exception> /// <exception cref="JsonException"></exception> - public ReadOnlyJsonWebKey(ReadOnlySpan<byte> rawValue) + public static ReadOnlyJsonWebKey FromUtf8Bytes(ReadOnlySpan<byte> rawValue) { - //Pare the raw value Utf8JsonReader reader = new (rawValue); - _doc = JsonDocument.ParseValue(ref reader); - //store element - _jwk = _doc.RootElement; - - //Set initial values - KeyId = _jwk.GetPropString("kid"); - KeyType = _jwk.GetPropString("kty"); - Algorithm = _jwk.GetPropString("alg"); - Use = _jwk.GetPropString("use"); + using JsonDocument doc = JsonDocument.ParseValue(ref reader); + JsonElement root = doc.RootElement; + return new ReadOnlyJsonWebKey(ref root); + } - //Create a JWT header from the values - JwtHeader = new Dictionary<string, string?>() - { - { "alg" , Algorithm }, - { "typ" , "JWT" }, - }; + /// <summary> + /// Creates a new instance of <see cref="ReadOnlyJsonWebKey"/> from a raw utf8 encoded json + /// memory segment + /// </summary> + /// <param name="rawValue">The utf8 encoded json binary sequence</param> + /// <returns>The readonly JWK object</returns> + /// <exception cref="ArgumentException"></exception> + /// <exception cref="JsonException"></exception> + public static ReadOnlyJsonWebKey FromUtf8Bytes(ReadOnlyMemory<byte> rawValue) + { + using JsonDocument doc = JsonDocument.Parse(rawValue); + JsonElement root = doc.RootElement; + return new ReadOnlyJsonWebKey(ref root); + } - //Configure key usage - KeyUse = (Use?.ToLower(null)) switch - { - "sig" => JwkKeyUsage.Signature, - "enc" => JwkKeyUsage.Encryption, - _ => JwkKeyUsage.None, - }; + /// <summary> + /// Creates a new instance of <see cref="ReadOnlyJsonWebKey"/> from a json string + /// </summary> + /// <param name="jsonString">The json encoded string to recover the JWK from</param> + /// <returns></returns> + public static ReadOnlyJsonWebKey FromJsonString(string jsonString) + { + using JsonDocument doc = JsonDocument.Parse(jsonString); + JsonElement root = doc.RootElement; + return new ReadOnlyJsonWebKey(ref root); } /// <summary> /// The key identifier /// </summary> - public string? KeyId { get; } + public string? KeyId => _properties.GetValueOrDefault("kid"); + /// <summary> /// The key type /// </summary> - public string? KeyType { get; } + public string? KeyType => _properties.GetValueOrDefault("kty"); + /// <summary> /// The key algorithm /// </summary> - public string? Algorithm { get; } + public string? Algorithm => _properties.GetValueOrDefault("alg"); + /// <summary> /// The key "use" value /// </summary> - public string? Use { get; } + public string? Use => _properties.GetValueOrDefault("use"); /// <summary> /// Returns the JWT header that matches this key @@ -133,12 +152,17 @@ namespace VNLib.Hashing.IdentityUtility public JwkKeyUsage KeyUse { get; } ///<inheritdoc/> - public string? GetKeyProperty(string propertyName) => _jwk.GetPropString(propertyName); + public string? GetKeyProperty(string propertyName) => _properties.GetValueOrDefault(propertyName); - ///<inheritdoc/> - protected override void Free() + /// <summary> + /// Attemts to erase all property values from memory by securely writing over them with zeros + /// </summary> + public void EraseValues() { - _doc?.Dispose(); + foreach(string? value in _properties.Values) + { + PrivateStringManager.EraseString(value); + } } } diff --git a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs index c373310..86535c3 100644 --- a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs +++ b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -176,7 +176,7 @@ namespace VNLib.Net.Http.Core /// <param name="lineBuf">The buffer read data from the transport with</param> /// <returns>0 if the request line was successfully parsed, a status code if the request could not be processed</returns> [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] - public static HttpStatusCode Http1ParseHeaders(this HttpRequest Request, ref Http1ParseState parseState, ref TransportReader reader, in HttpConfig Config, Span<char> lineBuf) + public static HttpStatusCode Http1ParseHeaders(this HttpRequest Request, ref Http1ParseState parseState, ref TransportReader reader, ref readonly HttpConfig Config, Span<char> lineBuf) { /* * Evil mutable struct, get a local mutable reference to the request's @@ -534,7 +534,7 @@ namespace VNLib.Net.Http.Core /// <param name="reader">The <see cref="VnStreamReader"/> to read lines from the transport</param> /// <returns>0 if the request line was successfully parsed, a status code if the request could not be processed</returns> [MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)] - public static HttpStatusCode Http1PrepareEntityBody(this HttpRequest Request, ref Http1ParseState parseState, ref TransportReader reader, in HttpConfig Config) + public static HttpStatusCode Http1PrepareEntityBody(this HttpRequest Request, ref Http1ParseState parseState, ref TransportReader reader, ref readonly HttpConfig Config) { /* * Evil mutable struct, get a local mutable reference to the request's diff --git a/lib/Net.Http/src/FileUpload.cs b/lib/Net.Http/src/FileUpload.cs index 794c623..d3104fd 100644 --- a/lib/Net.Http/src/FileUpload.cs +++ b/lib/Net.Http/src/FileUpload.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2023 Vaughn Nugent +* Copyright (c) 2024 Vaughn Nugent * * Library: VNLib * Package: VNLib.Net.Http @@ -45,6 +45,11 @@ namespace VNLib.Net.Http public readonly record struct FileUpload(Stream FileData, bool DisposeStream, ContentType ContentType, string? FileName) { /// <summary> + /// Gets the length of the <see cref="FileData"/> stream + /// </summary> + public long Length => FileData.Length; + + /// <summary> /// Disposes the stream if the handle is owned /// </summary> public readonly void Free() diff --git a/lib/Utils/src/ArgumentList.cs b/lib/Utils/src/ArgumentList.cs index 235e62c..c02ebee 100644 --- a/lib/Utils/src/ArgumentList.cs +++ b/lib/Utils/src/ArgumentList.cs @@ -24,6 +24,7 @@ using System; using System.Linq; +using System.Collections; using System.Collections.Generic; namespace VNLib.Utils @@ -31,7 +32,7 @@ namespace VNLib.Utils /// <summary> /// Provides methods for parsing an argument list /// </summary> - public class ArgumentList : IIndexable<int, string> + public class ArgumentList : IIndexable<int, string>, IEnumerable<string> { private readonly List<string> _args; @@ -96,6 +97,10 @@ namespace VNLib.Utils return index == -1 || index + 1 >= _args.Count ? null : this[index + 1]; } - + ///<inheritdoc/> + public IEnumerator<string> GetEnumerator() => _args.GetEnumerator(); + + ///<inheritdoc/> + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } }
\ No newline at end of file |