aboutsummaryrefslogtreecommitdiff
path: root/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src
diff options
context:
space:
mode:
Diffstat (limited to 'wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src')
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Base64SignatureEncoder.cs31
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCCipherUtil.cs171
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherFlags.cs60
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherVersion.cs36
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptMessageCipher.cs256
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/HexSignatureEncoder.cs31
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrSignatureEncoder.cs33
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs87
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCKeyUtil.cs185
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCPublicKey.cs37
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs37
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs251
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/IRandomSource.cs32
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/NcFallbackRandom.cs36
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Random/UnmanagedRandomSource.cs103
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NCSignatureUtil.cs175
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Singatures/NoscryptSigner.cs167
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj40
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/FunctionTable.cs155
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCUtil.cs104
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");
+ }
+ }
+}