/* wolfCrypt-Test.cs
 *
 * Copyright (C) 2006-2026 wolfSSL Inc.
 *
 * This file is part of wolfSSL.
 *
 * wolfSSL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * wolfSSL 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
 */

/* Tests for the wolfCrypt C# wrapper */

using System;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using wolfSSL.CSharp;
using System.Runtime.InteropServices;
using static wolfSSL.CSharp.wolfcrypt;

public class wolfCrypt_Test_CSharp
{
    private static void random_test()
    {
        int ret, i, zeroCount = 0;
        Byte[] data = new Byte[128];

        Console.WriteLine("Starting RNG test");

        /* Random Test */
        ret = wolfcrypt.Random(data, data.Length);
        if (ret == 0)
        {
            /* Check for 0's */
            for (i = 0; i < (int)data.Length; i++)
            {
                if (data[i] == 0)
                {
                    zeroCount++;
                }
            }
            if (zeroCount == data.Length)
            {
                Console.WriteLine("RNG zero check error");
            }
            else
            {
                Console.WriteLine("RNG Test Passed");
            }
        }
        else
        {
            Console.WriteLine("RNG Error" + wolfcrypt.GetError(ret));
        }
    } /* END random_test */

    private static void ecc_test(string hashAlgorithm, int keySize)
    {
        int ret;
        IntPtr rng = IntPtr.Zero;
        IntPtr PrivKey = IntPtr.Zero;
        IntPtr PubKey = IntPtr.Zero;
        IntPtr key = IntPtr.Zero;

        Console.WriteLine("\nStarting ECC" + (keySize*8) + " test for " + hashAlgorithm + "...");

        /* Create a new RNG context */
        rng = wolfcrypt.RandomNew();
        if (rng == IntPtr.Zero)
        {
            throw new Exception("RNG initialization failed.");
        }

        /* Generate ECC Key Pair */
        Console.WriteLine("Testing ECC Key Generation...");
        key = wolfcrypt.EccMakeKey(keySize, rng);
        if (key == IntPtr.Zero)
        {
            throw new Exception("EccMakeKey failed");
        }
        Console.WriteLine("ECC Key Generation test passed.");

        /* Export and Import Key */
        Console.WriteLine("Testing ECC Key Export and Import...");
        byte[] privateKeyDer;
        ret = wolfcrypt.EccExportPrivateKeyToDer(key, out privateKeyDer);
        if (ret < 0) {
            throw new Exception("ExportPrivateKeyToDer failed");
        }
        byte[] publicKeyDer;
        ret = wolfcrypt.EccExportPublicKeyToDer(key, out publicKeyDer, true);
        if (ret < 0) {
            throw new Exception("ExportPublicKeyToDer failed");
        }
        PrivKey = wolfcrypt.EccImportKey(privateKeyDer);
        if (PrivKey == IntPtr.Zero)
        {
            throw new Exception("EccImportKey Private failed");
        }

        PubKey = wolfcrypt.EccImportPublicKeyFromDer(publicKeyDer);
        if (PubKey == IntPtr.Zero)
        {
            throw new Exception("ImportPublicKeyFromDer Public failed");
        }
        Console.WriteLine("ECC Key Export and Import test passed.");

        /* Generate hash based on selected algorithm */
        byte[] dataToHash = System.Text.Encoding.UTF8.GetBytes("This is some data to hash");
        byte[] hash;

        switch (hashAlgorithm.ToUpper())
        {
            case "SHA256":
                using (SHA256 sha256 = SHA256.Create())
                {
                    hash = sha256.ComputeHash(dataToHash);
                }
                break;

            case "SHA384":
                using (SHA384 sha384 = SHA384.Create())
                {
                    hash = sha384.ComputeHash(dataToHash);
                }
                break;

            case "SHA512":
                using (SHA512 sha512 = SHA512.Create())
                {
                    hash = sha512.ComputeHash(dataToHash);
                }
                break;

            default:
                throw new Exception("Unsupported hash algorithm");
        }
        Console.WriteLine($"{hashAlgorithm} hash generated.");

        /* Sign Data */
        Console.WriteLine("Testing ECC Signature Creation...");
        byte[] signature = new byte[wolfcrypt.ECC_MAX_SIG_SIZE];
        int signLength = wolfcrypt.EccSign(PrivKey, hash, signature);
        if (signLength <= 0)
        {
            throw new Exception("EccSign failed");
        }

        byte[] actualSignature = new byte[signLength];
        Array.Copy(signature, 0, actualSignature, 0, signLength);

        Console.WriteLine($"ECC Signature Creation test passed. Signature Length: {signLength}");

        /* Verify Signature */
        Console.WriteLine("Testing ECC Signature Verification...");
        int verifyResult = wolfcrypt.EccVerify(PubKey, actualSignature, hash);
        if (verifyResult != 0)
        {
            throw new Exception("EccVerify failed");
        }
        Console.WriteLine("ECC Signature Verification test passed.");

        /* Cleanup */
        if (key != IntPtr.Zero) wolfcrypt.EccFreeKey(key);
        if (PubKey != IntPtr.Zero) wolfcrypt.EccFreeKey(PubKey);
        if (PrivKey != IntPtr.Zero) wolfcrypt.EccFreeKey(PrivKey);
        if (rng != IntPtr.Zero) wolfcrypt.RandomFree(rng);
    } /* END ecc_test */

