 /****************************************************************************
 File Name        : wolfSSLClientTask.c
 Author           : wolfSSL Inc.
 Date Created     : March 25, 2016
 Current Revision : 1.0
 Notes            : This file contains a simple TLS client task with the goal
                    of demonstrating how the wolfSSL embedded SSL/TLS library
                    can be used with the uTasker stack. This client connects
                    to a server over TLS (using raw TCP sockets), sends an
                    HTTP GET message, reads the response, and closes
                    the socket.

Copyright (C) wolfSSL, Inc. 2020
 *****************************************************************************/

#include "config.h"
#include "wolfSSLClientTask.h"
#include "wolfssl/wolfcrypt/settings.h"
#include "wolfssl/wolfcrypt/aes.h"
#include "wolfssl/wolfcrypt/rsa.h"
#include "wolfssl/ssl.h"

#define TEST_TCP_PORT       0x80
#define TEST_BUFFER_LENGTH  100
#define MAX_TCP_LENGTH      1460
#define RECV_BUFFER_LENGTH  1500

/* ---------------------------- STRUCTS / ENUMS ---------------------------- */

typedef enum
{
    clientInit       = 0,
    clientCryptoTest = 1,
    clientIdle       = 2,
    clientConnecting = 3,
    clientTLSInit    = 4,
    clientTLSConnect = 5,
    clientTLSSend    = 6,
    clientTLSRecv    = 7,
    clientShutdown   = 8,
} clientStates;


/* func_args from wolfCrypt test.h, so don't have to pull in other stuff */
typedef struct func_args {
    int    argc;
    char** argv;
    int    return_code;
} func_args;


typedef struct stTCP_MESSAGE
{
    TCP_HEADER    tTCP_Header;
    unsigned char ucTCP_Message[MAX_TCP_LENGTH];
} TCP_MESSAGE;


/* wolfSSL send callback context struct */
typedef struct stUTASKER_SENDCTX {
    TCP_MESSAGE*  message;      /* TCP frame to send */
    USOCKET*      socket;       /* socket pointer */
    unsigned int  dataLen;      /* length of data in message frame */
    unsigned char ackd;         /* has ACK been received for data (0:1) */
    unsigned char flags;        /* socket flags, ie: TCP_FLAG_PUSH */
} UTASKER_SENDCTX;


/* wolfSSL recv callback context struct */
typedef struct stUTASKER_RECVCTX {
    USOCKET*       socket;      /* socket pointer */
    unsigned int   used;        /* bytes used in buffer */
    unsigned int   offset;      /* current offset in buffer, for processing */
    unsigned int   bufLen;      /* total size of buffer in bytes */
    unsigned char* buffer;      /* recv data buffer */
} UTASKER_RECVCTX;

/* ------------------------------- VARIABLES ------------------------------- */

static USOCKET client_socket = 0;           /* client socket */
static TCP_MESSAGE stMessage;               /* structure to hold TCP frame   */
static UTASKER_SENDCTX sendCtx;             /* wolfSSL send callback context */
static UTASKER_RECVCTX recvCtx;             /* wolfSSL recv callback context */
static unsigned char ucRecvBuffer[RECV_BUFFER_LENGTH];    /* TCP recv buffer */
static clientStates clientState = clientCryptoTest;       /* TLS task state  */

static WOLFSSL_CTX* sslCtx;                 /* wolfSSL context context */
static WOLFSSL*     ssl;                    /* wolfSSL session object */

/* server IP address and port */
static unsigned char ucRemoteIP[IPV4_LENGTH] = { 93,184,216,34 };
static unsigned int  uiRemotePort = 443;

/* ------------------------------- PROTOTYPES ------------------------------ */

int CacheRecvBuffer(UTASKER_RECVCTX* ctx, unsigned char* data,
                    unsigned short length);
int ResetRecvBuffer(UTASKER_RECVCTX* ctx);

/* ---------------------------- SOCKET LISTENERS --------------------------- */

