aboutsummaryrefslogtreecommitdiff
path: root/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-06-10 22:08:52 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-06-10 22:08:52 -0400
commit5e32450ccf9186e86a7596a7d774621cf81c62ff (patch)
treef30f3671357f8c47fba448347d30fdf436c18227 /wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src
parenta74f96251bcc81fb2c94fe75dd6f8043fd35fe0b (diff)
feat: Begin migrating noscrypt c# library
Diffstat (limited to 'wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src')
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IEncryptionVersion.cs36
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrCrypto.cs164
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IRandomSource.cs32
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/LibNoscrypt.cs202
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs88
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip04EncryptionVersion.cs37
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip44EncryptionVersion.cs37
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCPublicKey.cs31
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs31
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs194
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs41
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Message.cs36
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs144
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs237
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs287
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs234
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Taskfile.yaml70
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/UnmanagedRandomSource.cs104
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj28
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/FunctionTable.cs127
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCEncryptionArgs.cs31
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCMacVerifyArgs.cs34
22 files changed, 2225 insertions, 0 deletions
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IEncryptionVersion.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IEncryptionVersion.cs
new file mode 100644
index 0000000..e9aab2b
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IEncryptionVersion.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/>.
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ /// <summary>
+ /// Represents a message encryption version used by the Nostr protocol
+ /// </summary>
+ public interface IEncryptionVersion
+ {
+ /// <summary>
+ /// The noscrypt compatible encryption version
+ /// </summary>
+ internal uint Version { get; }
+
+ /// <summary>
+ /// Calculates the required buffer size for the specified data size
+ /// </summary>
+ /// <param name="dataSize">The size of the input data</param>
+ /// <returns>The estimated size of the buffer required to complete the opeation</returns>
+ internal int CalcBufferSize(int dataSize);
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrCrypto.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrCrypto.cs
new file mode 100644
index 0000000..49c0cc0
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrCrypto.cs
@@ -0,0 +1,164 @@
+// 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 interface INostrCrypto
+ {
+
+ /// <summary>
+ /// Gets a nostr public key from a secret key.
+ /// </summary>
+ /// <param name="secretKey">A reference to the secret key to get the public key from</param>
+ /// <param name="publicKey">A reference to the public key structure to write the recovered key to</param>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ void GetPublicKey(ref readonly NCSecretKey secretKey, ref NCPublicKey publicKey);
+
+ /// <summary>
+ /// Validates a secret key is in a valid format.
+ /// </summary>
+ /// <param name="secretKey">A readonly reference to key structure to validate</param>
+ /// <returns>True if the key is consiered valid against the secp256k1 curve</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ bool ValidateSecretKey(ref readonly NCSecretKey secretKey);
+
+ /// <summary>
+ /// Signs the supplied data with the secret key and random32 nonce, then writes
+ /// the message signature to the supplied sig64 buffer.
+ /// </summary>
+ /// <param name="secretKey">The secret key used to sign the message</param>
+ /// <param name="random32">A highly secure random nonce used to seed the signature</param>
+ /// <param name="data">A pointer to the first byte in the message to sign</param>
+ /// <param name="dataSize">The size of the message in bytes</param>
+ /// <param name="sig64">A pointer to the first byte of a 64 byte buffer used to write the message signature to</param>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ void SignData(
+ ref readonly NCSecretKey secretKey,
+ ref readonly byte random32,
+ ref readonly byte data,
+ uint dataSize,
+ ref byte sig64
+ );
+
+ /// <summary>
+ /// Performs cryptographic verification of the supplied data
+ /// against the supplied public key.
+ /// </summary>
+ /// <param name="pubKey">The signer's public key</param>
+ /// <param name="data">A pointer to the first byte in the message to sign</param>
+ /// <param name="dataSize">The number of bytes in the message</param>
+ /// <param name="sig64">A pointer to the signature buffer</param>
+ /// <returns>True if the signature could be verified against the public key. False otherwise</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ bool VerifyData(
+ ref readonly NCPublicKey pubKey,
+ ref readonly byte data,
+ uint dataSize,
+ ref readonly byte sig64
+ );
+
+ /// <summary>
+ /// Computes a nip44 message authentication code (MAC) using the supplied key and payload.
+ /// </summary>
+ /// <param name="hmacKey32">The key returned during a
+ /// <see cref="Encrypt(ref readonly NCSecretKey, ref readonly NCPublicKey, ref readonly byte, ref readonly byte, ref byte, uint, ref byte)"/>
+ /// </param>
+ /// <param name="payload">A pointer to a buffer </param>
+ /// <param name="payloadSize">The size of the buffer to compute the mac of, in bytes</param>
+ /// <param name="hmacOut32">A pointer to the 32byte buffer to write the mac to</param>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ void ComputeMac(
+ ref readonly byte hmacKey32,
+ ref readonly byte payload,
+ uint payloadSize,
+ ref byte hmacOut32
+ );
+
+ /// <summary>
+ /// Verifies a nip44 message authentication code (MAC) against the supplied key and payload.
+ /// </summary>
+ /// <param name="secretKey">A pointer to the receiver's secret key</param>
+ /// <param name="publicKey">A pointer to senders the public key</param>
+ /// <param name="nonce32">A pointer to the 32byte nonce buffer</param>
+ /// <param name="mac32">A pointer to the 32byte message buffer</param>
+ /// <param name="payload">A pointer to the message buffer</param>
+ /// <param name="payloadSize">The size in bytes of the payload buffer</param>
+ /// <returns>True if the message authentication code (MAC) matches, false otherwise </returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ bool VerifyMac(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte mac32,
+ ref readonly byte payload,
+ uint payloadSize
+ );
+
+ /// <summary>
+ /// Encrypts a message using the supplied secret key, public key, and nonce. When this function
+ /// returns, the cipherText buffer will contain the encrypted message, and the hmacKeyOut32 buffer
+ /// will contain the key used to compute the message authentication code (MAC).
+ /// <para>
+ /// NOTE: The cipherText buffer must be at least as large as the plaintext buffer. The
+ /// size parameter must be the size of the number of bytes to encrypt.
+ /// </para>
+ /// </summary>
+ /// <param name="secretKey">A pointer to the receiver's secret key</param>
+ /// <param name="publicKey">A pointer to senders the public key</param>
+ /// <param name="nonce32">A pointer to the 32byte nonce used for message encryption</param>
+ /// <param name="plainText">A pointer to the plaintext buffer to encrypt</param>
+ /// <param name="cipherText">A pointer to the cyphertext buffer to write encrypted data to (must be as large or larger than the plaintext buffer)</param>
+ /// <param name="size">The size of the data to encrypt</param>
+ /// <param name="hmacKeyOut32"></param>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ void EncryptNip44(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte plainText,
+ ref byte cipherText,
+ uint size,
+ ref byte hmacKeyOut32
+ );
+
+ /// <summary>
+ /// Decrypts a message using the supplied secret key, public key, and the original message
+ /// nonce.
+ /// </summary>
+ /// <param name="secretKey">A pointer to the receiver's secret key</param>
+ /// <param name="publicKey">A pointer to senders the public key</param>
+ /// <param name="nonce32">A pointer to the 32byte nonce used for message encryption</param>
+ /// <param name="plainText">A pointer to the plaintext buffer to write plaintext data to (must be as large or larger than the ciphertext buffer)</param>
+ /// <param name="cipherText">A pointer to the cyphertext buffer to read encrypted data from</param>
+ /// <param name="size">The size of the buffer to decrypt</param>
+ void DecryptNip44(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte cipherText,
+ ref byte plainText,
+ uint size
+ );
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IRandomSource.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IRandomSource.cs
new file mode 100644
index 0000000..5c5f2ac
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/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
+{
+ /// <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/LibNoscrypt.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/LibNoscrypt.cs
new file mode 100644
index 0000000..e2b3ebe
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/LibNoscrypt.cs
@@ -0,0 +1,202 @@
+// 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;
+using VNLib.Utils.Extensions;
+using VNLib.Utils.Memory;
+using VNLib.Utils.Native;
+
+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 LibNoscrypt(SafeLibraryHandle Library, bool OwnsHandle) : VnDisposeable
+ {
+ //Values that match the noscrypt.h header
+ public const int NC_SEC_KEY_SIZE = 32;
+ public const int NC_SEC_PUBKEY_SIZE = 32;
+ public const int NC_ENCRYPTION_NONCE_SIZE = 32;
+ public const int NC_PUBKEY_SIZE = 32;
+ public const int NC_SIGNATURE_SIZE = 64;
+ public const int NC_CONV_KEY_SIZE = 32;
+ public const int NC_MESSAGE_KEY_SIZE = 32;
+ public const int NC_HMAC_KEY_SIZE = 32;
+ public const int NC_ENCRYPTION_MAC_SIZE = 32;
+ public const int NC_CONVERSATION_KEY_SIZE = 32;
+ public const int CTX_ENTROPY_SIZE = 32;
+
+ public const uint NC_ENC_VERSION_NIP04 = 0x00000004u;
+ public const uint NC_ENC_VERSION_NIP44 = 0x00000002c;
+
+ public const NCResult NC_SUCCESS = 0;
+ public const byte E_NULL_PTR = 0x01;
+ public const byte E_INVALID_ARG = 0x02;
+ public const byte E_INVALID_CTX = 0x03;
+ public const byte E_ARGUMENT_OUT_OF_RANGE = 0x04;
+ public const byte E_OPERATION_FAILED = 0x05;
+ public const byte E_VERSION_NOT_SUPPORTED = 0x06;
+
+ 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, 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>
+ /// Initializes a new NostrCrypto context wraper directly that owns the internal context.
+ /// This may be done once at app startup and is thread-safe for the rest of the
+ /// application lifetime.
+ /// </summary>
+ /// <param name="library"></param>
+ /// <param name="heap">The heap to allocate the context from</param>
+ /// <param name="entropy32">The random entropy data to initialize the context with</param>
+ /// <returns>The library wrapper handle</returns>
+ public NostrCrypto InitializeCrypto(IUnmangedHeap heap, ReadOnlySpan<byte> entropy32)
+ {
+ ArgumentNullException.ThrowIfNull(heap);
+
+ //Create the crypto interface from the new context object
+ return new NostrCrypto(
+ context: Initialize(heap, entropy32),
+ ownsContext: true
+ );
+ }
+
+ ///<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>
+ public static LibNoscrypt 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 LibNoscrypt(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>
+ public static LibNoscrypt Load(string path) => Load(path, DllImportSearchPath.SafeDirectories);
+
+ }
+}
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..dbd9372
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs
@@ -0,0 +1,88 @@
+// 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 static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+using NCResult = System.Int64;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ /// <summary>
+ /// Represents a context for the native library
+ /// </summary>
+ /// <param name="Heap">The heap the handle was allocated from</param>
+ /// <param name="Library">A reference to the native library</param>
+ public sealed class NCContext : SafeHandleZeroOrMinusOneIsInvalid
+ {
+ private readonly IUnmangedHeap Heap;
+
+ /// <summary>
+ /// The library this context was created from
+ /// </summary>
+ public LibNoscrypt Library { get; }
+
+ internal NCContext(IntPtr handle, IUnmangedHeap heap, LibNoscrypt 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, 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/NCNip04EncryptionVersion.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip04EncryptionVersion.cs
new file mode 100644
index 0000000..69234fe
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip04EncryptionVersion.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 static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ /// <summary>
+ /// The NIP04 encryption version used by the Nostr protocol
+ /// </summary>
+ public sealed class NCNip04EncryptionVersion : IEncryptionVersion
+ {
+ /// <summary>
+ /// A static nip04 encryption version instance
+ /// </summary>
+ public static readonly NCNip04EncryptionVersion Instance = new();
+
+ ///<inheritdoc/>
+ uint IEncryptionVersion.Version => NC_ENC_VERSION_NIP04;
+
+ ///<inheritdoc/>
+ int IEncryptionVersion.CalcBufferSize(int dataSize) => Nip04Util.CalcBufferSize(dataSize);
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip44EncryptionVersion.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip44EncryptionVersion.cs
new file mode 100644
index 0000000..e572dd7
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip44EncryptionVersion.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 static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ /// <summary>
+ /// The NIP44 encryption version used by the Nostr protocol
+ /// </summary>
+ public sealed class NCNip44EncryptionVersion : IEncryptionVersion
+ {
+ /// <summary>
+ /// A static nip44 encryption version instance
+ /// </summary>
+ public static readonly NCNip44EncryptionVersion Instance = new();
+
+ ///<inheritdoc/>
+ uint IEncryptionVersion.Version => NC_ENC_VERSION_NIP44;
+
+ ///<inheritdoc/>
+ int IEncryptionVersion.CalcBufferSize(int dataSize) => Nip44Util.CalcBufferSize(dataSize);
+ }
+
+}
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..0699d7d
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCPublicKey.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;
+using System.Runtime.InteropServices;
+
+using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+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
+ {
+ 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..b586d26
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.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.Runtime.InteropServices;
+
+using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+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
+ {
+ private fixed byte key[NC_SEC_KEY_SIZE];
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs
new file mode 100644
index 0000000..e212125
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs
@@ -0,0 +1,194 @@
+// 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 System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+using NCResult = System.Int64;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+
+ public static class NCUtil
+ {
+ /// <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);
+ }
+
+ 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
+ byte errorCode = (byte)(asPositive & 0xFF);
+ byte argNumber = (byte)((asPositive >> 8) & 0xFF);
+
+ switch (errorCode)
+ {
+ case E_NULL_PTR:
+ RaiseNullArgExceptionForArgumentNumber<T>(argNumber);
+ break;
+ case E_INVALID_ARG:
+ RaiseArgExceptionForArgumentNumber<T>(argNumber);
+ break;
+ case E_ARGUMENT_OUT_OF_RANGE:
+ RaiseOORExceptionForArgumentNumber<T>(argNumber);
+ break;
+ case E_INVALID_CTX:
+ throw new InvalidOperationException("The library context object is null or invalid");
+ case E_OPERATION_FAILED:
+ RaiseOperationFailedException(raiseOnFailure);
+ break;
+ case E_VERSION_NOT_SUPPORTED:
+ throw new NotSupportedException("The requested version is not supported");
+
+ default:
+ if(raiseOnFailure)
+ {
+ throw new InvalidOperationException($"The operation failed for an unknown reason, code: {errorCode: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");
+ }
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs
new file mode 100644
index 0000000..82704db
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs
@@ -0,0 +1,41 @@
+// 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.Extensions;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ public static class Nip04Util
+ {
+ public static bool IsValidPayload(ReadOnlySpan<char> payload)
+ {
+ /* Iv is base64 encoded so it should be 33% larger than 16 byte iv */
+ ReadOnlySpan<char> iv = payload.SliceAfterParam("?iv=");
+ return iv.Length > 20 && iv.Length <= 26;
+ }
+
+ public static ReadOnlySpan<char> GetIV(ReadOnlySpan<char> payload) => payload.SliceAfterParam("?iv=");
+
+ public static ReadOnlySpan<char> GetCipherText(ReadOnlySpan<char> payload) => payload.SliceBeforeParam("?iv=");
+
+ public static int CalcBufferSize(int dataSize)
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Message.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Message.cs
new file mode 100644
index 0000000..b78530b
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Message.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;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ public readonly ref struct Nip44Message(ReadOnlySpan<byte> payload)
+ {
+ readonly ReadOnlySpan<byte> _payload = payload;
+
+ public ReadOnlySpan<byte> Payload => _payload;
+
+ public ReadOnlySpan<byte> Nonce => Nip44Util.GetNonceFromPayload(_payload);
+
+ public ReadOnlySpan<byte> Ciphertext => Nip44Util.GetCiphertextFromPayload(_payload);
+
+ public ReadOnlySpan<byte> Mac => Nip44Util.GetMacFromPayload(_payload);
+
+ public ReadOnlySpan<byte> NonceAndCiphertext => Nip44Util.GetNonceAndCiphertext(_payload);
+
+ public byte Version => Nip44Util.GetMessageVersion(_payload);
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs
new file mode 100644
index 0000000..23c9127
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs
@@ -0,0 +1,144 @@
+// 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.Buffers.Binary;
+using System.Runtime.InteropServices;
+
+using VNLib.Utils.Memory;
+
+using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+
+ /// <summary>
+ /// Provides a set of utility methods for working with the Noscrypt library
+ /// </summary>
+ public static class Nip44Util
+ {
+ /// <summary>
+ /// Calculates the required NIP44 encryption buffer size for
+ /// the specified input data size
+ /// </summary>
+ /// <param name="dataSize">The size (in bytes) of the encoded data to encrypt</param>
+ /// <returns>The exact size of the padded buffer output</returns>
+ public static int CalcBufferSize(int dataSize)
+ {
+ /*
+ * Taken from https://github.com/nostr-protocol/nips/blob/master/44.md
+ *
+ * Not gonna lie, kinda dumb branches. I guess they want to save space
+ * with really tiny messages... Dunno, but whatever RTFM
+ */
+
+ //Min message size is 32 bytes
+ int minSize = Math.Max(dataSize, 32);
+
+ //find the next power of 2 that will fit the min size
+ int nexPower = 1 << ((int)Math.Log2(minSize - 1)) + 1;
+
+ int chunk = nexPower <= 256 ? 32 : nexPower / 8;
+
+ return (chunk * ((int)Math.Floor((double)((minSize - 1) / chunk)) + 1)) + sizeof(ushort);
+ }
+
+ /// <summary>
+ /// Calculates the final buffer size required to hold the encrypted data
+ /// </summary>
+ /// <param name="dataSize">The size (in bytes) of plaintext data to encrypt</param>
+ /// <returns>The number of bytes required to store the final nip44 message</returns>
+ public static int CalcFinalBufferSize(int dataSize)
+ {
+ /* version + nonce + payload + mac */
+ return CalcBufferSize(dataSize) + NC_ENCRYPTION_NONCE_SIZE + NC_ENCRYPTION_MAC_SIZE + 1;
+ }
+
+ /// <summary>
+ /// Formats the plaintext data into a buffer that can be properly encrypted.
+ /// The output buffer must be zeroed, or can be zeroed using the
+ /// <paramref name="zeroOutput"/> parameter. Use <see cref="CalcBufferSize(uint)"/>
+ /// to determine the required output buffer size.
+ /// </summary>
+ /// <param name="plaintextData">A buffer containing plaintext data to copy to the output</param>
+ /// <param name="output">The output data buffer to format</param>
+ /// <param name="zeroOutput">A value that indicates if the buffer should be zeroed before use</param>
+ public static void FormatBuffer(ReadOnlySpan<byte> plaintextData, Span<byte> output, bool zeroOutput)
+ {
+ //First zero out the buffer
+ if (zeroOutput)
+ {
+ MemoryUtil.InitializeBlock(output);
+ }
+
+ //Make sure the output buffer is large enough so we dont overrun it
+ ArgumentOutOfRangeException.ThrowIfLessThan(output.Length, plaintextData.Length + sizeof(ushort), nameof(output));
+
+ //Write the data size to the first 2 bytes
+ ushort dataSize = (ushort)plaintextData.Length;
+ BinaryPrimitives.WriteUInt16BigEndian(output, dataSize);
+
+ //Copy the plaintext data to the output buffer after the data size
+ MemoryUtil.Memmove(
+ in MemoryMarshal.GetReference(plaintextData),
+ 0,
+ ref MemoryMarshal.GetReference(output),
+ sizeof(ushort),
+ (uint)plaintextData.Length
+ );
+
+ //We assume the remaining buffer is zeroed out
+ }
+
+ public static ReadOnlySpan<byte> GetNonceFromPayload(ReadOnlySpan<byte> message)
+ {
+ //The nonce is 32 bytes following the 1st byte version number of the message
+ return message.Slice(1, NC_ENCRYPTION_NONCE_SIZE);
+ }
+
+ public static ReadOnlySpan<byte> GetCiphertextFromPayload(ReadOnlySpan<byte> message)
+ {
+ //Message is between the nonce and the trailing mac
+ int payloadSize = message.Length - (1 + NC_ENCRYPTION_NONCE_SIZE + NC_ENCRYPTION_MAC_SIZE);
+ return message.Slice(1 + NC_ENCRYPTION_NONCE_SIZE, payloadSize);
+ }
+
+ public static ReadOnlySpan<byte> GetMacFromPayload(ReadOnlySpan<byte> message)
+ {
+ //The mac is the last 32 bytes of the message
+ return message[^NC_ENCRYPTION_MAC_SIZE..];
+ }
+
+ public static ReadOnlySpan<byte> GetNonceAndCiphertext(ReadOnlySpan<byte> message)
+ {
+ //The nonce is 32 bytes following the 1st byte version number of the message
+ return message.Slice(1, NC_ENCRYPTION_NONCE_SIZE + GetCiphertextFromPayload(message).Length);
+ }
+
+ public static byte GetMessageVersion(ReadOnlySpan<byte> message)
+ {
+ //The first byte is the message version
+ return message[0];
+ }
+
+ public static ReadOnlySpan<byte> GetPlaintextMessage(ReadOnlySpan<byte> plaintextPayload)
+ {
+ ushort ptLength = BinaryPrimitives.ReadUInt16BigEndian(plaintextPayload);
+ return plaintextPayload.Slice(sizeof(ushort), ptLength);
+ }
+
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs
new file mode 100644
index 0000000..aaabc08
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs
@@ -0,0 +1,237 @@
+// 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 static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+
+ public static class NoscryptExtensions
+ {
+ public static void EncryptNip44(
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ReadOnlySpan<byte> nonce32,
+ ReadOnlySpan<byte> plainText,
+ Span<byte> hmackKeyOut32,
+ Span<byte> cipherText
+ )
+ {
+ ArgumentNullException.ThrowIfNull(lib);
+
+ //Chacha requires the output buffer to be at-least the size of the input buffer
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(plainText.Length, cipherText.Length, nameof(plainText));
+
+ //Nonce must be exactly 32 bytes
+ ArgumentOutOfRangeException.ThrowIfNotEqual(nonce32.Length, NC_ENCRYPTION_NONCE_SIZE, nameof(nonce32));
+
+ ArgumentOutOfRangeException.ThrowIfNotEqual(hmackKeyOut32.Length, NC_HMAC_KEY_SIZE, nameof(hmackKeyOut32));
+
+ //Encrypt data, use the plaintext buffer size as the data size
+ lib.EncryptNip44(
+ secretKey: in secretKey,
+ publicKey: in publicKey,
+ nonce32: in MemoryMarshal.GetReference(nonce32),
+ plainText: in MemoryMarshal.GetReference(plainText),
+ cipherText: ref MemoryMarshal.GetReference(cipherText),
+ size: (uint)plainText.Length,
+ hmacKeyOut32: ref MemoryMarshal.GetReference(hmackKeyOut32)
+ );
+ }
+
+ public static unsafe void EncryptNip44(
+ this INostrCrypto lib,
+ ref NCSecretKey secretKey,
+ ref NCPublicKey publicKey,
+ void* nonce32,
+ void* hmacKeyOut32,
+ void* plainText,
+ void* cipherText,
+ uint size
+ )
+ {
+ ArgumentNullException.ThrowIfNull(plainText);
+ ArgumentNullException.ThrowIfNull(cipherText);
+ ArgumentNullException.ThrowIfNull(nonce32);
+
+ //Spans are easer to forward references from pointers without screwing up arguments
+ EncryptNip44(
+ lib: lib,
+ secretKey: in secretKey,
+ publicKey: in publicKey,
+ nonce32: new ReadOnlySpan<byte>(nonce32, NC_ENCRYPTION_NONCE_SIZE),
+ plainText: new ReadOnlySpan<byte>(plainText, (int)size),
+ hmackKeyOut32: new Span<byte>(hmacKeyOut32, NC_HMAC_KEY_SIZE),
+ cipherText: new Span<byte>(cipherText, (int)size)
+ );
+ }
+
+
+ public static void DecryptNip44(
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ReadOnlySpan<byte> nonce32,
+ ReadOnlySpan<byte> cipherText,
+ Span<byte> plainText
+ )
+ {
+ ArgumentNullException.ThrowIfNull(lib);
+
+ //Chacha requires the output buffer to be at-least the size of the input buffer
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(cipherText.Length, plainText.Length, nameof(cipherText));
+
+ //Nonce must be exactly 32 bytes
+ ArgumentOutOfRangeException.ThrowIfNotEqual(nonce32.Length, 32, nameof(nonce32));
+
+ //Decrypt data, use the ciphertext buffer size as the data size
+ lib.DecryptNip44(
+ secretKey: in secretKey,
+ publicKey: in publicKey,
+ nonce32: in MemoryMarshal.GetReference(nonce32),
+ cipherText: in MemoryMarshal.GetReference(cipherText),
+ plainText: ref MemoryMarshal.GetReference(plainText),
+ size: (uint)cipherText.Length
+ );
+ }
+
+ public static unsafe void DecryptNip44(
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ void* nonce32,
+ void* cipherText,
+ void* plainText,
+ uint size
+ )
+ {
+ ArgumentNullException.ThrowIfNull(nonce32);
+ ArgumentNullException.ThrowIfNull(cipherText);
+ ArgumentNullException.ThrowIfNull(plainText);
+
+ //Spans are easer to forward references from pointers without screwing up arguments
+ DecryptNip44(
+ lib: lib,
+ secretKey: in secretKey,
+ publicKey: in publicKey,
+ nonce32: new Span<byte>(nonce32, NC_ENCRYPTION_NONCE_SIZE),
+ cipherText: new Span<byte>(cipherText, (int)size),
+ plainText: new Span<byte>(plainText, (int)size)
+ );
+ }
+
+ public static bool VerifyMac(
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ReadOnlySpan<byte> nonce32,
+ ReadOnlySpan<byte> mac32,
+ ReadOnlySpan<byte> payload
+ )
+ {
+ ArgumentNullException.ThrowIfNull(lib);
+ ArgumentOutOfRangeException.ThrowIfZero(payload.Length, nameof(payload));
+ ArgumentOutOfRangeException.ThrowIfNotEqual(nonce32.Length, NC_ENCRYPTION_NONCE_SIZE, nameof(nonce32));
+ ArgumentOutOfRangeException.ThrowIfNotEqual(mac32.Length, NC_ENCRYPTION_MAC_SIZE, nameof(mac32));
+
+ //Verify the HMAC
+ return lib.VerifyMac(
+ secretKey: in secretKey,
+ publicKey: in publicKey,
+ nonce32: in MemoryMarshal.GetReference(nonce32),
+ mac32: in MemoryMarshal.GetReference(mac32),
+ payload: in MemoryMarshal.GetReference(payload),
+ payloadSize: (uint)payload.Length
+ );
+ }
+
+ public static unsafe bool VerifyMac(
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ void* nonce32,
+ void* mac32,
+ void* payload,
+ uint payloadSize
+ )
+ {
+ ArgumentNullException.ThrowIfNull(nonce32);
+ ArgumentNullException.ThrowIfNull(mac32);
+ ArgumentNullException.ThrowIfNull(payload);
+
+ //Spans are easer to forward references from pointers without screwing up arguments
+ return VerifyMac(
+ lib: lib,
+ secretKey: in secretKey,
+ publicKey: in publicKey,
+ nonce32: new Span<byte>(nonce32, NC_ENCRYPTION_NONCE_SIZE),
+ mac32: new Span<byte>(mac32, NC_ENCRYPTION_MAC_SIZE),
+ payload: new Span<byte>(payload, (int)payloadSize)
+ );
+ }
+
+ public static void SignData(
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secKey,
+ ReadOnlySpan<byte> random32,
+ ReadOnlySpan<byte> data,
+ Span<byte> signatureBuffer
+ )
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(signatureBuffer.Length, NC_SIGNATURE_SIZE, nameof(signatureBuffer));
+ ArgumentOutOfRangeException.ThrowIfLessThan(random32.Length, 32, nameof(random32));
+ ArgumentOutOfRangeException.ThrowIfZero(data.Length, nameof(data));
+
+ lib.SignData(
+ secretKey: in secKey,
+ random32: in MemoryMarshal.GetReference(random32),
+ data: in MemoryMarshal.GetReference(data),
+ dataSize: (uint)data.Length,
+ sig64: ref MemoryMarshal.GetReference(signatureBuffer)
+ );
+ }
+
+#if DEBUG
+ /*
+ * Conversation key is not meant to be a public api. Callers
+ * should use Encrypt/Decrypt methods to handle encryption.
+ *
+ * This method exists for vector testing purposes only.
+ */
+ public static void GetConverstationKey(
+ this NostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ Span<byte> conversationKeyOut32
+ )
+ {
+ ArgumentNullException.ThrowIfNull(lib);
+ ArgumentOutOfRangeException.ThrowIfNotEqual(conversationKeyOut32.Length, NC_CONVERSATION_KEY_SIZE, nameof(conversationKeyOut32));
+
+ //Get the conversation key
+ lib.GetConverstationKey(
+ secretKey: in secretKey,
+ publicKey: in publicKey,
+ key32: ref MemoryMarshal.GetReference(conversationKeyOut32)
+ );
+
+ }
+#endif
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs
new file mode 100644
index 0000000..c4bef05
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs
@@ -0,0 +1,287 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using System;
+using System.Runtime.CompilerServices;
+using System.Diagnostics.CodeAnalysis;
+
+using VNLib.Utils.Cryptography.Noscrypt.@internal;
+using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+using NCResult = System.Int64;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+
+ /// <summary>
+ /// A default implementation of the <see cref="INostrCrypto"/> interface
+ /// </summary>
+ /// <param name="context">The initialized library context</param>
+ public unsafe class NostrCrypto(NCContext context, bool ownsContext) : VnDisposeable, INostrCrypto
+ {
+ /// <summary>
+ /// Gets the underlying library context.
+ /// </summary>
+ public NCContext Context => context;
+
+ private ref readonly FunctionTable Functions => ref context.Library.Functions;
+
+ ///<inheritdoc/>
+ public void DecryptNip44(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte cipherText,
+ ref byte plainText,
+ uint size
+ )
+ {
+ Check();
+
+ ThrowIfNullRef(in nonce32, nameof(nonce32));
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText, pNonce = &nonce32)
+ {
+ NCEncryptionArgs data = new()
+ {
+ //Set input data to the cipher text to decrypt and the output data to the plaintext buffer
+ dataSize = size,
+ hmacKeyOut32 = null,
+ inputData = pCipherText,
+ outputData = pTextPtr,
+ nonce32 = pNonce,
+ version = NC_ENC_VERSION_NIP44
+ };
+
+ NCResult result = Functions.NCDecrypt.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, &data);
+ NCUtil.CheckResult<FunctionTable.NCDecryptDelegate>(result, true);
+ }
+ }
+
+ ///<inheritdoc/>
+ public void EncryptNip44(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte plainText,
+ ref byte cipherText,
+ uint size,
+ ref byte hmackKeyOut32
+ )
+ {
+ Check();
+
+ ThrowIfNullRef(in nonce32, nameof(nonce32));
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText, pHmacKeyOut = &hmackKeyOut32, pNonce = &nonce32)
+ {
+ NCEncryptionArgs data = new()
+ {
+ nonce32 = pNonce,
+ hmacKeyOut32 = pHmacKeyOut,
+ //Set input data to the plaintext to encrypt and the output data to the cipher text buffer
+ inputData = pTextPtr,
+ outputData = pCipherText,
+ dataSize = size,
+ version = NC_ENC_VERSION_NIP44 //Force nip44 encryption
+ };
+
+ NCResult result = Functions.NCEncrypt.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, &data);
+ NCUtil.CheckResult<FunctionTable.NCEncryptDelegate>(result, true);
+ }
+ }
+
+ ///<inheritdoc/>
+ public void GetPublicKey(ref readonly NCSecretKey secretKey, ref NCPublicKey publicKey)
+ {
+ Check();
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ {
+ NCResult result = Functions.NCGetPublicKey.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey);
+ NCUtil.CheckResult<FunctionTable.NCGetPublicKeyDelegate>(result, true);
+ }
+ }
+
+ ///<inheritdoc/>
+ public void SignData(
+ ref readonly NCSecretKey secretKey,
+ ref readonly byte random32,
+ ref readonly byte data,
+ uint dataSize,
+ ref byte sig64
+ )
+ {
+ Check();
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (byte* pData = &data, pSig = &sig64, pRandom = &random32)
+ {
+ NCResult result = Functions.NCSignData.Invoke(
+ ctx: context.DangerousGetHandle(),
+ sk: pSecKey,
+ random32: pRandom,
+ data: pData,
+ dataSize,
+ sig64: pSig
+ );
+
+ NCUtil.CheckResult<FunctionTable.NCSignDataDelegate>(result, true);
+ }
+ }
+
+ ///<inheritdoc/>
+ public bool ValidateSecretKey(ref readonly NCSecretKey secretKey)
+ {
+ Check();
+
+ IntPtr libCtx = context.DangerousGetHandle();
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ {
+ /*
+ * Validate should return a result of 1 if the secret key is valid
+ * or a 0 if it is not.
+ */
+ NCResult result = Functions.NCValidateSecretKey.Invoke(libCtx, pSecKey);
+ NCUtil.CheckResult<FunctionTable.NCValidateSecretKeyDelegate>(result, false);
+
+ return result == NC_SUCCESS;
+ }
+ }
+
+ ///<inheritdoc/>
+ public bool VerifyData(
+ ref readonly NCPublicKey pubKey,
+ ref readonly byte data,
+ uint dataSize,
+ ref readonly byte sig64
+ )
+ {
+ Check();
+
+ fixed(NCPublicKey* pPubKey = &pubKey)
+ fixed (byte* pData = &data, pSig = &sig64)
+ {
+ NCResult result = Functions.NCVerifyData.Invoke(context.DangerousGetHandle(), pPubKey, pData, dataSize, pSig);
+ NCUtil.CheckResult<FunctionTable.NCVerifyDataDelegate>(result, false);
+
+ return result == NC_SUCCESS;
+ }
+ }
+
+ ///<inheritdoc/>
+ public bool VerifyMac(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte mac32,
+ ref readonly byte payload,
+ uint payloadSize
+ )
+ {
+ Check();
+
+ //Check pointers we need to use
+ ThrowIfNullRef(in nonce32, nameof(nonce32));
+ ThrowIfNullRef(in mac32, nameof(mac32));
+ ThrowIfNullRef(in payload, nameof(payload));
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pPayload = &payload, pMac = &mac32, pNonce = &nonce32)
+ {
+
+ NCMacVerifyArgs args = new()
+ {
+ payloadSize = payloadSize,
+ payload = pPayload,
+ mac32 = pMac,
+ nonce32 = pNonce
+ };
+
+ //Exec and bypass failure
+ NCResult result = Functions.NCVerifyMac.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, &args);
+ NCUtil.CheckResult<FunctionTable.NCVerifyMacDelegate>(result, false);
+
+ //Result should be success if the hmac is valid
+ return result == NC_SUCCESS;
+ }
+ }
+
+ public void ComputeMac(ref readonly byte hmacKey32, ref readonly byte payload, uint payloadSize, ref byte hmacOut32)
+ {
+ Check();
+
+ //Library will check for null pointers, since they are all arguments
+ fixed (byte* pKey = &hmacKey32, pPayload = &payload, pOut = &hmacOut32)
+ {
+ NCResult result = Functions.NCComputeMac.Invoke(context.DangerousGetHandle(), pKey, pPayload, payloadSize, pOut);
+ NCUtil.CheckResult<FunctionTable.NCComputeMacDelegate>(result, true);
+ }
+ }
+
+#if DEBUG
+
+ /// <summary>
+ /// DEBUG ONLY: Gets the conversation key for the supplied secret key and public key
+ /// </summary>
+ /// <param name="secretKey">The sender's private key</param>
+ /// <param name="publicKey">The receiver's public key</param>
+ /// <param name="key32">A pointer to the 32byte buffer to write the conversation key to</param>
+ public void GetConverstationKey(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref byte key32
+ )
+ {
+ Check();
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pKey = &key32)
+ {
+ NCResult result = Functions.NCGetConversationKey.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, pKey);
+ NCUtil.CheckResult<FunctionTable.NCGetConversationKeyDelegate>(result, true);
+ }
+ }
+
+#endif
+
+ ///<inheritdoc/>
+ protected override void Free()
+ {
+ if(ownsContext)
+ {
+ context.Dispose();
+ }
+ }
+
+ private static void ThrowIfNullRef([DoesNotReturnIf(false)] ref readonly byte value, string name)
+ {
+ if(Unsafe.IsNullRef(in value))
+ {
+ throw new ArgumentNullException(name);
+ }
+ }
+
+
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs
new file mode 100644
index 0000000..c70839c
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs
@@ -0,0 +1,234 @@
+// 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.CompilerServices;
+using System.Runtime.InteropServices;
+
+using VNLib.Utils.Extensions;
+using VNLib.Utils.Memory;
+
+using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+
+ public sealed class NostrEncryptedMessage(IEncryptionVersion version, INostrCrypto lib) : VnDisposeable
+ {
+ private readonly INostrCrypto library = lib;
+
+ private NCSecretKey _fromKey;
+ private NCPublicKey _toKey;
+ private Buffer32 _nonce32;
+
+ /// <summary>
+ /// The message encryption version used by this instance
+ /// </summary>
+ public uint Version { get; } = version.Version;
+
+ /// <summary>
+ /// The message nonce created during encryption event
+ /// </summary>
+ public unsafe Span<byte> Nonce
+ {
+ get
+ {
+ Debug.Assert(NC_ENCRYPTION_NONCE_SIZE == sizeof(Buffer32));
+ return MemoryMarshal.CreateSpan(ref GetNonceRef(), sizeof(Buffer32));
+ }
+ }
+
+ /// <summary>
+ /// Gets the size of the buffer required to encrypt the specified data size
+ /// </summary>
+ /// <param name="dataSize">The size of the message raw plaintext message to send</param>
+ /// <returns>The minimum number of bytes required for message encryption output</returns>
+ /// <exception cref="NotSupportedException"></exception>
+ public int GetOutputBufferSize(int dataSize)
+ => version.CalcBufferSize(dataSize);
+
+ /// <summary>
+ /// Sets the encryption secret key for the message
+ /// </summary>
+ /// <param name="secKey">The secret key buffer</param>
+ /// <returns>The current instance for chaining</returns>
+ /// <exception cref="ArgumentException"></exception>
+ public NostrEncryptedMessage SetSecretKey(ReadOnlySpan<byte> secKey)
+ => SetSecretKey(in NCUtil.AsSecretKey(secKey));
+
+ /// <summary>
+ /// Sets the encryption secret key for the message
+ /// </summary>
+ /// <param name="secKey">The secret key structure to copy</param>
+ /// <returns>The current instance for chaining</returns>
+ /// <exception cref="ArgumentException"></exception>
+ public NostrEncryptedMessage SetSecretKey(ref readonly NCSecretKey secKey)
+ {
+ MemoryUtil.CloneStruct(in secKey, ref _fromKey);
+ return this;
+ }
+
+ /// <summary>
+ /// Assigns the public key used to encrypt the message as the
+ /// receiver of the message
+ /// </summary>
+ /// <param name="pubKey">The user's public key receiving the message</param>
+ /// <returns>The current instance for chaining</returns>
+ /// <exception cref="ArgumentException"></exception>
+ public NostrEncryptedMessage SetPublicKey(ReadOnlySpan<byte> pubKey)
+ => SetPublicKey(in NCUtil.AsPublicKey(pubKey));
+
+ /// <summary>
+ /// Assigns the public key used to encrypt the message as the
+ /// receiver of the message
+ /// </summary>
+ /// <param name="pubKey">The user's public key receiving the message</param>
+ /// <returns>The current instance for chaining</returns>
+ /// <exception cref="ArgumentException"></exception>
+ public NostrEncryptedMessage SetPublicKey(ref readonly NCPublicKey pubKey)
+ {
+ MemoryUtil.CloneStruct(in pubKey, ref _toKey);
+ return this;
+ }
+
+ /// <summary>
+ /// Assigns the nonce to the message. Must be <see cref="NC_ENCRYPTION_NONCE_SIZE"/>
+ /// in length
+ /// </summary>
+ /// <param name="nonce">The nonce value to copy</param>
+ /// <returns>The current instance for chaining</returns>
+ /// <exception cref="ArgumentException"></exception>
+ public NostrEncryptedMessage SetNonce(ReadOnlySpan<byte> nonce)
+ {
+ MemoryUtil.CopyStruct(nonce, ref _nonce32);
+ return this;
+ }
+
+ /// <summary>
+ /// Assigns a random nonce using the specified random source
+ /// </summary>
+ /// <param name="rng">The random source to genrate a random nonce from</param>
+ /// <returns>The current instance for chaining</returns>
+ public NostrEncryptedMessage SetRandomNonce(IRandomSource rng)
+ {
+ rng.GetRandomBytes(Nonce);
+ return this;
+ }
+
+ /// <summary>
+ /// Encrypts the plaintext message and writes the encrypted message to the
+ /// specified buffer, along with a 32 byte mac of the message
+ /// </summary>
+ /// <param name="plaintext">The plaintext data to encrypt</param>
+ /// <param name="message">The message output buffer to write encrypted data to</param>
+ /// <param name="macOut32">A buffer to write the computed message mac to</param>
+ /// <returns>The number of bytes writtn to the message output buffer</returns>
+ /// <remarks>
+ /// The message buffer must be at-least the size of the output buffer, and it is not
+ /// initialized before the encryption operation.
+ /// </remarks>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public int EncryptMessage(ReadOnlySpan<byte> plaintext, Span<byte> message, Span<byte> macOut32)
+ {
+ return Version switch
+ {
+ NC_ENC_VERSION_NIP44 => EncryptNip44(plaintext, message, macOut32),
+ _ => throw new NotSupportedException("NIP04 encryption is not supported"),
+ };
+ }
+
+ private int EncryptNip44(ReadOnlySpan<byte> plaintext, Span<byte> message, Span<byte> macOut32)
+ {
+ int payloadSize = GetOutputBufferSize(plaintext.Length);
+
+ ArgumentOutOfRangeException.ThrowIfZero(plaintext.Length, nameof(plaintext));
+ ArgumentOutOfRangeException.ThrowIfZero(message.Length, nameof(message));
+ ArgumentOutOfRangeException.ThrowIfLessThan(message.Length, payloadSize, nameof(message));
+ ArgumentOutOfRangeException.ThrowIfLessThan(macOut32.Length, NC_ENCRYPTION_MAC_SIZE, nameof(macOut32));
+
+ /*
+ * Alloc temp buffer to copy formatted payload to data to for the encryption
+ * operation. Encryption will write directly to the message buffer
+ */
+
+ using UnsafeMemoryHandle<byte> ptPayloadBuf = MemoryUtil.UnsafeAllocNearestPage<byte>(payloadSize, true);
+ using UnsafeMemoryHandle<byte> hmacKeyBuf = MemoryUtil.UnsafeAlloc<byte>(NC_HMAC_KEY_SIZE, true);
+ Debug.Assert(hmacKeyBuf.Length == NC_HMAC_KEY_SIZE);
+
+ Nip44Util.FormatBuffer(plaintext, ptPayloadBuf.Span, false);
+
+ library.EncryptNip44(
+ in _fromKey,
+ in _toKey,
+ in GetNonceRef(),
+ in ptPayloadBuf.GetReference(),
+ ref MemoryMarshal.GetReference(message),
+ (uint)payloadSize, //IMPORTANT: Format buffer will pad the buffer to the exact size
+ ref hmacKeyBuf.GetReference() //Must set the hmac key buffer
+ );
+
+ //Safe to clear the plain text copy buffer
+ MemoryUtil.InitializeBlock(
+ ref ptPayloadBuf.GetReference(),
+ ptPayloadBuf.GetIntLength()
+ );
+
+
+ //Compute message mac, key should be set by the encryption operation
+ library.ComputeMac(
+ in hmacKeyBuf.GetReference(),
+ in MemoryMarshal.GetReference(message),
+ (uint)payloadSize, //Again set exact playload size
+ ref MemoryMarshal.GetReference(macOut32)
+ );
+
+ //Safe to clear the hmac key buffer
+ MemoryUtil.InitializeBlock(
+ ref hmacKeyBuf.GetReference(),
+ hmacKeyBuf.GetIntLength()
+ );
+
+ return payloadSize;
+ }
+
+ private ref byte GetNonceRef() => ref Unsafe.As<Buffer32, byte>(ref _nonce32);
+
+ protected override void Free()
+ {
+ //Zero all internal memory
+ MemoryUtil.ZeroStruct(ref _fromKey);
+ MemoryUtil.ZeroStruct(ref _toKey);
+ MemoryUtil.ZeroStruct(ref _nonce32);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="NostrEncryptedMessage"/> with the nip44 encryption
+ /// method.
+ /// </summary>
+ /// <param name="lib">The nostr crypto implementation instance to use</param>
+ /// <returns>The intialzied message instance</returns>
+ public static NostrEncryptedMessage CreateNip44Cipher(INostrCrypto lib)
+ => new(NCNip44EncryptionVersion.Instance, lib);
+
+
+ [StructLayout(LayoutKind.Sequential, Size = 32)]
+ unsafe struct Buffer32
+ {
+ fixed byte value[32];
+ }
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Taskfile.yaml b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Taskfile.yaml
new file mode 100644
index 0000000..0b441a3
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Taskfile.yaml
@@ -0,0 +1,70 @@
+# https://taskfile.dev
+
+#Called by the vnbuild system to produce builds for my website
+#https://www.vaughnnugent.com/resources/software
+
+#This taskfile is called from the root of a project that is being built
+#and the purpose of this taskfile is to package up the output of a build
+#from the solution file, and package it up into a tgz files for distribution
+
+version: '3'
+
+vars:
+ TARGET: '{{.USER_WORKING_DIR}}/bin'
+ RELEASE_DIR: "./bin/release/{{.TARGET_FRAMEWORK}}/publish"
+
+tasks:
+
+ #when build succeeds, archive the output into a tgz
+ postbuild_success:
+ dir: '{{.USER_WORKING_DIR}}'
+ cmds:
+ #pack up source code
+ - task: packsource
+
+ #run post in debug mode
+ - task: postbuild
+ vars: { BUILD_MODE: debug }
+
+ #remove uncessary files from the release dir
+ - powershell -Command "Get-ChildItem -Recurse '{{.RELEASE_DIR}}/' -Include *.pdb,*.xml | Remove-Item"
+
+ #run post in release mode
+ - task: postbuild
+ vars: { BUILD_MODE: release }
+
+
+ postbuild_failed:
+ dir: '{{.USER_WORKING_DIR}}'
+ cmds: []
+
+ postbuild:
+ dir: '{{.USER_WORKING_DIR}}'
+ internal: true
+ vars:
+ #the build output directory
+ BUILD_OUT: "{{.USER_WORKING_DIR}}/bin/{{.BUILD_MODE}}/{{.TARGET_FRAMEWORK}}/publish"
+
+ cmds:
+ #copy license and readme to target
+ - cd .. && powershell -Command "Copy-Item -Path ./build.readme.md -Destination '{{.BUILD_OUT}}/readme.md'"
+
+ #tar outputs
+ - cd "{{.BUILD_OUT}}" && tar -czf "{{.TARGET}}/{{.BUILD_MODE}}.tgz" .
+
+ packsource:
+ dir: '{{.USER_WORKING_DIR}}'
+ internal: true
+ cmds:
+ #copy source code to target
+ - powershell -Command "Get-ChildItem -Include *.cs,*.csproj -Recurse | Where { \$_.FullName -notlike '*\obj\*' -and \$_.FullName -notlike '*\bin\*' } | Resolve-Path -Relative | tar --files-from - -czf '{{.TARGET}}/src.tgz'"
+
+
+#Remove the output dirs on clean
+ clean:
+ dir: '{{.USER_WORKING_DIR}}'
+ ignore_error: true
+ cmds:
+ - for: ['bin/', 'obj/']
+ cmd: powershell Remove-Item -Recurse '{{.ITEM}}'
+
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/UnmanagedRandomSource.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/UnmanagedRandomSource.cs
new file mode 100644
index 0000000..91ff64b
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/UnmanagedRandomSource.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.Runtime.InteropServices;
+
+using VNLib.Utils;
+using VNLib.Utils.Native;
+using VNLib.Utils.Extensions;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+
+ /// <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/VNLib.Utils.Cryptography.Noscrypt.csproj b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj
new file mode 100644
index 0000000..7e7f5de
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/VNLib.Utils.Cryptography.Noscrypt.csproj
@@ -0,0 +1,28 @@
+<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>
+ <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0122" />
+ <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0122" />
+ </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..aa916eb
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/FunctionTable.cs
@@ -0,0 +1,127 @@
+// 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 NCEncryptDelegate NCEncrypt;
+ public readonly NCDecryptDelegate NCDecrypt;
+ public readonly NCVerifyMacDelegate NCVerifyMac;
+ public readonly NCComputeMacDelegate NCComputeMac;
+
+#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>();
+ NCEncrypt = library.DangerousGetFunction<NCEncryptDelegate>();
+ NCDecrypt = library.DangerousGetFunction<NCDecryptDelegate>();
+ NCVerifyMac = library.DangerousGetFunction<NCVerifyMacDelegate>();
+ NCComputeMac = library.DangerousGetFunction<NCComputeMacDelegate>();
+
+#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* sk, byte* data, uint dataSize, byte* sig64);
+
+ [SafeMethodName("NCEncrypt")]
+ internal delegate NCResult NCEncryptDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCEncryptionArgs* data);
+
+ [SafeMethodName("NCDecrypt")]
+ internal delegate NCResult NCDecryptDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCEncryptionArgs* data);
+
+ [SafeMethodName("NCVerifyMac")]
+ internal delegate NCResult NCVerifyMacDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCMacVerifyArgs* args);
+
+ [SafeMethodName("NCComputeMac")]
+ internal delegate NCResult NCComputeMacDelegate(IntPtr ctx, byte* hmacKey32, byte* payload, uint payloadSize, byte* hmacOut32);
+
+ [SafeMethodName("NCGetConversationKey")]
+ internal delegate NCResult NCGetConversationKeyDelegate(nint ctx, NCSecretKey* sk, NCPublicKey* pk, byte* keyOut32);
+
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCEncryptionArgs.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCEncryptionArgs.cs
new file mode 100644
index 0000000..a63d3b3
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCEncryptionArgs.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;
+using System.Runtime.InteropServices;
+
+namespace VNLib.Utils.Cryptography.Noscrypt.@internal
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct NCEncryptionArgs
+ {
+ public byte* nonce32;
+ public byte* hmacKeyOut32;
+ public byte* inputData;
+ public byte* outputData;
+ public uint dataSize;
+ public uint version;
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCMacVerifyArgs.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCMacVerifyArgs.cs
new file mode 100644
index 0000000..8a9ba1f
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/internal/NCMacVerifyArgs.cs
@@ -0,0 +1,34 @@
+// 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.@internal
+{
+ internal unsafe struct NCMacVerifyArgs
+ {
+ /* The message authentication code certifying the Nip44 payload */
+ public byte* mac32;
+
+ /* The nonce used for the original message encryption */
+ public byte* nonce32;
+
+ /* The message payload data */
+ public byte* payload;
+
+ /* The size of the payload data */
+ public uint payloadSize;
+ }
+}