    private static void ecies_test(int keySize)
    {
        /* maximum encrypted message:
         * msgSz (14) + pad (2) + pubKeySz(1+66*2) + ivSz(16) + digestSz(32) = 197 */
        int bufferSize = wolfcrypt.MAX_ECIES_TEST_SZ;
        const string message = "Hello wolfSSL!";
        byte[] salt = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

        IntPtr a = IntPtr.Zero;
        IntPtr b = IntPtr.Zero;
        IntPtr aCtx = IntPtr.Zero;
        IntPtr bCtx = IntPtr.Zero;
        IntPtr rng = IntPtr.Zero;
        IntPtr heap = IntPtr.Zero;

        byte[] plaintext = new byte[bufferSize];
        byte[] encrypted = new byte[bufferSize];
        byte[] decrypted = new byte[bufferSize];

        try
        {
            Console.WriteLine($"\nStarting ECIES test for {keySize} byte key size...");

            /* Create a new RNG context */
            rng = wolfcrypt.RandomNew();
            if (rng == IntPtr.Zero)
            {
                throw new Exception("RNG initialization failed.");
            }

            /* Initialize keys */
            a = wolfcrypt.EccMakeKey(keySize, rng);
            b = wolfcrypt.EccMakeKey(keySize, rng);
            if (a == IntPtr.Zero || b == IntPtr.Zero)
            {
                throw new Exception("Key generation failed.");
            }
            Console.WriteLine("ECC key generation passed.");

            /* Create ECIES contexts for encryption and decryption */
            aCtx = wolfcrypt.EciesNewCtx((int)wolfcrypt.ecFlags.REQ_RESP_CLIENT, rng, heap);
            bCtx = wolfcrypt.EciesNewCtx((int)wolfcrypt.ecFlags.REQ_RESP_SERVER, rng, heap);
            if (aCtx == IntPtr.Zero || bCtx == IntPtr.Zero)
            {
                throw new Exception("Context creation failed.");
            }
            Console.WriteLine("ECC context creation passed.");

            /* Set KDF salt */
            if (wolfcrypt.EciesSetKdfSalt(aCtx, salt) != 0 ||
                wolfcrypt.EciesSetKdfSalt(bCtx, salt) != 0)
            {
                throw new Exception("Failed to set KDF salt.");
            }
            Console.WriteLine("KDF salt setup passed.");

            /* Prepare plaintext */
            Array.Clear(plaintext, 0, plaintext.Length);
            Array.Copy(Encoding.ASCII.GetBytes(message), plaintext, message.Length);
            /* Pad to block size */
            int plaintextLen = ((message.Length + (wolfcrypt.AES_BLOCK_SIZE - 1)) /
                                wolfcrypt.AES_BLOCK_SIZE) * wolfcrypt.AES_BLOCK_SIZE;

            /* Encrypt message */
            int ret = wolfcrypt.EciesEncrypt(a, b, plaintext, (uint)plaintextLen, encrypted, aCtx);
            if (ret < 0)
            {
                throw new Exception("Encryption failed.");
            }

            int encryptedLen = ret;
            Console.WriteLine("ECC encryption passed.");

            /* Decrypt message */
            ret = wolfcrypt.EciesDecrypt(b, a, encrypted, (uint)encryptedLen, decrypted, bCtx);
            if (ret < 0)
            {
                throw new Exception("Decryption failed.");
            }

            int decryptedLen = ret;
            Console.WriteLine("ECC decryption passed.");

            /* Compare decrypted text to original plaintext */
            if (decryptedLen != plaintextLen || !wolfcrypt.ByteArrayVerify(plaintext, decrypted))
            {
                throw new Exception("Decrypted text does not match original plaintext.");
            }
            Console.WriteLine("Decrypted text matches original plaintext.");
        }
        finally
        {
            /* Cleanup key and context */
            if (a != IntPtr.Zero) wolfcrypt.EccFreeKey(a);
            if (b != IntPtr.Zero) wolfcrypt.EccFreeKey(b);
            if (aCtx != IntPtr.Zero) wolfcrypt.EciesFreeCtx(aCtx);
            if (bCtx != IntPtr.Zero) wolfcrypt.EciesFreeCtx(bCtx);
            if (rng != IntPtr.Zero) wolfcrypt.RandomFree(rng);
        }
    } /* END ecies_test */

    private static void ecdhe_test(int keySize)
    {
        int ret;
        IntPtr rng = IntPtr.Zero;
        IntPtr keyA = IntPtr.Zero;
        IntPtr keyB = IntPtr.Zero;
        IntPtr publicKeyA = IntPtr.Zero;
        IntPtr publicKeyB = IntPtr.Zero;
        byte[] derKey;

        Console.WriteLine("\nStarting ECDHE shared secret test for " + keySize + " key size...");

        /* Create RNG */
        Console.WriteLine("Generating RNG...");
        rng = RandomNew();
        if (rng == IntPtr.Zero)
        {
            throw new Exception("Failed to generate RNG.");
        }
        Console.WriteLine("RNG generation test passed.");

        /* Generate Key Pair A */
        Console.WriteLine("Generating Key Pair A...");
        keyA = wolfcrypt.EccMakeKey(keySize, rng);
        if (keyA == IntPtr.Zero)
        {
            throw new Exception("Failed to generate key pair A.");
        }

        /* Generate Key Pair B */
        Console.WriteLine("Generating Key Pair B...");
        keyB = wolfcrypt.EccMakeKey(keySize, rng);
        if (keyB == IntPtr.Zero)
        {
            throw new Exception("Failed to generate key pair B.");
        }
        Console.WriteLine("ECC Key generation test passed.");

        /* Export Public Key B to DER format */
        Console.WriteLine("Exporting Public Key B to DER format...");
        ret = wolfcrypt.EccExportPublicKeyToDer(keyB, out derKey, true);
        if (ret < 0 || derKey == null)
        {
            throw new Exception("EccExportPublicKeyToDer failed");
        }

        /* Decode Public Key B from DER format */
        Console.WriteLine("Decoding Public Key B from DER format...");
        publicKeyB = wolfcrypt.EccImportPublicKeyFromDer(derKey);
        if (publicKeyB == IntPtr.Zero)
        {
            throw new Exception("Failed to decode public key B from DER format.");
        }
        Console.WriteLine("ECC Export and Import test passed.");

        /* Compute Shared Secret using Private Key A and Public Key B */
        Console.WriteLine("Computing Shared Secret using Private Key A and Public Key B...");
        byte[] sharedSecretA = new byte[keySize];
        int retA = wolfcrypt.EcdheSharedSecret(keyA, publicKeyB, sharedSecretA, rng);
        if (retA != 0)
        {
            throw new Exception("Failed to compute shared secret A. Error code: " + retA);
        }
        Console.WriteLine("ECC shared secret created using private Key A.");

        /* Export Public Key A to DER format */
        Console.WriteLine("Exporting Public Key A to DER format...");
        ret = wolfcrypt.EccExportPublicKeyToDer(keyA, out derKey, true);
        if (ret < 0 || derKey == null)
        {
            throw new Exception("EccExportPublicKeyToDer failed");
        }

        /* Decode Public Key A from DER format */
        Console.WriteLine("Decoding Public Key A from DER format...");
        publicKeyA = wolfcrypt.EccImportPublicKeyFromDer(derKey);
        if (publicKeyA == IntPtr.Zero)
        {
            throw new Exception("Failed to decode public key A from DER format.");
        }

        /* Compute Shared Secret using Private Key B and Public Key A */
        Console.WriteLine("Computing Shared Secret using Private Key B and Public Key A...");
        byte[] sharedSecretB = new byte[keySize];
        int retB = wolfcrypt.EcdheSharedSecret(keyB, publicKeyA, sharedSecretB, rng);
        if (retB != 0)
        {
            throw new Exception("Failed to compute shared secret B. Error code: " + retB);
        }
        Console.WriteLine("ECC shared secret created using private Key B.");

        /* Compare Shared Secrets */
        Console.WriteLine("Comparing Shared Secrets...");
        if (!wolfcrypt.ByteArrayVerify(sharedSecretA, sharedSecretB))
        {
            throw new Exception("Shared secrets do not match.");
        }
        else
        {
            Console.WriteLine("ECC shared secret match.");
        }

        /* Cleanup */
        if (keyA != IntPtr.Zero) wolfcrypt.EccFreeKey(keyA);
        if (keyB != IntPtr.Zero) wolfcrypt.EccFreeKey(keyB);
        if (publicKeyA != IntPtr.Zero) wolfcrypt.EccFreeKey(publicKeyA);
        if (publicKeyB != IntPtr.Zero) wolfcrypt.EccFreeKey(publicKeyB);
        if (rng != IntPtr.Zero) wolfcrypt.RandomFree(rng);
    } /* END ecdhe_test */

