aboutsummaryrefslogtreecommitdiff
path: root/lib/Hashing.Portable/src
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-03-24 21:01:06 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-03-24 21:01:06 -0400
commit55859158fbd0bf54473a0baeb486045a025c7c5d (patch)
tree380fe100af2b29246398237bfe95f392dc513ffe /lib/Hashing.Portable/src
parentdd0f384ec3b2fd86ec03aa0fb42387091b5430a7 (diff)
Squashed commit of the following:
commit 6c1667be23597513537f8190e2f55d65eb9b7c7a Author: vnugent <public@vaughnnugent.com> Date: Fri Mar 22 12:01:53 2024 -0400 refactor: Overhauled native library loading and lazy init commit ebf688f2f974295beabf7b5def7e6f6f150551d0 Author: vnugent <public@vaughnnugent.com> Date: Wed Mar 20 22:16:17 2024 -0400 refactor: Update compression header files and macros + Ci build commit 9c7b564911080ccd5cbbb9851a0757b05e1e9047 Author: vnugent <public@vaughnnugent.com> Date: Tue Mar 19 21:54:49 2024 -0400 refactor: JWK overhaul & add length getter to FileUpload commit 6d8c3444e09561e5957491b3cc1ae858e0abdd14 Author: vnugent <public@vaughnnugent.com> Date: Mon Mar 18 16:13:20 2024 -0400 feat: Add FNV1a software checksum and basic correction tests commit 00d182088cecefc08ca80b1faee9bed3f215f40b Author: vnugent <public@vaughnnugent.com> Date: Fri Mar 15 01:05:27 2024 -0400 chore: #6 Use utils filewatcher instead of built-in commit d513c10d9895c6693519ef1d459c6a5a76929436 Author: vnugent <public@vaughnnugent.com> Date: Sun Mar 10 21:58:14 2024 -0400 source tree project location updated
Diffstat (limited to 'lib/Hashing.Portable/src')
-rw-r--r--lib/Hashing.Portable/src/Argon2/VnArgon2.cs14
-rw-r--r--lib/Hashing.Portable/src/Checksums/FNV1a.cs113
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/JsonWebKey.cs38
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/ReadOnlyJsonWebKey.cs120
-rw-r--r--lib/Hashing.Portable/src/Native/MonoCypher/MonoCypherLibrary.cs8
5 files changed, 230 insertions, 63 deletions
diff --git a/lib/Hashing.Portable/src/Argon2/VnArgon2.cs b/lib/Hashing.Portable/src/Argon2/VnArgon2.cs
index 9d98050..b88c232 100644
--- a/lib/Hashing.Portable/src/Argon2/VnArgon2.cs
+++ b/lib/Hashing.Portable/src/Argon2/VnArgon2.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Hashing.Portable
@@ -24,7 +24,6 @@
using System;
using System.Text;
-using System.Threading;
using System.Diagnostics;
using System.Buffers.Text;
using System.Security.Cryptography;
@@ -33,6 +32,7 @@ using System.Runtime.InteropServices;
using VNLib.Utils.Memory;
using VNLib.Utils.Native;
using VNLib.Utils.Extensions;
+using VNLib.Utils.Resources;
using VNLib.Hashing.Native.MonoCypher;
namespace VNLib.Hashing
@@ -52,12 +52,12 @@ namespace VNLib.Hashing
public const string ARGON2_LIB_ENVIRONMENT_VAR_NAME = "VNLIB_ARGON2_DLL_PATH";
private static readonly Encoding LocEncoding = Encoding.Unicode;
- private static readonly Lazy<IUnmangedHeap> _heap = new (static () => MemoryUtil.InitializeNewHeapForProcess(true), LazyThreadSafetyMode.PublicationOnly);
- private static readonly Lazy<IArgon2Library> _nativeLibrary = new(LoadSharedLibInternal, LazyThreadSafetyMode.PublicationOnly);
+ private static readonly LazyInitializer<IUnmangedHeap> _heap = new (static () => MemoryUtil.InitializeNewHeapForProcess(true));
+ private static readonly LazyInitializer<IArgon2Library> _nativeLibrary = new(LoadSharedLibInternal);
//Private heap initialized to 10k size, and all allocated buffers will be zeroed when allocated
- private static IUnmangedHeap PwHeap => _heap.Value;
+ private static IUnmangedHeap PwHeap => _heap.Instance;
private static IArgon2Library LoadSharedLibInternal()
{
@@ -70,7 +70,7 @@ namespace VNLib.Hashing
Trace.WriteLine("Using the native MonoCypher library for Argon2 password hashing", "VnArgon2");
//Load shared monocyphter argon2 library
- return MonoCypherLibrary.Shared.Argon2CreateLibrary(_heap.Value);
+ return MonoCypherLibrary.Shared.Argon2CreateLibrary(_heap.Instance);
}
else
{
@@ -90,7 +90,7 @@ namespace VNLib.Hashing
/// </summary>
/// <returns>The shared library instance</returns>
/// <exception cref="DllNotFoundException"></exception>
- public static IArgon2Library GetOrLoadSharedLib() => _nativeLibrary.Value;
+ public static IArgon2Library GetOrLoadSharedLib() => _nativeLibrary.Instance;
/// <summary>
/// Loads a native Argon2 shared library from the specified path
diff --git a/lib/Hashing.Portable/src/Checksums/FNV1a.cs b/lib/Hashing.Portable/src/Checksums/FNV1a.cs
new file mode 100644
index 0000000..f150638
--- /dev/null
+++ b/lib/Hashing.Portable/src/Checksums/FNV1a.cs
@@ -0,0 +1,113 @@
+/*
+* Copyright (c) 2024 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Hashing.Portable
+* File: FNV1a.cs
+*
+* FNV1a.cs is part of VNLib.Hashing.Portable which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Hashing.Portable is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Hashing.Portable is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Hashing.Portable. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace VNLib.Hashing.Checksums
+{
+ /// <summary>
+ /// A managed software implementation of the FNV-1a 64-bit non cryptographic hash algorithm
+ /// </summary>
+ public static class FNV1a
+ {
+ /*
+ * Constants taken from the spec
+ * https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
+ */
+ const ulong FNV_PRIME = 0x100000001b3UL;
+ const ulong FNV_OFFSET_BASIS = 0xcbf29ce484222325UL;
+
+ /// <summary>
+ /// Computes the next 64-bit FNV-1a hash value using the current hash
+ /// value and the next byte of data.
+ /// </summary>
+ /// <param name="initalizer">The inital hash to begin the computation with</param>
+ /// <param name="data"></param>
+ /// <param name="length"></param>
+ /// <returns>The next value of the checksum representing current and previously computed segments</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static ulong Update64(ulong initalizer, ref byte data, nuint length)
+ {
+ if (Unsafe.IsNullRef(ref data))
+ {
+ throw new ArgumentNullException(nameof(data));
+ }
+
+ ulong digest = initalizer;
+
+ for (nuint i = 0; i < length; i++)
+ {
+ digest ^= Unsafe.AddByteOffset(ref data, i);
+ digest *= FNV_PRIME;
+ }
+
+ return digest;
+ }
+
+ /// <summary>
+ /// Computes the next 64-bit FNV-1a hash value using the current hash
+ /// value and the next byte of data.
+ /// </summary>
+ /// <param name="initalizer">The initial hash to begin the computation with</param>
+ /// <param name="data">A span structure pointing to the memory block to compute the digest of</param>
+ /// <returns>The next value of the checksum representing current and previously computed segments</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static ulong Update64(ulong initalizer, ReadOnlySpan<byte> data)
+ {
+ ref byte r0 = ref MemoryMarshal.GetReference(data);
+ return Update64(initalizer, ref r0, (nuint)data.Length);
+ }
+
+ /// <summary>
+ /// Begins computing the FNV-1a 64-bit hash of the input data and returns the
+ /// initial hash value which may be updated if more data is available
+ /// </summary>
+ /// <param name="data">A managed pointer to the first byte of the sequence to compute</param>
+ /// <param name="length">A platform specific integer representing the length of the input data</param>
+ /// <returns>The 64bit unsigned integer representing the message sum or digest</returns>
+ /// <remarks>
+ /// WARNING: This function produces a non-cryptographic hash and should not be used for
+ /// security or cryptographic purposes. It is intended for fast data integrity checks
+ /// </remarks>
+ public static ulong Compute64(ref byte data, nuint length) => Update64(FNV_OFFSET_BASIS, ref data, length);
+
+ /// <summary>
+ /// Computes the next 64-bit FNV-1a hash value using the current hash
+ /// value and the next byte of data.
+ /// </summary>
+ /// <param name="data">A span structure pointing to the memory block to compute the digest of</param>
+ /// <returns>The 64bit unsigned integer representng the message sum or digest</returns>
+ /// <remarks>
+ /// WARNING: This function produces a non-cryptographic hash and should not be used for
+ /// security or cryptographic purposes. It is intended for fast data integrity checks
+ /// </remarks>
+ public static ulong Compute64(ReadOnlySpan<byte> data)
+ {
+ ref byte r0 = ref MemoryMarshal.GetReference(data);
+ return Compute64(ref r0, (nuint)data.Length);
+ }
+ }
+}
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/Hashing.Portable/src/Native/MonoCypher/MonoCypherLibrary.cs b/lib/Hashing.Portable/src/Native/MonoCypher/MonoCypherLibrary.cs
index 11e524b..4c6f405 100644
--- a/lib/Hashing.Portable/src/Native/MonoCypher/MonoCypherLibrary.cs
+++ b/lib/Hashing.Portable/src/Native/MonoCypher/MonoCypherLibrary.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Hashing.Portable
@@ -25,10 +25,10 @@
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
-using System.Threading;
using VNLib.Utils;
using VNLib.Utils.Native;
+using VNLib.Utils.Resources;
using VNLib.Utils.Extensions;
namespace VNLib.Hashing.Native.MonoCypher
@@ -50,7 +50,7 @@ namespace VNLib.Hashing.Native.MonoCypher
/// <returns>true if the user enabled the default library, false otherwise</returns>
public static bool CanLoadDefaultLibrary() => string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(MONOCYPHER_LIB_ENVIRONMENT_VAR_NAME)) == false;
- private static readonly Lazy<MonoCypherLibrary> _defaultLib = new (LoadDefaultLibraryInternal, LazyThreadSafetyMode.PublicationOnly);
+ private static readonly LazyInitializer<MonoCypherLibrary> _defaultLib = new (LoadDefaultLibraryInternal);
/// <summary>
/// Gets the default MonoCypher library for the current process
@@ -59,7 +59,7 @@ namespace VNLib.Hashing.Native.MonoCypher
/// this property to ensure that the default library can be loaded
/// </para>
/// </summary>
- public static MonoCypherLibrary Shared => _defaultLib.Value;
+ public static MonoCypherLibrary Shared => _defaultLib.Instance;
/// <summary>
/// Loads a new instance of the MonoCypher library with environment defaults