// 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 . using System; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using VNLib.Utils.Memory; using static NVault.Crypto.Noscrypt.LibNoscrypt; using NCResult = System.Int64; namespace NVault.Crypto.Noscrypt { public static class NCUtil { /// /// Gets a span of bytes from the current secret key /// structure /// /// /// The secret key data span public unsafe static Span AsSpan(this ref NCSecretKey key) { //Safe to cast secret key to bytes, then we can make a span to its memory ref byte asBytes = ref Unsafe.As(ref key); return MemoryMarshal.CreateSpan(ref asBytes, sizeof(NCSecretKey)); } /// /// Gets a span of bytes from the current public key /// structure /// /// /// The public key data as a data span public unsafe static Span AsSpan(this ref NCPublicKey key) { //Safe to cast secret key to bytes, then we can make a span to its memory ref byte asBytes = ref Unsafe.As(ref key); return MemoryMarshal.CreateSpan(ref asBytes, sizeof(NCPublicKey)); } /// /// Casts a span of bytes to a secret key reference. Note that /// the new structure reference will point to the same memory /// as the span. /// /// The secret key data /// A mutable secret key reference /// public unsafe static ref NCSecretKey AsSecretKey(Span span) { ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCSecretKey), nameof(span)); ref byte asBytes = ref MemoryMarshal.GetReference(span); return ref Unsafe.As(ref asBytes); } /// /// Casts a span of bytes to a public key reference. Note that /// the new structure reference will point to the same memory /// as the span. /// /// The public key data span /// A mutable reference to the public key structure /// public unsafe static ref NCPublicKey AsPublicKey(Span span) { ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCPublicKey), nameof(span)); ref byte asBytes = ref MemoryMarshal.GetReference(span); return ref Unsafe.As(ref asBytes); } /// /// Casts a read-only span of bytes to a secret key reference. Note that /// the new structure reference will point to the same memory as the span. /// /// The secret key data span /// A readonly refernce to the secret key structure /// public unsafe static ref readonly NCSecretKey AsSecretKey(ReadOnlySpan span) { ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCSecretKey), nameof(span)); ref byte asBytes = ref MemoryMarshal.GetReference(span); return ref Unsafe.As(ref asBytes); } /// /// Casts a read-only span of bytes to a public key reference. Note that /// the new structure reference will point to the same memory as the span. /// /// The public key data span /// A readonly reference to the public key structure /// public unsafe static ref readonly NCPublicKey AsPublicKey(ReadOnlySpan span) { ArgumentOutOfRangeException.ThrowIfLessThan(span.Length, sizeof(NCPublicKey), nameof(span)); ref byte asBytes = ref MemoryMarshal.GetReference(span); return ref Unsafe.As(ref asBytes); } /// /// Initializes a new NostrCrypto context wraper directly that owns the internal context. /// This may be done once at app startup and is thread-safe for the rest of the /// application lifetime. /// /// /// The heap to allocate the context from /// The random entropy data to initialize the context with /// The library wrapper handle public static NostrCrypto InitializeCrypto(this LibNoscrypt library, IUnmangedHeap heap, ReadOnlySpan entropy32) { ArgumentNullException.ThrowIfNull(library); ArgumentNullException.ThrowIfNull(heap); //Initialize the context NCContext context = library.Initialize(heap, entropy32); //Create the crypto interface return new NostrCrypto(context, true); } internal static void CheckResult(NCResult result, bool raiseOnFailure) where T : Delegate { //Only negative values are errors if (result >= NC_SUCCESS) { return; } NCResult asPositive = -result; // Error code are only 8 bits, if an argument error occured, the // argument number will be in the next upper 8 bits byte errorCode = (byte)(asPositive & 0xFF); byte argNumber = (byte)((asPositive >> 8) & 0xFF); switch (errorCode) { case E_NULL_PTR: RaiseNullArgExceptionForArgumentNumber(argNumber); break; case E_INVALID_ARG: RaiseArgExceptionForArgumentNumber(argNumber); break; case E_ARGUMENT_OUT_OF_RANGE: RaiseOORExceptionForArgumentNumber(argNumber); break; case E_INVALID_CTX: throw new InvalidOperationException("The library context object is null or invalid"); case E_OPERATION_FAILED: RaiseOperationFailedException(raiseOnFailure); break; } } private static void RaiseOperationFailedException(bool raise) { if (raise) { throw new InvalidOperationException("The operation failed for an unknown reason"); } } private static void RaiseNullArgExceptionForArgumentNumber(int argNumber) where T : Delegate { //Get delegate parameters Type type = typeof(T); ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; throw new ArgumentNullException(arg.Name, "Argument is null or invalid cannot continue"); } private static void RaiseArgExceptionForArgumentNumber(int argNumber) where T : Delegate { //Get delegate parameters Type type = typeof(T); ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; throw new ArgumentException("Argument is null or invalid cannot continue", arg.Name); } private static void RaiseOORExceptionForArgumentNumber(int argNumber) where T : Delegate { //Get delegate parameters Type type = typeof(T); ParameterInfo arg = type.GetMethod("Invoke")!.GetParameters()[argNumber]; throw new ArgumentOutOfRangeException(arg.Name, "Argument is out of range of acceptable values"); } } }