    private static void rsa_test(string hashAlgorithm, int keySize)
    {
        IntPtr key = IntPtr.Zero;
        IntPtr heap = IntPtr.Zero;
        int devId = wolfcrypt.INVALID_DEVID;

        Console.WriteLine("\nStarting RSA" + keySize + " test for " + hashAlgorithm + "...");

        /* Generate RSA Key Pair */
        Console.WriteLine("Testing RSA Key Generation...");
        key = wolfcrypt.RsaMakeKey(heap, devId, keySize);
        if (key == IntPtr.Zero)
        {
            throw new Exception("RsaMakeKey failed");
        }
        Console.WriteLine("RSA Key Generation test passed.");

        /* Generate hash based on selected algorithm */
        byte[] dataToHash = System.Text.Encoding.UTF8.GetBytes("This is some data to hash");
        byte[] hash;

        switch (hashAlgorithm.ToUpper())
        {
            case "SHA256":
                using (SHA256 sha256 = SHA256.Create())
                {
                    hash = sha256.ComputeHash(dataToHash);
                }
                break;

            case "SHA384":
                using (SHA384 sha384 = SHA384.Create())
                {
                    hash = sha384.ComputeHash(dataToHash);
                }
                break;

            case "SHA512":
                using (SHA512 sha512 = SHA512.Create())
                {
                    hash = sha512.ComputeHash(dataToHash);
                }
                break;

            default:
                throw new Exception("Unsupported hash algorithm");
        }
        Console.WriteLine($"{hashAlgorithm} hash generated.");

        /* Sign Data */
        Console.WriteLine("Testing RSA Signature Creation...");
        byte[] signature = new byte[keySize / 8];
        int signLength = wolfcrypt.RsaSignSSL(key, hash, signature);
        if (signLength <= 0)
        {
            throw new Exception("RsaSignSSL failed");
        }

        byte[] actualSignature = new byte[signLength];
        Array.Copy(signature, 0, actualSignature, 0, signLength);

        Console.WriteLine($"RSA Signature Creation test passed. Signature Length: {signLength}");

        /* Verify Signature */
        Console.WriteLine("Testing RSA Signature Verification...");
        int verifyResult = wolfcrypt.RsaVerifySSL(key, actualSignature, hash);
        if (verifyResult != 0)
        {
            throw new Exception("RsaVerifySSL failed");
        }
        Console.WriteLine("RSA Signature Verification test passed.");

        /* Cleanup */
        if (key != IntPtr.Zero) wolfcrypt.RsaFreeKey(key);
    } /* END rsa_test */

    private static void ed25519_test()
    {
        int ret;
        IntPtr key = IntPtr.Zero;
        byte[] privKey;
        byte[] pubKey;
        uint pubKeySz;

        Console.WriteLine("\nStarting ED25519 tests...");

        IntPtr heap = IntPtr.Zero;
        int devId = wolfcrypt.INVALID_DEVID;

        /* Generate ED25519 Key Pair */
        Console.WriteLine("Testing ED25519 Key Generation...");
        key = wolfcrypt.Ed25519MakeKey(heap, devId);
        if (key == IntPtr.Zero)
        {
            throw new Exception("Ed25519MakeKey failed");
        }
        Console.WriteLine("ED25519 Key Generation test passed.");

        /* Export and Import Key */
        Console.WriteLine("Testing ED25519 Key Export and Import...");
        /* Export Private */
        ret = wolfcrypt.Ed25519ExportKeyToDer(key, out privKey);
        if (ret < 0 || privKey == null)
        {
            throw new Exception("Ed25519ExportKeyToDer failed");
        }
        /* Export Public */
        ret = wolfcrypt.Ed25519ExportPublicKeyToDer(key, out pubKey, true);
        if (ret < 0 || pubKey == null)
        {
            throw new Exception("Ed25519ExportKeyToDer failed");
        }
        /* Import Private */
        IntPtr importedPrivKey = wolfcrypt.Ed25519PrivateKeyDecode(privKey);
        if (importedPrivKey == IntPtr.Zero)
        {
            throw new Exception("Ed25519PrivateKeyDecode failed");
        }
        /* Import Public */
        IntPtr importedPubKey = wolfcrypt.Ed25519PublicKeyDecode(pubKey);
        if (importedPubKey == IntPtr.Zero)
        {
            throw new Exception("Ed25519PublicKeyDecode failed");
        }
        Console.WriteLine("ED25519 Key Export and Import test passed.");

        /* Generate a hash */
        byte[] dataToHash = System.Text.Encoding.UTF8.GetBytes("This is some data to hash");

        /* Sign Data */
        Console.WriteLine("Testing ED25519 Signature Creation...");
        byte[] signature;

        ret = wolfcrypt.Ed25519SignMsg(dataToHash, out signature, key);
        if (ret != 0)
        {
            throw new Exception("Ed25519SignMsg failed");
        }
        Console.WriteLine($"ED25519 Signature Creation test passed. Signature Length: {signature.Length}");

        /* Verify Signature */
        Console.WriteLine("Testing ED25519 Signature Verification...");
        ret = wolfcrypt.Ed25519VerifyMsg(signature, dataToHash, key);
        if (ret != 0)
        {
            throw new Exception("Ed25519VerifyMsg failed");
        }
        Console.WriteLine("ED25519 Signature Verification test passed.");

        /* test importing a raw public key */
        ret = wolfcrypt.Ed25519ExportPublic(key, pubKey, out pubKeySz);
        if (ret < 0 || pubKey == null) {
            throw new Exception("Ed25519ExportPublic failed");
        }
        Console.WriteLine("ED25519 Export Raw Public test passed.");

        if (importedPubKey != IntPtr.Zero) {
            wolfcrypt.Ed25519FreeKey(importedPubKey);
        }
        ret = wolfcrypt.Ed25519ImportPublic(pubKey, pubKeySz, out importedPubKey);
        if (importedPubKey == IntPtr.Zero) {
            throw new Exception("Ed25519ImportPublic failed");
        }
        Console.WriteLine("ED25519 Import Raw Public test passed.");

        /* Cleanup */
        if (key != IntPtr.Zero) {
            wolfcrypt.Ed25519FreeKey(key);
        }
        if (importedPubKey != IntPtr.Zero) {
            wolfcrypt.Ed25519FreeKey(importedPubKey);
        }
        if (importedPrivKey != IntPtr.Zero) {
            wolfcrypt.Ed25519FreeKey(importedPrivKey);
        }

    } /* END ed25519_test */