static int fnClientListener(USOCKET Socket, unsigned char ucEvent,
                          unsigned char *ucIp_Data, unsigned short usPortLen)
{
    int ret = 0;

    switch (ucEvent)
    {
        case TCP_EVENT_CONREQ:
        case TCP_EVENT_CONNECTED:
            fnDebugMsg("status: TCP_EVENT_CONNECTED\r\n");
            clientState = clientTLSInit;
            break;

        case TCP_EVENT_ACK:
            fnDebugMsg("status: TCP_EVENT_ACK\r\n");
            /* ACK received, set ackd variable in CTX msg */
            sendCtx.ackd = 1;
            break;

        case TCP_EVENT_ARP_RESOLUTION_FAILED:
            fnDebugMsg("status: TCP_EVENT_ARP_RESOLUTION_FAILED\r\n");
            break;

        case TCP_EVENT_PARTIAL_ACK:
            fnDebugMsg("status: TCP_EVENT_PARTIAL_ACK\r\n");
            break;

        case TCP_EVENT_REGENERATE:
            /* frame lost, need to resend last frame, use cached */
            fnDebugMsg("status: TCP_EVENT_REGENERATE\r\n");
            if (sendCtx.ackd == 0) {
                ret = fnSendTCP(*sendCtx.socket,
                                (unsigned char*)&sendCtx.message->tTCP_Header,
                                sendCtx.dataLen, sendCtx.flags);
                if (ret > 0) {
                    return APP_SENT_DATA;
                }
            }
            break;

        case TCP_EVENT_DATA:
            /* data received from server */
            fnDebugMsg("status: TCP_EVENT_DATA\r\n");

            /* copy data into our temp context buffer */
            if (CacheRecvBuffer(&recvCtx, ucIp_Data, usPortLen) < 0) {
                return APP_REJECT_DATA;
            }

            break;

        case TCP_EVENT_ABORT:
        case TCP_EVENT_CLOSE:
        case TCP_EVENT_CLOSED:
            /* server closed connection */
            fnDebugMsg("status: TCP_EVENT_CLOSE || TCP_EVENT_CLOSED\r\n");
            break;
    }
    return APP_ACCEPT;
}

/* ---------------------------- HELPER FUNCTIONS --------------------------- */

/*
 * Copies received data into context buffer to be processed by app task.
 * Returns copied length on success, negative value on error.
 */
int CacheRecvBuffer(UTASKER_RECVCTX* ctx, unsigned char* data,
                    unsigned short length)
{
    if (ctx == NULL || ctx->buffer == NULL || data == NULL)
        return -1;

    /* error if already in use, or too small */
    if (ctx->used > 0 || ctx->bufLen < length)
        return -1;

    uMemcpy(ctx->buffer, data, length);
    ctx->used = length;

    return length;
}

/* Resets the context buffer and sizes
 * Returns 0 on success, -1 on error */
int ResetRecvBuffer(UTASKER_RECVCTX* ctx)
{
    if (ctx == NULL)
        return -1;

    uMemset(ctx->buffer, 0, ctx->bufLen);
    ctx->offset = 0;
    ctx->used = 0;

    return 0;
}

/* -------------------- wolfSSL SEND/RECV/VERIFY CALLBACKS ----------------- */


/*
 * wolfSSL receive callback
 *
 * This function is called by wolfSSL whenever it needs to read data.
 * Returns number of bytes received, or negative error.
 * WOLFSSL_CBIO_ERR_WANT_READ should be returned if more data is
 * needed to be read and wolfSSL should call this function again.
 */
int UTasker_Receive(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
    unsigned int copied = 0;
    UTASKER_RECVCTX* stCtx = (UTASKER_RECVCTX*)ctx;

    if (stCtx == NULL || stCtx->buffer == NULL) {
        fnDebugMsg("UTasker_Receive invalid parameters\r\n");
        return WOLFSSL_CBIO_ERR_GENERAL;
    }

    /* check for socket closed, if no data left return error */
    if ((fnGetTCP_state(*stCtx->socket) & TCP_STATE_CLOSED) &&
        (stCtx->used == 0)) {
        fnDebugMsg("uTasker_Receive socket closed\r\n");
        return WOLFSSL_CBIO_ERR_GENERAL;
    }

    if (stCtx->used == 0) {
        return WOLFSSL_CBIO_ERR_WANT_READ;
    }

    /* copy either desired sz or number of bytes free in buffer */
    copied = min((unsigned int)sz, stCtx->used - stCtx->offset);

    uMemcpy(buf, stCtx->buffer + stCtx->offset, copied);
    stCtx->offset += copied;

    if (stCtx->offset == stCtx->used) {
        /* packet has been drained, reset */
        stCtx->offset = 0;
        stCtx->used = 0;
        uMemset(stCtx->buffer, 0, stCtx->bufLen);
    }

    return copied;
}

