diff options
author | vnugent <public@vaughnnugent.com> | 2023-12-01 21:00:49 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-12-01 21:00:49 -0500 |
commit | 54984ef915a3bf640e06015bd294bd2186b3a588 (patch) | |
tree | 51bc8384138a5eca389186f178fa5fd669cf3916 /back-end/libs/NVault.Crypto.Secp256k1/src | |
parent | c4205bfe23dc321c77e2ff032fcb355d16e5d6c0 (diff) |
internal polish, minor refactors & manifest v3 progression
Diffstat (limited to 'back-end/libs/NVault.Crypto.Secp256k1/src')
5 files changed, 192 insertions, 86 deletions
diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs index 9931698..556bba7 100644 --- a/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs +++ b/back-end/libs/NVault.Crypto.Secp256k1/src/ContextExtensions.cs @@ -26,69 +26,16 @@ 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); - [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); - } - public static unsafe class ContextExtensions { /// <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="CryptographicException"></exception> - public static Secp256k1Context CreateContext(this LibSecp256k1 Lib) - { - //Protect for released lib - Lib.SafeLibHandle.ThrowIfClosed(); - - //Create new context - IntPtr context = Lib._create(1); - - if (context == IntPtr.Zero) - { - throw new CryptographicException("Failed to create the new Secp256k1 context"); - } - - return new Secp256k1Context(Lib, context); - } - - /// <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> @@ -108,13 +55,22 @@ namespace NVault.Crypto.Secp256k1 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, secretKey)) + if (!context.CreateKeyPair(&keyPair, &secKeyStruct)) { return ERRNO.E_FAIL; } @@ -161,9 +117,9 @@ namespace NVault.Crypto.Secp256k1 /// <exception cref="CryptographicException"></exception> public static ERRNO GeneratePubKeyFromSecret(this in Secp256k1Context context, ReadOnlySpan<byte> secretKey, Span<byte> pubKeyBuffer) { - if (secretKey.Length != SecretKeySize) + if (secretKey.Length != sizeof(Secp256k1SecretKey)) { - throw new CryptographicException($"Your secret key must be exactly {SecretKeySize} bytes long"); + throw new CryptographicException($"Your secret key must be exactly {sizeof(Secp256k1SecretKey)} bytes long"); } if (pubKeyBuffer.Length < XOnlyPublicKeySize) @@ -176,12 +132,13 @@ namespace NVault.Crypto.Secp256k1 //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, secretKey)) + if (!context.CreateKeyPair(&keyPair, &secKeyStruct)) { return ERRNO.E_FAIL; } @@ -220,9 +177,9 @@ namespace NVault.Crypto.Secp256k1 /// <exception cref="CryptographicException"></exception> public static bool VerifySecretKey(this in Secp256k1Context context, ReadOnlySpan<byte> secretKey) { - if (secretKey.Length != SecretKeySize) + if (secretKey.Length != sizeof(Secp256k1SecretKey)) { - throw new CryptographicException($"Your secret key must be exactly {SecretKeySize} bytes long"); + throw new CryptographicException($"Your secret key must be exactly {sizeof(Secp256k1SecretKey)} bytes long"); } context.Lib.SafeLibHandle.ThrowIfClosed(); @@ -252,9 +209,9 @@ namespace NVault.Crypto.Secp256k1 /// <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 != SecretKeySize) + if (secretKey.Length != sizeof(Secp256k1SecretKey)) { - throw new ArgumentException($"Your secret key buffer must be exactly {SecretKeySize} bytes long"); + throw new ArgumentException($"Your secret key buffer must be exactly {sizeof(Secp256k1SecretKey)} bytes long"); } //Init callback state struct diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs index f3afc33..907eaa4 100644 --- a/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs +++ b/back-end/libs/NVault.Crypto.Secp256k1/src/LibSecp256k1.cs @@ -23,16 +23,17 @@ 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 SecretKeySize = 32; 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 @@ -51,15 +52,14 @@ namespace NVault.Crypto.Secp256k1 { public fixed byte data[64]; } - //Native methods [SafeMethodName("secp256k1_context_create")] - internal delegate IntPtr CreateContext(int flags); + internal delegate IntPtr ContextCreate(int flags); [SafeMethodName("secp256k1_context_destroy")] - internal delegate void DestroyContext(IntPtr context); + internal delegate void ContextDestroy(IntPtr context); [SafeMethodName("secp256k1_context_randomize")] internal delegate int RandomizeContext(IntPtr context, byte* seed32); @@ -94,7 +94,6 @@ namespace NVault.Crypto.Secp256k1 EcdhHasFunc hashFunc, void* dataPtr ); - /// <summary> @@ -132,14 +131,32 @@ namespace NVault.Crypto.Secp256k1 } /// <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 CreateContext _create; + internal readonly ContextCreate _create; internal readonly RandomizeContext _randomize; - internal readonly DestroyContext _destroy; + internal readonly ContextDestroy _destroy; internal readonly KeypairXOnlyPub _createXonly; internal readonly XOnlyPubkeySerialize _serializeXonly; internal readonly SignHash _signHash; @@ -165,10 +182,10 @@ namespace NVault.Crypto.Secp256k1 SafeLibHandle = handle ?? throw new ArgumentNullException(nameof(handle)); //Get all method handles and store them - _create = handle.DangerousGetMethod<CreateContext>(); + _create = handle.DangerousGetMethod<ContextCreate>(); _createKeyPair = handle.DangerousGetMethod<KeypairCreate>(); _randomize = handle.DangerousGetMethod<RandomizeContext>(); - _destroy = handle.DangerousGetMethod<DestroyContext>(); + _destroy = handle.DangerousGetMethod<ContextDestroy>(); _createXonly = handle.DangerousGetMethod<KeypairXOnlyPub>(); _serializeXonly = handle.DangerousGetMethod<XOnlyPubkeySerialize>(); _signHash = handle.DangerousGetMethod<SignHash>(); @@ -205,9 +222,9 @@ namespace NVault.Crypto.Secp256k1 //Protect for released lib SafeLibHandle.ThrowIfClosed(); - if(buffer.Length != SecretKeySize) + if(buffer.Length != sizeof(Secp256k1SecretKey)) { - throw new ArgumentException($"Buffer must be exactly {SecretKeySize} bytes long", nameof(buffer)); + throw new ArgumentException($"Buffer must be exactly {sizeof(Secp256k1SecretKey)} bytes long", nameof(buffer)); } //Fill the buffer with random bytes @@ -227,6 +244,28 @@ namespace NVault.Crypto.Secp256k1 _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 diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs new file mode 100644 index 0000000..4ee745c --- /dev/null +++ b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256HashFuncState.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2023 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 index 67f1d9f..f0a795a 100644 --- a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs +++ b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1Context.cs @@ -14,7 +14,6 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. using System; -using System.Runtime.InteropServices; using VNLib.Utils.Extensions; using VNLib.Utils.Memory; @@ -54,15 +53,12 @@ namespace NVault.Crypto.Secp256k1 return result; } - internal unsafe readonly bool CreateKeyPair(KeyPair* keyPair, ReadOnlySpan<byte> secretKey) + internal unsafe readonly bool CreateKeyPair(KeyPair* keyPair, Secp256k1SecretKey* secretKey) { Lib.SafeLibHandle.ThrowIfClosed(); - fixed (byte* sk = &MemoryMarshal.GetReference(secretKey)) - { - //Create the keypair from the secret key - return Lib._createKeyPair(Context, keyPair, sk) == 1; - } + //Create the keypair from the secret key + return Lib._createKeyPair(Context, keyPair, (byte*)secretKey) == 1; } /// <summary> diff --git a/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs new file mode 100644 index 0000000..7224720 --- /dev/null +++ b/back-end/libs/NVault.Crypto.Secp256k1/src/Secp256k1SecretKey.cs @@ -0,0 +1,58 @@ +// Copyright (C) 2023 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 |