    private static void curve25519_test()
    {
        int ret;
        IntPtr keyA = IntPtr.Zero;
        IntPtr keyB = IntPtr.Zero;
        IntPtr publicKeyA = IntPtr.Zero;
        IntPtr publicKeyB = IntPtr.Zero;
        byte[] rawPub, rawPrivate, derKey;

        Console.WriteLine("\nStarting Curve25519 shared secret test...");

        /* Generate Key Pair A */
        Console.WriteLine("Generating Key Pair A...");
        keyA = wolfcrypt.Curve25519MakeKey(IntPtr.Zero, 0);
        if (keyA == IntPtr.Zero)
        {
            throw new Exception("Failed to generate key pair A.");
        }

        /* Generate Key Pair B */
        Console.WriteLine("Generating Key Pair B...");
        keyB = wolfcrypt.Curve25519MakeKey(IntPtr.Zero, 0);
        if (keyB == IntPtr.Zero)
        {
            throw new Exception("Failed to generate key pair B.");
        }
        Console.WriteLine("Curve25519 Key generation test passed.");


        /* Export Public Key A private and public to raw format */
        wolfcrypt.Curve25519ExportKeyRaw(keyA, out rawPrivate, out rawPub);
        /* Export Public Key B public to raw format */
        rawPub = wolfcrypt.Curve25519ExportPublicKey(keyB);
        /* rawPub / rawPrivate - not used */


        /* Export Public Key B to DER format */
        Console.WriteLine("Exporting Public Key B to DER format...");
        ret = wolfcrypt.Curve25519ExportPublicKeyToDer(keyB, out derKey, true);
        if (ret < 0 || derKey == null)
        {
            throw new Exception("Curve25519ExportPublicKeyToDer failed");
        }

        /* Decode Public Key B from DER format */
        Console.WriteLine("Decoding Public Key B from DER format...");
        publicKeyB = wolfcrypt.Curve25519PublicKeyDecode(derKey);
        if (publicKeyB == IntPtr.Zero)
        {
            throw new Exception("Failed to decode public key B from DER format.");
        }
        Console.WriteLine("Curve25519 Export and Import test passed.");

        /* Compute Shared Secret using Private Key A and Public Key B */
        Console.WriteLine("Computing Shared Secret using Private Key A and Public Key B...");
        byte[] sharedSecretA = new byte[wolfcrypt.ED25519_KEY_SIZE];
        int retA = wolfcrypt.Curve25519SharedSecret(keyA, publicKeyB, sharedSecretA);
        if (retA != 0)
        {
            throw new Exception("Failed to compute shared secret A. Error code: " + retA);
        }
        Console.WriteLine("Curve25519 shared secret created using private Key A.");

        /* Export Public Key A to DER format */
        Console.WriteLine("Exporting Public Key A to DER format...");
        ret = wolfcrypt.Curve25519ExportPublicKeyToDer(keyA, out derKey, true);
        if (ret < 0 || derKey == null)
        {
            throw new Exception("Curve25519ExportPublicKeyToDer failed");
        }

        /* Decode Public Key A from DER format */
        Console.WriteLine("Decoding Public Key A from DER format...");
        publicKeyA = wolfcrypt.Curve25519PublicKeyDecode(derKey);
        if (publicKeyA == IntPtr.Zero)
        {
            throw new Exception("Failed to decode public key A from DER format.");
        }

        /* Compute Shared Secret using Private Key B and Public Key A */
        Console.WriteLine("Computing Shared Secret using Private Key B and Public Key A...");
        byte[] sharedSecretB = new byte[wolfcrypt.ED25519_KEY_SIZE];
        int retB = wolfcrypt.Curve25519SharedSecret(keyB, publicKeyA, sharedSecretB);
        if (retB != 0)
        {
            throw new Exception("Failed to compute shared secret B. Error code: " + retB);
        }
        Console.WriteLine("Curve25519 shared secret created using private Key B.");

        /* Compare Shared Secrets */
        Console.WriteLine("Comparing Shared Secrets...");
        if (!wolfcrypt.ByteArrayVerify(sharedSecretA, sharedSecretB))
        {
            throw new Exception("Shared secrets do not match.");
        }
        else
        {
            Console.WriteLine("Curve25519 shared secret match.");
        }

        /* Cleanup */
        if (keyA != IntPtr.Zero) wolfcrypt.Curve25519FreeKey(keyA);
        if (keyB != IntPtr.Zero) wolfcrypt.Curve25519FreeKey(keyB);
        if (publicKeyA != IntPtr.Zero) wolfcrypt.Curve25519FreeKey(publicKeyA);
        if (publicKeyB != IntPtr.Zero) wolfcrypt.Curve25519FreeKey(publicKeyB);
    } /* END curve25519_test */

