aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--back-end/plugins/nvault/src/Model/NostrContext.cs4
-rw-r--r--lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/LibNoscryptTests.cs86
-rw-r--r--lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/NVault.Crypto.NoscryptTests.csproj23
-rw-r--r--lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/Properties/launchSettings.json8
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs96
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/INostrCrypto.cs32
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs170
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCContext.cs88
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs29
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCPublicKey.cs31
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCSecretKey.cs31
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCUtil.cs175
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj2
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs196
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs200
-rw-r--r--nvault.build.sln7
16 files changed, 1124 insertions, 54 deletions
diff --git a/back-end/plugins/nvault/src/Model/NostrContext.cs b/back-end/plugins/nvault/src/Model/NostrContext.cs
index bdd1319..7dc29d0 100644
--- a/back-end/plugins/nvault/src/Model/NostrContext.cs
+++ b/back-end/plugins/nvault/src/Model/NostrContext.cs
@@ -23,7 +23,7 @@ namespace NVault.Plugins.Vault.Model
internal class NostrContext : TransactionalDbContext, IDbTableDefinition
{
- public DbSet<NostrRelay> Relays { get; set; }
+ public DbSet<NostrRelay> NostrRelays { get; set; }
public DbSet<NostrKeyMeta> NostrPublicKeys { get; set; }
@@ -38,7 +38,7 @@ namespace NVault.Plugins.Vault.Model
public void OnDatabaseCreating(IDbContextBuilder builder, object? userState)
{
//Configure relay table
- builder.DefineTable<NostrRelay>(nameof(Relays))
+ builder.DefineTable<NostrRelay>(nameof(NostrRelays))
.WithColumn(r => r.Id)
.MaxLength(50)
.Next()
diff --git a/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/LibNoscryptTests.cs b/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/LibNoscryptTests.cs
new file mode 100644
index 0000000..d254253
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/LibNoscryptTests.cs
@@ -0,0 +1,86 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System.Runtime.CompilerServices;
+
+using VNLib.Hashing;
+using VNLib.Utils.Memory;
+
+namespace NVault.Crypto.Noscrypt.Tests
+{
+ [TestClass()]
+ public class LibNoscryptTests
+ {
+ const string NoscryptLib = @"F:\Programming\noscrypt\out\build\x64-debug\Debug\noscrypt.dll";
+
+ [TestMethod()]
+ public void InitializeTest()
+ {
+ //Random context seed
+ ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
+
+ using LibNoscrypt library = LibNoscrypt.Load(NoscryptLib);
+
+ //Init new context and interface
+ NCContext context = library.Initialize(MemoryUtil.Shared, seed);
+
+ using NostrCrypto crypto = new(context, true);
+ }
+
+ [TestMethod()]
+ public void ValidateSecretKeyTest()
+ {
+ //Random context seed
+ ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
+ ReadOnlySpan<byte> secretKey = RandomHash.GetRandomBytes(32);
+
+ Span<byte> publicKey = stackalloc byte[32];
+
+ using LibNoscrypt library = LibNoscrypt.Load(NoscryptLib);
+
+ //Init new context and interface
+ NCContext context = library.Initialize(MemoryUtil.Shared, seed);
+
+ using NostrCrypto crypto = new(context, true);
+
+ //validate the secret key
+ Assert.IsTrue(crypto.ValidateSecretKey(in NCUtil.AsSecretKey(secretKey)));
+
+ //Generate the public key
+ crypto.GetPublicKey(
+ in NCUtil.AsSecretKey(secretKey),
+ ref NCUtil.AsPublicKey(publicKey)
+ );
+
+ //Make sure the does not contain all zeros
+ Assert.IsTrue(publicKey.ToArray().Any(b => b != 0));
+ }
+
+ //Test argument validations
+ [TestMethod()]
+ public void TestPublicApiArgValidations()
+ {
+ //Random context seed
+ ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
+
+ using LibNoscrypt library = LibNoscrypt.Load(NoscryptLib);
+
+ //Init new context and interface
+ NCContext context = library.Initialize(MemoryUtil.Shared, seed);
+
+ using NostrCrypto crypto = new(context, true);
+
+ NCSecretKey secKey = default;
+ NCPublicKey pubKey = default;
+
+ //noThrow (its a bad sec key but it should not throw)
+ crypto.ValidateSecretKey(ref secKey);
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.ValidateSecretKey(ref Unsafe.NullRef<NCSecretKey>()));
+
+ //public key
+ //NoThrow
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(ref Unsafe.NullRef<NCSecretKey>(), ref pubKey));
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(in secKey, ref Unsafe.NullRef<NCPublicKey>()));
+
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/NVault.Crypto.NoscryptTests.csproj b/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/NVault.Crypto.NoscryptTests.csproj
new file mode 100644
index 0000000..d3bbd9c
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/NVault.Crypto.NoscryptTests.csproj
@@ -0,0 +1,23 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>net8.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <Nullable>enable</Nullable>
+
+ <IsPackable>false</IsPackable>
+ <IsTestProject>true</IsTestProject>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
+ <PackageReference Include="MSTest.TestAdapter" Version="3.0.4" />
+ <PackageReference Include="MSTest.TestFramework" Version="3.0.4" />
+ <PackageReference Include="coverlet.collector" Version="6.0.0" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\src\NVault.Crypto.Noscrypt.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/Properties/launchSettings.json b/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/Properties/launchSettings.json
new file mode 100644
index 0000000..b3894a7
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "NVault.Crypto.NoscryptTests": {
+ "commandName": "Project",
+ "nativeDebugging": true
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs b/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs
new file mode 100644
index 0000000..fe1613a
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs
@@ -0,0 +1,96 @@
+// 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;
+using VNLib.Utils.Native;
+
+using NCResult = System.Int64;
+
+namespace NVault.Crypto.Noscrypt
+{
+ 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;
+
+ private FunctionTable(SafeLibraryHandle library)
+ {
+ //Load the required high-level api functions
+ NCGetContextStructSize = library.DangerousGetMethod<NCGetContextStructSizeDelegate>();
+ NCInitContext = library.DangerousGetMethod<NCInitContextDelegate>();
+ NCReInitContext = library.DangerousGetMethod<NCReInitContextDelegate>();
+ NCDestroyContext = library.DangerousGetMethod<NCDestroyContextDelegate>();
+ NCGetPublicKey = library.DangerousGetMethod<NCGetPublicKeyDelegate>();
+ NCValidateSecretKey = library.DangerousGetMethod<NCValidateSecretKeyDelegate>();
+ NCSignData = library.DangerousGetMethod<NCSignDataDelegate>();
+ NCVerifyData = library.DangerousGetMethod<NCVerifyDataDelegate>();
+ NCSignData = library.DangerousGetMethod<NCSignDataDelegate>();
+ NCVerifyData = library.DangerousGetMethod<NCVerifyDataDelegate>();
+ NCEncrypt = library.DangerousGetMethod<NCEncryptDelegate>();
+ NCDecrypt = library.DangerousGetMethod<NCDecryptDelegate>();
+ }
+
+ /// <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);
+
+ //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, nint dataSize, byte* sig64);
+
+ [SafeMethodName("NCVerifyData")]
+ internal delegate NCResult NCVerifyDataDelegate(IntPtr ctx, NCPublicKey* sk, byte* data, nint dataSize, byte* sig64);
+
+ [SafeMethodName("NCEncrypt")]
+ internal delegate NCResult NCEncryptDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCCryptoData* data);
+
+ [SafeMethodName("NCDecrypt")]
+ internal delegate NCResult NCDecryptDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCCryptoData* data);
+ }
+}
diff --git a/lib/NVault.Crypto.Noscrypt/src/INostrCrypto.cs b/lib/NVault.Crypto.Noscrypt/src/INostrCrypto.cs
new file mode 100644
index 0000000..11be0d0
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/INostrCrypto.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/>.
+
+namespace NVault.Crypto.Noscrypt
+{
+ public interface INostrCrypto
+ {
+ void GetPublicKey(ref readonly NCSecretKey secretKey, ref NCPublicKey publicKey);
+
+ bool ValidateSecretKey(ref readonly NCSecretKey secretKey);
+
+ void SignData(ref readonly NCSecretKey secretKey, ref readonly byte random32, ref readonly byte data, nint dataSize, ref byte sig64);
+
+ void VerifyData(ref readonly NCPublicKey pubKey, ref readonly byte data, nint dataSize, ref byte sig64);
+
+ void Encrypt(ref readonly NCSecretKey secretKey, ref readonly NCPublicKey publicKey, ref readonly byte nonce, ref readonly byte plainText, ref byte cipherText, uint size);
+
+ void Decrypt(ref readonly NCSecretKey secretKey, ref readonly NCPublicKey publicKey, ref readonly byte nonce, ref readonly byte cipherText, ref byte plainText, uint size);
+ }
+}
diff --git a/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs b/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
index ec0ce2d..4f892b3 100644
--- a/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
@@ -1,18 +1,37 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
+// 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 System.Text;
-using System.Threading.Tasks;
using VNLib.Utils;
using VNLib.Utils.Extensions;
+using VNLib.Utils.Memory;
using VNLib.Utils.Native;
using NCResult = System.Int64;
namespace NVault.Crypto.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
@@ -25,62 +44,96 @@ namespace NVault.Crypto.Noscrypt
public const int NC_MESSAGE_KEY_SIZE = 32;
public const int CTX_ENTROPY_SIZE = 32;
- //STRUCTS MUST MATCH THE NOSCRYPT.H HEADER
+ 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;
- [StructLayout(LayoutKind.Sequential, Size = NC_SEC_KEY_SIZE)]
- internal struct NCSecretKey
- {
- public fixed byte key[NC_SEC_KEY_SIZE];
- }
+ private readonly FunctionTable _functions = FunctionTable.BuildFunctionTable(Library);
- [StructLayout(LayoutKind.Sequential, Size = NC_SEC_PUBKEY_SIZE)]
- internal struct NCPublicKey
+ /// <summary>
+ /// Gets a reference to the loaded function table for
+ /// the native library
+ /// </summary>
+ internal ref readonly FunctionTable Functions
{
- public fixed byte key[NC_SEC_PUBKEY_SIZE];
+ get
+ {
+ Check();
+ Library.ThrowIfClosed();
+ return ref _functions;
+ }
}
- [StructLayout(LayoutKind.Sequential)]
- internal struct NCCryptoData
+ /// <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)
{
- public fixed byte nonce[NC_ENCRYPTION_NONCE_SIZE];
- public void* inputData;
- public void* outputData;
- public uint dataSize;
- }
-
- //FUCNTIONS
- [SafeMethodName("NCGetContextStructSize")]
- internal delegate uint NCGetContextStructSizeDelegate();
-
- [SafeMethodName("NCInitContext")]
- internal delegate NCResult NCInitContextDelegate(void* ctx , byte* entropy32);
-
- [SafeMethodName("NCReInitContext")]
- internal delegate NCResult NCReInitContextDelegate(void* ctx, byte* entropy32);
-
- [SafeMethodName("NCDestroyContext")]
- internal delegate NCResult NCDestroyContextDelegate(void* ctx);
-
- [SafeMethodName("NCGetPublicKey")]
- internal delegate NCResult NCGetPublicKeyDelegate(void* ctx, NCSecretKey* secKey, NCPublicKey* publicKey);
+ ArgumentNullException.ThrowIfNull(heap);
- [SafeMethodName("NCValidateSecretKey")]
- internal delegate NCResult NCValidateSecretKeyDelegate(void* ctx, NCSecretKey* secKey);
+ //Entropy must be exactly 32 bytes
+ ArgumentOutOfRangeException.ThrowIfNotEqual(size, CTX_ENTROPY_SIZE);
- [SafeMethodName("NCSignData")]
- internal delegate NCResult NCSignDataDelegate(void* ctx, NCSecretKey* secKey, byte* random32, byte* data, long dataSize, byte* sig64);
+ //Get struct size
+ nuint ctxSize = Functions.NCGetContextStructSize.Invoke();
- [SafeMethodName("NCVerifyData")]
- internal delegate NCResult NCVerifyDataDelegate(void* ctx, NCPublicKey* pubKey, byte* data, long dataSize, byte* sig64);
-
- [SafeMethodName("NCSignDigest")]
- internal delegate NCResult NCSignDigestDelegate(void* ctx, NCSecretKey* secKey, byte* random32, byte* digest32, byte* sig64);
+ //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);
+ }
- [SafeMethodName("NCVerifyDigest")]
- internal delegate NCResult NCVerifyDigestDelegate(void* ctx, NCPublicKey* pubKey, byte* digest32, byte* sig64);
+ NCUtil.CheckResult<FunctionTable.NCInitContextDelegate>(result);
+ 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
+ );
+ }
///<inheritdoc/>
protected override void Free()
@@ -88,20 +141,35 @@ namespace NVault.Crypto.Noscrypt
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/lib/NVault.Crypto.Noscrypt/src/NCContext.cs b/lib/NVault.Crypto.Noscrypt/src/NCContext.cs
new file mode 100644
index 0000000..a9bd47d
--- /dev/null
+++ b/lib/NVault.Crypto.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 NVault.Crypto.Noscrypt.LibNoscrypt;
+
+using NCResult = System.Int64;
+
+namespace NVault.Crypto.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);
+ }
+ }
+
+ ///<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/lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs b/lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs
new file mode 100644
index 0000000..1224fa6
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs
@@ -0,0 +1,29 @@
+// 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;
+
+namespace NVault.Crypto.Noscrypt
+{
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct NCCryptoData
+ {
+ public fixed byte nonce[LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE];
+
+ public void* inputData;
+ public void* outputData;
+ public uint dataSize;
+ }
+}
diff --git a/lib/NVault.Crypto.Noscrypt/src/NCPublicKey.cs b/lib/NVault.Crypto.Noscrypt/src/NCPublicKey.cs
new file mode 100644
index 0000000..9a7ba46
--- /dev/null
+++ b/lib/NVault.Crypto.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 NVault.Crypto.Noscrypt.LibNoscrypt;
+
+namespace NVault.Crypto.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/lib/NVault.Crypto.Noscrypt/src/NCSecretKey.cs b/lib/NVault.Crypto.Noscrypt/src/NCSecretKey.cs
new file mode 100644
index 0000000..ebd4e9d
--- /dev/null
+++ b/lib/NVault.Crypto.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 NVault.Crypto.Noscrypt.LibNoscrypt;
+
+namespace NVault.Crypto.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/lib/NVault.Crypto.Noscrypt/src/NCUtil.cs b/lib/NVault.Crypto.Noscrypt/src/NCUtil.cs
new file mode 100644
index 0000000..61c2068
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/NCUtil.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.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using static NVault.Crypto.Noscrypt.LibNoscrypt;
+
+using NCResult = System.Int64;
+
+namespace NVault.Crypto.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.ThrowIfNotEqual(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.ThrowIfNotEqual(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.ThrowIfNotEqual(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.ThrowIfNotEqual(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) 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:
+ 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/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj b/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj
index 2416535..1424aee 100644
--- a/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj
+++ b/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj
@@ -16,7 +16,7 @@
<Description>Provides a managed library for the noscrypt native library, along with other helper types for NVault</Description>
<Copyright>Copyright © 2024 Vaughn Nugent</Copyright>
<PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/NVault</PackageProjectUrl>
- <RepositoryUrl>https://github.com/VnUgE/NVault/tree/master/</RepositoryUrl>
+ <RepositoryUrl>https://github.com/VnUgE/NVault/tree/master/lib/NVault.Crypto.Noscrypt</RepositoryUrl>
</PropertyGroup>
<ItemGroup>
diff --git a/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs b/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs
new file mode 100644
index 0000000..18762c0
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs
@@ -0,0 +1,196 @@
+// 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;
+
+namespace NVault.Crypto.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 uint CalcBufferSize(uint dataSize)
+ {
+ //always add leading 2 bytes for the encoded data size
+ dataSize += sizeof(ushort);
+
+ //Min message size is 32 bytes
+ uint minSize = Math.Max(dataSize, 32);
+
+ //calculate the next power of 2
+ uint nextPow2 = 1;
+ while (nextPow2 < minSize)
+ {
+ nextPow2 <<= 1;
+ }
+
+ return nextPow2;
+ }
+
+ /// <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(
+ ref MemoryMarshal.GetReference(plaintextData),
+ sizeof(ushort),
+ ref MemoryMarshal.GetReference(output),
+ 0,
+ (uint)plaintextData.Length
+ );
+
+ //We assume the remaining buffer is zeroed out
+ }
+
+ public static void Encrypt(
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ReadOnlySpan<byte> nonce32,
+ ReadOnlySpan<byte> plainText,
+ 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, 32, nameof(nonce32));
+
+ //Encrypt data, use the plaintext buffer size as the data size
+ lib.Encrypt(
+ in secretKey,
+ in publicKey,
+ in MemoryMarshal.GetReference(nonce32),
+ in MemoryMarshal.GetReference(plainText),
+ ref MemoryMarshal.GetReference(cipherText),
+ (uint)plainText.Length
+ );
+ }
+
+ public static unsafe void Encrypt(
+ this INostrCrypto lib,
+ ref NCSecretKey secretKey,
+ ref NCPublicKey publicKey,
+ void* nonce32,
+ 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
+ Encrypt(
+ lib,
+ in secretKey,
+ in publicKey,
+ new Span<byte>(nonce32, 32),
+ new Span<byte>(plainText, (int)size),
+ new Span<byte>(cipherText, (int)size)
+ );
+ }
+
+
+ public static void Decrypt(
+ 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.Decrypt(
+ in secretKey,
+ in publicKey,
+ in MemoryMarshal.GetReference(nonce32),
+ in MemoryMarshal.GetReference(cipherText),
+ ref MemoryMarshal.GetReference(plainText),
+ (uint)cipherText.Length
+ );
+ }
+
+ public static unsafe void Decrypt(
+ 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
+ Decrypt(
+ lib,
+ in secretKey,
+ in publicKey,
+ new Span<byte>(nonce32, 32),
+ new Span<byte>(cipherText, (int)size),
+ new Span<byte>(plainText, (int)size)
+ );
+ }
+ }
+}
diff --git a/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs b/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs
new file mode 100644
index 0000000..6b547de
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs
@@ -0,0 +1,200 @@
+// 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 VNLib.Utils;
+
+using NCResult = System.Int64;
+
+namespace NVault.Crypto.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 Decrypt(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce,
+ ref readonly byte cipherText,
+ ref byte plainText,
+ uint size
+ )
+ {
+ Check();
+
+ IntPtr libCtx = context.DangerousGetHandle();
+
+ NCCryptoData data = default;
+ data.dataSize = size;
+
+ //Copy nonce to struct memory buffer
+ Unsafe.CopyBlock(
+ ref Unsafe.AsRef<byte>(data.nonce),
+ in nonce,
+ LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE
+ );
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText)
+ {
+ //Set input data to the cipher text to decrypt and the output data to the plaintext buffer
+ data.inputData = pCipherText;
+ data.outputData = pTextPtr;
+
+ NCResult result = Functions.NCDecrypt.Invoke(libCtx, pSecKey, pPubKey, &data);
+ NCUtil.CheckResult<FunctionTable.NCDecryptDelegate>(result);
+ }
+ }
+
+ ///<inheritdoc/>
+ public void Encrypt(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce,
+ ref readonly byte plainText,
+ ref byte cipherText,
+ uint size
+ )
+ {
+ Check();
+
+ IntPtr libCtx = context.DangerousGetHandle();
+
+ NCCryptoData data = default;
+ data.dataSize = size;
+
+ //Copy nonce to struct memory buffer
+ Unsafe.CopyBlock(
+ ref Unsafe.AsRef<byte>(data.nonce),
+ in nonce,
+ 0
+ );
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText)
+ {
+ //Set input data to the plaintext to encrypt and the output data to the cipher text buffer
+ data.inputData = pTextPtr;
+ data.outputData = pCipherText;
+
+ NCResult result = Functions.NCEncrypt.Invoke(libCtx, pSecKey, pPubKey, &data);
+ NCUtil.CheckResult<FunctionTable.NCEncryptDelegate>(result);
+ }
+ }
+
+ ///<inheritdoc/>
+ public void GetPublicKey(ref readonly NCSecretKey secretKey, ref NCPublicKey publicKey)
+ {
+ Check();
+
+ IntPtr libCtx = context.DangerousGetHandle();
+
+ fixed(NCSecretKey* pSecKey = &secretKey)
+ fixed(NCPublicKey* pPubKey = &publicKey)
+ {
+ NCResult result = Functions.NCGetPublicKey.Invoke(libCtx, pSecKey, pPubKey);
+ NCUtil.CheckResult<FunctionTable.NCGetPublicKeyDelegate>(result);
+ }
+ }
+
+ ///<inheritdoc/>
+ public void SignData(
+ ref readonly NCSecretKey secretKey,
+ ref readonly byte random32,
+ ref readonly byte data,
+ nint dataSize,
+ ref byte sig64
+ )
+ {
+ Check();
+
+ IntPtr libCtx = context.DangerousGetHandle();
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed(byte* pData = &data, pSig = &sig64, pRandom = &random32)
+ {
+ NCResult result = Functions.NCSignData.Invoke(libCtx, pSecKey, pRandom, pData, dataSize, pSig);
+ NCUtil.CheckResult<FunctionTable.NCSignDataDelegate>(result);
+ }
+ }
+
+ ///<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);
+
+ //Result should be 1 if the secret key is valid
+ return result == 1;
+ }
+ }
+
+ ///<inheritdoc/>
+ public void VerifyData(
+ ref readonly NCPublicKey pubKey,
+ ref readonly byte data,
+ nint dataSize,
+ ref byte sig64
+ )
+ {
+ Check();
+
+ IntPtr libCtx = context.DangerousGetHandle();
+
+ fixed(NCPublicKey* pPubKey = &pubKey)
+ fixed (byte* pData = &data, pSig = &sig64)
+ {
+ NCResult result = Functions.NCVerifyData.Invoke(libCtx, pPubKey, pData, dataSize, pSig);
+ NCUtil.CheckResult<FunctionTable.NCVerifyDataDelegate>(result);
+ }
+ }
+
+
+ ///<inheritdoc/>
+ protected override void Free()
+ {
+ if(ownsContext)
+ {
+ context.Dispose();
+ }
+ }
+ }
+}
diff --git a/nvault.build.sln b/nvault.build.sln
index adafdc4..50c74c8 100644
--- a/nvault.build.sln
+++ b/nvault.build.sln
@@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NVault.Crypto.Secp256k1", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NVault.VaultExtensions", "lib\NVault.VaultExtensions\src\NVault.VaultExtensions.csproj", "{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NVault.Crypto.NoscryptTests", "lib\NVault.Crypto.Noscrypt\NVault.Crypto.NoscryptTests\NVault.Crypto.NoscryptTests.csproj", "{7273CA9C-BA83-4438-8CDF-0B1605CD03CD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -39,6 +41,10 @@ Global
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7273CA9C-BA83-4438-8CDF-0B1605CD03CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7273CA9C-BA83-4438-8CDF-0B1605CD03CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7273CA9C-BA83-4438-8CDF-0B1605CD03CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7273CA9C-BA83-4438-8CDF-0B1605CD03CD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -48,6 +54,7 @@ Global
{686B9DB8-5BA9-49AB-9761-6B661A5027BD} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
{7952F25A-DA19-492E-B797-839082053DE4} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
+ {7273CA9C-BA83-4438-8CDF-0B1605CD03CD} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ACF01A45-B7F0-4055-84F2-A3596713C9BB}