/*
 * wolfSSL send callback
 *
 * This function is called by wolfSSL whenever it needs to send data.
 * Returns number of bytes sent, or negative error.
 * WOLFSSL_CBIO_ERR_WANT_WRITE should be returned if more data is
 * needed to be sent and wolfSSL should call this function again.
 */
int UTasker_Send(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
    int ret, send;
    UTASKER_SENDCTX* stCtx = (UTASKER_SENDCTX*)ctx;

    if (stCtx == NULL || stCtx->socket == NULL || stCtx->message == NULL) {
        fnDebugMsg("UTasker_Send, invalid parameters\r\n");
        return WOLFSSL_CBIO_ERR_GENERAL;
    }

    /* return error if socket closed */
    if (fnGetTCP_state(*stCtx->socket) & TCP_STATE_CLOSED) {
        fnDebugMsg("uTasker_Receive socket closed\r\n");
        return WOLFSSL_CBIO_ERR_GENERAL;
    }

    /* return WANT_WRITE if ACK has not been received for last frame */
    if (stCtx->dataLen != 0 && stCtx->ackd == 0) {
        return WOLFSSL_CBIO_ERR_WANT_WRITE;
    }

    send = min(sz, MAX_TCP_LENGTH);
    uMemcpy(stCtx->message->ucTCP_Message, buf, send);
    stCtx->dataLen = send;

    ret = fnSendTCP(*stCtx->socket,
                    (unsigned char*)&stCtx->message->tTCP_Header,
                    (unsigned short)send, stCtx->flags);

    if (ret == NO_ARP_ENTRY) {
        /* dest address must be resolved, try again */
        return WOLFSSL_CBIO_ERR_WANT_WRITE;
    }
    else if (ret <= 0) {
        /* no data sent or socket error */
        return WOLFSSL_CBIO_ERR_GENERAL;
    }

    stCtx->ackd = 0;

    return (int)send;
}

/*
 * wolfSSL verification callback
 *
 * This function is called when peer certificate verification
 * fails. The exact error code can be retrieved through
 * store->error. Returning "1" from this function will
 * allow the SSL/TLS handshake to continue as if verification
 * succeeded. Returning "1" here is not recommended.
 */
int myVerify(int preverify, WOLFSSL_X509_STORE_CTX* store)
{
    (void)preverify;
    char buffer[80];

    fnDebugMsg("In verification callback, error = \r\n");
    fnDebugDec(store->error, 0);
    fnDebugMsg(", ");
    fnDebugMsg(wolfSSL_ERR_error_string(store->error, buffer));
    fnDebugMsg("\r\n");

    return 0;
}

/* ------------------------------- APP TASK -------------------------------- */


/*
 * wolfSSL client app task
 */