    private static void mlkem_test(wolfcrypt.MlKemTypes type)
    {
        int ret = 0;
        IntPtr keyA = IntPtr.Zero;
        IntPtr keyB = IntPtr.Zero;
        IntPtr heap = IntPtr.Zero;
        int devId = wolfcrypt.INVALID_DEVID;
        byte[] pubA = null;
        byte[] privA = null;
        byte[] cipherText = null;
        byte[] sharedSecretA = null;
        byte[] sharedSecretB = null;

        try
        {
            Console.WriteLine("\nStarting " + type + " shared secret test ...");

            /* Generate Key Pair */
            Console.WriteLine("Testing ML-KEM Key Generation...");

            Console.WriteLine("Generate Key Pair A...");
            keyA = wolfcrypt.MlKemMakeKey(type, heap, devId);
            if (keyA == IntPtr.Zero)
            {
                ret = -1;
                Console.Error.WriteLine("Failed to generate key pair A.");
            }
            if (ret == 0)
            {
                Console.WriteLine("Initialize Key B for decode...");
                keyB = wolfcrypt.MlKemNew(type, heap, devId);
                if (keyB == IntPtr.Zero)
                {
                    ret = -1;
                    Console.Error.WriteLine("Failed to initialize key B for decode.");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-KEM Key Generation test passed.");
            }

            /* Encode */
            if (ret == 0)
            {
                Console.WriteLine("Testing ML-KEM Key Encode...");
                ret = wolfcrypt.MlKemEncodePublicKey(keyA, out pubA);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to encode public key of A. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                ret = wolfcrypt.MlKemEncodePrivateKey(keyA, out privA);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to encode private key of A. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-KEM Key Encode test passed.");
            }

            /* Encapsulate */
            if (ret == 0)
            {
                Console.WriteLine("Testing ML-KEM Encapsulation...");
                ret = wolfcrypt.MlKemEncapsulate(keyA, out cipherText, out sharedSecretA);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to encapsulate. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-KEM Encapsulation test passed.");
            }
            
            /* Decode */
            if (ret == 0)
            {
                Console.WriteLine("Testing ML-KEM Decode...");
                ret = wolfcrypt.MlKemDecodePrivateKey(keyB, privA);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to decode private key of A. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                ret = wolfcrypt.MlKemDecodePublicKey(keyB, pubA);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to decode public key of A. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-KEM Decode test passed.");
            }

            /* Decapsulate */
            if (ret == 0)
            {
                Console.WriteLine("Testing ML-KEM Decapsulation...");
                ret = wolfcrypt.MlKemDecapsulate(keyB, cipherText, out sharedSecretB);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to decapsulate. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-KEM Decapsulation test passed.");
            }

            /* Check */
            if (ret == 0)
            {
                Console.WriteLine("Comparing Shared Secrets...");
                if (!wolfcrypt.ByteArrayVerify(sharedSecretA, sharedSecretB))
                {
                    ret = -1;
                    Console.Error.WriteLine($"Shared secrets do not match. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-KEM shared secret match.");
            }

            if (ret != 0)
            {
                throw new Exception("ML-KEM test failed.");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"ML-KEM test failed: {ex.Message}");
            throw;
        }
        finally
        {
            /* Cleanup */
            if (keyA != IntPtr.Zero)
            {
                ret = wolfcrypt.MlKemFreeKey(ref keyA);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to free MlKem key A. Error code: {ret}");
                }
            }
            if (keyB != IntPtr.Zero)
            {
                ret = wolfcrypt.MlKemFreeKey(ref keyB);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to free MlKem key B. Error code: {ret}");
                }
            }
        }
    } /* END mlkem_test */

    private static void mldsa_test(wolfcrypt.MlDsaLevels level)
    {
        int ret = 0;
        IntPtr key = IntPtr.Zero;
        IntPtr importKey = IntPtr.Zero;
        IntPtr heap = IntPtr.Zero;
        int devId = wolfcrypt.INVALID_DEVID;
        byte[] privateKey = null;
        byte[] publicKey = null;
        byte[] message = Encoding.UTF8.GetBytes("This is some data to sign with ML-DSA");
        byte[] signature = null;

        try
        {
            Console.WriteLine("\nStarting " + level + " key generation and signature test ...");

            /* Generate Key */
            Console.WriteLine("Testing ML-DSA Key Generation...");
            key = wolfcrypt.MlDsaMakeKey(heap, devId, level);
            if (key == IntPtr.Zero)
            {
                ret = -1;
                Console.Error.WriteLine("Failed to generate keypair.");
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-DSA Key Generation test passed.");
            }

            /* Export */
            if (ret == 0)
            {
                Console.WriteLine("Testing ML-DSA Key Export...");
                ret = wolfcrypt.MlDsaExportPrivateKey(key, out privateKey);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to export private key. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                ret = wolfcrypt.MlDsaExportPublicKey(key, out publicKey);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to export public key. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-DSA Key Export test passed.");
            }

            /* Import into a fresh key to test the full import workflow */
            if (ret == 0)
            {
                Console.WriteLine("Testing ML-DSA Key Import...");
                /* Free the keygen key and create a fresh one for import */
                wolfcrypt.MlDsaFreeKey(ref key);
                importKey = wolfcrypt.MlDsaNew(heap, devId, level);
                if (importKey == IntPtr.Zero)
                {
                    ret = -1;
                    Console.Error.WriteLine("Failed to allocate key for import.");
                }
            }
            if (ret == 0)
            {
                ret = wolfcrypt.MlDsaImportPrivateKey(privateKey, importKey);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to import private key. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                ret = wolfcrypt.MlDsaImportPublicKey(publicKey, importKey);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to import public key. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-DSA Key Import test passed.");
            }

            /* Sign with imported key */
            if (ret == 0)
            {
                Console.WriteLine("Testing ML-DSA Signature Creation...");
                ret = wolfcrypt.MlDsaSignMsg(importKey, message, out signature);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to sign. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine($"ML-DSA Signature Creation test passed. Signature Length: {signature.Length}");
            }

            /* Verify with imported key */
            if (ret == 0)
            {
                Console.WriteLine("Testing ML-DSA Signature Verification...");
                ret = wolfcrypt.MlDsaVerifyMsg(importKey, message, signature);
                if (ret != 0)
                {
                    Console.Error.WriteLine($"Failed to verify message. Error code: {ret}");
                }
            }
            if (ret == 0)
            {
                Console.WriteLine("ML-DSA Signature Verification test passed.");
            }

            if (ret != 0)
            {
                throw new Exception("ML-DSA test failed.");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"ML-DSA test failed: {ex.Message}");
            throw;
        }
        finally
        {
            if (key != IntPtr.Zero)
            {
                wolfcrypt.MlDsaFreeKey(ref key);
            }
            if (importKey != IntPtr.Zero)
            {
                wolfcrypt.MlDsaFreeKey(ref importKey);
            }
        }

    } /* END mldsa_test */

