diff options
Diffstat (limited to 'wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption')
4 files changed, 451 insertions, 0 deletions
diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCUtilCipher.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCUtilCipher.cs new file mode 100644 index 0000000..72bb75a --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NCUtilCipher.cs @@ -0,0 +1,128 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +using System; +using System.Diagnostics; +using VNLib.Utils.Cryptography.Noscrypt.@internal; + +using NCResult = System.Int64; + +namespace VNLib.Utils.Cryptography.Noscrypt.Encryption +{ + internal static class NCUtilCipher + { + /* + * This class wraps the low-level cipher functions provided by + * the Noscrypt utility side-car library. + */ + + public static nint Alloc(NCContext ctx, uint version, uint flags) + { + nint cipher = GetTable(ctx).NCUtilCipherAlloc(version, flags); + + if (cipher == nint.Zero) + { + throw new OutOfMemoryException("Failed to allocate cipher context"); + } + + //Ensure flags are identical to those set during allocation + Debug.Assert(GetFlags(ctx, cipher) == flags); + + return cipher; + } + + public static uint GetFlags(NCContext ctx, nint cipher) + { + NCResult result = GetTable(ctx).NCUtilCipherGetFlags(cipher); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherGetFlagsDelegate>(result, raiseOnFailure: true); + + return (uint)result; + } + + public static void Free(NCContext ctx, nint cipher) => GetTable(ctx).NCUtilCipherFree(cipher); + + public static int GetIvSize(NCContext ctx, nint cipher) + { + NCResult result = GetTable(ctx).NCUtilCipherGetIvSize(cipher); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherGetIvSizeDelegate>(result, raiseOnFailure: true); + + return checked((int)result); + } + + public static unsafe void SetProperty(NCContext ctx, nint cipher, uint property, ref readonly byte value, uint valueLen) + { + fixed (byte* valPtr = &value) + { + NCResult result = GetTable(ctx).NCUtilCipherSetProperty(cipher, property, valPtr, valueLen); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherSetPropertyDelegate>(result, raiseOnFailure: true); + } + } + + public static uint GetOutputSize(NCContext ctx, nint cipher) + { + NCResult result = GetTable(ctx).NCUtilCipherGetOutputSize(cipher); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherGetOutputSizeDelegate>(result, raiseOnFailure: true); + + return (uint)result; + } + + public static unsafe uint ReadOutput(NCContext ctx, nint cipher, ref byte outputData, uint outLen) + { + fixed (byte* outPtr = &outputData) + { + NCResult result = GetTable(ctx).NCUtilCipherReadOutput(cipher, outPtr, outLen); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherReadOutputDelegate>(result, raiseOnFailure: true); + + return (uint)result; + } + } + + public static unsafe void InitCipher(NCContext ctx, nint cipher, byte* inputPtr, uint inputSize) + { + NCResult result = GetTable(ctx).NCUtilCipherInit(cipher, inputPtr, inputSize); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherInitDelegate>(result, raiseOnFailure: true); + } + + public static unsafe void Update( + NCContext ctx, + nint cipher, + ref readonly NCSecretKey localKey, + ref readonly NCPublicKey remoteKey + ) + { + fixed (NCSecretKey* sk = &localKey) + fixed (NCPublicKey* pk = &remoteKey) + { + NCResult result = GetTable(ctx).NCUtilCipherUpdate( + cipher: cipher, + libContext: ctx.DangerousGetHandle(), + secKey: sk, + pubKey: pk + ); + + NCUtil.CheckResult<FunctionTable.NCUtilCipherInitDelegate>(result, raiseOnFailure: true); + } + } + + private static ref readonly FunctionTable GetTable(NCContext ctx) => ref ctx.Library.Functions; + } + +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipher.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipher.cs new file mode 100644 index 0000000..b30ea44 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipher.cs @@ -0,0 +1,227 @@ +// 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.Threading; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using VNLib.Utils.Memory; +using VNLib.Utils.Cryptography.Noscrypt.Random; +using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; + +namespace VNLib.Utils.Cryptography.Noscrypt.Encryption +{ + /// <summary> + /// The noscrypt util cipher wapper + /// </summary> + /// <param name="ctx"></param> + /// <param name="flags">Cipher creation flags</param> + /// <param name="version">The cipher specification version</param> + public sealed class NoscryptCipher(NCContext ctx, NoscryptCipherVersion version, NoscryptCipherFlags flags) : VnDisposeable + { + private IMemoryHandle<byte>? _ivBuffer; + private readonly nint _cipher = NCUtilCipher.Alloc(ctx, (uint)version, (uint)flags); + + /// <summary> + /// The cipher standard version used by this instance + /// </summary> + public NoscryptCipherVersion Version => version; + + /// <summary> + /// Gets the flags set for the cipher instance + /// </summary> + public uint GetFlags() => NCUtilCipher.GetFlags(ctx, _cipher); + + /// <summary> + /// Gets the cipher's initilaization vector size (or nonce) + /// </summary> + /// <returns>The size of the IV in bytes</returns> + public int GetIvSize() => NCUtilCipher.GetIvSize(ctx, _cipher); + + /// <summary> + /// Gets the internal heap buffer that holds the cipher's initalization + /// vector. + /// </summary> + /// <returns>The mutable span of the cipher's IV buffer</returns> + public Span<byte> IvBuffer + { + get => LazyInitializer.EnsureInitialized(ref _ivBuffer, AllocIvBuffer).Span; + } + + /// <summary> + /// Sets the cipher's initialization vector to a random value using + /// the specified random source + /// </summary> + /// <param name="rng">The random source</param> + public void SetRandomIv(IRandomSource rng) + { + ArgumentNullException.ThrowIfNull(rng); + rng.GetRandomBytes(IvBuffer); + } + + /// <summary> + /// Performs the cipher operation on the input data using the specified + /// local and remote keys. + /// </summary> + /// <param name="localKey">The secret key of the local user</param> + /// <param name="remoteKey">The public key of the remote user</param> + /// <param name="inputData">A pointer to the first byte in the buffer sequence</param> + /// <param name="inputSize">The size of the input buffer in bytes</param> + /// <exception cref="ArgumentNullException"></exception> + /// <remarks> + /// If the <see cref="NoscryptCipherFlags.Reusable"/> flag is + /// set, this function may be considered independent and called repeatedly. + /// </remarks> + public unsafe void Update( + ref readonly NCSecretKey localKey, + ref readonly NCPublicKey remoteKey, + ref readonly byte inputData, + uint inputSize + ) + { + if (Unsafe.IsNullRef(in localKey)) + { + throw new ArgumentNullException(nameof(localKey)); + } + + if (Unsafe.IsNullRef(in remoteKey)) + { + throw new ArgumentNullException(nameof(remoteKey)); + } + + if (Unsafe.IsNullRef(in inputData)) + { + throw new ArgumentNullException(nameof(inputData)); + } + + /* + * Initializing the cipher requires the context holding a pointer + * to the input data, so it has to be pinned in a fixed statment + * for the duration of the update operation. + * + * So init and update must be done as an atomic operation. + * + * If ciphers have the Reusable flag set this function may be called + * repeatedly. The results of this operation can be considered + * independent. + */ + + fixed (byte* inputPtr = &inputData) + { + NCUtilCipher.InitCipher(ctx, _cipher, inputPtr, inputSize); + + NCUtilCipher.Update(ctx, _cipher, in localKey, in remoteKey); + } + } + + /// <summary> + /// Performs the cipher operation on the input data using the specified + /// local and remote keys. + /// </summary> + /// <param name="localKey">The secret key of the local user</param> + /// <param name="remoteKey">The public key of the remote user</param> + /// <param name="input">The buffer sequence to read the input data from</param> + /// <exception cref="ArgumentNullException"></exception> + public void Update( + ref readonly NCSecretKey localKey, + ref readonly NCPublicKey remoteKey, + ReadOnlySpan<byte> input + ) + { + Update( + in localKey, + in remoteKey, + inputData: ref MemoryMarshal.GetReference(input), //If empty, null ref will throw + inputSize: (uint)input.Length + ); + } + + /// <summary> + /// Gets the size of the output buffer required to read the cipher output + /// </summary> + /// <returns>The size of the output in bytes</returns> + public int GetOutputSize() => checked((int)NCUtilCipher.GetOutputSize(ctx, _cipher)); + + /// <summary> + /// Reads the output data from the cipher into the specified buffer + /// </summary> + /// <param name="outputData">A reference to the first byte in the buffer sequence</param> + /// <param name="size">The size of the buffer sequence</param> + /// <returns>The number of bytes written to the buffer</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public int ReadOutput(ref byte outputData, int size) + { + ArgumentOutOfRangeException.ThrowIfLessThan(size, GetOutputSize()); + + return checked((int)NCUtilCipher.ReadOutput(ctx, _cipher, ref outputData, (uint)size)); + } + + /// <summary> + /// Reads the output data from the cipher into the specified buffer + /// </summary> + /// <param name="buffer">The buffer sequence to write output data to</param> + /// <returns>The number of bytes written to the buffer</returns> + /// <exception cref="ArgumentOutOfRangeException"></exception> + public int ReadOutput(Span<byte> buffer) + { + return ReadOutput( + ref MemoryMarshal.GetReference(buffer), + buffer.Length + ); + } + + private IMemoryHandle<byte> AllocIvBuffer() + { + //Use the context heap to allocate the internal iv buffer + MemoryHandle<byte> buffer = MemoryUtil.SafeAlloc<byte>(ctx.Heap, GetIvSize(), zero: true); + + try + { + /* + * Assign the buffer reference to the cipher context + * + * NOTE: This pointer will be held as long as the cipher + * context is allocated. So the buffer must be held until + * the cipher is freed. Because of this an umnanaged heap + * buffer is required so we don't need to pin managed memory + * nor worry about the GC moving the buffer. + */ + NCUtilCipher.SetProperty( + ctx, + _cipher, + NC_ENC_SET_IV, + ref buffer.GetReference(), + (uint)buffer.Length + ); + } + catch + { + buffer.Dispose(); + throw; + } + + return buffer; + } + + ///<inheritdoc/> + protected override void Free() + { + NCUtilCipher.Free(ctx, _cipher); + _ivBuffer?.Dispose(); + } + } + +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherFlags.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherFlags.cs new file mode 100644 index 0000000..275c90c --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherFlags.cs @@ -0,0 +1,60 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +using System; + +namespace VNLib.Utils.Cryptography.Noscrypt.Encryption +{ + /// <summary> + /// Cipher specific flags that control the behavior of a cipher + /// instance + /// </summary> + [Flags] + public enum NoscryptCipherFlags : uint + { + /// <summary> + /// Puts the cipher into encryption mode + /// </summary> + ModeEncryption = 0x00u, + + /// <summary> + /// Puts the cipher into decryption mode + /// </summary> + ModeDecryption = 0x01u, + + /// <summary> + /// Forces all internal memory to be freed when + /// the cipher is freed + /// </summary> + ZeroOnFree = 0x02u, + + /// <summary> + /// Disables mac verification during decryption operations, + /// by default nip44 macs are verified before the decryption + /// operation. + /// </summary> + MacNoVerify = 0x04u, + + /// <summary> + /// Allows allocated cipher instances to be reused multiple + /// times. Otherwise the cipher may only be used once after + /// allocation. + /// </summary> + Reusable = 0x08u, + + EncryptDefault = ModeEncryption | Reusable | ZeroOnFree, + DecryptDefault = ModeDecryption | Reusable | ZeroOnFree + } +} diff --git a/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherVersion.cs b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherVersion.cs new file mode 100644 index 0000000..763a5f4 --- /dev/null +++ b/wrappers/dotnet/VNLib.Utils.Cryptography.Noscrypt/src/Encryption/NoscryptCipherVersion.cs @@ -0,0 +1,36 @@ +// Copyright (C) 2024 Vaughn Nugent +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. + +using static VNLib.Utils.Cryptography.Noscrypt.NoscryptLibrary; + +namespace VNLib.Utils.Cryptography.Noscrypt.Encryption +{ + /// <summary> + /// The Noscrypt utility cipher encryption + /// standard version + /// </summary> + public enum NoscryptCipherVersion : uint + { + /// <summary> + /// Tells the cipher to use the NIP04 encryption standard + /// </summary> + Nip04 = NC_ENC_VERSION_NIP04, + + /// <summary> + /// Tells the cipher to use the NIP44 encryption standard + /// </summary> + Nip44 = NC_ENC_VERSION_NIP44 + } +} |