diff options
Diffstat (limited to 'back-end')
24 files changed, 235 insertions, 1370 deletions
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs deleted file mode 100644 index bb014df..0000000 --- a/back-end/libs/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/back-end/libs/NVault.Crypto.Secp256k1/src/IRandomSource.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/IRandomSource.cs deleted file mode 100644 index 4e1861d..0000000 --- a/back-end/libs/NVault.Crypto.Secp256k1/src/IRandomSource.cs +++ /dev/null @@ -1,32 +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; - -namespace NVault.Crypto.Secp256k1 -{ - /// <summary> - /// Represents a generator for random data, that fills abinary buffer with random bytes - /// on demand. - /// </summary> - public interface IRandomSource - { - /// <summary> - /// Fills the given buffer with random bytes - /// </summary> - /// <param name="buffer">Binary buffer to fill with random data</param> - void GetRandomBytes(Span<byte> buffer); - } -}
\ No newline at end of file diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs deleted file mode 100644 index 8dda269..0000000 --- a/back-end/libs/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/back-end/libs/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj b/back-end/libs/NVault.Crypto.Secp256k1/src/NVault.Crypto.Secp256k1.csproj deleted file mode 100644 index 5014d89..0000000 --- a/back-end/libs/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-ci0109" /> - <PackageReference Include="VNLib.Utils" Version="0.1.0-ci0109" /> - </ItemGroup> - -</Project> diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs deleted file mode 100644 index c82321c..0000000 --- a/back-end/libs/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/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs deleted file mode 100644 index dfb3ff8..0000000 --- a/back-end/libs/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/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs deleted file mode 100644 index 35734ae..0000000 --- a/back-end/libs/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/back-end/libs/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs deleted file mode 100644 index 360de21..0000000 --- a/back-end/libs/NVault.Crypto.Secp256k1/src/UnmanagedRandomSource.cs +++ /dev/null @@ -1,101 +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; -using VNLib.Utils.Native; -using VNLib.Utils.Extensions; - -namespace NVault.Crypto.Secp256k1 -{ - - /// <summary> - /// A wrapper class for an unmanaged random source that conforms to the <see cref="IRandomSource"/> interface - /// </summary> - public class UnmanagedRandomSource : VnDisposeable, IRandomSource - { - public const string METHOD_NAME = "getRandomBytes"; - - unsafe delegate void UnmanagedRandomSourceDelegate(byte* buffer, int size); - - - private readonly bool OwnsHandle; - private readonly SafeLibraryHandle _library; - private readonly UnmanagedRandomSourceDelegate _getRandomBytes; - - /// <summary> - /// Loads the unmanaged random source from the given library - /// and attempts to get the random bytes method <see cref="METHOD_NAME"/> - /// </summary> - /// <param name="path"></param> - /// <param name="search"></param> - /// <returns>The wrapped library that conforms to the <see cref="IRandomSource"/></returns> - public static UnmanagedRandomSource LoadLibrary(string path, DllImportSearchPath search) - { - //Try to load the library - SafeLibraryHandle lib = SafeLibraryHandle.LoadLibrary(path, search); - try - { - return new UnmanagedRandomSource(lib, true); - } - catch - { - //release lib - lib.Dispose(); - throw; - } - } - - /// <summary> - /// Creates the unmanaged random source from the given library - /// </summary> - /// <param name="lib">The library handle to wrap</param> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="EntryPointNotFoundException"></exception> - public UnmanagedRandomSource(SafeLibraryHandle lib, bool ownsHandle) - { - lib.ThrowIfClosed(); - - _library = lib; - - //get the method delegate - _getRandomBytes = lib.DangerousGetMethod<UnmanagedRandomSourceDelegate>(METHOD_NAME); - - OwnsHandle = ownsHandle; - } - - public unsafe void GetRandomBytes(Span<byte> buffer) - { - _library.ThrowIfClosed(); - - //Fix buffer and call unmanaged method - fixed(byte* ptr = &MemoryMarshal.GetReference(buffer)) - { - _getRandomBytes(ptr, buffer.Length); - } - } - - ///<inheritdoc/> - protected override void Free() - { - if (OwnsHandle) - { - _library.Dispose(); - } - } - } -}
\ No newline at end of file diff --git a/back-end/libs/NVault.VaultExtensions/src/IClientAccessScope.cs b/back-end/libs/NVault.VaultExtensions/src/IClientAccessScope.cs deleted file mode 100644 index c79f75e..0000000 --- a/back-end/libs/NVault.VaultExtensions/src/IClientAccessScope.cs +++ /dev/null @@ -1,48 +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.Collections.Generic; - -namespace NVault.VaultExtensions -{ - /// <summary> - /// Represents a user auth token access scope - /// configuration. - /// </summary> - public interface IClientAccessScope - { - /// <summary> - /// The list of policies for new token generation - /// </summary> - IList<string> Policies { get; } - - /// <summary> - /// Allows the user to renew the access token - /// </summary> - bool Renewable { get; } - - /// <summary> - /// The token - /// </summary> - string TokenTtl { get; } - - /// <summary> - /// The explicit number of token uses allowed by the genreated token, - /// 0 for unlimited uses - /// </summary> - int NumberOfUses { get; } - } -}
\ No newline at end of file diff --git a/back-end/libs/NVault.VaultExtensions/src/IKvVaultStore.cs b/back-end/libs/NVault.VaultExtensions/src/IKvVaultStore.cs deleted file mode 100644 index 037fe6c..0000000 --- a/back-end/libs/NVault.VaultExtensions/src/IKvVaultStore.cs +++ /dev/null @@ -1,52 +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.Threading.Tasks; - -using VNLib.Utils.Memory; - -namespace NVault.VaultExtensions -{ - /// <summary> - /// Represents a vault key-value store that can be used to store secrets - /// </summary> - public interface IKvVaultStore - { - /// <summary> - /// Deletes a secret from the vault - /// </summary> - /// <param name="user">The user scope of the secret</param> - /// <param name="path">The path to the secret</param> - /// <returns>A task that returns when the operation has completed</returns> - Task DeleteSecretAsync(VaultUserScope user, string path); - - /// <summary> - /// Sets a secret in the vault at the specified path and user scope - /// </summary> - /// <param name="user">The user scope to store the value at</param> - /// <param name="path">The path to the secret</param> - /// <param name="secret">The secret value to set</param> - /// <returns>A task that resolves when the secret has been updated</returns> - Task SetSecretAsync(VaultUserScope user, string path, PrivateString secret); - - /// <summary> - /// Gets a secret from the vault at the specified path and user scope - /// </summary> - /// <param name="user">The user scope to get the value from</param> - /// <param name="path">The secret path</param> - /// <returns>A task that resolves the secret if found, null otherwise</returns> - Task<PrivateString?> GetSecretAsync(VaultUserScope user, string path); - } -}
\ No newline at end of file diff --git a/back-end/libs/NVault.VaultExtensions/src/IVaultClientScope.cs b/back-end/libs/NVault.VaultExtensions/src/IVaultClientScope.cs deleted file mode 100644 index d53bc4a..0000000 --- a/back-end/libs/NVault.VaultExtensions/src/IVaultClientScope.cs +++ /dev/null @@ -1,33 +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/>. - -namespace NVault.VaultExtensions -{ - /// <summary> - /// Represents a vault client scope configuration - /// </summary> - public interface IVaultClientScope - { - /// <summary> - /// The mount point for the vault - /// </summary> - string? MountPoint { get; } - - /// <summary> - /// The entry path for the vault - /// </summary> - string? EntryPath { get; } - } -}
\ No newline at end of file diff --git a/back-end/libs/NVault.VaultExtensions/src/IVaultKvClientScope.cs b/back-end/libs/NVault.VaultExtensions/src/IVaultKvClientScope.cs deleted file mode 100644 index f763473..0000000 --- a/back-end/libs/NVault.VaultExtensions/src/IVaultKvClientScope.cs +++ /dev/null @@ -1,29 +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/>. - -namespace NVault.VaultExtensions -{ - /// <summary> - /// A key-value specific scoped client - /// </summary> - public interface IVaultKvClientScope : IVaultClientScope - { - /// <summary> - /// The property to store the secret value in the - /// storage dictionary - /// </summary> - string StorageProperty { get; } - } -}
\ No newline at end of file diff --git a/back-end/libs/NVault.VaultExtensions/src/KvVaultStorage.cs b/back-end/libs/NVault.VaultExtensions/src/KvVaultStorage.cs deleted file mode 100644 index 8a2b9b6..0000000 --- a/back-end/libs/NVault.VaultExtensions/src/KvVaultStorage.cs +++ /dev/null @@ -1,66 +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.Threading.Tasks; - -using VaultSharp; - -using VNLib.Utils.Memory; - -namespace NVault.VaultExtensions -{ - /// <summary> - /// An abstract kv storage implementation that uses the vault client to store secrets - /// </summary> - public abstract class KvVaultStorage : IKvVaultStore - { - /// <summary> - /// The vault client - /// </summary> - protected abstract IVaultClient Client { get; } - - /// <summary> - /// The storage scope - /// </summary> - protected abstract IVaultKvClientScope Scope { get; } - - public virtual Task DeleteSecretAsync(VaultUserScope user, string path) - { - string tPath = TranslatePath(path); - return Client.DeleteSecretAsync(Scope, user, tPath); - } - - public virtual Task SetSecretAsync(VaultUserScope user, string path, PrivateString secret) - { - string tPath = TranslatePath(path); - return Client.SetSecretAsync(Scope, user, tPath, secret); - } - - public virtual Task<PrivateString?> GetSecretAsync(VaultUserScope user, string path) - { - string tPath = TranslatePath(path); - return Client.GetSecretAsync(Scope, user, tPath); - } - - /// <summary> - /// Translates a realtive item path to a full path - /// within the scope of the storage. This may be used to - /// extend the scope of the operation - /// </summary> - /// <param name="path">The item path to scope</param> - /// <returns>The further scoped vault path for the item</returns> - public virtual string TranslatePath(string path) => path; - } -}
\ No newline at end of file diff --git a/back-end/libs/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj b/back-end/libs/NVault.VaultExtensions/src/NVault.VaultExtensions.csproj deleted file mode 100644 index e5dbe8c..0000000 --- a/back-end/libs/NVault.VaultExtensions/src/NVault.VaultExtensions.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.VaultExtensions</RootNamespace> - <AssemblyName>NVault.VaultExtensions</AssemblyName> - </PropertyGroup> - - <PropertyGroup> - <Authors>Vaughn Nugent</Authors> - <Company>Vaughn Nugent</Company> - <Product>NVault.VaultExtensions</Product> - <Description>A Hashicorp Vault unified extension library 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="VaultSharp" Version="1.13.0.1" /> - <PackageReference Include="VNLib.Plugins.Extensions.Loading" Version="0.1.0-ci0047" /> - </ItemGroup> - -</Project> diff --git a/back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs b/back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs deleted file mode 100644 index d90941a..0000000 --- a/back-end/libs/NVault.VaultExtensions/src/VaultClientExtensions.cs +++ /dev/null @@ -1,156 +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.Threading; -using System.Threading.Tasks; -using System.Collections.Generic; - -using VaultSharp; -using VaultSharp.V1.Commons; - -using VNLib.Utils.Memory; -using VNLib.Plugins.Essentials.Extensions; - - -namespace NVault.VaultExtensions -{ - - public static class VaultClientExtensions - { - - private static string GetKeyPath(IVaultClientScope client, in VaultUserScope scope, string itemPath) - { - //Allow for null entry path - return client.EntryPath == null ? $"{scope.UserId}/{itemPath}" : $"{client.EntryPath}/{scope.UserId}/{itemPath}"; - } - - - public static Task<PrivateString?> GetSecretAsync(this IVaultClient client, IVaultKvClientScope scope, VaultUserScope user, string path) - { - return GetSecretAsync(client, scope, user, path, scope.StorageProperty); - } - - public static async Task<PrivateString?> GetSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path, string property) - { - //Get the path complete path for the scope - string fullPath = GetKeyPath(scope, user, path); - - //Get the secret from the vault - Secret<SecretData> result = await client.V1.Secrets.KeyValue.V2.ReadSecretAsync(fullPath, mountPoint:scope.MountPoint); - - //Try to get the secret value from the store - string? value = result.Data.Data.GetValueOrDefault(property)?.ToString(); - - //Return the secret value as a private string - return value == null ? null : PrivateString.ToPrivateString(value, true); - } - - /// <summary> - /// Writes a secret to the vault that is scoped by the vault scope, and the user scope. - /// </summary> - /// <param name="client"></param> - /// <param name="scope">The client scope configuration</param> - /// <param name="user">The user scope to isolate the </param> - /// <param name="path">The item path within the current scope</param> - /// <param name="secret">The secret value to set at the desired property</param> - /// <returns>A task that resolves when the secret has been updated</returns> - public static async Task<CurrentSecretMetadata> SetSecretAsync(this IVaultClient client, IVaultKvClientScope scope, VaultUserScope user, string path, PrivateString secret) - { - Dictionary<string, string> secretDict = new() - { - //Dangerous cast, but we know the type - { scope.StorageProperty, (string)secret } - }; - - //Await the result so we be sure the secret is not destroyed - return await SetSecretAsync(client, scope, user, path, secretDict); - } - - /// <summary> - /// Writes a secret to the vault that is scoped by the vault scope, and the user scope. - /// </summary> - /// <param name="client"></param> - /// <param name="scope">The client scope configuration</param> - /// <param name="user">The user scope to isolate the </param> - /// <param name="path">The item path within the current scope</param> - /// <param name="secret">The secret value to set at the desired property</param> - /// <returns>A task that resolves when the secret has been updated</returns> - public static async Task<CurrentSecretMetadata> SetSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path, IDictionary<string, string> secret) - { - //Get the path complete path for the scope - string fullPath = GetKeyPath(scope, user, path); - - //Get the secret from the vault - Secret<CurrentSecretMetadata> result = await client.V1.Secrets.KeyValue.V2.WriteSecretAsync(fullPath, secret, mountPoint:scope.MountPoint); - - return result.Data; - } - - /// <summary> - /// Deletes a secret from the vault that is scoped by the vault scope, and the user scope. - /// </summary> - /// <param name="client"></param> - /// <param name="scope">The client scope</param> - /// <param name="user">The vault user scope</param> - /// <param name="path">The path to the storage</param> - /// <returns>A task that resolves when the delete operation has completed</returns> - public static Task DeleteSecretAsync(this IVaultClient client, IVaultClientScope scope, VaultUserScope user, string path) - { - string fullApth = GetKeyPath(scope, user, path); - return client.V1.Secrets.KeyValue.V2.DeleteSecretAsync(fullApth, mountPoint:scope.MountPoint); - } - - /// <summary> - /// Deletes a secret from the vault - /// </summary> - /// <param name="user">The user scope of the secret</param> - /// <param name="path">The path to the secret</param> - /// <param name="cancellation">A token to cancel the operation</param> - /// <returns>A task that returns when the operation has completed</returns> - public static Task DeleteSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, CancellationToken cancellation) - { - return store.DeleteSecretAsync(user, path).WaitAsync(cancellation); - } - - - /// <summary> - /// Gets a secret from the vault at the specified path and user scope - /// </summary> - /// <param name="user">The user scope to get the value from</param> - /// <param name="path">The secret path</param> - /// <param name="cancellation">A token to cancel the operation</param> - /// <returns>A task that resolves the secret if found, null otherwise</returns> - public static Task<PrivateString?> GetSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, CancellationToken cancellation) - { - return store.GetSecretAsync(user, path).WaitAsync(cancellation); - } - - - /// <summary> - /// Sets a secret in the vault at the specified path and user scope - /// </summary> - /// <param name="user">The user scope to store the value at</param> - /// <param name="path">The path to the secret</param> - /// <param name="secret">The secret value to set</param> - /// <param name="cancellation">The cancellation token</param> - /// <returns>A task that resolves when the secret has been updated</returns> - public static Task SetSecretAsync(this IKvVaultStore store, VaultUserScope user, string path, PrivateString secret, CancellationToken cancellation) - { - return store.SetSecretAsync(user, path, secret).WaitAsync(cancellation); - } - - - } -}
\ No newline at end of file diff --git a/back-end/libs/NVault.VaultExtensions/src/VaultUserScope.cs b/back-end/libs/NVault.VaultExtensions/src/VaultUserScope.cs deleted file mode 100644 index 0e8796c..0000000 --- a/back-end/libs/NVault.VaultExtensions/src/VaultUserScope.cs +++ /dev/null @@ -1,25 +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/>. - -namespace NVault.VaultExtensions -{ - /// <summary> - /// Represents a user scope for the vault. It isolates the user's - /// secrets from other users. - /// </summary> - /// <param name="UserId">The id of the user to scope the vault to</param> - public readonly record struct VaultUserScope(string UserId) - { } -}
\ No newline at end of file diff --git a/back-end/plugins/nvault/src/Endpoints/Endpoint.cs b/back-end/plugins/nvault/src/Endpoints/Endpoint.cs index b8aa0c9..5e8bf09 100644 --- a/back-end/plugins/nvault/src/Endpoints/Endpoint.cs +++ b/back-end/plugins/nvault/src/Endpoints/Endpoint.cs @@ -59,6 +59,7 @@ namespace NVault.Plugins.Vault.Endpoints private readonly INostrOperations _vault; private readonly NostrRelayStore _relays; private readonly NostrKeyMetaStore _publicKeyStore; + private readonly NostrEventHistoryStore _eventHistoryStore; private readonly bool AllowDelete; private readonly ILogProvider? _abnoxiousLog; @@ -69,12 +70,13 @@ namespace NVault.Plugins.Vault.Endpoints AllowDelete = config.TryGetValue("allow_delete", out JsonElement adEl) && adEl.GetBoolean(); - - DbContextOptions options = plugin.GetContextOptions(); + IAsyncLazy<DbContextOptions> options = plugin.GetContextOptionsAsync(); _relays = new NostrRelayStore(options); _publicKeyStore = new NostrKeyMetaStore(options); - _vault = new NostrOpProvider(plugin); + _eventHistoryStore = new NostrEventHistoryStore(options); + + _vault = new NostrOpProvider(plugin); //Check for obnoxious logging if (plugin.HostArgs.HasArgument("--nvault-obnoxious")) @@ -118,6 +120,22 @@ namespace NVault.Plugins.Vault.Endpoints return VfReturnType.VirtualSkip; } + if(entity.QueryArgs.IsArgumentSet("type", "getEvents")) + { + //Get the event history + List<NostrEventEntry> events = _eventHistoryStore.ListRental.Rent(); + + //Get the first page of events for the user + await _eventHistoryStore.GetUserPageAsync(events, entity.Session.UserID, 0, 100); + + //Return all events for the user + entity.CloseResponseJson(HttpStatusCode.OK, events); + + _eventHistoryStore.ListRental.Return(events); + + return VfReturnType.VirtualSkip; + } + return VfReturnType.NotFound; } @@ -174,6 +192,15 @@ namespace NVault.Plugins.Vault.Endpoints return VirtualOk(entity, webm); } + //Create new event entry and store it + NostrEventEntry newEvent = NostrEventEntry.FromEvent(entity.Session.UserID, nEvent); + result = await _eventHistoryStore.CreateUserRecordAsync(newEvent, entity.Session.UserID, entity.EventCancellation); + + if (!result) + { + Log.Warn("Failed to store event in history, {evid} for user {userid}", nEvent.Id, entity.Session.UserID[..8]); + } + webm.Result = nEvent; webm.Success = true; @@ -443,21 +470,23 @@ namespace NVault.Plugins.Vault.Endpoints { ValErrWebMessage webMessage = new (); - if(entity.QueryArgs.IsArgumentSet("type", "identity")) + //common id argument + string? id = entity.QueryArgs.GetValueOrDefault("id"); + + if (entity.QueryArgs.IsArgumentSet("type", "identity")) { if (webMessage.Assert(AllowDelete, "Deleting identies are now allowed")) { return VirtualClose(entity, webMessage, HttpStatusCode.Forbidden); } - if (!entity.QueryArgs.TryGetNonEmptyValue("key_id", out string? keyId)) + if (webMessage.Assert(id != null, "No key id specified")) { - webMessage.Result = "No key id specified"; return VirtualClose(entity, webMessage, HttpStatusCode.BadRequest); } //Get the key metadata - NostrKeyMeta? meta = await _publicKeyStore.GetSingleUserRecordAsync(keyId, entity.Session.UserID); + NostrKeyMeta? meta = await _publicKeyStore.GetSingleUserRecordAsync(id, entity.Session.UserID); if (webMessage.Assert(meta != null, "Key metadata not found")) { @@ -471,13 +500,56 @@ namespace NVault.Plugins.Vault.Endpoints await _vault.DeleteCredentialAsync(scope, meta, entity.EventCancellation); //Remove the key metadata - await _publicKeyStore.DeleteUserRecordAsync(keyId, entity.Session.UserID); + await _publicKeyStore.DeleteUserRecordAsync(id, entity.Session.UserID); webMessage.Result = "Successfully deleted identity"; webMessage.Success = true; return VirtualOk(entity, webMessage); } + if(entity.QueryArgs.IsArgumentSet("type", "relay")) + { + if(webMessage.Assert(id != null, "No relay id specified")) + { + return VirtualClose(entity, webMessage, HttpStatusCode.BadRequest); + } + + //Delete the relay + if(await _relays.DeleteUserRecordAsync(id, entity.Session.UserID)) + { + webMessage.Result = "Successfully deleted relay"; + webMessage.Success = true; + } + else + { + webMessage.Result = "Failed to delete relay"; + } + + return VirtualOk(entity, webMessage); + } + + if(entity.QueryArgs.IsArgumentSet("type", "event")) + { + //Internal event id is required + if(webMessage.Assert(id != null, "No event id specified")) + { + return VirtualClose(entity, webMessage, HttpStatusCode.BadRequest); + } + + //Delete the event + if(await _eventHistoryStore.DeleteUserRecordAsync(id, entity.Session.UserID)) + { + webMessage.Result = "Successfully deleted event"; + webMessage.Success = true; + } + else + { + webMessage.Result = "Failed to delete event"; + } + + return VirtualOk(entity, webMessage); + } + return VfReturnType.NotFound; } diff --git a/back-end/plugins/nvault/src/Model/NostrContext.cs b/back-end/plugins/nvault/src/Model/NostrContext.cs index 15900e2..bdd1319 100644 --- a/back-end/plugins/nvault/src/Model/NostrContext.cs +++ b/back-end/plugins/nvault/src/Model/NostrContext.cs @@ -25,7 +25,9 @@ namespace NVault.Plugins.Vault.Model { public DbSet<NostrRelay> Relays { get; set; } - public DbSet<NostrKeyMeta> PublicKeys { get; set; } + public DbSet<NostrKeyMeta> NostrPublicKeys { get; set; } + + public DbSet<NostrEventEntry> NostrEvents { get; set; } public NostrContext() { } @@ -68,7 +70,7 @@ namespace NVault.Plugins.Vault.Model .WithColumn(r => r.Version); //Setup public key table - builder.DefineTable<NostrKeyMeta>(nameof(PublicKeys)) + builder.DefineTable<NostrKeyMeta>(nameof(NostrPublicKeys)) .WithColumn(r => r.Id) .Next() @@ -95,6 +97,29 @@ namespace NVault.Plugins.Vault.Model //Finally, version, it should be set to the timestamp from annotations .WithColumn(r => r.Version); + + //Setup event table + builder.DefineTable<NostrEventEntry>(nameof(NostrEvents)) + .WithColumn(r => r.Id) //PK attribute is set from model base + .Next() + + .WithColumn(r => r.UserId) + .Next() + + .WithColumn(r => r.EventData) + .AllowNull(true) + .Next() + + .WithColumn(r => r.Created) + .AllowNull(false) + .Next() + + .WithColumn(r => r.LastModified) + .AllowNull(false) + .Next() + + //Finally, version, it should be set to the timestamp from annotations + .WithColumn(r => r.Version); } } } diff --git a/back-end/plugins/nvault/src/Model/NostrEvent.cs b/back-end/plugins/nvault/src/Model/NostrEvent.cs index 9bbfd63..ca01e1a 100644 --- a/back-end/plugins/nvault/src/Model/NostrEvent.cs +++ b/back-end/plugins/nvault/src/Model/NostrEvent.cs @@ -22,6 +22,7 @@ using VNLib.Plugins.Extensions.Validation; namespace NVault.Plugins.Vault.Model { + internal sealed class NostrEvent { public const int MAX_CONTENT_LENGTH = 16 * 1024; @@ -86,5 +87,6 @@ namespace NVault.Plugins.Vault.Model return val; } + } }
\ No newline at end of file diff --git a/back-end/plugins/nvault/src/Model/NostrEventEntry.cs b/back-end/plugins/nvault/src/Model/NostrEventEntry.cs new file mode 100644 index 0000000..9fa5aa2 --- /dev/null +++ b/back-end/plugins/nvault/src/Model/NostrEventEntry.cs @@ -0,0 +1,47 @@ +// 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.Text.Json; +using System.Text.Json.Serialization; + +using VNLib.Plugins.Extensions.Data; +using VNLib.Plugins.Extensions.Data.Abstractions; + + +namespace NVault.Plugins.Vault.Model +{ + internal sealed class NostrEventEntry : DbModelBase, IUserEntity + { + public override string Id { get; set; } + + public override DateTime Created { get; set; } + + public override DateTime LastModified { get; set; } + + //Never share userids with the client + [JsonIgnore] + public string? UserId { get; set; } + + public string? EventData { get; set; } + + public static NostrEventEntry FromEvent(string userId, NostrEvent @event) => new() + { + EventData= JsonSerializer.Serialize(@event), + UserId = userId, + }; + + } +}
\ No newline at end of file diff --git a/back-end/plugins/nvault/src/Model/NostrEventHistoryStore.cs b/back-end/plugins/nvault/src/Model/NostrEventHistoryStore.cs new file mode 100644 index 0000000..68b13f7 --- /dev/null +++ b/back-end/plugins/nvault/src/Model/NostrEventHistoryStore.cs @@ -0,0 +1,69 @@ +// 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.Linq; + +using Microsoft.EntityFrameworkCore; + +using VNLib.Plugins.Extensions.Data; +using VNLib.Plugins.Extensions.Data.Abstractions; +using VNLib.Plugins.Extensions.Loading; + +namespace NVault.Plugins.Vault.Model +{ + internal class NostrEventHistoryStore(IAsyncLazy<DbContextOptions> Options) : DbStore<NostrEventEntry> + { + ///<inheritdoc/> + public override IDbQueryLookup<NostrEventEntry> QueryTable { get; } = new DbQueries(); + + ///<inheritdoc/> + public override IDbContextHandle GetNewContext() => new NostrContext(Options.Value); + + ///<inheritdoc/> + public override string GetNewRecordId() => Guid.NewGuid().ToString("N"); + + public override void OnRecordUpdate(NostrEventEntry newRecord, NostrEventEntry existing) + { + existing.EventData = newRecord.EventData; + existing.UserId = newRecord.UserId; + newRecord.LastModified = DateTime.UtcNow; + } + + sealed record class DbQueries() : IDbQueryLookup<NostrEventEntry> + { + public IQueryable<NostrEventEntry> GetCollectionQueryBuilder(IDbContextHandle context, params string[] constraints) + { + string userId = constraints[0]; + + return from r in context.Set<NostrEventEntry>() + where r.UserId == userId + orderby r.LastModified descending + select r; + } + + public IQueryable<NostrEventEntry> GetSingleQueryBuilder(IDbContextHandle context, params string[] constraints) + { + string id = constraints[0]; + string userId = constraints[1]; + + //Get entity for the given user by its id + return from r in context.Set<NostrEventEntry>() + where r.Id == id && r.UserId == userId + select r; + } + } + } +} diff --git a/back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs b/back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs index bfb6a26..b57d021 100644 --- a/back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs +++ b/back-end/plugins/nvault/src/Model/NostrKeyMetaStore.cs @@ -20,20 +20,17 @@ using Microsoft.EntityFrameworkCore; using VNLib.Plugins.Extensions.Data; using VNLib.Plugins.Extensions.Data.Abstractions; +using VNLib.Plugins.Extensions.Loading; namespace NVault.Plugins.Vault.Model { - internal sealed class NostrKeyMetaStore : DbStore<NostrKeyMeta> + internal sealed class NostrKeyMetaStore(IAsyncLazy<DbContextOptions> Options) : DbStore<NostrKeyMeta> { - private readonly DbContextOptions _options; - - public NostrKeyMetaStore(DbContextOptions options) => _options = options; - ///<inheritdoc/> public override IDbQueryLookup<NostrKeyMeta> QueryTable { get; } = new DbQueries(); ///<inheritdoc/> - public override IDbContextHandle GetNewContext() => new NostrContext(_options); + public override IDbContextHandle GetNewContext() => new NostrContext(Options.Value); ///<inheritdoc/> public override string GetNewRecordId() => Guid.NewGuid().ToString("N"); diff --git a/back-end/plugins/nvault/src/Model/NostrRelayStore.cs b/back-end/plugins/nvault/src/Model/NostrRelayStore.cs index 699124b..799492a 100644 --- a/back-end/plugins/nvault/src/Model/NostrRelayStore.cs +++ b/back-end/plugins/nvault/src/Model/NostrRelayStore.cs @@ -20,23 +20,18 @@ using Microsoft.EntityFrameworkCore; using VNLib.Plugins.Extensions.Data; using VNLib.Plugins.Extensions.Data.Abstractions; +using VNLib.Plugins.Extensions.Loading; namespace NVault.Plugins.Vault.Model { - internal class NostrRelayStore : DbStore<NostrRelay> + internal class NostrRelayStore(IAsyncLazy<DbContextOptions> Options) : DbStore<NostrRelay> { - private readonly DbContextOptions _options; - - public NostrRelayStore(DbContextOptions options) - { - _options = options; - } ///<inheritdoc/> public override IDbQueryLookup<NostrRelay> QueryTable { get; } = new DbQueries(); ///<inheritdoc/> - public override IDbContextHandle GetNewContext() => new NostrContext(_options); + public override IDbContextHandle GetNewContext() => new NostrContext(Options.Value); ///<inheritdoc/> public override string GetNewRecordId() => Guid.NewGuid().ToString("N"); diff --git a/back-end/plugins/nvault/src/NVault.csproj b/back-end/plugins/nvault/src/NVault.csproj index d83dd2f..e876eec 100644 --- a/back-end/plugins/nvault/src/NVault.csproj +++ b/back-end/plugins/nvault/src/NVault.csproj @@ -27,9 +27,11 @@ </ItemGroup> <ItemGroup> - <ProjectReference Include="..\..\..\libs\NVault.Crypto.Secp256k1\src\NVault.Crypto.Secp256k1.csproj" /> - <ProjectReference Include="..\..\..\libs\NVault.VaultExtensions\src\NVault.VaultExtensions.csproj" /> + <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> + <ItemGroup> <None Update="NVault.example.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> |