    private static void aes_gcm_test()
    {
        IntPtr aes = IntPtr.Zero;
        byte[] key;
        byte[] iv;
        byte[] plaintext;
        byte[] ciphertext;
        byte[] addAuth;
        byte[] authTag;
        byte[] decrypted;
        int ret;

        try
        {
            Console.WriteLine("\nStarting AES-GCM tests...");

            IntPtr heap = IntPtr.Zero;
            int devId = wolfcrypt.INVALID_DEVID;

            /* Initialize AES-GCM Context */
            Console.WriteLine("Testing AES-GCM Initialization...");

            /*
             * This is from the Test Case 16 from the document Galois/
             * Counter Mode of Operation (GCM) by McGrew and
             * Viega.
             */

            key = new byte[32]
            {
                0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
                0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08,
                0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c,
                0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08
            };

            iv = new byte[12]
            {
                0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad,
                0xde, 0xca, 0xf8, 0x88
            };

            plaintext = new byte[]
            {
                0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5,
                0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a,
                0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda,
                0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72,
                0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53,
                0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25,
                0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57,
                0xba, 0x63, 0x7b, 0x39
            };


            ciphertext = new byte[]
            {
                0x52, 0x2d, 0xc1, 0xf0, 0x99, 0x56, 0x7d, 0x07,
                0xf4, 0x7f, 0x37, 0xa3, 0x2a, 0x84, 0x42, 0x7d,
                0x64, 0x3a, 0x8c, 0xdc, 0xbf, 0xe5, 0xc0, 0xc9,
                0x75, 0x98, 0xa2, 0xbd, 0x25, 0x55, 0xd1, 0xaa,
                0x8c, 0xb0, 0x8e, 0x48, 0x59, 0x0d, 0xbb, 0x3d,
                0xa7, 0xb0, 0x8b, 0x10, 0x56, 0x82, 0x88, 0x38,
                0xc5, 0xf6, 0x1e, 0x63, 0x93, 0xba, 0x7a, 0x0a,
                0xbc, 0xc9, 0xf6, 0x62
            };

            addAuth = new byte[]
            {
                0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
                0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef,
                0xab, 0xad, 0xda, 0xd2
            };

            authTag = new byte[16];

            aes = wolfcrypt.AesNew(heap, devId);
            if (aes == IntPtr.Zero)
            {
                throw new Exception($"AesNew failed with error code {aes}");
            }
            Console.WriteLine("AesNew test passed.");

            /* Set AES-GCM Key */
            Console.WriteLine("Testing AES-GCM Key Setting...");
            uint len = (uint)key.Length;
            ret = wolfcrypt.AesGcmSetKey(aes, key);
            if (ret != 0)
            {
                throw new Exception($"AesGcmSetKey failed with error code {ret}");
            }
            Console.WriteLine("AES-GCM Key Setting test passed.");

            /* Encryption */
            Console.WriteLine("Testing AES-GCM Encryption...");
            ret = wolfcrypt.AesGcmEncrypt(aes, iv, plaintext, ciphertext, authTag, addAuth);
            if (ret != 0)
            {
                throw new Exception($"AesGcmEncrypt failed with error code {ret}");
            }
            Console.WriteLine($"AES-GCM Encryption test passed. Ciphertext Length: {ciphertext.Length}");

            /* Decryption */
            Console.WriteLine("Testing AES-GCM Decryption...");
            decrypted = new byte[plaintext.Length];

            ret = wolfcrypt.AesGcmDecrypt(aes, iv, ciphertext, decrypted, authTag, addAuth);
            if (ret != 0)
            {
                throw new Exception($"AesGcmDecrypt failed with error code {ret}");
            }

            /* Verify Decryption */
            if (!plaintext.SequenceEqual(decrypted))
            {
                throw new Exception("Decryption failed: decrypted data does not match original plaintext.");
            }
            Console.WriteLine("AES-GCM Decryption test passed.");

        }
        catch (Exception ex)
        {
            Console.WriteLine($"AES-GCM test failed: {ex.Message}");
        }
        finally
        {
            /* Cleanup */
            if (aes != IntPtr.Zero)
            {
                wolfcrypt.AesGcmFree(aes);
            }
        }
    } /* END aes_gcm_test */

    private static void hash_test(uint hashType)
    {
        IntPtr hash = IntPtr.Zero;
        IntPtr heap = IntPtr.Zero;
        int devId = wolfcrypt.INVALID_DEVID;

        /* Get the enum name */
        string hashTypeName = Enum.GetName(typeof(wolfcrypt.hashType), hashType);

        Console.WriteLine($"\nStarting hash test for {hashTypeName}...");

        /* Allocate new hash context */
        Console.WriteLine("Testing hash context allocation...");
        hash = wolfcrypt.HashNew(hashType, heap, devId);
        if (hash == IntPtr.Zero)
        {
            Console.WriteLine($"HashNew failed for {hashTypeName}");
            return;
        }
        Console.WriteLine("Hash context allocation test passed.");

        /* Initialize the hash context with the specified hash type */
        Console.WriteLine("Testing hash initialization...");
        int initResult = wolfcrypt.InitHash(hash, hashType);
        if (initResult != 0)
        {
            Console.WriteLine($"InitHash failed for {hashTypeName}");
            wolfcrypt.HashFree(hash, hashType);
            return;
        }
        Console.WriteLine("Hash initialization test passed.");

        /* Update the hash with data */
        byte[] dataToHash = Encoding.UTF8.GetBytes("This is some data to hash");
        Console.WriteLine("Testing hash update...");
        int updateResult = wolfcrypt.HashUpdate(hash, hashType, dataToHash);
        if (updateResult != 0)
        {
            Console.WriteLine($"HashUpdate failed for {hashTypeName}");
            wolfcrypt.HashFree(hash, hashType);
            return;
        }
        Console.WriteLine("Hash update test passed.");

        /* Finalize the hash and get the result */
        Console.WriteLine("Testing hash finalization...");
        byte[] hashOutput;
        int finalResult = wolfcrypt.HashFinal(hash, hashType, out hashOutput);
        if (finalResult != 0)
        {
            Console.WriteLine($"HashFinal failed for {hashType}");
            wolfcrypt.HashFree(hash, hashType);
            return;
        }
        Console.WriteLine($"Hash finalization test passed for {hashTypeName}. Hash Length: {hashOutput.Length}");

        /* Output the hash result */
        Console.WriteLine($"Hash Output ({hashTypeName}): {BitConverter.ToString(hashOutput).Replace("-", "")}");

        /* Cleanup */
        Console.WriteLine("Testing hash cleanup...");
        int freeResult = wolfcrypt.HashFree(hash, hashType);
        if (freeResult != 0)
        {
            Console.WriteLine($"HashFree failed for {hashTypeName}");
        }
        else
        {
            Console.WriteLine("Hash cleanup test passed.");
        }
    } /* END hash_test */