extern void fnTLSClientTask(TTASKTABLE *ptrTaskTable)
{
    int ret, errorCode, msgSz = 0;
    char msg[64];
    char reply[1024];

    /* run crypto tests */
    if (clientState == clientCryptoTest)
    {
        fnDebugMsg("Starting wolfSSL Client Task\r\n");

        ret = WolfCryptTest();
        if (ret == 0) {
            fnDebugMsg("status: wolfCrypt Tests Passed!\r\n\r\n");
            clientState = clientInit;
        }
        else {
            fnDebugMsg("ERROR: wolfCrypt Tests Failed!\r\n\r\n");
            clientState = clientIdle;
        }
    }

    /* init socket and app state */
    if (clientState == clientInit)
    {
        /* create socket */
        client_socket = fnGetTCP_Socket(TOS_MINIMISE_DELAY,
                                        TCP_DEFAULT_TIMEOUT,
                                        fnClientListener);
        if (client_socket >= 0) {
            fnDebugMsg("status: Created socket\r\n");
            clientState = clientConnecting;
        }
    }

    /* connect socket */
    if (clientState == clientConnecting)
    {
        ret = fnTCP_Connect(client_socket, ucRemoteIP,
                            uiRemotePort, 0, 0);

        if (ret != client_socket) {
            fnDebugMsg("ERROR: fnTCP_Connect() failed\r\n");
            clientState = clientIdle;
        }
        else {
            fnDebugMsg("status: Socket connected\r\n");
            clientState = clientTLSInit;
        }
    }

    /* set up SSL/TLS context */
    if (clientState == clientTLSInit)
    {
        /* for debug, compile wolfSSL with DEBUG_WOLFSSL defined */
        /* wolfSSL_Debugging_ON(); */

        wolfSSL_Init();

        /* create wolfSSL context */
        sslCtx = wolfSSL_CTX_new(wolfTLSv1_2_client_method());
        if (sslCtx == NULL) {
            fnDebugMsg("ERROR: wolfSSL_CTX_new() failed\r\n");
            clientState = clientShutdown;
        }
        else {
            fnDebugMsg("status: Created WOLFSSL_CTX\r\n");
        }

        /* turn on peer verification, register callback */
        wolfSSL_CTX_set_verify(sslCtx, SSL_VERIFY_PEER, myVerify);
        fnDebugMsg("status: Enabled peer verification\r\n");

        /* load root CA certificate to verify peer */
        ret = wolfSSL_CTX_load_verify_buffer(sslCtx, digicert_ca_2048,
                                             sizeof(digicert_ca_2048),
                                             SSL_FILETYPE_ASN1);
        if (ret != SSL_SUCCESS) {
            fnDebugMsg("ERROR: wolfSSL_CTX_load_verify_buffer\r\n");
            clientState = clientShutdown;
        }
        else {
            fnDebugMsg("status: Loaded trusted CA certificates\r\n");
        }

        /* set up wolfSSL send/recv context details */
        sendCtx.socket = &client_socket;
        sendCtx.message = &stMessage;
        sendCtx.ackd = 0;
        sendCtx.dataLen = 0;
        sendCtx.flags = 0;
        recvCtx.socket = &client_socket;
        recvCtx.buffer = ucRecvBuffer;
        recvCtx.bufLen = sizeof(ucRecvBuffer);

        /* register wolfSSL send/recv callbacks */
        wolfSSL_SetIOSend(sslCtx, UTasker_Send);
        wolfSSL_SetIORecv(sslCtx, UTasker_Receive);

        /* create wolfSSL session */
        ssl = wolfSSL_new(sslCtx);
        if (ssl == NULL) {
            fnDebugMsg("ERROR: wolfSSL_new\r\n");
            clientState = clientShutdown;
        }
        else {
            fnDebugMsg("status: Created WOLFSSL session object\r\n");
        }

        /* register wolfSSL read/write callback contexts */
        wolfSSL_SetIOReadCtx(ssl, &recvCtx);
        wolfSSL_SetIOWriteCtx(ssl, &sendCtx);

        clientState = clientTLSConnect;
    }

    /* perform SSL/TLS handshake with peer */
    if (clientState == clientTLSConnect)
    {
        ret = wolfSSL_connect(ssl);
        errorCode = wolfSSL_get_error(ssl, ret);
        if (ret != SSL_SUCCESS && (errorCode != SSL_ERROR_WANT_READ &&
                                   errorCode != SSL_ERROR_WANT_WRITE)) {
            fnDebugMsg("ERROR: wolfSSL_connect: ");
            errorCode = wolfSSL_get_error(ssl, ret);
            fnDebugDec(errorCode, DISPLAY_NEGATIVE);
            fnDebugMsg("\r\n");
            clientState = clientShutdown;
        }
        else if (ret == SSL_SUCCESS) {
            fnDebugMsg("wolfSSL_connect() ok, sending GET...\r\n");
            clientState = clientTLSSend;
        }
    }

    /* send HTTP GET over SSL/TLS */
    if (clientState == clientTLSSend)
    {
        msgSz = 28;
        strncpy(msg, "GET /index.html HTTP/1.0\r\n\r\n", msgSz);
        ret = wolfSSL_write(ssl, msg, msgSz);
        errorCode = wolfSSL_get_error(ssl, ret);
        if (errorCode != SSL_ERROR_WANT_WRITE) {
            if (ret != msgSz) {
                fnDebugMsg("ERROR: wolfSSL_write() failed: ");
                errorCode = wolfSSL_get_error(ssl, ret);
                fnDebugDec(errorCode, DISPLAY_NEGATIVE);
                fnDebugMsg("\r\n");
                clientState = clientShutdown;
            }
            else {
                clientState = clientTLSRecv;
            }
        }
    }

    /* read server response */
    if (clientState == clientTLSRecv)
    {
        ret = wolfSSL_read(ssl, reply, sizeof(reply) - 1);
        errorCode = wolfSSL_get_error(ssl, ret);
        if (errorCode != SSL_ERROR_WANT_READ) {
            if (ret > 0) {
                fnDebugMsg("Server response: ");
                reply[ret] = 0;
                fnDebugMsg(reply);
                fnDebugMsg("\r\n");
            }
            else if (ret < 0) {
                fnDebugMsg("ERROR: wolfSSL_read() failed: ");
                errorCode = wolfSSL_get_error(ssl, ret);
                fnDebugDec(errorCode, DISPLAY_NEGATIVE);
                fnDebugMsg("\r\n");
            }
            clientState = clientShutdown;
        }
    }

    /* free resources and shutdown */
    if (clientState == clientShutdown)
    {
        fnReleaseTCP_Socket(client_socket);
        wolfSSL_free(ssl);
        wolfSSL_CTX_free(sslCtx);
        wolfSSL_Cleanup();

        fnDebugMsg("status: Released Resources\r\n");

        clientState = clientIdle;
    }
}

