aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-06-20 21:38:00 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-06-20 21:38:00 -0400
commiteefbfce0af26be62ec3b329e4ef78f12f5f71c98 (patch)
treea948724be366cdcbba022a6594dd32b01388c295
parent0e0d7701979cd09e67cbd0137016ba6a5bb3b803 (diff)
push latest c-sharp changes
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Base64SignatureEncoder.cs31
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/HexSignatureEncoder.cs31
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrEncryptionVersion.cs (renamed from wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IEncryptionVersion.cs)13
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrSignatureEncoder.cs33
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs13
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip04EncryptionVersion.cs13
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip44EncryptionVersion.cs10
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCPublicKey.cs8
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs8
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs2
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NcFallbackRandom.cs36
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs9
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44MessageSegments.cs (renamed from wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Message.cs)2
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs58
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs2
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs (renamed from wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/LibNoscrypt.cs)110
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptSigner.cs133
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs16
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs234
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrMessageCipher.cs424
-rw-r--r--wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/tests/LibNoscryptTests.cs97
21 files changed, 922 insertions, 361 deletions
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Base64SignatureEncoder.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Base64SignatureEncoder.cs
new file mode 100644
index 0000000..c5078a4
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Base64SignatureEncoder.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using System;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ public sealed class Base64SignatureEncoder : INostrSignatureEncoder
+ {
+ /// <summary>
+ /// Shared formatter instance for base64 signatures
+ /// </summary>
+ public static Base64SignatureEncoder Instance { get; } = new Base64SignatureEncoder();
+
+ ///<inheritdoc/>
+ public string GetString(ReadOnlySpan<byte> signature) => Convert.ToBase64String(signature);
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/HexSignatureEncoder.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/HexSignatureEncoder.cs
new file mode 100644
index 0000000..6a60c73
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/HexSignatureEncoder.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using System;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ public sealed class HexSignatureEncoder : INostrSignatureEncoder
+ {
+ /// <summary>
+ /// Shared formatter instance for hex signatures
+ /// </summary>
+ public static HexSignatureEncoder Instance { get; } = new HexSignatureEncoder();
+
+ ///<inheritdoc/>
+ public string GetString(ReadOnlySpan<byte> signature) => Convert.ToHexString(signature);
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IEncryptionVersion.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrEncryptionVersion.cs
index e9aab2b..3a26466 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/IEncryptionVersion.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrEncryptionVersion.cs
@@ -18,7 +18,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt
/// <summary>
/// Represents a message encryption version used by the Nostr protocol
/// </summary>
- public interface IEncryptionVersion
+ public interface INostrEncryptionVersion
{
/// <summary>
/// The noscrypt compatible encryption version
@@ -26,11 +26,18 @@ namespace VNLib.Utils.Cryptography.Noscrypt
internal uint Version { get; }
/// <summary>
- /// Calculates the required buffer size for the specified data size
+ /// Calculates the required payload 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);
+ internal int GetPayloadBufferSize(int dataSize);
+
+ /// <summary>
+ /// Calculates the required message buffer size for the specified data size
+ /// </summary>
+ /// <param name="dataSize">Plain text data size</param>
+ /// <returns>The estimated size of the buffer required to complete the opeation</returns>
+ internal int GetMessageBufferSize(int dataSize);
}
}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrSignatureEncoder.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrSignatureEncoder.cs
new file mode 100644
index 0000000..b8c69f5
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/INostrSignatureEncoder.cs
@@ -0,0 +1,33 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using System;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ /// <summary>
+ /// Encodes a message signature into it's string representation
+ /// </summary>
+ public interface INostrSignatureEncoder
+ {
+ /// <summary>
+ /// Creates a string of the encoded signature data
+ /// </summary>
+ /// <param name="signature">The signature data to encode into the string</param>
+ /// <returns>The encoded signature string</returns>
+ string GetString(ReadOnlySpan<byte> signature);
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs
index dbd9372..8f8c6b4 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCContext.cs
@@ -21,17 +21,16 @@ using Microsoft.Win32.SafeHandles;
using VNLib.Utils.Extensions;
using VNLib.Utils.Memory;
-using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+using VNLib.Utils.Cryptography.Noscrypt.@internal;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
using NCResult = System.Int64;
namespace VNLib.Utils.Cryptography.Noscrypt
{
/// <summary>
- /// Represents a context for the native library
+ /// The noscrypt library context
/// </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;
@@ -39,9 +38,9 @@ namespace VNLib.Utils.Cryptography.Noscrypt
/// <summary>
/// The library this context was created from
/// </summary>
- public LibNoscrypt Library { get; }
+ public NoscryptLibrary Library { get; }
- internal NCContext(IntPtr handle, IUnmangedHeap heap, LibNoscrypt library) :base(true)
+ internal NCContext(IntPtr handle, IUnmangedHeap heap, NoscryptLibrary library) :base(true)
{
ArgumentNullException.ThrowIfNull(heap);
ArgumentNullException.ThrowIfNull(library);
@@ -61,7 +60,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt
public unsafe void Reinitalize(ref byte entropy, int size)
{
//Entropy must be exactly 32 bytes
- ArgumentOutOfRangeException.ThrowIfNotEqual(size, CTX_ENTROPY_SIZE);
+ ArgumentOutOfRangeException.ThrowIfNotEqual(size, NC_CTX_ENTROPY_SIZE);
this.ThrowIfClosed();
fixed (byte* p = &entropy)
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip04EncryptionVersion.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip04EncryptionVersion.cs
index 69234fe..beb21c2 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip04EncryptionVersion.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip04EncryptionVersion.cs
@@ -13,14 +13,14 @@
// 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;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
namespace VNLib.Utils.Cryptography.Noscrypt
{
/// <summary>
/// The NIP04 encryption version used by the Nostr protocol
/// </summary>
- public sealed class NCNip04EncryptionVersion : IEncryptionVersion
+ public sealed class NCNip04EncryptionVersion : INostrEncryptionVersion
{
/// <summary>
/// A static nip04 encryption version instance
@@ -28,10 +28,13 @@ namespace VNLib.Utils.Cryptography.Noscrypt
public static readonly NCNip04EncryptionVersion Instance = new();
///<inheritdoc/>
- uint IEncryptionVersion.Version => NC_ENC_VERSION_NIP04;
-
+ uint INostrEncryptionVersion.Version => NC_ENC_VERSION_NIP04;
+
+ ///<inheritdoc/>
+ int INostrEncryptionVersion.GetMessageBufferSize(int dataSize) => Nip04Util.CalcBufferSize(dataSize);
+
///<inheritdoc/>
- int IEncryptionVersion.CalcBufferSize(int dataSize) => Nip04Util.CalcBufferSize(dataSize);
+ int INostrEncryptionVersion.GetPayloadBufferSize(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
index e572dd7..0d5907a 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip44EncryptionVersion.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCNip44EncryptionVersion.cs
@@ -13,14 +13,14 @@
// 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;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
namespace VNLib.Utils.Cryptography.Noscrypt
{
/// <summary>
/// The NIP44 encryption version used by the Nostr protocol
/// </summary>
- public sealed class NCNip44EncryptionVersion : IEncryptionVersion
+ public sealed class NCNip44EncryptionVersion : INostrEncryptionVersion
{
/// <summary>
/// A static nip44 encryption version instance
@@ -28,10 +28,12 @@ namespace VNLib.Utils.Cryptography.Noscrypt
public static readonly NCNip44EncryptionVersion Instance = new();
///<inheritdoc/>
- uint IEncryptionVersion.Version => NC_ENC_VERSION_NIP44;
+ uint INostrEncryptionVersion.Version => NC_ENC_VERSION_NIP44;
+
+ int INostrEncryptionVersion.GetMessageBufferSize(int dataSize) => Nip44Util.CalcFinalBufferSize(dataSize);
///<inheritdoc/>
- int IEncryptionVersion.CalcBufferSize(int dataSize) => Nip44Util.CalcBufferSize(dataSize);
+ int INostrEncryptionVersion.GetPayloadBufferSize(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
index 0699d7d..57d7c3f 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCPublicKey.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCPublicKey.cs
@@ -14,9 +14,10 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
namespace VNLib.Utils.Cryptography.Noscrypt
{
@@ -26,6 +27,11 @@ namespace VNLib.Utils.Cryptography.Noscrypt
[StructLayout(LayoutKind.Sequential, Size = NC_SEC_PUBKEY_SIZE)]
public unsafe struct NCPublicKey
{
+ /// <summary>
+ /// Gets a null <see cref="NCPublicKey"/> reference.
+ /// </summary>
+ public static ref NCPublicKey NullRef => ref Unsafe.NullRef<NCPublicKey>();
+
private fixed byte key[NC_SEC_PUBKEY_SIZE];
}
}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs
index b586d26..18f025b 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCSecretKey.cs
@@ -13,9 +13,10 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
namespace VNLib.Utils.Cryptography.Noscrypt
{
@@ -26,6 +27,11 @@ namespace VNLib.Utils.Cryptography.Noscrypt
[StructLayout(LayoutKind.Sequential, Size = NC_SEC_KEY_SIZE)]
public unsafe struct NCSecretKey
{
+ /// <summary>
+ /// Gets a null reference to a secret key
+ /// </summary>
+ public static ref NCSecretKey NullRef => ref Unsafe.NullRef<NCSecretKey>();
+
private fixed byte key[NC_SEC_KEY_SIZE];
}
}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs
index e212125..49c66c1 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NCUtil.cs
@@ -18,7 +18,7 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
-using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
using NCResult = System.Int64;
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NcFallbackRandom.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NcFallbackRandom.cs
new file mode 100644
index 0000000..0949ad8
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NcFallbackRandom.cs
@@ -0,0 +1,36 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using System;
+
+using VNLib.Hashing;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ /// <summary>
+ /// A fallback crypographic random source used for default
+ /// rng if you wish
+ /// </summary>
+ public sealed class NcFallbackRandom : IRandomSource
+ {
+ /// <summary>
+ /// Gets the shared instance of the fallback random source
+ /// </summary>
+ public static NcFallbackRandom Shared { get; } = new NcFallbackRandom();
+
+ /// <inheritdoc/>
+ public void GetRandomBytes(Span<byte> buffer) => RandomHash.GetRandomBytes(buffer);
+ }
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs
index 82704db..c1906f0 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip04Util.cs
@@ -14,6 +14,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System;
+using System.Buffers.Text;
using VNLib.Utils.Extensions;
@@ -36,6 +37,14 @@ namespace VNLib.Utils.Cryptography.Noscrypt
{
throw new NotImplementedException();
}
+
+ static readonly int MaxEncodedIvLength = Base64.GetMaxEncodedToUtf8Length(16);
+
+ public static int CalcMessageBufferSize(int dataSize)
+ {
+ int bufSize = CalcBufferSize(dataSize);
+ return bufSize + "?iv=".Length + MaxEncodedIvLength;
+ }
}
}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Message.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44MessageSegments.cs
index b78530b..ddc2d68 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Message.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44MessageSegments.cs
@@ -17,7 +17,7 @@ using System;
namespace VNLib.Utils.Cryptography.Noscrypt
{
- public readonly ref struct Nip44Message(ReadOnlySpan<byte> payload)
+ public readonly ref struct Nip44MessageSegments(ReadOnlySpan<byte> payload)
{
readonly ReadOnlySpan<byte> _payload = payload;
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs
index 23c9127..2aebee1 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Nip44Util.cs
@@ -19,7 +19,7 @@ using System.Runtime.InteropServices;
using VNLib.Utils.Memory;
-using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
namespace VNLib.Utils.Cryptography.Noscrypt
{
@@ -92,16 +92,51 @@ namespace VNLib.Utils.Cryptography.Noscrypt
//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
+ src: in MemoryMarshal.GetReference(plaintextData),
+ srcOffset: 0,
+ dst: ref MemoryMarshal.GetReference(output),
+ dstOffset: sizeof(ushort),
+ elementCount: (uint)plaintextData.Length
);
//We assume the remaining buffer is zeroed out
}
+ public static void WriteNip44Message(
+ ReadOnlySpan<byte> payloadBuffer,
+ byte version,
+ ReadOnlySpan<byte> mac,
+ Span<byte> outBuffer
+ )
+ {
+ int requiredBufferSize = CalcFinalBufferSize(payloadBuffer.Length);
+
+ //Make sure the output buffer is large enough so we dont overrun it
+ ArgumentOutOfRangeException.ThrowIfLessThan(outBuffer.Length, requiredBufferSize, nameof(outBuffer));
+ ArgumentOutOfRangeException.ThrowIfLessThan(mac.Length, NC_ENCRYPTION_MAC_SIZE, nameof(mac));
+
+ //Write the version number to the first byte
+ outBuffer[0] = version;
+
+ //Copy the payload buffer to the output buffer after the version number
+ MemoryUtil.Memmove(
+ src: in MemoryMarshal.GetReference(payloadBuffer),
+ srcOffset: 0,
+ dst: ref MemoryMarshal.GetReference(outBuffer),
+ dstOffset: 1,
+ elementCount: (uint)payloadBuffer.Length
+ );
+
+ //Copy the mac to the end of the output buffer
+ MemoryUtil.Memmove(
+ src: in MemoryMarshal.GetReference(mac),
+ srcOffset: 0,
+ dst: ref MemoryMarshal.GetReference(outBuffer),
+ dstOffset: (uint)(requiredBufferSize - NC_ENCRYPTION_MAC_SIZE),
+ elementCount: NC_ENCRYPTION_MAC_SIZE
+ );
+ }
+
public static ReadOnlySpan<byte> GetNonceFromPayload(ReadOnlySpan<byte> message)
{
//The nonce is 32 bytes following the 1st byte version number of the message
@@ -139,6 +174,17 @@ namespace VNLib.Utils.Cryptography.Noscrypt
return plaintextPayload.Slice(sizeof(ushort), ptLength);
}
+ public static bool IsValidPlaintextMessage(ReadOnlySpan<byte> plaintextPayload)
+ {
+ ushort ptLength = BinaryPrimitives.ReadUInt16BigEndian(plaintextPayload);
+ return ptLength == plaintextPayload.Length - sizeof(ushort);
+ }
+
+ public static Range GetPlaintextRange(ReadOnlySpan<byte> plaintextPayload)
+ {
+ ushort ptLength = BinaryPrimitives.ReadUInt16BigEndian(plaintextPayload);
+ return new Range(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
index aaabc08..89bd057 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptExtensions.cs
@@ -16,7 +16,7 @@
using System;
using System.Runtime.InteropServices;
-using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
namespace VNLib.Utils.Cryptography.Noscrypt
{
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/LibNoscrypt.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs
index 32a07f4..108a713 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/LibNoscrypt.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptLibrary.cs
@@ -16,11 +16,9 @@
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 VNLib.Utils.Extensions;
using VNLib.Utils.Cryptography.Noscrypt.@internal;
@@ -34,37 +32,41 @@ namespace VNLib.Utils.Cryptography.Noscrypt
/// </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
+ public unsafe sealed class NoscryptLibrary(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 uint NC_ENC_SET_VERSION = 0x01u;
- public const uint NC_ENC_SET_NIP44_NONCE = 0x02u;
- public const uint NC_ENC_SET_NIP44_MAC_KEY = 0x03u;
- public const uint NC_ENC_SET_NIP04_KEY = 0x04u;
- public const uint NC_ENC_SET_NIP04_IV = 0x05u;
-
- 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;
+ public const string NoscryptDefaultLibraryName = "noscrypt";
+
+ //Constant values match the noscrypt.h header
+ public const int NC_SEC_KEY_SIZE = 0x20;
+ public const int NC_SEC_PUBKEY_SIZE = 0x20;
+ public const int NC_ENCRYPTION_NONCE_SIZE = 0x20;
+ public const int NC_PUBKEY_SIZE = 0x20;
+ public const int NC_SIGNATURE_SIZE = 0x40;
+ public const int NC_CONV_KEY_SIZE = 0x20;
+ public const int NC_MESSAGE_KEY_SIZE = 0x20;
+ public const int NC_HMAC_KEY_SIZE = 0x20;
+ public const int NC_ENCRYPTION_MAC_SIZE = 0x20;
+ public const int NC_CONVERSATION_KEY_SIZE = 0x20;
+ public const int NC_CTX_ENTROPY_SIZE = 0x20;
+ public const int NC_SIG_ENTROPY_SIZE = 0x20;
+
+ public const uint NC_ENC_VERSION_NIP04 = 0x00000004u;
+ public const uint NC_ENC_VERSION_NIP44 = 0x00000002c;
+
+ public const uint NC_ENC_SET_VERSION = 0x01u;
+ public const uint NC_ENC_SET_NIP44_NONCE = 0x02u;
+ public const uint NC_ENC_SET_NIP44_MAC_KEY = 0x03u;
+ public const uint NC_ENC_SET_NIP04_KEY = 0x04u;
+ public const uint NC_ENC_SET_NIP04_IV = 0x05u;
+
+ //Noscrypt error codes
+ public const NCResult NC_SUCCESS = 0x00;
+ 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);
@@ -103,7 +105,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt
ArgumentNullException.ThrowIfNull(heap);
//Entropy must be exactly 32 bytes
- ArgumentOutOfRangeException.ThrowIfNotEqual(size, CTX_ENTROPY_SIZE);
+ ArgumentOutOfRangeException.ThrowIfNotEqual(size, NC_CTX_ENTROPY_SIZE);
//Get struct size
nuint ctxSize = Functions.NCGetContextStructSize.Invoke();
@@ -155,7 +157,6 @@ namespace VNLib.Utils.Cryptography.Noscrypt
/// 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>
@@ -170,6 +171,29 @@ namespace VNLib.Utils.Cryptography.Noscrypt
);
}
+ /// <summary>
+ /// Initializes a new NostrCrypto context wraper directly that owns the internal context.
+ /// This may be done once at app startup and is thread-safe for the rest of the
+ /// application lifetime.
+ /// </summary>
+ /// <param name="heap">The heap to allocate the context from</param>
+ /// <param name="random">Random source used to generate context entropy</param>
+ /// <returns>The library wrapper handle</returns>
+ public NostrCrypto InitializeCrypto(IUnmangedHeap heap, IRandomSource random)
+ {
+ ArgumentNullException.ThrowIfNull(random);
+
+ //Get random bytes for context entropy
+ Span<byte> entropy = stackalloc byte[NC_CTX_ENTROPY_SIZE];
+ random.GetRandomBytes(entropy);
+
+ NostrCrypto nc = InitializeCrypto(heap, entropy);
+
+ MemoryUtil.InitializeBlock(entropy);
+
+ return nc;
+ }
+
///<inheritdoc/>
protected override void Free()
{
@@ -187,7 +211,8 @@ namespace VNLib.Utils.Cryptography.Noscrypt
/// <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)
+ /// <exception cref="DllNotFoundException"></exception>
+ public static NoscryptLibrary Load(string path, DllImportSearchPath search)
{
//Load the native library
SafeLibraryHandle handle = SafeLibraryHandle.LoadLibrary(path, search);
@@ -195,7 +220,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt
Trace.WriteLine($"Loaded noscrypt library 0x{handle.DangerousGetHandle():x} from {path}");
//Create the wrapper
- return new LibNoscrypt(handle, true);
+ return new NoscryptLibrary(handle, true);
}
/// <summary>
@@ -204,7 +229,14 @@ namespace VNLib.Utils.Cryptography.Noscrypt
/// </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);
-
+ /// <exception cref="DllNotFoundException"></exception>
+ public static NoscryptLibrary Load(string path) => Load(path, DllImportSearchPath.SafeDirectories);
+
+ /// <summary>
+ /// Attempts to load the default noscrypt library from the system search path
+ /// </summary>
+ /// <returns>The loaded library instance</returns>
+ /// <exception cref="DllNotFoundException"></exception>
+ public static NoscryptLibrary LoadDefault() => Load(NoscryptDefaultLibraryName, DllImportSearchPath.SafeDirectories);
}
}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptSigner.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptSigner.cs
new file mode 100644
index 0000000..586fa46
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NoscryptSigner.cs
@@ -0,0 +1,133 @@
+// 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.Memory;
+
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+ /// <summary>
+ /// A simple wrapper class to sign nostr message data using
+ /// the noscrypt library
+ /// </summary>
+ /// <param name="noscrypt">The noscrypt library instance</param>
+ /// <param name="random">A random entropy pool used to source random data for signature entropy</param>
+ public class NoscryptSigner(INostrCrypto noscrypt, IRandomSource random)
+ {
+ /// <summary>
+ /// Gets the size of the buffer required to hold the signature
+ /// </summary>
+ public static int SignatureBufferSize => NC_SIGNATURE_SIZE;
+
+ /// <summary>
+ /// Signs a message using the specified private key and message data
+ /// </summary>
+ /// <param name="hexPrivateKey">The hexadecimal private key used to sign the message</param>
+ /// <param name="message">The message data to sign</param>
+ /// <param name="format">A encoder used to convert the signature data to an encoded string</param>
+ /// <returns>The string encoded nostr signature</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public string SignData(string hexPrivateKey, ReadOnlySpan<byte> message, INostrSignatureEncoder? format = null)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(hexPrivateKey);
+ ArgumentOutOfRangeException.ThrowIfNotEqual(hexPrivateKey.Length / 2, NC_SEC_KEY_SIZE, nameof(hexPrivateKey));
+
+ //Have to allocate array unfortunately
+ byte[] privKey = Convert.FromHexString(hexPrivateKey);
+ try
+ {
+ return SignData(privKey.AsSpan(), message, format);
+ }
+ finally
+ {
+ //Always zero key beofre leaving
+ MemoryUtil.InitializeBlock(privKey);
+ }
+ }
+
+ /// <summary>
+ /// Signs a message using the specified secret key and message data
+ /// </summary>
+ /// <param name="secretKey">The secret key data buffer</param>
+ /// <param name="message">The message data to sign</param>
+ /// <param name="format">A encoder used to convert the signature data to an encoded string</param>
+ /// <returns>The string encoded nostr signature</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public string SignData(
+ ReadOnlySpan<byte> secretKey,
+ ReadOnlySpan<byte> message,
+ INostrSignatureEncoder? format = null
+ )
+ {
+ return SignData(in NCUtil.AsSecretKey(secretKey), message, format);
+ }
+
+ /// <summary>
+ /// Signs a message using the specified secret key and message data
+ /// </summary>
+ /// <param name="secretkey">A reference to the secret key structurer</param>
+ /// <param name="message">The message data to sign</param>
+ /// <param name="format">A encoder used to convert the signature data to an encoded string</param>
+ /// <returns>The string encoded nostr signature</returns>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public string SignData(
+ ref readonly NCSecretKey secretkey,
+ ReadOnlySpan<byte> message,
+ INostrSignatureEncoder? format = null
+ )
+ {
+ //Default to hex encoding because that is the default NIP-01 format
+ format ??= HexSignatureEncoder.Instance;
+
+ Span<byte> sigBuffer = stackalloc byte[SignatureBufferSize];
+
+ SignData(message, sigBuffer);
+
+ return format.GetString(sigBuffer);
+ }
+
+
+ /// <summary>
+ /// Signs a message using the specified secret key and message data
+ /// </summary>
+ /// <param name="secretkey">A reference to the secret key structurer</param>
+ /// <param name="data">The message data to sign</param>
+ /// <param name="signature">A buffer to write signature data to</param>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public void SignData(
+ ref readonly NCSecretKey secretkey,
+ ReadOnlySpan<byte> data,
+ Span<byte> signature
+ )
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(signature.Length, NC_SIGNATURE_SIZE, nameof(signature));
+
+ //Signature generation required random entropy to be secure
+ Span<byte> entropy = stackalloc byte[NC_SIG_ENTROPY_SIZE];
+ random.GetRandomBytes(entropy);
+
+ noscrypt.SignData(in secretkey, entropy, data, signature);
+ }
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs
index ec2cf66..36e2381 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrCrypto.cs
@@ -18,7 +18,7 @@ using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
using VNLib.Utils.Cryptography.Noscrypt.@internal;
-using static VNLib.Utils.Cryptography.Noscrypt.LibNoscrypt;
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
using NCResult = System.Int64;
@@ -86,7 +86,11 @@ namespace VNLib.Utils.Cryptography.Noscrypt
fixed (NCSecretKey* pSecKey = &secretKey)
fixed (NCPublicKey* pPubKey = &publicKey)
- fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText, pHmacKeyOut = &hmackKeyOut32, pNonce = &nonce32)
+ fixed (byte* pCipherText = &cipherText,
+ pTextPtr = &plainText,
+ pHmacKeyOut = &hmackKeyOut32,
+ pNonce = &nonce32
+ )
{
NCEncryptionArgs data = new();
@@ -227,7 +231,13 @@ namespace VNLib.Utils.Cryptography.Noscrypt
}
}
- public void ComputeMac(ref readonly byte hmacKey32, ref readonly byte payload, uint payloadSize, ref byte hmacOut32)
+ ///<inheritdoc/>
+ public void ComputeMac(
+ ref readonly byte hmacKey32,
+ ref readonly byte payload,
+ uint payloadSize,
+ ref byte hmacOut32
+ )
{
Check();
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs
deleted file mode 100644
index c70839c..0000000
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrEncryptedMessage.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-// Copyright (C) 2024 Vaughn Nugent
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as
-// published by the Free Software Foundation, either version 3 of the
-// License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program. If not, see <https://www.gnu.org/licenses/>.
-
-using System;
-using System.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/NostrMessageCipher.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrMessageCipher.cs
new file mode 100644
index 0000000..0982d3a
--- /dev/null
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/NostrMessageCipher.cs
@@ -0,0 +1,424 @@
+// 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 System.Security.Authentication;
+
+using VNLib.Utils.Extensions;
+using VNLib.Utils.Memory;
+
+using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary;
+
+namespace VNLib.Utils.Cryptography.Noscrypt
+{
+
+ public sealed class NostrMessageCipher(INostrCrypto lib, INostrEncryptionVersion version) : VnDisposeable
+ {
+ const int Nip44MaxMessageSize = 65603;
+
+ private readonly INostrCrypto library = lib;
+
+ private NCSecretKey _fromKey;
+ private NCPublicKey _toKey;
+ private Buffer32 _nonce32;
+ private Buffer32 _mac32;
+
+ /// <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 => MemoryMarshal.CreateSpan(ref GetNonceRef(), sizeof(Buffer32));
+
+ /// <summary>
+ /// The message MAC set during encryption, and required for decryption
+ /// </summary>
+ public unsafe Span<byte> Mac => MemoryMarshal.CreateSpan(ref GetMacRef(), 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 GetPayloadBufferSize(int dataSize)
+ => version.GetPayloadBufferSize(dataSize);
+
+ /// <summary>
+ /// Gets the size of the buffer required to hold the full encrypted message data
+ /// for the encryption version used
+ /// </summary>
+ /// <param name="dataSize">The plaintext data size</param>
+ /// <returns>The estimated size of the output buffer</returns>
+ public int GetMessageBufferSize(int dataSize)
+ => version.GetMessageBufferSize(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 NostrMessageCipher 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 NostrMessageCipher 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 NostrMessageCipher 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 NostrMessageCipher 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 NostrMessageCipher 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 NostrMessageCipher SetRandomNonce(IRandomSource rng)
+ {
+ rng.GetRandomBytes(Nonce);
+ return this;
+ }
+
+ /// <summary>
+ /// Configures a 32 byte mac for the message for nip44 decryption
+ /// </summary>
+ /// <param name="mac">The message mac</param>
+ /// <returns>The current instance for chaining</returns>
+ public NostrMessageCipher SetMac(ReadOnlySpan<byte> mac)
+ {
+ MemoryUtil.CopyStruct(mac, ref _mac32);
+ return this;
+ }
+
+ /// <summary>
+ /// Decrypts a full nostr encrypted message and writes the plaintext
+ /// data to the output buffer
+ /// </summary>
+ /// <param name="message">The nostr message buffer to decrypt</param>
+ /// <param name="plaintext">The output plaintext buffer</param>
+ /// <returns>The number of bytes written the the plaintext buffer</returns>
+ /// <exception cref="FormatException"></exception>
+ /// <exception cref="NotSupportedException"></exception>
+ public int DecryptMessage(ReadOnlySpan<byte> message, Span<byte> plaintext)
+ {
+ return Version switch
+ {
+ NC_ENC_VERSION_NIP44 => DecryptNip44Message(message, plaintext),
+ _ => throw new NotSupportedException("NIP04 encryption is not supported"),
+ };
+ }
+
+ /// <summary>
+ /// Encrypts the plaintext message and writes the encrypted message to the
+ /// specified buffer. The output matches the format of the full nostr message
+ /// for the specified encryption version
+ /// </summary>
+ /// <param name="plaintext">The plaintext data to be encrypted</param>
+ /// <param name="message">The buffer to write the encrypted message data to</param>
+ /// <returns>The number of bytes written to the message buffer</returns>
+ /// <exception cref="NotSupportedException"></exception>
+ public int EncryptMessage(ReadOnlySpan<byte> plaintext, Span<byte> message)
+ {
+ return Version switch
+ {
+ NC_ENC_VERSION_NIP44 => EncryptNip44Message(plaintext, message),
+ _ => throw new NotSupportedException("NIP04 encryption is not supported"),
+ };
+ }
+
+ private int EncryptNip44Message(ReadOnlySpan<byte> plaintext, Span<byte> message)
+ {
+ int minRequiredOutSize = Nip44Util.CalcFinalBufferSize(plaintext.Length);
+
+ ArgumentOutOfRangeException.ThrowIfZero(plaintext.Length, nameof(plaintext));
+ ArgumentOutOfRangeException.ThrowIfLessThan(message.Length, minRequiredOutSize, nameof(message));
+
+ ForwardOnlyWriter<byte> messageWriter = new(message);
+
+ // From spec -> concat(version, nonce, ciphertext, mac)
+ messageWriter.Append(0x02); // Version
+ messageWriter.Append<byte>(Nonce); // nonce
+
+ //Encrypt plaintext and write directly the message buffer
+ int written = EncryptPayload(plaintext, messageWriter.Remaining);
+
+ messageWriter.Advance(written);
+
+ //Append the message mac, it was writen after the encryption operation
+ messageWriter.Append<byte>(Mac);
+
+ return messageWriter.Written;
+ }
+
+ /// <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 EncryptPayload(ReadOnlySpan<byte> plaintext, Span<byte> message)
+ {
+ return Version switch
+ {
+ NC_ENC_VERSION_NIP44 => EncryptNip44(plaintext, message),
+ _ => throw new NotSupportedException("NIP04 encryption is not supported"),
+ };
+ }
+
+ private int EncryptNip44(ReadOnlySpan<byte> plaintext, Span<byte> message)
+ {
+ int payloadSize = GetPayloadBufferSize(plaintext.Length);
+
+ ArgumentOutOfRangeException.ThrowIfZero(plaintext.Length, nameof(plaintext));
+ ArgumentOutOfRangeException.ThrowIfZero(message.Length, nameof(message));
+ ArgumentOutOfRangeException.ThrowIfLessThan(message.Length, payloadSize, nameof(message));
+
+ /*
+ * 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(
+ secretKey: in _fromKey,
+ publicKey: in _toKey,
+ nonce32: in GetNonceRef(),
+ plainText: in ptPayloadBuf.GetReference(),
+ cipherText: ref MemoryMarshal.GetReference(message),
+ size: (uint)payloadSize, //IMPORTANT: Format buffer will pad the buffer to the exact size
+ hmacKeyOut32: ref hmacKeyBuf.GetReference() //Must set the hmac key buffer
+ );
+
+
+ //Compute message mac, key should be set by the encryption operation
+ library.ComputeMac(
+ hmacKey32: in hmacKeyBuf.GetReference(),
+ payload: in MemoryMarshal.GetReference(message),
+ payloadSize: (uint)payloadSize, //Again set exact playload size
+ hmacOut32: ref GetMacRef()
+ );
+
+ //Clear buffers
+ MemoryUtil.InitializeBlock(ref hmacKeyBuf.GetReference(), hmacKeyBuf.IntLength);
+ MemoryUtil.InitializeBlock(ref ptPayloadBuf.GetReference(), ptPayloadBuf.IntLength);
+
+ return payloadSize;
+ }
+
+ private int DecryptNip44Message(ReadOnlySpan<byte> message, Span<byte> plaintext)
+ {
+ //Full Nip44 messages must be at-least 99 bytes in length
+ ArgumentOutOfRangeException.ThrowIfLessThan(message.Length, 99, nameof(message));
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(message.Length, Nip44MaxMessageSize, nameof(message));
+
+ //Message decoder used to get the nip44 message segments
+ Nip44MessageSegments msg = new(message);
+
+ if (msg.Version != 0x02)
+ {
+ return 0;
+ }
+
+ SetNonce(msg.Nonce);
+ SetMac(msg.Mac);
+
+ //Temporary buffer to write decrypted plaintext data to
+ using UnsafeMemoryHandle<byte> plaintextBuffer = MemoryUtil.UnsafeAllocNearestPage<byte>(msg.Ciphertext.Length, true);
+
+ int written = DecryptPayload(msg.Ciphertext, plaintextBuffer.Span);
+
+ Span<byte> ptOut = plaintextBuffer.AsSpan(0, written);
+
+ //Must check message bounds before returning a range
+ if (!Nip44Util.IsValidPlaintextMessage(ptOut))
+ {
+ throw new FormatException("Plaintext data was not properly encrypted because it was not properly formatted or decryption failed");
+ }
+
+ Range msgRange = Nip44Util.GetPlaintextRange(ptOut);
+ Debug.Assert(msgRange.Start.Value > 0);
+ Debug.Assert(msgRange.End.Value > 0);
+
+ int ptLength = msgRange.End.Value - msgRange.Start.Value;
+
+ Debug.Assert(ptLength > 0);
+
+ //Write the wrapped plaintext (unpadded) to the output plaintext buffer
+ MemoryUtil.Memmove(
+ src: in plaintextBuffer.GetReference(),
+ srcOffset: (uint)msgRange.Start.Value,
+ dst: ref MemoryMarshal.GetReference(plaintext),
+ dstOffset: 0,
+ elementCount: (uint)ptLength
+ );
+
+ return ptLength;
+ }
+
+ /// <summary>
+ /// Decrypts a nostr encrypted message in it's full binary from.
+ /// </summary>
+ /// <param name="payload"></param>
+ /// <param name="plaintext"></param>
+ /// <returns>The number of bytes written to the output buffer, or an error code if an error occured during the encryption</returns>
+ /// <exception cref="NotSupportedException"></exception>
+ public int DecryptPayload(ReadOnlySpan<byte> payload, Span<byte> plaintext)
+ {
+ return Version switch
+ {
+ NC_ENC_VERSION_NIP44 => DecryptNip44Payload(payload, plaintext),
+ _ => throw new NotSupportedException("NIP04 encryption is not supported"),
+ };
+ }
+
+ private int DecryptNip44Payload(ReadOnlySpan<byte> message, Span<byte> plaintext)
+ {
+ ArgumentOutOfRangeException.ThrowIfZero(message.Length, nameof(message));
+ ArgumentOutOfRangeException.ThrowIfZero(plaintext.Length, nameof(plaintext));
+
+ //Validate the incoming message for a nip44 message
+ ArgumentOutOfRangeException.ThrowIfLessThan(message.Length, 32, nameof(message));
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(message.Length, Nip44MaxMessageSize, nameof(message));
+
+ //Plaintext buffer must be large enough to hold the decrypted message
+ ArgumentOutOfRangeException.ThrowIfLessThan(plaintext.Length, message.Length, nameof(plaintext));
+
+ bool macValid = library.VerifyMac(
+ in _fromKey,
+ in _toKey,
+ nonce32: in GetNonceRef(),
+ mac32: in GetMacRef(),
+ payload: ref MemoryMarshal.GetReference(message),
+ (uint)message.Length
+ );
+
+ if (!macValid)
+ {
+ throw new AuthenticationException("Message MAC is invalid");
+ }
+
+ library.DecryptNip44(
+ in _fromKey,
+ in _toKey,
+ nonce32: in GetNonceRef(),
+ cipherText: in MemoryMarshal.GetReference(message),
+ plainText: ref MemoryMarshal.GetReference(plaintext),
+ (uint)message.Length
+ );
+
+ //Return the number of bytes written to the output buffer
+ return message.Length;
+ }
+
+ private unsafe ref byte GetNonceRef()
+ {
+ Debug.Assert(NC_ENCRYPTION_NONCE_SIZE == sizeof(Buffer32));
+ return ref Unsafe.As<Buffer32, byte>(ref _nonce32);
+ }
+
+ private unsafe ref byte GetMacRef()
+ {
+ Debug.Assert(NC_ENCRYPTION_MAC_SIZE == sizeof(Buffer32));
+ return ref Unsafe.As<Buffer32, byte>(ref _mac32);
+ }
+
+ protected override void Free()
+ {
+ //Zero all internal memory
+ MemoryUtil.ZeroStruct(ref _fromKey);
+ MemoryUtil.ZeroStruct(ref _toKey);
+ MemoryUtil.ZeroStruct(ref _nonce32);
+ MemoryUtil.ZeroStruct(ref _mac32);
+ }
+
+ /// <summary>
+ /// Initializes a new <see cref="NostrMessageCipher"/> 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 NostrMessageCipher CreateNip44Cipher(INostrCrypto lib)
+ => new(lib, NCNip44EncryptionVersion.Instance);
+
+
+ [StructLayout(LayoutKind.Sequential, Size = 32)]
+ unsafe struct Buffer32
+ {
+ fixed byte value[32];
+ }
+ }
+
+}
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/tests/LibNoscryptTests.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/tests/LibNoscryptTests.cs
index b55e9ff..73a62d9 100644
--- a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/tests/LibNoscryptTests.cs
+++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/tests/LibNoscryptTests.cs
@@ -3,10 +3,10 @@
using System;
using System.Text;
using System.Text.Json;
-using System.Runtime.CompilerServices;
using VNLib.Hashing;
using VNLib.Utils.Memory;
+using VNLib.Utils.Extensions;
namespace VNLib.Utils.Cryptography.Noscrypt.Tests
{
@@ -14,7 +14,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Tests
public class LibNoscryptTests : IDisposable
{
- const string NoscryptLibWinDebug = @"../../../../../../../out/build/x64-debug/noscrypt.dll";
+ const string NoscryptLibWinDebug = @"../../../../../../../out/build/x64-debug/Debug/noscrypt.dll";
//Keys generated using npx noskey package
@@ -27,14 +27,14 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Tests
const string Nip44VectorTestFile = "nip44.vectors.json";
#nullable disable
- private LibNoscrypt _testLib;
+ private NoscryptLibrary _testLib;
private JsonDocument _testVectors;
#nullable enable
[TestInitialize]
public void Initialize()
{
- _testLib = LibNoscrypt.Load(NoscryptLibWinDebug);
+ _testLib = NoscryptLibrary.Load(NoscryptLibWinDebug);
_testVectors = JsonDocument.Parse(File.ReadAllText(Nip44VectorTestFile));
}
@@ -126,11 +126,11 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Tests
//noThrow (its a bad sec key but it should not throw)
crypto.ValidateSecretKey(ref secKey);
- Assert.ThrowsException<ArgumentNullException>(() => crypto.ValidateSecretKey(ref Unsafe.NullRef<NCSecretKey>()));
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.ValidateSecretKey(ref NCSecretKey.NullRef));
//public key
- Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(ref Unsafe.NullRef<NCSecretKey>(), ref pubKey));
- Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(in secKey, ref Unsafe.NullRef<NCPublicKey>()));
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(ref NCSecretKey.NullRef, ref pubKey));
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(in secKey, ref NCPublicKey.NullRef));
}
[TestMethod()]
@@ -159,48 +159,41 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Tests
{
using NostrCrypto nc = _testLib.InitializeCrypto(MemoryUtil.Shared, RandomHash.GetRandomBytes(32));
- Span<byte> macOut32 = stackalloc byte[32];
+ using NostrMessageCipher cipher = NostrMessageCipher.CreateNip44Cipher(nc);
foreach (EncryptionVector v in GetEncryptionVectors())
- {
- using NostrEncryptedMessage msg = NostrEncryptedMessage.CreateNip44Cipher(nc);
+ {
ReadOnlySpan<byte> secKey1 = Convert.FromHexString(v.sec1);
ReadOnlySpan<byte> secKey2 = Convert.FromHexString(v.sec2);
ReadOnlySpan<byte> plainText = Encoding.UTF8.GetBytes(v.plaintext);
ReadOnlySpan<byte> nonce = Convert.FromHexString(v.nonce);
ReadOnlySpan<byte> message = Convert.FromBase64String(v.payload);
- ReadOnlySpan<byte> conversationKey = Convert.FromHexString(v.conversation_key);
-
- Nip44Message nip44Message = new(message);
-
- int ptSize = msg.GetOutputBufferSize(plainText.Length);
-
- Assert.AreEqual<int>(nip44Message.Ciphertext.Length, ptSize);
- Assert.AreEqual<byte>(nip44Message.Version, 0x02);
- Assert.IsTrue(nonce.SequenceEqual(nip44Message.Nonce));
NCPublicKey pub2;
//Recover public keys
nc.GetPublicKey(in NCUtil.AsSecretKey(secKey2), ref pub2);
- Span<byte> actualCiphertext = new byte[ptSize + 32];
+ int outBufferSize = cipher.GetMessageBufferSize(plainText.Length);
+
+ Span<byte> encryptedNote = new byte[outBufferSize];
- msg.SetSecretKey(secKey1)
+ cipher.SetSecretKey(secKey1)
.SetPublicKey(in pub2)
.SetNonce(nonce);
- int written = msg.EncryptMessage(plainText, actualCiphertext, macOut32);
+ int written = cipher.EncryptMessage(plainText, encryptedNote);
+ Assert.IsTrue(written > 0);
- actualCiphertext = actualCiphertext[..written];
+ encryptedNote = encryptedNote[..written];
//Make sure the cipher text matches the expected payload
- if (!actualCiphertext.SequenceEqual(nip44Message.Ciphertext))
+ if (!encryptedNote.SequenceEqual(message))
{
Console.WriteLine($"Input data: {v.plaintext}");
- Console.WriteLine($" \n{Convert.ToHexString(actualCiphertext)}\n{Convert.ToHexString(nip44Message.Ciphertext)}");
- Assert.Fail($"Cipher text does not match expected payload");
+ Console.WriteLine($" \n{Convert.ToHexString(encryptedNote)}\n{Convert.ToHexString(message)}");
+ Assert.Fail($"Cipher text does not match expected message");
}
}
}
@@ -216,7 +209,7 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Tests
ReadOnlySpan<byte> secKey2 = Convert.FromHexString(v.sec2);
ReadOnlySpan<byte> message = Convert.FromBase64String(v.payload);
- Nip44Message nip44Message = new(message);
+ Nip44MessageSegments nip44Message = new(message);
Assert.AreEqual<byte>(nip44Message.Version, 0x02);
NCPublicKey pub2;
@@ -277,9 +270,11 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Tests
[TestMethod()]
public void CorrectDecryptionTest()
{
- using NostrCrypto nc = _testLib.InitializeCrypto(MemoryUtil.Shared, RandomHash.GetRandomBytes(32));
+ using NostrCrypto nc = _testLib.InitializeCrypto(MemoryUtil.Shared, NcFallbackRandom.Shared);
- Span<byte> hmacKeyOut = stackalloc byte[LibNoscrypt.NC_HMAC_KEY_SIZE];
+ using NostrMessageCipher msgCipher = NostrMessageCipher.CreateNip44Cipher(nc);
+
+ using IMemoryHandle<byte> ptBuffer = MemoryUtil.SafeAllocNearestPage(1200, false);
foreach (EncryptionVector vector in GetEncryptionVectors())
{
@@ -289,38 +284,30 @@ namespace VNLib.Utils.Cryptography.Noscrypt.Tests
ReadOnlySpan<byte> nonce = Convert.FromHexString(vector.nonce);
ReadOnlySpan<byte> message = Convert.FromBase64String(vector.payload);
- Nip44Message nip44Message = new(message);
-
- Assert.IsTrue(nip44Message.Version == 0x02);
- Assert.IsTrue(nonce.SequenceEqual(nip44Message.Nonce));
-
- NCPublicKey pub1;
+ NCPublicKey pub1 = default;
//Recover public keys
nc.GetPublicKey(in NCUtil.AsSecretKey(secKey1), ref pub1);
-
- Span<byte> plaintextOut = new byte[ Nip44Util.CalcBufferSize(expectedPt.Length) ];
-
- Assert.IsTrue(nip44Message.Ciphertext.Length == plaintextOut.Length);
-
- /*
- * Decrypting messages requires the public key of the sender
- * and the secret key of the receiver
- */
- nc.Decrypt(
- in NCUtil.AsSecretKey(secKey2),
- in pub1,
- nip44Message.Nonce,
- nip44Message.Ciphertext,
- plaintextOut
- );
- ReadOnlySpan<byte> actualPt = Nip44Util.GetPlaintextMessage(plaintextOut);
+ msgCipher.SetPublicKey(in pub1)
+ .SetSecretKey(secKey2);
+
+ int outLen = msgCipher.DecryptMessage(message, ptBuffer.Span);
+
+ Assert.IsTrue(outLen > 0);
- Assert.AreEqual<int>(expectedPt.Length, actualPt.Length);
- Assert.IsTrue(actualPt.SequenceEqual(expectedPt));
+ Span<byte> plaintext = ptBuffer.AsSpan(0, outLen);
- MemoryUtil.InitializeBlock(hmacKeyOut);
+ if (!plaintext.SequenceEqual(expectedPt))
+ {
+ Console.WriteLine($"Input data: {vector.plaintext}");
+ Console.WriteLine($" \n{Convert.ToHexString(plaintext)}\n{Convert.ToHexString(expectedPt)}");
+ Assert.Fail("Decrypted data does not match expected plaintext");
+ }
+ else
+ {
+ Assert.IsTrue(nonce.SequenceEqual(msgCipher.Nonce));
+ }
}
}