    private static void hpke_test(wolfcrypt.HpkeKem kem,
        wolfcrypt.HpkeKdf kdf, wolfcrypt.HpkeAead aead)
    {
        IntPtr hpke = IntPtr.Zero;
        IntPtr receiverKey = IntPtr.Zero;
        IntPtr deserializedKey = IntPtr.Zero;
        IntPtr ephemeralKey = IntPtr.Zero;

        try
        {
            Console.WriteLine("\nStarting HPKE Base mode test...");

            /* Initialize HPKE context */
            Console.WriteLine("Initializing HPKE context...");
            hpke = wolfcrypt.HpkeInit(kem, kdf, aead);
            if (hpke == IntPtr.Zero)
            {
                throw new Exception("HpkeInit failed");
            }
            Console.WriteLine("HPKE context initialization passed.");

            /* Generate receiver keypair */
            Console.WriteLine("Generating receiver keypair...");
            receiverKey = wolfcrypt.HpkeGenerateKeyPair(hpke);
            if (receiverKey == IntPtr.Zero)
            {
                throw new Exception("HpkeGenerateKeyPair (receiver) failed");
            }
            Console.WriteLine("Receiver keypair generation passed.");

            /* Serialize and deserialize public key (round-trip) */
            Console.WriteLine("Testing public key serialize/deserialize round-trip...");
            byte[] pubKeyBytes = wolfcrypt.HpkeSerializePublicKey(hpke, receiverKey);
            if (pubKeyBytes == null)
            {
                throw new Exception("HpkeSerializePublicKey failed");
            }
            Console.WriteLine($"Serialized public key length: {pubKeyBytes.Length}");

            deserializedKey = wolfcrypt.HpkeDeserializePublicKey(hpke, pubKeyBytes);
            if (deserializedKey == IntPtr.Zero)
            {
                throw new Exception("HpkeDeserializePublicKey failed");
            }

            /* Verify round-trip by re-serializing */
            byte[] pubKeyBytes2 = wolfcrypt.HpkeSerializePublicKey(hpke, deserializedKey);
            if (pubKeyBytes2 == null || !wolfcrypt.ByteArrayVerify(pubKeyBytes, pubKeyBytes2))
            {
                throw new Exception("Public key round-trip verification failed");
            }
            Console.WriteLine("Public key round-trip test passed.");

            /* Generate ephemeral keypair for sender */
            Console.WriteLine("Generating ephemeral keypair...");
            ephemeralKey = wolfcrypt.HpkeGenerateKeyPair(hpke);
            if (ephemeralKey == IntPtr.Zero)
            {
                throw new Exception("HpkeGenerateKeyPair (ephemeral) failed");
            }
            Console.WriteLine("Ephemeral keypair generation passed.");

            /* Define test data */
            byte[] info = Encoding.UTF8.GetBytes("HPKE .NET Test");
            byte[] aad = Encoding.UTF8.GetBytes("additional data");
            byte[] plaintext = Encoding.UTF8.GetBytes("Hello HPKE from wolfCrypt .NET!");

            /* Seal (encrypt) */
            Console.WriteLine("Testing HpkeSealBase...");
            byte[] encCiphertext = wolfcrypt.HpkeSealBase(hpke, ephemeralKey,
                receiverKey, info, aad, plaintext);
            if (encCiphertext == null)
            {
                throw new Exception("HpkeSealBase failed");
            }
            Console.WriteLine($"HpkeSealBase passed. Output length: {encCiphertext.Length}");

            /* Open (decrypt) */
            Console.WriteLine("Testing HpkeOpenBase...");
            byte[] decrypted = wolfcrypt.HpkeOpenBase(hpke, receiverKey,
                encCiphertext, info, aad, plaintext.Length);
            if (decrypted == null)
            {
                throw new Exception("HpkeOpenBase failed");
            }
            Console.WriteLine("HpkeOpenBase passed.");

            /* Compare plaintext and decrypted */
            if (!wolfcrypt.ByteArrayVerify(plaintext, decrypted))
            {
                throw new Exception("Decrypted text does not match original plaintext");
            }
            Console.WriteLine("HPKE Base mode test PASSED.");

            /* Test convenience overload (no ephemeral key) */
            Console.WriteLine("Testing HpkeSealBase convenience overload...");
            byte[] encCiphertext2 = wolfcrypt.HpkeSealBase(hpke, receiverKey,
                info, aad, plaintext, kem);
            if (encCiphertext2 == null)
            {
                throw new Exception("HpkeSealBase (convenience) failed");
            }
            Console.WriteLine($"HpkeSealBase convenience passed. Output length: {encCiphertext2.Length}");

            byte[] decrypted2 = wolfcrypt.HpkeOpenBase(hpke, receiverKey,
                encCiphertext2, info, aad, kem);
            if (decrypted2 == null)
            {
                throw new Exception("HpkeOpenBase (convenience) failed");
            }
            if (!wolfcrypt.ByteArrayVerify(plaintext, decrypted2))
            {
                throw new Exception("Convenience seal/open: decrypted text does not match");
            }
            Console.WriteLine("HPKE convenience overload test PASSED.");

            /* Empty plaintext round-trip - native API accepts ptSz == 0 */
            Console.WriteLine("Testing HpkeSealBase/OpenBase with empty plaintext...");
            byte[] emptyPt = new byte[0];
            byte[] encEmpty = wolfcrypt.HpkeSealBase(hpke, receiverKey,
                info, aad, emptyPt, kem);
            if (encEmpty == null)
            {
                throw new Exception("HpkeSealBase with empty plaintext failed");
            }
            byte[] decEmpty = wolfcrypt.HpkeOpenBase(hpke, receiverKey,
                encEmpty, info, aad, kem);
            if (decEmpty == null || decEmpty.Length != 0)
            {
                throw new Exception("HpkeOpenBase with empty plaintext: round-trip failed");
            }
            Console.WriteLine("Empty plaintext round-trip test PASSED.");

            /* Negative test: tampered ciphertext should fail */
            Console.WriteLine("Testing HpkeOpenBase with tampered ciphertext...");
            byte[] tampered = (byte[])encCiphertext.Clone();
            tampered[tampered.Length - 1] ^= 0xFF; /* flip last byte (in tag) */
            byte[] badResult = wolfcrypt.HpkeOpenBase(hpke, receiverKey,
                tampered, info, aad, plaintext.Length);
            if (badResult != null)
            {
                throw new Exception("HpkeOpenBase should fail with tampered ciphertext");
            }
            Console.WriteLine("Tampered ciphertext test PASSED (correctly rejected).");

            /* Negative test: mismatched AAD should fail */
            Console.WriteLine("Testing HpkeOpenBase with mismatched AAD...");
            byte[] wrongAad = Encoding.UTF8.GetBytes("wrong aad");
            badResult = wolfcrypt.HpkeOpenBase(hpke, receiverKey,
                encCiphertext, info, wrongAad, plaintext.Length);
            if (badResult != null)
            {
                throw new Exception("HpkeOpenBase should fail with mismatched AAD");
            }
            Console.WriteLine("Mismatched AAD test PASSED (correctly rejected).");

            /* Null info/aad round-trip - exercises the null-marshaling path through P/Invoke */
            Console.WriteLine("Testing HpkeSealBase/OpenBase with null info and aad...");
            byte[] encNull = wolfcrypt.HpkeSealBase(hpke, receiverKey,
                null, null, plaintext, kem);
            if (encNull == null)
            {
                throw new Exception("HpkeSealBase with null info/aad failed");
            }
            byte[] decNull = wolfcrypt.HpkeOpenBase(hpke, receiverKey,
                encNull, null, null, kem);
            if (decNull == null || !wolfcrypt.ByteArrayVerify(plaintext, decNull))
            {
                throw new Exception("HpkeOpenBase with null info/aad: round-trip failed");
            }
            Console.WriteLine("Null info/aad round-trip test PASSED.");

            /* Negative test: invalid ptLen should fail */
            Console.WriteLine("Testing HpkeOpenBase with invalid ptLen...");
            badResult = wolfcrypt.HpkeOpenBase(hpke, receiverKey,
                encCiphertext, info, aad, plaintext.Length + 1);
            if (badResult != null)
            {
                throw new Exception("HpkeOpenBase should fail with incorrect ptLen");
            }
            Console.WriteLine("Invalid ptLen test PASSED (correctly rejected).");
        }
        finally
        {
            /* Cleanup */
            if (ephemeralKey != IntPtr.Zero)
                wolfcrypt.HpkeFreeKey(hpke, ephemeralKey, kem);
            if (deserializedKey != IntPtr.Zero)
                wolfcrypt.HpkeFreeKey(hpke, deserializedKey, kem);
            if (receiverKey != IntPtr.Zero)
                wolfcrypt.HpkeFreeKey(hpke, receiverKey, kem);
            if (hpke != IntPtr.Zero)
                wolfcrypt.HpkeFree(hpke);
        }
    } /* END hpke_test */

