diff options
Diffstat (limited to 'wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src')
20 files changed, 2027 insertions, 0 deletions
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Base64SignatureEncoder.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Base64SignatureEncoder.cs new file mode 100644 index 0000000..c5078a4 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Base64SignatureEncoder.cs @@ -0,0 +1,31 @@ +// 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; + +namespace VNLib.Utils.Cryptography.Noscrypt +{ + public sealed class Base64SignatureEncoder : INostrSignatureEncoder + { + /// <summary> + /// Shared formatter instance for base64 signatures + /// </summary> + public static Base64SignatureEncoder Instance { get; } = new Base64SignatureEncoder(); + + ///<inheritdoc/> + public string GetString(ReadOnlySpan<byte> signature) => Convert.ToBase64String(signature); + } + +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCCipherUtil.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCCipherUtil.cs new file mode 100644 index 0000000..5dc2a94 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCCipherUtil.cs @@ -0,0 +1,171 @@ +// 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.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 +{ + public unsafe static class NCCipherUtil + { + /* + * This class wraps the low-level cipher functions provided by + * the Noscrypt utility side-car library. + */ + + internal static nint Alloc(NCContext ctx, uint version, uint flags) + { + nint cipher = GetTable(ctx).NCUtilCipherAlloc(version, flags); + + if (cipher == nint.Zero) + { + throw new OutOfMemoryException("Failed to allocate cipher context"); + } + + //Ensure flags are identical to those set during allocation + Debug.Assert(GetFlags(ctx, cipher) == flags); + + return cipher; + } + + internal static uint GetFlags(NCContext ctx, nint cipher) + { + NCResult result = GetTable(ctx).NCUtilCipherGetFlags(cipher); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherGetFlagsDelegate>(result, raiseOnFailure: true); + + return (uint)result; + } + + internal static void Free(NCContext ctx, nint cipher) => GetTable(ctx).NCUtilCipherFree(cipher); + + internal static int GetIvSize(NCContext ctx, nint cipher) + { + NCResult result = GetTable(ctx).NCUtilCipherGetIvSize(cipher); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherGetIvSizeDelegate>(result, raiseOnFailure: true); + + return checked((int)result); + } + + internal static void SetProperty(NCContext ctx, nint cipher, uint property, ref readonly byte value, uint valueLen) + { + fixed (byte* valPtr = &value) + { + NCResult result = GetTable(ctx).NCUtilCipherSetProperty(cipher, property, valPtr, valueLen); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherSetPropertyDelegate>(result, raiseOnFailure: true); + } + } + + internal static uint GetOutputSize(NCContext ctx, nint cipher) + { + NCResult result = GetTable(ctx).NCUtilCipherGetOutputSize(cipher); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherGetOutputSizeDelegate>(result, raiseOnFailure: true); + + return (uint)result; + } + + internal static uint ReadOutput(NCContext ctx, nint cipher, ref byte outputData, uint outLen) + { + fixed (byte* outPtr = &outputData) + { + NCResult result = GetTable(ctx).NCUtilCipherReadOutput(cipher, outPtr, outLen); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherReadOutputDelegate>(result, raiseOnFailure: true); + + return (uint)result; + } + } + + 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); + } + + internal static void Update( + NCContext ctx, + nint cipher, + ref readonly NCSecretKey localKey, + ref readonly NCPublicKey remoteKey + ) + { + fixed (NCSecretKey* sk = &localKey) + fixed (NCPublicKey* pk = &remoteKey) + { + NCResult result = GetTable(ctx).NCUtilCipherUpdate( + cipher: cipher, + libContext: ctx.DangerousGetHandle(), + secKey: sk, + pubKey: pk + ); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherInitDelegate>(result, raiseOnFailure: true); + } + } + + +#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/NoscryptCipherFlags.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherFlags.cs new file mode 100644 index 0000000..275c90c --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherFlags.cs @@ -0,0 +1,60 @@ +// 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; + +namespace VNLib.Utils.Cryptography.Noscrypt.Encryption +{ + /// <summary> + /// Cipher specific flags that control the behavior of a cipher + /// instance + /// </summary> + [Flags] + public enum NoscryptCipherFlags : uint + { + /// <summary> + /// Puts the cipher into encryption mode + /// </summary> + ModeEncryption = 0x00u, + + /// <summary> + /// Puts the cipher into decryption mode + /// </summary> + ModeDecryption = 0x01u, + + /// <summary> + /// Forces all internal memory to be freed when + /// the cipher is freed + /// </summary> + ZeroOnFree = 0x02u, + + /// <summary> + /// Disables mac verification during decryption operations, + /// by default nip44 macs are verified before the decryption + /// operation. + /// </summary> + MacNoVerify = 0x04u, + + /// <summary> + /// Allows allocated cipher instances to be reused multiple + /// times. Otherwise the cipher may only be used once after + /// allocation. + /// </summary> + Reusable = 0x08u, + + EncryptDefault = ModeEncryption | Reusable | ZeroOnFree, + DecryptDefault = ModeDecryption | Reusable | ZeroOnFree + } +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherVersion.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherVersion.cs new file mode 100644 index 0000000..763a5f4 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherVersion.cs @@ -0,0 +1,36 @@ +// 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 static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; + +namespace VNLib.Utils.Cryptography.Noscrypt.Encryption +{ + /// <summary> + /// The Noscrypt utility cipher encryption + /// standard version + /// </summary> + public enum NoscryptCipherVersion : uint + { + /// <summary> + /// Tells the cipher to use the NIP04 encryption standard + /// </summary> + Nip04 = NC_ENC_VERSION_NIP04, + + /// <summary> + /// Tells the cipher to use the NIP44 encryption standard + /// </summary> + Nip44 = NC_ENC_VERSION_NIP44 + } +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptMessageCipher.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptMessageCipher.cs new file mode 100644 index 0000000..e44addf --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptMessageCipher.cs @@ -0,0 +1,256 @@ +// 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.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; + +namespace VNLib.Utils.Cryptography.Noscrypt.Encryption +{ + /// <summary> + /// The noscrypt util cipher wapper + /// </summary> + /// <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 class NoscryptMessageCipher : SafeHandleMinusOneIsInvalid + { + private readonly NCContext _context; + private readonly NoscryptCipherVersion _version; + private IMemoryHandle<byte>? _ivBuffer; + + 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; + + /// <summary> + /// Gets the flags set for the cipher instance + /// </summary> + 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() => NCCipherUtil.GetIvSize(_context, handle); + + /// <summary> + /// Gets the internal heap buffer that holds the cipher's initalization + /// vector. + /// </summary> + /// <returns>The mutable span of the cipher's IV buffer</returns> + public Span<byte> IvBuffer + { + get => LazyInitializer.EnsureInitialized(ref _ivBuffer, AllocIvBuffer).Span; + } + + /// <summary> + /// Sets the cipher's initialization vector to a random value using + /// the specified random source + /// </summary> + /// <param name="rng">The random source</param> + public void SetRandomIv(IRandomSource rng) + { + ArgumentNullException.ThrowIfNull(rng); + rng.GetRandomBytes(IvBuffer); + } + + /// <summary> + /// Performs the cipher operation on the input data using the specified + /// local and remote keys. + /// </summary> + /// <param name="localKey">The secret key of the local user</param> + /// <param name="remoteKey">The public key of the remote user</param> + /// <param name="inputData">A pointer to the first byte in the buffer sequence</param> + /// <param name="inputSize">The size of the input buffer in bytes</param> + /// <exception cref="ArgumentNullException"></exception> + /// <remarks> + /// If the <see cref="NoscryptCipherFlags.Reusable"/> flag is + /// set, this function may be considered independent and called repeatedly. + /// </remarks> + public unsafe void Update( + ref readonly NCSecretKey localKey, + ref readonly NCPublicKey remoteKey, + ref readonly byte inputData, + uint inputSize + ) + { + if (Unsafe.IsNullRef(in localKey)) + { + throw new ArgumentNullException(nameof(localKey)); + } + + if (Unsafe.IsNullRef(in remoteKey)) + { + throw new ArgumentNullException(nameof(remoteKey)); + } + + if (Unsafe.IsNullRef(in inputData)) + { + throw new ArgumentNullException(nameof(inputData)); + } + + /* + * Initializing the cipher requires the context holding a pointer + * to the input data, so it has to be pinned in a fixed statment + * for the duration of the update operation. + * + * So init and update must be done as an atomic operation. + * + * If ciphers have the Reusable flag set this function may be called + * repeatedly. The results of this operation can be considered + * independent. + */ + + fixed (byte* inputPtr = &inputData) + { + NCCipherUtil.InitCipher(_context, handle, inputPtr, inputSize); + + NCCipherUtil.Update(_context, handle, in localKey, in remoteKey); + } + } + + /// <summary> + /// Performs the cipher operation on the input data using the specified + /// local and remote keys. + /// </summary> + /// <param name="localKey">The secret key of the local user</param> + /// <param name="remoteKey">The public key of the remote user</param> + /// <param name="input">The buffer sequence to read the input data from</param> + /// <exception cref="ArgumentNullException"></exception> + public void Update( + ref readonly NCSecretKey localKey, + ref readonly NCPublicKey remoteKey, + ReadOnlySpan<byte> input + ) + { + Update( + in localKey, + in remoteKey, + inputData: ref MemoryMarshal.GetReference(input), //If empty, null ref will throw + inputSize: (uint)input.Length + ); + } + + /// <summary> + /// 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)NCCipherUtil.GetOutputSize(_context, handle)); + + /// <summary> + /// Reads the output data from the cipher into the specified buffer + /// </summary> + /// <param name="outputData">A reference to the first byte in the buffer sequence</param> + /// <param name="size">The size of the buffer sequence</param> + /// <returns>The number of bytes written to the buffer</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public int ReadOutput(ref byte outputData, int size) + { + ArgumentOutOfRangeException.ThrowIfLessThan(size, GetOutputSize()); + + return checked((int)NCCipherUtil.ReadOutput(_context, handle, ref outputData, (uint)size)); + } + + /// <summary> + /// Reads the output data from the cipher into the specified buffer + /// </summary> + /// <param name="buffer">The buffer sequence to write output data to</param> + /// <returns>The number of bytes written to the buffer</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public int ReadOutput(Span<byte> buffer) + { + return ReadOutput( + ref MemoryMarshal.GetReference(buffer), + buffer.Length + ); + } + + private IMemoryHandle<byte> AllocIvBuffer() + { + //Use the context heap to allocate the internal iv buffer + MemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(_context.Heap, GetIvSize(), zero: true); + + try + { + /* + * Assign the buffer reference to the cipher context + * + * NOTE: This pointer will be held as long as the cipher + * context is allocated. So the buffer must be held until + * the cipher is freed. Because of this an umnanaged heap + * buffer is required so we don't need to pin managed memory + * nor worry about the GC moving the buffer. + */ + NCCipherUtil.SetProperty( + _context, + DangerousGetHandle(), + NC_ENC_SET_IV, + ref buffer.GetReference(), + (uint)buffer.Length + ); + } + catch + { + buffer.Dispose(); + throw; + } + + return buffer; + } + + ///<inheritdoc/> + protected override bool ReleaseHandle() + { + NCCipherUtil.Free(_context, handle); + _ivBuffer?.Dispose(); + + return true; + } + } + +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/HexSignatureEncoder.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/HexSignatureEncoder.cs new file mode 100644 index 0000000..6a60c73 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/HexSignatureEncoder.cs @@ -0,0 +1,31 @@ +// 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; + +namespace VNLib.Utils.Cryptography.Noscrypt +{ + public sealed class HexSignatureEncoder : INostrSignatureEncoder + { + /// <summary> + /// Shared formatter instance for hex signatures + /// </summary> + public static HexSignatureEncoder Instance { get; } = new HexSignatureEncoder(); + + ///<inheritdoc/> + public string GetString(ReadOnlySpan<byte> signature) => Convert.ToHexString(signature); + } + +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrSignatureEncoder.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrSignatureEncoder.cs new file mode 100644 index 0000000..b8c69f5 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrSignatureEncoder.cs @@ -0,0 +1,33 @@ +// 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; + +namespace VNLib.Utils.Cryptography.Noscrypt +{ + /// <summary> + /// Encodes a message signature into it's string representation + /// </summary> + public interface INostrSignatureEncoder + { + /// <summary> + /// Creates a string of the encoded signature data + /// </summary> + /// <param name="signature">The signature data to encode into the string</param> + /// <returns>The encoded signature string</returns> + string GetString(ReadOnlySpan<byte> signature); + } + +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs new file mode 100644 index 0000000..61128eb --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs @@ -0,0 +1,87 @@ +// 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.Diagnostics; + +using Microsoft.Win32.SafeHandles; + +using VNLib.Utils.Extensions; +using VNLib.Utils.Memory; + +using VNLib.Utils.Cryptography.Noscrypt.@internal; +using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; + +using NCResult = System.Int64; + +namespace VNLib.Utils.Cryptography.Noscrypt +{ + /// <summary> + /// The noscrypt library context + /// </summary> + public sealed class NCContext : SafeHandleZeroOrMinusOneIsInvalid + { + internal readonly IUnmangedHeap Heap; + + /// <summary> + /// The library this context was created from + /// </summary> + public NoscryptLibrary Library { get; } + + internal NCContext(IntPtr handle, IUnmangedHeap heap, NoscryptLibrary library) :base(true) + { + ArgumentNullException.ThrowIfNull(heap); + ArgumentNullException.ThrowIfNull(library); + + Heap = heap; + Library = library; + + //Store the handle + SetHandle(handle); + } + + /// <summary> + /// Reinitializes the context with the specified entropy + /// </summary> + /// <param name="entropy">The randomness buffer used to randomize the context</param> + /// <param name="size">The random data buffer size (must be 32 bytes)</param> + public unsafe void Reinitalize(ref byte entropy, int size) + { + //Entropy must be exactly 32 bytes + ArgumentOutOfRangeException.ThrowIfNotEqual(size, NC_CTX_ENTROPY_SIZE); + + this.ThrowIfClosed(); + fixed (byte* p = &entropy) + { + NCResult result = Library.Functions.NCReInitContext.Invoke(handle, p); + NCUtil.CheckResult<FunctionTable.NCReInitContextDelegate>(result, true); + } + } + + ///<inheritdoc/> + protected override bool ReleaseHandle() + { + if (!Library.IsClosed) + { + //destroy the context + Library.Functions.NCDestroyContext.Invoke(handle); + Trace.WriteLine($"Destroyed noscrypt context 0x{handle:x}"); + } + + //Free the handle + return Heap.Free(ref handle); + } + } +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCKeyUtil.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCKeyUtil.cs new file mode 100644 index 0000000..50e1c9a --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCKeyUtil.cs @@ -0,0 +1,185 @@ +// 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 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 +{ + /// <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 + /// structure + /// </summary> + /// <param name="key"></param> + /// <returns>The secret key data span</returns> + public unsafe static Span<byte> AsSpan(this ref NCSecretKey key) + { + //Safe to cast secret key to bytes, then we can make a span to its memory + ref byte asBytes = ref Unsafe.As<NCSecretKey, byte>(ref key); + return MemoryMarshal.CreateSpan(ref asBytes, sizeof(NCSecretKey)); + } + + /// <summary> + /// Gets a span of bytes from the current public key + /// structure + /// </summary> + /// <param name="key"></param> + /// <returns>The public key data as a data span</returns> + public unsafe static Span<byte> AsSpan(this ref NCPublicKey key) + { + //Safe to cast secret key to bytes, then we can make a span to its memory + ref byte asBytes = ref Unsafe.As<NCPublicKey, byte>(ref key); + return MemoryMarshal.CreateSpan(ref asBytes, sizeof(NCPublicKey)); + } + + /// <summary> + /// Casts a span of bytes to a secret key reference. Note that + /// the new structure reference will point to the same memory + /// as the span. + /// </summary> + /// <param name="span">The secret key data</param> + /// <returns>A mutable secret key reference</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public unsafe static ref NCSecretKey AsSecretKey(Span<byte> span) + { + ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCSecretKey), nameof(span)); + + ref byte asBytes = ref MemoryMarshal.GetReference(span); + return ref Unsafe.As<byte, NCSecretKey>(ref asBytes); + } + + /// <summary> + /// Casts a span of bytes to a public key reference. Note that + /// the new structure reference will point to the same memory + /// as the span. + /// </summary> + /// <param name="span">The public key data span</param> + /// <returns>A mutable reference to the public key structure</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public unsafe static ref NCPublicKey AsPublicKey(Span<byte> span) + { + ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCPublicKey), nameof(span)); + + ref byte asBytes = ref MemoryMarshal.GetReference(span); + return ref Unsafe.As<byte, NCPublicKey>(ref asBytes); + } + + /// <summary> + /// Casts a read-only span of bytes to a secret key reference. Note that + /// the new structure reference will point to the same memory as the span. + /// </summary> + /// <param name="span">The secret key data span</param> + /// <returns>A readonly refernce to the secret key structure</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public unsafe static ref readonly NCSecretKey AsSecretKey(ReadOnlySpan<byte> span) + { + ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCSecretKey), nameof(span)); + + ref byte asBytes = ref MemoryMarshal.GetReference(span); + return ref Unsafe.As<byte, NCSecretKey>(ref asBytes); + } + + /// <summary> + /// Casts a read-only span of bytes to a public key reference. Note that + /// the new structure reference will point to the same memory as the span. + /// </summary> + /// <param name="span">The public key data span</param> + /// <returns>A readonly reference to the public key structure</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public unsafe static ref readonly NCPublicKey AsPublicKey(ReadOnlySpan<byte> span) + { + ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCPublicKey), nameof(span)); + + ref byte asBytes = ref MemoryMarshal.GetReference(span); + return ref Unsafe.As<byte, NCPublicKey>(ref asBytes); + } + + /// <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 + ) + { + Check(context); + + fixed (NCSecretKey* pSecKey = &secretKey) + fixed (NCPublicKey* pPubKey = &publicKey) + { + NCResult result = GetTable(context).NCGetPublicKey( + context.DangerousGetHandle(), + pSecKey, + pPubKey + ); + + NCUtil.CheckResult<FunctionTable.NCGetPublicKeyDelegate>(result, raiseOnFailure: true); + } + } + + /// <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 + ) + { + Check(context); + + fixed (NCSecretKey* pSecKey = &secretKey) + { + NCResult result = GetTable(context).NCValidateSecretKey( + context.DangerousGetHandle(), + pSecKey + ); + + NCUtil.CheckResult<FunctionTable.NCValidateSecretKeyDelegate>(result, raiseOnFailure: false); + + return result == NC_SUCCESS; + } + } + + 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/NCPublicKey.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCPublicKey.cs new file mode 100644 index 0000000..57d7c3f --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCPublicKey.cs @@ -0,0 +1,37 @@ +// 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 +{ + /// <summary> + /// Represents a user's secp256k1 public key for use with the Nostrcrypt library + /// </summary> + [StructLayout(LayoutKind.Sequential, Size = NC_SEC_PUBKEY_SIZE)] + public unsafe struct NCPublicKey + { + /// <summary> + /// Gets a null <see cref="NCPublicKey"/> reference. + /// </summary> + public static ref NCPublicKey NullRef => ref Unsafe.NullRef<NCPublicKey>(); + + private fixed byte key[NC_SEC_PUBKEY_SIZE]; + } +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs new file mode 100644 index 0000000..18f025b --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs @@ -0,0 +1,37 @@ +// 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.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; + +namespace VNLib.Utils.Cryptography.Noscrypt +{ + /// <summary> + /// Represents an nostr variant of a secp265k1 secret key that matches + /// the size of the native library + /// </summary> + [StructLayout(LayoutKind.Sequential, Size = NC_SEC_KEY_SIZE)] + public unsafe struct NCSecretKey + { + /// <summary> + /// Gets a null reference to a secret key + /// </summary> + public static ref NCSecretKey NullRef => ref Unsafe.NullRef<NCSecretKey>(); + + private fixed byte key[NC_SEC_KEY_SIZE]; + } +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs new file mode 100644 index 0000000..2df63eb --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs @@ -0,0 +1,251 @@ +// 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.Diagnostics; +using System.Runtime.InteropServices; + +using VNLib.Utils.Memory; +using VNLib.Utils.Native; +using VNLib.Utils.Extensions; + +using VNLib.Utils.Cryptography.Noscrypt.Random; +using VNLib.Utils.Cryptography.Noscrypt.@internal; + +using NCResult = System.Int64; + +namespace VNLib.Utils.Cryptography.Noscrypt +{ + + /// <summary> + /// Initializes the native library and provides access to the native functions + /// </summary> + /// <param name="Library">An existing noscrypt library handle</param> + /// <param name="OwnsHandle">A value that indicates if the instance owns the library handle</param> + 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; + public const int NC_SEC_PUBKEY_SIZE = 0x20; + public const int NC_PUBKEY_SIZE = 0x20; + public const int NC_SIGNATURE_SIZE = 0x40; + public const int NC_CONV_KEY_SIZE = 0x20; + public const int NC_MESSAGE_KEY_SIZE = 0x20; + public const int NC_HMAC_KEY_SIZE = 0x20; + public const int NC_ENCRYPTION_MAC_SIZE = 0x20; + public const int NC_CONVERSATION_KEY_SIZE = 0x20; + public const int NC_CTX_ENTROPY_SIZE = 0x20; + public const int NC_SIG_ENTROPY_SIZE = 0x20; + + public const uint NC_ENC_VERSION_NIP04 = 0x00000004u; + public const uint NC_ENC_VERSION_NIP44 = 0x00000002c; + + public const uint NC_ENC_SET_VERSION = 0x01u; + public const uint NC_ENC_SET_IV = 0x02u; + public const uint NC_ENC_SET_NIP44_MAC_KEY = 0x03u; + public const uint NC_ENC_SET_NIP04_KEY = 0x04u; + + public const NCResult NC_SUCCESS = 0x00; + + public enum NCErrorCodes : long + { + NC_SUCCESS = 0, + + //Generic argument related errors + E_NULL_PTR = 1, + E_INVALID_ARG = 2, + E_INVALID_CTX = 3, + E_ARGUMENT_OUT_OF_RANGE = 4, + E_OPERATION_FAILED = 5, + E_VERSION_NOT_SUPPORTED = 6, + + //Cipher errors + E_CIPHER_INVALID_FORMAT = 11, + E_CIPHER_BAD_NONCE = 12, + E_CIPHER_MAC_INVALID = 13, + E_CIPHER_NO_OUTPUT = 14, + E_CIPHER_BAD_INPUT = 15, + E_CIPHER_BAD_INPUT_SIZE = 16 + } + + //Cipher flags + public const uint NC_UTIL_CIPHER_MODE = 0x01u; + + + + private readonly FunctionTable _functions = FunctionTable.BuildFunctionTable(Library); + + /// <summary> + /// Gets a reference to the loaded function table for + /// the native library + /// </summary> + internal ref readonly FunctionTable Functions + { + get + { + Check(); + Library.ThrowIfClosed(); + return ref _functions; + } + } + + /// <summary> + /// Gets a value that determines if the library has been released + /// </summary> + internal bool IsClosed => Library.IsClosed || Library.IsInvalid; + + /// <summary> + /// 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"></param> + /// <param name="entropy32">Initialization entropy buffer</param> + /// <param name="size">The size of the buffer (must be 32 bytes)</param> + /// <returns>The inialized context</returns> + /// <exception cref="OutOfMemoryException"></exception> + /// <exception cref="ArgumentNullException"></exception> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public NCContext Initialize(IUnmangedHeap heap, ref readonly byte entropy32, int size) + { + ArgumentNullException.ThrowIfNull(heap); + + //Entropy must be exactly 32 bytes + ArgumentOutOfRangeException.ThrowIfNotEqual(size, NC_CTX_ENTROPY_SIZE); + + //Get struct size + nuint ctxSize = Functions.NCGetContextStructSize.Invoke(); + + //Allocate the context with the struct alignment on a heap + IntPtr ctx = heap.Alloc(1, ctxSize, true); + try + { + NCResult result; + fixed (byte* p = &entropy32) + { + result = Functions.NCInitContext.Invoke(ctx, p); + } + + NCUtil.CheckResult<FunctionTable.NCInitContextDelegate>(result, true); + + Trace.WriteLine($"Initialzied noscrypt context 0x{ctx:x}"); + + return new NCContext(ctx, heap, this); + } + catch + { + heap.Free(ref ctx); + throw; + } + } + + /// <summary> + /// 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"></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, ReadOnlySpan<byte> enropy32) + { + return Initialize( + heap, + ref MemoryMarshal.GetReference(enropy32), + enropy32.Length + ); + } + + /// <summary> + /// 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"></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); + + //Get random bytes for context entropy + Span<byte> entropy = stackalloc byte[NC_CTX_ENTROPY_SIZE]; + random.GetRandomBytes(entropy); + + return Initialize(heap, entropy); + } + + ///<inheritdoc/> + protected override void Free() + { + if (OwnsHandle) + { + Library.Dispose(); + Trace.WriteLine($"Disposed noscrypt library 0x{Library.DangerousGetHandle():x}"); + } + } + + /// <summary> + /// Loads the native library from the specified path and initializes the + /// function table for use. + /// </summary> + /// <param name="path">The native library path or name to load</param> + /// <param name="search">The search path options</param> + /// <returns>The loaded library instance</returns> + /// <exception cref="DllNotFoundException"></exception> + public static NoscryptLibrary Load(string path, DllImportSearchPath search) + { + //Load the native library + SafeLibraryHandle handle = SafeLibraryHandle.LoadLibrary(path, search); + + Trace.WriteLine($"Loaded noscrypt library 0x{handle.DangerousGetHandle():x} from {path}"); + + //Create the wrapper + return new NoscryptLibrary(handle, true); + } + + /// <summary> + /// Loads the native library from the specified path and initializes the + /// function table for use. + /// </summary> + /// <param name="path">The native library path or name to load</param> + /// <returns>The loaded library instance</returns> + /// <exception cref="DllNotFoundException"></exception> + public static NoscryptLibrary Load(string path) => Load(path, DllImportSearchPath.SafeDirectories); + + /// <summary> + /// Attempts to load the default noscrypt library from the system search path + /// </summary> + /// <returns>The loaded library instance</returns> + /// <exception cref="DllNotFoundException"></exception> + 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/Random/IRandomSource.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/IRandomSource.cs new file mode 100644 index 0000000..84430a0 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/IRandomSource.cs @@ -0,0 +1,32 @@ +// 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; + +namespace VNLib.Utils.Cryptography.Noscrypt.Random +{ + /// <summary> + /// Represents a generator for random data, that fills abinary buffer with random bytes + /// on demand. + /// </summary> + public interface IRandomSource + { + /// <summary> + /// Fills the given buffer with random bytes + /// </summary> + /// <param name="buffer">Binary buffer to fill with random data</param> + void GetRandomBytes(Span<byte> buffer); + } +}
\ No newline at end of file diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/NcFallbackRandom.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/NcFallbackRandom.cs new file mode 100644 index 0000000..5e0675e --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/NcFallbackRandom.cs @@ -0,0 +1,36 @@ +// 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.Hashing; + +namespace VNLib.Utils.Cryptography.Noscrypt.Random +{ + /// <summary> + /// A fallback crypographic random source used for default + /// rng if you wish + /// </summary> + public sealed class NCFallbackRandom : IRandomSource + { + /// <summary> + /// Gets the shared instance of the fallback random source + /// </summary> + public static NCFallbackRandom Shared { get; } = new NCFallbackRandom(); + + /// <inheritdoc/> + public void GetRandomBytes(Span<byte> buffer) => RandomHash.GetRandomBytes(buffer); + } +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/UnmanagedRandomSource.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/UnmanagedRandomSource.cs new file mode 100644 index 0000000..73ff374 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/UnmanagedRandomSource.cs @@ -0,0 +1,103 @@ +// 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.Native; +using VNLib.Utils.Extensions; + +namespace VNLib.Utils.Cryptography.Noscrypt.Random +{ + + /// <summary> + /// A wrapper class for an unmanaged random source that conforms to the <see cref="IRandomSource"/> interface + /// </summary> + public class UnmanagedRandomSource : VnDisposeable, IRandomSource + { + /// <summary> + /// The explicit name of the unmanaged random function to get random bytes + /// </summary> + public const string RngFunctionName = "GetRandomBytes"; + + unsafe delegate void UnmanagedRandomSourceDelegate(byte* buffer, int size); + + private readonly bool OwnsHandle; + private readonly SafeLibraryHandle _library; + private readonly UnmanagedRandomSourceDelegate _getRandomBytes; + + /// <summary> + /// Loads the unmanaged random source from the given library + /// and attempts to get the random bytes method <see cref="RngFunctionName"/> + /// </summary> + /// <param name="path"></param> + /// <param name="search"></param> + /// <returns>The wrapped library that conforms to the <see cref="IRandomSource"/></returns> + public static UnmanagedRandomSource LoadLibrary(string path, DllImportSearchPath search) + { + //Try to load the library + SafeLibraryHandle lib = SafeLibraryHandle.LoadLibrary(path, search); + try + { + return new UnmanagedRandomSource(lib, true); + } + catch + { + //release lib + lib.Dispose(); + throw; + } + } + + /// <summary> + /// Creates the unmanaged random source from the given library + /// </summary> + /// <param name="lib">The library handle to wrap</param> + /// <exception cref="ObjectDisposedException"></exception> + /// <exception cref="EntryPointNotFoundException"></exception> + public UnmanagedRandomSource(SafeLibraryHandle lib, bool ownsHandle) + { + lib.ThrowIfClosed(); + + _library = lib; + + //get the method delegate + _getRandomBytes = lib.DangerousGetFunction<UnmanagedRandomSourceDelegate>(RngFunctionName); + + OwnsHandle = ownsHandle; + } + + ///<inheritdoc/> + public unsafe void GetRandomBytes(Span<byte> buffer) + { + _library.ThrowIfClosed(); + + //Fix buffer and call unmanaged method + fixed (byte* ptr = &MemoryMarshal.GetReference(buffer)) + { + _getRandomBytes(ptr, buffer.Length); + } + } + + ///<inheritdoc/> + protected override void Free() + { + if (OwnsHandle) + { + _library.Dispose(); + } + } + } +}
\ No newline at end of file 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/Singatures/NoscryptSigner.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NoscryptSigner.cs new file mode 100644 index 0000000..063d2c0 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NoscryptSigner.cs @@ -0,0 +1,167 @@ +// 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.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.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(NCContext context, IRandomSource random) + { + /// <summary> + /// Gets the size of the buffer required to hold the signature + /// </summary> + public static int SignatureBufferSize => NC_SIGNATURE_SIZE; + + /// <summary> + /// Signs a message using the specified private key and message data + /// </summary> + /// <param name="hexPrivateKey">The hexadecimal private key used to sign the message</param> + /// <param name="message">The message data to sign</param> + /// <param name="format">A encoder used to convert the signature data to an encoded string</param> + /// <returns>The string encoded nostr signature</returns> + /// <exception cref="ArgumentException"></exception> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public string SignData(string hexPrivateKey, ReadOnlySpan<byte> message, INostrSignatureEncoder? format = null) + { + ArgumentException.ThrowIfNullOrWhiteSpace(hexPrivateKey); + ArgumentOutOfRangeException.ThrowIfNotEqual(hexPrivateKey.Length / 2, NC_SEC_KEY_SIZE, nameof(hexPrivateKey)); + + //Have to allocate array unfortunately + byte[] privKey = Convert.FromHexString(hexPrivateKey); + try + { + return SignData(privKey.AsSpan(), message, format); + } + finally + { + //Always zero key beofre leaving + MemoryUtil.InitializeBlock(privKey); + } + } + + /// <summary> + /// Signs a message using the specified secret key and message data + /// </summary> + /// <param name="secretKey">The secret key data buffer</param> + /// <param name="message">The message data to sign</param> + /// <param name="format">A encoder used to convert the signature data to an encoded string</param> + /// <returns>The string encoded nostr signature</returns> + /// <exception cref="ArgumentException"></exception> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public string SignData( + ReadOnlySpan<byte> secretKey, + ReadOnlySpan<byte> message, + INostrSignatureEncoder? format = null + ) + { + return SignData( + in NCKeyUtil.AsSecretKey(secretKey), + message, + format + ); + } + + /// <summary> + /// Signs a message using the specified secret key and message data + /// </summary> + /// <param name="secretkey">A reference to the secret key structurer</param> + /// <param name="message">The message data to sign</param> + /// <param name="format">A encoder used to convert the signature data to an encoded string</param> + /// <returns>The string encoded nostr signature</returns> + /// <exception cref="ArgumentException"></exception> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public string SignData( + ref readonly NCSecretKey secretkey, + ReadOnlySpan<byte> message, + INostrSignatureEncoder? format = null + ) + { + //Default to hex encoding because that is the default NIP-01 format + format ??= HexSignatureEncoder.Instance; + + Span<byte> sigBuffer = stackalloc byte[SignatureBufferSize]; + + SignData(message, sigBuffer); + + return format.GetString(sigBuffer); + } + + + /// <summary> + /// Signs a message using the specified secret key and message data + /// </summary> + /// <param name="secretkey">A reference to the secret key structurer</param> + /// <param name="data">The message data to sign</param> + /// <param name="signature">A buffer to write signature data to</param> + /// <exception cref="ArgumentException"></exception> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public void SignData( + ref readonly NCSecretKey secretkey, + ReadOnlySpan<byte> data, + Span<byte> signature + ) + { + ArgumentOutOfRangeException.ThrowIfLessThan(signature.Length, NC_SIGNATURE_SIZE, nameof(signature)); + + //Signature generation required random entropy to be secure + Span<byte> entropy = stackalloc byte[NC_SIG_ENTROPY_SIZE]; + random.GetRandomBytes(entropy); + + 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 new file mode 100644 index 0000000..416cfa3 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj @@ -0,0 +1,40 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net8.0</TargetFramework> + <Nullable>enable</Nullable> + <ImplicitUsings>false</ImplicitUsings> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> + <PackageReadmeFile>README.md</PackageReadmeFile> + <RootNamespace>VNLib.Utils.Cryptography.Noscrypt</RootNamespace> + <AssemblyName>VNLib.Utils.Cryptography.Noscrypt</AssemblyName> + </PropertyGroup> + + <PropertyGroup> + <Authors>Vaughn Nugent</Authors> + <Company>Vaughn Nugent</Company> + <Product>VNLib.Utils.Cryptography.Noscrypt</Product> + <Description>Provides a managed library wrapper for the noscrypt native library</Description> + <Copyright>Copyright © 2024 Vaughn Nugent</Copyright> + <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> + <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-ci0205" /> + <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0205" /> + </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 new file mode 100644 index 0000000..269ae4d --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/FunctionTable.cs @@ -0,0 +1,155 @@ +// 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.Native; +using VNLib.Utils.Extensions; + +using NCResult = System.Int64; + +namespace VNLib.Utils.Cryptography.Noscrypt.@internal +{ + + internal unsafe readonly struct FunctionTable + { + + public readonly NCGetContextStructSizeDelegate NCGetContextStructSize; + public readonly NCInitContextDelegate NCInitContext; + public readonly NCReInitContextDelegate NCReInitContext; + public readonly NCDestroyContextDelegate NCDestroyContext; + public readonly NCGetPublicKeyDelegate NCGetPublicKey; + public readonly NCValidateSecretKeyDelegate NCValidateSecretKey; + public readonly NCSignDataDelegate NCSignData; + public readonly NCVerifyDataDelegate NCVerifyData; + public readonly NCUtilCipherAllocDelegate NCUtilCipherAlloc; + public readonly NCUtilCipherFreeDelegate NCUtilCipherFree; + public readonly NCUtilCipherInitDelegate NCUtilCipherInit; + public readonly NCUtilCipherGetFlagsDelegate NCUtilCipherGetFlags; + public readonly NCUtilCipherGetOutputSizeDelegate NCUtilCipherGetOutputSize; + public readonly NCUtilCipherReadOutputDelegate NCUtilCipherReadOutput; + public readonly NCUtilCipherSetPropertyDelegate NCUtilCipherSetProperty; + public readonly NCUtilCipherUpdateDelegate NCUtilCipherUpdate; + public readonly NCUtilCipherGetIvSizeDelegate NCUtilCipherGetIvSize; + + +#if DEBUG + public readonly NCGetConversationKeyDelegate NCGetConversationKey; +#endif + + private FunctionTable(SafeLibraryHandle library) + { + //Load the required high-level api functions + NCGetContextStructSize = library.DangerousGetFunction<NCGetContextStructSizeDelegate>(); + NCInitContext = library.DangerousGetFunction<NCInitContextDelegate>(); + NCReInitContext = library.DangerousGetFunction<NCReInitContextDelegate>(); + NCDestroyContext = library.DangerousGetFunction<NCDestroyContextDelegate>(); + NCGetPublicKey = library.DangerousGetFunction<NCGetPublicKeyDelegate>(); + NCValidateSecretKey = library.DangerousGetFunction<NCValidateSecretKeyDelegate>(); + NCSignData = library.DangerousGetFunction<NCSignDataDelegate>(); + NCVerifyData = library.DangerousGetFunction<NCVerifyDataDelegate>(); + NCSignData = library.DangerousGetFunction<NCSignDataDelegate>(); + NCVerifyData = library.DangerousGetFunction<NCVerifyDataDelegate>(); + + //Cipher util library functions + NCUtilCipherAlloc = library.DangerousGetFunction<NCUtilCipherAllocDelegate>(); + NCUtilCipherFree = library.DangerousGetFunction<NCUtilCipherFreeDelegate>(); + NCUtilCipherInit = library.DangerousGetFunction<NCUtilCipherInitDelegate>(); + NCUtilCipherGetFlags = library.DangerousGetFunction<NCUtilCipherGetFlagsDelegate>(); + NCUtilCipherGetOutputSize = library.DangerousGetFunction<NCUtilCipherGetOutputSizeDelegate>(); + NCUtilCipherReadOutput = library.DangerousGetFunction<NCUtilCipherReadOutputDelegate>(); + NCUtilCipherSetProperty = library.DangerousGetFunction<NCUtilCipherSetPropertyDelegate>(); + NCUtilCipherUpdate = library.DangerousGetFunction<NCUtilCipherUpdateDelegate>(); + NCUtilCipherGetIvSize = library.DangerousGetFunction<NCUtilCipherGetIvSizeDelegate>(); + +#if DEBUG + NCGetConversationKey = library.DangerousGetFunction<NCGetConversationKeyDelegate>(); +#endif + } + + /// <summary> + /// Initialize a new function table from the specified library + /// </summary> + /// <param name="library"></param> + /// <returns>The function table structure</returns> + /// <exception cref="MissingMemberException"></exception> + /// <exception cref="EntryPointNotFoundException"></exception> + public static FunctionTable BuildFunctionTable(SafeLibraryHandle library) => new (library); + + /* + * ################################################ + * + * Functions match the noscrypt.h header file + * + * ################################################ + */ + + //FUCNTIONS + [SafeMethodName("NCGetContextStructSize")] + internal delegate uint NCGetContextStructSizeDelegate(); + + [SafeMethodName("NCInitContext")] + internal delegate NCResult NCInitContextDelegate(IntPtr ctx, byte* entropy32); + + [SafeMethodName("NCReInitContext")] + internal delegate NCResult NCReInitContextDelegate(IntPtr ctx, byte* entropy32); + + [SafeMethodName("NCDestroyContext")] + internal delegate NCResult NCDestroyContextDelegate(IntPtr ctx); + + [SafeMethodName("NCGetPublicKey")] + internal delegate NCResult NCGetPublicKeyDelegate(IntPtr ctx, NCSecretKey* secKey, NCPublicKey* publicKey); + + [SafeMethodName("NCValidateSecretKey")] + internal delegate NCResult NCValidateSecretKeyDelegate(IntPtr ctx, NCSecretKey* secKey); + + [SafeMethodName("NCSignData")] + 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* pk, byte* data, uint dataSize, byte* sig64); + + [SafeMethodName("NCGetConversationKey")] + internal delegate NCResult NCGetConversationKeyDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, byte* keyOut32); + + + [SafeMethodName("NCUtilCipherAlloc")] + internal delegate IntPtr NCUtilCipherAllocDelegate(uint version, uint flags); + + [SafeMethodName("NCUtilCipherFree")] + internal delegate void NCUtilCipherFreeDelegate(IntPtr cipher); + + [SafeMethodName("NCUtilCipherInit")] + internal delegate NCResult NCUtilCipherInitDelegate(IntPtr cipher, byte* inputData, uint inputLen); + + [SafeMethodName("NCUtilCipherGetFlags")] + internal delegate NCResult NCUtilCipherGetFlagsDelegate(IntPtr cipher); + + [SafeMethodName("NCUtilCipherGetOutputSize")] + internal delegate NCResult NCUtilCipherGetOutputSizeDelegate(IntPtr cipher); + + [SafeMethodName("NCUtilCipherReadOutput")] + internal delegate NCResult NCUtilCipherReadOutputDelegate(IntPtr cipher, byte* outputData, uint outputLen); + + [SafeMethodName("NCUtilCipherSetProperty")] + internal delegate NCResult NCUtilCipherSetPropertyDelegate(IntPtr cipher, uint property, byte* value, uint valueLen); + + [SafeMethodName("NCUtilCipherUpdate")] + internal delegate NCResult NCUtilCipherUpdateDelegate(IntPtr cipher, IntPtr libContext, NCSecretKey* secKey, NCPublicKey* pubKey); + + [SafeMethodName("NCUtilCipherGetIvSize")] + internal delegate NCResult NCUtilCipherGetIvSizeDelegate(IntPtr cipher); + } +} 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"); + } + } +} |