diff options
Diffstat (limited to 'wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src')
12 files changed, 524 insertions, 475 deletions
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCUtilCipher.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCCipherUtil.cs index 72bb75a..5dc2a94 100644 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCUtilCipher.cs +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCCipherUtil.cs @@ -15,20 +15,24 @@ using System; using System.Diagnostics; +using System.Runtime.InteropServices; + using VNLib.Utils.Cryptography.Noscrypt.@internal; +using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; + using NCResult = System.Int64; namespace VNLib.Utils.Cryptography.Noscrypt.Encryption { - internal static class NCUtilCipher + public unsafe static class NCCipherUtil { /* * This class wraps the low-level cipher functions provided by * the Noscrypt utility side-car library. */ - public static nint Alloc(NCContext ctx, uint version, uint flags) + internal static nint Alloc(NCContext ctx, uint version, uint flags) { nint cipher = GetTable(ctx).NCUtilCipherAlloc(version, flags); @@ -43,7 +47,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption return cipher; } - public static uint GetFlags(NCContext ctx, nint cipher) + internal static uint GetFlags(NCContext ctx, nint cipher) { NCResult result = GetTable(ctx).NCUtilCipherGetFlags(cipher); @@ -52,9 +56,9 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption return (uint)result; } - public static void Free(NCContext ctx, nint cipher) => GetTable(ctx).NCUtilCipherFree(cipher); + internal static void Free(NCContext ctx, nint cipher) => GetTable(ctx).NCUtilCipherFree(cipher); - public static int GetIvSize(NCContext ctx, nint cipher) + internal static int GetIvSize(NCContext ctx, nint cipher) { NCResult result = GetTable(ctx).NCUtilCipherGetIvSize(cipher); @@ -63,7 +67,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption return checked((int)result); } - public static unsafe void SetProperty(NCContext ctx, nint cipher, uint property, ref readonly byte value, uint valueLen) + internal static void SetProperty(NCContext ctx, nint cipher, uint property, ref readonly byte value, uint valueLen) { fixed (byte* valPtr = &value) { @@ -73,7 +77,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption } } - public static uint GetOutputSize(NCContext ctx, nint cipher) + internal static uint GetOutputSize(NCContext ctx, nint cipher) { NCResult result = GetTable(ctx).NCUtilCipherGetOutputSize(cipher); @@ -82,7 +86,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption return (uint)result; } - public static unsafe uint ReadOutput(NCContext ctx, nint cipher, ref byte outputData, uint outLen) + internal static uint ReadOutput(NCContext ctx, nint cipher, ref byte outputData, uint outLen) { fixed (byte* outPtr = &outputData) { @@ -94,14 +98,14 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption } } - public static unsafe void InitCipher(NCContext ctx, nint cipher, byte* inputPtr, uint inputSize) + internal static void InitCipher(NCContext ctx, nint cipher, byte* inputPtr, uint inputSize) { NCResult result = GetTable(ctx).NCUtilCipherInit(cipher, inputPtr, inputSize); NCUtil.CheckResult<FunctionTable.NCUtilCipherInitDelegate>(result, raiseOnFailure: true); } - public static unsafe void Update( + internal static void Update( NCContext ctx, nint cipher, ref readonly NCSecretKey localKey, @@ -122,7 +126,46 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption } } - private static ref readonly FunctionTable GetTable(NCContext ctx) => ref ctx.Library.Functions; + +#if DEBUG + /* + * Conversation key is not meant to be a public api. Callers + * should use Encrypt/Decrypt methods to handle encryption. + * + * This method exists for vector testing purposes only. + */ + public static void GetConverstationKey( + NCContext ctx, + ref readonly NCSecretKey localKey, + ref readonly NCPublicKey remoteKey, + Span<byte> conversationKeyOut32 + ) + { + ArgumentNullException.ThrowIfNull(ctx); + ArgumentOutOfRangeException.ThrowIfNotEqual( + conversationKeyOut32.Length, + NC_CONVERSATION_KEY_SIZE, + nameof(conversationKeyOut32) + ); + + fixed (NCSecretKey* sk = &localKey) + fixed (NCPublicKey* pk = &remoteKey) + fixed(byte* convKey32Ptr = &MemoryMarshal.GetReference(conversationKeyOut32)) + { + NCResult result = GetTable(ctx).NCGetConversationKey( + ctx: ctx.DangerousGetHandle(), + sk, + pk, + convKey32Ptr + ); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherInitDelegate>(result, raiseOnFailure: true); + } + } +#endif + + private static ref readonly FunctionTable GetTable(NCContext ctx) + => ref ctx.Library.Functions; } } diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipher.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptMessageCipher.cs index b30ea44..e44addf 100644 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipher.cs +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptMessageCipher.cs @@ -18,6 +18,8 @@ using System.Threading; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + using VNLib.Utils.Memory; using VNLib.Utils.Cryptography.Noscrypt.Random; using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; @@ -27,29 +29,54 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption /// <summary> /// The noscrypt util cipher wapper /// </summary> - /// <param name="ctx"></param> - /// <param name="flags">Cipher creation flags</param> + /// <param name="ctx">A reference to the library context object</param> + /// <param name="cipher">A pointer to an existing cipher context that this instance owns</param> /// <param name="version">The cipher specification version</param> - public sealed class NoscryptCipher(NCContext ctx, NoscryptCipherVersion version, NoscryptCipherFlags flags) : VnDisposeable + public class NoscryptMessageCipher : SafeHandleMinusOneIsInvalid { + private readonly NCContext _context; + private readonly NoscryptCipherVersion _version; private IMemoryHandle<byte>? _ivBuffer; - private readonly nint _cipher = NCUtilCipher.Alloc(ctx, (uint)version, (uint)flags); + + private NoscryptMessageCipher(NCContext ctx, nint cipher, NoscryptCipherVersion version) + : base(ownsHandle: true) + { + SetHandle(cipher); + _context = ctx; + _version = version; + } + + /// <summary> + /// Allocates and initializes a new cipher instance using the specified + /// </summary> + /// <param name="context">A reference to the noscrypt library context</param> + /// <param name="version">The encryption standard to use</param> + /// <param name="flags">The raw cipher flags to the pass to the cipher initialization function</param> + /// <returns>A new <see cref="NoscryptMessageCipher"/> instance</returns> + public static NoscryptMessageCipher Create(NCContext context, NoscryptCipherVersion version, NoscryptCipherFlags flags) + { + return new( + context, + NCCipherUtil.Alloc(context, (uint)version, (uint)flags), + version + ); + } /// <summary> /// The cipher standard version used by this instance /// </summary> - public NoscryptCipherVersion Version => version; + public NoscryptCipherVersion Version => _version; /// <summary> /// Gets the flags set for the cipher instance /// </summary> - public uint GetFlags() => NCUtilCipher.GetFlags(ctx, _cipher); + public uint GetFlags() => NCCipherUtil.GetFlags(_context, handle); /// <summary> /// Gets the cipher's initilaization vector size (or nonce) /// </summary> /// <returns>The size of the IV in bytes</returns> - public int GetIvSize() => NCUtilCipher.GetIvSize(ctx, _cipher); + public int GetIvSize() => NCCipherUtil.GetIvSize(_context, handle); /// <summary> /// Gets the internal heap buffer that holds the cipher's initalization @@ -121,9 +148,9 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption fixed (byte* inputPtr = &inputData) { - NCUtilCipher.InitCipher(ctx, _cipher, inputPtr, inputSize); + NCCipherUtil.InitCipher(_context, handle, inputPtr, inputSize); - NCUtilCipher.Update(ctx, _cipher, in localKey, in remoteKey); + NCCipherUtil.Update(_context, handle, in localKey, in remoteKey); } } @@ -153,7 +180,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption /// Gets the size of the output buffer required to read the cipher output /// </summary> /// <returns>The size of the output in bytes</returns> - public int GetOutputSize() => checked((int)NCUtilCipher.GetOutputSize(ctx, _cipher)); + public int GetOutputSize() => checked((int)NCCipherUtil.GetOutputSize(_context, handle)); /// <summary> /// Reads the output data from the cipher into the specified buffer @@ -166,7 +193,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption { ArgumentOutOfRangeException.ThrowIfLessThan(size, GetOutputSize()); - return checked((int)NCUtilCipher.ReadOutput(ctx, _cipher, ref outputData, (uint)size)); + return checked((int)NCCipherUtil.ReadOutput(_context, handle, ref outputData, (uint)size)); } /// <summary> @@ -186,7 +213,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption private IMemoryHandle<byte> AllocIvBuffer() { //Use the context heap to allocate the internal iv buffer - MemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(ctx.Heap, GetIvSize(), zero: true); + MemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(_context.Heap, GetIvSize(), zero: true); try { @@ -199,9 +226,9 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption * buffer is required so we don't need to pin managed memory * nor worry about the GC moving the buffer. */ - NCUtilCipher.SetProperty( - ctx, - _cipher, + NCCipherUtil.SetProperty( + _context, + DangerousGetHandle(), NC_ENC_SET_IV, ref buffer.GetReference(), (uint)buffer.Length @@ -217,10 +244,12 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Encryption } ///<inheritdoc/> - protected override void Free() + protected override bool ReleaseHandle() { - NCUtilCipher.Free(ctx, _cipher); + NCCipherUtil.Free(_context, handle); _ivBuffer?.Dispose(); + + return true; } } diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrCrypto.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrCrypto.cs deleted file mode 100644 index 9b4d36c..0000000 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrCrypto.cs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 2024 Vaughn Nugent -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -using System; - -using VNLib.Utils.Cryptography.Noscrypt.Encryption; - -namespace VNLib.Utils.Cryptography.Noscrypt -{ - public interface INostrCrypto - { - - /// <summary> - /// Gets a nostr public key from a secret key. - /// </summary> - /// <param name="secretKey">A reference to the secret key to get the public key from</param> - /// <param name="publicKey">A reference to the public key structure to write the recovered key to</param> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="ArgumentNullException"></exception> - void GetPublicKey(ref readonly NCSecretKey secretKey, ref NCPublicKey publicKey); - - /// <summary> - /// Validates a secret key is in a valid format. - /// </summary> - /// <param name="secretKey">A readonly reference to key structure to validate</param> - /// <returns>True if the key is consiered valid against the secp256k1 curve</returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="ArgumentNullException"></exception> - bool ValidateSecretKey(ref readonly NCSecretKey secretKey); - - /// <summary> - /// Allocates a new cipher instance with the supplied options. - /// </summary> - /// <param name="version">The cipher specification version</param> - /// <param name="flags">The cipher initialziation flags</param> - /// <returns>The cipher instance</returns> - NoscryptCipher AllocCipher(NoscryptCipherVersion version, NoscryptCipherFlags flags); - - /// <summary> - /// Signs the supplied data with the secret key and random32 nonce, then writes - /// the message signature to the supplied sig64 buffer. - /// </summary> - /// <param name="secretKey">The secret key used to sign the message</param> - /// <param name="random32">A highly secure random nonce used to seed the signature</param> - /// <param name="data">A pointer to the first byte in the message to sign</param> - /// <param name="dataSize">The size of the message in bytes</param> - /// <param name="sig64">A pointer to the first byte of a 64 byte buffer used to write the message signature to</param> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="ArgumentNullException"></exception> - void SignData( - ref readonly NCSecretKey secretKey, - ref readonly byte random32, - ref readonly byte data, - uint dataSize, - ref byte sig64 - ); - - /// <summary> - /// Performs cryptographic verification of the supplied data - /// against the supplied public key. - /// </summary> - /// <param name="pubKey">The signer's public key</param> - /// <param name="data">A pointer to the first byte in the message to sign</param> - /// <param name="dataSize">The number of bytes in the message</param> - /// <param name="sig64">A pointer to the signature buffer</param> - /// <returns>True if the signature could be verified against the public key. False otherwise</returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="ArgumentNullException"></exception> - bool VerifyData( - ref readonly NCPublicKey pubKey, - ref readonly byte data, - uint dataSize, - ref readonly byte sig64 - ); - } -} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCKeyUtil.cs index 307bbc1..50e1c9a 100644 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCKeyUtil.cs @@ -14,18 +14,21 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. using System; -using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using VNLib.Utils.Extensions; +using VNLib.Utils.Cryptography.Noscrypt.@internal; using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; using NCResult = System.Int64; namespace VNLib.Utils.Cryptography.Noscrypt { - - public static class NCUtil + /// <summary> + /// Contains utility methods for working with nostr keys + /// </summary> + public static unsafe class NCKeyUtil { /// <summary> /// Gets a span of bytes from the current secret key @@ -115,79 +118,68 @@ namespace VNLib.Utils.Cryptography.Noscrypt return ref Unsafe.As<byte, NCPublicKey>(ref asBytes); } - internal static void CheckResult<T>(NCResult result, bool raiseOnFailure) where T : Delegate + /// <summary> + /// Gets a nostr public key from a secret key. + /// </summary> + /// <param name="secretKey">A reference to the secret key to get the public key from</param> + /// <param name="publicKey">A reference to the public key structure to write the recovered key to</param> + /// <exception cref="ArgumentException"></exception> + /// <exception cref="ArgumentNullException"></exception> + public static void GetPublicKey( + NCContext context, + ref readonly NCSecretKey secretKey, + ref NCPublicKey publicKey + ) { - //Only negative values are errors - if (result >= NC_SUCCESS) - { - return; - } - - NCResult asPositive = -result; + Check(context); - // Error code are only 8 bits, if an argument error occured, the - // argument number will be in the next upper 8 bits - NCErrorCodes errorCode = (NCErrorCodes)(asPositive & 0xFF); - byte argNumber = (byte)((asPositive >> 8) & 0xFF); - - switch (errorCode) + fixed (NCSecretKey* pSecKey = &secretKey) + fixed (NCPublicKey* pPubKey = &publicKey) { - case NCErrorCodes.E_NULL_PTR: - RaiseNullArgExceptionForArgumentNumber<T>(argNumber); - break; - case NCErrorCodes.E_INVALID_ARG: - RaiseArgExceptionForArgumentNumber<T>(argNumber); - break; - case NCErrorCodes.E_ARGUMENT_OUT_OF_RANGE: - RaiseOORExceptionForArgumentNumber<T>(argNumber); - break; - case NCErrorCodes.E_INVALID_CTX: - throw new InvalidOperationException("The library context object is null or invalid"); - case NCErrorCodes.E_OPERATION_FAILED: - RaiseOperationFailedException(raiseOnFailure); - break; - case NCErrorCodes.E_VERSION_NOT_SUPPORTED: - throw new NotSupportedException("The requested version is not supported"); - - default: - if(raiseOnFailure) - { - throw new InvalidOperationException($"The operation failed with error, code: {errorCode} for arugment {argNumber:x}"); - } - break; + NCResult result = GetTable(context).NCGetPublicKey( + context.DangerousGetHandle(), + pSecKey, + pPubKey + ); + + NCUtil.CheckResult<FunctionTable.NCGetPublicKeyDelegate>(result, raiseOnFailure: true); } } - private static void RaiseOperationFailedException(bool raise) + /// <summary> + /// Validates a secret key is in a valid format. + /// </summary> + /// <param name="secretKey">A readonly reference to key structure to validate</param> + /// <returns>True if the key is consiered valid against the secp256k1 curve</returns> + /// <exception cref="ArgumentException"></exception> + /// <exception cref="ArgumentNullException"></exception> + public static bool ValidateSecretKey( + NCContext context, + ref readonly NCSecretKey secretKey + ) { - if (raise) + Check(context); + + fixed (NCSecretKey* pSecKey = &secretKey) { - throw new InvalidOperationException("The operation failed for an unknown reason"); - } - } + NCResult result = GetTable(context).NCValidateSecretKey( + context.DangerousGetHandle(), + pSecKey + ); - private static void RaiseNullArgExceptionForArgumentNumber<T>(int argNumber) where T : Delegate - { - //Get delegate parameters - Type type = typeof(T); - ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; - throw new ArgumentNullException(arg.Name, "Argument is null or invalid cannot continue"); - } + NCUtil.CheckResult<FunctionTable.NCValidateSecretKeyDelegate>(result, raiseOnFailure: false); - private static void RaiseArgExceptionForArgumentNumber<T>(int argNumber) where T : Delegate - { - //Get delegate parameters - Type type = typeof(T); - ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; - throw new ArgumentException("Argument is null or invalid cannot continue", arg.Name); + return result == NC_SUCCESS; + } } - private static void RaiseOORExceptionForArgumentNumber<T>(int argNumber) where T : Delegate + private static void Check(NCContext? context) { - //Get delegate parameters - Type type = typeof(T); - ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; - throw new ArgumentOutOfRangeException(arg.Name, "Argument is out of range of acceptable values"); + ArgumentNullException.ThrowIfNull(context); + context.ThrowIfClosed(); } + + private static ref readonly FunctionTable GetTable(NCContext ctx) + => ref ctx.Library.Functions; } } diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs deleted file mode 100644 index e96ff96..0000000 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (C) 2024 Vaughn Nugent -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; - -namespace VNLib.Utils.Cryptography.Noscrypt -{ - - public static class NoscryptExtensions - { - public static void SignData( - this INostrCrypto lib, - ref readonly NCSecretKey secKey, - ReadOnlySpan<byte> random32, - ReadOnlySpan<byte> data, - Span<byte> signatureBuffer - ) - { - ArgumentOutOfRangeException.ThrowIfLessThan(signatureBuffer.Length, NC_SIGNATURE_SIZE, nameof(signatureBuffer)); - ArgumentOutOfRangeException.ThrowIfLessThan(random32.Length, 32, nameof(random32)); - ArgumentOutOfRangeException.ThrowIfZero(data.Length, nameof(data)); - - lib.SignData( - secretKey: in secKey, - random32: in MemoryMarshal.GetReference(random32), - data: in MemoryMarshal.GetReference(data), - dataSize: (uint)data.Length, - sig64: ref MemoryMarshal.GetReference(signatureBuffer) - ); - } - -#if DEBUG - /* - * Conversation key is not meant to be a public api. Callers - * should use Encrypt/Decrypt methods to handle encryption. - * - * This method exists for vector testing purposes only. - */ - public static void GetConverstationKey( - this NostrCrypto lib, - ref readonly NCSecretKey secretKey, - ref readonly NCPublicKey publicKey, - Span<byte> conversationKeyOut32 - ) - { - ArgumentNullException.ThrowIfNull(lib); - ArgumentOutOfRangeException.ThrowIfNotEqual(conversationKeyOut32.Length, NC_CONVERSATION_KEY_SIZE, nameof(conversationKeyOut32)); - - //Get the conversation key - lib.GetConverstationKey( - secretKey: in secretKey, - publicKey: in publicKey, - key32: ref MemoryMarshal.GetReference(conversationKeyOut32) - ); - - } -#endif - } -} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs index 35c6a49..2df63eb 100644 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs @@ -37,6 +37,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt public unsafe sealed class NoscryptLibrary(SafeLibraryHandle Library, bool OwnsHandle) : VnDisposeable { public const string NoscryptDefaultLibraryName = "noscrypt"; + public const string NoscryptDllPathEnvName = "NOSCRYPT_DLL_PATH"; //Constant values match the noscrypt.h header public const int NC_SEC_KEY_SIZE = 0x20; @@ -172,33 +173,16 @@ namespace VNLib.Utils.Cryptography.Noscrypt } /// <summary> - /// Initializes a new NostrCrypto context wraper directly that owns the internal context. - /// This may be done once at app startup and is thread-safe for the rest of the - /// application lifetime. - /// </summary> - /// <param name="heap">The heap to allocate the context from</param> - /// <param name="entropy32">The random entropy data to initialize the context with</param> - /// <returns>The library wrapper handle</returns> - public NostrCrypto InitializeCrypto(IUnmangedHeap heap, ReadOnlySpan<byte> entropy32) - { - ArgumentNullException.ThrowIfNull(heap); - - //Create the crypto interface from the new context object - return new NostrCrypto( - context: Initialize(heap, entropy32), - ownsContext: true - ); - } - - /// <summary> - /// Initializes a new NostrCrypto context wraper directly that owns the internal context. - /// This may be done once at app startup and is thread-safe for the rest of the - /// application lifetime. + /// Initialize a new NCContext for use. This may be done once at app startup + /// and is thread-safe for the rest of the application lifetime. /// </summary> - /// <param name="heap">The heap to allocate the context from</param> - /// <param name="random">Random source used to generate context entropy</param> - /// <returns>The library wrapper handle</returns> - public NostrCrypto InitializeCrypto(IUnmangedHeap heap, IRandomSource random) + /// <param name="heap"></param> + /// <param name="enropy32">The 32byte random seed/nonce for the noscrypt context</param> + /// <returns>The inialized context</returns> + /// <exception cref="OutOfMemoryException"></exception> + /// <exception cref="ArgumentNullException"></exception> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public NCContext Initialize(IUnmangedHeap heap, IRandomSource random) { ArgumentNullException.ThrowIfNull(random); @@ -206,13 +190,9 @@ namespace VNLib.Utils.Cryptography.Noscrypt Span<byte> entropy = stackalloc byte[NC_CTX_ENTROPY_SIZE]; random.GetRandomBytes(entropy); - NostrCrypto nc = InitializeCrypto(heap, entropy); - - MemoryUtil.InitializeBlock(entropy); - - return nc; + return Initialize(heap, entropy); } - + ///<inheritdoc/> protected override void Free() { @@ -256,6 +236,16 @@ namespace VNLib.Utils.Cryptography.Noscrypt /// </summary> /// <returns>The loaded library instance</returns> /// <exception cref="DllNotFoundException"></exception> - public static NoscryptLibrary LoadDefault() => Load(NoscryptDefaultLibraryName, DllImportSearchPath.SafeDirectories); + public static NoscryptLibrary LoadDefault() + { + string? libPath = Environment.GetEnvironmentVariable(NoscryptDllPathEnvName); + libPath ??= NoscryptDefaultLibraryName; + + Console.WriteLine("Loading library {0}", libPath); + + libPath = libPath.Replace("\"", ""); + + return Load(libPath); + } } } diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs deleted file mode 100644 index 1e833d2..0000000 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2024 Vaughn Nugent -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -using System; -using System.Runtime.CompilerServices; -using System.Diagnostics.CodeAnalysis; - -using VNLib.Utils.Cryptography.Noscrypt.@internal; -using VNLib.Utils.Cryptography.Noscrypt.Encryption; -using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; - -using NCResult = System.Int64; - -namespace VNLib.Utils.Cryptography.Noscrypt -{ - /// <summary> - /// A default implementation of the <see cref="INostrCrypto"/> interface - /// </summary> - /// <param name="context">The initialized library context</param> - public unsafe class NostrCrypto(NCContext context, bool ownsContext) : VnDisposeable, INostrCrypto - { - /// <summary> - /// Gets the underlying library context. - /// </summary> - public NCContext Context => context; - - private ref readonly FunctionTable Functions => ref context.Library.Functions; - - ///<inheritdoc/> - public NoscryptCipher AllocCipher(NoscryptCipherVersion version, NoscryptCipherFlags flags) => new (context, version, flags); - - ///<inheritdoc/> - public void GetPublicKey(ref readonly NCSecretKey secretKey, ref NCPublicKey publicKey) - { - Check(); - - fixed (NCSecretKey* pSecKey = &secretKey) - fixed (NCPublicKey* pPubKey = &publicKey) - { - NCResult result = Functions.NCGetPublicKey.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey); - NCUtil.CheckResult<FunctionTable.NCGetPublicKeyDelegate>(result, true); - } - } - - ///<inheritdoc/> - public void SignData( - ref readonly NCSecretKey secretKey, - ref readonly byte random32, - ref readonly byte data, - uint dataSize, - ref byte sig64 - ) - { - Check(); - - fixed (NCSecretKey* pSecKey = &secretKey) - fixed (byte* pData = &data, pSig = &sig64, pRandom = &random32) - { - NCResult result = Functions.NCSignData.Invoke( - ctx: context.DangerousGetHandle(), - sk: pSecKey, - random32: pRandom, - data: pData, - dataSize, - sig64: pSig - ); - - NCUtil.CheckResult<FunctionTable.NCSignDataDelegate>(result, true); - } - } - - ///<inheritdoc/> - public bool ValidateSecretKey(ref readonly NCSecretKey secretKey) - { - Check(); - - IntPtr libCtx = context.DangerousGetHandle(); - - fixed (NCSecretKey* pSecKey = &secretKey) - { - /* - * Validate should return a result of 1 if the secret key is valid - * or a 0 if it is not. - */ - NCResult result = Functions.NCValidateSecretKey.Invoke(libCtx, pSecKey); - NCUtil.CheckResult<FunctionTable.NCValidateSecretKeyDelegate>(result, false); - - return result == NC_SUCCESS; - } - } - - ///<inheritdoc/> - public bool VerifyData( - ref readonly NCPublicKey pubKey, - ref readonly byte data, - uint dataSize, - ref readonly byte sig64 - ) - { - Check(); - - fixed(NCPublicKey* pPubKey = &pubKey) - fixed (byte* pData = &data, pSig = &sig64) - { - NCResult result = Functions.NCVerifyData.Invoke(context.DangerousGetHandle(), pPubKey, pData, dataSize, pSig); - NCUtil.CheckResult<FunctionTable.NCVerifyDataDelegate>(result, false); - - return result == NC_SUCCESS; - } - } - -#if DEBUG - - /// <summary> - /// DEBUG ONLY: Gets the conversation key for the supplied secret key and public key - /// </summary> - /// <param name="secretKey">The sender's private key</param> - /// <param name="publicKey">The receiver's public key</param> - /// <param name="key32">A pointer to the 32byte buffer to write the conversation key to</param> - public void GetConverstationKey( - ref readonly NCSecretKey secretKey, - ref readonly NCPublicKey publicKey, - ref byte key32 - ) - { - Check(); - - fixed (NCSecretKey* pSecKey = &secretKey) - fixed (NCPublicKey* pPubKey = &publicKey) - fixed (byte* pKey = &key32) - { - NCResult result = Functions.NCGetConversationKey.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, pKey); - NCUtil.CheckResult<FunctionTable.NCGetConversationKeyDelegate>(result, true); - } - } - -#endif - ///<inheritdoc/> - protected override void Free() - { - if(ownsContext) - { - context.Dispose(); - } - } - - private static void ThrowIfNullRef([DoesNotReturnIf(false)] ref readonly byte value, string name) - { - if(Unsafe.IsNullRef(in value)) - { - throw new ArgumentNullException(name); - } - } - } -} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NCSignatureUtil.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NCSignatureUtil.cs new file mode 100644 index 0000000..2755ceb --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NCSignatureUtil.cs @@ -0,0 +1,175 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +using System; +using System.Runtime.InteropServices; + +using VNLib.Utils.Extensions; +using VNLib.Utils.Cryptography.Noscrypt.@internal; +using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; + +using NCResult = System.Int64; + +namespace VNLib.Utils.Cryptography.Noscrypt.Singatures +{ + + /// <summary> + /// Contains utility methods for signing and verifying data using the noscrypt library + /// </summary> + public unsafe static class NCSignatureUtil + { + /// <summary> + /// Signs the data using the supplied secret key and + /// entropy pointer + /// </summary> + /// <param name="context">The initialized context memory to pass to the library</param> + /// <param name="secretKey">A reference to a structure containing the private key data</param> + /// <param name="random32">A pointer to a 32 byte buffer containing high entropy random data</param> + /// <param name="data">A pointer to a buffer containing the data to sign</param> + /// <param name="dataSize">The size of the data buffer in bytes</param> + /// <param name="sig64">A pointer to a 64 byte buffer to write signature data to</param> + /// <exception cref="InvalidOperationException"></exception> + public static void SignData( + NCContext context, + ref readonly NCSecretKey secretKey, + ref readonly byte random32, + ref readonly byte data, + uint dataSize, + ref byte sig64 + ) + { + Check(context); + + fixed (NCSecretKey* pSecKey = &secretKey) + fixed (byte* pData = &data, pSig = &sig64, pRandom = &random32) + { + NCResult result = GetTable(context).NCSignData( + ctx: context.DangerousGetHandle(), + sk: pSecKey, + random32: pRandom, + data: pData, + dataSize, + sig64: pSig + ); + + NCUtil.CheckResult<FunctionTable.NCSignDataDelegate>(result, raiseOnFailure: true); + } + } + + /// <summary> + /// Verifies signed data against the supplied public key + /// </summary> + /// <param name="context">The initialized context memory to pass to the library</param> + /// <param name="publicKey">A reference to a structure containing the public key data</param> + /// <param name="data">A pointer to a buffer containing the data to verify</param> + /// <param name="dataSize">The size of the data buffer in bytes</param> + /// <param name="sig64">A pointer to a 64 byte buffer to read signature data from</param> + /// <returns>True if the signature was signed by the supplied public key, false otherwise</returns> + public static bool VerifyData( + NCContext context, + ref readonly NCPublicKey publicKey, + ref readonly byte data, + uint dataSize, + ref readonly byte sig64 + ) + { + Check(context); + + fixed (NCPublicKey* pPubKey = &publicKey) + fixed (byte* pData = &data, pSig = &sig64) + { + NCResult result = GetTable(context).NCVerifyData( + context.DangerousGetHandle(), + pk: pPubKey, + data: pData, + dataSize, + sig64: pSig + ); + + NCUtil.CheckResult<FunctionTable.NCVerifyDataDelegate>(result, false); + + return result == NC_SUCCESS; + } + } + + /// <summary> + /// Signs the data using the supplied secret key and + /// entropy pointer + /// </summary> + /// <param name="context">The initialized context memory to pass to the library</param> + /// <param name="secretKey">A reference to a structure containing the private key data</param> + /// <param name="random32">A pointer to a 32 byte buffer containing high entropy random data</param> + /// <param name="data">A pointer to a buffer containing the data to sign</param> + /// <param name="signatureBuffer">A pointer to a 64 byte buffer to write signature data to</param> + /// <exception cref="InvalidOperationException"></exception> + public static void SignData( + NCContext context, + ref readonly NCSecretKey secretKey, + ReadOnlySpan<byte> random32, + ReadOnlySpan<byte> data, + Span<byte> signatureBuffer + ) + { + ArgumentOutOfRangeException.ThrowIfLessThan(signatureBuffer.Length, NC_SIGNATURE_SIZE, nameof(signatureBuffer)); + ArgumentOutOfRangeException.ThrowIfLessThan(random32.Length, 32, nameof(random32)); + ArgumentOutOfRangeException.ThrowIfZero(data.Length, nameof(data)); + + SignData( + context, + secretKey: in secretKey, + random32: in MemoryMarshal.GetReference(random32), + data: in MemoryMarshal.GetReference(data), + dataSize: (uint)data.Length, + sig64: ref MemoryMarshal.GetReference(signatureBuffer) + ); + } + + /// <summary> + /// Verifies signed data against the supplied public key + /// </summary> + /// <param name="context">The initialized context memory to pass to the library</param> + /// <param name="publicKey">A reference to a structure containing the public key data</param> + /// <param name="data">A pointer to a buffer containing the data to verify</param> + /// <param name="signatureBuffer">A pointer to a 64 byte buffer to read signature data from</param> + /// <returns>True if the signature was signed by the supplied public key, false otherwise</returns> + public static bool VerifyData( + NCContext context, + ref readonly NCPublicKey publicKey, + ReadOnlySpan<byte> data, + ReadOnlySpan<byte> signatureBuffer + ) + { + ArgumentOutOfRangeException.ThrowIfLessThan(signatureBuffer.Length, NC_SIGNATURE_SIZE, nameof(signatureBuffer)); + ArgumentOutOfRangeException.ThrowIfZero(data.Length, nameof(data)); + + return VerifyData( + context, + publicKey: in publicKey, + data: in MemoryMarshal.GetReference(data), + dataSize: (uint)data.Length, + sig64: ref MemoryMarshal.GetReference(signatureBuffer) + ); + } + + private static void Check(NCContext? context) + { + ArgumentNullException.ThrowIfNull(context); + context.ThrowIfClosed(); + } + + private static ref readonly FunctionTable GetTable(NCContext ctx) + => ref ctx.Library.Functions; + } +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptSigner.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NoscryptSigner.cs index c81790b..063d2c0 100644 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptSigner.cs +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NoscryptSigner.cs @@ -14,21 +14,23 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. using System; -using VNLib.Utils.Cryptography.Noscrypt.Random; -using VNLib.Utils.Extensions; -using VNLib.Utils.Memory; +using VNLib.Utils.Memory; +using VNLib.Utils.Extensions; +using VNLib.Utils.Cryptography.Noscrypt; +using VNLib.Utils.Cryptography.Noscrypt.Random; using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; -namespace VNLib.Utils.Cryptography.Noscrypt +namespace VNLib.Utils.Cryptography.Noscrypt.Singatures { + /// <summary> /// A simple wrapper class to sign nostr message data using /// the noscrypt library /// </summary> /// <param name="noscrypt">The noscrypt library instance</param> /// <param name="random">A random entropy pool used to source random data for signature entropy</param> - public class NoscryptSigner(INostrCrypto noscrypt, IRandomSource random) + public class NoscryptSigner(NCContext context, IRandomSource random) { /// <summary> /// Gets the size of the buffer required to hold the signature @@ -72,12 +74,16 @@ namespace VNLib.Utils.Cryptography.Noscrypt /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public string SignData( - ReadOnlySpan<byte> secretKey, - ReadOnlySpan<byte> message, + ReadOnlySpan<byte> secretKey, + ReadOnlySpan<byte> message, INostrSignatureEncoder? format = null ) { - return SignData(in NCUtil.AsSecretKey(secretKey), message, format); + return SignData( + in NCKeyUtil.AsSecretKey(secretKey), + message, + format + ); } /// <summary> @@ -90,8 +96,8 @@ namespace VNLib.Utils.Cryptography.Noscrypt /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public string SignData( - ref readonly NCSecretKey secretkey, - ReadOnlySpan<byte> message, + ref readonly NCSecretKey secretkey, + ReadOnlySpan<byte> message, INostrSignatureEncoder? format = null ) { @@ -115,8 +121,8 @@ namespace VNLib.Utils.Cryptography.Noscrypt /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> public void SignData( - ref readonly NCSecretKey secretkey, - ReadOnlySpan<byte> data, + ref readonly NCSecretKey secretkey, + ReadOnlySpan<byte> data, Span<byte> signature ) { @@ -126,7 +132,35 @@ namespace VNLib.Utils.Cryptography.Noscrypt Span<byte> entropy = stackalloc byte[NC_SIG_ENTROPY_SIZE]; random.GetRandomBytes(entropy); - noscrypt.SignData(in secretkey, entropy, data, signature); + NCSignatureUtil.SignData( + context, + in secretkey, + entropy, + data, + signature + ); + } + + public bool VerifyData( + ReadOnlySpan<byte> publicKey, + ReadOnlySpan<byte> data, + ReadOnlySpan<byte> sig + ) + { + return VerifyData( + in NCKeyUtil.AsPublicKey(publicKey), + data, + sig + ); + } + + public bool VerifyData( + ref readonly NCPublicKey pk, + ReadOnlySpan<byte> data, + ReadOnlySpan<byte> sig + ) + { + return NCSignatureUtil.VerifyData(context, in pk, data, sig); } } diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj index 4d1868c..29a4b8f 100644 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj @@ -1,5 +1,5 @@ <Project Sdk="Microsoft.NET.Sdk"> - + <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> @@ -9,7 +9,7 @@ <RootNamespace>VNLib.Utils.Cryptography.Noscrypt</RootNamespace> <AssemblyName>VNLib.Utils.Cryptography.Noscrypt</AssemblyName> </PropertyGroup> - + <PropertyGroup> <Authors>Vaughn Nugent</Authors> <Company>Vaughn Nugent</Company> @@ -19,10 +19,22 @@ <PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/Noscrypt</PackageProjectUrl> <RepositoryUrl>https://github.com/VnUgE/noscryot/tree/master/dotnet/VNLib.Utils.Cryptography.Noscrypt</RepositoryUrl> </PropertyGroup> - + <ItemGroup> - <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0124" /> - <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0124" /> + <None Include="..\README.md"> + <Pack>True</Pack> + <PackagePath>\</PackagePath> + </None> + <None Include="..\..\..\..\LICENSE"> + <Pack>True</Pack> + <PackagePath>\</PackagePath> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </None> </ItemGroup> - + + <ItemGroup> + <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0202" /> + <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0202" /> + </ItemGroup> + </Project> diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/FunctionTable.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/FunctionTable.cs index 0cda5e2..269ae4d 100644 --- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/FunctionTable.cs +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/FunctionTable.cs @@ -119,7 +119,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.@internal internal delegate NCResult NCSignDataDelegate(IntPtr ctx, NCSecretKey* sk, byte* random32, byte* data, uint dataSize, byte* sig64); [SafeMethodName("NCVerifyData")] - internal delegate NCResult NCVerifyDataDelegate(IntPtr ctx, NCPublicKey* sk, byte* data, uint dataSize, byte* sig64); + internal delegate NCResult NCVerifyDataDelegate(IntPtr ctx, NCPublicKey* pk, byte* data, uint dataSize, byte* sig64); [SafeMethodName("NCGetConversationKey")] internal delegate NCResult NCGetConversationKeyDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, byte* keyOut32); diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCUtil.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCUtil.cs new file mode 100644 index 0000000..16f46c6 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCUtil.cs @@ -0,0 +1,104 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +using System; +using System.Reflection; +using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; + +using NCResult = System.Int64; + +namespace VNLib.Utils.Cryptography.Noscrypt.@internal +{ + + public static class NCUtil + { + + + internal static void CheckResult<T>(NCResult result, bool raiseOnFailure) where T : Delegate + { + //Only negative values are errors + if (result >= NC_SUCCESS) + { + return; + } + + NCResult asPositive = -result; + + // Error code are only 8 bits, if an argument error occured, the + // argument number will be in the next upper 8 bits + NCErrorCodes errorCode = (NCErrorCodes)(asPositive & 0xFF); + byte argNumber = (byte)(asPositive >> 8 & 0xFF); + + switch (errorCode) + { + case NCErrorCodes.E_NULL_PTR: + RaiseNullArgExceptionForArgumentNumber<T>(argNumber); + break; + case NCErrorCodes.E_INVALID_ARG: + RaiseArgExceptionForArgumentNumber<T>(argNumber); + break; + case NCErrorCodes.E_ARGUMENT_OUT_OF_RANGE: + RaiseOORExceptionForArgumentNumber<T>(argNumber); + break; + case NCErrorCodes.E_INVALID_CTX: + throw new InvalidOperationException("The library context object is null or invalid"); + case NCErrorCodes.E_OPERATION_FAILED: + RaiseOperationFailedException(raiseOnFailure); + break; + case NCErrorCodes.E_VERSION_NOT_SUPPORTED: + throw new NotSupportedException("The requested version is not supported"); + + default: + if (raiseOnFailure) + { + throw new InvalidOperationException($"The operation failed with error, code: {errorCode} for arugment {argNumber:x}"); + } + break; + } + } + + private static void RaiseOperationFailedException(bool raise) + { + if (raise) + { + throw new InvalidOperationException("The operation failed for an unknown reason"); + } + } + + private static void RaiseNullArgExceptionForArgumentNumber<T>(int argNumber) where T : Delegate + { + //Get delegate parameters + Type type = typeof(T); + ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; + throw new ArgumentNullException(arg.Name, "Argument is null or invalid cannot continue"); + } + + private static void RaiseArgExceptionForArgumentNumber<T>(int argNumber) where T : Delegate + { + //Get delegate parameters + Type type = typeof(T); + ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; + throw new ArgumentException("Argument is null or invalid cannot continue", arg.Name); + } + + private static void RaiseOORExceptionForArgumentNumber<T>(int argNumber) where T : Delegate + { + //Get delegate parameters + Type type = typeof(T); + ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; + throw new ArgumentOutOfRangeException(arg.Name, "Argument is out of range of acceptable values"); + } + } +} |