    public static void standard_log(int lvl, StringBuilder msg)
    {
        Console.WriteLine(msg);
    }

    public static void Main(string[] args)
    {
        try
        {
            Console.WriteLine("Starting Cryptographic Tests...\n");

            wolfcrypt.Init();

            /* setup logging to stdout */
            wolfcrypt.SetLogging(standard_log);

            random_test();

            Console.WriteLine("\nStarting ECC tests");

            ecc_test("SHA256", 32); /* Uses SHA-256 (32 byte hash) */
            ecc_test("SHA384", 32); /* Uses SHA-384 (32 byte hash) */
            ecc_test("SHA512", 32); /* Uses SHA-512 (32 byte hash) */

            Console.WriteLine("\nStarting ECIES tests");

            ecies_test(32); /* ECIES test (32 byte key size) */
            ecies_test(48); /* ECIES test (48 byte key size) */
            ecies_test(66); /* ECIES test (66 byte key size) */

            Console.WriteLine("\nStarting ECDHE tests");

            ecdhe_test(32); /* ECDHE shared secret test (32 byte key size) */
            ecdhe_test(48); /* ECDHE shared secret test (48 byte key size) */
            ecdhe_test(66); /* ECDHE shared secret test (66 byte key size) */

            Console.WriteLine("\nStarting RSA tests");

            rsa_test("SHA256", 2048); /* Uses SHA-256 (2048 bit hash) */
            rsa_test("SHA384", 2048); /* Uses SHA-384 (2048 bit hash) */
            rsa_test("SHA512", 2048); /* Uses SHA-512 (2048 bit hash) */

            Console.WriteLine("\nStarting ED25519 test");

            ed25519_test(); /* ED25519 test */

            Console.WriteLine("\nStarting curve25519 test");

            curve25519_test(); /* curve25519 shared secret test */

            Console.WriteLine("\nStarting ML-KEM test");

            mlkem_test(wolfcrypt.MlKemTypes.ML_KEM_512); /* ML-KEM test */
            mlkem_test(wolfcrypt.MlKemTypes.ML_KEM_768); /* ML-KEM test */
            mlkem_test(wolfcrypt.MlKemTypes.ML_KEM_1024); /* ML-KEM test */

            Console.WriteLine("\nStarting ML-DSA test");

            mldsa_test(wolfcrypt.MlDsaLevels.ML_DSA_44); /* ML-DSA test */
            mldsa_test(wolfcrypt.MlDsaLevels.ML_DSA_65); /* ML-DSA test */
            mldsa_test(wolfcrypt.MlDsaLevels.ML_DSA_87); /* ML-DSA test */

            Console.WriteLine("\nStarting AES-GCM test");

            aes_gcm_test(); /* AES_GCM test */

            Console.WriteLine("\nStarting HASH tests");

            hash_test((uint)wolfcrypt.hashType.WC_HASH_TYPE_SHA256); /* SHA-256 HASH test */
            hash_test((uint)wolfcrypt.hashType.WC_HASH_TYPE_SHA384); /* SHA-384 HASH test */
            hash_test((uint)wolfcrypt.hashType.WC_HASH_TYPE_SHA512); /* SHA-512 HASH test */
            hash_test((uint)wolfcrypt.hashType.WC_HASH_TYPE_SHA3_256); /* SHA3_256 HASH test */

            Console.WriteLine("\nStarting HPKE tests");

            hpke_test(wolfcrypt.HpkeKem.DHKEM_P256_HKDF_SHA256,
                wolfcrypt.HpkeKdf.HKDF_SHA256,
                wolfcrypt.HpkeAead.AES_128_GCM);
            hpke_test(wolfcrypt.HpkeKem.DHKEM_X25519_HKDF_SHA256,
                wolfcrypt.HpkeKdf.HKDF_SHA256,
                wolfcrypt.HpkeAead.AES_128_GCM);

            wolfcrypt.Cleanup();

            Console.WriteLine("\nAll tests completed successfully");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An error occurred: {ex.Message}");
            Environment.Exit(-1);
        }
    }
}
