aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-02-18 13:47:24 -0500
committerLibravatar vnugent <public@vaughnnugent.com>2024-02-18 13:47:24 -0500
commit8bf42df576f494f89b6058ad2dd8a9d5cdbff0a1 (patch)
tree2723a9befa767f0abce9865b112d5833db88b136
parentd9f4778896407ebe6e1b8bb439d2c175b4a22f45 (diff)
refactor: deprecate secp256k1 native, transition to noscrypt library
-rw-r--r--back-end/plugins/nvault/src/INostrCryptoProvider.cs10
-rw-r--r--back-end/plugins/nvault/src/ManagedCryptoprovider.cs16
-rw-r--r--back-end/plugins/nvault/src/Model/NostrContext.cs2
-rw-r--r--back-end/plugins/nvault/src/Model/NostrEventEntry.cs5
-rw-r--r--back-end/plugins/nvault/src/Model/NostrKeyMeta.cs9
-rw-r--r--back-end/plugins/nvault/src/Model/NostrRelay.cs10
-rw-r--r--back-end/plugins/nvault/src/NVault.csproj9
-rw-r--r--back-end/plugins/nvault/src/NativeSecp256k1Library.cs217
-rw-r--r--back-end/plugins/nvault/src/NoscryptProvider.cs148
-rw-r--r--back-end/plugins/nvault/src/NostrOpProvider.cs91
-rw-r--r--back-end/plugins/nvault/src/Properties/launchSettings.json8
-rw-r--r--extension/src/entries/contentScript/primary/components/PromptPopup.vue22
-rw-r--r--lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/LibNoscryptTests.cs86
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs33
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/INostrCrypto.cs30
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/IRandomSource.cs (renamed from lib/NVault.Crypto.Secp256k1/src/IRandomSource.cs)2
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs4
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCContext.cs2
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCExtensions.cs46
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs34
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NCUtil.cs45
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj4
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs93
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs91
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/Properties/launchSettings.json8
-rw-r--r--lib/NVault.Crypto.Noscrypt/src/UnmanagedRandomSource.cs (renamed from lib/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs)8
-rw-r--r--lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs277
-rw-r--r--lib/NVault.Crypto.Noscrypt/tests/NVault.Crypto.NoscryptTests.csproj (renamed from lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/NVault.Crypto.NoscryptTests.csproj)12
-rw-r--r--lib/NVault.Crypto.Noscrypt/tests/Properties/launchSettings.json (renamed from lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/Properties/launchSettings.json)0
-rw-r--r--lib/NVault.Crypto.Noscrypt/tests/nip44.vectors.json648
-rw-r--r--lib/NVault.Crypto.Secp256k1/src/ContextExtensions.cs274
-rw-r--r--lib/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs284
-rw-r--r--lib/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj27
-rw-r--r--lib/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs56
-rw-r--r--lib/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs76
-rw-r--r--lib/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs58
-rw-r--r--lib/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj2
-rw-r--r--nvault.build.sln19
38 files changed, 1517 insertions, 1249 deletions
diff --git a/back-end/plugins/nvault/src/INostrCryptoProvider.cs b/back-end/plugins/nvault/src/INostrCryptoProvider.cs
index c65d3e9..797e034 100644
--- a/back-end/plugins/nvault/src/INostrCryptoProvider.cs
+++ b/back-end/plugins/nvault/src/INostrCryptoProvider.cs
@@ -28,14 +28,14 @@ namespace NVault.Plugins.Vault
int GetSignatureBufferSize();
/// <summary>
- /// Signs a message digest with the specified private key and writes
+ /// Signs a message with the specified private key and writes
/// the signature to the specified buffer.
/// </summary>
- /// <param name="key"></param>
- /// <param name="digest"></param>
- /// <param name="signatureBuffer"></param>
+ /// <param name="key">The secret key used to sign the message</param>
+ /// <param name="data">The message data to sign</param>
+ /// <param name="signatureBuffer">The signature output buffer</param>
/// <returns>The number of bytes written to the signature buffer, 0 or less if the operation failed</returns>
- ERRNO SignMessage(ReadOnlySpan<byte> key, ReadOnlySpan<byte> digest, Span<byte> signatureBuffer);
+ ERRNO SignData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> signatureBuffer);
/// <summary>
/// Determines the exact size of the buffer required to hold a key pair during
diff --git a/back-end/plugins/nvault/src/ManagedCryptoprovider.cs b/back-end/plugins/nvault/src/ManagedCryptoprovider.cs
index fb15b8f..fe585cc 100644
--- a/back-end/plugins/nvault/src/ManagedCryptoprovider.cs
+++ b/back-end/plugins/nvault/src/ManagedCryptoprovider.cs
@@ -18,11 +18,12 @@ using System.Text.Json;
using System.Collections.Generic;
using VNLib.Utils;
+using VNLib.Utils.Memory;
using VNLib.Utils.Logging;
using VNLib.Plugins;
using VNLib.Plugins.Extensions.Loading;
-using NVault.Crypto.Secp256k1;
+using NVault.Crypto.Noscrypt;
namespace NVault.Plugins.Vault
{
@@ -59,24 +60,27 @@ namespace NVault.Plugins.Vault
string nativePath = config.GetRequiredProperty("lib_crypto", p => p.GetString()!);
//Load native library path
- _provider = NativeSecp256k1Library.LoadLibrary(nativePath, random);
- plugin.Log.Verbose("Loaded native Secp256k1 library from {path}", nativePath);
+ _provider = NoscryptProvider.LoadLibrary(nativePath, random, MemoryUtil.Shared);
+ plugin.Log.Verbose("Loaded native Noscrypt library from {path}", nativePath);
}
///<inheritdoc/>
public int GetSignatureBufferSize() => _provider.GetSignatureBufferSize();
///<inheritdoc/>
- public ERRNO SignMessage(ReadOnlySpan<byte> key, ReadOnlySpan<byte> digest, Span<byte> signatureBuffer) => _provider.SignMessage(key, digest, signatureBuffer);
+ public ERRNO SignData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> digest, Span<byte> signatureBuffer)
+ => _provider.SignData(key, digest, signatureBuffer);
///<inheritdoc/>
public KeyBufferSizes GetKeyBufferSize() => _provider.GetKeyBufferSize();
///<inheritdoc/>
- public bool TryGenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey) => _provider.TryGenerateKeyPair(publicKey, privateKey);
+ public bool TryGenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey)
+ => _provider.TryGenerateKeyPair(publicKey, privateKey);
///<inheritdoc/>
- public bool RecoverPublicKey(ReadOnlySpan<byte> privateKey, Span<byte> pubKey) => _provider.RecoverPublicKey(privateKey, pubKey);
+ public bool RecoverPublicKey(ReadOnlySpan<byte> privateKey, Span<byte> pubKey)
+ => _provider.RecoverPublicKey(privateKey, pubKey);
///<inheritdoc/>
public ERRNO DecryptMessage(ReadOnlySpan<byte> secretKey, ReadOnlySpan<byte> targetKey, ReadOnlySpan<byte> aseIv, ReadOnlySpan<byte> cyphterText, Span<byte> outputBuffer)
diff --git a/back-end/plugins/nvault/src/Model/NostrContext.cs b/back-end/plugins/nvault/src/Model/NostrContext.cs
index 7dc29d0..bfda43e 100644
--- a/back-end/plugins/nvault/src/Model/NostrContext.cs
+++ b/back-end/plugins/nvault/src/Model/NostrContext.cs
@@ -21,7 +21,7 @@ using VNLib.Plugins.Extensions.Loading.Sql;
namespace NVault.Plugins.Vault.Model
{
- internal class NostrContext : TransactionalDbContext, IDbTableDefinition
+ internal class NostrContext : DBContextBase, IDbTableDefinition
{
public DbSet<NostrRelay> NostrRelays { get; set; }
diff --git a/back-end/plugins/nvault/src/Model/NostrEventEntry.cs b/back-end/plugins/nvault/src/Model/NostrEventEntry.cs
index 9fa5aa2..b34fd9d 100644
--- a/back-end/plugins/nvault/src/Model/NostrEventEntry.cs
+++ b/back-end/plugins/nvault/src/Model/NostrEventEntry.cs
@@ -16,6 +16,7 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
+using System.ComponentModel.DataAnnotations;
using VNLib.Plugins.Extensions.Data;
using VNLib.Plugins.Extensions.Data.Abstractions;
@@ -25,6 +26,8 @@ namespace NVault.Plugins.Vault.Model
{
internal sealed class NostrEventEntry : DbModelBase, IUserEntity
{
+ [Key]
+ [MaxLength(64)]
public override string Id { get; set; }
public override DateTime Created { get; set; }
@@ -33,8 +36,10 @@ namespace NVault.Plugins.Vault.Model
//Never share userids with the client
[JsonIgnore]
+ [MaxLength(64)]
public string? UserId { get; set; }
+ [MaxLength(8000)]
public string? EventData { get; set; }
public static NostrEventEntry FromEvent(string userId, NostrEvent @event) => new()
diff --git a/back-end/plugins/nvault/src/Model/NostrKeyMeta.cs b/back-end/plugins/nvault/src/Model/NostrKeyMeta.cs
index 3f0b985..3568b8a 100644
--- a/back-end/plugins/nvault/src/Model/NostrKeyMeta.cs
+++ b/back-end/plugins/nvault/src/Model/NostrKeyMeta.cs
@@ -28,9 +28,11 @@ namespace NVault.Plugins.Vault.Model
internal class NostrKeyMeta : DbModelBase, IUserEntity
{
[Key]
- [MaxLength(50)]
+ [MaxLength(64)]
public override string Id { get; set; }
+
public override DateTime Created { get; set; }
+
public override DateTime LastModified { get; set; }
[JsonPropertyName("PublicKey")]
@@ -41,7 +43,7 @@ namespace NVault.Plugins.Vault.Model
[MaxLength(50)]
public string? UserId { get; set; }
- [MaxLength(100)]
+ [MaxLength(64)]
public string? UserName { get; set; }
public void CleanupFromUser()
@@ -60,8 +62,7 @@ namespace NVault.Plugins.Vault.Model
public void Merge(NostrKeyMeta other)
{
- if (other == null)
- throw new ArgumentNullException(nameof(other));
+ ArgumentNullException.ThrowIfNull(other);
//We only update username and key value
UserName = other.UserName;
diff --git a/back-end/plugins/nvault/src/Model/NostrRelay.cs b/back-end/plugins/nvault/src/Model/NostrRelay.cs
index ab8cea7..f45897f 100644
--- a/back-end/plugins/nvault/src/Model/NostrRelay.cs
+++ b/back-end/plugins/nvault/src/Model/NostrRelay.cs
@@ -31,22 +31,24 @@ namespace NVault.Plugins.Vault.Model
internal class NostrRelay : DbModelBase, IUserEntity
{
[Key]
- [MaxLength(50)]
+ [MaxLength(64)]
[JsonPropertyName("id")]
public override string Id { get; set; }
+
public override DateTime Created { get; set; }
+
public override DateTime LastModified { get; set; }
[JsonPropertyName("url")]
[MaxLength(200)]
- public string Url { get; set; }
+ public string? Url { get; set; }
[JsonPropertyName("flags")]
public NostrRelayFlags Flags { get; set; }
[JsonIgnore]
- [MaxLength(50)]
- public string UserId { get; set; }
+ [MaxLength(64)]
+ public string? UserId { get; set; }
public void CleanupFromUser()
{
diff --git a/back-end/plugins/nvault/src/NVault.csproj b/back-end/plugins/nvault/src/NVault.csproj
index e876eec..343fd46 100644
--- a/back-end/plugins/nvault/src/NVault.csproj
+++ b/back-end/plugins/nvault/src/NVault.csproj
@@ -21,14 +21,13 @@
<ItemGroup>
<PackageReference Include="FluentValidation" Version="11.9.0" />
- <PackageReference Include="VNLib.Plugins.Extensions.Data" Version="0.1.0-ci0047" />
- <PackageReference Include="VNLib.Plugins.Extensions.Validation" Version="0.1.0-ci0047" />
- <PackageReference Include="VNLib.Plugins.Extensions.Loading.Sql" Version="0.1.0-ci0047" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Data" Version="0.1.0-ci0049" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Validation" Version="0.1.0-ci0049" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Loading.Sql" Version="0.1.0-ci0049" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\lib\NVault.Crypto.Noscrypt\src\NVault.Crypto.Noscrypt.csproj" />
- <ProjectReference Include="..\..\..\..\lib\NVault.Crypto.Secp256k1\src\NVault.Crypto.Secp256k1.csproj" />
<ProjectReference Include="..\..\..\..\lib\NVault.VaultExtensions\src\NVault.VaultExtensions.csproj" />
</ItemGroup>
@@ -44,5 +43,5 @@
</None>
</ItemGroup>
-
+
</Project>
diff --git a/back-end/plugins/nvault/src/NativeSecp256k1Library.cs b/back-end/plugins/nvault/src/NativeSecp256k1Library.cs
deleted file mode 100644
index abbafaf..0000000
--- a/back-end/plugins/nvault/src/NativeSecp256k1Library.cs
+++ /dev/null
@@ -1,217 +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.Security.Cryptography;
-using System.Runtime.InteropServices;
-
-using NVault.Crypto.Secp256k1;
-
-using VNLib.Utils;
-using VNLib.Utils.Memory;
-
-namespace NVault.Plugins.Vault
-{
- internal sealed class NativeSecp256k1Library : VnDisposeable, INostrCryptoProvider
- {
- private readonly LibSecp256k1 _lib;
-
- private NativeSecp256k1Library(LibSecp256k1 lib)
- {
- _lib = lib;
- }
-
- /// <summary>
- /// Loads the native library from the specified path
- /// </summary>
- /// <param name="libFilePath">The library path</param>
- /// <param name="random">The optional random source</param>
- /// <returns>The loaded <see cref="NativeSecp256k1Library"/></returns>
- public static NativeSecp256k1Library LoadLibrary(string libFilePath, IRandomSource? random)
- {
- LibSecp256k1 lib = LibSecp256k1.LoadLibrary(libFilePath, DllImportSearchPath.SafeDirectories, random);
- return new(lib);
- }
-
- ///<inheritdoc/>
- public ERRNO DecryptMessage(ReadOnlySpan<byte> secretKey, ReadOnlySpan<byte> targetKey, ReadOnlySpan<byte> aesIv, ReadOnlySpan<byte> ciphterText, Span<byte> outputBuffer)
- {
- Check();
- //Start with new context
- using Secp256k1Context context = _lib.CreateContext();
-
- //Randomize context
- if (!context.Randomize())
- {
- return false;
- }
-
- //Get shared key
- byte[] sharedKeyBuffer = new byte[32];
-
- try
- {
- //Get the Secp256k1 shared key
- context.ComputeSharedKey(sharedKeyBuffer, targetKey, secretKey, HashFuncCallback, IntPtr.Zero);
-
- //Init the AES cipher
- using Aes aes = Aes.Create();
- aes.Key = sharedKeyBuffer;
- aes.Mode = CipherMode.CBC;
-
- return aes.DecryptCbc(ciphterText, aesIv, outputBuffer, PaddingMode.Zeros);
- }
- finally
- {
- //Zero out buffers
- MemoryUtil.InitializeBlock(sharedKeyBuffer);
- }
- }
-
- ///<inheritdoc/>
- public ERRNO EncryptMessage(ReadOnlySpan<byte> secretKey, ReadOnlySpan<byte> targetKey, ReadOnlySpan<byte> aesIv, ReadOnlySpan<byte> plainText, Span<byte> cipherText)
- {
- Check();
- //Start with new context
- using Secp256k1Context context = _lib.CreateContext();
-
- //Randomize context
- if (!context.Randomize())
- {
- return false;
- }
-
- //Get shared key
- byte[] sharedKeyBuffer = new byte[32];
-
- try
- {
- //Get the Secp256k1 shared key
- if(!context.ComputeSharedKey(sharedKeyBuffer, targetKey, secretKey, HashFuncCallback, IntPtr.Zero))
- {
- return ERRNO.E_FAIL;
- }
-
- //Init the AES cipher
- using Aes aes = Aes.Create();
- aes.Key = sharedKeyBuffer;
- aes.Mode = CipherMode.CBC;
-
- return aes.EncryptCbc(plainText, aesIv, cipherText, PaddingMode.Zeros);
- }
- finally
- {
- //Zero out buffers
- MemoryUtil.InitializeBlock(sharedKeyBuffer);
- }
- }
-
- static int HashFuncCallback(in Secp256HashFuncState state)
- {
- //Get function args
- Span<byte> sharedKey = state.GetOutput();
- ReadOnlySpan<byte> xCoord = state.GetXCoordArg();
-
- //Nostr literally just uses the shared x coord as the shared key
- xCoord.CopyTo(sharedKey);
-
- return xCoord.Length;
- }
-
- //Key sizes are constant
- ///<inheritdoc/>
- public KeyBufferSizes GetKeyBufferSize() => new(LibSecp256k1.SecretKeySize, LibSecp256k1.XOnlyPublicKeySize);
-
- //Signature sizes are constant
- ///<inheritdoc/>
- public int GetSignatureBufferSize() => LibSecp256k1.SignatureSize;
-
- ///<inheritdoc/>
- public bool RecoverPublicKey(ReadOnlySpan<byte> privateKey, Span<byte> pubKey)
- {
- Check();
-
- //Init new context
- using Secp256k1Context context = _lib.CreateContext();
-
- //Randomize context
- if (!context.Randomize())
- {
- return false;
- }
-
- //Recover public key from the privatkey
- return context.GeneratePubKeyFromSecret(privateKey, pubKey) == LibSecp256k1.XOnlyPublicKeySize;
- }
-
- ///<inheritdoc/>
- public ERRNO SignMessage(ReadOnlySpan<byte> key, ReadOnlySpan<byte> digest, Span<byte> signatureBuffer)
- {
- Check();
-
- //Init new context
- using Secp256k1Context context = _lib.CreateContext();
-
- //Randomize context
- if (!context.Randomize())
- {
- return false;
- }
-
- //Sign the message
- return context.SchnorSignDigest(key, digest, signatureBuffer);
- }
-
- ///<inheritdoc/>
- public void GetRandomBytes(Span<byte> bytes) => _lib.GetRandomBytes(bytes);
-
- ///<inheritdoc/>
- public bool TryGenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey)
- {
- //Trim buffers to the exact size required to avoid exceptions in the native lib
- privateKey = privateKey[..LibSecp256k1.SecretKeySize];
- publicKey = publicKey[..LibSecp256k1.XOnlyPublicKeySize];
-
- Check();
-
- //Init new context
- using Secp256k1Context context = _lib.CreateContext();
-
- //Randomize context
- if (!context.Randomize())
- {
- return false;
- }
-
- do
- {
- //Create the secret key and verify
- _lib.CreateSecretKey(privateKey);
- }
- while(context.VerifySecretKey(privateKey) == false);
-
- //Create the public key
- return context.GeneratePubKeyFromSecret(privateKey, publicKey) == LibSecp256k1.XOnlyPublicKeySize;
- }
-
- ///<inheritdoc/>
- protected override void Free()
- {
- _lib.Dispose();
- }
-
-
- }
-} \ No newline at end of file
diff --git a/back-end/plugins/nvault/src/NoscryptProvider.cs b/back-end/plugins/nvault/src/NoscryptProvider.cs
new file mode 100644
index 0000000..51774b0
--- /dev/null
+++ b/back-end/plugins/nvault/src/NoscryptProvider.cs
@@ -0,0 +1,148 @@
+// 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 NVault.Crypto.Noscrypt;
+
+using VNLib.Utils;
+using VNLib.Utils.Memory;
+using VNLib.Hashing;
+
+
+namespace NVault.Plugins.Vault
+{
+ internal sealed class NoscryptProvider(IRandomSource random, NostrCrypto noscrypt) : VnDisposeable, INostrCryptoProvider
+ {
+ const int MaxInvalidSecKeyAttempts = 10;
+
+ ///<inheritdoc/>
+ public ERRNO DecryptMessage(ReadOnlySpan<byte> secretKey, ReadOnlySpan<byte> targetKey, ReadOnlySpan<byte> aseIv, ReadOnlySpan<byte> cyphterText, Span<byte> outputBuffer)
+ {
+ return false;
+ }
+
+ ///<inheritdoc/>
+ public ERRNO EncryptMessage(ReadOnlySpan<byte> secretKey, ReadOnlySpan<byte> targetKey, ReadOnlySpan<byte> aesIv, ReadOnlySpan<byte> plainText, Span<byte> cipherText)
+ {
+ return false;
+ }
+
+ ///<inheritdoc/>
+ public KeyBufferSizes GetKeyBufferSize()
+ {
+ return new()
+ {
+ PrivateKeySize = LibNoscrypt.NC_SEC_KEY_SIZE,
+ PublicKeySize = LibNoscrypt.NC_SEC_PUBKEY_SIZE
+ };
+ }
+
+ ///<inheritdoc/>
+ public void GetRandomBytes(Span<byte> bytes) => random.GetRandomBytes(bytes);
+
+ ///<inheritdoc/>
+ public int GetSignatureBufferSize() => LibNoscrypt.NC_SIGNATURE_SIZE;
+
+ ///<inheritdoc/>
+ public bool RecoverPublicKey(ReadOnlySpan<byte> privateKey, Span<byte> pubKey)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(privateKey.Length, LibNoscrypt.NC_SEC_KEY_SIZE, nameof(privateKey));
+ ArgumentOutOfRangeException.ThrowIfLessThan(pubKey.Length, LibNoscrypt.NC_SEC_PUBKEY_SIZE, nameof(pubKey));
+
+ noscrypt.GetPublicKey(
+ in NCUtil.AsSecretKey(privateKey),
+ ref NCUtil.AsPublicKey(pubKey)
+ );
+
+ return true;
+ }
+
+ ///<inheritdoc/>
+ public ERRNO SignData(ReadOnlySpan<byte> key, ReadOnlySpan<byte> data, Span<byte> signatureBuffer)
+ {
+ //Get a buffer of message entropy for libnoscrypt
+ Span<byte> entropy = stackalloc byte[32];
+ random.GetRandomBytes(entropy);
+
+ ref readonly NCSecretKey secretKey = ref NCUtil.AsSecretKey(key);
+ noscrypt.SignData(in secretKey, entropy, data, signatureBuffer);
+
+ return LibNoscrypt.NC_SIGNATURE_SIZE;
+ }
+
+ ///<inheritdoc/>
+ public bool TryGenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(privateKey.Length, LibNoscrypt.NC_SEC_KEY_SIZE, nameof(privateKey));
+ ArgumentOutOfRangeException.ThrowIfLessThan(publicKey.Length, LibNoscrypt.NC_SEC_PUBKEY_SIZE, nameof(publicKey));
+
+ ref readonly NCSecretKey asSecretKey = ref NCUtil.AsSecretKey(privateKey);
+ ref NCPublicKey asPubKey = ref NCUtil.AsPublicKey(publicKey);
+
+ int loopCount = 0;
+
+ random.GetRandomBytes(privateKey);
+
+ //Validate the secret key data
+ while (noscrypt.ValidateSecretKey(in asSecretKey) == false)
+ {
+ if(loopCount++ > MaxInvalidSecKeyAttempts)
+ {
+ return false;
+ }
+
+ //Try to get random key again
+ random.GetRandomBytes(privateKey);
+ }
+
+ //Generate the public key after secret key is validated
+ noscrypt.GetPublicKey(in asSecretKey, ref asPubKey);
+ return true;
+ }
+
+ ///<inheritdoc/>
+ protected override void Free() => noscrypt.Dispose();
+
+
+ public static NoscryptProvider LoadLibrary(string libPath, IRandomSource? random, IUnmangedHeap heap)
+ {
+ //Fallback to platform random crypto
+ random ??= new PlatformRandom();
+
+ LibNoscrypt lib = LibNoscrypt.Load(libPath);
+
+ try
+ {
+ Span<byte> entropy = stackalloc byte[LibNoscrypt.CTX_ENTROPY_SIZE];
+ random.GetRandomBytes(entropy);
+
+ NostrCrypto nostr = lib.InitializeCrypto(heap, entropy);
+ return new NoscryptProvider(random, nostr);
+ }
+ catch
+ {
+ lib.Dispose();
+ throw;
+ }
+ }
+
+ sealed class PlatformRandom() : IRandomSource
+ {
+ ///<inheritdoc/>
+ public void GetRandomBytes(Span<byte> buffer) => RandomHash.GetRandomBytes(buffer);
+ }
+ }
+} \ No newline at end of file
diff --git a/back-end/plugins/nvault/src/NostrOpProvider.cs b/back-end/plugins/nvault/src/NostrOpProvider.cs
index 48ffe93..cc7342a 100644
--- a/back-end/plugins/nvault/src/NostrOpProvider.cs
+++ b/back-end/plugins/nvault/src/NostrOpProvider.cs
@@ -36,28 +36,16 @@ using NVault.Plugins.Vault.Model;
namespace NVault.Plugins.Vault
{
- internal sealed class NostrOpProvider : INostrOperations
+ internal sealed class NostrOpProvider(PluginBase plugin) : INostrOperations
{
public const int AES_IV_SIZE = 16;
public static int IvMaxBase64EncodedSize { get; } = Base64.GetMaxEncodedToUtf8Length(AES_IV_SIZE);
- private static JavaScriptEncoder _encoder { get; } = GetJsEncoder();
+ private static readonly JavaScriptEncoder _encoder = GetJsEncoder();
- readonly IKvVaultStore _vault;
- readonly INostrKeyEncoder _keyEncoder;
- readonly INostrCryptoProvider _cryptoProvider;
-
- public NostrOpProvider(PluginBase plugin)
- {
- //Use base64 key encoder
- _keyEncoder = new Base64KeyEncoder();
-
- //Setup crypto provider
- _cryptoProvider = plugin.CreateService<ManagedCryptoprovider>();
-
- //Get the vault
- _vault = plugin.CreateService<ManagedVaultClient>();
- }
+ private readonly IKvVaultStore _vault = plugin.CreateService<ManagedVaultClient>();
+ private readonly INostrKeyEncoder _keyEncoder = new Base64KeyEncoder();
+ private readonly INostrCryptoProvider _cryptoProvider = plugin.CreateService<ManagedCryptoprovider>();
///<inheritdoc/>
public Task<bool> CreateCredentialAsync(VaultUserScope scope, NostrKeyMeta newKey, CancellationToken cancellation)
@@ -167,56 +155,30 @@ namespace NVault.Plugins.Vault
private bool SignMessage(ReadOnlySpan<char> vaultKey, NostrEvent ev)
{
- //Decode the key
- int keyBufSize = _keyEncoder.GetKeyBufferSize(vaultKey);
-
- //Get the signature buffer size
- int sigBufSize = _cryptoProvider.GetSignatureBufferSize();
-
- //Alloc key buffer
- using IMemoryHandle<byte> buffHandle = MemoryUtil.SafeAllocNearestPage(keyBufSize + sigBufSize, true);
-
- //Wrap the buffer
- EvBuffer buffer = new(buffHandle, keyBufSize, sigBufSize, (int)HashAlg.SHA256);
+ Span<byte> keyBuffer = stackalloc byte[_keyEncoder.GetKeyBufferSize(vaultKey)];
try
{
//Decode the key
- ERRNO keySize = _keyEncoder.DecodeKey(vaultKey, buffer.KeyBuffer);
+ ERRNO keySize = _keyEncoder.DecodeKey(vaultKey, keyBuffer);
if (!keySize)
{
return false;
}
- //Get the event id/event digest from the event
- GetNostrEventId(ev, buffer.HashBuffer);
-
- //Store the event id
- ev.Id = Convert.ToHexString(buffer.HashBuffer).ToLower();
-
- //Sign the event
- ERRNO sigSize = _cryptoProvider.SignMessage(buffer.KeyBuffer[..(int)keySize], buffer.HashBuffer, buffer.SigBuffer);
-
- if (!sigSize)
- {
- return false;
- }
-
- //Store the signature as loewrcase hex
- ev.Signature = Convert.ToHexString(buffer.SigBuffer[..(int)sigSize]).ToLower();
- return true;
+ //Compute the message signature
+ return ComputeMessageSignature(ev, keyBuffer[0.. (int)keySize]);
}
finally
{
- //Zero the key buffer and key
- MemoryUtil.InitializeBlock(buffHandle.Span);
+ //Zero the key before returning
+ MemoryUtil.InitializeBlock(keyBuffer);
}
}
- private void GetNostrEventId(NostrEvent evnt, Span<byte> idHash)
+ private bool ComputeMessageSignature(NostrEvent evnt, ReadOnlySpan<byte> secKey)
{
-
JsonWriterOptions options = new()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
@@ -224,6 +186,9 @@ namespace NVault.Plugins.Vault
MaxDepth = 4
};
+ Span<byte> idHash = stackalloc byte[(int)HashAlg.SHA256];
+ Span<byte> sigBuffer = stackalloc byte[_cryptoProvider.GetSignatureBufferSize()];
+
using VnMemoryStream ms = new();
using (Utf8JsonWriter writer = new(ms, options))
{
@@ -259,8 +224,23 @@ namespace NVault.Plugins.Vault
//Compute the hash
if (!ManagedHash.ComputeHash(ms.AsSpan(), idHash, HashAlg.SHA256))
{
- throw new CryptographicException("Failed to compute event data hash");
+ return false;
+ }
+
+ //Set message id
+ evnt.Id = Convert.ToHexString(idHash).ToLower();
+
+ if(_cryptoProvider.SignData(secKey, ms.AsSpan(), sigBuffer) != sigBuffer.Length)
+ {
+ return false;
}
+
+ //Set the signature as lowercase hex
+ evnt.Signature = Convert.ToHexString(sigBuffer).ToLower();
+
+ MemoryUtil.InitializeBlock(sigBuffer);
+
+ return true;
}
private static JavaScriptEncoder GetJsEncoder()
@@ -442,14 +422,5 @@ namespace NVault.Plugins.Vault
MemoryUtil.InitializeBlock(ivBuffer);
}
}
-
- readonly record struct EvBuffer(IMemoryHandle<byte> Handle, int KeySize, int SigSize, int HashSize)
- {
- public readonly Span<byte> KeyBuffer => Handle.Span[..KeySize];
-
- public readonly Span<byte> SigBuffer => Handle.AsSpan(KeySize, SigSize);
-
- public readonly Span<byte> HashBuffer => Handle.AsSpan(KeySize + SigSize, HashSize);
- }
}
}
diff --git a/back-end/plugins/nvault/src/Properties/launchSettings.json b/back-end/plugins/nvault/src/Properties/launchSettings.json
new file mode 100644
index 0000000..0f138be
--- /dev/null
+++ b/back-end/plugins/nvault/src/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "NVault": {
+ "commandName": "Project",
+ "nativeDebugging": true
+ }
+ }
+} \ No newline at end of file
diff --git a/extension/src/entries/contentScript/primary/components/PromptPopup.vue b/extension/src/entries/contentScript/primary/components/PromptPopup.vue
index b2415b9..54b3778 100644
--- a/extension/src/entries/contentScript/primary/components/PromptPopup.vue
+++ b/extension/src/entries/contentScript/primary/components/PromptPopup.vue
@@ -83,7 +83,6 @@
</div>
</div>
</div>
-
</div>
</template>
@@ -91,6 +90,7 @@
import { ref, shallowRef } from 'vue'
import { storeToRefs } from 'pinia';
import { computed } from 'vue';
+import { get, set } from '@vueuse/core';
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
import { clone, first, isEqual } from 'lodash';
import { useStore } from '../../../store';
@@ -118,7 +118,6 @@ const event = computed<PropmtMessage | undefined>(() => {
return getPromptMessage(pending)
});
-const site = computed(() => new URL(event.value?.origin || "https://example.com").host)
const evData = computed(() => JSON.stringify(event.value || {}, null, 2))
const onSameOrigin = computed(() => isEqual(event.value?.origin, window.location.origin))
@@ -126,6 +125,8 @@ const onSameOrigin = computed(() => isEqual(event.value?.origin, window.location
const showPopup = computed(() => (store.permissions.isPopup || !store.settings.authPopup))
const showPrompt = computed(() => (onSameOrigin.value || store.permissions.isPopup) && event.value)
+const site = computed(() => get(showPrompt) ? new URL(event.value?.origin || "https://example.com").host : "")
+
const close = () => {
if(event.value){
store.plugins.permission.deny(event.value.uuid);
@@ -140,7 +141,7 @@ const allow = () => {
store.plugins.permission.allow(event.value.uuid, allowRuleType.value);
}
//Reset the rule type
- allowRuleType.value = CreateRuleType.AllowOnce
+ set(allowRuleType, CreateRuleType.AllowOnce);
}
//Listen for events
@@ -193,15 +194,16 @@ const getRuleName = (rule: CreateRuleType) => {
}
}
-const createOption = (rule: CreateRuleType): Option<CreateRuleType> => {
- return { name: getRuleName(rule), value: rule }
-}
+const lbOptions = ((): OptionGroup<CreateRuleType>[] => {
-const creatGroup = (name: string, options: Option<CreateRuleType>[]): OptionGroup<CreateRuleType> => {
- return { name, options }
-}
+ const createOption = (rule: CreateRuleType): Option<CreateRuleType> => {
+ return { name: getRuleName(rule), value: rule }
+ }
+
+ const creatGroup = (name: string, options: Option<CreateRuleType>[]): OptionGroup<CreateRuleType> => {
+ return { name, options }
+ }
-const lbOptions = ((): OptionGroup<CreateRuleType>[] => {
return[
creatGroup('Allow', [
createOption(CreateRuleType.AllowOnce),
diff --git a/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/LibNoscryptTests.cs b/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/LibNoscryptTests.cs
deleted file mode 100644
index d254253..0000000
--- a/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/LibNoscryptTests.cs
+++ /dev/null
@@ -1,86 +0,0 @@
-using Microsoft.VisualStudio.TestTools.UnitTesting;
-
-using System.Runtime.CompilerServices;
-
-using VNLib.Hashing;
-using VNLib.Utils.Memory;
-
-namespace NVault.Crypto.Noscrypt.Tests
-{
- [TestClass()]
- public class LibNoscryptTests
- {
- const string NoscryptLib = @"F:\Programming\noscrypt\out\build\x64-debug\Debug\noscrypt.dll";
-
- [TestMethod()]
- public void InitializeTest()
- {
- //Random context seed
- ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
-
- using LibNoscrypt library = LibNoscrypt.Load(NoscryptLib);
-
- //Init new context and interface
- NCContext context = library.Initialize(MemoryUtil.Shared, seed);
-
- using NostrCrypto crypto = new(context, true);
- }
-
- [TestMethod()]
- public void ValidateSecretKeyTest()
- {
- //Random context seed
- ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
- ReadOnlySpan<byte> secretKey = RandomHash.GetRandomBytes(32);
-
- Span<byte> publicKey = stackalloc byte[32];
-
- using LibNoscrypt library = LibNoscrypt.Load(NoscryptLib);
-
- //Init new context and interface
- NCContext context = library.Initialize(MemoryUtil.Shared, seed);
-
- using NostrCrypto crypto = new(context, true);
-
- //validate the secret key
- Assert.IsTrue(crypto.ValidateSecretKey(in NCUtil.AsSecretKey(secretKey)));
-
- //Generate the public key
- crypto.GetPublicKey(
- in NCUtil.AsSecretKey(secretKey),
- ref NCUtil.AsPublicKey(publicKey)
- );
-
- //Make sure the does not contain all zeros
- Assert.IsTrue(publicKey.ToArray().Any(b => b != 0));
- }
-
- //Test argument validations
- [TestMethod()]
- public void TestPublicApiArgValidations()
- {
- //Random context seed
- ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
-
- using LibNoscrypt library = LibNoscrypt.Load(NoscryptLib);
-
- //Init new context and interface
- NCContext context = library.Initialize(MemoryUtil.Shared, seed);
-
- using NostrCrypto crypto = new(context, true);
-
- NCSecretKey secKey = default;
- NCPublicKey pubKey = default;
-
- //noThrow (its a bad sec key but it should not throw)
- crypto.ValidateSecretKey(ref secKey);
- Assert.ThrowsException<ArgumentNullException>(() => crypto.ValidateSecretKey(ref Unsafe.NullRef<NCSecretKey>()));
-
- //public key
- //NoThrow
- Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(ref Unsafe.NullRef<NCSecretKey>(), ref pubKey));
- Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(in secKey, ref Unsafe.NullRef<NCPublicKey>()));
-
- }
- }
-} \ No newline at end of file
diff --git a/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs b/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs
index fe1613a..625f844 100644
--- a/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/FunctionTable.cs
@@ -22,6 +22,7 @@ using NCResult = System.Int64;
namespace NVault.Crypto.Noscrypt
{
+
internal unsafe readonly struct FunctionTable
{
@@ -35,22 +36,24 @@ namespace NVault.Crypto.Noscrypt
public readonly NCVerifyDataDelegate NCVerifyData;
public readonly NCEncryptDelegate NCEncrypt;
public readonly NCDecryptDelegate NCDecrypt;
+ public readonly NCVerifyMacDelegate NCVerifyMac;
private FunctionTable(SafeLibraryHandle library)
{
//Load the required high-level api functions
- NCGetContextStructSize = library.DangerousGetMethod<NCGetContextStructSizeDelegate>();
- NCInitContext = library.DangerousGetMethod<NCInitContextDelegate>();
- NCReInitContext = library.DangerousGetMethod<NCReInitContextDelegate>();
- NCDestroyContext = library.DangerousGetMethod<NCDestroyContextDelegate>();
- NCGetPublicKey = library.DangerousGetMethod<NCGetPublicKeyDelegate>();
- NCValidateSecretKey = library.DangerousGetMethod<NCValidateSecretKeyDelegate>();
- NCSignData = library.DangerousGetMethod<NCSignDataDelegate>();
- NCVerifyData = library.DangerousGetMethod<NCVerifyDataDelegate>();
- NCSignData = library.DangerousGetMethod<NCSignDataDelegate>();
- NCVerifyData = library.DangerousGetMethod<NCVerifyDataDelegate>();
- NCEncrypt = library.DangerousGetMethod<NCEncryptDelegate>();
- NCDecrypt = library.DangerousGetMethod<NCDecryptDelegate>();
+ NCGetContextStructSize = library.DangerousGetFunction<NCGetContextStructSizeDelegate>();
+ NCInitContext = library.DangerousGetFunction<NCInitContextDelegate>();
+ NCReInitContext = library.DangerousGetFunction<NCReInitContextDelegate>();
+ NCDestroyContext = library.DangerousGetFunction<NCDestroyContextDelegate>();
+ NCGetPublicKey = library.DangerousGetFunction<NCGetPublicKeyDelegate>();
+ NCValidateSecretKey = library.DangerousGetFunction<NCValidateSecretKeyDelegate>();
+ NCSignData = library.DangerousGetFunction<NCSignDataDelegate>();
+ NCVerifyData = library.DangerousGetFunction<NCVerifyDataDelegate>();
+ NCSignData = library.DangerousGetFunction<NCSignDataDelegate>();
+ NCVerifyData = library.DangerousGetFunction<NCVerifyDataDelegate>();
+ NCEncrypt = library.DangerousGetFunction<NCEncryptDelegate>();
+ NCDecrypt = library.DangerousGetFunction<NCDecryptDelegate>();
+ NCVerifyMac = library.DangerousGetFunction<NCVerifyMacDelegate>();
}
/// <summary>
@@ -88,9 +91,13 @@ namespace NVault.Crypto.Noscrypt
internal delegate NCResult NCVerifyDataDelegate(IntPtr ctx, NCPublicKey* sk, byte* data, nint dataSize, byte* sig64);
[SafeMethodName("NCEncrypt")]
- internal delegate NCResult NCEncryptDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCCryptoData* data);
+ internal delegate NCResult NCEncryptDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, byte* hmacKeyOut32, NCCryptoData* data);
[SafeMethodName("NCDecrypt")]
internal delegate NCResult NCDecryptDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCCryptoData* data);
+
+ [SafeMethodName("NCVerifyMac")]
+ internal delegate NCResult NCVerifyMacDelegate(IntPtr ctx, NCSecretKey* sk, NCPublicKey* pk, NCMacVerifyArgs* args);
+
}
}
diff --git a/lib/NVault.Crypto.Noscrypt/src/INostrCrypto.cs b/lib/NVault.Crypto.Noscrypt/src/INostrCrypto.cs
index 11be0d0..ad4eadb 100644
--- a/lib/NVault.Crypto.Noscrypt/src/INostrCrypto.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/INostrCrypto.cs
@@ -23,10 +23,34 @@ namespace NVault.Crypto.Noscrypt
void SignData(ref readonly NCSecretKey secretKey, ref readonly byte random32, ref readonly byte data, nint dataSize, ref byte sig64);
- void VerifyData(ref readonly NCPublicKey pubKey, ref readonly byte data, nint dataSize, ref byte sig64);
+ bool VerifyData(ref readonly NCPublicKey pubKey, ref readonly byte data, nint dataSize, ref byte sig64);
- void Encrypt(ref readonly NCSecretKey secretKey, ref readonly NCPublicKey publicKey, ref readonly byte nonce, ref readonly byte plainText, ref byte cipherText, uint size);
+ bool VerifyMac(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte mac32,
+ ref readonly byte payload,
+ nint payloadSize
+ );
- void Decrypt(ref readonly NCSecretKey secretKey, ref readonly NCPublicKey publicKey, ref readonly byte nonce, ref readonly byte cipherText, ref byte plainText, uint size);
+ void Encrypt(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce,
+ ref readonly byte plainText,
+ ref byte cipherText,
+ uint size,
+ ref byte hmacKeyOut32
+ );
+
+ void Decrypt(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce,
+ ref readonly byte cipherText,
+ ref byte plainText,
+ uint size
+ );
}
}
diff --git a/lib/NVault.Crypto.Secp256k1/src/IRandomSource.cs b/lib/NVault.Crypto.Noscrypt/src/IRandomSource.cs
index 4e1861d..ae4492a 100644
--- a/lib/NVault.Crypto.Secp256k1/src/IRandomSource.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/IRandomSource.cs
@@ -15,7 +15,7 @@
using System;
-namespace NVault.Crypto.Secp256k1
+namespace NVault.Crypto.Noscrypt
{
/// <summary>
/// Represents a generator for random data, that fills abinary buffer with random bytes
diff --git a/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs b/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
index 4f892b3..b155810 100644
--- a/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/LibNoscrypt.cs
@@ -42,6 +42,8 @@ namespace NVault.Crypto.Noscrypt
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 CTX_ENTROPY_SIZE = 32;
public const NCResult NC_SUCCESS = 0;
@@ -103,7 +105,7 @@ namespace NVault.Crypto.Noscrypt
result = Functions.NCInitContext.Invoke(ctx, p);
}
- NCUtil.CheckResult<FunctionTable.NCInitContextDelegate>(result);
+ NCUtil.CheckResult<FunctionTable.NCInitContextDelegate>(result, true);
Trace.WriteLine($"Initialzied noscrypt context 0x{ctx:x}");
diff --git a/lib/NVault.Crypto.Noscrypt/src/NCContext.cs b/lib/NVault.Crypto.Noscrypt/src/NCContext.cs
index a9bd47d..a4b02e0 100644
--- a/lib/NVault.Crypto.Noscrypt/src/NCContext.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/NCContext.cs
@@ -67,7 +67,7 @@ namespace NVault.Crypto.Noscrypt
fixed (byte* p = &entropy)
{
NCResult result = Library.Functions.NCReInitContext.Invoke(handle, p);
- NCUtil.CheckResult<FunctionTable.NCReInitContextDelegate>(result);
+ NCUtil.CheckResult<FunctionTable.NCReInitContextDelegate>(result, true);
}
}
diff --git a/lib/NVault.Crypto.Noscrypt/src/NCExtensions.cs b/lib/NVault.Crypto.Noscrypt/src/NCExtensions.cs
new file mode 100644
index 0000000..875721f
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/NCExtensions.cs
@@ -0,0 +1,46 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using System;
+using System.Runtime.InteropServices;
+
+using static NVault.Crypto.Noscrypt.LibNoscrypt;
+
+namespace NVault.Crypto.Noscrypt
+{
+ public static class NCExtensions
+ {
+ public static void SignData(
+ this NostrCrypto lib,
+ ref readonly NCSecretKey secKey,
+ ReadOnlySpan<byte> random32,
+ ReadOnlySpan<byte> data,
+ Span<byte> signatureBuffer
+ )
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(signatureBuffer.Length, NC_SIGNATURE_SIZE, nameof(signatureBuffer));
+ ArgumentOutOfRangeException.ThrowIfLessThan(random32.Length, 32, nameof(random32));
+ ArgumentOutOfRangeException.ThrowIfZero(data.Length, nameof(data));
+
+ lib.SignData(
+ in secKey,
+ in MemoryMarshal.GetReference(random32),
+ in MemoryMarshal.GetReference(data),
+ data.Length,
+ ref MemoryMarshal.GetReference(signatureBuffer)
+ );
+ }
+ }
+}
diff --git a/lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs b/lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs
new file mode 100644
index 0000000..b6a8f8c
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/NCMacVerifyArgs.cs
@@ -0,0 +1,34 @@
+// Copyright (C) 2024 Vaughn Nugent
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+using 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];
+
+ /* The nonce used for the original message encryption */
+ public fixed byte nonce[NC_ENCRYPTION_NONCE_SIZE];
+
+ /* The message payload data */
+ public byte* payload;
+
+ /* The size of the payload data */
+ public nint payloadSize;
+ }
+}
diff --git a/lib/NVault.Crypto.Noscrypt/src/NCUtil.cs b/lib/NVault.Crypto.Noscrypt/src/NCUtil.cs
index 61c2068..44c07aa 100644
--- a/lib/NVault.Crypto.Noscrypt/src/NCUtil.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/NCUtil.cs
@@ -18,12 +18,15 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using VNLib.Utils.Memory;
+
using static NVault.Crypto.Noscrypt.LibNoscrypt;
using NCResult = System.Int64;
namespace NVault.Crypto.Noscrypt
{
+
public static class NCUtil
{
/// <summary>
@@ -62,7 +65,7 @@ namespace NVault.Crypto.Noscrypt
/// <exception cref="ArgumentOutOfRangeException"></exception>
public unsafe static ref NCSecretKey AsSecretKey(Span<byte> span)
{
- ArgumentOutOfRangeException.ThrowIfNotEqual(span.Length, sizeof(NCSecretKey), nameof(span));
+ ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCSecretKey), nameof(span));
ref byte asBytes = ref MemoryMarshal.GetReference(span);
return ref Unsafe.As<byte, NCSecretKey>(ref asBytes);
@@ -78,7 +81,7 @@ namespace NVault.Crypto.Noscrypt
/// <exception cref="ArgumentOutOfRangeException"></exception>
public unsafe static ref NCPublicKey AsPublicKey(Span<byte> span)
{
- ArgumentOutOfRangeException.ThrowIfNotEqual(span.Length, sizeof(NCPublicKey), nameof(span));
+ ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCPublicKey), nameof(span));
ref byte asBytes = ref MemoryMarshal.GetReference(span);
return ref Unsafe.As<byte, NCPublicKey>(ref asBytes);
@@ -93,7 +96,7 @@ namespace NVault.Crypto.Noscrypt
/// <exception cref="ArgumentOutOfRangeException"></exception>
public unsafe static ref readonly NCSecretKey AsSecretKey(ReadOnlySpan<byte> span)
{
- ArgumentOutOfRangeException.ThrowIfNotEqual(span.Length, sizeof(NCSecretKey), nameof(span));
+ ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCSecretKey), nameof(span));
ref byte asBytes = ref MemoryMarshal.GetReference(span);
return ref Unsafe.As<byte, NCSecretKey>(ref asBytes);
@@ -108,13 +111,34 @@ namespace NVault.Crypto.Noscrypt
/// <exception cref="ArgumentOutOfRangeException"></exception>
public unsafe static ref readonly NCPublicKey AsPublicKey(ReadOnlySpan<byte> span)
{
- ArgumentOutOfRangeException.ThrowIfNotEqual(span.Length, sizeof(NCPublicKey), nameof(span));
+ ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCPublicKey), nameof(span));
ref byte asBytes = ref MemoryMarshal.GetReference(span);
return ref Unsafe.As<byte, NCPublicKey>(ref asBytes);
}
- internal static void CheckResult<T>(NCResult result) where T : Delegate
+ /// <summary>
+ /// Initializes a new NostrCrypto context wraper directly that owns the internal context.
+ /// This may be done once at app startup and is thread-safe for the rest of the
+ /// application lifetime.
+ /// </summary>
+ /// <param name="library"></param>
+ /// <param name="heap">The heap to allocate the context from</param>
+ /// <param name="entropy32">The random entropy data to initialize the context with</param>
+ /// <returns>The library wrapper handle</returns>
+ public static NostrCrypto InitializeCrypto(this LibNoscrypt library, IUnmangedHeap heap, ReadOnlySpan<byte> entropy32)
+ {
+ ArgumentNullException.ThrowIfNull(library);
+ ArgumentNullException.ThrowIfNull(heap);
+
+ //Initialize the context
+ NCContext context = library.Initialize(heap, entropy32);
+
+ //Create the crypto interface
+ return new NostrCrypto(context, true);
+ }
+
+ internal static void CheckResult<T>(NCResult result, bool raiseOnFailure) where T : Delegate
{
//Only negative values are errors
if (result >= NC_SUCCESS)
@@ -143,11 +167,20 @@ namespace NVault.Crypto.Noscrypt
case E_INVALID_CTX:
throw new InvalidOperationException("The library context object is null or invalid");
case E_OPERATION_FAILED:
- throw new InvalidOperationException("The operation failed for an unknown reason");
+ RaiseOperationFailedException(raiseOnFailure);
+ break;
}
}
+ private static void RaiseOperationFailedException(bool raise)
+ {
+ if (raise)
+ {
+ throw new InvalidOperationException("The operation failed for an unknown reason");
+ }
+ }
+
private static void RaiseNullArgExceptionForArgumentNumber<T>(int argNumber) where T : Delegate
{
//Get delegate parameters
diff --git a/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj b/lib/NVault.Crypto.Noscrypt/src/NVault.Crypto.Noscrypt.csproj
index 1424aee..8b7db16 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-ci0114" />
- <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0114" />
+ <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0115" />
+ <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0115" />
</ItemGroup>
</Project>
diff --git a/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs b/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs
index 18762c0..4eb4236 100644
--- a/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/Nip44Util.cs
@@ -21,6 +21,7 @@ using VNLib.Utils.Memory;
namespace NVault.Crypto.Noscrypt
{
+
/// <summary>
/// Provides a set of utility methods for working with the Noscrypt library
/// </summary>
@@ -32,22 +33,24 @@ namespace NVault.Crypto.Noscrypt
/// </summary>
/// <param name="dataSize">The size (in bytes) of the encoded data to encrypt</param>
/// <returns>The exact size of the padded buffer output</returns>
- public static uint CalcBufferSize(uint dataSize)
+ public static int CalcBufferSize(int dataSize)
{
- //always add leading 2 bytes for the encoded data size
- dataSize += sizeof(ushort);
+ /*
+ * Taken from https://github.com/nostr-protocol/nips/blob/master/44.md
+ *
+ * Not gonna lie, kinda dumb branches. I guess they want to save space
+ * with really tiny messages... Dunno, but whatever RTFM
+ */
//Min message size is 32 bytes
- uint minSize = Math.Max(dataSize, 32);
+ int minSize = Math.Max(dataSize, 32);
- //calculate the next power of 2
- uint nextPow2 = 1;
- while (nextPow2 < minSize)
- {
- nextPow2 <<= 1;
- }
+ //find the next power of 2 that will fit the min size
+ int nexPower = 1 << ((int)Math.Log2(minSize - 1)) + 1;
- return nextPow2;
+ int chunk = nexPower <= 256 ? 32 : nexPower / 8;
+
+ return chunk * ((int)Math.Floor((double)((minSize - 1) / chunk)) + 1);
}
/// <summary>
@@ -77,9 +80,9 @@ namespace NVault.Crypto.Noscrypt
//Copy the plaintext data to the output buffer after the data size
MemoryUtil.Memmove(
ref MemoryMarshal.GetReference(plaintextData),
- sizeof(ushort),
+ 0,
ref MemoryMarshal.GetReference(output),
- 0,
+ sizeof(ushort),
(uint)plaintextData.Length
);
@@ -92,6 +95,7 @@ namespace NVault.Crypto.Noscrypt
ref readonly NCPublicKey publicKey,
ReadOnlySpan<byte> nonce32,
ReadOnlySpan<byte> plainText,
+ Span<byte> hmackKeyOut32,
Span<byte> cipherText
)
{
@@ -101,7 +105,9 @@ namespace NVault.Crypto.Noscrypt
ArgumentOutOfRangeException.ThrowIfGreaterThan(plainText.Length, cipherText.Length, nameof(plainText));
//Nonce must be exactly 32 bytes
- ArgumentOutOfRangeException.ThrowIfNotEqual(nonce32.Length, 32, nameof(nonce32));
+ ArgumentOutOfRangeException.ThrowIfNotEqual(nonce32.Length, LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE, nameof(nonce32));
+
+ ArgumentOutOfRangeException.ThrowIfNotEqual(hmackKeyOut32.Length, LibNoscrypt.NC_HMAC_KEY_SIZE, nameof(hmackKeyOut32));
//Encrypt data, use the plaintext buffer size as the data size
lib.Encrypt(
@@ -110,7 +116,8 @@ namespace NVault.Crypto.Noscrypt
in MemoryMarshal.GetReference(nonce32),
in MemoryMarshal.GetReference(plainText),
ref MemoryMarshal.GetReference(cipherText),
- (uint)plainText.Length
+ (uint)plainText.Length,
+ ref MemoryMarshal.GetReference(hmackKeyOut32)
);
}
@@ -119,6 +126,7 @@ namespace NVault.Crypto.Noscrypt
ref NCSecretKey secretKey,
ref NCPublicKey publicKey,
void* nonce32,
+ void* hmacKeyOut32,
void* plainText,
void* cipherText,
uint size
@@ -133,8 +141,9 @@ namespace NVault.Crypto.Noscrypt
lib,
in secretKey,
in publicKey,
- new Span<byte>(nonce32, 32),
- new Span<byte>(plainText, (int)size),
+ new ReadOnlySpan<byte>(nonce32, 32),
+ new ReadOnlySpan<byte>(plainText, (int)size),
+ new Span<byte>(hmacKeyOut32, 32),
new Span<byte>(cipherText, (int)size)
);
}
@@ -192,5 +201,55 @@ namespace NVault.Crypto.Noscrypt
new Span<byte>(plainText, (int)size)
);
}
+
+ public static bool VerifyMac(
+ this INostrCrypto lib,
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ReadOnlySpan<byte> nonce32,
+ ReadOnlySpan<byte> mac32,
+ ReadOnlySpan<byte> payload
+ )
+ {
+ ArgumentNullException.ThrowIfNull(lib);
+ ArgumentOutOfRangeException.ThrowIfZero(payload.Length, nameof(payload));
+ ArgumentOutOfRangeException.ThrowIfNotEqual(nonce32.Length, LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE, nameof(nonce32));
+ ArgumentOutOfRangeException.ThrowIfNotEqual(mac32.Length, LibNoscrypt.NC_ENCRYPTION_MAC_SIZE, nameof(mac32));
+
+ //Verify the HMAC
+ return lib.VerifyMac(
+ 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,
+ uint payloadSize
+ )
+ {
+ ArgumentNullException.ThrowIfNull(nonce32);
+ ArgumentNullException.ThrowIfNull(mac32);
+ ArgumentNullException.ThrowIfNull(payload);
+
+ //Spans are easer to forward references from pointers without screwing up arguments
+ return VerifyMac(
+ lib,
+ in secretKey,
+ in publicKey,
+ new Span<byte>(nonce32, LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE),
+ new Span<byte>(mac32, LibNoscrypt.NC_ENCRYPTION_MAC_SIZE),
+ new Span<byte>(payload, (int)payloadSize)
+ );
+ }
}
}
diff --git a/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs b/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs
index 6b547de..e4d4574 100644
--- a/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/NostrCrypto.cs
@@ -15,6 +15,7 @@
using System;
using System.Runtime.CompilerServices;
+using System.Xml;
using VNLib.Utils;
@@ -69,7 +70,7 @@ namespace NVault.Crypto.Noscrypt
data.outputData = pTextPtr;
NCResult result = Functions.NCDecrypt.Invoke(libCtx, pSecKey, pPubKey, &data);
- NCUtil.CheckResult<FunctionTable.NCDecryptDelegate>(result);
+ NCUtil.CheckResult<FunctionTable.NCDecryptDelegate>(result, true);
}
}
@@ -80,7 +81,8 @@ namespace NVault.Crypto.Noscrypt
ref readonly byte nonce,
ref readonly byte plainText,
ref byte cipherText,
- uint size
+ uint size,
+ ref byte hmackKeyOut32
)
{
Check();
@@ -94,19 +96,19 @@ namespace NVault.Crypto.Noscrypt
Unsafe.CopyBlock(
ref Unsafe.AsRef<byte>(data.nonce),
in nonce,
- 0
+ LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE
);
fixed (NCSecretKey* pSecKey = &secretKey)
fixed (NCPublicKey* pPubKey = &publicKey)
- fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText)
+ fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText, pHmacKeyOut = &hmackKeyOut32)
{
//Set input data to the plaintext to encrypt and the output data to the cipher text buffer
data.inputData = pTextPtr;
data.outputData = pCipherText;
- NCResult result = Functions.NCEncrypt.Invoke(libCtx, pSecKey, pPubKey, &data);
- NCUtil.CheckResult<FunctionTable.NCEncryptDelegate>(result);
+ NCResult result = Functions.NCEncrypt.Invoke(libCtx, pSecKey, pPubKey, pHmacKeyOut, &data);
+ NCUtil.CheckResult<FunctionTable.NCEncryptDelegate>(result, true);
}
}
@@ -121,7 +123,7 @@ namespace NVault.Crypto.Noscrypt
fixed(NCPublicKey* pPubKey = &publicKey)
{
NCResult result = Functions.NCGetPublicKey.Invoke(libCtx, pSecKey, pPubKey);
- NCUtil.CheckResult<FunctionTable.NCGetPublicKeyDelegate>(result);
+ NCUtil.CheckResult<FunctionTable.NCGetPublicKeyDelegate>(result, true);
}
}
@@ -142,7 +144,7 @@ namespace NVault.Crypto.Noscrypt
fixed(byte* pData = &data, pSig = &sig64, pRandom = &random32)
{
NCResult result = Functions.NCSignData.Invoke(libCtx, pSecKey, pRandom, pData, dataSize, pSig);
- NCUtil.CheckResult<FunctionTable.NCSignDataDelegate>(result);
+ NCUtil.CheckResult<FunctionTable.NCSignDataDelegate>(result, true);
}
}
@@ -160,7 +162,7 @@ namespace NVault.Crypto.Noscrypt
* or a 0 if it is not.
*/
NCResult result = Functions.NCValidateSecretKey.Invoke(libCtx, pSecKey);
- NCUtil.CheckResult<FunctionTable.NCValidateSecretKeyDelegate>(result);
+ NCUtil.CheckResult<FunctionTable.NCValidateSecretKeyDelegate>(result, false);
//Result should be 1 if the secret key is valid
return result == 1;
@@ -168,7 +170,7 @@ namespace NVault.Crypto.Noscrypt
}
///<inheritdoc/>
- public void VerifyData(
+ public bool VerifyData(
ref readonly NCPublicKey pubKey,
ref readonly byte data,
nint dataSize,
@@ -183,10 +185,77 @@ namespace NVault.Crypto.Noscrypt
fixed (byte* pData = &data, pSig = &sig64)
{
NCResult result = Functions.NCVerifyData.Invoke(libCtx, pPubKey, pData, dataSize, pSig);
- NCUtil.CheckResult<FunctionTable.NCVerifyDataDelegate>(result);
+ NCUtil.CheckResult<FunctionTable.NCVerifyDataDelegate>(result, false);
+
+ return result == LibNoscrypt.NC_SUCCESS;
+ }
+ }
+
+ ///<inheritdoc/>
+ public bool VerifyMac(
+ ref readonly NCSecretKey secretKey,
+ ref readonly NCPublicKey publicKey,
+ ref readonly byte nonce32,
+ ref readonly byte mac32,
+ ref readonly byte payload,
+ nint payloadSize
+ )
+ {
+ 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));
+ }
+
+ if(Unsafe.IsNullRef(in payload))
+ {
+ throw new ArgumentNullException(nameof(payload));
+ }
+
+ IntPtr libCtx = context.DangerousGetHandle();
+
+ NCMacVerifyArgs args = new()
+ {
+ payloadSize = payloadSize,
+ };
+
+ //Copy nonce to struct memory buffer
+ Unsafe.CopyBlock(
+ ref Unsafe.AsRef<byte>(args.nonce),
+ in nonce32,
+ LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE
+ );
+
+ //Copy mac to struct memory buffer
+ Unsafe.CopyBlock(
+ ref Unsafe.AsRef<byte>(args.mac),
+ in mac32,
+ LibNoscrypt.NC_ENCRYPTION_MAC_SIZE
+ );
+
+ fixed(NCSecretKey* pSecKey = &secretKey)
+ fixed(NCPublicKey* pPubKey = &publicKey)
+ fixed (byte* pPayload = &payload)
+ {
+ args.payload = pPayload;
+
+ //Exec and bypass failure
+ NCResult result = Functions.NCVerifyMac.Invoke(libCtx, pSecKey, pPubKey, &args);
+ NCUtil.CheckResult<FunctionTable.NCVerifyMacDelegate>(result, false);
+
+ //Result should be success if the hmac is valid
+ return result == LibNoscrypt.NC_SUCCESS;
}
}
+
///<inheritdoc/>
protected override void Free()
diff --git a/lib/NVault.Crypto.Noscrypt/src/Properties/launchSettings.json b/lib/NVault.Crypto.Noscrypt/src/Properties/launchSettings.json
new file mode 100644
index 0000000..0793b2d
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/src/Properties/launchSettings.json
@@ -0,0 +1,8 @@
+{
+ "profiles": {
+ "NVault.Crypto.Noscrypt": {
+ "commandName": "Project",
+ "nativeDebugging": true
+ }
+ }
+} \ No newline at end of file
diff --git a/lib/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs b/lib/NVault.Crypto.Noscrypt/src/UnmanagedRandomSource.cs
index 360de21..2986e33 100644
--- a/lib/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs
+++ b/lib/NVault.Crypto.Noscrypt/src/UnmanagedRandomSource.cs
@@ -20,7 +20,7 @@ using VNLib.Utils;
using VNLib.Utils.Native;
using VNLib.Utils.Extensions;
-namespace NVault.Crypto.Secp256k1
+namespace NVault.Crypto.Noscrypt
{
/// <summary>
@@ -73,8 +73,8 @@ namespace NVault.Crypto.Secp256k1
_library = lib;
//get the method delegate
- _getRandomBytes = lib.DangerousGetMethod<UnmanagedRandomSourceDelegate>(METHOD_NAME);
-
+ _getRandomBytes = lib.DangerousGetFunction<UnmanagedRandomSourceDelegate>(METHOD_NAME);
+
OwnsHandle = ownsHandle;
}
@@ -83,7 +83,7 @@ namespace NVault.Crypto.Secp256k1
_library.ThrowIfClosed();
//Fix buffer and call unmanaged method
- fixed(byte* ptr = &MemoryMarshal.GetReference(buffer))
+ fixed (byte* ptr = &MemoryMarshal.GetReference(buffer))
{
_getRandomBytes(ptr, buffer.Length);
}
diff --git a/lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs b/lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs
new file mode 100644
index 0000000..128751e
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/tests/LibNoscryptTests.cs
@@ -0,0 +1,277 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+using System;
+using System.Buffers.Binary;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Text.Json;
+
+using VNLib.Hashing;
+using VNLib.Utils.Memory;
+
+namespace NVault.Crypto.Noscrypt.Tests
+{
+ [TestClass()]
+ public class LibNoscryptTests : IDisposable
+ {
+
+ const string NoscryptLibWinDebug = @"../../../../../../../noscrypt/out/build/x64-debug/Debug/noscrypt.dll";
+
+
+ //Keys generated using npx noskey package
+ const string TestPrivateKeyHex = "98c642360e7163a66cee5d9a842b252345b6f3f3e21bd3b7635d5e6c20c7ea36";
+ const string TestPublicKeyHex = "0db15182c4ad3418b4fbab75304be7ade9cfa430a21c1c5320c9298f54ea5406";
+
+ const string TestPrivateKeyHex2 = "3032cb8da355f9e72c9a94bbabae80ca99d3a38de1aed094b432a9fe3432e1f2";
+ const string TestPublicKeyHex2 = "421181660af5d39eb95e48a0a66c41ae393ba94ffeca94703ef81afbed724e5a";
+
+ const string Nip44VectorTestFile = "nip44.vectors.json";
+
+#nullable disable
+ private LibNoscrypt _testLib;
+ private JsonDocument _testVectors;
+#nullable enable
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ _testLib = LibNoscrypt.Load(NoscryptLibWinDebug);
+ _testVectors = JsonDocument.Parse(File.ReadAllText(Nip44VectorTestFile));
+ }
+
+
+ [TestMethod()]
+ public void InitializeTest()
+ {
+ //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);
+
+ using NostrCrypto crypto = new(context, true);
+ }
+
+ [TestMethod()]
+ public void ValidateSecretKeyTest()
+ {
+ //Random context seed
+ ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
+ ReadOnlySpan<byte> secretKey = RandomHash.GetRandomBytes(32);
+ Span<byte> publicKey = stackalloc byte[32];
+
+ using NostrCrypto crypto = _testLib.InitializeCrypto(MemoryUtil.Shared, seed);
+
+ //validate the secret key
+ Assert.IsTrue(crypto.ValidateSecretKey(in NCUtil.AsSecretKey(secretKey)));
+
+ //Generate the public key
+ crypto.GetPublicKey(
+ in NCUtil.AsSecretKey(secretKey),
+ ref NCUtil.AsPublicKey(publicKey)
+ );
+
+ //Make sure the does not contain all zeros
+ Assert.IsTrue(publicKey.ToArray().Any(b => b != 0));
+ }
+
+ [TestMethod()]
+ public void TestGetPublicKey()
+ {
+ //Random context seed
+ ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
+
+ using NostrCrypto crypto = _testLib.InitializeCrypto(MemoryUtil.Shared, seed);
+
+ //Test known key 1
+ TestKnownKeys(
+ crypto,
+ Convert.FromHexString(TestPrivateKeyHex),
+ Convert.FromHexString(TestPublicKeyHex)
+ );
+
+ //Test known key 2
+ TestKnownKeys(
+ crypto,
+ Convert.FromHexString(TestPrivateKeyHex2),
+ Convert.FromHexString(TestPublicKeyHex2)
+ );
+
+
+ static void TestKnownKeys(NostrCrypto lib, ReadOnlySpan<byte> knownSec, ReadOnlySpan<byte> kownPub)
+ {
+ NCPublicKey pubKey;
+
+ //Invoke test function
+ lib.GetPublicKey(
+ in NCUtil.AsSecretKey(knownSec),
+ ref pubKey
+ );
+
+ //Make sure known key matches the generated key
+ Assert.IsTrue(pubKey.AsSpan().SequenceEqual(kownPub));
+ }
+ }
+
+ //Test argument validations
+ [TestMethod()]
+ public void TestPublicApiArgValidations()
+ {
+ //Random context seed
+ ReadOnlySpan<byte> seed = RandomHash.GetRandomBytes(32);
+
+ using NostrCrypto crypto = _testLib.InitializeCrypto(MemoryUtil.Shared, seed);
+
+ NCSecretKey secKey = default;
+ NCPublicKey pubKey = default;
+
+ //noThrow (its a bad sec key but it should not throw)
+ crypto.ValidateSecretKey(ref secKey);
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.ValidateSecretKey(ref Unsafe.NullRef<NCSecretKey>()));
+
+ //public key
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(ref Unsafe.NullRef<NCSecretKey>(), ref pubKey));
+ Assert.ThrowsException<ArgumentNullException>(() => crypto.GetPublicKey(in secKey, ref Unsafe.NullRef<NCPublicKey>()));
+
+ }
+
+ [TestMethod()]
+ public void CalcPaddedLenTest()
+ {
+ //Get valid padding test vectors
+ (int, int)[] paddedSizes = _testVectors.RootElement.GetProperty("v2")
+ .GetProperty("valid")
+ .GetProperty("calc_padded_len")
+ .EnumerateArray()
+ .Select(v =>
+ {
+ int[] testVals = v.Deserialize<int[]>()!;
+ return (testVals[0], testVals[1]);
+ }).ToArray();
+
+
+ foreach ((int len, int paddedLen) in paddedSizes)
+ {
+ Assert.AreEqual<int>(paddedLen, Nip44Util.CalcBufferSize(len));
+ }
+ }
+
+ [TestMethod()]
+ public void EncryptionTest()
+ {
+ //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)
+ {
+ 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> conversationKey = Convert.FromHexString(v.conversation_key);
+
+ //Convert the plaintext data to a valid input buffer
+ ReadOnlySpan<byte> pt = ToInputData(plainText);
+ Span<byte> cipherText = new byte[pt.Length];
+
+ ReadOnlySpan<byte> mac = payload[..32]; //Last 32 bytes of the payload
+ ReadOnlySpan<byte> validCipherText = payload.Slice(33, pt.Length);
+
+ NCPublicKey pub1;
+ NCPublicKey pub2;
+
+ //Recover public keys
+ 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),
+ in pub2,
+ nonce,
+ pt,
+ hmacKeyOut,
+ cipherText
+ );
+
+ //Make sure the cipher text matches the expected payload
+ if (!cipherText.SequenceEqual(validCipherText))
+ {
+ 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)}");
+ Assert.Fail($"Cipher text does not match expected payload");
+ }
+ }
+
+ static byte[] ToInputData(ReadOnlySpan<byte> plaintext)
+ {
+ //Compute the required plaintext buffer size
+ int paddedSize = Nip44Util.CalcBufferSize(plaintext.Length + sizeof(ushort));
+
+ 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;
+ }
+ }
+
+ void IDisposable.Dispose()
+ {
+ _testLib.Dispose();
+ _testVectors.Dispose();
+ GC.SuppressFinalize(this);
+ }
+
+ private sealed class EncryptionVector
+ {
+ public string sec1 { get; set; } = string.Empty;
+
+ public string sec2 { get; set; } = string.Empty;
+
+ public string nonce { get; set; } = string.Empty;
+
+ 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/NVault.Crypto.NoscryptTests/NVault.Crypto.NoscryptTests.csproj b/lib/NVault.Crypto.Noscrypt/tests/NVault.Crypto.NoscryptTests.csproj
index d3bbd9c..2bfa5af 100644
--- a/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/NVault.Crypto.NoscryptTests.csproj
+++ b/lib/NVault.Crypto.Noscrypt/tests/NVault.Crypto.NoscryptTests.csproj
@@ -10,9 +10,9 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
- <PackageReference Include="MSTest.TestAdapter" Version="3.0.4" />
- <PackageReference Include="MSTest.TestFramework" Version="3.0.4" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-preview-24080-01" />
+ <PackageReference Include="MSTest.TestAdapter" Version="3.2.1" />
+ <PackageReference Include="MSTest.TestFramework" Version="3.2.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
@@ -20,4 +20,10 @@
<ProjectReference Include="..\src\NVault.Crypto.Noscrypt.csproj" />
</ItemGroup>
+ <ItemGroup>
+ <None Update="nip44.vectors.json">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </None>
+ </ItemGroup>
+
</Project>
diff --git a/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/Properties/launchSettings.json b/lib/NVault.Crypto.Noscrypt/tests/Properties/launchSettings.json
index b3894a7..b3894a7 100644
--- a/lib/NVault.Crypto.Noscrypt/NVault.Crypto.NoscryptTests/Properties/launchSettings.json
+++ b/lib/NVault.Crypto.Noscrypt/tests/Properties/launchSettings.json
diff --git a/lib/NVault.Crypto.Noscrypt/tests/nip44.vectors.json b/lib/NVault.Crypto.Noscrypt/tests/nip44.vectors.json
new file mode 100644
index 0000000..5353660
--- /dev/null
+++ b/lib/NVault.Crypto.Noscrypt/tests/nip44.vectors.json
@@ -0,0 +1,648 @@
+{
+ "v2": {
+ "valid": {
+ "get_conversation_key": [
+ {
+ "sec1": "315e59ff51cb9209768cf7da80791ddcaae56ac9775eb25b6dee1234bc5d2268",
+ "pub2": "c2f9d9948dc8c7c38321e4b85c8558872eafa0641cd269db76848a6073e69133",
+ "conversation_key": "3dfef0ce2a4d80a25e7a328accf73448ef67096f65f79588e358d9a0eb9013f1"
+ },
+ {
+ "sec1": "a1e37752c9fdc1273be53f68c5f74be7c8905728e8de75800b94262f9497c86e",
+ "pub2": "03bb7947065dde12ba991ea045132581d0954f042c84e06d8c00066e23c1a800",
+ "conversation_key": "4d14f36e81b8452128da64fe6f1eae873baae2f444b02c950b90e43553f2178b"
+ },
+ {
+ "sec1": "98a5902fd67518a0c900f0fb62158f278f94a21d6f9d33d30cd3091195500311",
+ "pub2": "aae65c15f98e5e677b5050de82e3aba47a6fe49b3dab7863cf35d9478ba9f7d1",
+ "conversation_key": "9c00b769d5f54d02bf175b7284a1cbd28b6911b06cda6666b2243561ac96bad7"
+ },
+ {
+ "sec1": "86ae5ac8034eb2542ce23ec2f84375655dab7f836836bbd3c54cefe9fdc9c19f",
+ "pub2": "59f90272378089d73f1339710c02e2be6db584e9cdbe86eed3578f0c67c23585",
+ "conversation_key": "19f934aafd3324e8415299b64df42049afaa051c71c98d0aa10e1081f2e3e2ba"
+ },
+ {
+ "sec1": "2528c287fe822421bc0dc4c3615878eb98e8a8c31657616d08b29c00ce209e34",
+ "pub2": "f66ea16104c01a1c532e03f166c5370a22a5505753005a566366097150c6df60",
+ "conversation_key": "c833bbb292956c43366145326d53b955ffb5da4e4998a2d853611841903f5442"
+ },
+ {
+ "sec1": "49808637b2d21129478041813aceb6f2c9d4929cd1303cdaf4fbdbd690905ff2",
+ "pub2": "74d2aab13e97827ea21baf253ad7e39b974bb2498cc747cdb168582a11847b65",
+ "conversation_key": "4bf304d3c8c4608864c0fe03890b90279328cd24a018ffa9eb8f8ccec06b505d"
+ },
+ {
+ "sec1": "af67c382106242c5baabf856efdc0629cc1c5b4061f85b8ceaba52aa7e4b4082",
+ "pub2": "bdaf0001d63e7ec994fad736eab178ee3c2d7cfc925ae29f37d19224486db57b",
+ "conversation_key": "a3a575dd66d45e9379904047ebfb9a7873c471687d0535db00ef2daa24b391db"
+ },
+ {
+ "sec1": "0e44e2d1db3c1717b05ffa0f08d102a09c554a1cbbf678ab158b259a44e682f1",
+ "pub2": "1ffa76c5cc7a836af6914b840483726207cb750889753d7499fb8b76aa8fe0de",
+ "conversation_key": "a39970a667b7f861f100e3827f4adbf6f464e2697686fe1a81aeda817d6b8bdf"
+ },
+ {
+ "sec1": "5fc0070dbd0666dbddc21d788db04050b86ed8b456b080794c2a0c8e33287bb6",
+ "pub2": "31990752f296dd22e146c9e6f152a269d84b241cc95bb3ff8ec341628a54caf0",
+ "conversation_key": "72c21075f4b2349ce01a3e604e02a9ab9f07e35dd07eff746de348b4f3c6365e"
+ },
+ {
+ "sec1": "1b7de0d64d9b12ddbb52ef217a3a7c47c4362ce7ea837d760dad58ab313cba64",
+ "pub2": "24383541dd8083b93d144b431679d70ef4eec10c98fceef1eff08b1d81d4b065",
+ "conversation_key": "dd152a76b44e63d1afd4dfff0785fa07b3e494a9e8401aba31ff925caeb8f5b1"
+ },
+ {
+ "sec1": "df2f560e213ca5fb33b9ecde771c7c0cbd30f1cf43c2c24de54480069d9ab0af",
+ "pub2": "eeea26e552fc8b5e377acaa03e47daa2d7b0c787fac1e0774c9504d9094c430e",
+ "conversation_key": "770519e803b80f411c34aef59c3ca018608842ebf53909c48d35250bd9323af6"
+ },
+ {
+ "sec1": "cffff919fcc07b8003fdc63bc8a00c0f5dc81022c1c927c62c597352190d95b9",
+ "pub2": "eb5c3cca1a968e26684e5b0eb733aecfc844f95a09ac4e126a9e58a4e4902f92",
+ "conversation_key": "46a14ee7e80e439ec75c66f04ad824b53a632b8409a29bbb7c192e43c00bb795"
+ },
+ {
+ "sec1": "64ba5a685e443e881e9094647ddd32db14444bb21aa7986beeba3d1c4673ba0a",
+ "pub2": "50e6a4339fac1f3bf86f2401dd797af43ad45bbf58e0801a7877a3984c77c3c4",
+ "conversation_key": "968b9dbbfcede1664a4ca35a5d3379c064736e87aafbf0b5d114dff710b8a946"
+ },
+ {
+ "sec1": "dd0c31ccce4ec8083f9b75dbf23cc2878e6d1b6baa17713841a2428f69dee91a",
+ "pub2": "b483e84c1339812bed25be55cff959778dfc6edde97ccd9e3649f442472c091b",
+ "conversation_key": "09024503c7bde07eb7865505891c1ea672bf2d9e25e18dd7a7cea6c69bf44b5d"
+ },
+ {
+ "sec1": "af71313b0d95c41e968a172b33ba5ebd19d06cdf8a7a98df80ecf7af4f6f0358",
+ "pub2": "2a5c25266695b461ee2af927a6c44a3c598b8095b0557e9bd7f787067435bc7c",
+ "conversation_key": "fe5155b27c1c4b4e92a933edae23726a04802a7cc354a77ac273c85aa3c97a92"
+ },
+ {
+ "sec1": "6636e8a389f75fe068a03b3edb3ea4a785e2768e3f73f48ffb1fc5e7cb7289dc",
+ "pub2": "514eb2064224b6a5829ea21b6e8f7d3ea15ff8e70e8555010f649eb6e09aec70",
+ "conversation_key": "ff7afacd4d1a6856d37ca5b546890e46e922b508639214991cf8048ddbe9745c"
+ },
+ {
+ "sec1": "94b212f02a3cfb8ad147d52941d3f1dbe1753804458e6645af92c7b2ea791caa",
+ "pub2": "f0cac333231367a04b652a77ab4f8d658b94e86b5a8a0c472c5c7b0d4c6a40cc",
+ "conversation_key": "e292eaf873addfed0a457c6bd16c8effde33d6664265697f69f420ab16f6669b"
+ },
+ {
+ "sec1": "aa61f9734e69ae88e5d4ced5aae881c96f0d7f16cca603d3bed9eec391136da6",
+ "pub2": "4303e5360a884c360221de8606b72dd316da49a37fe51e17ada4f35f671620a6",
+ "conversation_key": "8e7d44fd4767456df1fb61f134092a52fcd6836ebab3b00766e16732683ed848"
+ },
+ {
+ "sec1": "5e914bdac54f3f8e2cba94ee898b33240019297b69e96e70c8a495943a72fc98",
+ "pub2": "5bd097924f606695c59f18ff8fd53c174adbafaaa71b3c0b4144a3e0a474b198",
+ "conversation_key": "f5a0aecf2984bf923c8cd5e7bb8be262d1a8353cb93959434b943a07cf5644bc"
+ },
+ {
+ "sec1": "8b275067add6312ddee064bcdbeb9d17e88aa1df36f430b2cea5cc0413d8278a",
+ "pub2": "65bbbfca819c90c7579f7a82b750a18c858db1afbec8f35b3c1e0e7b5588e9b8",
+ "conversation_key": "2c565e7027eb46038c2263563d7af681697107e975e9914b799d425effd248d6"
+ },
+ {
+ "sec1": "1ac848de312285f85e0f7ec208aac20142a1f453402af9b34ec2ec7a1f9c96fc",
+ "pub2": "45f7318fe96034d23ee3ddc25b77f275cc1dd329664dd51b89f89c4963868e41",
+ "conversation_key": "b56e970e5057a8fd929f8aad9248176b9af87819a708d9ddd56e41d1aec74088"
+ },
+ {
+ "sec1": "295a1cf621de401783d29d0e89036aa1c62d13d9ad307161b4ceb535ba1b40e6",
+ "pub2": "840115ddc7f1034d3b21d8e2103f6cb5ab0b63cf613f4ea6e61ae3d016715cdd",
+ "conversation_key": "b4ee9c0b9b9fef88975773394f0a6f981ca016076143a1bb575b9ff46e804753"
+ },
+ {
+ "sec1": "a28eed0fe977893856ab9667e06ace39f03abbcdb845c329a1981be438ba565d",
+ "pub2": "b0f38b950a5013eba5ab4237f9ed29204a59f3625c71b7e210fec565edfa288c",
+ "conversation_key": "9d3a802b45bc5aeeb3b303e8e18a92ddd353375710a31600d7f5fff8f3a7285b"
+ },
+ {
+ "sec1": "7ab65af72a478c05f5c651bdc4876c74b63d20d04cdbf71741e46978797cd5a4",
+ "pub2": "f1112159161b568a9cb8c9dd6430b526c4204bcc8ce07464b0845b04c041beda",
+ "conversation_key": "943884cddaca5a3fef355e9e7f08a3019b0b66aa63ec90278b0f9fdb64821e79"
+ },
+ {
+ "sec1": "95c79a7b75ba40f2229e85756884c138916f9d103fc8f18acc0877a7cceac9fe",
+ "pub2": "cad76bcbd31ca7bbda184d20cc42f725ed0bb105b13580c41330e03023f0ffb3",
+ "conversation_key": "81c0832a669eea13b4247c40be51ccfd15bb63fcd1bba5b4530ce0e2632f301b"
+ },
+ {
+ "sec1": "baf55cc2febd4d980b4b393972dfc1acf49541e336b56d33d429bce44fa12ec9",
+ "pub2": "0c31cf87fe565766089b64b39460ebbfdedd4a2bc8379be73ad3c0718c912e18",
+ "conversation_key": "37e2344da9ecdf60ae2205d81e89d34b280b0a3f111171af7e4391ded93b8ea6"
+ },
+ {
+ "sec1": "6eeec45acd2ed31693c5256026abf9f072f01c4abb61f51cf64e6956b6dc8907",
+ "pub2": "e501b34ed11f13d816748c0369b0c728e540df3755bab59ed3327339e16ff828",
+ "conversation_key": "afaa141b522ddb27bb880d768903a7f618bb8b6357728cae7fb03af639b946e6"
+ },
+ {
+ "sec1": "261a076a9702af1647fb343c55b3f9a4f1096273002287df0015ba81ce5294df",
+ "pub2": "b2777c863878893ae100fb740c8fab4bebd2bf7be78c761a75593670380a6112",
+ "conversation_key": "76f8d2853de0734e51189ced523c09427c3e46338b9522cd6f74ef5e5b475c74"
+ },
+ {
+ "sec1": "ed3ec71ca406552ea41faec53e19f44b8f90575eda4b7e96380f9cc73c26d6f3",
+ "pub2": "86425951e61f94b62e20cae24184b42e8e17afcf55bafa58645efd0172624fae",
+ "conversation_key": "f7ffc520a3a0e9e9b3c0967325c9bf12707f8e7a03f28b6cd69ae92cf33f7036"
+ },
+ {
+ "sec1": "5a788fc43378d1303ac78639c59a58cb88b08b3859df33193e63a5a3801c722e",
+ "pub2": "a8cba2f87657d229db69bee07850fd6f7a2ed070171a06d006ec3a8ac562cf70",
+ "conversation_key": "7d705a27feeedf78b5c07283362f8e361760d3e9f78adab83e3ae5ce7aeb6409"
+ },
+ {
+ "sec1": "63bffa986e382b0ac8ccc1aa93d18a7aa445116478be6f2453bad1f2d3af2344",
+ "pub2": "b895c70a83e782c1cf84af558d1038e6b211c6f84ede60408f519a293201031d",
+ "conversation_key": "3a3b8f00d4987fc6711d9be64d9c59cf9a709c6c6481c2cde404bcc7a28f174e"
+ },
+ {
+ "sec1": "e4a8bcacbf445fd3721792b939ff58e691cdcba6a8ba67ac3467b45567a03e5c",
+ "pub2": "b54053189e8c9252c6950059c783edb10675d06d20c7b342f73ec9fa6ed39c9d",
+ "conversation_key": "7b3933b4ef8189d347169c7955589fc1cfc01da5239591a08a183ff6694c44ad"
+ },
+ {
+ "sec1": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139",
+ "pub2": "0000000000000000000000000000000000000000000000000000000000000002",
+ "conversation_key": "8b6392dbf2ec6a2b2d5b1477fc2be84d63ef254b667cadd31bd3f444c44ae6ba",
+ "note": "sec1 = n-2, pub2: random, 0x02"
+ },
+ {
+ "sec1": "0000000000000000000000000000000000000000000000000000000000000002",
+ "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdeb",
+ "conversation_key": "be234f46f60a250bef52a5ee34c758800c4ca8e5030bf4cc1a31d37ba2104d43",
+ "note": "sec1 = 2, pub2: rand"
+ },
+ {
+ "sec1": "0000000000000000000000000000000000000000000000000000000000000001",
+ "pub2": "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
+ "conversation_key": "3b4610cb7189beb9cc29eb3716ecc6102f1247e8f3101a03a1787d8908aeb54e",
+ "note": "sec1 == pub2"
+ }
+ ],
+ "get_message_keys": {
+ "conversation_key": "a1a3d60f3470a8612633924e91febf96dc5366ce130f658b1f0fc652c20b3b54",
+ "keys": [
+ {
+ "nonce": "e1e6f880560d6d149ed83dcc7e5861ee62a5ee051f7fde9975fe5d25d2a02d72",
+ "chacha_key": "f145f3bed47cb70dbeaac07f3a3fe683e822b3715edb7c4fe310829014ce7d76",
+ "chacha_nonce": "c4ad129bb01180c0933a160c",
+ "hmac_key": "027c1db445f05e2eee864a0975b0ddef5b7110583c8c192de3732571ca5838c4"
+ },
+ {
+ "nonce": "e1d6d28c46de60168b43d79dacc519698512ec35e8ccb12640fc8e9f26121101",
+ "chacha_key": "e35b88f8d4a8f1606c5082f7a64b100e5d85fcdb2e62aeafbec03fb9e860ad92",
+ "chacha_nonce": "22925e920cee4a50a478be90",
+ "hmac_key": "46a7c55d4283cb0df1d5e29540be67abfe709e3b2e14b7bf9976e6df994ded30"
+ },
+ {
+ "nonce": "cfc13bef512ac9c15951ab00030dfaf2626fdca638dedb35f2993a9eeb85d650",
+ "chacha_key": "020783eb35fdf5b80ef8c75377f4e937efb26bcbad0e61b4190e39939860c4bf",
+ "chacha_nonce": "d3594987af769a52904656ac",
+ "hmac_key": "237ec0ccb6ebd53d179fa8fd319e092acff599ef174c1fdafd499ef2b8dee745"
+ },
+ {
+ "nonce": "ea6eb84cac23c5c1607c334e8bdf66f7977a7e374052327ec28c6906cbe25967",
+ "chacha_key": "ff68db24b34fa62c78ac5ffeeaf19533afaedf651fb6a08384e46787f6ce94be",
+ "chacha_nonce": "50bb859aa2dde938cc49ec7a",
+ "hmac_key": "06ff32e1f7b29753a727d7927b25c2dd175aca47751462d37a2039023ec6b5a6"
+ },
+ {
+ "nonce": "8c2e1dd3792802f1f9f7842e0323e5d52ad7472daf360f26e15f97290173605d",
+ "chacha_key": "2f9daeda8683fdeede81adac247c63cc7671fa817a1fd47352e95d9487989d8b",
+ "chacha_nonce": "400224ba67fc2f1b76736916",
+ "hmac_key": "465c05302aeeb514e41c13ed6405297e261048cfb75a6f851ffa5b445b746e4b"
+ },
+ {
+ "nonce": "05c28bf3d834fa4af8143bf5201a856fa5fac1a3aee58f4c93a764fc2f722367",
+ "chacha_key": "1e3d45777025a035be566d80fd580def73ed6f7c043faec2c8c1c690ad31c110",
+ "chacha_nonce": "021905b1ea3afc17cb9bf96f",
+ "hmac_key": "74a6e481a89dcd130aaeb21060d7ec97ad30f0007d2cae7b1b11256cc70dfb81"
+ },
+ {
+ "nonce": "5e043fb153227866e75a06d60185851bc90273bfb93342f6632a728e18a07a17",
+ "chacha_key": "1ea72c9293841e7737c71567d8120145a58991aaa1c436ef77bf7adb83f882f1",
+ "chacha_nonce": "72f69a5a5f795465cee59da8",
+ "hmac_key": "e9daa1a1e9a266ecaa14e970a84bce3fbbf329079bbccda626582b4e66a0d4c9"
+ },
+ {
+ "nonce": "7be7338eaf06a87e274244847fe7a97f5c6a91f44adc18fcc3e411ad6f786dbf",
+ "chacha_key": "881e7968a1f0c2c80742ee03cd49ea587e13f22699730f1075ade01931582bf6",
+ "chacha_nonce": "6e69be92d61c04a276021565",
+ "hmac_key": "901afe79e74b19967c8829af23617d7d0ffbf1b57190c096855c6a03523a971b"
+ },
+ {
+ "nonce": "94571c8d590905bad7becd892832b472f2aa5212894b6ce96e5ba719c178d976",
+ "chacha_key": "f80873dd48466cb12d46364a97b8705c01b9b4230cb3ec3415a6b9551dc42eef",
+ "chacha_nonce": "3dda53569cfcb7fac1805c35",
+ "hmac_key": "e9fc264345e2839a181affebc27d2f528756e66a5f87b04bf6c5f1997047051e"
+ },
+ {
+ "nonce": "13a6ee974b1fd759135a2c2010e3cdda47081c78e771125e4f0c382f0284a8cb",
+ "chacha_key": "bc5fb403b0bed0d84cf1db872b6522072aece00363178c98ad52178d805fca85",
+ "chacha_nonce": "65064239186e50304cc0f156",
+ "hmac_key": "e872d320dde4ed3487958a8e43b48aabd3ced92bc24bb8ff1ccb57b590d9701a"
+ },
+ {
+ "nonce": "082fecdb85f358367b049b08be0e82627ae1d8edb0f27327ccb593aa2613b814",
+ "chacha_key": "1fbdb1cf6f6ea816349baf697932b36107803de98fcd805ebe9849b8ad0e6a45",
+ "chacha_nonce": "2e605e1d825a3eaeb613db9c",
+ "hmac_key": "fae910f591cf3c7eb538c598583abad33bc0a03085a96ca4ea3a08baf17c0eec"
+ },
+ {
+ "nonce": "4c19020c74932c30ec6b2d8cd0d5bb80bd0fc87da3d8b4859d2fb003810afd03",
+ "chacha_key": "1ab9905a0189e01cda82f843d226a82a03c4f5b6dbea9b22eb9bc953ba1370d4",
+ "chacha_nonce": "cbb2530ea653766e5a37a83a",
+ "hmac_key": "267f68acac01ac7b34b675e36c2cef5e7b7a6b697214add62a491bedd6efc178"
+ },
+ {
+ "nonce": "67723a3381497b149ce24814eddd10c4c41a1e37e75af161930e6b9601afd0ff",
+ "chacha_key": "9ecbd25e7e2e6c97b8c27d376dcc8c5679da96578557e4e21dba3a7ef4e4ac07",
+ "chacha_nonce": "ef649fcf335583e8d45e3c2e",
+ "hmac_key": "04dbbd812fa8226fdb45924c521a62e3d40a9e2b5806c1501efdeba75b006bf1"
+ },
+ {
+ "nonce": "42063fe80b093e8619b1610972b4c3ab9e76c14fd908e642cd4997cafb30f36c",
+ "chacha_key": "211c66531bbcc0efcdd0130f9f1ebc12a769105eb39608994bcb188fa6a73a4a",
+ "chacha_nonce": "67803605a7e5010d0f63f8c8",
+ "hmac_key": "e840e4e8921b57647369d121c5a19310648105dbdd008200ebf0d3b668704ff8"
+ },
+ {
+ "nonce": "b5ac382a4be7ac03b554fe5f3043577b47ea2cd7cfc7e9ca010b1ffbb5cf1a58",
+ "chacha_key": "b3b5f14f10074244ee42a3837a54309f33981c7232a8b16921e815e1f7d1bb77",
+ "chacha_nonce": "4e62a0073087ed808be62469",
+ "hmac_key": "c8efa10230b5ea11633816c1230ca05fa602ace80a7598916d83bae3d3d2ccd7"
+ },
+ {
+ "nonce": "e9d1eba47dd7e6c1532dc782ff63125db83042bb32841db7eeafd528f3ea7af9",
+ "chacha_key": "54241f68dc2e50e1db79e892c7c7a471856beeb8d51b7f4d16f16ab0645d2f1a",
+ "chacha_nonce": "a963ed7dc29b7b1046820a1d",
+ "hmac_key": "aba215c8634530dc21c70ddb3b3ee4291e0fa5fa79be0f85863747bde281c8b2"
+ },
+ {
+ "nonce": "a94ecf8efeee9d7068de730fad8daf96694acb70901d762de39fa8a5039c3c49",
+ "chacha_key": "c0565e9e201d2381a2368d7ffe60f555223874610d3d91fbbdf3076f7b1374dd",
+ "chacha_nonce": "329bb3024461e84b2e1c489b",
+ "hmac_key": "ac42445491f092481ce4fa33b1f2274700032db64e3a15014fbe8c28550f2fec"
+ },
+ {
+ "nonce": "533605ea214e70c25e9a22f792f4b78b9f83a18ab2103687c8a0075919eaaa53",
+ "chacha_key": "ab35a5e1e54d693ff023db8500d8d4e79ad8878c744e0eaec691e96e141d2325",
+ "chacha_nonce": "653d759042b85194d4d8c0a7",
+ "hmac_key": "b43628e37ba3c31ce80576f0a1f26d3a7c9361d29bb227433b66f49d44f167ba"
+ },
+ {
+ "nonce": "7f38df30ceea1577cb60b355b4f5567ff4130c49e84fed34d779b764a9cc184c",
+ "chacha_key": "a37d7f211b84a551a127ff40908974eb78415395d4f6f40324428e850e8c42a3",
+ "chacha_nonce": "b822e2c959df32b3cb772a7c",
+ "hmac_key": "1ba31764f01f69b5c89ded2d7c95828e8052c55f5d36f1cd535510d61ba77420"
+ },
+ {
+ "nonce": "11b37f9dbc4d0185d1c26d5f4ed98637d7c9701fffa65a65839fa4126573a4e5",
+ "chacha_key": "964f38d3a31158a5bfd28481247b18dd6e44d69f30ba2a40f6120c6d21d8a6ba",
+ "chacha_nonce": "5f72c5b87c590bcd0f93b305",
+ "hmac_key": "2fc4553e7cedc47f29690439890f9f19c1077ef3e9eaeef473d0711e04448918"
+ },
+ {
+ "nonce": "8be790aa483d4cdd843189f71f135b3ec7e31f381312c8fe9f177aab2a48eafa",
+ "chacha_key": "95c8c74d633721a131316309cf6daf0804d59eaa90ea998fc35bac3d2fbb7a94",
+ "chacha_nonce": "409a7654c0e4bf8c2c6489be",
+ "hmac_key": "21bb0b06eb2b460f8ab075f497efa9a01c9cf9146f1e3986c3bf9da5689b6dc4"
+ },
+ {
+ "nonce": "19fd2a718ea084827d6bd73f509229ddf856732108b59fc01819f611419fd140",
+ "chacha_key": "cc6714b9f5616c66143424e1413d520dae03b1a4bd202b82b0a89b0727f5cdc8",
+ "chacha_nonce": "1b7fd2534f015a8f795d8f32",
+ "hmac_key": "2bef39c4ce5c3c59b817e86351373d1554c98bc131c7e461ed19d96cfd6399a0"
+ },
+ {
+ "nonce": "3c2acd893952b2f6d07d8aea76f545ca45961a93fe5757f6a5a80811d5e0255d",
+ "chacha_key": "c8de6c878cb469278d0af894bc181deb6194053f73da5014c2b5d2c8db6f2056",
+ "chacha_nonce": "6ffe4f1971b904a1b1a81b99",
+ "hmac_key": "df1cd69dd3646fca15594284744d4211d70e7d8472e545d276421fbb79559fd4"
+ },
+ {
+ "nonce": "7dbea4cead9ac91d4137f1c0a6eebb6ba0d1fb2cc46d829fbc75f8d86aca6301",
+ "chacha_key": "c8e030f6aa680c3d0b597da9c92bb77c21c4285dd620c5889f9beba7446446b0",
+ "chacha_nonce": "a9b5a67d081d3b42e737d16f",
+ "hmac_key": "355a85f551bc3cce9a14461aa60994742c9bbb1c81a59ca102dc64e61726ab8e"
+ },
+ {
+ "nonce": "45422e676cdae5f1071d3647d7a5f1f5adafb832668a578228aa1155a491f2f3",
+ "chacha_key": "758437245f03a88e2c6a32807edfabff51a91c81ca2f389b0b46f2c97119ea90",
+ "chacha_nonce": "263830a065af33d9c6c5aa1f",
+ "hmac_key": "7c581cf3489e2de203a95106bfc0de3d4032e9d5b92b2b61fb444acd99037e17"
+ },
+ {
+ "nonce": "babc0c03fad24107ad60678751f5db2678041ff0d28671ede8d65bdf7aa407e9",
+ "chacha_key": "bd68a28bd48d9ffa3602db72c75662ac2848a0047a313d2ae2d6bc1ac153d7e9",
+ "chacha_nonce": "d0f9d2a1ace6c758f594ffdd",
+ "hmac_key": "eb435e3a642adfc9d59813051606fc21f81641afd58ea6641e2f5a9f123bb50a"
+ },
+ {
+ "nonce": "7a1b8aac37d0d20b160291fad124ab697cfca53f82e326d78fef89b4b0ea8f83",
+ "chacha_key": "9e97875b651a1d30d17d086d1e846778b7faad6fcbc12e08b3365d700f62e4fe",
+ "chacha_nonce": "ccdaad5b3b7645be430992eb",
+ "hmac_key": "6f2f55cf35174d75752f63c06cc7cbc8441759b142999ed2d5a6d09d263e1fc4"
+ },
+ {
+ "nonce": "8370e4e32d7e680a83862cab0da6136ef607014d043e64cdf5ecc0c4e20b3d9a",
+ "chacha_key": "1472bed5d19db9c546106de946e0649cd83cc9d4a66b087a65906e348dcf92e2",
+ "chacha_nonce": "ed02dece5fc3a186f123420b",
+ "hmac_key": "7b3f7739f49d30c6205a46b174f984bb6a9fc38e5ccfacef2dac04fcbd3b184e"
+ },
+ {
+ "nonce": "9f1c5e8a29cd5677513c2e3a816551d6833ee54991eb3f00d5b68096fc8f0183",
+ "chacha_key": "5e1a7544e4d4dafe55941fcbdf326f19b0ca37fc49c4d47e9eec7fb68cde4975",
+ "chacha_nonce": "7d9acb0fdc174e3c220f40de",
+ "hmac_key": "e265ab116fbbb86b2aefc089a0986a0f5b77eda50c7410404ad3b4f3f385c7a7"
+ },
+ {
+ "nonce": "c385aa1c37c2bfd5cc35fcdbdf601034d39195e1cabff664ceb2b787c15d0225",
+ "chacha_key": "06bf4e60677a13e54c4a38ab824d2ef79da22b690da2b82d0aa3e39a14ca7bdd",
+ "chacha_nonce": "26b450612ca5e905b937e147",
+ "hmac_key": "22208152be2b1f5f75e6bfcc1f87763d48bb7a74da1be3d102096f257207f8b3"
+ },
+ {
+ "nonce": "3ff73528f88a50f9d35c0ddba4560bacee5b0462d0f4cb6e91caf41847040ce4",
+ "chacha_key": "850c8a17a23aa761d279d9901015b2bbdfdff00adbf6bc5cf22bd44d24ecabc9",
+ "chacha_nonce": "4a296a1fb0048e5020d3b129",
+ "hmac_key": "b1bf49a533c4da9b1d629b7ff30882e12d37d49c19abd7b01b7807d75ee13806"
+ },
+ {
+ "nonce": "2dcf39b9d4c52f1cb9db2d516c43a7c6c3b8c401f6a4ac8f131a9e1059957036",
+ "chacha_key": "17f8057e6156ba7cc5310d01eda8c40f9aa388f9fd1712deb9511f13ecc37d27",
+ "chacha_nonce": "a8188daff807a1182200b39d",
+ "hmac_key": "47b89da97f68d389867b5d8a2d7ba55715a30e3d88a3cc11f3646bc2af5580ef"
+ }
+ ]
+ },
+ "calc_padded_len": [
+ [ 16, 32 ],
+ [ 32, 32 ],
+ [ 33, 64 ],
+ [ 37, 64 ],
+ [ 45, 64 ],
+ [ 49, 64 ],
+ [ 64, 64 ],
+ [ 65, 96 ],
+ [ 100, 128 ],
+ [ 111, 128 ],
+ [ 200, 224 ],
+ [ 250, 256 ],
+ [ 320, 320 ],
+ [ 383, 384 ],
+ [ 384, 384 ],
+ [ 400, 448 ],
+ [ 500, 512 ],
+ [ 512, 512 ],
+ [ 515, 640 ],
+ [ 700, 768 ],
+ [ 800, 896 ],
+ [ 900, 1024 ],
+ [ 1020, 1024 ],
+ [ 65536, 65536 ]
+ ],
+ "encrypt_decrypt": [
+ {
+ "sec1": "0000000000000000000000000000000000000000000000000000000000000001",
+ "sec2": "0000000000000000000000000000000000000000000000000000000000000002",
+ "conversation_key": "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d",
+ "nonce": "0000000000000000000000000000000000000000000000000000000000000001",
+ "plaintext": "a",
+ "payload": "AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABee0G5VSK0/9YypIObAtDKfYEAjD35uVkHyB0F4DwrcNaCXlCWZKaArsGrY6M9wnuTMxWfp1RTN9Xga8no+kF5Vsb"
+ },
+ {
+ "sec1": "0000000000000000000000000000000000000000000000000000000000000002",
+ "sec2": "0000000000000000000000000000000000000000000000000000000000000001",
+ "conversation_key": "c41c775356fd92eadc63ff5a0dc1da211b268cbea22316767095b2871ea1412d",
+ "nonce": "f00000000000000000000000000000f00000000000000000000000000000000f",
+ "plaintext": "🍕🫃",
+ "payload": "AvAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAPSKSK6is9ngkX2+cSq85Th16oRTISAOfhStnixqZziKMDvB0QQzgFZdjLTPicCJaV8nDITO+QfaQ61+KbWQIOO2Yj"
+ },
+ {
+ "sec1": "5c0c523f52a5b6fad39ed2403092df8cebc36318b39383bca6c00808626fab3a",
+ "sec2": "4b22aa260e4acb7021e32f38a6cdf4b673c6a277755bfce287e370c924dc936d",
+ "conversation_key": "3e2b52a63be47d34fe0a80e34e73d436d6963bc8f39827f327057a9986c20a45",
+ "nonce": "b635236c42db20f021bb8d1cdff5ca75dd1a0cc72ea742ad750f33010b24f73b",
+ "plaintext": "表ポあA鷗ŒéB逍Üߪąñ丂㐀𠀀",
+ "payload": "ArY1I2xC2yDwIbuNHN/1ynXdGgzHLqdCrXUPMwELJPc7s7JqlCMJBAIIjfkpHReBPXeoMCyuClwgbT419jUWU1PwaNl4FEQYKCDKVJz+97Mp3K+Q2YGa77B6gpxB/lr1QgoqpDf7wDVrDmOqGoiPjWDqy8KzLueKDcm9BVP8xeTJIxs="
+ },
+ {
+ "sec1": "8f40e50a84a7462e2b8d24c28898ef1f23359fff50d8c509e6fb7ce06e142f9c",
+ "sec2": "b9b0a1e9cc20100c5faa3bbe2777303d25950616c4c6a3fa2e3e046f936ec2ba",
+ "conversation_key": "d5a2f879123145a4b291d767428870f5a8d9e5007193321795b40183d4ab8c2b",
+ "nonce": "b20989adc3ddc41cd2c435952c0d59a91315d8c5218d5040573fc3749543acaf",
+ "plaintext": "ability🤝的 ȺȾ",
+ "payload": "ArIJia3D3cQc0sQ1lSwNWakTFdjFIY1QQFc/w3SVQ6yvbG2S0x4Yu86QGwPTy7mP3961I1XqB6SFFTzqDZZavhxoWMj7mEVGMQIsh2RLWI5EYQaQDIePSnXPlzf7CIt+voTD"
+ },
+ {
+ "sec1": "875adb475056aec0b4809bd2db9aa00cff53a649e7b59d8edcbf4e6330b0995c",
+ "sec2": "9c05781112d5b0a2a7148a222e50e0bd891d6b60c5483f03456e982185944aae",
+ "conversation_key": "3b15c977e20bfe4b8482991274635edd94f366595b1a3d2993515705ca3cedb8",
+ "nonce": "8d4442713eb9d4791175cb040d98d6fc5be8864d6ec2f89cf0895a2b2b72d1b1",
+ "plaintext": "pepper👀їжак",
+ "payload": "Ao1EQnE+udR5EXXLBA2Y1vxb6IZNbsL4nPCJWisrctGxY3AduCS+jTUgAAnfvKafkmpy15+i9YMwCdccisRa8SvzW671T2JO4LFSPX31K4kYUKelSAdSPwe9NwO6LhOsnoJ+"
+ },
+ {
+ "sec1": "eba1687cab6a3101bfc68fd70f214aa4cc059e9ec1b79fdb9ad0a0a4e259829f",
+ "sec2": "dff20d262bef9dfd94666548f556393085e6ea421c8af86e9d333fa8747e94b3",
+ "conversation_key": "4f1538411098cf11c8af216836444787c462d47f97287f46cf7edb2c4915b8a5",
+ "nonce": "2180b52ae645fcf9f5080d81b1f0b5d6f2cd77ff3c986882bb549158462f3407",
+ "plaintext": "( ͡° ͜ʖ ͡°)",
+ "payload": "AiGAtSrmRfz59QgNgbHwtdbyzXf/PJhogrtUkVhGLzQHv4qhKQwnFQ54OjVMgqCea/Vj0YqBSdhqNR777TJ4zIUk7R0fnizp6l1zwgzWv7+ee6u+0/89KIjY5q1wu6inyuiv"
+ },
+ {
+ "sec1": "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e",
+ "sec2": "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214",
+ "conversation_key": "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd",
+ "nonce": "e4cd5f7ce4eea024bc71b17ad456a986a74ac426c2c62b0a15eb5c5c8f888b68",
+ "plaintext": "مُنَاقَشَةُ سُبُلِ اِسْتِخْدَامِ اللُّغَةِ فِي النُّظُمِ الْقَائِمَةِ وَفِيم يَخُصَّ التَّطْبِيقَاتُ الْحاسُوبِيَّةُ،",
+ "payload": "AuTNX3zk7qAkvHGxetRWqYanSsQmwsYrChXrXFyPiItoIBsWu1CB+sStla2M4VeANASHxM78i1CfHQQH1YbBy24Tng7emYW44ol6QkFD6D8Zq7QPl+8L1c47lx8RoODEQMvNCbOk5ffUV3/AhONHBXnffrI+0025c+uRGzfqpYki4lBqm9iYU+k3Tvjczq9wU0mkVDEaM34WiQi30MfkJdRbeeYaq6kNvGPunLb3xdjjs5DL720d61Flc5ZfoZm+CBhADy9D9XiVZYLKAlkijALJur9dATYKci6OBOoc2SJS2Clai5hOVzR0yVeyHRgRfH9aLSlWW5dXcUxTo7qqRjNf8W5+J4jF4gNQp5f5d0YA4vPAzjBwSP/5bGzNDslKfcAH"
+ },
+ {
+ "sec1": "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e",
+ "sec2": "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214",
+ "conversation_key": "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd",
+ "nonce": "38d1ca0abef9e5f564e89761a86cee04574b6825d3ef2063b10ad75899e4b023",
+ "plaintext": "الكل في المجمو عة (5)",
+ "payload": "AjjRygq++eX1ZOiXYahs7gRXS2gl0+8gY7EK11iZ5LAjbOTrlfrxak5Lki42v2jMPpLSicy8eHjsWkkMtF0i925vOaKG/ZkMHh9ccQBdfTvgEGKzztedqDCAWb5TP1YwU1PsWaiiqG3+WgVvJiO4lUdMHXL7+zKKx8bgDtowzz4QAwI="
+ },
+ {
+ "sec1": "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e",
+ "sec2": "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214",
+ "conversation_key": "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd",
+ "nonce": "4f1a31909f3483a9e69c8549a55bbc9af25fa5bbecf7bd32d9896f83ef2e12e0",
+ "plaintext": "𝖑𝖆𝖟𝖞 社會科學院語學研究所",
+ "payload": "Ak8aMZCfNIOp5pyFSaVbvJryX6W77Pe9MtmJb4PvLhLgh/TsxPLFSANcT67EC1t/qxjru5ZoADjKVEt2ejdx+xGvH49mcdfbc+l+L7gJtkH7GLKpE9pQNQWNHMAmj043PAXJZ++fiJObMRR2mye5VHEANzZWkZXMrXF7YjuG10S1pOU="
+ },
+ {
+ "sec1": "d5633530f5bcfebceb5584cfbbf718a30df0751b729dd9a789b9f30c0587d74e",
+ "sec2": "b74e6a341fb134127272b795a08b59250e5fa45a82a2eb4095e4ce9ed5f5e214",
+ "conversation_key": "75fe686d21a035f0c7cd70da64ba307936e5ca0b20710496a6b6b5f573377bdd",
+ "nonce": "a3e219242d85465e70adcd640b564b3feff57d2ef8745d5e7a0663b2dccceb54",
+ "plaintext": "🙈 🙉 🙊 0️⃣ 1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣ 8️⃣ 9️⃣ 🔟 Powerلُلُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ冗",
+ "payload": "AqPiGSQthUZecK3NZAtWSz/v9X0u+HRdXnoGY7LczOtUf05aMF89q1FLwJvaFJYICZoMYgRJHFLwPiOHce7fuAc40kX0wXJvipyBJ9HzCOj7CgtnC1/cmPCHR3s5AIORmroBWglm1LiFMohv1FSPEbaBD51VXxJa4JyWpYhreSOEjn1wd0lMKC9b+osV2N2tpbs+rbpQem2tRen3sWflmCqjkG5VOVwRErCuXuPb5+hYwd8BoZbfCrsiAVLd7YT44dRtKNBx6rkabWfddKSLtreHLDysOhQUVOp/XkE7OzSkWl6sky0Hva6qJJ/V726hMlomvcLHjE41iKmW2CpcZfOedg=="
+ }
+ ],
+ "encrypt_decrypt_long_msg": [
+ {
+ "conversation_key": "8fc262099ce0d0bb9b89bac05bb9e04f9bc0090acc181fef6840ccee470371ed",
+ "nonce": "326bcb2c943cd6bb717588c9e5a7e738edf6ed14ec5f5344caa6ef56f0b9cff7",
+ "pattern": "x",
+ "repeat": 65535,
+ "plaintext_sha256": "09ab7495d3e61a76f0deb12cb0306f0696cbb17ffc12131368c7a939f12f56d3",
+ "payload_sha256": "90714492225faba06310bff2f249ebdc2a5e609d65a629f1c87f2d4ffc55330a"
+ },
+ {
+ "conversation_key": "56adbe3720339363ab9c3b8526ffce9fd77600927488bfc4b59f7a68ffe5eae0",
+ "nonce": "ad68da81833c2a8ff609c3d2c0335fd44fe5954f85bb580c6a8d467aa9fc5dd0",
+ "pattern": "!",
+ "repeat": 65535,
+ "plaintext_sha256": "6af297793b72ae092c422e552c3bb3cbc310da274bd1cf9e31023a7fe4a2d75e",
+ "payload_sha256": "8013e45a109fad3362133132b460a2d5bce235fe71c8b8f4014793fb52a49844"
+ },
+ {
+ "conversation_key": "7fc540779979e472bb8d12480b443d1e5eb1098eae546ef2390bee499bbf46be",
+ "nonce": "34905e82105c20de9a2f6cd385a0d541e6bcc10601d12481ff3a7575dc622033",
+ "pattern": "🦄",
+ "repeat": 16383,
+ "plaintext_sha256": "a249558d161b77297bc0cb311dde7d77190f6571b25c7e4429cd19044634a61f",
+ "payload_sha256": "b3348422471da1f3c59d79acfe2fe103f3cd24488109e5b18734cdb5953afd15"
+ }
+ ]
+ },
+ "invalid": {
+ "encrypt_msg_lengths": [ 0, 65536, 100000, 10000000 ],
+ "get_conversation_key": [
+ {
+ "sec1": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
+ "note": "sec1 higher than curve.n"
+ },
+ {
+ "sec1": "0000000000000000000000000000000000000000000000000000000000000000",
+ "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
+ "note": "sec1 is 0"
+ },
+ {
+ "sec1": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364139",
+ "pub2": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ "note": "pub2 is invalid, no sqrt, all-ff"
+ },
+ {
+ "sec1": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141",
+ "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
+ "note": "sec1 == curve.n"
+ },
+ {
+ "sec1": "0000000000000000000000000000000000000000000000000000000000000002",
+ "pub2": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
+ "note": "pub2 is invalid, no sqrt"
+ },
+ {
+ "sec1": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
+ "pub2": "0000000000000000000000000000000000000000000000000000000000000000",
+ "note": "pub2 is point of order 3 on twist"
+ },
+ {
+ "sec1": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
+ "pub2": "eb1f7200aecaa86682376fb1c13cd12b732221e774f553b0a0857f88fa20f86d",
+ "note": "pub2 is point of order 13 on twist"
+ },
+ {
+ "sec1": "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20",
+ "pub2": "709858a4c121e4a84eb59c0ded0261093c71e8ca29efeef21a6161c447bcaf9f",
+ "note": "pub2 is point of order 3319 on twist"
+ }
+ ],
+ "decrypt": [
+ {
+ "conversation_key": "ca2527a037347b91bea0c8a30fc8d9600ffd81ec00038671e3a0f0cb0fc9f642",
+ "nonce": "daaea5ca345b268e5b62060ca72c870c48f713bc1e00ff3fc0ddb78e826f10db",
+ "plaintext": "n o b l e",
+ "payload": "#Atqupco0WyaOW2IGDKcshwxI9xO8HgD/P8Ddt46CbxDbrhdG8VmJdU0MIDf06CUvEvdnr1cp1fiMtlM/GrE92xAc1K5odTpCzUB+mjXgbaqtntBUbTToSUoT0ovrlPwzGjyp",
+ "note": "unknown encryption version"
+ },
+ {
+ "conversation_key": "36f04e558af246352dcf73b692fbd3646a2207bd8abd4b1cd26b234db84d9481",
+ "nonce": "ad408d4be8616dc84bb0bf046454a2a102edac937c35209c43cd7964c5feb781",
+ "plaintext": "⚠️",
+ "payload": "AK1AjUvoYW3IS7C/BGRUoqEC7ayTfDUgnEPNeWTF/reBZFaha6EAIRueE9D1B1RuoiuFScC0Q94yjIuxZD3JStQtE8JMNacWFs9rlYP+ZydtHhRucp+lxfdvFlaGV/sQlqZz",
+ "note": "unknown encryption version 0"
+ },
+ {
+ "conversation_key": "ca2527a037347b91bea0c8a30fc8d9600ffd81ec00038671e3a0f0cb0fc9f642",
+ "nonce": "daaea5ca345b268e5b62060ca72c870c48f713bc1e00ff3fc0ddb78e826f10db",
+ "plaintext": "n o s t r",
+ "payload": "Atфupco0WyaOW2IGDKcshwxI9xO8HgD/P8Ddt46CbxDbrhdG8VmJZE0UICD06CUvEvdnr1cp1fiMtlM/GrE92xAc1EwsVCQEgWEu2gsHUVf4JAa3TpgkmFc3TWsax0v6n/Wq",
+ "note": "invalid base64"
+ },
+ {
+ "conversation_key": "cff7bd6a3e29a450fd27f6c125d5edeb0987c475fd1e8d97591e0d4d8a89763c",
+ "nonce": "09ff97750b084012e15ecb84614ce88180d7b8ec0d468508a86b6d70c0361a25",
+ "plaintext": "¯\\_(ツ)_/¯",
+ "payload": "Agn/l3ULCEAS4V7LhGFM6IGA17jsDUaFCKhrbXDANholyySBfeh+EN8wNB9gaLlg4j6wdBYh+3oK+mnxWu3NKRbSvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ "note": "invalid MAC"
+ },
+ {
+ "conversation_key": "cfcc9cf682dfb00b11357f65bdc45e29156b69db424d20b3596919074f5bf957",
+ "nonce": "65b14b0b949aaa7d52c417eb753b390e8ad6d84b23af4bec6d9bfa3e03a08af4",
+ "plaintext": "🥎",
+ "payload": "AmWxSwuUmqp9UsQX63U7OQ6K1thLI69L7G2b+j4DoIr0oRWQ8avl4OLqWZiTJ10vIgKrNqjoaX+fNhE9RqmR5g0f6BtUg1ijFMz71MO1D4lQLQfW7+UHva8PGYgQ1QpHlKgR",
+ "note": "invalid MAC"
+ },
+ {
+ "conversation_key": "5254827d29177622d40a7b67cad014fe7137700c3c523903ebbe3e1b74d40214",
+ "nonce": "7ab65dbb8bbc2b8e35cafb5745314e1f050325a864d11d0475ef75b3660d91c1",
+ "plaintext": "elliptic-curve cryptography",
+ "payload": "Anq2XbuLvCuONcr7V0UxTh8FAyWoZNEdBHXvdbNmDZHB573MI7R7rrTYftpqmvUpahmBC2sngmI14/L0HjOZ7lWGJlzdh6luiOnGPc46cGxf08MRC4CIuxx3i2Lm0KqgJ7vA",
+ "note": "invalid padding"
+ },
+ {
+ "conversation_key": "fea39aca9aa8340c3a78ae1f0902aa7e726946e4efcd7783379df8096029c496",
+ "nonce": "7d4283e3b54c885d6afee881f48e62f0a3f5d7a9e1cb71ccab594a7882c39330",
+ "plaintext": "noble",
+ "payload": "An1Cg+O1TIhdav7ogfSOYvCj9dep4ctxzKtZSniCw5MwRrrPJFyAQYZh5VpjC2QYzny5LIQ9v9lhqmZR4WBYRNJ0ognHVNMwiFV1SHpvUFT8HHZN/m/QarflbvDHAtO6pY16",
+ "note": "invalid padding"
+ },
+ {
+ "conversation_key": "0c4cffb7a6f7e706ec94b2e879f1fc54ff8de38d8db87e11787694d5392d5b3f",
+ "nonce": "6f9fd72667c273acd23ca6653711a708434474dd9eb15c3edb01ce9a95743e9b",
+ "plaintext": "censorship-resistant and global social network",
+ "payload": "Am+f1yZnwnOs0jymZTcRpwhDRHTdnrFcPtsBzpqVdD6b2NZDaNm/TPkZGr75kbB6tCSoq7YRcbPiNfJXNch3Tf+o9+zZTMxwjgX/nm3yDKR2kHQMBhVleCB9uPuljl40AJ8kXRD0gjw+aYRJFUMK9gCETZAjjmrsCM+nGRZ1FfNsHr6Z",
+ "note": "invalid padding"
+ },
+ {
+ "conversation_key": "5cd2d13b9e355aeb2452afbd3786870dbeecb9d355b12cb0a3b6e9da5744cd35",
+ "nonce": "b60036976a1ada277b948fd4caa065304b96964742b89d26f26a25263a5060bd",
+ "plaintext": "0",
+ "payload": "",
+ "note": "invalid payload length: 0"
+ },
+ {
+ "conversation_key": "d61d3f09c7dfe1c0be91af7109b60a7d9d498920c90cbba1e137320fdd938853",
+ "nonce": "1a29d02c8b4527745a2ccb38bfa45655deb37bc338ab9289d756354cea1fd07c",
+ "plaintext": "1",
+ "payload": "Ag==",
+ "note": "invalid payload length: 4"
+ },
+ {
+ "conversation_key": "873bb0fc665eb950a8e7d5971965539f6ebd645c83c08cd6a85aafbad0f0bc47",
+ "nonce": "c826d3c38e765ab8cc42060116cd1464b2a6ce01d33deba5dedfb48615306d4a",
+ "plaintext": "2",
+ "payload": "AqxgToSh3H7iLYRJjoWAM+vSv/Y1mgNlm6OWWjOYUClrFF8=",
+ "note": "invalid payload length: 48"
+ },
+ {
+ "conversation_key": "9f2fef8f5401ac33f74641b568a7a30bb19409c76ffdc5eae2db6b39d2617fbe",
+ "nonce": "9ff6484642545221624eaac7b9ea27133a4cc2356682a6033aceeef043549861",
+ "plaintext": "3",
+ "payload": "Ap/2SEZCVFIhYk6qx7nqJxM6TMI1ZoKmAzrO7vBDVJhhuZXWiM20i/tIsbjT0KxkJs2MZjh1oXNYMO9ggfk7i47WQA==",
+ "note": "invalid payload length: 92"
+ }
+ ]
+ }
+ }
+}
diff --git a/lib/NVault.Crypto.Secp256k1/src/ContextExtensions.cs b/lib/NVault.Crypto.Secp256k1/src/ContextExtensions.cs
deleted file mode 100644
index bb014df..0000000
--- a/lib/NVault.Crypto.Secp256k1/src/ContextExtensions.cs
+++ /dev/null
@@ -1,274 +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.Security.Cryptography;
-using System.Runtime.InteropServices;
-
-using VNLib.Hashing;
-using VNLib.Utils;
-using VNLib.Utils.Memory;
-using VNLib.Utils.Extensions;
-
-using static NVault.Crypto.Secp256k1.LibSecp256k1;
-
-namespace NVault.Crypto.Secp256k1
-{
- /// <summary>
- /// The callback function signature required for ECDH hash functions
- /// </summary>
- /// <param name="state">The callback state</param>
- /// <returns>The return value to be passed as a result of the operation</returns>
- public delegate int Secp256k1EcdhHashFunc(in Secp256HashFuncState state);
-
- public static unsafe class ContextExtensions
- {
- /// <summary>
- /// Signs a 32byte message digest with the specified secret key on the current context and writes the signature to the specified buffer
- /// </summary>
- /// <param name="context"></param>
- /// <param name="secretKey">The 32byte secret key used to sign messages from the user</param>
- /// <param name="digest">The 32byte message digest to compute the signature of</param>
- /// <param name="signature">The buffer to write the signature output to, must be at-least 64 bytes</param>
- /// <returns>The number of bytes written to the signature buffer, or less than 1 if the operation failed</returns>
- public static ERRNO SchnorSignDigest(this in Secp256k1Context context, ReadOnlySpan<byte> secretKey, ReadOnlySpan<byte> digest, Span<byte> signature)
- {
- //Check the signature buffer size
- if (signature.Length < SignatureSize)
- {
- return ERRNO.E_FAIL;
- }
-
- //Message digest must be exactly 32 bytes long
- if (digest.Length != (int)HashAlg.SHA256)
- {
- return ERRNO.E_FAIL;
- }
-
- //Secret key size must be exactly the size of the secret key struct
- if(secretKey.Length != sizeof(Secp256k1SecretKey))
- {
- return ERRNO.E_FAIL;
- }
-
- //Stack allocated keypair
- KeyPair keyPair = new();
-
- //Init the secret key struct from key data
- Secp256k1SecretKey secKeyStruct = MemoryMarshal.Read<Secp256k1SecretKey>(secretKey);
-
- //Randomize the context and create the keypair
- if (!context.CreateKeyPair(&keyPair, &secKeyStruct))
- {
- return ERRNO.E_FAIL;
- }
-
- //Create the random nonce
- byte* random = stackalloc byte[RandomBufferSize];
-
- //Fill the buffer with random bytes
- context.Lib.GetRandomBytes(new Span<byte>(random, RandomBufferSize));
-
- try
- {
- fixed (byte* sigPtr = &MemoryMarshal.GetReference(signature),
- digestPtr = &MemoryMarshal.GetReference(digest))
- {
- //Sign the message hash and write the output to the signature buffer
- if (context.Lib._signHash(context.Context, sigPtr, digestPtr, &keyPair, random) != 1)
- {
- return ERRNO.E_FAIL;
- }
- }
- }
- finally
- {
- //Erase entropy
- MemoryUtil.InitializeBlock(random, RandomBufferSize);
-
- //Clear the keypair, contains the secret key, even if its stack allocated
- MemoryUtil.ZeroStruct(&keyPair);
- }
-
- //Signature size is always 64 bytes
- return SignatureSize;
- }
-
- /// <summary>
- /// Generates an x-only Schnor encoded public key from the specified secret key on the
- /// current context and writes it to the specified buffer.
- /// </summary>
- /// <param name="context"></param>
- /// <param name="secretKey">The 32byte secret key used to derrive the public key from</param>
- /// <param name="pubKeyBuffer">The buffer to write the x-only Schnor encoded public key</param>
- /// <returns>The number of bytes written to the output buffer, or 0 if the operation failed</returns>
- /// <exception cref="CryptographicException"></exception>
- public static ERRNO GeneratePubKeyFromSecret(this in Secp256k1Context context, ReadOnlySpan<byte> secretKey, Span<byte> pubKeyBuffer)
- {
- if (secretKey.Length != sizeof(Secp256k1SecretKey))
- {
- throw new CryptographicException($"Your secret key must be exactly {sizeof(Secp256k1SecretKey)} bytes long");
- }
-
- if (pubKeyBuffer.Length < XOnlyPublicKeySize)
- {
- throw new CryptographicException($"Your public key buffer must be at least {XOnlyPublicKeySize} bytes long");
- }
-
- //Protect for released lib
- context.Lib.SafeLibHandle.ThrowIfClosed();
-
- //Stack allocated keypair and x-only public key
- Secp256k1PublicKey xOnlyPubKey = new();
- Secp256k1SecretKey secKeyStruct = MemoryMarshal.Read<Secp256k1SecretKey>(secretKey);
- KeyPair keyPair = new();
-
- try
- {
- //Init context and keypair
- if (!context.CreateKeyPair(&keyPair, &secKeyStruct))
- {
- return ERRNO.E_FAIL;
- }
-
- //X-only public key from the keypair
- if (context.Lib._createXonly(context.Context, &xOnlyPubKey, 0, &keyPair) != 1)
- {
- return ERRNO.E_FAIL;
- }
-
- fixed (byte* pubBuffer = &MemoryMarshal.GetReference(pubKeyBuffer))
- {
- //Serialize the public key to the buffer as an X-only public key without leading status byte
- if (context.Lib._serializeXonly(context.Context, pubBuffer, &xOnlyPubKey) != 1)
- {
- return ERRNO.E_FAIL;
- }
- }
- }
- finally
- {
- //Clear the keypair, contains the secret key, even if its stack allocated
- MemoryUtil.ZeroStruct(&keyPair);
- }
-
- //PubKey length is constant
- return XOnlyPublicKeySize;
- }
-
- /// <summary>
- /// Verifies that a given secret key is valid using the current context
- /// </summary>
- /// <param name="context"></param>
- /// <param name="secretKey">The secret key to verify</param>
- /// <returns>A boolean value that indicates if the secret key is valid or not</returns>
- /// <exception cref="CryptographicException"></exception>
- public static bool VerifySecretKey(this in Secp256k1Context context, ReadOnlySpan<byte> secretKey)
- {
- if (secretKey.Length != sizeof(Secp256k1SecretKey))
- {
- throw new CryptographicException($"Your secret key must be exactly {sizeof(Secp256k1SecretKey)} bytes long");
- }
-
- context.Lib.SafeLibHandle.ThrowIfClosed();
-
- //Get sec key ref and verify
- fixed(byte* ptr = &MemoryMarshal.GetReference(secretKey))
- {
- return context.Lib._secKeyVerify.Invoke(context.Context, ptr) == 1;
- }
- }
-
-
- [StructLayout(LayoutKind.Sequential)]
- private readonly ref struct EcdhHashFuncState
- {
- public readonly IntPtr HashFunc { get; init; }
- public readonly IntPtr Opaque { get; init; }
- public readonly int OutLen { get; init; }
- }
-
- /// <summary>
- /// Verifies that a given secret key is valid using the current context
- /// </summary>
- /// <param name="context"></param>
- /// <param name="secretKey">The secret key to verify</param>
- /// <returns>A boolean value that indicates if the secret key is valid or not</returns>
- /// <exception cref="ArgumentException"></exception>
- public static bool ComputeSharedKey(this in Secp256k1Context context, Span<byte> data, ReadOnlySpan<byte> xOnlyPubKey, ReadOnlySpan<byte> secretKey, Secp256k1EcdhHashFunc callback, IntPtr opaque)
- {
- if (secretKey.Length != sizeof(Secp256k1SecretKey))
- {
- throw new ArgumentException($"Your secret key buffer must be exactly {sizeof(Secp256k1SecretKey)} bytes long");
- }
-
- //Init callback state struct
- EcdhHashFuncState state = new()
- {
- HashFunc = Marshal.GetFunctionPointerForDelegate(callback),
- Opaque = opaque,
- OutLen = data.Length
- };
-
- context.Lib.SafeLibHandle.ThrowIfClosed();
-
- //Stack allocated keypair and x-only public key
- Secp256k1PublicKey peerPubKey = new();
-
- //Parse the public key from the buffer
- fixed (byte* pubkeyPtr = &MemoryMarshal.GetReference(xOnlyPubKey))
- {
- context.Lib._xOnlyPubkeyParse(context.Context, &peerPubKey, pubkeyPtr);
- }
-
- fixed (byte* dataPtr = &MemoryMarshal.GetReference(data),
- secKeyPtr = &MemoryMarshal.GetReference(secretKey))
- {
- return context.Lib._ecdh.Invoke(
- context.Context,
- dataPtr,
- &peerPubKey,
- secKeyPtr,
- UmanagedEcdhHashFuncCallback,
- &state
- ) == 1;
- }
-
- /*
- * Umanaged wrapper function for invoking the safe user callback
- * from the unmanaged lib
- */
- static int UmanagedEcdhHashFuncCallback(byte* output, byte* x32, byte* y32, void* opaque)
- {
- //Recover the callback
- if (opaque == null)
- {
- return 0;
- }
-
- EcdhHashFuncState* state = (EcdhHashFuncState*)opaque;
-
- //Init user-state structure
- Secp256HashFuncState userState = new(output, state->OutLen, x32, 32, new(opaque));
-
- //Recover the function pointer
- Secp256k1EcdhHashFunc callback = Marshal.GetDelegateForFunctionPointer<Secp256k1EcdhHashFunc>(state->HashFunc);
-
- //Invoke the callback
- return callback(in userState);
- }
- }
- }
-} \ No newline at end of file
diff --git a/lib/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs b/lib/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs
deleted file mode 100644
index 8dda269..0000000
--- a/lib/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs
+++ /dev/null
@@ -1,284 +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.Runtime.InteropServices;
-
-using VNLib.Hashing;
-using VNLib.Utils;
-using VNLib.Utils.Native;
-using VNLib.Utils.Extensions;
-
-namespace NVault.Crypto.Secp256k1
-{
-
- internal unsafe delegate int EcdhHasFunc(byte* output, byte* x32, byte* y32, void* data);
-
- public unsafe class LibSecp256k1 : VnDisposeable
- {
- public const int SignatureSize = 64;
- public const int RandomBufferSize = 32;
- public const int XOnlyPublicKeySize = 32;
-
- public static readonly int SecretKeySize = sizeof(Secp256k1SecretKey);
-
- /*
- * Unsafe structures that represent the native keypair and x-only public key
- * structures. They hold character arrays
- */
- [StructLayout(LayoutKind.Sequential, Size = 96)]
- internal struct KeyPair
- {
- public fixed byte data[96];
- }
-
- /// <summary>
- /// 1:1 with the secp256k1_pubkey structure
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Size = 64)]
- internal struct Secp256k1PublicKey
- {
- public fixed byte data[64];
- }
-
-
- //Native methods
- [SafeMethodName("secp256k1_context_create")]
- internal delegate IntPtr ContextCreate(int flags);
-
- [SafeMethodName("secp256k1_context_destroy")]
- internal delegate void ContextDestroy(IntPtr context);
-
- [SafeMethodName("secp256k1_context_randomize")]
- internal delegate int RandomizeContext(IntPtr context, byte* seed32);
-
- [SafeMethodName("secp256k1_keypair_create")]
- internal delegate int KeypairCreate(IntPtr context, KeyPair* keyPair, byte* secretKey);
-
- [SafeMethodName("secp256k1_keypair_xonly_pub")]
- internal delegate int KeypairXOnlyPub(IntPtr ctx, Secp256k1PublicKey* pubkey, int pk_parity, KeyPair* keypair);
-
- [SafeMethodName("secp256k1_xonly_pubkey_serialize")]
- internal delegate int XOnlyPubkeySerialize(IntPtr ctx, byte* output32, Secp256k1PublicKey* pubkey);
-
- [SafeMethodName("secp256k1_schnorrsig_sign32")]
- internal delegate int SignHash(IntPtr ctx, byte* sig64, byte* msg32, KeyPair* keypair, byte* aux_rand32);
-
- [SafeMethodName("secp256k1_ec_seckey_verify")]
- internal delegate int SecKeyVerify(IntPtr ctx, in byte* seckey);
-
- [SafeMethodName("secp256k1_ec_pubkey_serialize")]
- internal delegate int PubKeySerialize(IntPtr ctx, byte* outPubKey, ulong* outLen, Secp256k1PublicKey* pubKey, uint flags);
-
- [SafeMethodName("secp256k1_xonly_pubkey_parse")]
- internal delegate int XOnlyPubkeyParse(IntPtr ctx, Secp256k1PublicKey* pubkey, byte* input32);
-
- [SafeMethodName("secp256k1_ecdh")]
- internal delegate int Ecdh(
- IntPtr ctx,
- byte* output,
- Secp256k1PublicKey* pubkey,
- byte* scalar,
- EcdhHasFunc hashFunc,
- void* dataPtr
- );
-
-
- /// <summary>
- /// Loads the Secp256k1 library from the specified path and creates a wrapper class (loads methods from the library)
- /// </summary>
- /// <param name="dllPath">The realtive or absolute path to the shared library</param>
- /// <param name="search">The DLL probing path pattern</param>
- /// <returns>The <see cref="LibSecp256k1"/> library wrapper class</returns>
- /// <exception cref="DllNotFoundException"></exception>
- /// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="MissingMemberException"></exception>
- /// <exception cref="EntryPointNotFoundException"></exception>
- public static LibSecp256k1 LoadLibrary(string dllPath, DllImportSearchPath search, IRandomSource? random)
- {
- _ = dllPath?? throw new ArgumentNullException(nameof(dllPath));
-
- //try to load the library
- SafeLibraryHandle lib = SafeLibraryHandle.LoadLibrary(dllPath, search);
-
- //try to create the wrapper class, if it fails, dispose the library
- try
- {
- //setup fallback random source if null
- random ??= new FallbackRandom();
-
- //Create the lib
- return new LibSecp256k1(lib, random);
- }
- catch
- {
- //Dispose the library if the creation failed
- lib.Dispose();
- throw;
- }
- }
-
- /// <summary>
- /// Loads the Secp256k1 library from the specified path and creates a wrapper class (loads methods from the library)
- /// </summary>
- /// <param name="handle">The handle to the shared library</param>
- /// <param name="random">An optional random source to create random entropy and secrets from</param>
- /// <returns>The <see cref="LibSecp256k1"/> library wrapper class</returns>
- /// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="MissingMemberException"></exception>
- /// <exception cref="EntryPointNotFoundException"></exception>
- public static LibSecp256k1 FromHandle(SafeLibraryHandle handle, IRandomSource? random)
- {
- _ = handle ?? throw new ArgumentNullException(nameof(handle));
- //setup fallback random source if null
- random ??= new FallbackRandom();
- //Create the lib
- return new LibSecp256k1(handle, random);
- }
-
- /// <summary>
- /// The underlying library handle
- /// </summary>
- public SafeLibraryHandle SafeLibHandle { get; }
-
- internal readonly KeypairCreate _createKeyPair;
- internal readonly ContextCreate _create;
- internal readonly RandomizeContext _randomize;
- internal readonly ContextDestroy _destroy;
- internal readonly KeypairXOnlyPub _createXonly;
- internal readonly XOnlyPubkeySerialize _serializeXonly;
- internal readonly SignHash _signHash;
- internal readonly SecKeyVerify _secKeyVerify;
- internal readonly PubKeySerialize _pubKeySerialize;
- internal readonly Ecdh _ecdh;
- internal readonly XOnlyPubkeyParse _xOnlyPubkeyParse;
- private readonly IRandomSource _randomSource;
-
- /// <summary>
- /// Creates a new instance of the <see cref="LibSecp256k1"/> class from the specified library handle
- /// </summary>
- /// <param name="handle">The library handle that referrences the secp256k1 platform specific library</param>
- /// <remarks>
- /// This method attempts to capture all the native methods from the library, which may throw if the library is not valid.
- /// </remarks>
- /// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="MissingMemberException"></exception>
- /// <exception cref="EntryPointNotFoundException"></exception>
- public LibSecp256k1(SafeLibraryHandle handle, IRandomSource randomSource)
- {
- //Store library handle
- SafeLibHandle = handle ?? throw new ArgumentNullException(nameof(handle));
-
- //Get all method handles and store them
- _create = handle.DangerousGetMethod<ContextCreate>();
- _createKeyPair = handle.DangerousGetMethod<KeypairCreate>();
- _randomize = handle.DangerousGetMethod<RandomizeContext>();
- _destroy = handle.DangerousGetMethod<ContextDestroy>();
- _createXonly = handle.DangerousGetMethod<KeypairXOnlyPub>();
- _serializeXonly = handle.DangerousGetMethod<XOnlyPubkeySerialize>();
- _signHash = handle.DangerousGetMethod<SignHash>();
- _secKeyVerify = handle.DangerousGetMethod<SecKeyVerify>();
- _pubKeySerialize = handle.DangerousGetMethod<PubKeySerialize>();
- _ecdh = handle.DangerousGetMethod<Ecdh>();
- _xOnlyPubkeyParse = handle.DangerousGetMethod<XOnlyPubkeyParse>();
-
- //Store random source
- _randomSource = randomSource;
- }
-
- /// <summary>
- /// Creates a new instance of the <see cref="LibSecp256k1"/> class from the specified library handle
- /// with a fallback random source
- /// </summary>
- /// <param name="handle">The library handle</param>
- /// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="MissingMemberException"></exception>
- /// <exception cref="EntryPointNotFoundException"></exception>
- public LibSecp256k1(SafeLibraryHandle handle):this(handle, new FallbackRandom())
- {}
-
- /// <summary>
- /// Generates a new secret key and writes it to the specified buffer. The buffer size must be exactly <see cref="SecretKeySize"/> bytes long
- /// <para>
- /// NOTE: You should verify this validity of the key against the library with a new <see cref="Secp256k1Context"/>
- /// </para>
- /// </summary>
- /// <param name="buffer">The secret key buffer</param>
- /// <exception cref="ArgumentException"></exception>
- public void CreateSecretKey(Span<byte> buffer)
- {
- //Protect for released lib
- SafeLibHandle.ThrowIfClosed();
-
- if(buffer.Length != sizeof(Secp256k1SecretKey))
- {
- throw new ArgumentException($"Buffer must be exactly {sizeof(Secp256k1SecretKey)} bytes long", nameof(buffer));
- }
-
- //Fill the buffer with random bytes
- _randomSource.GetRandomBytes(buffer);
- }
-
- /// <summary>
- /// Fills the given buffer with random bytes from
- /// the internal random source
- /// </summary>
- /// <param name="buffer">The buffer to fill with random data</param>
- public void GetRandomBytes(Span<byte> buffer)
- {
- //Protect for released lib
- SafeLibHandle.ThrowIfClosed();
-
- _randomSource.GetRandomBytes(buffer);
- }
-
- /// <summary>
- /// Creates a new <see cref="Secp256k1Context"/> from the current managed library
- /// </summary>
- /// <param name="Lib"></param>
- /// <returns>The new <see cref="Secp256k1Context"/> object from the library</returns>
- /// <exception cref="OutOfMemoryException"></exception>
- public Secp256k1Context CreateContext()
- {
- //Protect for released lib
- SafeLibHandle.ThrowIfClosed();
-
- //Create new context
- IntPtr context = _create(1);
-
- if (context == IntPtr.Zero)
- {
- throw new OutOfMemoryException("Failed to create the new Secp256k1 context");
- }
-
- return new Secp256k1Context(this, context);
- }
-
- protected override void Free()
- {
- //Free native library
- SafeLibHandle.Dispose();
- }
-
- private record class FallbackRandom : IRandomSource
- {
- public void GetRandomBytes(Span<byte> buffer)
- {
- //Use the random generator from the crypto lib
- RandomHash.GetRandomBytes(buffer);
- }
- }
- }
-} \ No newline at end of file
diff --git a/lib/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj b/lib/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj
deleted file mode 100644
index efd78ab..0000000
--- a/lib/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj
+++ /dev/null
@@ -1,27 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
- <PropertyGroup>
- <TargetFramework>net8.0</TargetFramework>
- <Nullable>enable</Nullable>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
- <PackageReadmeFile>README.md</PackageReadmeFile>
- <RootNamespace>NVault.Crypto.Secp256k1</RootNamespace>
- <AssemblyName>NVault.Crypto.Secp256k1</AssemblyName>
- </PropertyGroup>
-
- <PropertyGroup>
- <Authors>Vaughn Nugent</Authors>
- <Company>Vaughn Nugent</Company>
- <Product>NVault.Crypto.Secp256k1</Product>
- <Description>Provides a managed library for the Bitcoin Core secp256k1 library, along with other helper types for NVault</Description>
- <Copyright>Copyright © 2024 Vaughn Nugent</Copyright>
- <PackageProjectUrl>https://www.vaughnnugent.com/resources/software/modules/NVault</PackageProjectUrl>
- <RepositoryUrl>https://github.com/VnUgE/NVault/tree/master/</RepositoryUrl>
- </PropertyGroup>
-
- <ItemGroup>
- <PackageReference Include="VNLib.Hashing.Portable" Version="0.1.0-ci0114" />
- <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0114" />
- </ItemGroup>
-
-</Project>
diff --git a/lib/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs b/lib/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs
deleted file mode 100644
index c82321c..0000000
--- a/lib/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs
+++ /dev/null
@@ -1,56 +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.Runtime.InteropServices;
-
-namespace NVault.Crypto.Secp256k1
-{
- [StructLayout(LayoutKind.Sequential)]
- public unsafe readonly ref struct Secp256HashFuncState
- {
-
- /// <summary>
- /// The opaque pointer passed to the hash function
- /// </summary>
- public readonly IntPtr Opaque { get; }
-
- private readonly byte* _output;
- private readonly byte* _xCoord;
- private readonly int _outputLength;
- private readonly int _xCoordLength;
-
- internal Secp256HashFuncState(byte* output, int outputLength, byte* xCoord, int xCoordLength, IntPtr opaque)
- {
- Opaque = opaque;
- _output = output;
- _outputLength = outputLength;
- _xCoord = xCoord;
- _xCoordLength = xCoordLength;
- }
-
- /// <summary>
- /// Gets the output buffer as a span
- /// </summary>
- /// <returns>The output buffer span</returns>
- public readonly Span<byte> GetOutput() => new(_output, _outputLength);
-
- /// <summary>
- /// Gets the x coordinate argument as a span
- /// </summary>
- /// <returns>The xcoordinate buffer span</returns>
- public readonly ReadOnlySpan<byte> GetXCoordArg() => new(_xCoord, _xCoordLength);
- }
-} \ No newline at end of file
diff --git a/lib/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs b/lib/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs
deleted file mode 100644
index dfb3ff8..0000000
--- a/lib/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs
+++ /dev/null
@@ -1,76 +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 VNLib.Utils.Extensions;
-using VNLib.Utils.Memory;
-
-using static NVault.Crypto.Secp256k1.LibSecp256k1;
-
-namespace NVault.Crypto.Secp256k1
-{
- /// <summary>
- /// Represents a Secp256k1 context, it is used to randomize the instance, create key pairs,
- /// and frees the context when disposed
- /// </summary>
- /// <param name="Lib">The <see cref="LibSecp256k1"/> library instance</param>
- /// <param name="Context">A pointer to the initialized context instance</param>
- public readonly record struct Secp256k1Context(LibSecp256k1 Lib, IntPtr Context) : IDisposable
- {
- /// <summary>
- /// Randomizes the context with random data using the library's random source
- /// </summary>
- /// <returns>True if the context was successfully randomized, false otherwise</returns>
- public unsafe readonly bool Randomize()
- {
- Lib.SafeLibHandle.ThrowIfClosed();
-
- //Randomze the context
- byte* entropy = stackalloc byte[RandomBufferSize];
-
- //Get random bytes
- Lib.GetRandomBytes(new Span<byte>(entropy, RandomBufferSize));
-
- //call native randomize method
- bool result = Lib._randomize(Context, entropy) == 1;
-
- //Zero the randomness buffer before returning to avoid leaking random data
- MemoryUtil.InitializeBlock(entropy, RandomBufferSize);
-
- return result;
- }
-
- internal unsafe readonly bool CreateKeyPair(KeyPair* keyPair, Secp256k1SecretKey* secretKey)
- {
- Lib.SafeLibHandle.ThrowIfClosed();
-
- //Create the keypair from the secret key
- return Lib._createKeyPair(Context, keyPair, (byte*)secretKey) == 1;
- }
-
- /// <summary>
- /// Releases the context instance and frees the memory
- /// </summary>
- public readonly void Dispose()
- {
- if (Context != IntPtr.Zero)
- {
- //Free the context
- Lib._destroy(Context);
- }
- }
- }
-} \ No newline at end of file
diff --git a/lib/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs b/lib/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs
deleted file mode 100644
index 35734ae..0000000
--- a/lib/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs
+++ /dev/null
@@ -1,58 +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.Runtime.InteropServices;
-
-using VNLib.Utils.Memory;
-
-namespace NVault.Crypto.Secp256k1
-{
- /// <summary>
- /// Represents a Secp256k1 secret key, the size is fixed, and should use
- /// the sizeof() operator to get the size
- /// </summary>
- [StructLayout(LayoutKind.Sequential, Size = 32)]
- public unsafe struct Secp256k1SecretKey
- {
- private fixed byte data[32];
-
- /// <summary>
- /// Implict cast to a span of raw bytes
- /// </summary>
- /// <param name="key">The secret key to cast</param>
- public static implicit operator Span<byte>(Secp256k1SecretKey key) => new(key.data, 32);
-
- /// <summary>
- /// Casts the secret key span to a <see cref="Secp256k1SecretKey"/> via a structure copy
- /// </summary>
- /// <param name="key">The key data to copy</param>
- /// <exception cref="ArgumentOutOfRangeException"></exception>
- public static explicit operator Secp256k1SecretKey(ReadOnlySpan<byte> key) => FromSpan(key);
-
- /// <summary>
- /// Creates a new <see cref="Secp256k1SecretKey"/> from a span of bytes
- /// by copying the bytes into the struct
- /// </summary>
- /// <param name="span">The secret key data to copy</param>
- /// <returns>An initilaized <see cref="Secp256k1SecretKey"/></returns>
- public static Secp256k1SecretKey FromSpan(ReadOnlySpan<byte> span)
- {
- Secp256k1SecretKey newKey = new();
- MemoryUtil.CopyStruct(span, ref newKey);
- return newKey;
- }
- }
-} \ No newline at end of file
diff --git a/lib/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj b/lib/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj
index e5dbe8c..dbbccfa 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-ci0047" />
+ <PackageReference Include="VNLib.Plugins.Extensions.Loading" Version="0.1.0-ci0049" />
</ItemGroup>
</Project>
diff --git a/nvault.build.sln b/nvault.build.sln
index 50c74c8..fd02f20 100644
--- a/nvault.build.sln
+++ b/nvault.build.sln
@@ -13,11 +13,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NVault.Crypto.Noscrypt", "lib\NVault.Crypto.Noscrypt\src\NVault.Crypto.Noscrypt.csproj", "{686B9DB8-5BA9-49AB-9761-6B661A5027BD}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NVault.Crypto.Secp256k1", "lib\NVault.Crypto.Secp256k1\src\NVault.Crypto.Secp256k1.csproj", "{7952F25A-DA19-492E-B797-839082053DE4}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NVault.VaultExtensions", "lib\NVault.VaultExtensions\src\NVault.VaultExtensions.csproj", "{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NVault.Crypto.NoscryptTests", "lib\NVault.Crypto.Noscrypt\NVault.Crypto.NoscryptTests\NVault.Crypto.NoscryptTests.csproj", "{7273CA9C-BA83-4438-8CDF-0B1605CD03CD}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NVault.Crypto.NoscryptTests", "lib\NVault.Crypto.Noscrypt\tests\NVault.Crypto.NoscryptTests.csproj", "{79879934-807C-4A39-B987-334249F3F6E7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -33,18 +31,14 @@ Global
{686B9DB8-5BA9-49AB-9761-6B661A5027BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{686B9DB8-5BA9-49AB-9761-6B661A5027BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{686B9DB8-5BA9-49AB-9761-6B661A5027BD}.Release|Any CPU.Build.0 = Release|Any CPU
- {7952F25A-DA19-492E-B797-839082053DE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7952F25A-DA19-492E-B797-839082053DE4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7952F25A-DA19-492E-B797-839082053DE4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7952F25A-DA19-492E-B797-839082053DE4}.Release|Any CPU.Build.0 = Release|Any CPU
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1}.Release|Any CPU.Build.0 = Release|Any CPU
- {7273CA9C-BA83-4438-8CDF-0B1605CD03CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7273CA9C-BA83-4438-8CDF-0B1605CD03CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7273CA9C-BA83-4438-8CDF-0B1605CD03CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7273CA9C-BA83-4438-8CDF-0B1605CD03CD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {79879934-807C-4A39-B987-334249F3F6E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {79879934-807C-4A39-B987-334249F3F6E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {79879934-807C-4A39-B987-334249F3F6E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {79879934-807C-4A39-B987-334249F3F6E7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -52,9 +46,8 @@ Global
GlobalSection(NestedProjects) = preSolution
{7ADAE9C2-1739-4E91-B59C-DB3B07909C07} = {352C778E-C12D-483D-9C10-7985D81A8E10}
{686B9DB8-5BA9-49AB-9761-6B661A5027BD} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
- {7952F25A-DA19-492E-B797-839082053DE4} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
{91F33820-C0CE-41FA-89A5-2B45FD0CB2B1} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
- {7273CA9C-BA83-4438-8CDF-0B1605CD03CD} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
+ {79879934-807C-4A39-B987-334249F3F6E7} = {74CA17B7-CE49-4E1F-A84D-0D2ABA0C96F5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ACF01A45-B7F0-4055-84F2-A3596713C9BB}