aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-03-30 10:53:45 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-03-30 10:53:45 -0400
commit86fdda1fdece6e8dc1e655b81bcd21a852c5d3fc (patch)
tree3dc3f77b0dbc78b5d3ead8b1da675ca9fc55bda3
parentbeb29f571063fc3e5147482a15275d1026aed699 (diff)
test: Fix testing bugs and tests are PASSING!
-rw-r--r--back-end/plugins/nvault/src/NVault.csproj14
-rw-r--r--extension/src/features/permissions.ts13
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs11
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs1
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs3
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs6
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj4
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/Nip44Message.cs36
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs157
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs168
-rw-r--r--lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs224
-rw-r--r--lib/NVault.Crypto.Noscrypt/tests/NVault.Crypto.NoscryptTests.csproj2
-rw-r--r--lib/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj2
13 files changed, 424 insertions, 217 deletions
diff --git a/back-end/plugins/nvault/src/NVault.csproj b/back-end/plugins/nvault/src/NVault.csproj
index 2a51610..57561d8 100644
--- a/back-end/plugins/nvault/src/NVault.csproj
+++ b/back-end/plugins/nvault/src/NVault.csproj
@@ -21,9 +21,9 @@
<ItemGroup>
<PackageReference Include="FluentValidation" Version="11.9.0" />
- <PackageReference Include="VNLib.Plugins.Extensions.Data" Version="0.1.0-ci0050" />
- <PackageReference Include="VNLib.Plugins.Extensions.Validation" Version="0.1.0-ci0050" />
- <PackageReference Include="VNLib.Plugins.Extensions.Loading.Sql" Version="0.1.0-ci0050" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Data" Version="0.1.0-ci0053" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Validation" Version="0.1.0-ci0053" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Loading.Sql" Version="0.1.0-ci0053" />
</ItemGroup>
<ItemGroup>
@@ -37,11 +37,9 @@
</None>
</ItemGroup>
- <ItemGroup>
- <None Update="NVault.json">
- <CopyToOutputDirectory>Always</CopyToOutputDirectory>
- </None>
- </ItemGroup>
+ <Target Condition="'$(BuildingInsideVisualStudio)' == true" Name="PostBuild" AfterTargets="PostBuildEvent">
+ <Exec Command="start xcopy &quot;$(TargetDir)&quot; &quot;F:\Programming\VNLib\devplugins\NVault&quot; /E /Y /R" />
+ </Target>
</Project>
diff --git a/extension/src/features/permissions.ts b/extension/src/features/permissions.ts
index 6eaaacb..8ce044e 100644
--- a/extension/src/features/permissions.ts
+++ b/extension/src/features/permissions.ts
@@ -61,7 +61,7 @@ export type MfaUpdateResult = TotpUpdateMessage
export interface PermissionApi extends FeatureApi, Watchable {
getRequests(): Promise<PermissionRequest[]>
allow(requestId: string, addRule: CreateRuleType): Promise<void>
- deny(requestId: string): Promise<void>
+ deny(requestId: string, addRule: CreateRuleType): Promise<void>
clearRequests(): Promise<void>
requestAndWaitResult(request: Partial<PermissionRequest>): Promise<PrStatus>
@@ -155,6 +155,7 @@ const useRuleSet = (slot: Ref<RuleSlot>) => {
const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useRuleSet>) => {
+ //Get the page html for the popup to show
const permPopupUrl = runtime.getURL("src/entries/contentScript/auth-popup.html")
defaults(slot.value, { requests: [] })
@@ -297,7 +298,7 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR
updateRequest(request, addRule)
},
- deny(requestId: string): void {
+ deny(requestId: string, addRule: CreateRuleType): void {
const request = getRequest(requestId)
if (!request) {
throw new Error("Request not found")
@@ -305,7 +306,7 @@ const usePermissions = (slot: Ref<PermissionSlot>, rules: ReturnType<typeof useR
//set denied
(request as Mutable<PermissionRequest>).status = PrStatus.Denied
//update request
- updateRequest(request, CreateRuleType.AllowOnce)
+ updateRequest(request, addRule)
},
clearAll: () => {
@@ -345,8 +346,8 @@ export const usePermissionApi = (): IFeatureExport<AppSettings, PermissionApi> =
getRequests: () => Promise.resolve(permissions.getAll()),
- deny(requestId: string) {
- permissions.deny(requestId)
+ deny(requestId: string, addRule: CreateRuleType) {
+ permissions.deny(requestId, addRule)
return Promise.resolve()
},
@@ -362,7 +363,7 @@ export const usePermissionApi = (): IFeatureExport<AppSettings, PermissionApi> =
}),
async requestAndWaitResult(request: Partial<PermissionRequest>) {
-
+
debugLog("Requesting permission", request)
//push request and show popup only if enabled
diff --git a/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs b/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs
index 625f844..6ca7dea 100644
--- a/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs
@@ -38,6 +38,10 @@ namespace NVault.Crypto.Noscrypt
public readonly NCDecryptDelegate NCDecrypt;
public readonly NCVerifyMacDelegate NCVerifyMac;
+#if DEBUG
+ public readonly NCGetConversationKeyDelegate NCGetConversationKey;
+#endif
+
private FunctionTable(SafeLibraryHandle library)
{
//Load the required high-level api functions
@@ -54,6 +58,10 @@ namespace NVault.Crypto.Noscrypt
NCEncrypt = library.DangerousGetFunction<NCEncryptDelegate>();
NCDecrypt = library.DangerousGetFunction<NCDecryptDelegate>();
NCVerifyMac = library.DangerousGetFunction<NCVerifyMacDelegate>();
+
+#if DEBUG
+ NCGetConversationKey = library.DangerousGetFunction<NCGetConversationKeyDelegate>();
+#endif
}
/// <summary>
@@ -99,5 +107,8 @@ namespace NVault.Crypto.Noscrypt
[SafeMethodName("NCVerifyMac")]
internal delegate NCResult NCVerifyMacDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCMacVerifyArgs* args);
+ [SafeMethodName("NCGetConversationKey")]
+ internal delegate NCResult NCGetConversationKeyDelegate(nint ctx, NCSecretKey* sk, NCPublicKey* pk, byte* keyOut32);
+
}
}
diff --git a/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs b/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
index b155810..996681c 100644
--- a/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
@@ -44,6 +44,7 @@ namespace NVault.Crypto.Noscrypt
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 NCResult NC_SUCCESS = 0;
diff --git a/lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs b/lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs
index 1224fa6..72b43b2 100644
--- a/lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/NCCryptoData.cs
@@ -20,8 +20,7 @@ namespace NVault.Crypto.Noscrypt
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct NCCryptoData
{
- public fixed byte nonce[LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE];
-
+ public byte* nonce;
public void* inputData;
public void* outputData;
public uint dataSize;
diff --git a/lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs b/lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs
index b6a8f8c..d2867f6 100644
--- a/lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs
@@ -13,17 +13,15 @@
// 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 NVault.Crypto.Noscrypt.LibNoscrypt;
-
namespace NVault.Crypto.Noscrypt
{
internal unsafe struct NCMacVerifyArgs
{
/* The message authentication code certifying the Nip44 payload */
- public fixed byte mac[NC_ENCRYPTION_MAC_SIZE];
+ public byte* mac;
/* The nonce used for the original message encryption */
- public fixed byte nonce[NC_ENCRYPTION_NONCE_SIZE];
+ public byte* nonce;
/* The message payload data */
public byte* payload;
diff --git a/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj b/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj
index 7d50f7c..00c2fec 100644
--- a/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj
+++ b/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj
@@ -20,8 +20,8 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0116" />
- <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0116" />
+ <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0118" />
+ <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0118" />
</ItemGroup>
</Project>
diff --git a/lib/NVault.Crypto.Noscrypt/src/Nip44Message.cs b/lib/NVault.Crypto.Noscrypt/src/Nip44Message.cs
new file mode 100644
index 0000000..839d970
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/Nip44Message.cs
@@ -0,0 +1,36 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using System;
+
+namespace NVault.Crypto.Noscrypt
+{
+ public readonly ref struct Nip44Message(ReadOnlySpan<byte> payload)
+ {
+ readonly ReadOnlySpan<byte> _payload = payload;
+
+ public ReadOnlySpan<byte> Payload => _payload;
+
+ public ReadOnlySpan<byte> Nonce => Nip44Util.GetNonceFromPayload(_payload);
+
+ public ReadOnlySpan<byte> Ciphertext => Nip44Util.GetCiphertextFromPayload(_payload);
+
+ public ReadOnlySpan<byte> Mac => Nip44Util.GetMacFromPayload(_payload);
+
+ public ReadOnlySpan<byte> NonceAndCiphertext => Nip44Util.GetNonceAndCiphertext(_payload);
+
+ public byte Version => Nip44Util.GetMessageVersion(_payload);
+ }
+}
diff --git a/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs b/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs
index 5ea2e7e..1f3248b 100644
--- a/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs
@@ -52,7 +52,7 @@ namespace NVault.Crypto.Noscrypt
int chunk = nexPower <= 256 ? 32 : nexPower / 8;
- return chunk * ((int)Math.Floor((double)((minSize - 1) / chunk)) + 1);
+ return (chunk * ((int)Math.Floor((double)((minSize - 1) / chunk)) + 1)) + sizeof(ushort);
}
/// <summary>
@@ -84,25 +84,63 @@ namespace NVault.Crypto.Noscrypt
in MemoryMarshal.GetReference(plaintextData),
0,
ref MemoryMarshal.GetReference(output),
- sizeof(ushort),
+ sizeof(ushort),
(uint)plaintextData.Length
);
//We assume the remaining buffer is zeroed out
}
+ public static ReadOnlySpan<byte> GetNonceFromPayload(ReadOnlySpan<byte> message)
+ {
+ //The nonce is 32 bytes following the 1st byte version number of the message
+ return message.Slice(1, NC_ENCRYPTION_NONCE_SIZE);
+ }
+
+ public static ReadOnlySpan<byte> GetCiphertextFromPayload(ReadOnlySpan<byte> message)
+ {
+ //Message is between the nonce and the trailing mac
+ int payloadSize = message.Length - (1 + NC_ENCRYPTION_NONCE_SIZE + NC_ENCRYPTION_MAC_SIZE);
+ return message.Slice(1 + NC_ENCRYPTION_NONCE_SIZE, payloadSize);
+ }
+
+ public static ReadOnlySpan<byte> GetMacFromPayload(ReadOnlySpan<byte> message)
+ {
+ //The mac is the last 32 bytes of the message
+ return message[^NC_ENCRYPTION_MAC_SIZE..];
+ }
+
+ public static ReadOnlySpan<byte> GetNonceAndCiphertext(ReadOnlySpan<byte> message)
+ {
+ //The nonce is 32 bytes following the 1st byte version number of the message
+ return message.Slice(1, NC_ENCRYPTION_NONCE_SIZE + GetCiphertextFromPayload(message).Length);
+ }
+
+ public static byte GetMessageVersion(ReadOnlySpan<byte> message)
+ {
+ //The first byte is the message version
+ return message[0];
+ }
+
+ public static ReadOnlySpan<byte> GetPlaintextMessage(ReadOnlySpan<byte> plaintextPayload)
+ {
+ ushort ptLength = BinaryPrimitives.ReadUInt16BigEndian(plaintextPayload);
+ return plaintextPayload.Slice(sizeof(ushort), ptLength);
+ }
+
+
public static void Encrypt(
- this INostrCrypto lib,
- ref readonly NCSecretKey secretKey,
- ref readonly NCPublicKey publicKey,
- ReadOnlySpan<byte> nonce32,
- ReadOnlySpan<byte> plainText,
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ReadOnlySpan<byte> nonce32,
+ ReadOnlySpan<byte> plainText,
Span<byte> hmackKeyOut32,
Span<byte> cipherText
)
{
ArgumentNullException.ThrowIfNull(lib);
-
+
//Chacha requires the output buffer to be at-least the size of the input buffer
ArgumentOutOfRangeException.ThrowIfGreaterThan(plainText.Length, cipherText.Length, nameof(plainText));
@@ -113,11 +151,11 @@ namespace NVault.Crypto.Noscrypt
//Encrypt data, use the plaintext buffer size as the data size
lib.Encrypt(
- in secretKey,
+ in secretKey,
in publicKey,
in MemoryMarshal.GetReference(nonce32),
in MemoryMarshal.GetReference(plainText),
- ref MemoryMarshal.GetReference(cipherText),
+ ref MemoryMarshal.GetReference(cipherText),
(uint)plainText.Length,
ref MemoryMarshal.GetReference(hmackKeyOut32)
);
@@ -128,7 +166,7 @@ namespace NVault.Crypto.Noscrypt
ref NCSecretKey secretKey,
ref NCPublicKey publicKey,
void* nonce32,
- void* hmacKeyOut32,
+ void* hmacKeyOut32,
void* plainText,
void* cipherText,
uint size
@@ -149,14 +187,14 @@ namespace NVault.Crypto.Noscrypt
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,
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ReadOnlySpan<byte> nonce32,
+ ReadOnlySpan<byte> cipherText,
Span<byte> plainText
)
{
@@ -170,28 +208,28 @@ namespace NVault.Crypto.Noscrypt
//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),
+ 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,
+ 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);
+ ArgumentNullException.ThrowIfNull(plainText);
//Spans are easer to forward references from pointers without screwing up arguments
Decrypt(
@@ -205,11 +243,11 @@ namespace NVault.Crypto.Noscrypt
}
public static bool VerifyMac(
- this INostrCrypto lib,
- ref readonly NCSecretKey secretKey,
- ref readonly NCPublicKey publicKey,
- ReadOnlySpan<byte> nonce32,
- ReadOnlySpan<byte> mac32,
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ReadOnlySpan<byte> nonce32,
+ ReadOnlySpan<byte> mac32,
ReadOnlySpan<byte> payload
)
{
@@ -220,22 +258,22 @@ namespace NVault.Crypto.Noscrypt
//Verify the HMAC
return lib.VerifyMac(
- in secretKey,
- in publicKey,
- in MemoryMarshal.GetReference(nonce32),
- in MemoryMarshal.GetReference(mac32),
- in MemoryMarshal.GetReference(payload),
+ in secretKey,
+ in publicKey,
+ in MemoryMarshal.GetReference(nonce32),
+ in MemoryMarshal.GetReference(mac32),
+ in MemoryMarshal.GetReference(payload),
payload.Length
);
}
public static unsafe bool VerifyMac(
- this INostrCrypto lib,
- ref readonly NCSecretKey secretKey,
- ref readonly NCPublicKey publicKey,
- void* nonce32,
- void* mac32,
- void* payload,
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ void* nonce32,
+ void* mac32,
+ void* payload,
uint payloadSize
)
{
@@ -253,5 +291,34 @@ namespace NVault.Crypto.Noscrypt
new Span<byte>(payload, (int)payloadSize)
);
}
+
+#if DEBUG
+ /*
+ * Conversation key is not meant to be a public api. Callers
+ * should use Encrypt/Decrypt methods to handle encryption.
+ *
+ * This method exists for vector testing purposes only.
+ */
+ public static void GetConverstationKey(
+ this NostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ Span<byte> conversationKeyOut32
+ )
+ {
+ ArgumentNullException.ThrowIfNull(lib);
+ ArgumentOutOfRangeException.ThrowIfNotEqual(conversationKeyOut32.Length, NC_CONVERSATION_KEY_SIZE, nameof(conversationKeyOut32));
+
+ //Get the conversation key
+ lib.GetConverstationKey(
+ in secretKey,
+ in publicKey,
+ ref MemoryMarshal.GetReference(conversationKeyOut32)
+ );
+
+ }
+#endif
+
}
+
}
diff --git a/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs b/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs
index 1bfccb0..30205a1 100644
--- a/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs
@@ -14,13 +14,16 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
using System;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
+using System.Diagnostics.CodeAnalysis;
using VNLib.Utils;
using NCResult = System.Int64;
using static NVault.Crypto.Noscrypt.LibNoscrypt;
+
namespace NVault.Crypto.Noscrypt
{
@@ -41,7 +44,7 @@ namespace NVault.Crypto.Noscrypt
public void Decrypt(
ref readonly NCSecretKey secretKey,
ref readonly NCPublicKey publicKey,
- ref readonly byte nonce,
+ ref readonly byte nonce32,
ref readonly byte cipherText,
ref byte plainText,
uint size
@@ -49,27 +52,22 @@ namespace NVault.Crypto.Noscrypt
{
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,
- NC_ENCRYPTION_NONCE_SIZE
- );
-
+ ThrowIfNullRef(in nonce32, nameof(nonce32));
+
fixed (NCSecretKey* pSecKey = &secretKey)
fixed (NCPublicKey* pPubKey = &publicKey)
- fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText)
+ fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText, pNonce = &nonce32)
{
- //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);
+ NCCryptoData data = new()
+ {
+ //Set input data to the cipher text to decrypt and the output data to the plaintext buffer
+ dataSize = size,
+ inputData = pCipherText,
+ outputData = pTextPtr,
+ nonce = pNonce
+ };
+
+ NCResult result = Functions.NCDecrypt.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, &data);
NCUtil.CheckResult<FunctionTable.NCDecryptDelegate>(result, true);
}
}
@@ -77,37 +75,32 @@ namespace NVault.Crypto.Noscrypt
///<inheritdoc/>
public void Encrypt(
ref readonly NCSecretKey secretKey,
- ref readonly NCPublicKey publicKey,
- ref readonly byte nonce,
- ref readonly byte plainText,
- ref byte cipherText,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte plainText,
+ ref byte cipherText,
uint size,
ref byte hmackKeyOut32
)
{
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,
- NC_ENCRYPTION_NONCE_SIZE
- );
-
+ ThrowIfNullRef(in nonce32, nameof(nonce32));
+
fixed (NCSecretKey* pSecKey = &secretKey)
fixed (NCPublicKey* pPubKey = &publicKey)
- fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText, pHmacKeyOut = &hmackKeyOut32)
+ fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText, pHmacKeyOut = &hmackKeyOut32, pNonce = &nonce32)
{
- //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, pHmacKeyOut, &data);
+ NCCryptoData data = new()
+ {
+ dataSize = size,
+ //Set input data to the plaintext to encrypt and the output data to the cipher text buffer
+ inputData = pTextPtr,
+ outputData = pCipherText,
+ nonce = pNonce
+ };
+
+ NCResult result = Functions.NCEncrypt.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, pHmacKeyOut, &data);
NCUtil.CheckResult<FunctionTable.NCEncryptDelegate>(result, true);
}
}
@@ -117,12 +110,10 @@ namespace NVault.Crypto.Noscrypt
{
Check();
- IntPtr libCtx = context.DangerousGetHandle();
-
fixed(NCSecretKey* pSecKey = &secretKey)
fixed(NCPublicKey* pPubKey = &publicKey)
{
- NCResult result = Functions.NCGetPublicKey.Invoke(libCtx, pSecKey, pPubKey);
+ NCResult result = Functions.NCGetPublicKey.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey);
NCUtil.CheckResult<FunctionTable.NCGetPublicKeyDelegate>(result, true);
}
}
@@ -137,13 +128,11 @@ namespace NVault.Crypto.Noscrypt
)
{
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);
+ NCResult result = Functions.NCSignData.Invoke(context.DangerousGetHandle(), pSecKey, pRandom, pData, dataSize, pSig);
NCUtil.CheckResult<FunctionTable.NCSignDataDelegate>(result, true);
}
}
@@ -178,13 +167,11 @@ namespace NVault.Crypto.Noscrypt
)
{
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);
+ NCResult result = Functions.NCVerifyData.Invoke(context.DangerousGetHandle(), pPubKey, pData, dataSize, pSig);
NCUtil.CheckResult<FunctionTable.NCVerifyDataDelegate>(result, false);
return result == NC_SUCCESS;
@@ -204,50 +191,25 @@ namespace NVault.Crypto.Noscrypt
Check();
//Check pointers we need to use
- if(Unsafe.IsNullRef(in nonce32))
- {
- throw new ArgumentNullException(nameof(nonce32));
- }
-
- if(Unsafe.IsNullRef(in mac32))
- {
- throw new ArgumentNullException(nameof(mac32));
- }
+ ThrowIfNullRef(in nonce32, nameof(nonce32));
+ ThrowIfNullRef(in mac32, nameof(mac32));
+ ThrowIfNullRef(in payload, nameof(payload));
- if(Unsafe.IsNullRef(in payload))
- {
- throw new ArgumentNullException(nameof(payload));
- }
-
- IntPtr libCtx = context.DangerousGetHandle();
-
- NCMacVerifyArgs args = new()
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pPayload = &payload, pMac = &mac32, pNonce = &nonce32)
{
- payloadSize = payloadSize,
- };
-
- //Copy nonce to struct memory buffer
- Unsafe.CopyBlock(
- ref Unsafe.AsRef<byte>(args.nonce),
- in nonce32,
- NC_ENCRYPTION_NONCE_SIZE
- );
- //Copy mac to struct memory buffer
- Unsafe.CopyBlock(
- ref Unsafe.AsRef<byte>(args.mac),
- in mac32,
- NC_ENCRYPTION_MAC_SIZE
- );
-
- fixed(NCSecretKey* pSecKey = &secretKey)
- fixed(NCPublicKey* pPubKey = &publicKey)
- fixed (byte* pPayload = &payload)
- {
- args.payload = pPayload;
+ NCMacVerifyArgs args = new()
+ {
+ payloadSize = payloadSize,
+ payload = pPayload,
+ mac = pMac,
+ nonce = pNonce
+ };
//Exec and bypass failure
- NCResult result = Functions.NCVerifyMac.Invoke(libCtx, pSecKey, pPubKey, &args);
+ NCResult result = Functions.NCVerifyMac.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, &args);
NCUtil.CheckResult<FunctionTable.NCVerifyMacDelegate>(result, false);
//Result should be success if the hmac is valid
@@ -255,7 +217,23 @@ namespace NVault.Crypto.Noscrypt
}
}
-
+ [Conditional("DEBUG")]
+ public void GetConverstationKey(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref byte key32
+ )
+ {
+ Check();
+
+ fixed (NCSecretKey* pSecKey = &secretKey)
+ fixed (NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pKey = &key32)
+ {
+ NCResult result = Functions.NCGetConversationKey.Invoke(context.DangerousGetHandle(), pSecKey, pPubKey, pKey);
+ NCUtil.CheckResult<FunctionTable.NCGetConversationKeyDelegate>(result, true);
+ }
+ }
///<inheritdoc/>
protected override void Free()
@@ -265,5 +243,13 @@ namespace NVault.Crypto.Noscrypt
context.Dispose();
}
}
+
+ private static void ThrowIfNullRef([DoesNotReturnIf(false)] ref readonly byte value, string name)
+ {
+ if(Unsafe.IsNullRef(in value))
+ {
+ throw new ArgumentNullException(name);
+ }
+ }
}
}
diff --git a/lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs b/lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs
index 128751e..a575ab5 100644
--- a/lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs
+++ b/lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs
@@ -2,9 +2,9 @@
using System;
using System.Buffers.Binary;
-using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
+using System.Runtime.CompilerServices;
using VNLib.Hashing;
using VNLib.Utils.Memory;
@@ -14,7 +14,7 @@ namespace NVault.Crypto.Noscrypt.Tests
[TestClass()]
public class LibNoscryptTests : IDisposable
{
-
+
const string NoscryptLibWinDebug = @"../../../../../../../noscrypt/out/build/x64-debug/Debug/noscrypt.dll";
@@ -46,11 +46,9 @@ namespace NVault.Crypto.Noscrypt.Tests
//Random context seed
ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
- using LibNoscrypt library = LibNoscrypt.Load(NoscryptLibWinDebug);
-
//Init new context and interface
- NCContext context = library.Initialize(MemoryUtil.Shared, seed);
-
+ NCContext context = _testLib.Initialize(MemoryUtil.Shared, seed);
+
using NostrCrypto crypto = new(context, true);
}
@@ -69,9 +67,9 @@ namespace NVault.Crypto.Noscrypt.Tests
//Generate the public key
crypto.GetPublicKey(
- in NCUtil.AsSecretKey(secretKey),
+ in NCUtil.AsSecretKey(secretKey),
ref NCUtil.AsPublicKey(publicKey)
- );
+ );
//Make sure the does not contain all zeros
Assert.IsTrue(publicKey.ToArray().Any(b => b != 0));
@@ -134,7 +132,6 @@ namespace NVault.Crypto.Noscrypt.Tests
//public key
Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(ref Unsafe.NullRef<NCSecretKey>(), ref pubKey));
Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(in secKey, ref Unsafe.NullRef<NCPublicKey>()));
-
}
[TestMethod()]
@@ -154,40 +151,35 @@ namespace NVault.Crypto.Noscrypt.Tests
foreach ((int len, int paddedLen) in paddedSizes)
{
- Assert.AreEqual<int>(paddedLen, Nip44Util.CalcBufferSize(len));
+ Assert.AreEqual<int>(paddedLen, Nip44Util.CalcBufferSize(len) - 2);
}
}
[TestMethod()]
- public void EncryptionTest()
+ public void CorrectEncryptionTest()
{
- //get valid encryption test vectors from vector file
- EncryptionVector[] vectors = _testVectors.RootElement.GetProperty("v2")
- .GetProperty("valid")
- .GetProperty("encrypt_decrypt")
- .EnumerateArray()
- .Select(v => v.Deserialize<EncryptionVector>()!)
- .ToArray();
-
using NostrCrypto nc = _testLib.InitializeCrypto(MemoryUtil.Shared, RandomHash.GetRandomBytes(32));
Span<byte> hmacKeyOut = stackalloc byte[LibNoscrypt.NC_HMAC_KEY_SIZE];
- foreach (EncryptionVector v in vectors)
- {
+ foreach (EncryptionVector v in GetEncryptionVectors())
+ {
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> payload = Convert.FromBase64String(v.payload);
+ ReadOnlySpan<byte> message = Convert.FromBase64String(v.payload);
ReadOnlySpan<byte> conversationKey = Convert.FromHexString(v.conversation_key);
+ Nip44Message nip44Message = new(message);
+
//Convert the plaintext data to a valid input buffer
- ReadOnlySpan<byte> pt = ToInputData(plainText);
- Span<byte> cipherText = new byte[pt.Length];
+ ReadOnlySpan<byte> pt = CreateAndFormatPlaintextOutputBuffer(plainText);
+ Span<byte> actualCiphertext = new byte[pt.Length];
- ReadOnlySpan<byte> mac = payload[..32]; //Last 32 bytes of the payload
- ReadOnlySpan<byte> validCipherText = payload.Slice(33, pt.Length);
+ Assert.AreEqual<byte>(nip44Message.Version, 0x02);
+ Assert.IsTrue(nonce.SequenceEqual(nip44Message.Nonce));
+ Assert.AreEqual<int>(nip44Message.Ciphertext.Length, pt.Length);
NCPublicKey pub1;
NCPublicKey pub2;
@@ -196,17 +188,6 @@ namespace NVault.Crypto.Noscrypt.Tests
nc.GetPublicKey(in NCUtil.AsSecretKey(secKey1), ref pub1);
nc.GetPublicKey(in NCUtil.AsSecretKey(secKey2), ref pub2);
- //Verify mac
- bool macValid = nc.VerifyMac(
- in NCUtil.AsSecretKey(secKey1),
- in pub2,
- nonce,
- mac,
- BuildMacData(cipherText, nonce)
- );
-
- Assert.IsTrue(macValid);
-
//Encrypt the plaintext
nc.Encrypt(
in NCUtil.AsSecretKey(secKey1),
@@ -214,45 +195,173 @@ namespace NVault.Crypto.Noscrypt.Tests
nonce,
pt,
hmacKeyOut,
- cipherText
+ actualCiphertext
);
//Make sure the cipher text matches the expected payload
- if (!cipherText.SequenceEqual(validCipherText))
+ if (!actualCiphertext.SequenceEqual(nip44Message.Ciphertext))
{
- Console.WriteLine($"Input data {v.plaintext}");
- Console.WriteLine($"Expected size: {BinaryPrimitives.ReadUInt16BigEndian(validCipherText)}, {plainText.Length}");
- Console.WriteLine($"Actual size {BinaryPrimitives.ReadUInt16BigEndian(pt)}, {plainText.Length}");
- Console.WriteLine($" \n{Convert.ToHexString(cipherText)}.\n{Convert.ToHexString(validCipherText)}");
+ 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");
}
}
+ }
- static byte[] ToInputData(ReadOnlySpan<byte> plaintext)
+ [TestMethod()]
+ public void ValidateMessageMacs()
+ {
+ using NostrCrypto nc = _testLib.InitializeCrypto(MemoryUtil.Shared, RandomHash.GetRandomBytes(32));
+
+ foreach (EncryptionVector v in GetEncryptionVectors())
{
- //Compute the required plaintext buffer size
- int paddedSize = Nip44Util.CalcBufferSize(plaintext.Length + sizeof(ushort));
+ ReadOnlySpan<byte> secKey1 = Convert.FromHexString(v.sec1);
+ ReadOnlySpan<byte> secKey2 = Convert.FromHexString(v.sec2);
+ ReadOnlySpan<byte> message = Convert.FromBase64String(v.payload);
- byte[] data = new byte[paddedSize];
+ Nip44Message nip44Message = new(message);
+ Assert.AreEqual<byte>(nip44Message.Version, 0x02);
- //Format the plaintext buffer
- Nip44Util.FormatBuffer(plaintext, data, true);
+ NCPublicKey pub2;
+
+ //Recover public key2
+ nc.GetPublicKey(in NCUtil.AsSecretKey(secKey2), ref pub2);
- return data;
+ bool success = nc.VerifyMac(
+ in NCUtil.AsSecretKey(secKey1),
+ in pub2,
+ nip44Message.Nonce,
+ nip44Message.Mac,
+ nip44Message.NonceAndCiphertext
+ );
+
+ if (!success)
+ {
+ Console.WriteLine($"Failed to validate MAC for message: {v.payload}");
+ Console.Write($"Mac hex value: {Convert.ToHexString(nip44Message.Mac)}");
+ Assert.Fail("Failed to validate MAC for message");
+ }
}
+ }
+
+ //Converstation key is only available in debug builds
+#if DEBUG
+
+ [TestMethod()]
+ public void ConverstationKeyTest()
+ {
+ using NostrCrypto nc = _testLib.InitializeCrypto(MemoryUtil.Shared, RandomHash.GetRandomBytes(32));
+
+ Span<byte> convKeyOut = stackalloc byte[32];
- static byte[] BuildMacData(ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> nonce)
+ foreach (EncryptionVector v in GetEncryptionVectors())
{
- byte[] macData = new byte[ciphertext.Length + nonce.Length];
+ ReadOnlySpan<byte> secKey1 = Convert.FromHexString(v.sec1);
+ ReadOnlySpan<byte> secKey2 = Convert.FromHexString(v.sec2);
+ ReadOnlySpan<byte> conversationKey = Convert.FromHexString(v.conversation_key);
+
+ NCPublicKey pubkey2 = default;
+ nc.GetPublicKey(in NCUtil.AsSecretKey(secKey2), ref pubkey2);
+
+ nc.GetConverstationKey(
+ in NCUtil.AsSecretKey(secKey1),
+ in pubkey2,
+ convKeyOut
+ );
+
+ Assert.IsTrue(conversationKey.SequenceEqual(convKeyOut));
+
+ MemoryUtil.InitializeBlock(convKeyOut);
+ }
+ }
+#endif
+
+
+ [TestMethod()]
+ public void CorrectDecryptionTest()
+ {
+ using NostrCrypto nc = _testLib.InitializeCrypto(MemoryUtil.Shared, RandomHash.GetRandomBytes(32));
+
+ Span<byte> hmacKeyOut = stackalloc byte[LibNoscrypt.NC_HMAC_KEY_SIZE];
+
+ foreach (EncryptionVector vector in GetEncryptionVectors())
+ {
+ ReadOnlySpan<byte> secKey1 = Convert.FromHexString(vector.sec1);
+ ReadOnlySpan<byte> secKey2 = Convert.FromHexString(vector.sec2);
+ ReadOnlySpan<byte> expectedPt = Encoding.UTF8.GetBytes(vector.plaintext);
+ 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;
+
+ //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
+ );
- //Nonce then cipher text
- nonce.CopyTo(macData);
- ciphertext.CopyTo(macData.AsSpan(nonce.Length));
+ ReadOnlySpan<byte> actualPt = Nip44Util.GetPlaintextMessage(plaintextOut);
- return macData;
+ Assert.AreEqual<int>(expectedPt.Length, actualPt.Length);
+ Assert.IsTrue(actualPt.SequenceEqual(expectedPt));
+
+ MemoryUtil.InitializeBlock(hmacKeyOut);
}
}
+
+ static byte[] CreateAndFormatPlaintextOutputBuffer(ReadOnlySpan<byte> plaintext)
+ {
+ //Compute the required plaintext buffer size
+ int paddedSize = Nip44Util.CalcBufferSize(plaintext.Length);
+
+ byte[] data = new byte[paddedSize];
+
+ //Format the plaintext buffer
+ Nip44Util.FormatBuffer(plaintext, data, true);
+
+ return data;
+ }
+
+ static byte[] BuildMacData(ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> nonce)
+ {
+ byte[] macData = new byte[ciphertext.Length + nonce.Length];
+
+ //Nonce then cipher text
+ nonce.CopyTo(macData);
+ ciphertext.CopyTo(macData.AsSpan(nonce.Length));
+
+ return macData;
+ }
+
+ EncryptionVector[] GetEncryptionVectors()
+ {
+ return _testVectors.RootElement.GetProperty("v2")
+ .GetProperty("valid")
+ .GetProperty("encrypt_decrypt")
+ .EnumerateArray()
+ .Select(v => v.Deserialize<EncryptionVector>()!)
+ .ToArray();
+ }
+
void IDisposable.Dispose()
{
_testLib.Dispose();
@@ -271,7 +380,8 @@ namespace NVault.Crypto.Noscrypt.Tests
public string plaintext { get; set; } = string.Empty;
public string payload { get; set; } = string.Empty;
+
public string conversation_key { get; set; } = string.Empty;
}
}
-} \ No newline at end of file
+}
diff --git a/lib/NVault.Crypto.Noscrypt/tests/NVault.Crypto.NoscryptTests.csproj b/lib/NVault.Crypto.Noscrypt/tests/NVault.Crypto.NoscryptTests.csproj
index ba6d289..ea65755 100644
--- a/lib/NVault.Crypto.Noscrypt/tests/NVault.Crypto.NoscryptTests.csproj
+++ b/lib/NVault.Crypto.Noscrypt/tests/NVault.Crypto.NoscryptTests.csproj
@@ -13,7 +13,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-preview-24080-01" />
<PackageReference Include="MSTest.TestAdapter" Version="3.2.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.2.2" />
- <PackageReference Include="coverlet.collector" Version="6.0.1">
+ <PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
diff --git a/lib/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj b/lib/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj
index 7ccefd6..5143864 100644
--- a/lib/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj
+++ b/lib/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj
@@ -21,7 +21,7 @@
<ItemGroup>
<PackageReference Include="VaultSharp" Version="1.13.0.1" />
- <PackageReference Include="VNLib.Plugins.Extensions.Loading" Version="0.1.0-ci0050" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Loading" Version="0.1.0-ci0053" />
</ItemGroup>
</Project>