/* ------------------------- wolfCrypt CRYPTO TESTS ------------------------ */


int WolfCryptTest(void)
{
    int ret;

#if !defined(NO_BIG_INT)
    if (CheckCtcSettings() != 1) {
        fnDebugMsg("ERROR: Build vs runtime math mismatch\r\n");
    }

#ifdef USE_FAST_MATH
    if (CheckFastMathSettings() != 1) {
        fnDebugMsg("ERROR: Build vs runtime fastmath FP_MAX_BITS \
                   mismatch\r\n");
        clientState = clientIdle;
    }
#endif /* USE_FAST_MATH */
#endif /* NO_BIG_INT */

#ifndef NO_MD5
    if ((ret = md5_test()) != 0)
        fnDebugMsg("MD5      test failed!\r\n");
    else
        fnDebugMsg("MD5      test passed!\r\n");
#endif

#ifdef WOLFSSL_MD2
    if ((ret = md2_test()) != 0)
        fnDebugMsg("MD2      test failed!\r\n");
    else
        fnDebugMsg("MD2      test passed!\r\n");
#endif

#ifndef NO_MD4
    if ((ret = md4_test()) != 0)
        fnDebugMsg("MD4      test failed!\r\n");
    else
        fnDebugMsg("MD4      test passed!\r\n");
#endif

#ifndef NO_SHA
    if ((ret = sha_test()) != 0)
        fnDebugMsg("SHA      test failed!\r\n");
    else
        fnDebugMsg("SHA      test passed!\r\n");
#endif

#ifndef NO_SHA256
    if ((ret = sha256_test()) != 0)
        fnDebugMsg("SHA-256  test failed!\r\n");
    else
        fnDebugMsg("SHA-256  test passed!\r\n");
#endif

#ifdef WOLFSSL_SHA384
    if ((ret = sha384_test()) != 0)
        fnDebugMsg("SHA-384  test failed!\r\n");
    else
        fnDebugMsg("SHA-384  test passed!\r\n");
#endif

#ifdef WOLFSSL_SHA512
    if ((ret = sha512_test()) != 0)
        fnDebugMsg("SHA-512  test failed!\r\n");
    else
        fnDebugMsg("SHA-512  test passed!\r\n");
#endif

#ifdef WOLFSSL_RIPEMD
    if ((ret = ripemd_test()) != 0)
        fnDebugMsg("RIPEMD   test failed!\r\n");
    else
        fnDebugMsg("RIPEMD   test passed!\r\n");
#endif

#ifdef HAVE_BLAKE2
    if ((ret = blake2b_test()) != 0)
        fnDebugMsg("BLAKE2b  test failed!\r\n");
    else
        fnDebugMsg("BLAKE2b  test passed!\r\n");
#endif

#ifndef NO_HMAC
#ifndef NO_MD5
    if ((ret = hmac_md5_test()) != 0)
        fnDebugMsg("HMAC-MD5 test failed!\r\n");
    else
        fnDebugMsg("HMAC-MD5 test passed!\r\n");
#endif

#ifndef NO_SHA
    if ((ret = hmac_sha_test()) != 0)
        fnDebugMsg("HMAC-SHA test failed!\r\n");
    else
        fnDebugMsg("HMAC-SHA test passed!\r\n");
#endif

#ifndef NO_SHA256
    if ((ret = hmac_sha256_test()) != 0)
        fnDebugMsg("HMAC-SHA256 test failed!\r\n");
    else
        fnDebugMsg("HMAC-SHA256 test passed!\r\n");
#endif

#ifdef WOLFSSL_SHA384
    if ((ret = hmac_sha384_test()) != 0)
        fnDebugMsg("HMAC-SHA384 test failed!\r\n");
    else
        fnDebugMsg("HMAC-SHA384 test passed!\r\n");
#endif

#ifdef WOLFSSL_SHA512
    if ((ret = hmac_sha512_test()) != 0)
        fnDebugMsg("HMAC-SHA512 test failed!\r\n");
    else
        fnDebugMsg("HMAC-SHA512 test passed!\r\n");
#endif

#ifdef HAVE_BLAKE2
    if ((ret = hmac_blake2b_test()) != 0)
        fnDebugMsg("HMAC-BLAKE2 test failed!\r\n");
    else
        fnDebugMsg("HMAC-BLAKE2 test passed!\r\n");
#endif

#ifdef HAVE_HKDF
    if ((ret = hkdf_test()) != 0)
        fnDebugMsg("HMAC-KDF    test failed!\r\n");
    else
        fnDebugMsg("HMAC-KDF    test passed!\r\n");
#endif

#endif

#ifdef HAVE_AESGCM
    if ((ret = gmac_test()) != 0)
        fnDebugMsg("GMAC     test failed!\r\n");
    else
        fnDebugMsg("GMAC     test passed!\r\n");
#endif

#ifndef NO_RC4
    if ((ret = arc4_test()) != 0)
        fnDebugMsg("ARC4     test failed!\r\n");
    else
        fnDebugMsg("ARC4     test passed!\r\n");
#endif

#ifdef HAVE_CHACHA
    if ((ret = chacha_test()) != 0)
        fnDebugMsg("Chacha   test failed!\r\n");
    else
        fnDebugMsg("Chacha   test passed!\r\n");
#endif

#ifdef HAVE_POLY1305
    if ((ret = poly1305_test()) != 0)
        fnDebugMsg("POLY1305 test failed!\r\n");
    else
        fnDebugMsg("POLY1305 test passed!\r\n");
#endif

#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
    if ((ret = chacha20_poly1305_aead_test()) != 0)
        fnDebugMsg("ChaCha20-Poly1305 AEAD test failed!\r\n");
    else
        fnDebugMsg("ChaCha20-Poly1305 AEAD test passed!\r\n");
#endif

#ifndef NO_DES3
    if ((ret = des_test()) != 0)
        fnDebugMsg("DES      test failed!\r\n");
    else
        fnDebugMsg("DES      test passed!\r\n");
#endif

#ifndef NO_DES3
    if ((ret = des3_test()) != 0)
        fnDebugMsg("DES3     test failed!\r\n");
    else
        fnDebugMsg("DES3     test passed!\r\n");
#endif

#ifndef NO_AES
    if ((ret = aes_test()) != 0)
        fnDebugMsg("AES      test failed!\r\n");
    else
        fnDebugMsg("AES      test passed!\r\n");

#ifdef HAVE_AESGCM
    if ((ret = aesgcm_test()) != 0)
        fnDebugMsg("AES-GCM  test failed!\r\n");
    else
        fnDebugMsg("AES-GCM  test passed!\r\n");
#endif

#ifdef HAVE_AESCCM
    if ((ret = aesccm_test()) != 0)
        fnDebugMsg("AES-CCM  test failed!\r\n");
    else
        fnDebugMsg("AES-CCM  test passed!\r\n");
#endif
#endif

#ifdef HAVE_CAMELLIA
    if ((ret = camellia_test()) != 0)
        fnDebugMsg("CAMELLIA test failed!\r\n");
    else
        fnDebugMsg("CAMELLIA test passed!\r\n");
#endif

    if ((ret = random_test()) != 0)
        fnDebugMsg("RANDOM   test failed!\r\n");
    else
        fnDebugMsg("RANDOM   test passed!\r\n");

#ifndef NO_RSA
    if ((ret = rsa_test()) != 0)
        fnDebugMsg("RSA      test failed!\r\n");
    else
        fnDebugMsg("RSA      test passed!\r\n");
#endif

#if defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_TEST_CERT)
    if ((ret = certext_test()) != 0)
        fnDebugMsg("CERT EXT test failed!\r\n");
    else
        fnDebugMsg("CERT EXT test passed!\r\n");
#endif

#ifndef NO_DH
    if ((ret = dh_test()) != 0)
        fnDebugMsg("DH       test failed!\r\n");
    else
        fnDebugMsg("DH       test passed!\r\n");
#endif

#ifndef NO_DSA
    if ((ret = dsa_test()) != 0)
        fnDebugMsg("DSA      test failed!\r\n");
    else
        fnDebugMsg("DSA      test passed!\r\n");
#endif

#ifdef WOLFCRYPT_HAVE_SRP
    if ((ret = srp_test()) != 0)
        fnDebugMsg("SRP      test failed!\r\n");
    else
        fnDebugMsg("SRP      test passed!\r\n");
#endif

#ifndef NO_PWDBASED
    if ((ret = pwdbased_test()) != 0)
        fnDebugMsg("PWDBASED test failed!\r\n");
    else
        fnDebugMsg("PWDBASED test passed!\r\n");
#endif

#ifdef OPENSSL_EXTRA
    if ((ret = openssl_test()) != 0)
        fnDebugMsg("OPENSSL  test failed!\r\n");
    else
        fnDebugMsg("OPENSSL  test passed!\r\n");
#endif

#ifdef HAVE_ECC
    if ((ret = ecc_test()) != 0)
        fnDebugMsg("ECC      test failed!\r\n");
    else
        fnDebugMsg("ECC      test passed!\r\n");
#ifdef HAVE_ECC_ENCRYPT
    if ((ret = ecc_encrypt_test()) != 0)
        fnDebugMsg("ECC Enc  test failed!\r\n");
    else
        fnDebugMsg("ECC Enc  test passed!\r\n");
#endif
#endif

#ifdef HAVE_CURVE25519
    if ((ret = curve25519_test()) != 0)
        fnDebugMsg("CURVE25519 test failed!\r\n");
    else
        fnDebugMsg("CURVE25519 test passed!\r\n");
#endif

#ifdef HAVE_ED25519
    if ((ret = ed25519_test()) != 0)
        fnDebugMsg("ED25519  test failed!\r\n");
    else
        fnDebugMsg("ED25519  test passed!\r\n");
#endif

#ifdef HAVE_LIBZ
    if ((ret = compress_test()) != 0)
        fnDebugMsg("COMPRESS test failed!\r\n");
    else
        fnDebugMsg("COMPRESS test passed!\r\n");
#endif

#ifdef HAVE_PKCS7
    if ((ret = pkcs7enveloped_test()) != 0)
        fnDebugMsg("PKCS7enveloped test failed!\r\n");
    else
        fnDebugMsg("PKCS7enveloped test passed!\r\n");

    if ((ret = pkcs7signed_test()) != 0)
        fnDebugMsg("PKCS7signed    test failed!\r\n");
    else
        fnDebugMsg("PKCS7signed    test passed!\r\n");
#endif

    return 0;
}

