// 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.Runtime.CompilerServices; using System.Xml; using VNLib.Utils; using NCResult = System.Int64; namespace NVault.Crypto.Noscrypt { /// /// A default implementation of the interface /// /// The initialized library context public unsafe class NostrCrypto(NCContext context, bool ownsContext) : VnDisposeable, INostrCrypto { /// /// Gets the underlying library context. /// public NCContext Context => context; private ref readonly FunctionTable Functions => ref context.Library.Functions; /// public void Decrypt( ref readonly NCSecretKey secretKey, ref readonly NCPublicKey publicKey, ref readonly byte nonce, ref readonly byte cipherText, ref byte plainText, uint size ) { Check(); IntPtr libCtx = context.DangerousGetHandle(); NCCryptoData data = default; data.dataSize = size; //Copy nonce to struct memory buffer Unsafe.CopyBlock( ref Unsafe.AsRef(data.nonce), in nonce, LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE ); fixed (NCSecretKey* pSecKey = &secretKey) fixed (NCPublicKey* pPubKey = &publicKey) fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText) { //Set input data to the cipher text to decrypt and the output data to the plaintext buffer data.inputData = pCipherText; data.outputData = pTextPtr; NCResult result = Functions.NCDecrypt.Invoke(libCtx, pSecKey, pPubKey, &data); NCUtil.CheckResult(result, true); } } /// public void Encrypt( ref readonly NCSecretKey secretKey, ref readonly NCPublicKey publicKey, ref readonly byte nonce, ref readonly byte plainText, ref byte cipherText, uint size, ref byte hmackKeyOut32 ) { Check(); IntPtr libCtx = context.DangerousGetHandle(); NCCryptoData data = default; data.dataSize = size; //Copy nonce to struct memory buffer Unsafe.CopyBlock( ref Unsafe.AsRef(data.nonce), in nonce, LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE ); fixed (NCSecretKey* pSecKey = &secretKey) fixed (NCPublicKey* pPubKey = &publicKey) fixed (byte* pCipherText = &cipherText, pTextPtr = &plainText, pHmacKeyOut = &hmackKeyOut32) { //Set input data to the plaintext to encrypt and the output data to the cipher text buffer data.inputData = pTextPtr; data.outputData = pCipherText; NCResult result = Functions.NCEncrypt.Invoke(libCtx, pSecKey, pPubKey, pHmacKeyOut, &data); NCUtil.CheckResult(result, true); } } /// public void GetPublicKey(ref readonly NCSecretKey secretKey, ref NCPublicKey publicKey) { Check(); IntPtr libCtx = context.DangerousGetHandle(); fixed(NCSecretKey* pSecKey = &secretKey) fixed(NCPublicKey* pPubKey = &publicKey) { NCResult result = Functions.NCGetPublicKey.Invoke(libCtx, pSecKey, pPubKey); NCUtil.CheckResult(result, true); } } /// public void SignData( ref readonly NCSecretKey secretKey, ref readonly byte random32, ref readonly byte data, nint dataSize, ref byte sig64 ) { Check(); IntPtr libCtx = context.DangerousGetHandle(); fixed (NCSecretKey* pSecKey = &secretKey) fixed(byte* pData = &data, pSig = &sig64, pRandom = &random32) { NCResult result = Functions.NCSignData.Invoke(libCtx, pSecKey, pRandom, pData, dataSize, pSig); NCUtil.CheckResult(result, true); } } /// public bool ValidateSecretKey(ref readonly NCSecretKey secretKey) { Check(); IntPtr libCtx = context.DangerousGetHandle(); fixed (NCSecretKey* pSecKey = &secretKey) { /* * Validate should return a result of 1 if the secret key is valid * or a 0 if it is not. */ NCResult result = Functions.NCValidateSecretKey.Invoke(libCtx, pSecKey); NCUtil.CheckResult(result, false); //Result should be 1 if the secret key is valid return result == 1; } } /// public bool VerifyData( ref readonly NCPublicKey pubKey, ref readonly byte data, nint dataSize, ref byte sig64 ) { Check(); IntPtr libCtx = context.DangerousGetHandle(); fixed(NCPublicKey* pPubKey = &pubKey) fixed (byte* pData = &data, pSig = &sig64) { NCResult result = Functions.NCVerifyData.Invoke(libCtx, pPubKey, pData, dataSize, pSig); NCUtil.CheckResult(result, false); return result == LibNoscrypt.NC_SUCCESS; } } /// public bool VerifyMac( ref readonly NCSecretKey secretKey, ref readonly NCPublicKey publicKey, ref readonly byte nonce32, ref readonly byte mac32, ref readonly byte payload, nint payloadSize ) { Check(); //Check pointers we need to use if(Unsafe.IsNullRef(in nonce32)) { throw new ArgumentNullException(nameof(nonce32)); } if(Unsafe.IsNullRef(in mac32)) { throw new ArgumentNullException(nameof(mac32)); } if(Unsafe.IsNullRef(in payload)) { throw new ArgumentNullException(nameof(payload)); } IntPtr libCtx = context.DangerousGetHandle(); NCMacVerifyArgs args = new() { payloadSize = payloadSize, }; //Copy nonce to struct memory buffer Unsafe.CopyBlock( ref Unsafe.AsRef(args.nonce), in nonce32, LibNoscrypt.NC_ENCRYPTION_NONCE_SIZE ); //Copy mac to struct memory buffer Unsafe.CopyBlock( ref Unsafe.AsRef(args.mac), in mac32, LibNoscrypt.NC_ENCRYPTION_MAC_SIZE ); fixed(NCSecretKey* pSecKey = &secretKey) fixed(NCPublicKey* pPubKey = &publicKey) fixed (byte* pPayload = &payload) { args.payload = pPayload; //Exec and bypass failure NCResult result = Functions.NCVerifyMac.Invoke(libCtx, pSecKey, pPubKey, &args); NCUtil.CheckResult(result, false); //Result should be success if the hmac is valid return result == LibNoscrypt.NC_SUCCESS; } } /// protected override void Free() { if(ownsContext) { context.Dispose(); } } } }