/* internal.c
 *
 * 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
 */

#include <wolfssl/wolfcrypt/libwolfssl_sources.h>

/*
 * internal.c Build Options:
 *
 * See also: tls.c for TLS extension/protocol options, tls13.c for TLS 1.3,
 *           ssl.c for SSL API layer, wc_port.c for platform/memory.
 *
 * Connection & Buffers:
 * LARGE_STATIC_BUFFERS:       Use large static I/O buffers            default: on
 * WOLFSSL_DISABLE_EARLY_SANITY_CHECKS:
 *                  Disable early sanity checks on TLS messages        default: off
 * WOLFSSL_NO_DTLS_SIZE_CHECK: Disable DTLS record size validation     default: off
 *
 * Cipher Suite Selection:
 * NO_CHAPOL_AEAD:             Disable ChaCha20-Poly1305 AEAD suites   default: off
 * WOLFSSL_OLDTLS_AEAD_CIPHERSUITES:
 *                  Enable AEAD cipher suites for pre-TLS 1.2          default: off
 * WOLFSSL_OLDTLS_SHA2_CIPHERSUITES:
 *                  Enable SHA-2 cipher suites for pre-TLS 1.2         default: off
 * WOLFSSL_NO_STRICT_CIPHER_SUITE:
 *                  Relax strict cipher suite validation               default: off
 * NO_RESUME_SUITE_CHECK:      Skip cipher suite check on resume       default: off
 * NO_FORCE_SCR_SAME_SUITE:    Allow different suite in renegotiation  default: off
 * CIPHER_NONCE:               Per-record cipher nonce for AEAD        default: off
 *
 * Certificate Validation:
 * WOLFSSL_SMALL_CERT_VERIFY:  Verify cert sig without DecodedCert     default: off
 * WOLFSSL_ALT_CERT_CHAINS:    Allow non-validated intermediate CAs    default: off
 * NO_CHECK_PRIVATE_KEY:       Skip key/cert matching validation       default: off
 * WOLFSSL_VERIFY_CB_ALL_CERTS:
 *                  Call verify callback for all chain certs           default: off
 * WOLFSSL_ALWAYS_VERIFY_CB:   Always invoke verify callback           default: off
 * WOLFSSL_ALLOW_NO_CN_IN_SAN: Allow certs with SAN but no CN          default: off
 * WOLFSSL_TRUST_PEER_CERT:    Direct trust of specific peer certs     default: off
 * WOLFSSL_LOCAL_X509_STORE:   Per-context X509 store                  default: off
 * WOLFSSL_APPLE_NATIVE_CERT_VALIDATION:
 *                  Use Apple native cert validation on macOS/iOS      default: off
 * WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION:
 *                  Testing mode for Apple cert validation             default: off
 * HAVE_DANE:                  DNS-based cert validation (DNSSEC)      default: off
 * HAVE_FALLBACK_SCSV:         TLS Fallback SCSV anti-downgrade        default: off
 * WOLFSSL_ACERT:              Attribute certificate support           default: off
 * WOLFSSL_DEBUG_CERTS:        Debug logging for cert processing       default: off
 * WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY:
 *                  Verify hostname using SAN only (not CN)            default: off
 *
 * Handshake Behavior:
 * OLD_HELLO_ALLOWED:          Allow SSLv2-format ClientHello          default: off
 * WOLFSSL_ALTERNATIVE_DOWNGRADE:
 *                  Alternative protocol downgrade detection           default: off
 * WOLFSSL_OLD_TIMINGPADVERIFY:
 *                  Old timing-based CBC padding verification          default: off
 * WOLFSSL_ECDSA_MATCH_HASH:   Match ECDSA hash to curve preference    default: off
 * WOLFSSL_STRONGEST_HASH_SIG: Prefer strongest hash in signatures     default: off
 * USE_ECDSA_KEYSZ_HASH_ALGO:  Select ECDSA hash by key size           default: off
 * WOLFSSL_ALLOW_TLS_SHA1:     Allow SHA-1 cipher suites/signatures    default: off
 * WOLFSSL_EXTRA_ALERTS:       Send additional TLS alert messages      default: off
 * WOLFSSL_NO_ETM_ALERT:       No alert on Encrypt-Then-MAC failure    default: off
 *
 * Secure Renegotiation & PSK:
 * WOLFSSL_SECURE_RENEGOTIATION_ON_BY_DEFAULT:
 *                  Enable secure renegotiation by default             default: off
 * WOLFSSL_PSK_IDENTITY_ALERT: Alert on PSK identity lookup failure    default: off
 *
 * Session Tickets:
 * WOLFSSL_NO_DEF_TICKET_ENC_CB:
 *                  No default ticket encryption callback              default: off
 * WOLFSSL_TICKET_ENC_CHACHA20_POLY1305:
 *                  ChaCha20-Poly1305 for ticket encryption            default: auto
 * WOLFSSL_TICKET_ENC_AES128_GCM:
 *                  AES128-GCM for ticket encryption                   default: auto
 * WOLFSSL_TICKET_ENC_AES256_GCM:
 *                  AES256-GCM for ticket encryption                   default: off
 * WOLFSSL_TICKET_DECRYPT_NO_CREATE:
 *                  No new ticket on successful decryption             default: off
 * WOLFSSL_TICKET_ENC_CBC_HMAC:
 *                  CBC+HMAC for ticket encryption (non-AEAD)          default: off
 * WOLFSSL_NO_TICKET_EXPIRE:   Disable ticket expiration checking      default: off
 *
 * TLS 1.3 Internals:
 * WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC:
 *                  Ignore plaintext alerts when encrypted expected    default: off
 * WOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE:
 *                  Disable peek returning WANT_READ for tickets       default: off
 * WOLFSSL_TLS13_IGNORE_AEAD_LIMITS:
 *                  Ignore AEAD message limits from RFC 8446           default: off
 * WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT:
 *                  Send more ACKs by default in DTLS 1.3              default: off
 *
 * DTLS:
 * WOLFSSL_DTLS_RESEND_ONLY_TIMEOUT:
 *                  Resend previous flight only on timeout             default: off
 * WOLFSSL_DTLS_DISALLOW_FUTURE:
 *                  Reject DTLS records with future epoch              default: off
 * WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS:
 *                  Allow DTLS records to span datagrams               default: off
 * WOLFSSL_DEBUG_DTLS:         Debug logging for DTLS operations       default: off
 *
 * Session Export:
 * WOLFSSL_SESSION_EXPORT:     Enable session export/import            default: off
 * WOLFSSL_SESSION_EXPORT_DEBUG:
 *                  Debug logging for session export/import            default: off
 * WOLFSSL_SESSION_EXPORT_NOPEER:
 *                  Export sessions without peer cert info             default: off
 *
 * Compatibility Layers:
 * WOLFSSL_MYSQL_COMPATIBLE:   MySQL protocol compatibility            default: off
 * WOLFSSL_OPENVPN:            OpenVPN compatibility behaviors         default: off
 *
 * Async & Non-blocking:
 * WOLFSSL_ASYNC_CRYPT_SW:     Software async crypto simulation        default: off
 * WC_X25519_NONBLOCK:         Non-blocking X25519 operations          default: off
 * HAVE_WOLF_EVENT:            Event-driven async processing           default: off
 *
 * Hardware/Platform TLS:
 * WOLFSSL_MAXQ10XX_TLS:       Maxim MAXQ10xx secure element           default: off
 * WOLFSSL_IOTSAFE:            IoTSAFE (GSMA) applet support           default: off
 * WOLFSSL_QNX_CAAM:           QNX CAAM crypto module support          default: off
 * HAVE_DH_DEFAULT_PARAMS:     Include default DH parameters           default: off
 * HAVE_EXT_CACHE:             External session cache callbacks        default: off
 *
 * Hardening:
 * WOLFSSL_HARDEN_TLS:         Implement RFC 9325 recommendations      default: off
 *   WOLFSSL_HARDEN_TLS_ALLOW_TRUNCATED_HMAC:  Allow truncated HMAC
 *   WOLFSSL_HARDEN_TLS_ALLOW_OLD_TLS:         Allow old TLS versions
 *   WOLFSSL_HARDEN_TLS_NO_SCR_CHECK:          No SCR check
 *   WOLFSSL_HARDEN_TLS_NO_PKEY_CHECK:         No public key check
 *   WOLFSSL_HARDEN_TLS_ALLOW_ALL_CIPHERSUITES: Allow all suites
 * WOLFSSL_NO_INIT_CTX_KEY:    Allow SSL objects without loaded keys   default: off
 */

#ifndef WOLFCRYPT_ONLY

#include <wolfssl/internal.h>
#include <wolfssl/error-ssl.h>
#include <wolfssl/wolfcrypt/asn.h>
#include <wolfssl/wolfcrypt/dh.h>
#include <wolfssl/wolfcrypt/hash.h>
#ifdef NO_INLINE
    #include <wolfssl/wolfcrypt/misc.h>
#else
    #define WOLFSSL_MISC_INCLUDED
    #include <wolfcrypt/src/misc.c>
#endif
#if defined(OPENSSL_EXTRA) && defined(WOLFCRYPT_HAVE_SRP) && !defined(NO_SHA)
    #include <wolfssl/wolfcrypt/srp.h>
#endif

#if defined(OPENSSL_EXTRA) && defined(HAVE_SECRET_CALLBACK)
    #include <wolfssl/wolfcrypt/coding.h>
#endif
#ifdef HAVE_LIBZ
    #include "zlib.h"
#endif

#ifdef WOLFSSL_QNX_CAAM
    /* included to get CAAM devId value */
    #include <wolfssl/wolfcrypt/port/caam/wolfcaam.h>
#endif

#ifdef HAVE_ARIA
    /* included to get ARIA devId value */
    #include <wolfssl/wolfcrypt/port/aria/aria-cryptocb.h>
#endif

#if defined(DEBUG_WOLFSSL) || defined(SHOW_SECRETS) || \
    defined(CHACHA_AEAD_TEST) || defined(WOLFSSL_SESSION_EXPORT_DEBUG)
    #ifndef NO_STDIO_FILESYSTEM
        #ifdef FUSION_RTOS
            #include <fclstdio.h>
        #else
            #include <stdio.h>
        #endif
    #endif
#endif

#ifdef __sun
    #include <sys/filio.h>
#endif


#define ERROR_OUT(err, eLabel) { ret = (int)(err); goto eLabel; }
#define CHECK_RET(ret, exp, eLabel) do { if (((ret) = (exp)) != 0) goto eLabel;\
                                       } while(0)

#ifdef _MSC_VER
    /* disable for while(0) cases at the .c level for now */
    #pragma warning(disable:4127)
#endif

#if defined(WOLFSSL_CALLBACKS) && !defined(LARGE_STATIC_BUFFERS)
    #error \
WOLFSSL_CALLBACKS needs LARGE_STATIC_BUFFERS, please add LARGE_STATIC_BUFFERS
#endif

#if defined(HAVE_SECURE_RENEGOTIATION) && defined(HAVE_RENEGOTIATION_INDICATION)
    #error Cannot use both secure-renegotiation and renegotiation-indication
#endif

#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)

#ifndef NO_WOLFSSL_CLIENT
    static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input,
                                   word32* inOutIdx, word32 size);
    #ifndef NO_CERTS
        static int DoCertificateRequest(WOLFSSL* ssl, const byte* input,
                                        word32* inOutIdx, word32 size);
    #endif
    #ifdef HAVE_SESSION_TICKET
        static int DoSessionTicket(WOLFSSL* ssl, const byte* input,
                                   word32* inOutIdx, word32 size);
    #endif
#endif /* !NO_WOLFSSL_CLIENT */


#ifndef NO_WOLFSSL_SERVER
    static int DoClientKeyExchange(WOLFSSL* ssl, byte* input,
                                   word32* inOutIdx, word32 size);
    #if (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \
                        defined(HAVE_ED448)) && !defined(WOLFSSL_NO_CLIENT_AUTH)
        static int DoCertificateVerify(WOLFSSL* ssl, byte* input,
                                       word32* inOutIdx, word32 size);
    #endif
#endif /* !NO_WOLFSSL_SERVER */

#endif /* !NO_TLS && !WOLFSSL_NO_TLS12 */

#if !defined(NO_WOLFSSL_SERVER) && defined(HAVE_SESSION_TICKET)
    #if defined(WOLFSSL_HAPROXY)
        #define SSL_TICKET_CTX(ssl) ssl->initial_ctx->ticketEncCtx
    #else
        #define SSL_TICKET_CTX(ssl) ssl->ctx->ticketEncCtx
    #endif
    #if !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_TLS)
        static int TicketEncCbCtx_Init(WOLFSSL_CTX* ctx,
                                       TicketEncCbCtx* keyCtx);
        static void TicketEncCbCtx_Free(TicketEncCbCtx* keyCtx);
        static int DefTicketEncCb(WOLFSSL* ssl,
                                  byte key_name[WOLFSSL_TICKET_NAME_SZ],
                                  byte iv[WOLFSSL_TICKET_IV_SZ],
                                  byte mac[WOLFSSL_TICKET_MAC_SZ],
                                  int enc, byte* ticket, int inLen, int* outLen,
                                  void* userCtx);
    #endif
#endif /* !NO_WOLFSSL_SERVER && HAVE_SESSION_TICKET */

int writeAeadAuthData(WOLFSSL* ssl, word16 sz, byte type, byte* additional,
                      byte dec, byte** seq, int verifyOrder);

#ifdef WOLFSSL_DTLS
    static int _DtlsCheckWindow(WOLFSSL* ssl);
#endif

#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) && \
    (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
#include <Security/SecCertificate.h>
#include <Security/SecTrust.h>
#include <Security/SecPolicy.h>
static int DoAppleNativeCertValidation(WOLFSSL* ssl, const WOLFSSL_BUFFER_INFO* certs,
                                            int totalCerts);
#endif /* #if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */

#ifdef WOLFSSL_DTLS13
#ifndef WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT
#define WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT 0
#endif
#endif /* WOLFSSL_DTLS13 */

enum processReply {
    doProcessInit = 0,
#ifndef NO_WOLFSSL_SERVER
    runProcessOldClientHello,
#endif
    getRecordLayerHeader,
    getData,
    verifyEncryptedMessage,
    decryptMessage,
    verifyMessage,
    runProcessingOneRecord,
    runProcessingOneMessage
};


#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
#if !defined(NO_WOLFSSL_SERVER) || !defined(NO_WOLFSSL_CLIENT)

/* Server random bytes for TLS v1.3 described downgrade protection mechanism. */
static const byte tls13Downgrade[7] = {
    0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44
};

#define TLS13_DOWNGRADE_SZ  sizeof(tls13Downgrade)

#endif /* !NO_WOLFSSL_SERVER || !NO_WOLFSSL_CLIENT */

#if !defined(NO_OLD_TLS) && !defined(WOLFSSL_AEAD_ONLY)
static int SSL_hmac(WOLFSSL* ssl, byte* digest, const byte* in, word32 sz,
                    int padLen, int content, int verify, int epochOrder);

#endif

#endif /* !NO_TLS && !WOLFSSL_NO_TLS12 */


#if !defined(NO_CERT) && defined(WOLFSSL_BLIND_PRIVATE_KEY)
int wolfssl_priv_der_blind(WC_RNG* rng, DerBuffer* key, DerBuffer** mask)
{
    int ret = 0;
    WC_RNG local_rng;

    if (key != NULL) {
        if (*mask != NULL) {
            FreeDer(mask);
        }
        ret = AllocDer(mask, key->length, key->type, key->heap);
        if ((ret == 0) && (rng == NULL)) {
            if (wc_InitRng(&local_rng) != 0) {
                ret = RNG_FAILURE_E;
            }
            else {
                rng = &local_rng;
            }
        }
        if (ret == 0) {
             ret = wc_RNG_GenerateBlock(rng, (*mask)->buffer, (*mask)->length);
        }
        if (ret == 0) {
            xorbuf(key->buffer, (*mask)->buffer, (*mask)->length);
        }

        if (rng == &local_rng) {
            wc_FreeRng(rng);
        }
    }

    return ret;
}

void wolfssl_priv_der_blind_toggle(DerBuffer* key, const DerBuffer* mask)
{
    if ((key != NULL) && (mask != NULL)) {
        xorbuf(key->buffer, mask->buffer, mask->length);
    }
}

DerBuffer *wolfssl_priv_der_unblind(const DerBuffer* key, const DerBuffer* mask)
{
    DerBuffer *ret;
    if ((key == NULL) || (mask == NULL))
        return NULL;
    if (mask->length > key->length)
        return NULL;
    if (AllocDer(&ret, key->length, key->type, key->heap) != 0)
        return NULL;
    xorbufout(ret->buffer, key->buffer, mask->buffer, mask->length);
    return ret;
}

void wolfssl_priv_der_unblind_free(DerBuffer* key)
{
    if (key != NULL)
        FreeDer(&key);
}

#endif /* !NO_CERT && WOLFSSL_BLIND_PRIVATE_KEY */


#if defined(WOLFSSL_RENESAS_FSPSM_TLS) || defined(WOLFSSL_RENESAS_TSIP_TLS)
#include <wolfssl/wolfcrypt/port/Renesas/renesas_cmn.h>
#endif

#if defined(OPENSSL_EXTRA) && defined(HAVE_SECRET_CALLBACK)

    static int  SessionSecret_callback(WOLFSSL* ssl, void* secret,
                                                  int* secretSz, void* ctx);
#ifdef WOLFSSL_TLS13
    static int  SessionSecret_callback_Tls13(WOLFSSL* ssl, int id,
                       const unsigned char* secret, int secretSz, void* ctx);
#endif

    /*
     * This function builds up string for key-logging then call user's
     * key-log-callback to pass the string.
     * The user's key-logging callback has been set via
     * wolfSSL_CTX_set_keylog_callback function. The logging string format is:
     * "<Label> <hex-encoded client random> <hex-encoded secret>"
     *
     * parameter
     *  - ssl: WOLFSSL object
     *  - secret: pointer to the buffer holding secret
     *  - secretSz: size of secret
     *  - label: for logging string
     *  - labelSz: label size
     * returns 0 on success, negative value on failure.
     */
    static int SessionSecret_callback_common(const WOLFSSL* ssl,
                       const unsigned char* secret, int secretSz,
                       const char* label, int labelSz)
    {
        wolfSSL_CTX_keylog_cb_func logCb = NULL;
        int buffSz;
        byte* log = NULL;
        word32 outSz;
        int idx;
        int ret;

        if (ssl == NULL || secret == NULL || secretSz == 0 ||
            label == NULL || labelSz == 0)
            return BAD_FUNC_ARG;
        if (ssl->arrays == NULL || ssl->ctx == NULL)
            return BAD_FUNC_ARG;

        /* get the user-callback func from CTX */
        logCb = ssl->ctx->keyLogCb;
        if (logCb == NULL)
            return 0;

        /* prepare a log string for passing user callback
         * "<Label> <hex-encoded client random> <hex-encoded secret>" */
        buffSz = labelSz + (RAN_LEN * 2) + 1 + secretSz * 2 + 1;
        log    = XMALLOC(buffSz, ssl->heap, DYNAMIC_TYPE_SECRET);
        if (log == NULL)
            return MEMORY_E;
#ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Add("SessionSecret log", log, buffSz);
#endif

        XMEMSET(log, 0, buffSz);
        XMEMCPY(log, label, labelSz - 1);    /* put label w/o terminator */
        log[labelSz - 1] = ' ';              /* '\0' -> ' ' */

        idx = labelSz;
        outSz = buffSz - idx;
        if ((ret = Base16_Encode(ssl->arrays->clientRandom, RAN_LEN,
                                            log + idx, &outSz)) == 0) {
            idx += (outSz - 1); /* reduce terminator byte */
            outSz = buffSz - idx;

            if (outSz > 1) {
                log[idx++] = ' ';        /* add space*/
                outSz = buffSz - idx;

                if ((ret = Base16_Encode((byte*)secret, secretSz,
                                log + idx, &outSz)) == 0) {
                    logCb(ssl, (char*)log);
                    ret = 0;
                }
            }
            else
                ret = MEMORY_E;
        }
        /* Zero out Base16 encoded secret and other data. */
        ForceZero(log, buffSz);
        XFREE(log, ssl->heap, DYNAMIC_TYPE_SECRET);
        return ret;
    }

    /* Label string for client random. */
    #define SSC_CR      "CLIENT_RANDOM"

    /*
     * This function builds up string for key-logging then call user's
     * key-log-callback to pass the string for TLS1.2 and older.
     * The user's key-logging callback has been set via
     * wolfSSL_CTX_set_keylog_callback function. The logging string format is:
     * "CLIENT_RANDOM <hex-encoded client random> <hex-encoded master-secret>"
     * parameter
     *  - ssl: WOLFSSL object
     *  - secret: pointer to the buffer holding master-secret
     *  - secretSz: size of secret
     *  - ctx: not used
     * returns 0 on success, negative value on failure.
     */
    static int SessionSecret_callback(WOLFSSL* ssl, void* secret,
                    int* secretSz, void* ctx)
    {
        int invalidCount;
        int i;
        (void)ctx;

        if (secret == NULL || secretSz == NULL || *secretSz == 0)
            return BAD_FUNC_ARG;

        /* make sure the given master-secret has a meaningful value */
        invalidCount = 0;
        for (i = 0; i < *secretSz; i++) {
            if (((byte*)secret)[i] == 0) {
                invalidCount++;
            }
        }
        if (invalidCount == *secretSz) {
            WOLFSSL_MSG("master-secret is not valid");
            return 0; /* ignore error */
        }

        return SessionSecret_callback_common(ssl, secret, *secretSz,
                                             SSC_CR, sizeof(SSC_CR));
    }

#if defined(WOLFSSL_TLS13)

     /* Label string for client early traffic secret. */
     #define SSC_TLS13_CETS     "CLIENT_EARLY_TRAFFIC_SECRET"
     /* Label string for client handshake traffic secret. */
     #define SSC_TLS13_CHTS     "CLIENT_HANDSHAKE_TRAFFIC_SECRET"
     /* Label string for server handshake traffic secret. */
     #define SSC_TLS13_SHTS     "SERVER_HANDSHAKE_TRAFFIC_SECRET"
     /* Label string for client traffic secret. */
     #define SSC_TLS13_CTS      "CLIENT_TRAFFIC_SECRET_0"
     /* Label string for server traffic secret. */
     #define SSC_TLS13_STS      "SERVER_TRAFFIC_SECRET_0"
     /* Label string for early exporter secret. */
     #define SSC_TLS13_EES      "EARLY_EXPORTER_SECRET"
     /* Label string for exporter secret. */
     #define SSC_TLS13_ES       "EXPORTER_SECRET"

    /*
     * This function builds up string for key-logging then call user's
     * key-log-callback to pass the string for TLS1.3.
     * The user's key-logging callback has been set via
     * wolfSSL_CTX_set_keylog_callback function. The logging string format is:
     * "<Label> <hex-encoded client random> <hex-encoded secret>"
     *
     * parameter
     *  - ssl: WOLFSSL object
     *  - id: type of secret for logging
     *  - secret: pointer to the buffer holding secret
     *  - secretSz: size of secret
     *  - ctx: not used
     * returns 0 on success, negative value on failure.
     */
    static int SessionSecret_callback_Tls13(WOLFSSL* ssl, int id,
        const unsigned char* secret, int secretSz, void* ctx)
    {
        const char* label;
        int         labelSz = 0;
        (void)ctx;

        switch (id) {
            case CLIENT_EARLY_TRAFFIC_SECRET:
                labelSz = sizeof(SSC_TLS13_CETS);
                label = SSC_TLS13_CETS;
                break;

            case CLIENT_HANDSHAKE_TRAFFIC_SECRET:
                labelSz = sizeof(SSC_TLS13_CHTS);
                label = SSC_TLS13_CHTS;
                break;

            case SERVER_HANDSHAKE_TRAFFIC_SECRET:
                labelSz = sizeof(SSC_TLS13_SHTS);
                label = SSC_TLS13_SHTS;
                break;

            case CLIENT_TRAFFIC_SECRET:
                labelSz = sizeof(SSC_TLS13_CTS);
                label = SSC_TLS13_CTS;
                break;

            case SERVER_TRAFFIC_SECRET:
                labelSz = sizeof(SSC_TLS13_STS);
                label = SSC_TLS13_STS;
                break;

            case EARLY_EXPORTER_SECRET:
                labelSz = sizeof(SSC_TLS13_EES);
                label = SSC_TLS13_EES;
                break;

            case EXPORTER_SECRET:
                labelSz = sizeof(SSC_TLS13_ES);
                label = SSC_TLS13_ES;
                break;

            default:
                return BAD_FUNC_ARG;
        }
        return SessionSecret_callback_common(ssl, secret, secretSz,
                                             label, labelSz);
    }
#endif /* WOLFSSL_TLS13*/
#endif /* OPENSSL_EXTRA && HAVE_SECRET_CALLBACK*/

int IsTLS(const WOLFSSL* ssl)
{
    if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_MINOR)
        return 1;
#ifdef WOLFSSL_DTLS
    if (ssl->version.major == DTLS_MAJOR)
        return 1;
#endif

    return 0;
}

int IsTLS_ex(const ProtocolVersion pv)
{
    if (pv.major == SSLv3_MAJOR && pv.minor >=TLSv1_MINOR)
        return 1;
#ifdef WOLFSSL_DTLS
    if (pv.major == DTLS_MAJOR)
        return 1;
#endif

    return 0;
}


int IsAtLeastTLSv1_2(const WOLFSSL* ssl)
{
    if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_2_MINOR)
        return 1;
#ifdef WOLFSSL_DTLS
    if (ssl->version.major == DTLS_MAJOR && ssl->version.minor <= DTLSv1_2_MINOR)
        return 1;
#endif

    return 0;
}

int IsAtLeastTLSv1_3(const ProtocolVersion pv)
{
    int ret;
    ret = (pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_3_MINOR);

#ifdef WOLFSSL_DTLS13
    if (ret == 0 && pv.major == DTLS_MAJOR && pv.minor <= DTLSv1_3_MINOR)
        return 1;
#endif

    return ret;
}

int IsEncryptionOn(const WOLFSSL* ssl, int isSend)
{
    if (ssl == NULL) {
        return BAD_FUNC_ARG;
    }
#ifdef WOLFSSL_DTLS
    /* For DTLS, epoch 0 is always not encrypted. */
    if (ssl->options.dtls && !isSend) {
        if (!IsAtLeastTLSv1_3(ssl->version) && ssl->keys.curEpoch == 0)
            return 0;
    #ifdef WOLFSSL_DTLS13
        else if (IsAtLeastTLSv1_3(ssl->version)
                     && w64IsZero(ssl->keys.curEpoch64))
            return 0;
    #endif /* WOLFSSL_DTLS13 */

    }
#endif /* WOLFSSL_DTLS */
#ifdef WOLFSSL_QUIC
        if (WOLFSSL_IS_QUIC(ssl) && IsAtLeastTLSv1_3(ssl->version)) {
            return 0;
        }
#endif /* WOLFSSL_QUIC */
    return ssl->keys.encryptionOn &&
        (isSend ? ssl->encrypt.setup : ssl->decrypt.setup);
}


#ifdef WOLFSSL_DTLS
/* Stream Control Transmission Protocol */
/* If SCTP is not enabled returns the state of the dtls option.
 * If SCTP is enabled returns dtls && !sctp. */
int IsDtlsNotSctpMode(WOLFSSL* ssl)
{
#ifdef WOLFSSL_SCTP
    return ssl->options.dtls && !ssl->options.dtlsSctp;
#else
    return ssl->options.dtls;
#endif
}

#if !defined(WOLFSSL_NO_TLS12) && !defined(NO_WOLFSSL_SERVER)
/* Secure Real-time Transport Protocol */
/* If SRTP is not enabled returns the state of the dtls option.
 * If SRTP is enabled returns dtls && !dtlsSrtpProfiles. */
int IsDtlsNotSrtpMode(WOLFSSL* ssl)
{
#ifdef WOLFSSL_SRTP
    return ssl->options.dtls && !ssl->dtlsSrtpProfiles;
#else
    return ssl->options.dtls;
#endif
}
#endif /* !WOLFSSL_NO_TLS12 && !NO_WOLFSSL_SERVER */
#endif /* WOLFSSL_DTLS */


#ifdef HAVE_LIBZ

    /* alloc user allocs to work with zlib */
    static void* myAlloc(void* opaque, unsigned int item, unsigned int size)
    {
        (void)opaque;
        return (void *)XMALLOC(item * size, opaque, DYNAMIC_TYPE_LIBZ);
    }


    static void myFree(void* opaque, void* memory)
    {
        (void)opaque;
        XFREE(memory, opaque, DYNAMIC_TYPE_LIBZ);
    }


    /* init zlib comp/decomp streams, 0 on success */
    static int InitStreams(WOLFSSL* ssl)
    {
        ssl->c_stream.zalloc = (alloc_func)myAlloc;
        ssl->c_stream.zfree  = (free_func)myFree;
        ssl->c_stream.opaque = (voidpf)ssl->heap;

        if (deflateInit(&ssl->c_stream, Z_DEFAULT_COMPRESSION) != Z_OK)
            return ZLIB_INIT_ERROR;

        ssl->didStreamInit = 1;

        ssl->d_stream.zalloc = (alloc_func)myAlloc;
        ssl->d_stream.zfree  = (free_func)myFree;
        ssl->d_stream.opaque = (voidpf)ssl->heap;

        if (inflateInit(&ssl->d_stream) != Z_OK) return ZLIB_INIT_ERROR;

        return 0;
    }


    static void FreeStreams(WOLFSSL* ssl)
    {
        if (ssl->didStreamInit) {
            deflateEnd(&ssl->c_stream);
            inflateEnd(&ssl->d_stream);
        }
    }


    /* compress in to out, return out size or error */
    static int myCompress(WOLFSSL* ssl, byte* in, int inSz, byte* out, int outSz)
    {
        int    err;
        int    currTotal = (int)ssl->c_stream.total_out;

        ssl->c_stream.next_in   = in;
        ssl->c_stream.avail_in  = inSz;
        ssl->c_stream.next_out  = out;
        ssl->c_stream.avail_out = outSz;

        err = deflate(&ssl->c_stream, Z_SYNC_FLUSH);
        if (err != Z_OK && err != Z_STREAM_END) return ZLIB_COMPRESS_ERROR;

        return (int)ssl->c_stream.total_out - currTotal;
    }


    /* decompress in to out, return out size or error */
    static int myDeCompress(WOLFSSL* ssl, byte* in,int inSz, byte* out,int outSz)
    {
        int    err;
        int    currTotal = (int)ssl->d_stream.total_out;

        ssl->d_stream.next_in   = in;
        ssl->d_stream.avail_in  = inSz;
        ssl->d_stream.next_out  = out;
        ssl->d_stream.avail_out = outSz;

        err = inflate(&ssl->d_stream, Z_SYNC_FLUSH);
        if (err != Z_OK && err != Z_STREAM_END) return ZLIB_DECOMPRESS_ERROR;

        return (int)ssl->d_stream.total_out - currTotal;
    }

#endif /* HAVE_LIBZ */


#ifdef WOLFSSL_SESSION_EXPORT
/**
 * serializes the cipher specs struct for exporting
 * @return the amount written to 'exp' buffer
 */
static int ExportCipherSpecState(WOLFSSL* ssl, byte* exp, word32 len, byte ver,
        int type)
{
    word32 idx = 0;
    CipherSpecs* specs;

    WOLFSSL_ENTER("ExportCipherSpecState");

    if (exp == NULL || ssl == NULL) {
        return BAD_FUNC_ARG;
    }

    specs = &ssl->specs;
    if (WOLFSSL_EXPORT_SPC_SZ > len) {
        return BUFFER_E;
    }

    XMEMSET(exp, 0, WOLFSSL_EXPORT_SPC_SZ);

    c16toa(specs->key_size, exp + idx);      idx += OPAQUE16_LEN;
    c16toa(specs->iv_size, exp + idx);       idx += OPAQUE16_LEN;
    c16toa(specs->block_size, exp + idx);    idx += OPAQUE16_LEN;
    c16toa(specs->aead_mac_size, exp + idx); idx += OPAQUE16_LEN;
    exp[idx++] = specs->bulk_cipher_algorithm;
    exp[idx++] = specs->cipher_type;
    exp[idx++] = specs->mac_algorithm;
    exp[idx++] = specs->kea;
    exp[idx++] = specs->sig_algo;
    exp[idx++] = specs->hash_size;
    exp[idx++] = specs->pad_size;
    exp[idx++] = specs->static_ecdh;

    if (idx != WOLFSSL_EXPORT_SPC_SZ) {
        WOLFSSL_MSG("WOLFSSL_EXPORT_SPC_SZ needs updated and export version");
        return DTLS_EXPORT_VER_E;
    }

    /* send over state of AES too */
    if (type == WOLFSSL_EXPORT_TLS &&
            ssl->specs.bulk_cipher_algorithm == wolfssl_aes) {
        byte *pt = (byte*)ssl->encrypt.aes->reg;

        if ((idx + 2*WC_AES_BLOCK_SIZE) > len) {
            WOLFSSL_MSG("Can not fit AES state into buffer");
            return BUFFER_E;
        }
        XMEMCPY(exp + idx, pt, WC_AES_BLOCK_SIZE);
        idx += WC_AES_BLOCK_SIZE;

        pt = (byte*)ssl->decrypt.aes->reg;
        XMEMCPY(exp + idx, pt, WC_AES_BLOCK_SIZE);
        idx += WC_AES_BLOCK_SIZE;
    }

    WOLFSSL_LEAVE("ExportCipherSpecState", idx);
    (void)ver;
    return idx;
}


/* serializes the key struct for exporting */
static int ExportKeyState(WOLFSSL* ssl, byte* exp, word32 len, byte ver,
        byte small, int type)
{
    word32 idx = 0;
    byte   sz;
    Keys* keys;

    WOLFSSL_ENTER("ExportKeyState");

    if (exp == NULL || ssl == NULL) {
        return BAD_FUNC_ARG;
    }

    keys = &(ssl->keys);

    if (DTLS_EXPORT_MIN_KEY_SZ > len) {
        WOLFSSL_MSG("Buffer not large enough for minimum key struct size");
        return BUFFER_E;
    }

    XMEMSET(exp, 0, DTLS_EXPORT_MIN_KEY_SZ);

    c32toa(keys->peer_sequence_number_hi, exp + idx); idx += OPAQUE32_LEN;
    c32toa(keys->peer_sequence_number_lo, exp + idx); idx += OPAQUE32_LEN;
    c32toa(keys->sequence_number_hi, exp + idx);      idx += OPAQUE32_LEN;
    c32toa(keys->sequence_number_lo, exp + idx);      idx += OPAQUE32_LEN;

    #if defined(WOLFSSL_DTLS)
    if (type == WOLFSSL_EXPORT_DTLS) {
        c16toa(keys->peerSeq[0].nextEpoch, exp + idx);  idx += OPAQUE16_LEN;
        c16toa(keys->peerSeq[0].nextSeq_hi, exp + idx); idx += OPAQUE16_LEN;
        c32toa(keys->peerSeq[0].nextSeq_lo, exp + idx); idx += OPAQUE32_LEN;
        c16toa(keys->curEpoch, exp + idx);   idx += OPAQUE16_LEN;
        c16toa(keys->curSeq_hi, exp + idx);  idx += OPAQUE16_LEN;
        c32toa(keys->curSeq_lo, exp + idx);  idx += OPAQUE32_LEN;
        c16toa(keys->peerSeq[0].prevSeq_hi, exp + idx); idx += OPAQUE16_LEN;
        c32toa(keys->peerSeq[0].prevSeq_lo, exp + idx); idx += OPAQUE32_LEN;

        c16toa(keys->dtls_peer_handshake_number, exp + idx);
        idx += OPAQUE16_LEN;
        c16toa(keys->dtls_expected_peer_handshake_number, exp + idx);
        idx += OPAQUE16_LEN;

        c16toa(keys->dtls_sequence_number_hi, exp + idx); idx += OPAQUE16_LEN;
        c32toa(keys->dtls_sequence_number_lo, exp + idx); idx += OPAQUE32_LEN;
        c16toa(keys->dtls_prev_sequence_number_hi, exp + idx);
        idx += OPAQUE16_LEN;
        c32toa(keys->dtls_prev_sequence_number_lo, exp + idx);
        idx += OPAQUE32_LEN;
        c16toa(keys->dtls_epoch, exp + idx);              idx += OPAQUE16_LEN;
        c16toa(keys->dtls_handshake_number, exp + idx);   idx += OPAQUE16_LEN;
    }
    #endif
    c32toa(keys->encryptSz, exp + idx);                   idx += OPAQUE32_LEN;
    c32toa(keys->padSz, exp + idx);                       idx += OPAQUE32_LEN;
    exp[idx++] = keys->encryptionOn;
    exp[idx++] = keys->decryptedCur;

    /* from here on the buffer needs checked because is variable length that
     * can be larger than DTLS_EXPORT_MIN_KEY_SZ */
#ifdef WOLFSSL_DTLS
    if (type == WOLFSSL_EXPORT_DTLS) {
        word32 i;
        if ((OPAQUE16_LEN * 2) + idx +
                (2 * (WOLFSSL_DTLS_WINDOW_WORDS * OPAQUE32_LEN)) > len) {
            WOLFSSL_MSG("Buffer not large enough for WOLFSSL_DTLS_WINDOW_WORDS");
            return BUFFER_E;
        }

        c16toa(WOLFSSL_DTLS_WINDOW_WORDS, exp + idx); idx += OPAQUE16_LEN;
        for (i = 0; i < WOLFSSL_DTLS_WINDOW_WORDS; i++) {
            c32toa(keys->peerSeq[0].window[i], exp + idx);
            idx += OPAQUE32_LEN;
        }
        c16toa(WOLFSSL_DTLS_WINDOW_WORDS, exp + idx); idx += OPAQUE16_LEN;
        for (i = 0; i < WOLFSSL_DTLS_WINDOW_WORDS; i++) {
            c32toa(keys->peerSeq[0].prevWindow[i], exp + idx);
            idx += OPAQUE32_LEN;
        }
    }
#endif

    if (idx >= len) {
        WOLFSSL_MSG("Buffer not large enough for truncated hmac flag");
        return BUFFER_E;
    }

#ifdef HAVE_TRUNCATED_HMAC
    sz         = ssl->truncated_hmac ? TRUNCATED_HMAC_SZ: ssl->specs.hash_size;
    exp[idx++] = ssl->truncated_hmac;
#else
    sz         = ssl->specs.hash_size;
    exp[idx++] = 0; /* no truncated hmac */
#endif

    sz = (small)? 0: sz;
    if (idx + (sz * 2) + OPAQUE8_LEN > len) {
        WOLFSSL_MSG("Buffer not large enough for MAC secret");
        return BUFFER_E;
    }

    exp[idx++] = sz;
    if (sz > 0) {
    #ifndef WOLFSSL_AEAD_ONLY
        XMEMCPY(exp + idx, keys->client_write_MAC_secret, sz); idx += sz;
        XMEMCPY(exp + idx, keys->server_write_MAC_secret, sz); idx += sz;
    #else
        XMEMSET(exp + idx, 0, sz); idx += sz;
        XMEMSET(exp + idx, 0, sz); idx += sz;
    #endif
    }

    sz = (small)? 0: ssl->specs.key_size;
    if (idx + (sz * 2) + OPAQUE8_LEN > len) {
        WOLFSSL_MSG("Buffer not large enough for write key");
        return BUFFER_E;
    }

    exp[idx++] = sz;
    if (sz > 0) {
        XMEMCPY(exp + idx, keys->client_write_key, sz); idx += sz;
        XMEMCPY(exp + idx, keys->server_write_key, sz); idx += sz;
    }

    sz = (small)? 0: ssl->specs.iv_size;
    if (idx + (sz * 2) + OPAQUE8_LEN + AEAD_MAX_EXP_SZ > len) {
        WOLFSSL_MSG("Buffer not large enough for IVs");
        return BUFFER_E;
    }

    exp[idx++] = sz;
    if (sz > 0) {
        XMEMCPY(exp + idx, keys->client_write_IV, sz); idx += sz;
        XMEMCPY(exp + idx, keys->server_write_IV, sz); idx += sz;
    }
    XMEMCPY(exp + idx, keys->aead_exp_IV, AEAD_MAX_EXP_SZ);
    idx += AEAD_MAX_EXP_SZ;

    sz = (small)? 0: ssl->specs.iv_size;
    if (idx + (sz * 2) + OPAQUE8_LEN > len) {
        WOLFSSL_MSG("Buffer not large enough for imp IVs");
        return BUFFER_E;
    }
    exp[idx++] = sz;
    if (sz > 0) {
        XMEMCPY(exp + idx, keys->aead_enc_imp_IV, sz); idx += sz;
        XMEMCPY(exp + idx, keys->aead_dec_imp_IV, sz); idx += sz;
    }

    /* DTLS_EXPORT_KEY_SZ is max value. idx size can vary */
    if (idx > DTLS_EXPORT_KEY_SZ) {
        WOLFSSL_MSG("DTLS_EXPORT_KEY_SZ needs updated and export version");
        return DTLS_EXPORT_VER_E;
    }

    WOLFSSL_LEAVE("ExportKeyState", idx);
    (void)ver;
    (void)type;
    return idx;
}


/**
 * Imports the buffer 'exp' into the 'ssl' CipherSpec structure.
 * @param ssl WOLFSSL structure to import into
 * @param exp input buffer to read from
 * @param len length of exp buffer
 * @param ver version of import buffer found
 * @param type flag for importing a TLS session or DTLS
 *
 * @return size of exp buffer consumed on success and negative value on fail
 */
static int ImportCipherSpecState(WOLFSSL* ssl, const byte* exp, word32 len,
        byte ver, int type)
{
    word32 idx = 0;
    CipherSpecs* specs;
    word32 tmp_seq_peer_lo;
    word32 tmp_seq_peer_hi;
    word32 tmp_seq_lo;
    word32 tmp_seq_hi;
    int ret;

    WOLFSSL_ENTER("ImportCipherSpecState");

    if (exp == NULL || ssl == NULL) {
        return BAD_FUNC_ARG;
    }

    specs= &(ssl->specs);

    if (WOLFSSL_EXPORT_SPC_SZ > len) {
        WOLFSSL_MSG("Buffer not large enough for max spec struct size");
        return BUFFER_E;
    }

    ato16(exp + idx, &specs->key_size);      idx += OPAQUE16_LEN;
    ato16(exp + idx, &specs->iv_size);       idx += OPAQUE16_LEN;
    ato16(exp + idx, &specs->block_size);    idx += OPAQUE16_LEN;
    ato16(exp + idx, &specs->aead_mac_size); idx += OPAQUE16_LEN;
    specs->bulk_cipher_algorithm = exp[idx++];
    specs->cipher_type           = exp[idx++];
    specs->mac_algorithm         = exp[idx++];
    specs->kea                   = exp[idx++];
    specs->sig_algo              = exp[idx++];
    specs->hash_size             = exp[idx++];
    specs->pad_size              = exp[idx++];
    specs->static_ecdh           = exp[idx++];

    if (specs->pad_size != PAD_MD5 && specs->pad_size != PAD_SHA) {
        WOLFSSL_MSG("Importing bad or unknown pad size");
        return BAD_STATE_E;
    }

    /* temporarily save the sequence numbers */
    tmp_seq_peer_lo = ssl->keys.peer_sequence_number_lo;
    tmp_seq_peer_hi = ssl->keys.peer_sequence_number_hi;
    tmp_seq_lo = ssl->keys.sequence_number_lo;
    tmp_seq_hi = ssl->keys.sequence_number_hi;

    if ((ret = SetKeysSide(ssl, ENCRYPT_AND_DECRYPT_SIDE)) < 0) {
        return ret;
    }

    /* reset sequence numbers after setting keys */
    ssl->keys.peer_sequence_number_lo = tmp_seq_peer_lo;
    ssl->keys.peer_sequence_number_hi = tmp_seq_peer_hi;
    ssl->keys.sequence_number_lo = tmp_seq_lo;
    ssl->keys.sequence_number_hi = tmp_seq_hi;

    if (type == WOLFSSL_EXPORT_TLS &&
            ssl->specs.bulk_cipher_algorithm == wolfssl_aes) {
        byte *pt = (byte*)ssl->encrypt.aes->reg;
        XMEMCPY(pt, exp + idx, WC_AES_BLOCK_SIZE);
        idx += WC_AES_BLOCK_SIZE;

        pt = (byte*)ssl->decrypt.aes->reg;
        XMEMCPY(pt, exp + idx, WC_AES_BLOCK_SIZE);
        idx += WC_AES_BLOCK_SIZE;
    }

    WOLFSSL_LEAVE("ImportCipherSpecState", idx);
    (void)ver;
    return idx;
}


/**
 * Import the Key structure
 *
 * @param ssl WOLFSSL structure to import into
 * @param exp buffer to read Key values from
 * @param len max length of buffer 'exp'
 * @param ver version of import buffer found
 * @param type flag for TLS vs DTLS
 *
 * @return amount of data read from exp on success or negative on fail
 */
static int ImportKeyState(WOLFSSL* ssl, const byte* exp, word32 len, byte ver,
        int type)
{
    word32 idx = 0;
    byte   sz;
    Keys  *keys;

    WOLFSSL_ENTER("ImportKeyState");

    if (exp == NULL || ssl == NULL) {
        return BAD_FUNC_ARG;
    }

    keys = &(ssl->keys);

    /* check minimum length -- includes byte used for size indicators */
    if (len < DTLS_EXPORT_MIN_KEY_SZ) {
        WOLFSSL_MSG("Buffer not large enough for minimum expected size");
        return BUFFER_E;
    }
    ato32(exp + idx, &keys->peer_sequence_number_hi); idx += OPAQUE32_LEN;
    ato32(exp + idx, &keys->peer_sequence_number_lo); idx += OPAQUE32_LEN;
    ato32(exp + idx, &keys->sequence_number_hi);      idx += OPAQUE32_LEN;
    ato32(exp + idx, &keys->sequence_number_lo);      idx += OPAQUE32_LEN;

    #if defined(WOLFSSL_DTLS)
    if (type == WOLFSSL_EXPORT_DTLS) {
        ato16(exp + idx, &keys->peerSeq[0].nextEpoch);  idx += OPAQUE16_LEN;
        ato16(exp + idx, &keys->peerSeq[0].nextSeq_hi); idx += OPAQUE16_LEN;
        ato32(exp + idx, &keys->peerSeq[0].nextSeq_lo); idx += OPAQUE32_LEN;
        ato16(exp + idx, &keys->curEpoch);   idx += OPAQUE16_LEN;
        ato16(exp + idx, &keys->curSeq_hi);  idx += OPAQUE16_LEN;
        ato32(exp + idx, &keys->curSeq_lo);  idx += OPAQUE32_LEN;
        ato16(exp + idx, &keys->peerSeq[0].prevSeq_hi); idx += OPAQUE16_LEN;
        ato32(exp + idx, &keys->peerSeq[0].prevSeq_lo); idx += OPAQUE32_LEN;

        ato16(exp + idx, &keys->dtls_peer_handshake_number);
        idx += OPAQUE16_LEN;
        ato16(exp + idx, &keys->dtls_expected_peer_handshake_number);
        idx += OPAQUE16_LEN;

        ato16(exp + idx, &keys->dtls_sequence_number_hi); idx += OPAQUE16_LEN;
        ato32(exp + idx, &keys->dtls_sequence_number_lo); idx += OPAQUE32_LEN;
        ato16(exp + idx, &keys->dtls_prev_sequence_number_hi);
        idx += OPAQUE16_LEN;
        ato32(exp + idx, &keys->dtls_prev_sequence_number_lo);
        idx += OPAQUE32_LEN;
        ato16(exp + idx, &keys->dtls_epoch);              idx += OPAQUE16_LEN;
        ato16(exp + idx, &keys->dtls_handshake_number);   idx += OPAQUE16_LEN;
    }
    #endif
    ato32(exp + idx, &keys->encryptSz);                   idx += OPAQUE32_LEN;
    ato32(exp + idx, &keys->padSz);                       idx += OPAQUE32_LEN;
    keys->encryptionOn = exp[idx++];
    keys->decryptedCur = exp[idx++];

    #if defined(WOLFSSL_DTLS)
    if (type == WOLFSSL_EXPORT_DTLS) {
        word16 i, wordCount, wordAdj = 0;

        /* do window */
        if (idx + OPAQUE16_LEN > len)
            return BUFFER_E;
        ato16(exp + idx, &wordCount);
        idx += OPAQUE16_LEN;

        if (wordCount > WOLFSSL_DTLS_WINDOW_WORDS) {
            wordAdj = (wordCount - WOLFSSL_DTLS_WINDOW_WORDS) * sizeof(word32);
            wordCount = WOLFSSL_DTLS_WINDOW_WORDS;
        }

        if (idx + (wordCount * OPAQUE32_LEN) + wordAdj > len)
            return BUFFER_E;
        XMEMSET(keys->peerSeq[0].window, 0xFF, DTLS_SEQ_SZ);
        for (i = 0; i < wordCount; i++) {
            ato32(exp + idx, &keys->peerSeq[0].window[i]);
            idx += OPAQUE32_LEN;
        }
        idx += wordAdj;

        /* do prevWindow */
        wordAdj = 0;
        if (idx + OPAQUE16_LEN > len)
            return BUFFER_E;
        ato16(exp + idx, &wordCount);
        idx += OPAQUE16_LEN;

        if (wordCount > WOLFSSL_DTLS_WINDOW_WORDS) {
            wordAdj = (wordCount - WOLFSSL_DTLS_WINDOW_WORDS) * sizeof(word32);
            wordCount = WOLFSSL_DTLS_WINDOW_WORDS;
        }

        if (idx + (wordCount * OPAQUE32_LEN) + wordAdj > len)
            return BUFFER_E;
        XMEMSET(keys->peerSeq[0].prevWindow, 0xFF, DTLS_SEQ_SZ);
        for (i = 0; i < wordCount; i++) {
            ato32(exp + idx, &keys->peerSeq[0].prevWindow[i]);
            idx += OPAQUE32_LEN;
        }
        idx += wordAdj;

    }
    #endif

#ifdef HAVE_TRUNCATED_HMAC
    ssl->truncated_hmac = exp[idx++];
#else
    idx++; /* no truncated hmac */
#endif
    sz = exp[idx++];
#ifndef WOLFSSL_AEAD_ONLY
    if (sz > sizeof(keys->client_write_MAC_secret) || (sz * 2) + idx > len) {
        WOLFSSL_MSG("Buffer not large enough for MAC import");
        return BUFFER_E;
    }
    if (sz > 0) {
        XMEMCPY(keys->client_write_MAC_secret, exp + idx, sz); idx += sz;
        XMEMCPY(keys->server_write_MAC_secret, exp + idx, sz); idx += sz;
    }
#else
    if ((sz * 2) + idx > len) {
        return BUFFER_E;
    }
    idx += sz; idx += sz;
#endif

    sz = exp[idx++];
    if (sz > sizeof(keys->client_write_key) || (sz * 2) + idx > len) {
        WOLFSSL_MSG("Buffer not large enough for key import");
        return BUFFER_E;
    }
    if (sz > 0) {
        XMEMCPY(keys->client_write_key, exp + idx, sz); idx += sz;
        XMEMCPY(keys->server_write_key, exp + idx, sz); idx += sz;
    }

    sz = exp[idx++];
    if (sz > sizeof(keys->client_write_IV) || (sz * 2) + idx > len) {
        WOLFSSL_MSG("Buffer not large enough for write IV import");
        return BUFFER_E;
    }
    if (sz > 0) {
        XMEMCPY(keys->client_write_IV, exp + idx, sz); idx += sz;
        XMEMCPY(keys->server_write_IV, exp + idx, sz); idx += sz;
    }
    XMEMCPY(keys->aead_exp_IV, exp + idx, AEAD_MAX_EXP_SZ);
    idx += AEAD_MAX_EXP_SZ;

    sz = exp[idx++];
    if (sz > sizeof(keys->aead_enc_imp_IV) || (sz * 2) + idx > len) {
        WOLFSSL_MSG("Buffer not large enough for imp IV import");
        return BUFFER_E;
    }
    if (sz > 0) {
        XMEMCPY(keys->aead_enc_imp_IV, exp + idx, sz); idx += sz;
        XMEMCPY(keys->aead_dec_imp_IV, exp + idx, sz); idx += sz;
    }

    WOLFSSL_LEAVE("ImportKeyState", idx);
    (void)ver;
    (void)type;
    return idx;
}


/* copy over necessary information from Options struct to buffer
 * On success returns size of buffer used on failure returns a negative value */
static int ExportOptions(WOLFSSL* ssl, byte* exp, word32 len, byte ver,
        int type)
{
    int      idx  = 0;
    word16   zero = 0;
    Options *options;

    WOLFSSL_ENTER("ExportOptions");

    if (ssl == NULL || exp == NULL || len < DTLS_EXPORT_OPT_SZ) {
        return BAD_FUNC_ARG;
    }

    options = &ssl->options;
    if (options == NULL) {
        return BAD_FUNC_ARG;
    }

    XMEMSET(exp, 0, DTLS_EXPORT_OPT_SZ);

    /* these options are kept and sent to indicate verify status and strength
     * of handshake */
    exp[idx++] = options->sendVerify;
    exp[idx++] = options->verifyPeer;
    exp[idx++] = options->verifyNone;
    exp[idx++] = options->downgrade;
#ifndef NO_DH
    c16toa(options->minDhKeySz, exp + idx); idx += OPAQUE16_LEN;
    c16toa(options->maxDhKeySz, exp + idx); idx += OPAQUE16_LEN;
    c16toa(options->dhKeySz, exp + idx);    idx += OPAQUE16_LEN;
#else
    c16toa(zero, exp + idx); idx += OPAQUE16_LEN;
    c16toa(zero, exp + idx); idx += OPAQUE16_LEN;
    c16toa(zero, exp + idx); idx += OPAQUE16_LEN;
#endif
#ifndef NO_RSA
    c16toa((word16)(options->minRsaKeySz), exp + idx); idx += OPAQUE16_LEN;
#else
    c16toa(zero, exp + idx); idx += OPAQUE16_LEN;
#endif
#ifdef HAVE_ECC
    c16toa((word16)(options->minEccKeySz), exp + idx); idx += OPAQUE16_LEN;
#else
    c16toa(zero, exp + idx); idx += OPAQUE16_LEN;
#endif

    /* these options are kept to indicate state and behavior */
#ifndef NO_PSK
    exp[idx++] = options->havePSK;
#else
    exp[idx++] = 0;
#endif
    exp[idx++] = options->sessionCacheOff;
    exp[idx++] = options->sessionCacheFlushOff;
    exp[idx++] = options->side;
    exp[idx++] = options->resuming;
    exp[idx++] = options->haveSessionId;
    exp[idx++] = options->tls;
    exp[idx++] = options->tls1_1;
    exp[idx++] = options->dtls;
    exp[idx++] = options->connReset;
    exp[idx++] = options->isClosed;
    exp[idx++] = options->closeNotify;
    exp[idx++] = options->sentNotify;
    exp[idx++] = options->usingCompression;
    exp[idx++] = options->haveRSA;
    exp[idx++] = options->haveECC;
    exp[idx++] = options->haveDH;
    exp[idx++] = 0; /* Historical: haveNTRU */
    exp[idx++] = 0; /* Historical: haveQSH */
    exp[idx++] = options->haveECDSAsig;
    exp[idx++] = options->haveStaticECC;
    exp[idx++] = options->havePeerVerify;
    exp[idx++] = options->usingPSK_cipher;
    exp[idx++] = options->usingAnon_cipher;
    exp[idx++] = 0; /* Historical: options->sendAlertState */
    exp[idx++] = options->partialWrite;
    exp[idx++] = options->quietShutdown;
    exp[idx++] = options->groupMessages;
#ifdef HAVE_POLY1305
    exp[idx++] = options->oldPoly;
#else
    exp[idx++] = 0;
#endif
#ifdef HAVE_ANON
    exp[idx++] = options->useAnon;
#else
    exp[idx++] = 0;
#endif
#ifdef HAVE_SESSION_TICKET
    exp[idx++] = options->createTicket;
    exp[idx++] = options->useTicket;
    exp[idx++] = options->noTicketTls12;
#ifdef WOLFSSL_TLS13
    if (ver > WOLFSSL_EXPORT_VERSION_3) {
        exp[idx++] = options->noTicketTls13;
    }
#else
    if (ver > WOLFSSL_EXPORT_VERSION_3) {
        exp[idx++] = 0;
    }
#endif
#else
    exp[idx++] = 0;
    exp[idx++] = 0;
    exp[idx++] = 0;
    if (ver > WOLFSSL_EXPORT_VERSION_3) {
        exp[idx++] = 0;
    }
#endif
    exp[idx++] = options->processReply;
    exp[idx++] = options->cipherSuite0;
    exp[idx++] = options->cipherSuite;
    exp[idx++] = options->serverState;
    exp[idx++] = options->clientState;
    exp[idx++] = options->handShakeState;
    exp[idx++] = options->handShakeDone;
    exp[idx++] = options->minDowngrade;
    exp[idx++] = options->connectState;
    exp[idx++] = options->acceptState;
    exp[idx++] = options->asyncState;

    if (type == WOLFSSL_EXPORT_TLS) {
#ifdef HAVE_ENCRYPT_THEN_MAC
        exp[idx++] = options->disallowEncThenMac;
        exp[idx++] = options->encThenMac;
        exp[idx++] = options->startedETMRead;
        exp[idx++] = options->startedETMWrite;
#else
        exp[idx++] = 0;
        exp[idx++] = 0;
        exp[idx++] = 0;
        exp[idx++] = 0;
#endif
    }

    if (ver > WOLFSSL_EXPORT_VERSION_4) {
#ifdef WOLFSSL_DTLS
        exp[idx++] = options->dtlsStateful;
#else
        exp[idx++] = 0;
#endif
    }

    /* version of connection */
    exp[idx++] = ssl->version.major;
    exp[idx++] = ssl->version.minor;

    (void)zero;

    /* check if changes were made and notify of need to update export version */
    switch (ver) {
        case WOLFSSL_EXPORT_VERSION_3:
            if (idx != DTLS_EXPORT_OPT_SZ_3) {
                WOLFSSL_MSG("Update DTLS_EXPORT_OPT_SZ and version of export");
                return DTLS_EXPORT_VER_E;
            }
            break;

        case WOLFSSL_EXPORT_VERSION_4:
            if (idx != DTLS_EXPORT_OPT_SZ_4 && type == WOLFSSL_EXPORT_DTLS) {
                WOLFSSL_MSG("Update DTLS_EXPORT_OPT_SZ and version of export");
                return DTLS_EXPORT_VER_E;
            }
            break;

        case WOLFSSL_EXPORT_VERSION:
            if (idx != DTLS_EXPORT_OPT_SZ && type == WOLFSSL_EXPORT_DTLS) {
                WOLFSSL_MSG("Update DTLS_EXPORT_OPT_SZ and version of export");
                return DTLS_EXPORT_VER_E;
            }
            break;

       default:
            WOLFSSL_MSG("New version case needs added to wolfSSL export");
            return DTLS_EXPORT_VER_E;
    }

    WOLFSSL_LEAVE("ExportOptions", idx);

    (void)type;
    return idx;
}


/* copy items from Export struct to Options struct
 * On success returns size of buffer used on failure returns a negative value */
static int ImportOptions(WOLFSSL* ssl, const byte* exp, word32 len, byte ver,
        int type)
{
    int idx = 0;
    Options* options = &ssl->options;

    switch (ver) {
        case WOLFSSL_EXPORT_VERSION:
            if (len < DTLS_EXPORT_OPT_SZ) {
                WOLFSSL_MSG("Sanity check on buffer size failed");
                return BAD_FUNC_ARG;
            }
            break;

        case WOLFSSL_EXPORT_VERSION_4:
            if (len < DTLS_EXPORT_OPT_SZ_4) {
                WOLFSSL_MSG("Sanity check on buffer size failed");
                return BAD_FUNC_ARG;
            }
            break;

        case WOLFSSL_EXPORT_VERSION_3:
            if (len < DTLS_EXPORT_OPT_SZ_3) {
                WOLFSSL_MSG("Sanity check on buffer size failed");
                return BAD_FUNC_ARG;
            }
            break;

        default:
            WOLFSSL_MSG("Export version not supported");
            return BAD_FUNC_ARG;
    }

    if (exp == NULL || options == NULL) {
        return BAD_FUNC_ARG;
    }


    /* these options are kept and sent to indicate verify status and strength
     * of handshake */
    options->sendVerify = exp[idx++];
    options->verifyPeer = exp[idx++];
    options->verifyNone = exp[idx++];
    options->downgrade  = exp[idx++];
#ifndef NO_DH
    ato16(exp + idx, &(options->minDhKeySz)); idx += OPAQUE16_LEN;
    ato16(exp + idx, &(options->maxDhKeySz)); idx += OPAQUE16_LEN;
    ato16(exp + idx, &(options->dhKeySz));    idx += OPAQUE16_LEN;
#else
    idx += OPAQUE16_LEN;
    idx += OPAQUE16_LEN;
    idx += OPAQUE16_LEN;
#endif
#ifndef NO_RSA
    ato16(exp + idx, (word16*)&(options->minRsaKeySz)); idx += OPAQUE16_LEN;
#else
    idx += OPAQUE16_LEN;
#endif
#ifdef HAVE_ECC
    ato16(exp + idx, (word16*)&(options->minEccKeySz)); idx += OPAQUE16_LEN;
#else
    idx += OPAQUE16_LEN;
#endif

    /* these options are kept to indicate state and behavior */
#ifndef NO_PSK
    options->havePSK = exp[idx++];
#else
    idx++;
#endif
    options->sessionCacheOff      = exp[idx++];
    options->sessionCacheFlushOff = exp[idx++];
    options->side                 = exp[idx++];
    options->resuming             = exp[idx++];
    options->haveSessionId    = exp[idx++];
    options->tls              = exp[idx++];
    options->tls1_1           = exp[idx++];
    options->dtls             = exp[idx++];
    options->connReset        = exp[idx++];
    options->isClosed         = exp[idx++];
    options->closeNotify      = exp[idx++];
    options->sentNotify       = exp[idx++];
    options->usingCompression = exp[idx++];
    options->haveRSA          = exp[idx++];
    options->haveECC          = exp[idx++];
    options->haveDH           = exp[idx++];
    idx++; /* Historical: haveNTRU */
    idx++; /* Historical: haveQSH */
    options->haveECDSAsig     = exp[idx++];
    options->haveStaticECC    = exp[idx++];
    options->havePeerVerify   = exp[idx++];
    options->usingPSK_cipher  = exp[idx++];
    options->usingAnon_cipher = exp[idx++];
    idx++; /* Historical: options->sendAlertState */
    options->partialWrite     = exp[idx++];
    options->quietShutdown    = exp[idx++];
    options->groupMessages    = exp[idx++];
#ifdef HAVE_POLY1305
    options->oldPoly = exp[idx++];      /* set when to use old rfc way of poly*/
#else
    idx++;
#endif
#ifdef HAVE_ANON
    options->useAnon = exp[idx++];     /* User wants to allow Anon suites */
#else
    idx++;
#endif
#ifdef HAVE_SESSION_TICKET
    options->createTicket  = exp[idx++]; /* Server to create new Ticket */
    options->useTicket     = exp[idx++]; /* Use Ticket not session cache */
    options->noTicketTls12 = exp[idx++]; /* Server won't create new Ticket */
#ifdef WOLFSSL_TLS13
    if (ver > WOLFSSL_EXPORT_VERSION_3) {
        options->noTicketTls13 = exp[idx++];/* Server won't create new Ticket */
    }
#else
    if (ver > WOLFSSL_EXPORT_VERSION_3) {
        idx++;
    }
#endif
#else
    idx++;
    idx++;
    idx++;
    if (ver > WOLFSSL_EXPORT_VERSION_3) {
        idx++;
    }
#endif
    options->processReply   = exp[idx++];
    options->cipherSuite0   = exp[idx++];
    options->cipherSuite    = exp[idx++];
    options->serverState    = exp[idx++];
    options->clientState    = exp[idx++];
    options->handShakeState = exp[idx++];
    options->handShakeDone  = exp[idx++];
    options->minDowngrade   = exp[idx++];
    options->connectState   = exp[idx++];
    options->acceptState    = exp[idx++];
    options->asyncState     = exp[idx++];

    if (type == WOLFSSL_EXPORT_TLS) {
#ifdef HAVE_ENCRYPT_THEN_MAC
        options->disallowEncThenMac = exp[idx++];
        options->encThenMac         = exp[idx++];
        options->startedETMRead     = exp[idx++];
        options->startedETMWrite    = exp[idx++];
#else
        idx++;
        idx++;
        idx++;
        idx++;
#endif
    }
#ifdef WOLFSSL_DTLS
    /* If we had a connection established, let's assume that we can act
     * statefully */
    options->dtlsStateful = 1;
#endif

    if (ver > WOLFSSL_EXPORT_VERSION_4) {
#ifdef WOLFSSL_DTLS
        options->dtlsStateful = exp[idx++];
#else
        idx++;
#endif
    }

    /* version of connection */
    if (ssl->version.major != exp[idx++] || ssl->version.minor != exp[idx++]) {
        WOLFSSL_MSG("Version mismatch ie DTLS v1 vs v1.2");
        return VERSION_ERROR;
    }

    /* set TLS 1.3 flag in options if this was a TLS 1.3 connection */
    if (ssl->version.major == SSLv3_MAJOR &&
            ssl->version.minor == TLSv1_3_MINOR) {
        options->tls1_3 = 1;
    }

    return idx;
}


#ifndef WOLFSSL_SESSION_EXPORT_NOPEER
static int ExportPeerInfo(WOLFSSL* ssl, byte* exp, word32 len, byte ver)
{
    int    idx  = 0;
    int    ipSz = MAX_EXPORT_IP; /* start as max size */
    int    fam  = 0;
    word16 port = 0;
    char   ip[MAX_EXPORT_IP];

    if (ver != WOLFSSL_EXPORT_VERSION) {
        WOLFSSL_MSG("Export version not supported");
        return BAD_FUNC_ARG;
    }

    if (ssl == NULL || exp == NULL ||
            len < (sizeof(ip) + 3 * WOLFSSL_EXPORT_LEN)) {
        return BAD_FUNC_ARG;
    }

    if (ssl->ctx->CBGetPeer == NULL) {
        WOLFSSL_MSG("No get peer call back set");
        return BAD_FUNC_ARG;
    }
    if (ssl->ctx->CBGetPeer(ssl, ip, &ipSz, &port, &fam) != WOLFSSL_SUCCESS) {
        WOLFSSL_MSG("Get peer callback error");
        return SOCKET_ERROR_E;
    }

    /* check that ipSz/fam is not negative or too large since user can set cb */
    if (ipSz < 0 || ipSz > MAX_EXPORT_IP || fam < 0) {
        WOLFSSL_MSG("Bad ipSz or fam returned from get peer callback");
        return SOCKET_ERROR_E;
    }

    c16toa((word16)fam, exp + idx);  idx += WOLFSSL_EXPORT_LEN;
    c16toa((word16)ipSz, exp + idx); idx += WOLFSSL_EXPORT_LEN;
    XMEMCPY(exp + idx, ip, ipSz);    idx += ipSz;
    c16toa(port, exp + idx);         idx += WOLFSSL_EXPORT_LEN;

    return idx;
}
#endif /* !WOLFSSL_SESSION_EXPORT_NOPEER */


static int ImportPeerInfo(WOLFSSL* ssl, const byte* buf, word32 len, byte ver)
{
    word16 idx = 0;
    word16 ipSz;
    word16 fam;
    word16 port;
    char   ip[MAX_EXPORT_IP];

    if (ver != WOLFSSL_EXPORT_VERSION && ver != WOLFSSL_EXPORT_VERSION_4 &&
            ver != WOLFSSL_EXPORT_VERSION_3) {
        WOLFSSL_MSG("Export version not supported");
        return BAD_FUNC_ARG;
    }

    if (len == 0) {
        WOLFSSL_MSG("No peer info sent");
        return 0;
    }

    if (ssl == NULL || buf == NULL || len < 3 * WOLFSSL_EXPORT_LEN) {
        return BAD_FUNC_ARG;
    }

    /* import sin family */
    ato16(buf + idx, &fam); idx += WOLFSSL_EXPORT_LEN;

    /* import ip address idx, and ipSz are unsigned but cast for enum */
    ato16(buf + idx, &ipSz); idx += WOLFSSL_EXPORT_LEN;
    if (ipSz >= sizeof(ip) || (word16)(idx + ipSz + WOLFSSL_EXPORT_LEN) > len) {
        return BUFFER_E;
    }
    XMEMSET(ip, 0, sizeof(ip));
    XMEMCPY(ip, buf + idx, ipSz); idx += ipSz;
    ip[ipSz] = '\0'; /* with check that ipSz less than ip this is valid */
    ato16(buf + idx, &port); idx += WOLFSSL_EXPORT_LEN;

    /* sanity check for a function to call, then use it to import peer info */
    if (ssl->ctx->CBSetPeer == NULL) {
        WOLFSSL_MSG("No set peer function");
        return BAD_FUNC_ARG;
    }
    if (ssl->ctx->CBSetPeer(ssl, ip, ipSz, port, fam) != WOLFSSL_SUCCESS) {
        WOLFSSL_MSG("Error setting peer info");
        return SOCKET_ERROR_E;
    }

    return idx;
}


#ifdef WOLFSSL_DTLS
/* WOLFSSL_LOCAL function that serializes the current WOLFSSL session state only
 * buf is used to hold the serialized WOLFSSL struct and sz is the size of buf
 * passed in.
 * On success returns the size of serialized session state.*/
int wolfSSL_dtls_export_state_internal(WOLFSSL* ssl, byte* buf, word32 sz)
{
    int ret;
    word32 idx      = 0;
    word32 totalLen = 0;

    WOLFSSL_ENTER("wolfSSL_dtls_export_state_internal");

    if (buf == NULL || ssl == NULL) {
        WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", BAD_FUNC_ARG);
        return BAD_FUNC_ARG;
    }

    totalLen += WOLFSSL_EXPORT_LEN * 2; /* 2 protocol bytes and 2 length bytes */
    /* each of the following have a 2 byte length before data */
    totalLen += WOLFSSL_EXPORT_LEN + DTLS_EXPORT_MIN_KEY_SZ;
    if (totalLen > sz) {
        WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", BUFFER_E);
        return BUFFER_E;
    }

    buf[idx++] =  (byte)DTLS_EXPORT_STATE_PRO;
    buf[idx++] = ((byte)DTLS_EXPORT_STATE_PRO & 0xF0) |
                 ((byte)WOLFSSL_EXPORT_VERSION & 0X0F);
    idx += WOLFSSL_EXPORT_LEN; /* leave room for total length */

    /* export keys struct and dtls state -- variable length stored in ret */
    idx += WOLFSSL_EXPORT_LEN; /* leave room for length */
    if ((ret = ExportKeyState(ssl, buf + idx, sz - idx,
                    WOLFSSL_EXPORT_VERSION, 1, WOLFSSL_EXPORT_DTLS)) < 0) {
        WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", ret);
        return ret;
    }
    c16toa((word16)ret, buf + idx - WOLFSSL_EXPORT_LEN); idx += ret;

    /* place total length of exported buffer minus 2 bytes protocol/version */
    c16toa((word16)(idx - WOLFSSL_EXPORT_LEN), buf + WOLFSSL_EXPORT_LEN);

#ifdef WOLFSSL_SESSION_EXPORT_DEBUG
    /* if compiled with debug options then print the version, protocol, size */
    {
        char debug[256];
        XSNPRINTF(debug, sizeof(debug), "Exporting DTLS session state\n"
                   "\tVersion  : %d\n\tProtocol : %02X%01X\n\tLength of: %d\n\n"
               , (int)WOLFSSL_EXPORT_VERSION, buf[0], (buf[1] >> 4), idx - 2);
        WOLFSSL_MSG(debug);
    }
#endif /* WOLFSSL_SESSION_EXPORT_DEBUG */

    WOLFSSL_LEAVE("wolfSSL_dtls_export_state_internal", idx);
    return idx;
}


/* On success return amount of buffer consumed */
int wolfSSL_dtls_import_state_internal(WOLFSSL* ssl, const byte* buf, word32 sz)
{
    word32 idx    = 0;
    word16 length = 0;
    int version;
    int ret;

    WOLFSSL_ENTER("wolfSSL_dtls_import_state_internal");
    /* check at least enough room for protocol and length */
    if (sz < WOLFSSL_EXPORT_LEN * 2 || ssl == NULL) {
        WOLFSSL_LEAVE("wolfSSL_dtls_import_state_internal", BAD_FUNC_ARG);
        return BAD_FUNC_ARG;
    }

    if (buf[idx++] !=  (byte)DTLS_EXPORT_STATE_PRO ||
            (buf[idx] & 0xF0) != ((byte)DTLS_EXPORT_PRO & 0xF0)) {
        WOLFSSL_MSG("Incorrect protocol");
        return BAD_FUNC_ARG;
    }
    version = buf[idx++] & 0x0F;

    ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
    if (length > sz - WOLFSSL_EXPORT_LEN) { /* subtract 2 for protocol */
        WOLFSSL_MSG("Buffer size sanity check failed");
        return BUFFER_E;
    }

#ifdef WOLFSSL_SESSION_EXPORT_DEBUG
    /* if compiled with debug options then print the version, protocol, size */
    {
        char debug[256];
        XSNPRINTF(debug, sizeof(debug), "Importing DTLS session state\n"
                   "\tVersion  : %d\n\tProtocol : %02X%01X\n\tLength of: %d\n\n"
               , (int)version, buf[0], (buf[1] >> 4), length);
        WOLFSSL_MSG(debug);
    }
#endif /* WOLFSSL_SESSION_EXPORT_DEBUG */

    /* perform sanity checks and extract Options information used */
    switch (version) {
        case WOLFSSL_EXPORT_VERSION:
            break;

        default:
            WOLFSSL_MSG("Bad export state version");
            return BAD_FUNC_ARG;

    }

    /* perform sanity checks and extract Keys struct */
    if (WOLFSSL_EXPORT_LEN + idx > sz) {
        WOLFSSL_MSG("Import Key struct error");
        return BUFFER_E;
    }
    ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
    if (length > DTLS_EXPORT_KEY_SZ || length + idx > sz) {
        WOLFSSL_MSG("Import Key struct error");
        return BUFFER_E;
    }
    if ((ret = ImportKeyState(ssl, buf + idx, length, version,
                    WOLFSSL_EXPORT_DTLS)) < 0) {
        WOLFSSL_MSG("Import Key struct error");
        WOLFSSL_LEAVE("wolfSSL_dtls_import_state_internal", ret);
        return ret;
    }
    idx += ret;

    WOLFSSL_LEAVE("wolfSSL_dtls_import_state_internal", ret);
    return idx;
}
#endif /* WOLFSSL_DTLS */


/**
 * Imports a serialized buffer (both TLS and DTLS)
 *
 * @param ssl WOLFSSL structure to import into
 * @param buf buffer containing serialized session
 * @param sz  size of buffer 'buf'
 * @param type flag for TLS or DTLS
 *
 * @return the size of serialized buffer on success
 */
int wolfSSL_session_import_internal(WOLFSSL* ssl, const unsigned char* buf,
        unsigned int sz, int type)
{
    word32 idx    = 0;
    word16 length = 0;
    int version   = 0;
    int ret = 0;
    int optSz = 0;
    int rc;

    WOLFSSL_ENTER("wolfSSL_session_import_internal");
    /* check at least enough room for protocol and length */
    if (sz < WOLFSSL_EXPORT_LEN * 2 || ssl == NULL) {
        ret = BAD_FUNC_ARG;
    }

    /* Check if is TLS export protocol */
    if (ret == 0) {
        byte validProto = 0; /* did we find a valid protocol */

        if (buf[idx]             ==  (byte)TLS_EXPORT_PRO &&
           (buf[idx + 1] & 0xF0) == ((byte)TLS_EXPORT_PRO & 0xF0)) {
            validProto = 1;
        }

        /* Check if is DTLS export protocol */
        if (buf[idx]             ==  (byte)DTLS_EXPORT_PRO &&
           (buf[idx + 1] & 0xF0) == ((byte)DTLS_EXPORT_PRO & 0xF0)) {
            validProto = 1;
        }

        if (validProto == 0) {
        #ifdef WOLFSSL_DTLS
            /* check if importing state only */
            return wolfSSL_dtls_import_state_internal(ssl, buf, sz);
        #else
            WOLFSSL_MSG("Invalid serialized session protocol value");
            ret = BAD_FUNC_ARG;
        #endif
        }
        idx += 1;
    }

    if (ret == 0) {
        version = buf[idx++] & 0x0F;
        ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
        if (length > sz - WOLFSSL_EXPORT_LEN) { /* subtract 2 for protocol */
            ret = BUFFER_E;
        }
    }

    /* if compiled with debug options then print the version, protocol, size */
#ifdef WOLFSSL_SESSION_EXPORT_DEBUG
    {
        char debug[256];
        XSNPRINTF(debug, sizeof(debug), "Importing DTLS session\n"
                   "\tVersion  : %d\n\tProtocol : %02X%01X\n\tLength of: %d\n\n"
               , (int)version, buf[0], (buf[1] >> 4), length);
        WOLFSSL_MSG(debug);
    }
#endif /* WOLFSSL_SESSION_EXPORT_DEBUG */

    /* perform sanity checks and extract Options information used */
    if (ret == 0) {
        switch (version) {
            case WOLFSSL_EXPORT_VERSION:
                if (type == WOLFSSL_EXPORT_DTLS) {
                    optSz = DTLS_EXPORT_OPT_SZ;
                }
                else {
                    optSz = TLS_EXPORT_OPT_SZ;
                }
                break;

            case WOLFSSL_EXPORT_VERSION_4:
                if (type == WOLFSSL_EXPORT_DTLS) {
                    optSz = DTLS_EXPORT_OPT_SZ_4;
                }
                else {
                    optSz = TLS_EXPORT_OPT_SZ_4;
                }
                break;

            case WOLFSSL_EXPORT_VERSION_3:
                WOLFSSL_MSG("Importing older version 3");
                optSz = DTLS_EXPORT_OPT_SZ_3;
                break;

            default:
                WOLFSSL_MSG("Bad export version");
                ret = BAD_FUNC_ARG;
        }
    }

    if (ret == 0 && (WOLFSSL_EXPORT_LEN + optSz + idx > sz)) {
        WOLFSSL_MSG("Import Options struct error");
        ret = BUFFER_E;
    }

    if (ret == 0) {
        ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
        if (length != optSz) {
            WOLFSSL_MSG("Import Options struct error");
            ret = BUFFER_E;
        }
    }

    if (ret == 0) {
        rc = ImportOptions(ssl, buf + idx, length, version, type);
        if (rc < 0) {
            WOLFSSL_MSG("Import Options struct error");
            ret = rc;
        }
        else {
            idx += length;
        }
    }

    /* perform sanity checks and extract Keys struct */
    if (ret == 0 && (WOLFSSL_EXPORT_LEN + idx > sz)) {
        WOLFSSL_MSG("Import Key struct error");
        ret = BUFFER_E;
    }

    if (ret == 0) {
        ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
        if (length > DTLS_EXPORT_KEY_SZ || length + idx > sz) {
            WOLFSSL_MSG("Import Key struct error");
            ret = BUFFER_E;
        }
    }

    if (ret == 0) {
        rc = ImportKeyState(ssl, buf + idx, length, version, type);
        if (rc < 0) {
            WOLFSSL_MSG("Import Key struct error");
            ret = rc;
        }
        else {
            idx += rc;
        }
    }

    /* perform sanity checks and extract CipherSpecs struct */
    if (ret == 0 && (WOLFSSL_EXPORT_LEN + WOLFSSL_EXPORT_SPC_SZ + idx > sz)) {
        WOLFSSL_MSG("Import CipherSpecs struct error");
        ret = BUFFER_E;
    }

    if (ret == 0) {
        ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
        if (length != WOLFSSL_EXPORT_SPC_SZ) {
            WOLFSSL_MSG("Import CipherSpecs struct error");
            ret = BUFFER_E;
        }
    }

    if (ret == 0) {
        rc = ImportCipherSpecState(ssl, buf + idx, length, version, type);
        if (rc < 0) {
            WOLFSSL_MSG("Import CipherSpecs struct error");
            ret = rc;
        }
        else {
            idx += rc;
        }
    }

    /* perform sanity checks and extract DTLS peer info */
    if (ret == 0 && (WOLFSSL_EXPORT_LEN + idx > sz)) {
        WOLFSSL_MSG("Import DTLS peer info error");
        ret = BUFFER_E;
    }

    if (ret == 0) {
        ato16(buf + idx, &length); idx += WOLFSSL_EXPORT_LEN;
        if (idx + length > sz) {
            WOLFSSL_MSG("Import DTLS peer info error");
            ret = BUFFER_E;
        }
    }

    if (ret == 0) {
        rc = ImportPeerInfo(ssl, buf + idx, length, version);
        if (rc < 0) {
            WOLFSSL_MSG("Import Peer Addr error");
            ret = rc;
        }
        else {
            idx += rc;
        }
    }

    /* make sure is a valid suite used */
    if (ret == 0 && wolfSSL_get_cipher(ssl) == NULL) {
        WOLFSSL_MSG("Can not match cipher suite imported");
        ret = MATCH_SUITE_ERROR;
    }

#ifndef WOLFSSL_AEAD_ONLY
    /* set hmac function to use when verifying */
    if (ret == 0 && (ssl->options.tls == 1 || ssl->options.tls1_1 == 1 ||
                     ssl->options.dtls == 1)) {
    #if !defined(WOLFSSL_RENESAS_FSPSM_TLS) && \
        !defined(WOLFSSL_RENESAS_TSIP_TLS)
        ssl->hmac = TLS_hmac;
    #else
        ssl->hmac = Renesas_cmn_TLS_hmac;
    #endif
    }

    /* do not allow stream ciphers with DTLS, except for NULL cipher */
    if (ret == 0 && ssl->specs.cipher_type == stream &&
        ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null) {
        WOLFSSL_MSG("Can not import stream ciphers for DTLS");
        ret = SANITY_CIPHER_E;
    }
#endif /* !WOLFSSL_AEAD_ONLY */

    if (ret != 0) {
        idx = ret;
    }
    WOLFSSL_LEAVE("wolfSSL_session_import_internal", idx);
    return idx;
}


/**
 * Handles serializing the session information.
 *
 * @param ssl WOLFSSL structure to serialize session from
 * @param buf output buffer to hold serialized session
 * @param sz  the size of buffer 'buf', if too small then gets updated
 * @param type if the input WOLFSSL structure is expected to be TLS or DTLS
 *              1 for yes is TLS and 0 for no is DTLS
 *
 * @return the size of serialized buffer on success and negative values on fail
 */
int wolfSSL_session_export_internal(WOLFSSL* ssl, byte* buf, word32* sz,
        int type)
{
    int ret = 0;
    word32 idx      = 0;
    word32 totalLen = 0;

    WOLFSSL_ENTER("wolfSSL_session_export_internal");

    if (ssl == NULL) {
        WOLFSSL_MSG("unexpected null argument");
        ret = BAD_FUNC_ARG;
    }

    if (ret == 0) {
        totalLen += WOLFSSL_EXPORT_LEN * 2; /* 2 protocol bytes and 2 length bytes */
        /* each of the following have a 2 byte length before data */
        totalLen += WOLFSSL_EXPORT_LEN + DTLS_EXPORT_OPT_SZ;
        totalLen += WOLFSSL_EXPORT_LEN + DTLS_EXPORT_KEY_SZ;
        totalLen += WOLFSSL_EXPORT_LEN + WOLFSSL_EXPORT_SPC_SZ;
        #ifdef WOLFSSL_DTLS
        if (type == WOLFSSL_EXPORT_DTLS) {
            totalLen += WOLFSSL_EXPORT_LEN + ssl->buffers.dtlsCtx.peer.sz;
        }
        #endif
    }

    /* check if sz is sufficient for the worst-case scenario computed above,
     * TLS cipher states add more */
    if (ret == 0 && (totalLen > *sz || buf == NULL)) {
        WOLFSSL_MSG("export buffer was too small or null");
        *sz = totalLen;

        /* possible AES state needed */
        if (type == WOLFSSL_EXPORT_TLS) {
            *sz += WC_AES_BLOCK_SIZE*2;
        }
        ret = WC_NO_ERR_TRACE(LENGTH_ONLY_E);
    }

    if (ret == 0) {
        buf[idx++] =  (byte)(type == WOLFSSL_EXPORT_TLS)? TLS_EXPORT_PRO :
                    DTLS_EXPORT_PRO;
        buf[idx++] = ((byte)((type == WOLFSSL_EXPORT_TLS)? TLS_EXPORT_PRO :
                    DTLS_EXPORT_PRO) & 0xF0)
                    | ((byte)WOLFSSL_EXPORT_VERSION & 0X0F);

        idx += WOLFSSL_EXPORT_LEN; /* leave spot for length of total buffer  */

        idx += WOLFSSL_EXPORT_LEN;
        ret = ExportOptions(ssl, buf + idx, *sz - idx, WOLFSSL_EXPORT_VERSION,
                type);
        if (ret >= 0) {
            c16toa((word16)ret, buf + idx - WOLFSSL_EXPORT_LEN);
            idx += ret;
            ret  = 0;
        }
    }

    /* export keys struct and dtls state -- variable length stored in ret */
    if (ret == 0) {
        idx += WOLFSSL_EXPORT_LEN; /* leave room for length */
        ret = ExportKeyState(ssl, buf + idx, *sz - idx, WOLFSSL_EXPORT_VERSION,
                0, type);
        if (ret >= 0) {
            c16toa((word16)ret, buf + idx - WOLFSSL_EXPORT_LEN); idx += ret;
            ret = 0;
        }
    }

    /* export of cipher specs struct */
    if (ret == 0) {
        c16toa((word16)WOLFSSL_EXPORT_SPC_SZ, buf + idx);
        idx += WOLFSSL_EXPORT_LEN;
        ret = ExportCipherSpecState(ssl, buf + idx, *sz - idx,
                                                 WOLFSSL_EXPORT_VERSION, type);
        if (ret >= 0) {
            idx += ret;
            ret  = 0;
        }
    }

    /* export of peer information */
    if (ret == 0) {
        idx += WOLFSSL_EXPORT_LEN;
    #ifdef WOLFSSL_SESSION_EXPORT_NOPEER
        ret = 0; /* not saving peer port/ip information */
    #else
        ret = ExportPeerInfo(ssl, buf + idx, *sz - idx, WOLFSSL_EXPORT_VERSION);
    #endif
        if (ret >= 0) {
            c16toa(ret, buf + idx - WOLFSSL_EXPORT_LEN);
            idx += ret;
            ret  = 0;
        }
    }

    if (ret != 0 && ret != WC_NO_ERR_TRACE(LENGTH_ONLY_E) && buf != NULL) {
        /*in a fail case clear the buffer which could contain partial key info*/
        XMEMSET(buf, 0, *sz);
    }

    /* place total length of exported buffer minus 2 bytes protocol/version */
    if (ret == 0) {
        c16toa((word16)(idx - WOLFSSL_EXPORT_LEN), buf + WOLFSSL_EXPORT_LEN);
        ret = idx;

    #ifdef WOLFSSL_SESSION_EXPORT_DEBUG
        {
            char debug[256];
            XSNPRINTF(debug, sizeof(debug), "Exporting TLS session\n"
                   "\tVersion  : %d\n\tProtocol : %02X%01X\n\tLength of: %d\n\n"
                  ,(int)WOLFSSL_EXPORT_VERSION, buf[0], (buf[1] >> 4), idx - 2);
            WOLFSSL_MSG(debug);
        }
    #endif /* WOLFSSL_SESSION_EXPORT_DEBUG */
    }

    if (ret >= 0) {
        *sz = ret;
    }

    WOLFSSL_LEAVE("wolfSSL_session_export_internal", ret);
    return ret;
}
#endif /* WOLFSSL_SESSION_EXPORT */


void InitSSL_Method(WOLFSSL_METHOD* method, ProtocolVersion pv)
{
    method->version    = pv;
    method->side       = WOLFSSL_CLIENT_END;
    method->downgrade  = 0;
}

#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_EITHER_SIDE) || \
    defined(WOLFSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)
int InitSSL_Side(WOLFSSL* ssl, word16 side)
{
    if (ssl == NULL)
        return BAD_FUNC_ARG;

    /* set side */
    ssl->options.side = side;

    /* reset options that are side specific */
#ifdef HAVE_ECC
    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        ssl->options.haveECDSAsig  = 1; /* always on client side */
        ssl->options.haveECC = 1;       /* server turns on with ECC key cert */
        ssl->options.haveStaticECC = 1; /* server can turn on by loading key */
    }
#elif defined(HAVE_ED25519) || defined(HAVE_ED448)
    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        ssl->options.haveECDSAsig  = 1; /* always on client side */
        ssl->options.haveECC  = 1;      /* server turns on with ECC key cert */
    }
#endif
#ifdef HAVE_FALCON
    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        ssl->options.haveFalconSig  = 1; /* always on client side */
    }
#endif /* HAVE_FALCON */
#ifdef HAVE_DILITHIUM
    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        ssl->options.haveDilithiumSig  = 1; /* always on client side */
    }
#endif /* HAVE_DILITHIUM */

#if defined(HAVE_EXTENDED_MASTER) && !defined(NO_WOLFSSL_CLIENT)
    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        if ((ssl->ctx->method->version.major == SSLv3_MAJOR) &&
             (ssl->ctx->method->version.minor >= TLSv1_MINOR)) {
            ssl->options.haveEMS = 1;
        }
    #ifdef WOLFSSL_DTLS
        if (ssl->ctx->method->version.major == DTLS_MAJOR)
            ssl->options.haveEMS = 1;
    #endif /* WOLFSSL_DTLS */
    }
#endif /* HAVE_EXTENDED_MASTER && !NO_WOLFSSL_CLIENT */

#if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER)
    if (ssl->options.dtls && ssl->options.side == WOLFSSL_SERVER_END) {
        int ret;
        ret = wolfSSL_DTLS_SetCookieSecret(ssl, NULL, 0);
        if (ret != 0) {
            WOLFSSL_MSG("DTLS Cookie Secret error");
            return ret;
        }
    #if defined(WOLFSSL_DTLS13)
        if (IsAtLeastTLSv1_3(ssl->version)) {
        #if defined(WOLFSSL_SEND_HRR_COOKIE)
            ret = wolfSSL_send_hrr_cookie(ssl, NULL, 0);
            if (ret != WOLFSSL_SUCCESS) {
                WOLFSSL_MSG("DTLS1.3 Cookie secret error");
                return ret;
            }
        #endif /* WOLFSSL_SEND_HRR_COOKIE */
        #if defined(WOLFSSL_DTLS_CH_FRAG) && defined(WOLFSSL_HAVE_MLKEM)
            /* Allow fragmentation of the second ClientHello due to the
             * large PQC key share. */
            ret = wolfSSL_dtls13_allow_ch_frag(ssl, 1);
            if (ret != WOLFSSL_SUCCESS) {
                WOLFSSL_MSG("DTLS1.3 CH frag error");
                return ret;
            }
        #endif /* WOLFSSL_DTLS_CH_FRAG && WOLFSSL_HAVE_MLKEM */
        }
    #endif /* WOLFSSL_DTLS13 */
    }
#endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */

    /* Forcefully reinitialize suites here as the side may have changed,
     * unless the user has explicitly set cipher suites.
     * Two separate checks to ensure suites are always allocated, to avoid
     * failing suites == NULL check in InitSSL_Suites. */
    if (ssl->suites && !ssl->suites->setSuites) {
        FreeSuites(ssl);
    }
    if (!ssl->suites) {
        AllocateSuites(ssl);
    }
    return InitSSL_Suites(ssl);
}
#endif /* OPENSSL_EXTRA || WOLFSSL_EITHER_SIDE ||
        * WOLFSSL_EXTRA || WOLFSSL_WPAS_SMALL */

#if defined(WOLFSSL_SYS_CRYPTO_POLICY)
/* Check the wolfssl method meets minimum requirements for
 * the given security level.
 *
 * Returns  0 if method meets security level.
 * Returns  CRYPTO_POLICY_FORBIDDEN otherwise.
 * */
static int wolfSSL_crypto_policy_method_allowed(WOLFSSL_METHOD * method,
                                                int level)
{
    if (level == 0) {
        /* permissive, no restrictions. */
        return 0;
    }

    #ifdef WOLFSSL_DTLS
    if (method->version.major == DTLS_MAJOR) {
        if (method->version.minor == DTLS_MINOR) {
            /* sec level must be 1 or lower. */
            if (level > 1) {
                return CRYPTO_POLICY_FORBIDDEN;
            }
        }
    }
    else
    #endif /* WOLFSSL_DTLS */
    {
        if (method->version.minor == SSLv3_MINOR) {
            /* sec level must be 0. */
            if (level > 0) {
                return CRYPTO_POLICY_FORBIDDEN;
            }
        }
        else if (method->version.minor == TLSv1_MINOR ||
                 method->version.minor == TLSv1_1_MINOR) {
            /* sec level must be 1 or lower. */
            if (level > 1) {
                return CRYPTO_POLICY_FORBIDDEN;
            }
        }
    }

    /* nothing else to check, all other combinations ok. */

    return 0;
}

/* Configure the CTX to conform to the security policy.
 *
 * Also, check the WOLFSSL_METHOD against the supplied security
 * level.
 *
 * Returns  CRYPTO_POLICY_FORBIDDEN if not allowed per policy.
 * Returns  BAD_FUNC_ARG on null args.
 * Returns  0 if ok.
 * */
int wolfSSL_crypto_policy_init_ctx(WOLFSSL_CTX * ctx,
                                   WOLFSSL_METHOD * method)
{
    byte         minDowngrade = 0x00;
    #ifdef WOLFSSL_DTLS
    int          dtls = 0;
    #endif /* WOLFSSL_DTLS */
    int          level = 0;
    #if !defined(NO_DH) || !defined(NO_RSA)
    word16       minKeySz = 0; /* minimum DH or RSA key size */
    #endif /* !NO_DH || !NO_RSA*/
    #ifdef HAVE_ECC
    short        minEccKeySz = 0; /* minimum allowed ECC key size */
    #endif /* HAVE_ECC */


    if (ctx == NULL || method == NULL) {
        return BAD_FUNC_ARG;
    }

    #ifdef WOLFSSL_DTLS
    dtls = (method->version.major == DTLS_MAJOR);
    #endif /* WOLFSSL_DTLS */

    /* get the crypto policy security level. */
    level = wolfSSL_crypto_policy_get_level();

    if (level < 0 || level > 5) {
        WOLFSSL_MSG_EX("crypto_policy_init_ctx: invalid level: %d", level);
        return BAD_FUNC_ARG;
    }

    /* Check requested method per security level. */
    if (wolfSSL_crypto_policy_method_allowed(method, level) != 0) {
        WOLFSSL_MSG_EX("crypto_policy_init_ctx: "
                       "method=%d, SECLEVEL=%d combination not allowed",
                       method->version.minor, level);
        return CRYPTO_POLICY_FORBIDDEN;
    }

    /* Set appropriate min downgrade per security level. */
    #ifdef WOLFSSL_DTLS
    if (dtls) {
        switch (level) {
        case 1:
            minDowngrade = DTLS_MINOR;
            break;
        case 2:
        case 3:
        case 4:
        case 5:
            minDowngrade = DTLSv1_2_MINOR;
            break;
        case 0:
        default:
            /* Permissive, no restrictions. Allow defaults. */
            minDowngrade = WOLFSSL_MIN_DTLS_DOWNGRADE;
            break;
        }
    }
    else
    #endif /* WOLFSSL_DTLS */
    {
        switch (level) {
        case 1:
            /* prohibit SSLv3 and lower. */
            minDowngrade = TLSv1_MINOR;
            break;
        case 2:
        case 3:
        case 4:
        case 5:
            /* prohibit TLSv1_1 and lower. */
            minDowngrade = TLSv1_2_MINOR;
            break;
        case 0:
        default:
            ctx->minDowngrade = WOLFSSL_MIN_DOWNGRADE;
            break;
        }
    }

    /* Set min RSA and DH key size. */
    #if !defined(NO_DH) || !defined(NO_RSA)
    switch (level) {
    case 1:
        minKeySz = 128; /* 1024 bits / 8 */
        break;
    case 2:
        minKeySz = 256; /* 2048 bits / 8 */
        break;
    case 3:
        minKeySz = 384; /* 3072 bits / 8 */
        break;
    case 4:
        minKeySz = 960; /* 7680 bits / 8 */
        break;
    case 5:
        minKeySz = 1920; /* 15360 bits / 8 */
        break;
    case 0:
    default:
        break;
    }
    #endif /* !NO_DH || !NO_RSA*/

    /* Set min ECC key size. */
    #ifdef HAVE_ECC
    switch (level) {
    case 1:
        minEccKeySz = 20; /* 160 bits / 8 */
        break;
    case 2:
        minEccKeySz = 28; /* 224 bits / 8 */
        break;
    case 3:
        minEccKeySz = 32; /* 256 bits / 8 */
        break;
    case 4:
        minEccKeySz = 48; /* 384 bits / 8 */
        break;
    case 5:
        minEccKeySz = 64; /* 512 bits / 8 */
        break;
    default:
    case 0:
        break;
    }
    #endif /* HAVE_ECC */

    /* Finally set the ctx values. */
    ctx->minDowngrade = minDowngrade;
    ctx->secLevel = level;
    ctx->method = method;

    #if !defined(NO_DH) || !defined(NO_RSA)
    if (minKeySz > 0) {
        #ifndef NO_DH
        if (minKeySz > MAX_DHKEY_SZ) {
            WOLFSSL_MSG_EX("crypto_policy_init_ctx: minKeySz=%d, "
                           "but MAX_DHKEY_SZ=%d",
                           minKeySz, MAX_DHKEY_SZ);
            return CRYPTO_POLICY_FORBIDDEN;
        }
        ctx->minDhKeySz  = minKeySz;
        ctx->maxDhKeySz  = MAX_DHKEY_SZ;
        #endif /* NO_DH */
        #ifndef NO_RSA
        ctx->minRsaKeySz = minKeySz;
        #endif /* NO_RSA */
    }
    #endif /* !NO_DH || !NO_RSA*/

    #ifdef HAVE_ECC
    if (minEccKeySz > 0) {
        ctx->minEccKeySz  = minEccKeySz;
    }
    #endif /* HAVE_ECC */

    return 0;
}
#endif /* WOLFSSL_SYS_CRYPTO_POLICY */

/* Initialize SSL context, return 0 on success */
int InitSSL_Ctx(WOLFSSL_CTX* ctx, WOLFSSL_METHOD* method, void* heap)
{
    int ret = 0;

    XMEMSET(ctx, 0, sizeof(WOLFSSL_CTX));

    ctx->method   = method;
    if (heap == NULL) {
        ctx->heap = ctx;  /* defaults to self */
    }
    else {
        ctx->heap = heap; /* wolfSSL_CTX_load_static_memory sets */
    }
    ctx->timeout  = WOLFSSL_SESSION_TIMEOUT;

#ifdef WOLFSSL_DTLS
    if (method->version.major == DTLS_MAJOR) {
        ctx->minDowngrade = WOLFSSL_MIN_DTLS_DOWNGRADE;
    }
    else
#endif /* WOLFSSL_DTLS */
    {
        /* current default: TLSv1_MINOR */
        ctx->minDowngrade = WOLFSSL_MIN_DOWNGRADE;
    }

    wolfSSL_RefWithMutexInit(&ctx->ref, &ret);
#ifdef WOLFSSL_REFCNT_ERROR_RETURN
    if (ret < 0) {
        WOLFSSL_MSG("Mutex error on CTX init");
        ctx->err = CTX_INIT_MUTEX_E;
        WOLFSSL_ERROR_VERBOSE(BAD_MUTEX_E);
        return BAD_MUTEX_E;
    }
#else
    (void)ret;
#endif

#ifndef NO_CERTS
    ctx->privateKeyDevId = INVALID_DEVID;
#ifdef WOLFSSL_DUAL_ALG_CERTS
    ctx->altPrivateKeyDevId = INVALID_DEVID;
#endif
#endif

#ifndef NO_DH
    ctx->minDhKeySz  = MIN_DHKEY_SZ;
    ctx->maxDhKeySz  = MAX_DHKEY_SZ;
#endif
#ifndef NO_RSA
    ctx->minRsaKeySz = MIN_RSAKEY_SZ;
#endif

#ifdef HAVE_ECC
    ctx->minEccKeySz  = MIN_ECCKEY_SZ;
    ctx->eccTempKeySz = ECDHE_SIZE;
#endif
#ifdef HAVE_FALCON
    ctx->minFalconKeySz = MIN_FALCONKEY_SZ;
#endif /* HAVE_FALCON */
#ifdef HAVE_DILITHIUM
    ctx->minDilithiumKeySz = MIN_DILITHIUMKEY_SZ;
#endif /* HAVE_DILITHIUM */
    ctx->verifyDepth = MAX_CHAIN_DEPTH;
#ifdef OPENSSL_EXTRA
    ctx->cbioFlag = WOLFSSL_CBIO_NONE;
#endif

#ifdef HAVE_NETX
    ctx->CBIORecv = NetX_Receive;
    ctx->CBIOSend = NetX_Send;
#elif defined(WOLFSSL_APACHE_MYNEWT) && !defined(WOLFSSL_LWIP)
    ctx->CBIORecv = Mynewt_Receive;
    ctx->CBIOSend = Mynewt_Send;
#elif defined WOLFSSL_LWIP_NATIVE
    ctx->CBIORecv = LwIPNativeReceive;
    ctx->CBIOSend = LwIPNativeSend;
#elif defined(WOLFSSL_GNRC)
    ctx->CBIORecv = GNRC_ReceiveFrom;
    ctx->CBIOSend = GNRC_SendTo;
#elif defined WOLFSSL_ISOTP
    ctx->CBIORecv = ISOTP_Receive;
    ctx->CBIOSend = ISOTP_Send;
#elif !defined(WOLFSSL_USER_IO)
    #ifdef MICRIUM
        ctx->CBIORecv = MicriumReceive;
        ctx->CBIOSend = MicriumSend;
        #ifdef WOLFSSL_DTLS
            if (method->version.major == DTLS_MAJOR) {
                ctx->CBIORecv   = MicriumReceiveFrom;
                ctx->CBIOSend   = MicriumSendTo;
            }
            #ifdef WOLFSSL_SESSION_EXPORT
                #error Micrium port does not support DTLS session export yet
            #endif
        #endif
    #elif defined WOLFSSL_UIP
        ctx->CBIORecv = uIPReceive;
        ctx->CBIOSend = uIPSend;
        #ifdef WOLFSSL_DTLS
        if (method->version.major == DTLS_MAJOR) {
            ctx->CBIOSendTo = uIPSendTo;
            ctx->CBIORecvFrom = uIPRecvFrom;
        }
        #endif
    #else
        ctx->CBIORecv = EmbedReceive;
        ctx->CBIOSend = EmbedSend;
        #ifdef WOLFSSL_SESSION_EXPORT
            ctx->CBGetPeer = EmbedGetPeer;
            ctx->CBSetPeer = EmbedSetPeer;
        #endif
        #ifdef WOLFSSL_DTLS
            if (method->version.major == DTLS_MAJOR) {
                ctx->CBIORecv   = EmbedReceiveFrom;
                ctx->CBIOSend   = EmbedSendTo;
            }
        #endif
    #endif /* MICRIUM */
#endif /* WOLFSSL_USER_IO */

#if defined(HAVE_RPK)
    wolfSSL_CTX_set_client_cert_type(ctx, NULL, 0); /* set to default */
    wolfSSL_CTX_set_server_cert_type(ctx, NULL, 0); /* set to default */
#endif /* HAVE_RPK */

#ifdef HAVE_FALCON
    if (method->side == WOLFSSL_CLIENT_END)
        ctx->haveFalconSig = 1;        /* always on client side */
                                       /* server can turn on by loading key */
#endif /* HAVE_FALCON */
#ifdef HAVE_DILITHIUM
    if (method->side == WOLFSSL_CLIENT_END)
        ctx->haveDilithiumSig = 1;     /* always on client side */
                                       /* server can turn on by loading key */
#endif /* HAVE_DILITHIUM */
#ifdef HAVE_ECC
    if (method->side == WOLFSSL_CLIENT_END) {
        ctx->haveECDSAsig  = 1;        /* always on client side */
        ctx->haveECC  = 1;             /* server turns on with ECC key cert */
        ctx->haveStaticECC = 1;        /* server can turn on by loading key */
    }
#elif defined(HAVE_ED25519) || defined(HAVE_ED448)
    if (method->side == WOLFSSL_CLIENT_END) {
        ctx->haveECDSAsig  = 1;        /* always on client side */
        ctx->haveECC  = 1;             /* server turns on with ECC key cert */
    }
#endif

#ifdef WOLFSSL_QNX_CAAM
    /* default to try using CAAM when built */
    ctx->devId = WOLFSSL_CAAM_DEVID;
#elif defined(HAVE_ARIA) && defined(WOLF_CRYPTO_CB)
    ctx->devId = WOLFSSL_ARIA_DEVID;
#else
    ctx->devId = INVALID_DEVID;
#endif

#if defined(WOLFSSL_DTLS)
    #ifdef WOLFSSL_SCTP
        ctx->dtlsMtuSz = MAX_RECORD_SIZE;
    #elif defined(WOLFSSL_DTLS_MTU)
        ctx->dtlsMtuSz = MAX_MTU;
    #endif
#endif

#ifndef NO_CERTS
    ctx->cm = wolfSSL_CertManagerNew_ex(heap);
    if (ctx->cm == NULL) {
        WOLFSSL_MSG("Bad Cert Manager New");
        WOLFSSL_ERROR_VERBOSE(BAD_CERT_MANAGER_ERROR);
        return BAD_CERT_MANAGER_ERROR;
    }
    #ifdef OPENSSL_EXTRA
    /* setup WOLFSSL_X509_STORE */
    ctx->x509_store.cm = ctx->cm;
    /* set pointer back to x509 store */
    ctx->cm->x509_store_p = &ctx->x509_store;

    /* WOLFSSL_X509_VERIFY_PARAM */
    if ((ctx->param = (WOLFSSL_X509_VERIFY_PARAM*)XMALLOC(
                           sizeof(WOLFSSL_X509_VERIFY_PARAM),
                           heap, DYNAMIC_TYPE_OPENSSL)) == NULL) {
        WOLFSSL_MSG("ctx->param memory error");
        return MEMORY_E;
    }
    XMEMSET(ctx->param, 0, sizeof(WOLFSSL_X509_VERIFY_PARAM));

    /* WOLFSSL_X509_LOOKUP */
    if ((ctx->x509_store.lookup.dirs = (WOLFSSL_BY_DIR*)XMALLOC(
                           sizeof(WOLFSSL_BY_DIR),
                           heap, DYNAMIC_TYPE_OPENSSL)) == NULL) {
        WOLFSSL_MSG("ctx->x509_store.lookup.dirs: allocation error");
        return MEMORY_E;
    }
    XMEMSET(ctx->x509_store.lookup.dirs, 0, sizeof(WOLFSSL_BY_DIR));

    /* param */
    if ((ctx->x509_store.param = (WOLFSSL_X509_VERIFY_PARAM*)XMALLOC(
                           sizeof(WOLFSSL_X509_VERIFY_PARAM),
                           heap, DYNAMIC_TYPE_OPENSSL)) == NULL) {
        WOLFSSL_MSG("ctx->x509_store.param: allocation error");
        return MEMORY_E;
    }
    XMEMSET(ctx->x509_store.param, 0, sizeof(WOLFSSL_X509_VERIFY_PARAM));

    if (wc_InitMutex(&ctx->x509_store.lookup.dirs->lock) != 0) {
        WOLFSSL_MSG("Bad mutex init");
        WOLFSSL_ERROR_VERBOSE(BAD_MUTEX_E);
        return BAD_MUTEX_E;
    }
    #endif
#endif

#if defined(HAVE_EXTENDED_MASTER) && !defined(NO_WOLFSSL_CLIENT)
    if (method->side == WOLFSSL_CLIENT_END) {
        if ((method->version.major == SSLv3_MAJOR) &&
             (method->version.minor >= TLSv1_MINOR)) {

            ctx->haveEMS = 1;
        }
#ifdef WOLFSSL_DTLS
        if (method->version.major == DTLS_MAJOR)
            ctx->haveEMS = 1;
#endif /* WOLFSSL_DTLS */
    }
#endif /* HAVE_EXTENDED_MASTER && !NO_WOLFSSL_CLIENT */

#if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_SERVER)
#if !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_TLS)
    ret = TicketEncCbCtx_Init(ctx, &ctx->ticketKeyCtx);
    if (ret != 0) return ret;
    ctx->ticketEncCb = DefTicketEncCb;
    ctx->ticketEncCtx = (void*)&ctx->ticketKeyCtx;
#endif
    ctx->ticketHint = SESSION_TICKET_HINT_DEFAULT;
#if defined(WOLFSSL_TLS13)
    ctx->maxTicketTls13 = 1; /* default to sending a session ticket if compiled
                                in */
#endif
#endif

#ifdef WOLFSSL_EARLY_DATA
    ctx->maxEarlyDataSz = MAX_EARLY_DATA_SZ;
#endif

#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
#if defined(WOLFSSL_TLS13) && !defined(HAVE_SUPPORTED_CURVES)
    ctx->noPskDheKe = 1;
#endif
#endif

#if defined(WOLFSSL_QT) && !defined(NO_PSK)
    /* Qt retrieves supported cipher list at initialization
     * from get_cipher_compat().
     * Qt doesn't allow to use a cipher if it is not in the supported list.
     * Therefore, we need to enable PSK cipher at the beginning.
     */
    ctx->havePSK = 1;
#endif
    ctx->heap = heap; /* wolfSSL_CTX_load_static_memory sets */

#ifdef HAVE_WOLF_EVENT
    ret = wolfEventQueue_Init(&ctx->event_queue);
#endif /* HAVE_WOLF_EVENT */

#ifdef WOLFSSL_MAXQ10XX_TLS
    /* Let maxq10xx know what TLS version we are using. */
    ctx->devId = MAXQ_DEVICE_ID;
    maxq10xx_SetupPkCallbacks(ctx, &method->version);
#endif /* WOLFSSL_MAXQ10XX_TLS */

#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
    /* Should only be set when wolfSSL_CTX_load_system_CA_certs() is called */
    ctx->doAppleNativeCertValidationFlag = 0;
#endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */

#if defined(WOLFSSL_SYS_CRYPTO_POLICY)
    ret = wolfSSL_crypto_policy_init_ctx(ctx, method);
    if (ret != 0) {
        WOLFSSL_MSG_EX("crypto_policy_init_ctx returned %d", ret);
        return ret;
    }
#endif /* WOLFSSL_SYS_CRYPTO_POLICY */

    return ret;
}


#ifdef HAVE_EX_DATA_CLEANUP_HOOKS
void wolfSSL_CRYPTO_cleanup_ex_data(WOLFSSL_CRYPTO_EX_DATA* ex_data)
{
    int n_ex_data = (int)(sizeof ex_data->ex_data / sizeof ex_data->ex_data[0]);
    for (--n_ex_data; n_ex_data >= 0; --n_ex_data) {
        if (ex_data->ex_data[n_ex_data] != NULL)
            (void)wolfSSL_CRYPTO_set_ex_data_with_cleanup(ex_data, n_ex_data,
                                                          NULL, NULL);
    }
}
#endif /* HAVE_EX_DATA_CLEANUP_HOOKS */

#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
/* free all ech configs in the list */
void FreeEchConfigs(WOLFSSL_EchConfig* configs, void* heap)
{
    WOLFSSL_EchConfig* working_config = configs;
    WOLFSSL_EchConfig* next_config;

    while (working_config != NULL) {
        next_config = working_config->next;

        XFREE(working_config->cipherSuites, heap, DYNAMIC_TYPE_TMP_BUFFER);
        XFREE(working_config->publicName, heap, DYNAMIC_TYPE_TMP_BUFFER);

        XFREE(working_config->raw, heap, DYNAMIC_TYPE_TMP_BUFFER);

        if (working_config->receiverPrivkey != NULL) {
            wc_HpkeFreeKey(NULL, working_config->kemId,
                working_config->receiverPrivkey, heap);
        }

        XFREE(working_config, heap, DYNAMIC_TYPE_TMP_BUFFER);

        working_config = next_config;
    }

    (void)heap;
}
#endif /* WOLFSSL_TLS13 && HAVE_ECH */

/* In case contexts are held in array and don't want to free actual ctx. */

/* The allocations done in InitSSL_Ctx must be free'd with ctx->onHeapHint
 * logic. A WOLFSSL_CTX can be assigned a static memory heap hint using
 * wolfSSL_CTX_load_static_memory after CTX creation, which means variables
 * allocated in InitSSL_Ctx were allocated from heap and should be free'd with
 * a NULL heap hint. */
void SSL_CtxResourceFree(WOLFSSL_CTX* ctx)
{
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && \
                     defined(HAVE_TLS_EXTENSIONS) && !defined(NO_WOLFSSL_SERVER)
    int i;
#endif
    void* heapAtCTXInit = ctx->heap;
#ifdef WOLFSSL_STATIC_MEMORY
    if (ctx->onHeapHint == 0) {
        heapAtCTXInit = NULL;
    }
#endif

#ifdef HAVE_EX_DATA_CLEANUP_HOOKS
    wolfSSL_CRYPTO_cleanup_ex_data(&ctx->ex_data);
#endif

#ifdef HAVE_WOLF_EVENT
    wolfEventQueue_Free(&ctx->event_queue);
#endif /* HAVE_WOLF_EVENT */

#ifndef NO_TLS /* its a static global see ssl.c "gNoTlsMethod" */
    XFREE(ctx->method, heapAtCTXInit, DYNAMIC_TYPE_METHOD);
#endif
    ctx->method = NULL;

    XFREE(ctx->suites, ctx->heap, DYNAMIC_TYPE_SUITES);
    ctx->suites = NULL;

#ifndef NO_DH
    XFREE(ctx->serverDH_G.buffer, ctx->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    ctx->serverDH_G.buffer = NULL;
    XFREE(ctx->serverDH_P.buffer, ctx->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    ctx->serverDH_P.buffer = NULL;
#endif /* !NO_DH */

#ifdef SINGLE_THREADED
    if (ctx->rng) {
        wc_FreeRng(ctx->rng);
        XFREE(ctx->rng, ctx->heap, DYNAMIC_TYPE_RNG);
        ctx->rng = NULL;
    }
#endif /* SINGLE_THREADED */

#ifndef NO_CERTS
    if (ctx->privateKey != NULL && ctx->privateKey->buffer != NULL) {
        ForceZero(ctx->privateKey->buffer, ctx->privateKey->length);
    }
    FreeDer(&ctx->privateKey);
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
    FreeDer(&ctx->privateKeyMask);
#endif
#ifdef WOLFSSL_DUAL_ALG_CERTS
    if (ctx->altPrivateKey != NULL && ctx->altPrivateKey->buffer != NULL) {
        ForceZero(ctx->altPrivateKey->buffer, ctx->altPrivateKey->length);
    }
    FreeDer(&ctx->altPrivateKey);
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
    FreeDer(&ctx->altPrivateKeyMask);
#endif
#endif /* WOLFSSL_DUAL_ALG_CERTS */
#ifdef OPENSSL_ALL
    wolfSSL_EVP_PKEY_free(ctx->privateKeyPKey);
#endif
    FreeDer(&ctx->certificate);
    #ifdef KEEP_OUR_CERT
        if (ctx->ourCert && ctx->ownOurCert) {
            wolfSSL_X509_free(ctx->ourCert);
            ctx->ourCert = NULL;
        }
    #endif /* KEEP_OUR_CERT */
    FreeDer(&ctx->certChain);
    wolfSSL_CertManagerFree(ctx->cm);
    ctx->cm = NULL;
    #ifdef OPENSSL_ALL
        if (ctx->x509_store.objs != NULL) {
            wolfSSL_sk_X509_OBJECT_pop_free(ctx->x509_store.objs, NULL);
            ctx->x509_store.objs = NULL;
        }
    #endif
    #if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || \
        defined(WOLFSSL_WPAS_SMALL)
        wolfSSL_X509_STORE_free(ctx->x509_store_pt);
    #endif
    #ifndef WOLFSSL_NO_CA_NAMES
        wolfSSL_sk_X509_NAME_pop_free(ctx->client_ca_names, NULL);
        ctx->client_ca_names = NULL;
        wolfSSL_sk_X509_NAME_pop_free(ctx->ca_names, NULL);
        ctx->ca_names = NULL;
    #endif
    #ifdef OPENSSL_EXTRA
        if (ctx->x509Chain) {
            wolfSSL_sk_X509_pop_free(ctx->x509Chain, NULL);
            ctx->x509Chain = NULL;
        }
    #endif
    #ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
        if (ctx->testTrustedCAs != NULL) {
            CFRelease(ctx->testTrustedCAs);
            ctx->testTrustedCAs = NULL;
        }
    #endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */
#endif /* !NO_CERTS */

#ifdef HAVE_TLS_EXTENSIONS
#if !defined(NO_TLS)
    TLSX_FreeAll(ctx->extensions, ctx->heap);
#endif /* !NO_TLS */
#ifndef NO_WOLFSSL_SERVER
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) \
 || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    if (ctx->certOcspRequest) {
        FreeOcspRequest(ctx->certOcspRequest);
        XFREE(ctx->certOcspRequest, ctx->heap, DYNAMIC_TYPE_OCSP_REQUEST);
    }
#endif

#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
    for (i = 0; i < MAX_CHAIN_DEPTH; i++) {
        if (ctx->chainOcspRequest[i]) {
            FreeOcspRequest(ctx->chainOcspRequest[i]);
            XFREE(ctx->chainOcspRequest[i], ctx->heap, DYNAMIC_TYPE_OCSP_REQUEST);
            ctx->chainOcspRequest[i] = NULL;
        }
    }
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
#endif /* !NO_WOLFSSL_SERVER */

#endif /* HAVE_TLS_EXTENSIONS */
#ifdef OPENSSL_EXTRA
    if (ctx->alpn_cli_protos) {
        XFREE((void*)ctx->alpn_cli_protos, ctx->heap, DYNAMIC_TYPE_OPENSSL);
        ctx->alpn_cli_protos = NULL;
    }
    XFREE(ctx->param, heapAtCTXInit, DYNAMIC_TYPE_OPENSSL);
    ctx->param = NULL;

    if (ctx->x509_store.param) {
        XFREE(ctx->x509_store.param, heapAtCTXInit, DYNAMIC_TYPE_OPENSSL);
        ctx->x509_store.param = NULL;
    }

    if (ctx->x509_store.lookup.dirs) {
#if defined(OPENSSL_ALL) && !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR)
        if (ctx->x509_store.lookup.dirs->dir_entry) {
            wolfSSL_sk_BY_DIR_entry_free(ctx->x509_store.lookup.dirs->dir_entry);
        }

#endif
        wc_FreeMutex(&ctx->x509_store.lookup.dirs->lock);
        XFREE(ctx->x509_store.lookup.dirs, heapAtCTXInit, DYNAMIC_TYPE_OPENSSL);
    }
#endif
#ifdef WOLFSSL_STATIC_EPHEMERAL
    #ifndef NO_DH
    FreeDer(&ctx->staticKE.dhKey);
    #endif
    #ifdef HAVE_ECC
    FreeDer(&ctx->staticKE.ecKey);
    #endif
    #ifdef HAVE_CURVE25519
    FreeDer(&ctx->staticKE.x25519Key);
    #endif
    #ifdef HAVE_CURVE448
    FreeDer(&ctx->staticKE.x448Key);
    #endif
    #ifndef SINGLE_THREADED
    if (ctx->staticKELockInit) {
        wc_FreeMutex(&ctx->staticKELock);
        ctx->staticKELockInit = 0;
    }
    #endif
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
    FreeEchConfigs(ctx->echConfigs, ctx->heap);
    ctx->echConfigs = NULL;
#endif
    (void)heapAtCTXInit;
}


void FreeSSL_Ctx(WOLFSSL_CTX* ctx)
{
    int isZero;
    int ret;
    void* heap = ctx->heap;
#ifdef WOLFSSL_STATIC_MEMORY
    if (ctx->onHeapHint == 0) {
        heap = NULL;
    }
#endif

    /* decrement CTX reference count */
    wolfSSL_RefWithMutexDec(&ctx->ref, &isZero, &ret);
#ifdef WOLFSSL_REFCNT_ERROR_RETURN
    if (ret < 0) {
        /* check error state, if mutex error code then mutex init failed but
         * CTX was still malloc'd */
        if (ctx->err == WC_NO_ERR_TRACE(CTX_INIT_MUTEX_E)) {
            SSL_CtxResourceFree(ctx);
            XFREE(ctx, heap, DYNAMIC_TYPE_CTX);
        }
        return;
    }
#else
    (void)ret;
#endif

    if (isZero) {
        WOLFSSL_MSG("CTX ref count down to 0, doing full free");
#if defined(OPENSSL_EXTRA) && defined(WOLFCRYPT_HAVE_SRP) && \
    !defined(NO_SHA256) && !defined(WC_NO_RNG)
        if (ctx->srp != NULL) {
            XFREE(ctx->srp_password, ctx->heap, DYNAMIC_TYPE_SRP);
            ctx->srp_password = NULL;
            wc_SrpTerm(ctx->srp);
            XFREE(ctx->srp, ctx->heap, DYNAMIC_TYPE_SRP);
            ctx->srp = NULL;
        }
#endif
        SSL_CtxResourceFree(ctx);
#if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_SERVER) && \
    !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_TLS)
        TicketEncCbCtx_Free(&ctx->ticketKeyCtx);
#endif
        wolfSSL_RefWithMutexFree(&ctx->ref);
        XFREE(ctx, heap, DYNAMIC_TYPE_CTX);
    }
    else {
        WOLFSSL_MSG("CTX ref count not 0 yet, no free");
    }
    (void)heap; /* not used in some builds */
}


/* Set cipher pointers to null */
void InitCiphers(WOLFSSL* ssl)
{
#ifdef BUILD_ARC4
    ssl->encrypt.arc4 = NULL;
    ssl->decrypt.arc4 = NULL;
#endif
#ifdef BUILD_DES3
    ssl->encrypt.des3 = NULL;
    ssl->decrypt.des3 = NULL;
#endif
#ifdef BUILD_AES
    ssl->encrypt.aes = NULL;
    ssl->decrypt.aes = NULL;
#endif
#ifdef HAVE_ARIA
    ssl->encrypt.aria = NULL;
    ssl->decrypt.aria = NULL;
#endif
#ifdef HAVE_CAMELLIA
    ssl->encrypt.cam = NULL;
    ssl->decrypt.cam = NULL;
#endif
#ifdef HAVE_CHACHA
    ssl->encrypt.chacha = NULL;
    ssl->decrypt.chacha = NULL;
#endif
#if defined(HAVE_POLY1305) && defined(HAVE_ONE_TIME_AUTH)
    ssl->auth.poly1305 = NULL;
#endif
    ssl->encrypt.setup = 0;
    ssl->decrypt.setup = 0;
#ifdef HAVE_ONE_TIME_AUTH
    ssl->auth.setup    = 0;
#endif

#ifdef WOLFSSL_DTLS13
    XMEMSET(&ssl->dtlsRecordNumberEncrypt, 0,
        sizeof(ssl->dtlsRecordNumberEncrypt));
    XMEMSET(&ssl->dtlsRecordNumberDecrypt, 0,
         sizeof(ssl->dtlsRecordNumberDecrypt));
#endif /* WOLFSSL_DTLS13 */

}

static void FreeCiphersSide(Ciphers *cipher, void* heap)
{
#ifdef BUILD_ARC4
    wc_Arc4Free(cipher->arc4);
    XFREE(cipher->arc4, heap, DYNAMIC_TYPE_CIPHER);
    cipher->arc4 = NULL;
#endif
#ifdef BUILD_DES3
    wc_Des3Free(cipher->des3);
    XFREE(cipher->des3, heap, DYNAMIC_TYPE_CIPHER);
    cipher->des3 = NULL;
#endif
#if defined(BUILD_AES) || defined(BUILD_AESGCM) || defined(HAVE_ARIA)
    /* See: InitKeys() in keys.c on addition of BUILD_AESGCM check (enc->aes,
     * dec->aes) */
    wc_AesFree(cipher->aes);
    XFREE(cipher->aes, heap, DYNAMIC_TYPE_CIPHER);
    cipher->aes = NULL;
#endif
#if defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)
    wc_Sm4Free(cipher->sm4);
    XFREE(cipher->sm4, heap, DYNAMIC_TYPE_CIPHER);
    cipher->sm4 = NULL;
#endif
#if (defined(BUILD_AESGCM) || defined(BUILD_AESCCM) || defined(HAVE_ARIA)) && \
    !defined(WOLFSSL_NO_TLS12)
    XFREE(cipher->additional, heap, DYNAMIC_TYPE_CIPHER);
    cipher->additional = NULL;
#endif
#ifdef CIPHER_NONCE
    XFREE(cipher->nonce, heap, DYNAMIC_TYPE_CIPHER);
    cipher->nonce = NULL;
#endif
#ifdef HAVE_ARIA
    wc_AriaFreeCrypt(cipher->aria);
    XFREE(cipher->aria, heap, DYNAMIC_TYPE_CIPHER);
    cipher->aria = NULL;
#endif
#ifdef HAVE_CAMELLIA
    XFREE(cipher->cam, heap, DYNAMIC_TYPE_CIPHER);
    cipher->cam = NULL;
#endif
#ifdef HAVE_CHACHA
    if (cipher->chacha)
        ForceZero(cipher->chacha, sizeof(ChaCha));
    XFREE(cipher->chacha, heap, DYNAMIC_TYPE_CIPHER);
    cipher->chacha = NULL;
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_NULL_CIPHER)
    wc_HmacFree(cipher->hmac);
    XFREE(cipher->hmac, heap, DYNAMIC_TYPE_CIPHER);
    cipher->hmac = NULL;
#endif

    (void)cipher;
    (void)heap;
}

/* Free ciphers */
void FreeCiphers(WOLFSSL* ssl)
{
    FreeCiphersSide(&ssl->encrypt, ssl->heap);
    FreeCiphersSide(&ssl->decrypt, ssl->heap);

#if defined(HAVE_POLY1305) && defined(HAVE_ONE_TIME_AUTH)
    if (ssl->auth.poly1305)
        ForceZero(ssl->auth.poly1305, sizeof(Poly1305));
    XFREE(ssl->auth.poly1305, ssl->heap, DYNAMIC_TYPE_CIPHER);
    ssl->auth.poly1305 = NULL;
#endif

#ifdef WOLFSSL_DTLS13
#ifdef BUILD_AES
    wc_AesFree(ssl->dtlsRecordNumberEncrypt.aes);
    wc_AesFree(ssl->dtlsRecordNumberDecrypt.aes);
    XFREE(ssl->dtlsRecordNumberEncrypt.aes, ssl->heap, DYNAMIC_TYPE_CIPHER);
    XFREE(ssl->dtlsRecordNumberDecrypt.aes, ssl->heap, DYNAMIC_TYPE_CIPHER);
    ssl->dtlsRecordNumberEncrypt.aes = NULL;
    ssl->dtlsRecordNumberDecrypt.aes = NULL;
#endif /* BUILD_AES */
#ifdef HAVE_CHACHA
    XFREE(ssl->dtlsRecordNumberEncrypt.chacha, ssl->heap, DYNAMIC_TYPE_CIPHER);
    XFREE(ssl->dtlsRecordNumberDecrypt.chacha, ssl->heap, DYNAMIC_TYPE_CIPHER);
    ssl->dtlsRecordNumberEncrypt.chacha = NULL;
    ssl->dtlsRecordNumberDecrypt.chacha = NULL;
#endif /* HAVE_CHACHA */
#endif /* WOLFSSL_DTLS13 */
}

void InitCipherSpecs(CipherSpecs* cs)
{
    XMEMSET(cs, 0, sizeof(CipherSpecs));

    cs->bulk_cipher_algorithm = INVALID_BYTE;
    cs->cipher_type           = INVALID_BYTE;
    cs->mac_algorithm         = INVALID_BYTE;
    cs->kea                   = INVALID_BYTE;
    cs->sig_algo              = INVALID_BYTE;
}

#if defined(USE_ECDSA_KEYSZ_HASH_ALGO) || (defined(WOLFSSL_TLS13) && \
                                                              defined(HAVE_ECC))
static int GetMacDigestSize(byte macAlgo)
{
    switch (macAlgo) {
    #ifndef NO_SHA
        case sha_mac:
            return WC_SHA_DIGEST_SIZE;
    #endif
    #ifndef NO_SHA256
        case sha256_mac:
            return WC_SHA256_DIGEST_SIZE;
    #endif
    #ifdef WOLFSSL_SHA384
        case sha384_mac:
            return WC_SHA384_DIGEST_SIZE;
    #endif
    #ifdef WOLFSSL_SHA512
        case sha512_mac:
            return WC_SHA512_DIGEST_SIZE;
    #endif
    #ifdef WOLFSSL_SM3
        case sm3_mac:
            return WC_SM3_DIGEST_SIZE;
    #endif
        default:
            break;
    }
    return NOT_COMPILED_IN;
}
#endif /* USE_ECDSA_KEYSZ_HASH_ALGO || (WOLFSSL_TLS13 && HAVE_ECC) */

#define ADD_HASH_SIG_ALGO(out, inOutIdx, major, minor)  \
    do {                                                \
        if ((out) != NULL) {                            \
            (out)[*(inOutIdx)    ] = (major);           \
            (out)[*(inOutIdx) + 1] = (minor);           \
        }                                               \
        *(inOutIdx) += 2;                               \
    } while (0)

static WC_INLINE void AddSuiteHashSigAlgo(byte* hashSigAlgo, byte macAlgo,
    byte sigAlgo, int keySz, word16* inOutIdx)
{
    int addSigAlgo = 1;

#ifdef USE_ECDSA_KEYSZ_HASH_ALGO
    if (sigAlgo == ecc_dsa_sa_algo) {
        int digestSz = GetMacDigestSize(macAlgo);
        /* do not add sig/algos with digest size larger than key size */
        if (digestSz <= 0 || (keySz > 0 && digestSz > keySz)) {
            addSigAlgo = 0;
        }
    }
#else
    (void)keySz;
#endif /* USE_ECDSA_KEYSZ_HASH_ALGO */

    if (addSigAlgo) {
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        if (sigAlgo == sm2_sa_algo) {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
                SM2_SA_MAJOR, SM2_SA_MINOR);
        }
        else
    #endif
    #ifdef HAVE_ED25519
        if (sigAlgo == ed25519_sa_algo) {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
                ED25519_SA_MAJOR, ED25519_SA_MINOR);
        }
        else
    #endif
    #ifdef HAVE_ED448
        if (sigAlgo == ed448_sa_algo) {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
                ED448_SA_MAJOR, ED448_SA_MINOR);
        }
        else
    #endif
    #ifdef HAVE_FALCON
        if (sigAlgo == falcon_level1_sa_algo) {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
                FALCON_LEVEL1_SA_MAJOR, FALCON_LEVEL1_SA_MINOR);
        }
        else
        if (sigAlgo == falcon_level5_sa_algo) {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
                FALCON_LEVEL5_SA_MAJOR, FALCON_LEVEL5_SA_MINOR);
        }
        else
    #endif /* HAVE_FALCON */
    #ifdef HAVE_DILITHIUM
        if (sigAlgo == dilithium_level2_sa_algo) {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
                DILITHIUM_LEVEL2_SA_MAJOR, DILITHIUM_LEVEL2_SA_MINOR);
        }
        else
        if (sigAlgo == dilithium_level3_sa_algo) {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
                DILITHIUM_LEVEL3_SA_MAJOR, DILITHIUM_LEVEL3_SA_MINOR);
        }
        else
        if (sigAlgo == dilithium_level5_sa_algo) {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx,
                DILITHIUM_LEVEL5_SA_MAJOR, DILITHIUM_LEVEL5_SA_MINOR);
        }
        else
    #endif /* HAVE_DILITHIUM */
#ifdef WC_RSA_PSS
        if (sigAlgo == rsa_pss_sa_algo) {
            /* RSA PSS is sig then mac */
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, sigAlgo, macAlgo);
    #ifdef WOLFSSL_TLS13
            /* Add the certificate algorithm as well */
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, sigAlgo,
                PSS_RSAE_TO_PSS_PSS(macAlgo));
    #endif
        }
        else
#endif
#ifdef HAVE_ECC_BRAINPOOL
        if (sigAlgo == ecc_brainpool_sa_algo) {
            if (macAlgo == sha512_mac) {
                ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, NEW_SA_MAJOR,
                    ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR);
            }
            else if (macAlgo == sha384_mac) {
                ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, NEW_SA_MAJOR,
                    ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR);
            }
            else if (macAlgo == sha256_mac) {
                ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, NEW_SA_MAJOR,
                    ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR);
            }
        }
        else
#endif
        {
            ADD_HASH_SIG_ALGO(hashSigAlgo, inOutIdx, macAlgo, sigAlgo);
        }
    }
}

void InitSuitesHashSigAlgo(byte* hashSigAlgo, int haveSig, int tls1_2,
    int tls1_3, int keySz, word16* len)
{
    word16 idx = 0;

    (void)tls1_2;
    (void)tls1_3;
    (void)keySz;

#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448)
    if (haveSig & SIG_ECDSA) {
#ifdef HAVE_ECC
    #ifdef WOLFSSL_SHA512
        AddSuiteHashSigAlgo(hashSigAlgo, sha512_mac, ecc_dsa_sa_algo, keySz,
            &idx);
    #ifdef HAVE_ECC_BRAINPOOL
        if (tls1_3) {
            AddSuiteHashSigAlgo(hashSigAlgo, sha512_mac, ecc_brainpool_sa_algo,
                keySz, &idx);
        }
    #endif
    #endif
    #ifdef WOLFSSL_SHA384
        AddSuiteHashSigAlgo(hashSigAlgo, sha384_mac, ecc_dsa_sa_algo, keySz,
            &idx);
    #ifdef HAVE_ECC_BRAINPOOL
        if (tls1_3) {
            AddSuiteHashSigAlgo(hashSigAlgo, sha384_mac, ecc_brainpool_sa_algo,
                keySz, &idx);
        }
    #endif
    #endif
    #ifndef NO_SHA256
        AddSuiteHashSigAlgo(hashSigAlgo, sha256_mac, ecc_dsa_sa_algo, keySz,
            &idx);
    #ifdef HAVE_ECC_BRAINPOOL
        if (tls1_3) {
            AddSuiteHashSigAlgo(hashSigAlgo, sha256_mac, ecc_brainpool_sa_algo,
                keySz, &idx);
        }
    #endif
    #endif
    #if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                                            defined(WOLFSSL_ALLOW_TLS_SHA1))
        AddSuiteHashSigAlgo(hashSigAlgo, sha_mac, ecc_dsa_sa_algo, keySz, &idx);
    #endif
#endif
    #ifdef HAVE_ED25519
        AddSuiteHashSigAlgo(hashSigAlgo, no_mac, ed25519_sa_algo, keySz, &idx);
    #endif
    #ifdef HAVE_ED448
        AddSuiteHashSigAlgo(hashSigAlgo, no_mac, ed448_sa_algo, keySz, &idx);
    #endif
    }
#endif /* HAVE_ECC || HAVE_ED25519 || HAVE_ED448 */
#if defined(HAVE_ECC) && defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
    if (haveSig & SIG_SM2) {
        AddSuiteHashSigAlgo(hashSigAlgo, sm3_mac, sm2_sa_algo, keySz,
            &idx);
    }
#endif
#ifdef HAVE_FALCON
    if (haveSig & SIG_FALCON) {
        AddSuiteHashSigAlgo(hashSigAlgo, no_mac, falcon_level1_sa_algo, keySz,
            &idx);
        AddSuiteHashSigAlgo(hashSigAlgo, no_mac, falcon_level5_sa_algo, keySz,
            &idx);
    }
#endif /* HAVE_FALCON */
#ifdef HAVE_DILITHIUM
    if (haveSig & SIG_DILITHIUM) {
        AddSuiteHashSigAlgo(hashSigAlgo, no_mac, dilithium_level2_sa_algo,
            keySz, &idx);
        AddSuiteHashSigAlgo(hashSigAlgo, no_mac, dilithium_level3_sa_algo,
            keySz, &idx);
        AddSuiteHashSigAlgo(hashSigAlgo, no_mac, dilithium_level5_sa_algo,
            keySz, &idx);
    }
#endif /* HAVE_DILITHIUM */
    if (haveSig & SIG_RSA) {
    #ifdef WC_RSA_PSS
        if (tls1_2) {
        #ifdef WOLFSSL_SHA512
            AddSuiteHashSigAlgo(hashSigAlgo, sha512_mac, rsa_pss_sa_algo, keySz,
                &idx);
        #endif
        #ifdef WOLFSSL_SHA384
            AddSuiteHashSigAlgo(hashSigAlgo, sha384_mac, rsa_pss_sa_algo, keySz,
                &idx);
        #endif
        #ifndef NO_SHA256
            AddSuiteHashSigAlgo(hashSigAlgo, sha256_mac, rsa_pss_sa_algo, keySz,
                &idx);
        #endif
        }
    #endif
    #ifdef WOLFSSL_SHA512
        AddSuiteHashSigAlgo(hashSigAlgo, sha512_mac, rsa_sa_algo, keySz, &idx);
    #endif
    #ifdef WOLFSSL_SHA384
        AddSuiteHashSigAlgo(hashSigAlgo, sha384_mac, rsa_sa_algo, keySz, &idx);
    #endif
    #ifndef NO_SHA256
        AddSuiteHashSigAlgo(hashSigAlgo, sha256_mac, rsa_sa_algo, keySz, &idx);
    #endif
    #ifdef WOLFSSL_SHA224
        AddSuiteHashSigAlgo(hashSigAlgo, sha224_mac, rsa_sa_algo, keySz, &idx);
    #endif
    #if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                                            defined(WOLFSSL_ALLOW_TLS_SHA1))
        AddSuiteHashSigAlgo(hashSigAlgo, sha_mac, rsa_sa_algo, keySz, &idx);
    #endif
    }

#ifdef HAVE_ANON
    if (haveSig & SIG_ANON) {
        AddSuiteHashSigAlgo(hashSigAlgo, sha_mac, anonymous_sa_algo, keySz,
            &idx);
    }
#endif

    *len = idx;
}

int AllocateCtxSuites(WOLFSSL_CTX* ctx)
{
    if (ctx->suites == NULL) {
        ctx->suites = (Suites*)XMALLOC(sizeof(Suites), ctx->heap,
                                       DYNAMIC_TYPE_SUITES);
        if (ctx->suites == NULL) {
            WOLFSSL_MSG("Memory alloc for Suites failed");
            return MEMORY_ERROR;
        }
        XMEMSET(ctx->suites, 0, sizeof(Suites));
    }
    return 0;
}

/* Call this when the ssl object needs to have its own ssl->suites object */
int AllocateSuites(WOLFSSL* ssl)
{
    if (ssl->suites == NULL) {
        ssl->suites = (Suites*)XMALLOC(sizeof(Suites), ssl->heap,
                                       DYNAMIC_TYPE_SUITES);
        if (ssl->suites == NULL) {
            WOLFSSL_MSG("Suites Memory error");
            return MEMORY_ERROR;
        }
        if (ssl->ctx != NULL && ssl->ctx->suites != NULL)
            XMEMCPY(ssl->suites, ssl->ctx->suites, sizeof(Suites));
        else
            XMEMSET(ssl->suites, 0, sizeof(Suites));
    }
    return 0;
}

void InitSuites(Suites* suites, ProtocolVersion pv, int keySz, word16 haveRSA,
                word16 havePSK, word16 haveDH, word16 haveECDSAsig,
                word16 haveECC, word16 haveStaticRSA, word16 haveStaticECC,
                word16 haveAnon, word16 haveNull, word16 haveAES128,
                word16 haveSHA1, word16 haveRC4, int side)
{
    word16 idx = 0;
    int    tls    = pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_MINOR;
    int    tls1_2 = pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_2_MINOR;
    int    tls1_3 = IsAtLeastTLSv1_3(pv);
    int    dtls   = 0;
    int    haveRSAsig = 1;

#ifdef WOLFSSL_DTLS
    if (pv.major == DTLS_MAJOR) {
        dtls   = 1;
        tls    = 1;
        /* May be dead assignments dependent upon configuration */
        (void) dtls;
        (void) tls;
        tls1_2 = pv.minor <= DTLSv1_2_MINOR;
    }
#endif /* WOLFSSL_DTLS */

    (void)tls;  /* shut up compiler */
    (void)tls1_2;
    (void)tls1_3;
    (void)dtls;
    (void)haveDH;
    (void)havePSK;
    (void)haveStaticRSA;
    (void)haveStaticECC;
    (void)haveECC;
    (void)haveECDSAsig;
    (void)side;
    (void)haveRSA;    /* some builds won't read */
    (void)haveRSAsig; /* non ecc builds won't read */
    (void)haveAnon;   /* anon ciphers optional */
    (void)haveNull;
    (void)haveAES128;
    (void)haveSHA1;
    (void)haveRC4;

    if (suites == NULL) {
        WOLFSSL_MSG("InitSuites pointer error");
        return;
    }

    if (suites->setSuites)
        return;      /* trust user settings, don't override */

#ifdef WOLFSSL_TLS13
#ifdef BUILD_TLS_AES_256_GCM_SHA384
    if (tls1_3) {
        suites->suites[idx++] = TLS13_BYTE;
        suites->suites[idx++] = TLS_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_AES_128_GCM_SHA256
    if (tls1_3 && haveAES128) {
        suites->suites[idx++] = TLS13_BYTE;
        suites->suites[idx++] = TLS_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_CHACHA20_POLY1305_SHA256
    if (tls1_3) {
        suites->suites[idx++] = TLS13_BYTE;
        suites->suites[idx++] = TLS_CHACHA20_POLY1305_SHA256;
    }
#endif

#ifdef BUILD_TLS_AES_128_CCM_SHA256
    if (tls1_3 && haveAES128) {
        suites->suites[idx++] = TLS13_BYTE;
        suites->suites[idx++] = TLS_AES_128_CCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_AES_128_CCM_8_SHA256
    if (tls1_3 && haveAES128) {
        suites->suites[idx++] = TLS13_BYTE;
        suites->suites[idx++] = TLS_AES_128_CCM_8_SHA256;
    }
#endif

#ifdef BUILD_TLS_SM4_GCM_SM3
    if (tls1_3) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_SM4_GCM_SM3;
    }
#endif

#ifdef BUILD_TLS_SM4_CCM_SM3
    if (tls1_3) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_SM4_CCM_SM3;
    }
#endif

#ifdef HAVE_NULL_CIPHER
    #ifdef BUILD_TLS_SHA256_SHA256
        if (tls1_3 && haveNull) {
            suites->suites[idx++] = ECC_BYTE;
            suites->suites[idx++] = TLS_SHA256_SHA256;
        }
    #endif

    #ifdef BUILD_TLS_SHA384_SHA384
        if (tls1_3 && haveNull) {
            suites->suites[idx++] = ECC_BYTE;
            suites->suites[idx++] = TLS_SHA384_SHA384;
        }
    #endif
#endif
#endif /* WOLFSSL_TLS13 */

#ifndef WOLFSSL_NO_TLS12

#if !defined(NO_WOLFSSL_SERVER) && !defined(NO_RSA)
    if (side == WOLFSSL_SERVER_END && haveStaticECC) {
        haveRSA = 0;   /* can't do RSA with ECDSA key */
    }

    if (side == WOLFSSL_SERVER_END && haveECDSAsig) {
        haveRSAsig = 0;     /* can't have RSA sig if signed by ECDSA */
    }
#endif /* !NO_WOLFSSL_SERVER */

#ifdef NO_RSA
    haveRSAsig = 0;    /* can't have RSA sig if don't have RSA */
#endif

#ifdef HAVE_RENEGOTIATION_INDICATION
    if (side == WOLFSSL_CLIENT_END) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_EMPTY_RENEGOTIATION_INFO_SCSV;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    if (tls1_2 && haveECC) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    if (tls1_2 && haveECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    /* OpenSSL enables ECDHE when using ECDHE aliases without RSA */
    #ifdef OPENSSL_EXTRA
    if ((tls1_2 && haveRSA) || (tls1_2 && haveECDSAsig)) {
    #else
    if (tls1_2 && haveRSA) {
    #endif
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    #ifdef OPENSSL_EXTRA
    if ((tls1_2 && haveRSA && haveAES128) ||
        (tls1_2 && haveECDSAsig && haveAES128)) {
    #else
    if (tls1_2 && haveRSA && haveAES128) {
    #endif
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
    if (tls1_2 && haveDH && haveRSA) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
    if (tls1_2 && haveDH && haveRSA && haveAES128) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_256_GCM_SHA384
    if (tls1_2 && haveRSA && haveStaticRSA) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_128_GCM_SHA256
    if (tls1_2 && haveRSA && haveStaticRSA && haveAES128) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
    if (tls1_2 && haveECC && haveStaticECC) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
    if (tls1_2 && haveECC && haveStaticECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
    if (tls1_2 && haveRSAsig && haveStaticECC) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
    if (tls1_2 && haveRSAsig && haveStaticECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384
    if (tls1_2 && haveECC) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256
    if (tls1_2 && haveECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384
    if (tls1_2 && haveDH && havePSK) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_DH_anon_WITH_AES_128_CBC_SHA
    if (tls1_2 && haveDH && haveAnon && haveAES128 && haveSHA1) {
      suites->suites[idx++] = CIPHER_BYTE;
      suites->suites[idx++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_DH_anon_WITH_AES_256_GCM_SHA384
    if (tls1_2 && haveDH && haveAnon) {
      suites->suites[idx++] = CIPHER_BYTE;
      suites->suites[idx++] = TLS_DH_anon_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256
    if (tls1_2 && haveDH && havePSK && haveAES128) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_256_GCM_SHA384
    if (tls1_2 && havePSK) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_256_GCM_SHA384;
    }
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_128_GCM_SHA256
    if (tls1_2 && havePSK && haveAES128) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_128_GCM_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
    if (tls1_2 && haveECC) {
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
    #ifdef OPENSSL_EXTRA
    if ((tls1_2 && haveRSA) || (tls1_2 && haveECDSAsig)) {
    #else
    if (tls1_2 && haveRSA) {
    #endif
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
    }
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
    if (tls1_2 && haveRSA) {
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
    }
#endif

/* Place as higher priority for MYSQL */
#if defined(WOLFSSL_MYSQL_COMPATIBLE)
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
    if (tls && haveDH && haveRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
    }
#endif
#endif /* WOLFSSL_MYSQL_COMPATIBLE */

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
    #ifdef OPENSSL_EXTRA
    if ((tls1_2 && haveRSA && haveAES128) ||
        (tls1_2 && haveECDSAsig && haveAES128)) {
    #else
    if (tls1_2 && haveRSA && haveAES128) {
    #endif
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
    if (tls1_2 && haveECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
    if (tls1_2 && haveRSAsig && haveStaticECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
    if (tls1_2 && haveECC && haveStaticECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
    #ifdef OPENSSL_EXTRA
    if ((tls1_2 && haveRSA) || (tls1_2 && haveECDSAsig)) {
    #else
    if (tls1_2 && haveRSA) {
    #endif
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
    if (tls1_2 && haveECC) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
    if (tls1_2 && haveRSAsig && haveStaticECC) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
    if (tls1_2 && haveECC && haveStaticECC) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
    if (tls && haveECC && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
    if (tls && haveECC && haveStaticECC && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
    if (tls && haveECC && haveAES128 && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
    if (tls && haveECC && haveStaticECC && haveAES128 && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
    if (!dtls && tls && haveECC && haveSHA1 && haveRC4) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_RC4_128_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_RC4_128_SHA
    if (!dtls && tls && haveECC && haveStaticECC && haveSHA1 && haveRC4) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_RC4_128_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
    if (tls && haveECC && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
    if (tls && haveECC && haveStaticECC && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
    #ifdef OPENSSL_EXTRA
    if ((tls && haveRSA && haveSHA1) || (tls && haveECDSAsig && haveSHA1)) {
    #else
    if (tls && haveRSA && haveSHA1) {
    #endif
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
    if (tls && haveRSAsig && haveStaticECC && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_256_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
    #ifdef OPENSSL_EXTRA
    if ((tls && haveRSA && haveAES128 && haveSHA1) ||
        (tls && haveECDSAsig && haveAES128 && haveSHA1)) {
    #else
    if (tls && haveRSA && haveAES128 && haveSHA1) {
    #endif
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
    if (tls && haveRSAsig && haveStaticECC && haveAES128 && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_RSA_WITH_AES_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_RC4_128_SHA
    if (!dtls && tls && haveRSA && haveSHA1 && haveRC4) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_RC4_128_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_RC4_128_SHA
    if (!dtls && tls && haveRSAsig && haveStaticECC && haveSHA1 && haveRC4) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_RSA_WITH_RC4_128_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
    #ifdef OPENSSL_EXTRA
    if ((tls && haveRSA && haveSHA1) || (tls && haveECDSAsig && haveSHA1)) {
    #else
    if (tls && haveRSA && haveSHA1) {
    #endif
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
    if (tls && haveRSAsig && haveStaticECC && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CCM
    if (tls1_2 && haveECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_CCM;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
    if (tls1_2 && haveECC && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
    if (tls1_2 && haveECC) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_128_CCM_8
    if (tls1_2 && haveRSA && haveStaticRSA && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_AES_128_CCM_8;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_256_CCM_8
    if (tls1_2 && haveRSA && haveStaticRSA) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_AES_256_CCM_8;
    }
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveDH && haveRSA)
#else
    if (tls && haveDH && haveRSA)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
    }
#endif /* BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 */

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveDH && haveRSA && haveAES128)
#else
    if (tls && haveDH && haveRSA && haveAES128)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
    }
#endif /* BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 */

/* Place as higher priority for MYSQL testing */
#if !defined(WOLFSSL_MYSQL_COMPATIBLE)
#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
    if (tls && haveDH && haveRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
    }
#endif
#endif /* !WOLFSSL_MYSQL_COMPATIBLE */

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
    if (tls && haveDH && haveRSA && haveAES128 && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
    if (tls && haveDH && haveRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveRSA && haveStaticRSA)
#else
    if (tls && haveRSA && haveStaticRSA)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveRSA && haveStaticRSA && haveAES128)
#else
    if (tls && haveRSA && haveStaticRSA && haveAES128)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA
    if (tls && haveRSA && haveStaticRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_AES_256_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA
    if (tls && haveRSA && haveStaticRSA && haveAES128 && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_AES_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_CHACHA20_OLD_POLY1305_SHA256
    if (tls1_2 && haveECC) {
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] =
                              TLS_ECDHE_ECDSA_WITH_CHACHA20_OLD_POLY1305_SHA256;
    }
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256
    #ifdef OPENSSL_EXTRA
    if ((tls1_2 && haveRSA) || (tls1_2 && haveECDSAsig)) {
    #else
    if (tls1_2 && haveRSA) {
    #endif
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] = TLS_ECDHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256;
    }
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256
    #ifdef OPENSSL_EXTRA
    if ((tls1_2 && haveRSA) || (tls1_2 && haveECDSAsig)) {
    #else
    if (tls1_2 && haveRSA) {
    #endif
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256;
    }
#endif /* BUILD_TLS_DHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256 */

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_NULL_SHA
    if (tls && haveECC && haveNull && haveSHA1) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_NULL_SHA;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_NULL_MD5
    if (tls && haveRSA && haveNull && haveStaticRSA) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_NULL_MD5;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_NULL_SHA
    if (tls && haveRSA && haveNull && haveStaticRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_NULL_SHA;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_NULL_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveRSA && haveNull && haveStaticRSA)
#else
    if (tls && haveRSA && haveNull && haveStaticRSA)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_NULL_SHA256;
    }
#endif /* BUILD_TLS_RSA_WITH_NULL_SHA256 */

#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA
    if (tls && havePSK && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_256_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveDH && havePSK)
#else
    if (tls && haveDH && havePSK)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_AES_256_CBC_SHA384;
    }
#endif /* BUILD_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 */

#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA384
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK)
#else
    if (tls && havePSK)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_256_CBC_SHA384;
    }
#endif /* BUILD_TLS_PSK_WITH_AES_256_CBC_SHA384 */

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveDH && havePSK && haveAES128)
#else
    if (tls && haveDH && havePSK && haveAES128)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_AES_128_CBC_SHA256;
    }
#endif /* BUILD_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 */

#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK && haveAES128)
#else
    if (tls && havePSK && haveAES128)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_128_CBC_SHA256;
    }
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA
    if (tls && havePSK && haveAES128 && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_128_CCM
    if (tls && haveDH && havePSK && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_AES_128_CCM;
    }
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_256_CCM
    if (tls && haveDH && havePSK) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_AES_256_CCM;
    }
#endif

#ifdef BUILD_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK)
#else
    if (tls && havePSK)
#endif
    {
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_CHACHA20_POLY1305_SHA256;
    }
#endif /* BUILD_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 */

#ifdef BUILD_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK)
#else
    if (tls && havePSK)
#endif
    {
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] = TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256;
    }
#endif /* BUILD_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 */

#ifdef BUILD_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK)
#else
    if (tls && havePSK)
#endif
    {
        suites->suites[idx++] = CHACHA_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256;
    }
#endif /* BUILD_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 */

#ifdef BUILD_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK && haveAES128)
#else
    if (tls && havePSK && haveAES128)
#endif
    {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256;
    }
#endif /* BUILD_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 */

#ifdef BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK && haveAES128)
#else
    if (tls && havePSK && haveAES128)
#endif
    {
        suites->suites[idx++] = ECDHE_PSK_BYTE;
        suites->suites[idx++] = TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256;
    }
#endif /* BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 */

#ifdef BUILD_TLS_PSK_WITH_AES_128_CCM
    if (tls && havePSK && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_128_CCM;
    }
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_256_CCM
    if (tls && havePSK) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_256_CCM;
    }
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_128_CCM_8
    if (tls && havePSK && haveAES128) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_128_CCM_8;
    }
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_256_CCM_8
    if (tls && havePSK) {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_AES_256_CCM_8;
    }
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_NULL_SHA384
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveDH && havePSK)
#else
    if (tls && haveDH && havePSK && haveNull)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_NULL_SHA384;
    }
#endif /* BUILD_TLS_DHE_PSK_WITH_NULL_SHA384 */

#ifdef BUILD_TLS_PSK_WITH_NULL_SHA384
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK && haveNull)
#else
    if (tls && havePSK && haveNull)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_NULL_SHA384;
    }
#endif /* BUILD_TLS_PSK_WITH_NULL_SHA384 */

#ifdef BUILD_TLS_ECDHE_PSK_WITH_NULL_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK && haveNull)
#else
    if (tls && havePSK && haveNull)
#endif
    {
        suites->suites[idx++] = ECC_BYTE;
        suites->suites[idx++] = TLS_ECDHE_PSK_WITH_NULL_SHA256;
    }
#endif /* BUILD_TLS_ECDHE_PSK_WITH_NULL_SHA256 */

#ifdef BUILD_TLS_DHE_PSK_WITH_NULL_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveDH && havePSK && haveNull)
#else
    if (tls && haveDH && havePSK && haveNull)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_PSK_WITH_NULL_SHA256;
    }
#endif /* BUILD_TLS_DHE_PSK_WITH_NULL_SHA256 */

#ifdef BUILD_TLS_PSK_WITH_NULL_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && havePSK && haveNull)
#else
    if (tls && havePSK && haveNull)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_NULL_SHA256;
    }
#endif /* BUILD_TLS_PSK_WITH_NULL_SHA256 */

#ifdef BUILD_TLS_PSK_WITH_NULL_SHA
    if (tls && havePSK && haveNull) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_PSK_WITH_NULL_SHA;
    }
#endif

#ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA
    if (!dtls && haveRSA && haveStaticRSA && haveSHA1 && haveRC4) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = SSL_RSA_WITH_RC4_128_SHA;
    }
#endif

#ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5
    if (!dtls && haveRSA && haveStaticRSA && haveRC4) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = SSL_RSA_WITH_RC4_128_MD5;
    }
#endif

#ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA
    if (haveRSA && haveStaticRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = SSL_RSA_WITH_3DES_EDE_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
    if (tls && haveRSA && haveStaticRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_CAMELLIA_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
    if (tls && haveDH && haveRSA && haveStaticRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
    if (tls && haveRSA && haveStaticRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_CAMELLIA_256_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
    if (tls && haveDH && haveRSA && haveStaticRSA && haveSHA1) {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA;
    }
#endif

#ifdef BUILD_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveRSA && haveStaticRSA)
#else
    if (tls && haveRSA && haveStaticRSA)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256;
    }
#endif /* BUILD_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 */

#ifdef BUILD_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveDH && haveRSA && haveStaticRSA)
#else
    if (tls && haveDH && haveRSA && haveStaticRSA)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256;
    }
#endif /* BUILD_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 */

#ifdef BUILD_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveRSA && haveStaticRSA)
#else
    if (tls && haveRSA && haveStaticRSA)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256;
    }
#endif /* BUILD_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 */

#ifdef BUILD_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
#ifndef WOLFSSL_OLDTLS_SHA2_CIPHERSUITES
    if (tls1_2 && haveDH && haveRSA && haveStaticRSA)
#else
    if (tls && haveDH && haveRSA && haveStaticRSA)
#endif
    {
        suites->suites[idx++] = CIPHER_BYTE;
        suites->suites[idx++] = TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256;
    }
#endif /* BUILD_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 */

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
    if (tls && haveECC) {
        suites->suites[idx++] = SM_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
    if (tls && haveECC) {
        suites->suites[idx++] = SM_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3;
    }
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
    if (tls && haveECC) {
        suites->suites[idx++] = SM_BYTE;
        suites->suites[idx++] = TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3;
    }
#endif

#endif /* !WOLFSSL_NO_TLS12 */

    suites->suiteSz = idx;

    if (suites->hashSigAlgoSz == 0) {
        InitSuitesHashSigAlgo(suites->hashSigAlgo, SIG_ALL, tls1_2, tls1_3,
            keySz, &suites->hashSigAlgoSz);
    }

    /* Moved to the end as we set some of the vars but never use them */
    (void)tls;  /* shut up compiler */
    (void)tls1_2;
    (void)dtls;
    (void)haveDH;
    (void)havePSK;
    (void)haveStaticRSA;
    (void)haveStaticECC;
    (void)haveECC;
    (void)haveECDSAsig;
    (void)side;
    (void)haveRSA;    /* some builds won't read */
    (void)haveRSAsig; /* non ecc builds won't read */
    (void)haveAnon;   /* anon ciphers optional */
    (void)haveNull;
}

#if !defined(NO_WOLFSSL_SERVER) || !defined(NO_CERTS) || \
    (!defined(NO_WOLFSSL_CLIENT) && (!defined(NO_DH) || defined(HAVE_ECC)))

/* Decode the signature algorithm.
 *
 * input     The encoded signature algorithm.
 * hashalgo  The hash algorithm.
 * hsType    The signature type.
 */
void DecodeSigAlg(const byte* input, byte* hashAlgo, byte* hsType)
{
    *hsType = invalid_sa_algo;
    switch (input[0]) {
        case NEW_SA_MAJOR:
    #ifdef HAVE_ED25519
            /* ED25519: 0x0807 */
            if (input[1] == ED25519_SA_MINOR) {
                *hsType = ed25519_sa_algo;
                /* Hash performed as part of sign/verify operation. */
                *hashAlgo = sha512_mac;
            }
            else
    #endif
    #ifdef HAVE_ED448
            /* ED448: 0x0808 */
            if (input[1] == ED448_SA_MINOR) {
                *hsType = ed448_sa_algo;
                /* Hash performed as part of sign/verify operation. */
                *hashAlgo = sha512_mac;
            }
            else
    #endif
    #ifdef WC_RSA_PSS
            /* PSS PSS signatures: 0x080[9-b] */
            if (input[1] >= pss_sha256 && input[1] <= pss_sha512) {
                *hsType   = rsa_pss_pss_algo;
                *hashAlgo = PSS_PSS_HASH_TO_MAC(input[1]);
            }
            else
    #endif
    #ifdef HAVE_ECC_BRAINPOOL
            /* RFC 8734 TLS 1.3 Brainpool curves */
            if (input[1] == ECDSA_BRAINPOOLP256R1TLS13_SHA256_MINOR) {
                *hsType = ecc_brainpool_sa_algo;
                *hashAlgo = sha256_mac;
            }
            else if (input[1] == ECDSA_BRAINPOOLP384R1TLS13_SHA384_MINOR) {
                *hsType = ecc_brainpool_sa_algo;
                *hashAlgo = sha384_mac;
            }
            else if (input[1] == ECDSA_BRAINPOOLP512R1TLS13_SHA512_MINOR) {
                *hsType = ecc_brainpool_sa_algo;
                *hashAlgo = sha512_mac;
            }
            else
    #endif
            {
                *hsType   = input[0];
                *hashAlgo = input[1];
            }
            break;
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        case SM2_SA_MAJOR:
            /* SM2: 0x0708 */
            if (input[1] == SM2_SA_MINOR) {
                *hsType = sm2_sa_algo;
                /* Hash performed as part of sign/verify operation. */
                *hashAlgo = sm3_mac;
            }
            break;
    #endif
    /* Hash performed as part of sign/verify operation.
     * However, if we want a dual alg signature with a classic algorithm as
     * alternative, we need an explicit hash algo here. */
    #ifdef HAVE_FALCON
        case FALCON_SA_MAJOR:
            if (input[1] == FALCON_LEVEL1_SA_MINOR) {
                *hsType = falcon_level1_sa_algo;
                *hashAlgo = sha256_mac;
            }
            else if (input[1] == FALCON_LEVEL5_SA_MINOR) {
                *hsType = falcon_level5_sa_algo;
                *hashAlgo = sha512_mac;
            }
            break;
    #endif /* HAVE_FALCON */
    #ifdef HAVE_DILITHIUM
        case DILITHIUM_SA_MAJOR:
            if (input[1] == DILITHIUM_LEVEL2_SA_MINOR) {
                *hsType = dilithium_level2_sa_algo;
                *hashAlgo = sha256_mac;
            }
            else if (input[1] == DILITHIUM_LEVEL3_SA_MINOR) {
                *hsType = dilithium_level3_sa_algo;
                *hashAlgo = sha384_mac;
            }
            else if (input[1] == DILITHIUM_LEVEL5_SA_MINOR) {
                *hsType = dilithium_level5_sa_algo;
                *hashAlgo = sha512_mac;
            }
            break;
    #endif /* HAVE_DILITHIUM */
        default:
            *hashAlgo = input[0];
            *hsType   = input[1];
            break;
    }
}
#endif /* !NO_WOLFSSL_SERVER || !NO_CERTS  ||
        * ( !NO_WOLFSSL_CLIENT && (!NO_DH || HAVE_ECC) ) */

#ifndef WOLFSSL_NO_TLS12
#if !defined(NO_WOLFSSL_SERVER) || !defined(NO_WOLFSSL_CLIENT)
#if !defined(NO_DH) || defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
             defined(HAVE_CURVE448) || (!defined(NO_RSA) && defined(WC_RSA_PSS))

enum wc_HashType HashAlgoToType(int hashAlgo)
{
    switch (hashAlgo) {
    #ifdef WOLFSSL_SHA512
        case sha512_mac:
            return WC_HASH_TYPE_SHA512;
    #endif
    #ifdef WOLFSSL_SHA384
        case sha384_mac:
            return WC_HASH_TYPE_SHA384;
    #endif
    #ifdef WOLFSSL_SM3
        case sm3_mac:
            return WC_HASH_TYPE_SM3;
    #endif
    #ifndef NO_SHA256
        case sha256_mac:
            return WC_HASH_TYPE_SHA256;
    #endif
    #ifdef WOLFSSL_SHA224
        case sha224_mac:
            return WC_HASH_TYPE_SHA224;
    #endif
    #if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                             defined(WOLFSSL_ALLOW_TLS_SHA1))
        case sha_mac:
            return WC_HASH_TYPE_SHA;
    #endif
        default:
            WOLFSSL_MSG("Bad hash sig algo");
            break;
    }

    return WC_HASH_TYPE_NONE;
}
#endif /* !NO_DH || HAVE_ECC || (!NO_RSA && WC_RSA_PSS) */
#endif /* !NO_WOLFSSL_SERVER || !NO_WOLFSSL_CLIENT */
#endif /* !WOLFSSL_NO_TLS12 */

#ifndef NO_CERTS

void InitX509Name(WOLFSSL_X509_NAME* name, int dynamicFlag, void* heap)
{
    (void)dynamicFlag;

    if (name != NULL) {
        XMEMSET(name, 0, sizeof(WOLFSSL_X509_NAME));
        name->name        = name->staticName;
        name->heap = heap;
        name->dynamicName = 0;
    }
}


void FreeX509Name(WOLFSSL_X509_NAME* name)
{
    if (name != NULL) {
        if (name->dynamicName) {
            XFREE(name->name, name->heap, DYNAMIC_TYPE_SUBJECT_CN);
            name->name = NULL;
        }
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
        {
            int i;
            for (i = 0; i < MAX_NAME_ENTRIES; i++) {
                if (name->entry[i].object != NULL)
                    wolfSSL_ASN1_OBJECT_free(name->entry[i].object);
                if (name->entry[i].value != NULL)
                    wolfSSL_ASN1_STRING_free(name->entry[i].value);
                XMEMSET(&name->entry[i], 0, sizeof(WOLFSSL_X509_NAME_ENTRY));
            }
        }
#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
#ifdef OPENSSL_ALL
        if (name->entries) {
            wolfSSL_sk_X509_NAME_ENTRY_free(name->entries);
            name->entries = NULL;
        }
#endif
    }
}


/* Initialize wolfSSL X509 type */
void InitX509(WOLFSSL_X509* x509, int dynamicFlag, void* heap)
{
    if (x509 == NULL) {
        WOLFSSL_MSG("Null parameter passed in!");
        return;
    }

    XMEMSET(x509, 0, sizeof(WOLFSSL_X509));

    x509->heap = heap;
    InitX509Name(&x509->issuer, 0, heap);
    InitX509Name(&x509->subject, 0, heap);
    x509->dynamicMemory  = (byte)dynamicFlag;
#if defined(OPENSSL_EXTRA_X509_SMALL) || defined(OPENSSL_EXTRA)
    {
        int ret;
        wolfSSL_RefInit(&x509->ref, &ret);
        (void)ret;
    }
#endif
}


/* Free wolfSSL X509 type */
void FreeX509(WOLFSSL_X509* x509)
{
    #if defined(WOLFSSL_CERT_REQ) && defined(OPENSSL_ALL) \
    &&  defined( WOLFSSL_CUSTOM_OID)
    int idx;
    #endif /* WOLFSSL_CERT_REQ && OPENSSL_ALL && WOLFSSL_CUSTOM_OID */
    if (x509 == NULL)
        return;

    FreeX509Name(&x509->issuer);
    FreeX509Name(&x509->subject);
    if (x509->pubKey.buffer) {
        XFREE(x509->pubKey.buffer, x509->heap, DYNAMIC_TYPE_PUBLIC_KEY);
        x509->pubKey.buffer = NULL;
    }
    FreeDer(&x509->derCert);
    XFREE(x509->sig.buffer, x509->heap, DYNAMIC_TYPE_SIGNATURE);
    x509->sig.buffer = NULL;
    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
        if (x509->authKeyIdSrc != NULL) {
            XFREE(x509->authKeyIdSrc, x509->heap, DYNAMIC_TYPE_X509_EXT);
        }
        else {
            XFREE(x509->authKeyId, x509->heap, DYNAMIC_TYPE_X509_EXT);
        }
        x509->authKeyIdSrc = NULL;
        x509->authKeyId = NULL;
        XFREE(x509->subjKeyId, x509->heap, DYNAMIC_TYPE_X509_EXT);
        x509->subjKeyId = NULL;
        wolfSSL_ASN1_STRING_free(x509->subjKeyIdStr);
        x509->subjKeyIdStr = NULL;
        XFREE(x509->authInfo, x509->heap, DYNAMIC_TYPE_X509_EXT);
        x509->authInfo = NULL;
        XFREE(x509->rawCRLInfo, x509->heap, DYNAMIC_TYPE_X509_EXT);
        x509->rawCRLInfo = NULL;
        XFREE(x509->CRLInfo, x509->heap, DYNAMIC_TYPE_X509_EXT);
        x509->CRLInfo = NULL;
        #if defined(OPENSSL_ALL) || defined(OPENSSL_EXTRA) || \
            defined(WOLFSSL_QT)
        XFREE(x509->authInfoCaIssuer, x509->heap, DYNAMIC_TYPE_X509_EXT);
        if (x509->ext_sk != NULL) {
            wolfSSL_sk_X509_EXTENSION_pop_free(x509->ext_sk, NULL);
        }
        if (x509->ext_sk_full != NULL) {
            wolfSSL_sk_X509_EXTENSION_pop_free(x509->ext_sk_full, NULL);
        }
        #endif /* OPENSSL_ALL || WOLFSSL_QT */
        #ifdef OPENSSL_EXTRA
        /* Free serialNumber that was set by wolfSSL_X509_get_serialNumber */
        if (x509->serialNumber != NULL) {
            wolfSSL_ASN1_INTEGER_free(x509->serialNumber);
        }
        #endif
        if (x509->extKeyUsageSrc != NULL) {
            XFREE(x509->extKeyUsageSrc, x509->heap, DYNAMIC_TYPE_X509_EXT);
            x509->extKeyUsageSrc= NULL;
        }
    #endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
    #if defined(OPENSSL_ALL)
        if (x509->algor.algorithm) {
            wolfSSL_ASN1_OBJECT_free(x509->algor.algorithm);
            x509->algor.algorithm = NULL;
        }
        if (x509->key.algor) {
            wolfSSL_X509_ALGOR_free(x509->key.algor);
            x509->key.algor = NULL;
        }
        if (x509->key.pkey) {
            wolfSSL_EVP_PKEY_free(x509->key.pkey);
            x509->key.pkey = NULL;
        }
        if (x509->subjAltNameSrc != NULL) {
            XFREE(x509->subjAltNameSrc, x509->heap, DYNAMIC_TYPE_X509_EXT);
            x509->subjAltNameSrc= NULL;
        }
    #endif /* OPENSSL_ALL */
    #if defined(WOLFSSL_CERT_REQ) && defined(OPENSSL_ALL)
        if (x509->reqAttributes) {
            wolfSSL_sk_pop_free(x509->reqAttributes, NULL);
        }
    #ifdef WOLFSSL_CUSTOM_OID
        for (idx = 0; idx < x509->customExtCount; idx++) {
            XFREE(x509->custom_exts[idx].oid, x509->heap,
                  DYNAMIC_TYPE_X509_EXT);
            XFREE(x509->custom_exts[idx].val, x509->heap,
                  DYNAMIC_TYPE_X509_EXT);
        }
    #endif /* WOLFSSL_CUSTOM_OID */
    #endif /* WOLFSSL_CERT_REQ && OPENSSL_ALL */
    if (x509->altNames) {
        FreeAltNames(x509->altNames, x509->heap);
        x509->altNames = NULL;
    }
    #ifndef IGNORE_NAME_CONSTRAINTS
    if (x509->permittedNames) {
        FreeNameSubtrees(x509->permittedNames, x509->heap);
        x509->permittedNames = NULL;
    }
    if (x509->excludedNames) {
        FreeNameSubtrees(x509->excludedNames, x509->heap);
        x509->excludedNames = NULL;
    }
    #endif

    #ifdef WOLFSSL_DUAL_ALG_CERTS
    XFREE(x509->sapkiDer, x509->heap, DYNAMIC_TYPE_X509_EXT);
    x509->sapkiDer = NULL;
    XFREE(x509->altSigAlgDer, x509->heap, DYNAMIC_TYPE_X509_EXT);
    x509->altSigAlgDer = NULL;
    if (x509->altSigValDer) {
        XFREE(x509->altSigValDer, x509->heap, DYNAMIC_TYPE_X509_EXT);
        x509->altSigValDer= NULL;
    }
    #endif /* WOLFSSL_DUAL_ALG_CERTS */

    #if defined(OPENSSL_EXTRA_X509_SMALL) || defined(OPENSSL_EXTRA)
        wolfSSL_RefFree(&x509->ref);
    #endif
}


#if !defined(NO_WOLFSSL_SERVER) || (!defined(NO_WOLFSSL_CLIENT) && \
    !defined(WOLFSSL_NO_CLIENT_AUTH))
#if !defined(WOLFSSL_NO_TLS12)
/* Encode the signature algorithm into buffer.
 *
 * hashalgo  The hash algorithm.
 * hsType   The signature type.
 * output    The buffer to encode into.
 */
static WC_INLINE void EncodeSigAlg(byte hashAlgo, byte hsType, byte* output)
{
    switch (hsType) {
#ifdef HAVE_ECC
        case ecc_dsa_sa_algo:
            output[0] = hashAlgo;
            output[1] = ecc_dsa_sa_algo;
            break;
#endif
#ifdef HAVE_ED25519
        case ed25519_sa_algo:
            output[0] = ED25519_SA_MAJOR;
            output[1] = ED25519_SA_MINOR;
            (void)hashAlgo;
            break;
#endif
#ifdef HAVE_ED448
        case ed448_sa_algo:
            output[0] = ED448_SA_MAJOR;
            output[1] = ED448_SA_MINOR;
            (void)hashAlgo;
            break;
#endif
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        case sm2_sa_algo:
            output[0] = SM2_SA_MAJOR;
            output[1] = SM2_SA_MINOR;
            (void)hashAlgo;
            break;
#endif
#ifndef NO_RSA
        case rsa_sa_algo:
            output[0] = hashAlgo;
            output[1] = rsa_sa_algo;
            break;
    #ifdef WC_RSA_PSS
        /* PSS signatures: 0x080[4-6] */
        case rsa_pss_sa_algo:
            output[0] = rsa_pss_sa_algo;
            output[1] = hashAlgo;
            break;
    #endif
#endif
        default:
            break;
    }
    (void)hashAlgo;
    (void)output;
}
#endif

#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && \
    !defined(WOLFSSL_NO_CLIENT_AUTH)
static void SetDigest(WOLFSSL* ssl, int hashAlgo)
{
    switch (hashAlgo) {
    #if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                              defined(WOLFSSL_ALLOW_TLS_SHA1))
        case sha_mac:
            ssl->options.dontFreeDigest = 1;
            ssl->buffers.digest.buffer = ssl->hsHashes->certHashes.sha;
            ssl->buffers.digest.length = WC_SHA_DIGEST_SIZE;
            break;
    #endif /* !NO_SHA */
    #ifndef NO_SHA256
        case sha256_mac:
            ssl->options.dontFreeDigest = 1;
            ssl->buffers.digest.buffer = ssl->hsHashes->certHashes.sha256;
            ssl->buffers.digest.length = WC_SHA256_DIGEST_SIZE;
            break;
    #endif /* !NO_SHA256 */
    #ifdef WOLFSSL_SM3
        case sm3_mac:
            ssl->options.dontFreeDigest = 1;
            ssl->buffers.digest.buffer = ssl->hsHashes->certHashes.sm3;
            ssl->buffers.digest.length = WC_SM3_DIGEST_SIZE;
            break;
    #endif /* WOLFSSL_SM2 */
    #ifdef WOLFSSL_SHA384
        case sha384_mac:
            ssl->options.dontFreeDigest = 1;
            ssl->buffers.digest.buffer = ssl->hsHashes->certHashes.sha384;
            ssl->buffers.digest.length = WC_SHA384_DIGEST_SIZE;
            break;
    #endif /* WOLFSSL_SHA384 */
    #ifdef WOLFSSL_SHA512
        case sha512_mac:
            ssl->options.dontFreeDigest = 1;
            ssl->buffers.digest.buffer = ssl->hsHashes->certHashes.sha512;
            ssl->buffers.digest.length = WC_SHA512_DIGEST_SIZE;
            break;
    #endif /* WOLFSSL_SHA512 */
        default:
            break;
    } /* switch */
}
#endif /* !WOLFSSL_NO_TLS12 && !WOLFSSL_NO_CLIENT_AUTH */
#endif /* !NO_WOLFSSL_SERVER || !NO_WOLFSSL_CLIENT */
#endif /* !NO_CERTS */

word32 MacSize(const WOLFSSL* ssl)
{
#ifdef HAVE_TRUNCATED_HMAC
    word32 digestSz = ssl->truncated_hmac ? (byte)TRUNCATED_HMAC_SZ
                                          : ssl->specs.hash_size;
#else
    word32 digestSz = ssl->specs.hash_size;
#endif

    return digestSz;
}

#ifndef NO_RSA
#if (!defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)) || \
    (defined(WC_RSA_PSS) && defined(HAVE_PK_CALLBACKS))
#if !defined(NO_WOLFSSL_SERVER) || !defined(NO_WOLFSSL_CLIENT)
static int TypeHash(int hashAlgo)
{
    switch (hashAlgo) {
    #ifdef WOLFSSL_SHA512
        case sha512_mac:
            return SHA512h;
    #endif
    #ifdef WOLFSSL_SHA384
        case sha384_mac:
            return SHA384h;
    #endif
    #ifndef NO_SHA256
        case sha256_mac:
            return SHA256h;
    #endif
    #ifdef WOLFSSL_SHA224
        case sha224_mac:
            return SHA224h;
    #endif
    #ifndef NO_SHA
        case sha_mac:
            return SHAh;
    #endif
        default:
            break;
    }

    return 0;
}
#endif /* !NO_WOLFSSL_SERVER && !NO_WOLFSSL_CLIENT */
#endif /* !WOLFSSL_NO_TLS12 */

#if defined(WC_RSA_PSS)
int ConvertHashPss(int hashAlgo, enum wc_HashType* hashType, int* mgf)
{
    switch (hashAlgo) {
        #ifdef WOLFSSL_SHA512
        case sha512_mac:
            *hashType = WC_HASH_TYPE_SHA512;
            if (mgf != NULL)
                *mgf = WC_MGF1SHA512;
            break;
        #endif
        #ifdef WOLFSSL_SHA384
        case sha384_mac:
            *hashType = WC_HASH_TYPE_SHA384;
            if (mgf != NULL)
                *mgf = WC_MGF1SHA384;
            break;
        #endif
        #ifndef NO_SHA256
        case sha256_mac:
            *hashType = WC_HASH_TYPE_SHA256;
            if (mgf != NULL)
                *mgf = WC_MGF1SHA256;
            break;
        #endif
        default:
            return BAD_FUNC_ARG;
    }

    return 0;
}
#endif

#if (!defined(NO_WOLFSSL_SERVER) || !defined(WOLFSSL_NO_CLIENT_AUTH)) && \
    (!defined(WOLFSSL_RSA_PUBLIC_ONLY) && !defined(WOLFSSL_RSA_VERIFY_ONLY))
int RsaSign(WOLFSSL* ssl, const byte* in, word32 inSz, byte* out,
            word32* outSz, int sigAlgo, int hashAlgo, RsaKey* key,
            DerBuffer* keyBufInfo)
{
    int ret;
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;
    (void)sigAlgo;
    (void)hashAlgo;

    WOLFSSL_ENTER("RsaSign");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    if (key) {
        ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
        if (ret != 0)
            return ret;
    }
#endif

#if defined(WC_RSA_PSS)
    if (sigAlgo == rsa_pss_sa_algo) {
        enum wc_HashType hashType = WC_HASH_TYPE_NONE;
        int mgf = 0;

        ret = ConvertHashPss(hashAlgo, &hashType, &mgf);
        if (ret != 0)
            return ret;

    #if defined(HAVE_PK_CALLBACKS)
        if (ssl->ctx->RsaPssSignCb) {
            void* ctx = wolfSSL_GetRsaPssSignCtx(ssl);
            ret = ssl->ctx->RsaPssSignCb(ssl, in, inSz, out, outSz,
                                         TypeHash(hashAlgo), mgf,
                                         keyBuf, keySz, ctx);
        }
        else
    #endif
        {
            ret = wc_RsaPSS_Sign(in, inSz, out, *outSz, hashType, mgf, key,
                                                                      ssl->rng);
        }
    }
    else
#endif
#if defined(HAVE_PK_CALLBACKS)
    if (ssl->ctx->RsaSignCb) {
        void* ctx = wolfSSL_GetRsaSignCtx(ssl);
        ret = ssl->ctx->RsaSignCb(ssl, in, inSz, out, outSz, keyBuf, keySz,
                                                                          ctx);
    }
    else
#endif /*HAVE_PK_CALLBACKS */
        ret = wc_RsaSSL_Sign(in, inSz, out, *outSz, key, ssl->rng);

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (key && ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    /* For positive response return in outSz */
    if (ret > 0) {
        *outSz = (word32)ret;
        ret = 0;
    }

    WOLFSSL_LEAVE("RsaSign", ret);

    return ret;
}
#endif

int RsaVerify(WOLFSSL* ssl, byte* in, word32 inSz, byte** out, int sigAlgo,
              int hashAlgo, RsaKey* key, buffer* keyBufInfo)
{
    int ret = WC_NO_ERR_TRACE(SIG_VERIFY_E);

#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;
    (void)sigAlgo;
    (void)hashAlgo;

    WOLFSSL_ENTER("RsaVerify");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#if defined(WC_RSA_PSS)
    if (sigAlgo == rsa_pss_sa_algo || sigAlgo == rsa_pss_pss_algo) {
        enum wc_HashType hashType = WC_HASH_TYPE_NONE;
        int mgf = 0;

        ret = ConvertHashPss(hashAlgo, &hashType, &mgf);
        if (ret != 0)
            return ret;
#ifdef HAVE_PK_CALLBACKS
        if (ssl->ctx->RsaPssVerifyCb) {
            void* ctx = wolfSSL_GetRsaPssVerifyCtx(ssl);
            ret = ssl->ctx->RsaPssVerifyCb(ssl, in, inSz, out,
                                           TypeHash(hashAlgo), mgf,
                                           keyBuf, keySz, ctx);
        }
        else
#endif /*HAVE_PK_CALLBACKS */
            ret = wc_RsaPSS_VerifyInline(in, inSz, out, hashType, mgf, key);
    }
    else
#endif
#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->RsaVerifyCb) {
        void* ctx = wolfSSL_GetRsaVerifyCtx(ssl);
        ret = ssl->ctx->RsaVerifyCb(ssl, in, inSz, out, keyBuf, keySz, ctx);
    }
    #if !defined(WOLFSSL_RENESAS_FSPSM_TLS) && \
        !defined(WOLFSSL_RENESAS_TSIP_TLS)
    else
    #else
    if (!ssl->ctx->RsaVerifyCb || ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
    #endif
#endif /*HAVE_PK_CALLBACKS */
    {
        ret = wc_RsaSSL_VerifyInline(in, inSz, out, key);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("RsaVerify", ret);

    return ret;
}

/* Verify RSA signature, 0 on success */
/* This function is used to check the sign result */
int VerifyRsaSign(WOLFSSL* ssl, byte* verifySig, word32 sigSz,
    const byte* plain, word32 plainSz, int sigAlgo, int hashAlgo, RsaKey* key,
    DerBuffer* keyBufInfo)
{
    byte* out = NULL;  /* inline result */
    int   ret;
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;
    (void)sigAlgo;
    (void)hashAlgo;

    WOLFSSL_ENTER("VerifyRsaSign");

    if (verifySig == NULL || plain == NULL) {
        return BAD_FUNC_ARG;
    }

    if (sigSz > ENCRYPT_LEN) {
        WOLFSSL_MSG("Signature buffer too big");
        return BUFFER_E;
    }

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    if (key) {
        ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
        if (ret != 0)
            return ret;
    }
#endif

#if defined(WC_RSA_PSS)
    if (sigAlgo == rsa_pss_sa_algo) {
        enum wc_HashType hashType = WC_HASH_TYPE_NONE;
        int mgf = 0;

        ret = ConvertHashPss(hashAlgo, &hashType, &mgf);
        if (ret != 0)
            return ret;
    #ifdef HAVE_PK_CALLBACKS
        if (ssl->ctx->RsaPssSignCheckCb) {
            /* The key buffer includes private/public portion,
                but only public is used */
            /* If HSM hardware is checking the signature result you can
                optionally skip the sign check and return 0 */
            /* The ctx here is the RsaSignCtx set using wolfSSL_SetRsaSignCtx */
            void* ctx = wolfSSL_GetRsaPssSignCtx(ssl);
            ret = ssl->ctx->RsaPssSignCheckCb(ssl, verifySig, sigSz, &out,
                                           TypeHash(hashAlgo), mgf,
                                           keyBuf, keySz, ctx);
            if (ret > 0) {
                ret = wc_RsaPSS_CheckPadding(plain, plainSz, out, (word32)ret,
                                             hashType);
                if (ret != 0) {
                    ret = VERIFY_CERT_ERROR;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        }
        else
    #endif /* HAVE_PK_CALLBACKS */
        {
            ret = wc_RsaPSS_VerifyInline(verifySig, sigSz, &out, hashType, mgf,
                                         key);
            if (ret > 0) {
    #ifdef HAVE_SELFTEST
                ret = wc_RsaPSS_CheckPadding(plain, plainSz, out, ret,
                                             hashType);
    #else
                ret = wc_RsaPSS_CheckPadding_ex(plain, plainSz, out, (word32)ret,
                                                hashType, -1,
                                                mp_count_bits(&key->n));
    #endif
                if (ret != 0) {
                    ret = VERIFY_CERT_ERROR;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        }

    }
    else
#endif /* WC_RSA_PSS */
    {
    #ifdef HAVE_PK_CALLBACKS
        if (ssl->ctx->RsaSignCheckCb) {
            /* The key buffer includes private/public portion,
                but only public is used */
            /* If HSM hardware is checking the signature result you can
                optionally skip the sign check and return 0 */
            /* The ctx here is the RsaSignCtx set using wolfSSL_SetRsaSignCtx */
            void* ctx = wolfSSL_GetRsaSignCtx(ssl);
            ret = ssl->ctx->RsaSignCheckCb(ssl, verifySig, sigSz, &out,
                keyBuf, keySz, ctx);
        }
        else
    #endif /* HAVE_PK_CALLBACKS */
        {
            ret = wc_RsaSSL_VerifyInline(verifySig, sigSz, &out, key);
        }

        if (ret > 0) {
            if (ret != (int)plainSz || !out ||
                                            XMEMCMP(plain, out, plainSz) != 0) {
                WOLFSSL_MSG("RSA Signature verification failed");
                ret = RSA_SIGN_FAULT;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
            else {
                ret = 0;  /* RSA reset */
            }
        }
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (key && ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("VerifyRsaSign", ret);

    return ret;
}

#ifndef WOLFSSL_NO_TLS12

#if (!defined(NO_WOLFSSL_SERVER) || !defined(WOLFSSL_NO_CLIENT_AUTH)) && \
    !defined(WOLFSSL_RSA_PUBLIC_ONLY)
int RsaDec(WOLFSSL* ssl, byte* in, word32 inSz, byte** out, word32* outSz,
    RsaKey* key, DerBuffer* keyBufInfo)
{
    byte *outTmp;
    byte mask;
    int ret;
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("RsaDec");

    outTmp = *out;
#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->RsaDecCb) {
        void* ctx = wolfSSL_GetRsaDecCtx(ssl);
        ret = ssl->ctx->RsaDecCb(ssl, in, inSz, &outTmp, keyBuf, keySz, ctx);
    }
    else
#endif /* HAVE_PK_CALLBACKS */
    {
        #ifdef WC_RSA_BLINDING
            ret = wc_RsaSetRNG(key, ssl->rng);
            if (ret != 0)
                return ret;
        #endif
        ret = wc_RsaPrivateDecryptInline(in, inSz, &outTmp, key);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    mask = ctMaskGT(ret, 0);
    *outSz = (word32)(ret & (int)(sword8)mask);
    ret &= (int)(sword8)(~mask);
    /* Copy pointer */
    ctMaskCopy(mask, (byte*)out, (byte*)&outTmp, sizeof(*out));

    WOLFSSL_LEAVE("RsaDec", ret);

    return ret;
}
#endif /* !NO_WOLFSSL_SERVER) || !WOLFSSL_NO_CLIENT_AUTH */

#ifndef WOLFSSL_RSA_VERIFY_ONLY
int RsaEnc(WOLFSSL* ssl, const byte* in, word32 inSz, byte* out, word32* outSz,
    RsaKey* key, buffer* keyBufInfo)
{
    int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("RsaEnc");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->RsaEncCb) {
        void* ctx = wolfSSL_GetRsaEncCtx(ssl);
        ret = ssl->ctx->RsaEncCb(ssl, in, inSz, out, outSz, keyBuf, keySz, ctx);
    }
    #if !defined(WOLFSSL_RENESAS_FSPSM_TLS) && \
        !defined(WOLFSSL_RENESAS_TSIP_TLS)
    else
    #else
    if (!ssl->ctx->RsaEncCb || ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
    #endif
#endif /* HAVE_PK_CALLBACKS */
    {
        ret = wc_RsaPublicEncrypt(in, inSz, out, *outSz, key, ssl->rng);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    /* For positive response return in outSz */
    if (ret > 0) {
        *outSz = (word32)ret;
        ret = 0;
    }

    WOLFSSL_LEAVE("RsaEnc", ret);

    return ret;
}
#endif

#endif /* !WOLFSSL_NO_TLS12 */

#endif /* NO_RSA */

#ifdef HAVE_ECC

int EccSign(WOLFSSL* ssl, const byte* in, word32 inSz, byte* out,
    word32* outSz, ecc_key* key, DerBuffer* keyBufInfo)
{
    int ret;
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("EccSign");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    if (key) {
        ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
        if (ret != 0)
            return ret;
    }
#endif

#if defined(HAVE_PK_CALLBACKS)
    if (ssl->ctx->EccSignCb) {
        void* ctx = wolfSSL_GetEccSignCtx(ssl);
        if (ctx == NULL) {
            /* Try to get the WOLFSSL_CTX EccSignCtx*/
            ctx = wolfSSL_CTX_GetEccSignCtx(ssl->ctx);
        }
        ret = ssl->ctx->EccSignCb(ssl, in, inSz, out, outSz, keyBuf,
            keySz, ctx);
#if defined(WOLFSSL_RENESAS_TSIP_TLS)
        if (ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
            ret = wc_ecc_sign_hash(in, inSz, out, outSz, ssl->rng, key);
        }
#endif /* WOLFSSL_RENESAS_TSIP_TLS */
    }
    else
#endif /* HAVE_PK_CALLBACKS */
    {
        ret = wc_ecc_sign_hash(in, inSz, out, outSz, ssl->rng, key);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (key && ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("EccSign", ret);

    return ret;
}

int EccVerify(WOLFSSL* ssl, const byte* in, word32 inSz, const byte* out,
    word32 outSz, ecc_key* key, buffer* keyBufInfo)
{
    int ret = WC_NO_ERR_TRACE(SIG_VERIFY_E);
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    /* Check hash length */
    if ((outSz > WC_MAX_DIGEST_SIZE) ||
        (outSz < WC_MIN_DIGEST_SIZE)) {
        return BAD_LENGTH_E;
    }

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("EccVerify");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->EccVerifyCb) {
        void* ctx = wolfSSL_GetEccVerifyCtx(ssl);
        ret = ssl->ctx->EccVerifyCb(ssl, in, inSz, out, outSz, keyBuf, keySz,
            &ssl->eccVerifyRes, ctx);
    }
    #if !defined(WOLFSSL_RENESAS_FSPSM_TLS) && \
        !defined(WOLFSSL_RENESAS_TSIP_TLS) && \
        !defined(WOLFSSL_MAXQ108X)
    else
    #else
    if (!ssl->ctx->EccVerifyCb || ret == WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE))
    #endif
#endif /* HAVE_PK_CALLBACKS  */
    {
        ret = wc_ecc_verify_hash(in, inSz, out, outSz, &ssl->eccVerifyRes, key);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
    else
#endif /* WOLFSSL_ASYNC_CRYPT */
    {
        if (ret != 0 || ssl->eccVerifyRes == 0) {
            if (ret == 0) {
                ret = VERIFY_SIGN_ERROR;
            }
            WOLFSSL_ERROR_VERBOSE(ret);
        }
        else {
            ret = 0;
        }
    }

    WOLFSSL_LEAVE("EccVerify", ret);

    return ret;
}

int EccSharedSecret(WOLFSSL* ssl, ecc_key* priv_key, ecc_key* pub_key,
        byte* pubKeyDer, word32* pubKeySz, byte* out, word32* outlen,
        int side)
{
    int ret;
#ifdef WOLFSSL_ASYNC_CRYPT
    WC_ASYNC_DEV* asyncDev = NULL;
#endif

    (void)ssl;
    (void)pubKeyDer;
    (void)pubKeySz;
    (void)side;

    WOLFSSL_ENTER("EccSharedSecret");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    if (priv_key != NULL) {
        asyncDev = &priv_key->asyncDev;
        ret = wolfSSL_AsyncInit(ssl, asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
        if (ret != 0)
            return ret;
    }
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->EccSharedSecretCb) {
        void* ctx = wolfSSL_GetEccSharedSecretCtx(ssl);
        ecc_key* otherKey = (side == WOLFSSL_CLIENT_END) ? pub_key : priv_key;
        ret = ssl->ctx->EccSharedSecretCb(ssl, otherKey, pubKeyDer,
            pubKeySz, out, outlen, side, ctx);
    }
    else
#endif
    {
#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \
    !defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2)) && \
    !defined(HAVE_SELFTEST)
        ret = wc_ecc_set_rng(priv_key, ssl->rng);
        if (ret == 0)
#endif
        {
            PRIVATE_KEY_UNLOCK();
            ret = wc_ecc_shared_secret(priv_key, pub_key, out, outlen);
            PRIVATE_KEY_LOCK();
        }
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("EccSharedSecret", ret);

    return ret;
}

int EccMakeKey(WOLFSSL* ssl, ecc_key* key, ecc_key* peer)
{
    int ret = 0;
    int keySz = 0;
    int ecc_curve = ECC_CURVE_DEF;

    WOLFSSL_ENTER("EccMakeKey");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_NONE);
    if (ret != 0)
        return ret;
#endif

    /* get key size */
    if (peer == NULL || peer->dp == NULL) {
        keySz = ssl->eccTempKeySz;
        /* get curve type */
        if (ssl->ecdhCurveOID > 0) {
            ecc_curve = wc_ecc_get_oid(ssl->ecdhCurveOID, NULL, NULL);
        }
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && \
        (defined(WOLFSSL_SM4_CBC) || defined(WOLFSSL_SM4_GCM) || \
         defined(WOLFSSL_SM4_CCM))
        if ((ssl->options.cipherSuite0 == SM_BYTE) && (0
        #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
            || (ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3)
        #endif
        #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
            || (ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3)
        #endif
        #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
            || (ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3)
        #endif
            )) {
            keySz = 32;
            ecc_curve = ECC_SM2P256V1;
        }
    #endif
    }
    else {
        keySz = peer->dp->size;
        ecc_curve = peer->dp->id;
    }

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->EccKeyGenCb) {
        void* ctx = wolfSSL_GetEccKeyGenCtx(ssl);
        ret = ssl->ctx->EccKeyGenCb(ssl, key, (unsigned int)keySz, ecc_curve, ctx);
    }
    else
#endif
    {
        ret = wc_ecc_make_key_ex(ssl->rng, keySz, key, ecc_curve);
    }

    /* make sure the curve is set for TLS */
    if (ret == 0 && key->dp) {
        ssl->ecdhCurveOID = key->dp->oidSum;
    #if defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE)
        ssl->namedGroup = 0;
    #endif
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("EccMakeKey", ret);

    return ret;
}
#endif /* HAVE_ECC */

#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)

int Sm2wSm3Sign(WOLFSSL* ssl, const byte* id, word32 idSz, const byte* in,
    word32 inSz, byte* out, word32* outSz, ecc_key* key, DerBuffer* keyBufInfo)
{
    int ret;
    byte hash[WC_SM3_DIGEST_SIZE];

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("Sm2wSm3Sign");

    ret = wc_ecc_sm2_create_digest(id, idSz, in, inSz, WC_HASH_TYPE_SM3, hash,
        sizeof(hash), key);
    if (ret == 0) {
        ret = wc_ecc_sm2_sign_hash(hash, sizeof(hash), out, outSz, ssl->rng,
            key);
    }

    WOLFSSL_LEAVE("Sm2wSm3Sign", ret);

    return ret;
}

int Sm2wSm3Verify(WOLFSSL* ssl, const byte* id, word32 idSz, const byte* sig,
    word32 sigSz, const byte* msg, word32 msgSz, ecc_key* key,
    buffer* keyBufInfo)
{
    int ret = WC_NO_ERR_TRACE(SIG_VERIFY_E);
    byte hash[WC_SM3_DIGEST_SIZE];

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("Sm2wSm3Verify");

    ret = wc_ecc_sm2_create_digest(id, idSz, msg, msgSz, WC_HASH_TYPE_SM3, hash,
        sizeof(hash), key);
    if (ret == 0) {
        ret = wc_ecc_sm2_verify_hash(sig, sigSz, hash, sizeof(hash),
            &ssl->eccVerifyRes, key);
        if (ret == 0 && ssl->eccVerifyRes == 0) {
            ret = VERIFY_SIGN_ERROR;
        }
    }
    if (ret != 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
    }

    WOLFSSL_LEAVE("Sm2wSm3Verify", ret);

    return ret;
}

#endif /* WOLFSSL_SM2 && WOLFSSL_SM3 */

#ifdef HAVE_ED25519
/* Check whether the key contains a public key.
 * If not then pull it out of the leaf certificate.
 *
 * ssl  SSL/TLS object.
 * returns MEMORY_E when unable to allocate memory, a parsing error, otherwise
 * 0 on success.
 */
int Ed25519CheckPubKey(WOLFSSL* ssl)
{
#ifndef HAVE_ED25519_KEY_IMPORT
    (void)ssl;
    return NOT_COMPILED_IN;
#else /* HAVE_ED25519_KEY_IMPORT */
    ed25519_key* key = (ed25519_key*)ssl->hsKey;
    int ret = 0;

    /* Public key required for signing. */
    if (key != NULL && !key->pubKeySet) {
        const unsigned char* pubKey;
        word32 pubKeySz;

        ret = wc_CertGetPubKey(ssl->buffers.certificate->buffer,
            ssl->buffers.certificate->length, &pubKey, &pubKeySz);
        if (ret == 0) {
            ret = wc_ed25519_import_public(pubKey, pubKeySz, key);
        }
    }

    return ret;
#endif /* HAVE_ED25519_KEY_IMPORT */
}

/* Sign the data using EdDSA and key using Ed25519.
 *
 * ssl    SSL object.
 * in     Data or message to sign.
 * inSz   Length of the data.
 * out    Buffer to hold signature.
 * outSz  On entry, size of the buffer. On exit, the size of the signature.
 * key    The private Ed25519 key data.
 * keySz  The length of the private key data in bytes.
 * ctx    The callback context.
 * returns 0 on success, otherwise the value is an error.
 */
int Ed25519Sign(WOLFSSL* ssl, const byte* in, word32 inSz, byte* out,
                word32* outSz, ed25519_key* key, DerBuffer* keyBufInfo)
{
#ifndef HAVE_ED25519_SIGN
    (void)ssl;
    (void)in;
    (void)inSz;
    (void)out;
    (void)outSz;
    (void)key;
    (void)keyBufInfo;
    return NOT_COMPILED_IN;
#else /* HAVE_ED25519_SIGN */
    int ret;
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("Ed25519Sign");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#if defined(HAVE_PK_CALLBACKS)
    if (ssl->ctx->Ed25519SignCb) {
        void* ctx = wolfSSL_GetEd25519SignCtx(ssl);
        ret = ssl->ctx->Ed25519SignCb(ssl, in, inSz, out, outSz, keyBuf,
            keySz, ctx);
    }
    else
#endif /* HAVE_PK_CALLBACKS */
    {
        ret = wc_ed25519_sign_msg(in, inSz, out, outSz, key);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("Ed25519Sign", ret);

    return ret;
#endif /* HAVE_ED25519_SIGN */
}

/* Verify the data using EdDSA and key using Ed25519.
 *
 * ssl    SSL object.
 * in     Signature data.
 * inSz   Length of the signature data in bytes.
 * msg    Message to verify.
 * outSz  Length of message in bytes.
 * key    The public Ed25519 key data.
 * keySz  The length of the private key data in bytes.
 * ctx    The callback context.
 * returns 0 on success, otherwise the value is an error.
 */
int Ed25519Verify(WOLFSSL* ssl, const byte* in, word32 inSz, const byte* msg,
                  word32 msgSz, ed25519_key* key, buffer* keyBufInfo)
{
#ifndef HAVE_ED25519_VERIFY
    (void)ssl;
    (void)in;
    (void)inSz;
    (void)msg;
    (void)msgSz;
    (void)key;
    (void)keyBufInfo;
    return NOT_COMPILED_IN;
#else /* HAVE_ED25519_VERIFY */
    int ret;
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("Ed25519Verify");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->Ed25519VerifyCb) {
        void* ctx = wolfSSL_GetEd25519VerifyCtx(ssl);
        ret = ssl->ctx->Ed25519VerifyCb(ssl, in, inSz, msg, msgSz, keyBuf,
                                        keySz, &ssl->eccVerifyRes, ctx);
    }
    else
#endif /* HAVE_PK_CALLBACKS  */
    {
        ret = wc_ed25519_verify_msg(in, inSz, msg, msgSz,
                                    &ssl->eccVerifyRes, key);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
    else
#endif /* WOLFSSL_ASYNC_CRYPT */
    {
        ret = (ret != 0 || ssl->eccVerifyRes == 0) ? VERIFY_SIGN_ERROR : 0;
    }

    WOLFSSL_LEAVE("Ed25519Verify", ret);

    return ret;
#endif /* HAVE_ED25519_VERIFY */
}
#endif /* HAVE_ED25519 */

#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)

#ifdef HAVE_CURVE25519
#ifdef HAVE_PK_CALLBACKS
    /* Gets X25519 key for shared secret callback testing
     * Client side: returns peer key
     * Server side: returns private key
     */
    static int X25519GetKey(WOLFSSL* ssl, curve25519_key** otherKey)
    {
        struct curve25519_key* tmpKey = NULL;

        if (ssl == NULL || otherKey == NULL) {
            return BAD_FUNC_ARG;
        }

        if (ssl->options.side == WOLFSSL_CLIENT_END) {
            if (!ssl->peerX25519Key || !ssl->peerX25519KeyPresent ||
                                       !ssl->peerX25519Key->dp) {
                return NO_PEER_KEY;
            }
            tmpKey = (struct curve25519_key*)ssl->peerX25519Key;
        }
        else if (ssl->options.side == WOLFSSL_SERVER_END) {
            if (!ssl->eccTempKeyPresent) {
                return NO_PRIVATE_KEY;
            }
            tmpKey = (struct curve25519_key*)ssl->eccTempKey;
        }

        if (tmpKey) {
            *otherKey = (curve25519_key *)tmpKey;
            return 0;
        }
        else {
            return NO_PEER_KEY;
        }
    }
#endif /* HAVE_PK_CALLBACKS */

static int X25519SharedSecret(WOLFSSL* ssl, curve25519_key* priv_key,
        curve25519_key* pub_key, byte* pubKeyDer, word32* pubKeySz,
        byte* out, word32* outlen, int side)
{
    int ret;

    (void)ssl;
    (void)pubKeyDer;
    (void)pubKeySz;
    (void)side;

    WOLFSSL_ENTER("X25519SharedSecret");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &priv_key->asyncDev, WC_ASYNC_FLAG_NONE);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->X25519SharedSecretCb) {
        curve25519_key* otherKey = NULL;

        ret = X25519GetKey(ssl, &otherKey);
        if (ret == 0) {
            void* ctx = wolfSSL_GetX25519SharedSecretCtx(ssl);
            ret = ssl->ctx->X25519SharedSecretCb(ssl, otherKey, pubKeyDer,
                pubKeySz, out, outlen, side, ctx);
        }
    }
    else
#endif
    {
    #ifdef WOLFSSL_CURVE25519_BLINDING
        ret = wc_curve25519_set_rng(priv_key, ssl->rng);
        if (ret == 0)
    #endif
        {
            ret = wc_curve25519_shared_secret_ex(priv_key, pub_key, out, outlen,
                                                 EC25519_LITTLE_ENDIAN);
        }
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &priv_key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("X25519SharedSecret", ret);

    return ret;
}

static int X25519MakeKey(WOLFSSL* ssl, curve25519_key* key,
        curve25519_key* peer)
{
    int ret = 0;

    (void)peer;

    WOLFSSL_ENTER("X25519MakeKey");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_NONE);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->X25519KeyGenCb) {
        void* ctx = wolfSSL_GetX25519KeyGenCtx(ssl);
        ret = ssl->ctx->X25519KeyGenCb(ssl, key, CURVE25519_KEYSIZE, ctx);
    }
    else
#endif
    {
        ret = wc_curve25519_make_key(ssl->rng, CURVE25519_KEYSIZE, key);
    }

    if (ret == 0) {
        ssl->ecdhCurveOID = ECC_X25519_OID;
    #if defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE)
        ssl->namedGroup = 0;
    #endif
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("X25519MakeKey", ret);

    return ret;
}
#endif /* HAVE_CURVE25519 */

#endif /* !NO_TLS && !WOLFSSL_NO_TLS12 */

#ifdef HAVE_ED448
/* Check whether the key contains a public key.
 * If not then pull it out of the leaf certificate.
 *
 * ssl  SSL/TLS object.
 * returns MEMORY_E when unable to allocate memory, a parsing error, otherwise
 * 0 on success.
 */
int Ed448CheckPubKey(WOLFSSL* ssl)
{
#ifndef HAVE_ED448_KEY_IMPORT
    (void)ssl;
    return NOT_COMPILED_IN;
#else /* HAVE_ED448_KEY_IMPORT */
    ed448_key* key = (ed448_key*)ssl->hsKey;
    int ret = 0;

    /* Public key required for signing. */
    if (key != NULL && !key->pubKeySet) {
        const unsigned char* pubKey;
        word32 pubKeySz;

        ret = wc_CertGetPubKey(ssl->buffers.certificate->buffer,
            ssl->buffers.certificate->length, &pubKey, &pubKeySz);
        if (ret == 0) {
            ret = wc_ed448_import_public(pubKey, pubKeySz, key);
        }
    }

    return ret;
#endif /* HAVE_ED448_KEY_IMPORT */
}

/* Sign the data using EdDSA and key using Ed448.
 *
 * ssl    SSL object.
 * in     Data or message to sign.
 * inSz   Length of the data.
 * out    Buffer to hold signature.
 * outSz  On entry, size of the buffer. On exit, the size of the signature.
 * key    The private Ed448 key data.
 * keySz  The length of the private key data in bytes.
 * ctx    The callback context.
 * returns 0 on success, otherwise the value is an error.
 */
int Ed448Sign(WOLFSSL* ssl, const byte* in, word32 inSz, byte* out,
              word32* outSz, ed448_key* key, DerBuffer* keyBufInfo)
{
#ifndef HAVE_ED448_SIGN
    (void)ssl;
    (void)in;
    (void)inSz;
    (void)out;
    (void)outSz;
    (void)key;
    (void)keyBufInfo;
    return NOT_COMPILED_IN;
#else /* HAVE_ED448_SIGN */
    int ret;
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("Ed448Sign");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#if defined(HAVE_PK_CALLBACKS)
    if (ssl->ctx->Ed448SignCb) {
        void* ctx = wolfSSL_GetEd448SignCtx(ssl);
        ret = ssl->ctx->Ed448SignCb(ssl, in, inSz, out, outSz, keyBuf, keySz,
            ctx);
    }
    else
#endif /* HAVE_PK_CALLBACKS */
    {
        ret = wc_ed448_sign_msg(in, inSz, out, outSz, key, NULL, 0);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("Ed448Sign", ret);

    return ret;
#endif /* HAVE_ED448_SIGN */
}

/* Verify the data using EdDSA and key using Ed448.
 *
 * ssl    SSL object.
 * in     Signature data.
 * inSz   Length of the signature data in bytes.
 * msg    Message to verify.
 * outSz  Length of message in bytes.
 * key    The public Ed448 key data.
 * keySz  The length of the private key data in bytes.
 * ctx    The callback context.
 * returns 0 on success, otherwise the value is an error.
 */
int Ed448Verify(WOLFSSL* ssl, const byte* in, word32 inSz, const byte* msg,
                word32 msgSz, ed448_key* key, buffer* keyBufInfo)
{
#ifndef HAVE_ED448_VERIFY
    (void)ssl;
    (void)in;
    (void)inSz;
    (void)msg;
    (void)msgSz;
    (void)key;
    (void)keyBufInfo;
    return NOT_COMPILED_IN;
#else /* HAVE_ED448_VERIFY */
    int ret;
#ifdef HAVE_PK_CALLBACKS
    const byte* keyBuf = NULL;
    word32 keySz = 0;

    if (keyBufInfo) {
        keyBuf = keyBufInfo->buffer;
        keySz = keyBufInfo->length;
    }
#endif

    (void)ssl;
    (void)keyBufInfo;

    WOLFSSL_ENTER("Ed448Verify");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->Ed448VerifyCb) {
        void* ctx = wolfSSL_GetEd448VerifyCtx(ssl);
        ret = ssl->ctx->Ed448VerifyCb(ssl, in, inSz, msg, msgSz, keyBuf, keySz,
             &ssl->eccVerifyRes, ctx);
    }
    else
#endif /* HAVE_PK_CALLBACKS  */
    {
        ret = wc_ed448_verify_msg(in, inSz, msg, msgSz, &ssl->eccVerifyRes, key,
            NULL, 0);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
    else
#endif /* WOLFSSL_ASYNC_CRYPT */
    {
        ret = (ret != 0 || ssl->eccVerifyRes == 0) ? VERIFY_SIGN_ERROR : 0;
    }

    WOLFSSL_LEAVE("Ed448Verify", ret);

    return ret;
#endif /* HAVE_ED448_VERIFY */
}
#endif /* HAVE_ED448 */

#ifndef WOLFSSL_NO_TLS12

#ifdef HAVE_CURVE448
#ifdef HAVE_PK_CALLBACKS
    /* Gets X448 key for shared secret callback testing
     * Client side: returns peer key
     * Server side: returns private key
     */
    static int X448GetKey(WOLFSSL* ssl, curve448_key** otherKey)
    {
        struct curve448_key* tmpKey = NULL;

        if (ssl == NULL || otherKey == NULL) {
            return BAD_FUNC_ARG;
        }

        if (ssl->options.side == WOLFSSL_CLIENT_END) {
            if (!ssl->peerX448Key || !ssl->peerX448KeyPresent) {
                return NO_PEER_KEY;
            }
            tmpKey = (struct curve448_key*)ssl->peerX448Key;
        }
        else if (ssl->options.side == WOLFSSL_SERVER_END) {
            if (!ssl->eccTempKeyPresent) {
                return NO_PRIVATE_KEY;
            }
            tmpKey = (struct curve448_key*)ssl->eccTempKey;
        }

        if (tmpKey) {
            *otherKey = (curve448_key *)tmpKey;
            return 0;
        }
        else {
            return NO_PEER_KEY;
        }
    }
#endif /* HAVE_PK_CALLBACKS */

static int X448SharedSecret(WOLFSSL* ssl, curve448_key* priv_key,
                            curve448_key* pub_key, byte* pubKeyDer,
                            word32* pubKeySz, byte* out, word32* outlen,
                            int side)
{
    int ret;

    (void)ssl;
    (void)pubKeyDer;
    (void)pubKeySz;
    (void)side;

    WOLFSSL_ENTER("X448SharedSecret");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &priv_key->asyncDev, WC_ASYNC_FLAG_CALL_AGAIN);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->X448SharedSecretCb) {
        curve448_key* otherKey = NULL;

        ret = X448GetKey(ssl, &otherKey);
        if (ret == 0) {
            void* ctx = wolfSSL_GetX448SharedSecretCtx(ssl);
            ret = ssl->ctx->X448SharedSecretCb(ssl, otherKey, pubKeyDer,
                pubKeySz, out, outlen, side, ctx);
        }
    }
    else
#endif
    {
        ret = wc_curve448_shared_secret_ex(priv_key, pub_key, out, outlen,
            EC448_LITTLE_ENDIAN);
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &priv_key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("X448SharedSecret", ret);

    return ret;
}

static int X448MakeKey(WOLFSSL* ssl, curve448_key* key, curve448_key* peer)
{
    int ret = 0;

    (void)peer;

    WOLFSSL_ENTER("X448MakeKey");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &key->asyncDev, WC_ASYNC_FLAG_NONE);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->X448KeyGenCb) {
        void* ctx = wolfSSL_GetX448KeyGenCtx(ssl);
        ret = ssl->ctx->X448KeyGenCb(ssl, key, CURVE448_KEY_SIZE, ctx);
    }
    else
#endif
    {
        ret = wc_curve448_make_key(ssl->rng, CURVE448_KEY_SIZE, key);
    }

    if (ret == 0) {
        ssl->ecdhCurveOID = ECC_X448_OID;
    #if defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE)
        ssl->namedGroup = 0;
    #endif
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &key->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("X448MakeKey", ret);

    return ret;
}
#endif /* HAVE_CURVE448 */

#endif /* !WOLFSSL_NO_TLS12 */

#if !defined(NO_CERTS) || !defined(NO_PSK)
#if !defined(NO_DH)

int DhGenKeyPair(WOLFSSL* ssl, DhKey* dhKey,
    byte* priv, word32* privSz,
    byte* pub, word32* pubSz)
{
    int ret;

    WOLFSSL_ENTER("DhGenKeyPair");

    if (ssl == NULL || dhKey == NULL) {
        return BAD_FUNC_ARG;
    }

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &dhKey->asyncDev, WC_ASYNC_FLAG_NONE);
    if (ret != 0)
        return ret;
#endif

#if defined(HAVE_PK_CALLBACKS)
    ret = NOT_COMPILED_IN;
    if (ssl && ssl->ctx && ssl->ctx->DhGenerateKeyPairCb) {
        ret = ssl->ctx->DhGenerateKeyPairCb(dhKey, ssl->rng, priv, privSz,
                                            pub, pubSz);
    }
    if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN))
#endif
    {
        PRIVATE_KEY_UNLOCK();
        ret = wc_DhGenerateKeyPair(dhKey, ssl->rng, priv, privSz, pub, pubSz);
        PRIVATE_KEY_LOCK();
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &dhKey->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("DhGenKeyPair", ret);

    return ret;
}

int DhAgree(WOLFSSL* ssl, DhKey* dhKey,
    const byte* priv, word32 privSz,
    const byte* otherPub, word32 otherPubSz,
    byte* agree, word32* agreeSz,
    const byte* prime, word32 primeSz)
{
    int ret;

    (void)ssl;

    WOLFSSL_ENTER("DhAgree");

#ifdef WOLFSSL_ASYNC_CRYPT
    /* initialize event */
    ret = wolfSSL_AsyncInit(ssl, &dhKey->asyncDev, WC_ASYNC_FLAG_NONE);
    if (ret != 0)
        return ret;
#endif

#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->DhAgreeCb) {
        void* ctx = wolfSSL_GetDhAgreeCtx(ssl);

        WOLFSSL_MSG("Calling DhAgree Callback Function");
        ret = ssl->ctx->DhAgreeCb(ssl, dhKey, priv, privSz,
                    otherPub, otherPubSz, agree, agreeSz, ctx);
    }
    else
#endif
    {
#if !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)
        /* check the public key has valid number */
        if (dhKey != NULL && (prime == NULL || primeSz == 0)) {
            /* wc_DhCheckPubKey does not do exponentiation */
            ret = wc_DhCheckPubKey(dhKey, otherPub, otherPubSz);
        }
        else {
            ret = wc_DhCheckPubValue(prime, primeSz, otherPub, otherPubSz);
        }
        if (ret != 0) {
            /* translate to valid error (wc_DhCheckPubValue returns MP_VAL -1) */
            ret = PEER_KEY_ERROR;
            WOLFSSL_ERROR_VERBOSE(ret);

    #ifdef OPENSSL_EXTRA
            SendAlert(ssl, alert_fatal, illegal_parameter);
    #endif
        }
        else
#endif
        {
            PRIVATE_KEY_UNLOCK();
            ret = wc_DhAgree(dhKey, agree, agreeSz, priv, privSz, otherPub,
                    otherPubSz);
            PRIVATE_KEY_LOCK();
        }
    }

    /* Handle async pending response */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl, &dhKey->asyncDev);
    }
#endif /* WOLFSSL_ASYNC_CRYPT */

    WOLFSSL_LEAVE("DhAgree", ret);

    (void)prime;
    (void)primeSz;

    return ret;
}
#endif /* !NO_DH */
#endif /* !NO_CERTS || !NO_PSK */


#ifdef HAVE_PK_CALLBACKS
int wolfSSL_IsPrivatePkSet(WOLFSSL* ssl)
{
    int pkcbset = 0;
    (void)ssl;

#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
                                                                !defined(NO_RSA)
    if (0
    #ifdef HAVE_ECC
        || (ssl->ctx->EccSignCb != NULL &&
                                        ssl->buffers.keyType == ecc_dsa_sa_algo)
    #endif
    #ifdef HAVE_ED25519
        || (ssl->ctx->Ed25519SignCb != NULL &&
                                        ssl->buffers.keyType == ed25519_sa_algo)
    #endif
    #ifdef HAVE_ED448
        || (ssl->ctx->Ed448SignCb != NULL &&
                                          ssl->buffers.keyType == ed448_sa_algo)
    #endif
    #ifndef NO_RSA
        || (ssl->ctx->RsaSignCb != NULL && ssl->buffers.keyType == rsa_sa_algo)
        || (ssl->ctx->RsaDecCb != NULL && ssl->buffers.keyType == rsa_kea)
        #ifdef WC_RSA_PSS
        || (ssl->ctx->RsaPssSignCb != NULL &&
                                        ssl->buffers.keyType == rsa_pss_sa_algo)
        #endif
    #endif
    ) {
        pkcbset = 1;
    }
#endif
    return pkcbset;
}

int wolfSSL_CTX_IsPrivatePkSet(WOLFSSL_CTX* ctx)
{
    int pkcbset = 0;
    (void)ctx;
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
                                                                !defined(NO_RSA)
    if (0
    #ifdef HAVE_ECC
        || ctx->EccSignCb != NULL
    #endif
    #ifdef HAVE_ED25519
        || ctx->Ed25519SignCb != NULL
    #endif
    #ifdef HAVE_ED448
        || ctx->Ed448SignCb != NULL
    #endif
    #ifndef NO_RSA
        || ctx->RsaSignCb != NULL
        || ctx->RsaDecCb != NULL
        #ifdef WC_RSA_PSS
        || ctx->RsaPssSignCb != NULL
        #endif
    #endif
    ) {
        pkcbset = 1;
    }
#endif
    return pkcbset;
}
#endif /* HAVE_PK_CALLBACKS */

static void InitSuites_EitherSide(Suites* suites, ProtocolVersion pv, int keySz,
        word16 haveRSA, word16 havePSK, word16 haveDH, word16 haveECDSAsig,
        word16 haveECC, word16 haveStaticECC,
        word16 haveAnon,
        int side)
{
    /* make sure server has DH params, and add PSK if there */
    if (side == WOLFSSL_SERVER_END) {
        InitSuites(suites, pv, keySz, haveRSA, havePSK, haveDH, haveECDSAsig,
                   haveECC, TRUE, haveStaticECC,
                   haveAnon, TRUE, TRUE, TRUE, TRUE, side);
    }
    else {
        InitSuites(suites, pv, keySz, haveRSA, havePSK, TRUE, haveECDSAsig,
                   haveECC, TRUE, haveStaticECC,
                   haveAnon, TRUE, TRUE, TRUE, TRUE, side);
    }
}

void InitSSL_CTX_Suites(WOLFSSL_CTX* ctx)
{
    int keySz = 0;
    byte havePSK = 0;
    byte haveAnon = 0;
    byte haveRSA = 0;
#ifndef NO_RSA
    haveRSA = 1;
#endif
#ifndef NO_PSK
    havePSK = ctx->havePSK;
#endif /* NO_PSK */
#ifdef HAVE_ANON
    haveAnon = ctx->useAnon;
#endif /* HAVE_ANON*/
#ifndef NO_CERTS
    keySz = ctx->privateKeySz;
#endif
    InitSuites_EitherSide(ctx->suites, ctx->method->version, keySz,
            haveRSA, havePSK, ctx->haveDH, ctx->haveECDSAsig, ctx->haveECC,
            ctx->haveStaticECC,
            haveAnon, ctx->method->side);
}

int InitSSL_Suites(WOLFSSL* ssl)
{
    int keySz = 0;
    byte havePSK = 0;
    byte haveAnon = 0;
    byte haveRSA = 0;
    byte haveMcast = 0;
    byte haveCertSetupCb = 0;

    (void)haveAnon; /* Squash unused var warnings */
    (void)haveMcast;
    (void)haveCertSetupCb;

    if (!ssl)
        return BAD_FUNC_ARG;

#ifndef NO_RSA
    haveRSA = 1;
#endif
#ifndef NO_PSK
    havePSK = (byte)ssl->options.havePSK;
#endif /* NO_PSK */
#if !defined(NO_CERTS) && !defined(WOLFSSL_SESSION_EXPORT)
#ifdef HAVE_ANON
    haveAnon = (byte)ssl->options.useAnon;
#endif /* HAVE_ANON*/
#ifdef WOLFSSL_MULTICAST
    haveMcast = (byte)ssl->options.haveMcast;
#endif /* WOLFSSL_MULTICAST */
#endif /* !NO_CERTS && !WOLFSSL_SESSION_EXPORT */
#if defined(WOLFSSL_TLS13) && !defined(NO_CERTS) && defined(OPENSSL_EXTRA)
    if (ssl->ctx->certSetupCb != NULL)
        haveCertSetupCb = 1;
#endif /* WOLFSSL_TLS13 && !NO_CERTS && OPENSSL_EXTRA */

#ifdef WOLFSSL_EARLY_DATA
    if (ssl->options.side == WOLFSSL_SERVER_END)
        ssl->options.maxEarlyDataSz = ssl->ctx->maxEarlyDataSz;
#endif
#if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
               ((defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)) || \
                (defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \
                (defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)))
    ssl->options.cacheMessages = ssl->options.side == WOLFSSL_SERVER_END ||
                                      ssl->buffers.keyType == ed25519_sa_algo ||
                                      ssl->buffers.keyType == ed448_sa_algo ||
                                      ssl->buffers.keyType == sm2_sa_algo;
#endif

#ifndef NO_CERTS
    keySz = ssl->buffers.keySz;
#endif

    if (ssl->suites != NULL) {
        InitSuites_EitherSide(ssl->suites, ssl->version, keySz, haveRSA,
                havePSK, ssl->options.haveDH, ssl->options.haveECDSAsig,
                ssl->options.haveECC, ssl->options.haveStaticECC,
                ssl->options.useAnon, ssl->options.side);
    }

#if !defined(NO_CERTS) && !defined(WOLFSSL_SESSION_EXPORT)
    /* make sure server has cert and key unless using PSK, Anon,
     * Multicast or cert setup callback. This should be true even if just
     * switching ssl ctx */
    if (ssl->options.side == WOLFSSL_SERVER_END &&
            !havePSK && !haveAnon && !haveMcast && !haveCertSetupCb) {

        /* server certificate must be loaded */
        if ((!ssl->buffers.certificate || !ssl->buffers.certificate->buffer)
#ifdef WOLFSSL_CERT_SETUP_CB
                && ssl->ctx->certSetupCb == NULL
#endif
                ) {
            WOLFSSL_MSG("Server missing certificate");
            WOLFSSL_ERROR_VERBOSE(NO_PRIVATE_KEY);
            return NO_PRIVATE_KEY;
        }

        if ((!ssl->buffers.key || !ssl->buffers.key->buffer)
#ifdef WOLFSSL_CERT_SETUP_CB
                && ssl->ctx->certSetupCb == NULL
#endif
                ) {
            /* allow no private key if using existing key */
        #ifdef WOLF_PRIVATE_KEY_ID
            if (ssl->devId != INVALID_DEVID
            #ifdef HAVE_PK_CALLBACKS
                || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)
            #endif
            ) {
                WOLFSSL_MSG("Allowing no server private key (external)");
            }
            else
        #endif
            {
                WOLFSSL_MSG("Server missing private key");
                WOLFSSL_ERROR_VERBOSE(NO_PRIVATE_KEY);
                return NO_PRIVATE_KEY;
            }
        }
    }
#endif

    return WOLFSSL_SUCCESS;
}

/* This function inherits a WOLFSSL_CTX's fields into an SSL object.
   It is used during initialization and to switch an ssl's CTX with
   wolfSSL_Set_SSL_CTX.  Requires ssl->suites alloc and ssl-arrays with PSK
   unless writeDup is on.

   ssl      object to initialize
   ctx      parent factory
   writeDup flag indicating this is a write dup only

   WOLFSSL_SUCCESS return value on success */
int SetSSL_CTX(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
{
    int ret = WOLFSSL_SUCCESS; /* set default ret */
    byte newSSL;

    WOLFSSL_ENTER("SetSSL_CTX");
    if (!ssl || !ctx)
        return BAD_FUNC_ARG;

    newSSL = ssl->ctx == NULL; /* Assign after null check */

#ifndef NO_PSK
    if (ctx->server_hint[0] && ssl->arrays == NULL && !writeDup) {
        return BAD_FUNC_ARG;  /* needed for copy below */
    }
#endif

    /* decrement previous CTX reference count if exists.
     * This should only happen if switching ctxs!*/
    if (!newSSL) {
        WOLFSSL_MSG("freeing old ctx to decrement reference count. Switching ctx.");
        wolfSSL_CTX_free(ssl->ctx);
    }

    /* increment CTX reference count */
    ret = wolfSSL_CTX_up_ref(ctx);
#ifdef WOLFSSL_REFCNT_ERROR_RETURN
    if (ret != WOLFSSL_SUCCESS) {
        return ret;
    }
#else
    (void)ret;
#endif

    ssl->ctx     = ctx; /* only for passing to calls, options could change */
    /* Don't change version on a SSL object that has already started a
     * handshake */
#if defined(WOLFSSL_HAPROXY)
    if (ssl->initial_ctx == NULL) {
        ret = wolfSSL_CTX_up_ref(ctx);
        if (ret == WOLFSSL_SUCCESS) {
            ssl->initial_ctx = ctx; /* Save access to session key materials */
        }
        else {
        #ifdef WOLFSSL_REFCNT_ERROR_RETURN
            return ret;
        #else
            (void)ret;
        #endif
        }
    }
#endif
    if (!ssl->msgsReceived.got_client_hello &&
            !ssl->msgsReceived.got_server_hello)
        ssl->version = ctx->method->version;
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)
    ssl->options.mask = ctx->mask;
    ssl->options.minProto = ctx->minProto;
    ssl->options.maxProto = ctx->maxProto;
#endif
#ifdef OPENSSL_EXTRA
    #ifdef WOLFSSL_TLS13
    if (ssl->version.minor == TLSv1_3_MINOR &&
     (ssl->options.mask & WOLFSSL_OP_NO_TLSv1_3) == WOLFSSL_OP_NO_TLSv1_3) {
        if (!ctx->method->downgrade) {
            WOLFSSL_MSG("\tInconsistent protocol options. TLS 1.3 set but not "
                        "allowed and downgrading disabled.");
            WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
            return VERSION_ERROR;
        }
        WOLFSSL_MSG("\tOption set to not allow TLSv1.3, Downgrading");
        ssl->version.minor = TLSv1_2_MINOR;
    }
    #endif
    if (ssl->version.minor == TLSv1_2_MINOR &&
     (ssl->options.mask & WOLFSSL_OP_NO_TLSv1_2) == WOLFSSL_OP_NO_TLSv1_2) {
        if (!ctx->method->downgrade) {
            WOLFSSL_MSG("\tInconsistent protocol options. TLS 1.2 set but not "
                        "allowed and downgrading disabled.");
            WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
            return VERSION_ERROR;
        }
        WOLFSSL_MSG("\tOption set to not allow TLSv1.2, Downgrading");
        ssl->version.minor = TLSv1_1_MINOR;
    }
    if (ssl->version.minor == TLSv1_1_MINOR &&
     (ssl->options.mask & WOLFSSL_OP_NO_TLSv1_1) == WOLFSSL_OP_NO_TLSv1_1) {
        if (!ctx->method->downgrade) {
            WOLFSSL_MSG("\tInconsistent protocol options. TLS 1.1 set but not "
                        "allowed and downgrading disabled.");
            WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
            return VERSION_ERROR;
        }
        WOLFSSL_MSG("\tOption set to not allow TLSv1.1, Downgrading");
        ssl->options.tls1_1 = 0;
        ssl->version.minor = TLSv1_MINOR;
    }
    if (ssl->version.minor == TLSv1_MINOR &&
        (ssl->options.mask & WOLFSSL_OP_NO_TLSv1) == WOLFSSL_OP_NO_TLSv1) {
        if (!ctx->method->downgrade) {
            WOLFSSL_MSG("\tInconsistent protocol options. TLS 1 set but not "
                        "allowed and downgrading disabled.");
            WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
            return VERSION_ERROR;
        }
        WOLFSSL_MSG("\tOption set to not allow TLSv1, Downgrading");
        ssl->options.tls    = 0;
        ssl->options.tls1_1 = 0;
        ssl->version.minor = SSLv3_MINOR;
    }
    if (ssl->version.minor == SSLv3_MINOR &&
        (ssl->options.mask & WOLFSSL_OP_NO_SSLv3) == WOLFSSL_OP_NO_SSLv3) {
        WOLFSSL_MSG("\tError, option set to not allow SSLv3");
        WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
        return VERSION_ERROR;
    }

    if (ssl->version.minor < ssl->options.minDowngrade) {
        WOLFSSL_MSG("\tversion below minimum allowed, fatal error");
        WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
        return VERSION_ERROR;
    }
#endif

#ifdef HAVE_ECC
    ssl->eccTempKeySz = ctx->eccTempKeySz;
    ssl->ecdhCurveOID = ctx->ecdhCurveOID;
#endif
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448)
    ssl->pkCurveOID = ctx->pkCurveOID;
#endif

#ifdef OPENSSL_EXTRA
    ssl->CBIS         = ctx->CBIS;
#endif
    ssl->timeout = ctx->timeout;
    ssl->verifyCallback    = ctx->verifyCallback;
    /* If we are setting the ctx on an already initialized SSL object
     * then we possibly already have a side defined. Don't overwrite unless
     * the context has a well defined role. */
    if (newSSL || ctx->method->side != WOLFSSL_NEITHER_END) {
        ssl->options.side  = (word16)(ctx->method->side);
    }
    ssl->options.downgrade        = (word16)(ctx->method->downgrade);
    ssl->options.minDowngrade     = ctx->minDowngrade;
    ssl->options.haveRSA          = ctx->haveRSA;
    ssl->options.haveDH           = ctx->haveDH;
#if  !defined(NO_CERTS) && !defined(NO_DH)
    /* Its possible that DH algorithm parameters were set in the ctx, recalc
     * cipher suites. */
    if (ssl->options.haveDH && ctx->serverDH_P.buffer != NULL &&
        ctx->serverDH_G.buffer != NULL) {
        if (ssl->suites == NULL) {
            if (AllocateSuites(ssl) != 0) {
                return MEMORY_E;
            }
        }
        InitSuites(ssl->suites, ssl->version, ssl->buffers.keySz,
                   ssl->options.haveRSA,
#ifdef NO_PSK
                   0,
#else
                   ctx->havePSK,
#endif
                   ssl->options.haveDH,
                   ssl->options.haveECDSAsig, ssl->options.haveECC, TRUE,
                   ssl->options.haveStaticECC, ssl->options.useAnon,
                   TRUE, TRUE, TRUE, TRUE, ssl->options.side);
    }
#endif /* !NO_CERTS && !NO_DH */
    ssl->options.haveECDSAsig     = ctx->haveECDSAsig;
    ssl->options.haveECC          = ctx->haveECC;
    ssl->options.haveStaticECC    = ctx->haveStaticECC;
    ssl->options.haveFalconSig    = ctx->haveFalconSig;
    ssl->options.haveDilithiumSig = ctx->haveDilithiumSig;

#ifndef NO_PSK
    ssl->options.havePSK       = (word16)(ctx->havePSK);
    ssl->options.client_psk_cb = ctx->client_psk_cb;
    ssl->options.server_psk_cb = ctx->server_psk_cb;
    ssl->options.psk_ctx       = ctx->psk_ctx;
#ifdef WOLFSSL_TLS13
    ssl->options.client_psk_cs_cb    = ctx->client_psk_cs_cb;
    ssl->options.client_psk_tls13_cb = ctx->client_psk_tls13_cb;
    ssl->options.server_psk_tls13_cb = ctx->server_psk_tls13_cb;
#endif
#endif /* NO_PSK */
#ifdef WOLFSSL_EARLY_DATA
    if (ssl->options.side == WOLFSSL_SERVER_END)
        ssl->options.maxEarlyDataSz = ctx->maxEarlyDataSz;
#endif

#ifdef HAVE_ANON
    ssl->options.useAnon = ctx->useAnon;
#endif
#ifndef NO_DH
    ssl->options.minDhKeySz = ctx->minDhKeySz;
    ssl->options.maxDhKeySz = ctx->maxDhKeySz;
#endif
#ifndef NO_RSA
    ssl->options.minRsaKeySz = ctx->minRsaKeySz;
  #ifdef WC_RSA_PSS
    ssl->useRsaPss = ctx->useRsaPss;
  #endif
#endif
#ifdef HAVE_ECC
    ssl->options.minEccKeySz = ctx->minEccKeySz;
#endif
#ifdef HAVE_FALCON
    ssl->options.minFalconKeySz = ctx->minFalconKeySz;
#endif /* HAVE_FALCON */
#ifdef HAVE_DILITHIUM
    ssl->options.minDilithiumKeySz = ctx->minDilithiumKeySz;
#endif /* HAVE_DILITHIUM */
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    ssl->options.verifyDepth = ctx->verifyDepth;
#endif

    ssl->options.sessionCacheOff      = ctx->sessionCacheOff;
    ssl->options.sessionCacheFlushOff = ctx->sessionCacheFlushOff;
#ifdef HAVE_EXT_CACHE
    ssl->options.internalCacheOff     = ctx->internalCacheOff;
    ssl->options.internalCacheLookupOff = ctx->internalCacheLookupOff;
#endif

    ssl->options.verifyPeer     = ctx->verifyPeer;
    ssl->options.verifyNone     = ctx->verifyNone;
    ssl->options.failNoCert     = ctx->failNoCert;
    ssl->options.failNoCertxPSK = ctx->failNoCertxPSK;
    ssl->options.sendVerify     = ctx->sendVerify;

    ssl->options.partialWrite  = ctx->partialWrite;
    ssl->options.quietShutdown = ctx->quietShutdown;
    ssl->options.groupMessages = ctx->groupMessages;

#ifndef NO_DH
    #if !defined(WOLFSSL_OLD_PRIME_CHECK) && !defined(HAVE_FIPS) && \
        !defined(HAVE_SELFTEST)
        ssl->options.dhKeyTested = ctx->dhKeyTested;
    #endif
    ssl->buffers.serverDH_P = ctx->serverDH_P;
    ssl->buffers.serverDH_G = ctx->serverDH_G;
#endif

#if defined(HAVE_RPK)
    ssl->options.rpkConfig  = ctx->rpkConfig;
    ssl->options.rpkState   = ctx->rpkState;
#endif /* HAVE_RPK */

#ifndef NO_CERTS
#ifdef WOLFSSL_COPY_CERT
    /* If WOLFSSL_COPY_CERT is defined, always copy the cert */
    if (ctx->certificate != NULL) {
        ret = AllocCopyDer(&ssl->buffers.certificate, ctx->certificate->buffer,
            ctx->certificate->length, ctx->certificate->type,
            ctx->certificate->heap);
        if (ret != 0) {
            return ret;
        }

        ssl->buffers.weOwnCert = 1;
        ret = WOLFSSL_SUCCESS;
    }
    if (ctx->certChain != NULL) {
        ret = AllocCopyDer(&ssl->buffers.certChain, ctx->certChain->buffer,
            ctx->certChain->length, ctx->certChain->type,
            ctx->certChain->heap);
        if (ret != 0) {
            return ret;
        }

        ssl->buffers.weOwnCertChain = 1;
        ret = WOLFSSL_SUCCESS;
    }
#else
    /* ctx still owns certificate, certChain, key, dh, and cm */
    ssl->buffers.certificate = ctx->certificate;
    ssl->buffers.certChain = ctx->certChain;
#endif
    ssl->buffers.certChainCnt = ctx->certChainCnt;
#ifndef WOLFSSL_BLIND_PRIVATE_KEY
#ifdef WOLFSSL_COPY_KEY
    if (ctx->privateKey != NULL) {
        if (ssl->buffers.key != NULL) {
            FreeDer(&ssl->buffers.key);
        }
        ret = AllocCopyDer(&ssl->buffers.key, ctx->privateKey->buffer,
            ctx->privateKey->length, ctx->privateKey->type,
            ctx->privateKey->heap);
        if (ret != 0) {
            return ret;
        }
        ssl->buffers.weOwnKey = 1;
        ret = WOLFSSL_SUCCESS;
    }
    else {
        ssl->buffers.key      = ctx->privateKey;
    }
#else
    ssl->buffers.key      = ctx->privateKey;
#endif
#else
    if (ctx->privateKey != NULL) {
        ret = AllocCopyDer(&ssl->buffers.key, ctx->privateKey->buffer,
            ctx->privateKey->length, ctx->privateKey->type,
            ctx->privateKey->heap);
        if (ret != 0) {
            return ret;
        }
        ssl->buffers.weOwnKey = 1;
        /* Blind the private key for the SSL with new random mask. */
        wolfssl_priv_der_blind_toggle(ssl->buffers.key, ctx->privateKeyMask);
        ret = wolfssl_priv_der_blind(ssl->rng, ssl->buffers.key,
            &ssl->buffers.keyMask);
        if (ret != 0) {
            return ret;
        }
        ret = WOLFSSL_SUCCESS;
    }
#endif
    ssl->buffers.keyType  = ctx->privateKeyType;
    ssl->buffers.keyId    = ctx->privateKeyId;
    ssl->buffers.keyLabel = ctx->privateKeyLabel;
    ssl->buffers.keySz    = ctx->privateKeySz;
    ssl->buffers.keyDevId = ctx->privateKeyDevId;
#ifdef WOLFSSL_DUAL_ALG_CERTS
#ifndef WOLFSSL_BLIND_PRIVATE_KEY
    ssl->buffers.altKey   = ctx->altPrivateKey;
#else
    if (ctx->altPrivateKey != NULL) {
        ret = AllocCopyDer(&ssl->buffers.altKey, ctx->altPrivateKey->buffer,
            ctx->altPrivateKey->length, ctx->altPrivateKey->type,
            ctx->altPrivateKey->heap);
        if (ret != 0) {
            return ret;
        }
        /* Blind the private key for the SSL with new random mask. */
        wolfssl_priv_der_blind_toggle(ssl->buffers.altKey,
            ctx->altPrivateKeyMask);
        ret = wolfssl_priv_der_blind(ssl->rng, ssl->buffers.altKey,
            &ssl->buffers.altKeyMask);
        if (ret != 0) {
            return ret;
        }
        ret = WOLFSSL_SUCCESS;
    }
#endif
    ssl->buffers.altKeyType  = ctx->altPrivateKeyType;
    ssl->buffers.altKeyId    = ctx->altPrivateKeyId;
    ssl->buffers.altKeyLabel = ctx->altPrivateKeyLabel;
    ssl->buffers.altKeySz    = ctx->altPrivateKeySz;
    ssl->buffers.altKeyDevId = ctx->altPrivateKeyDevId;
#endif /* WOLFSSL_DUAL_ALG_CERTS */
#endif
#if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
               ((defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)) || \
                (defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \
                (defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)))
    ssl->options.cacheMessages = ssl->options.side == WOLFSSL_SERVER_END ||
                                      ssl->buffers.keyType == ed25519_sa_algo ||
                                      ssl->buffers.keyType == ed448_sa_algo ||
                                      ssl->buffers.keyType == sm2_sa_algo;
#endif


#ifdef WOLFSSL_ASYNC_CRYPT
    ssl->devId = ctx->devId;
#endif

    if (writeDup == 0) {
#ifndef NO_PSK
        if (ctx->server_hint[0]) {   /* set in CTX */
            XSTRNCPY(ssl->arrays->server_hint, ctx->server_hint,
                                    sizeof(ssl->arrays->server_hint));
            ssl->arrays->server_hint[MAX_PSK_ID_LEN] = '\0'; /* null term */
        }
#endif /* NO_PSK */

        if (ssl->suites != NULL) {
            if (ctx->suites == NULL)
                XMEMSET(ssl->suites, 0, sizeof(Suites));
            else
                XMEMCPY(ssl->suites, ctx->suites, sizeof(Suites));
        }

        if (ssl->options.side != WOLFSSL_NEITHER_END) {
            /* Defer initializing suites until accept or connect */
            ret = InitSSL_Suites(ssl);
        }
    }  /* writeDup check */

    if (ctx->mask != 0 && wolfSSL_set_options(ssl, (long)ctx->mask) == 0) {
        WOLFSSL_MSG("wolfSSL_set_options error");
        return BAD_FUNC_ARG;
    }

#ifdef WOLFSSL_SESSION_EXPORT
    #ifdef WOLFSSL_DTLS
    ssl->dtls_export = ctx->dtls_export; /* export function for session */
    #endif
#endif

#ifdef WOLFSSL_WOLFSENTRY_HOOKS
    ssl->AcceptFilter = ctx->AcceptFilter;
    ssl->AcceptFilter_arg = ctx->AcceptFilter_arg;
    ssl->ConnectFilter = ctx->ConnectFilter;
    ssl->ConnectFilter_arg = ctx->ConnectFilter_arg;
#endif

#ifdef OPENSSL_EXTRA
    ssl->readAhead = ctx->readAhead;
#endif
#if defined(OPENSSL_EXTRA) && !defined(NO_BIO)
    /* Don't change recv callback if currently using BIO's */
    if (ssl->CBIORecv != SslBioReceive)
#endif
        ssl->CBIORecv = ctx->CBIORecv;
#if defined(OPENSSL_EXTRA) && !defined(NO_BIO)
    /* Don't change send callback if currently using BIO's */
    if (ssl->CBIOSend != SslBioSend)
#endif
        ssl->CBIOSend = ctx->CBIOSend;
    ssl->verifyDepth = ctx->verifyDepth;

    return ret;
}

int InitHandshakeHashes(WOLFSSL* ssl)
{
    int ret;

    /* make sure existing handshake hashes are free'd */
    if (ssl->hsHashes != NULL) {
        FreeHandshakeHashes(ssl);
    }

    /* allocate handshake hashes */
    ssl->hsHashes = (HS_Hashes*)XMALLOC(sizeof(HS_Hashes), ssl->heap,
                                                           DYNAMIC_TYPE_HASHES);
    if (ssl->hsHashes == NULL) {
        WOLFSSL_MSG("HS_Hashes Memory error");
        return MEMORY_E;
    }
    XMEMSET(ssl->hsHashes, 0, sizeof(HS_Hashes));

#if !defined(NO_MD5) && !defined(NO_OLD_TLS)
    ret = wc_InitMd5_ex(&ssl->hsHashes->hashMd5, ssl->heap, ssl->devId);
    if (ret != 0)
        return ret;
    #ifdef WOLFSSL_HASH_FLAGS
        wc_Md5SetFlags(&ssl->hsHashes->hashMd5, WC_HASH_FLAG_WILLCOPY);
    #endif
#endif
#if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                          defined(WOLFSSL_ALLOW_TLS_SHA1))
    ret = wc_InitSha_ex(&ssl->hsHashes->hashSha, ssl->heap, ssl->devId);
    if (ret != 0)
        return ret;
    #ifdef WOLFSSL_HASH_FLAGS
        wc_ShaSetFlags(&ssl->hsHashes->hashSha, WC_HASH_FLAG_WILLCOPY);
    #endif
#endif
#ifndef NO_SHA256
    ret = wc_InitSha256_ex(&ssl->hsHashes->hashSha256, ssl->heap, ssl->devId);
    if (ret != 0)
        return ret;
    #ifdef WOLFSSL_HASH_FLAGS
        wc_Sha256SetFlags(&ssl->hsHashes->hashSha256, WC_HASH_FLAG_WILLCOPY);
    #endif
#endif
#ifdef WOLFSSL_SHA384
    ret = wc_InitSha384_ex(&ssl->hsHashes->hashSha384, ssl->heap, ssl->devId);
    if (ret != 0)
        return ret;
    #ifdef WOLFSSL_HASH_FLAGS
        wc_Sha384SetFlags(&ssl->hsHashes->hashSha384, WC_HASH_FLAG_WILLCOPY);
    #endif
#endif
#ifdef WOLFSSL_SHA512
    ret = wc_InitSha512_ex(&ssl->hsHashes->hashSha512, ssl->heap, ssl->devId);
    if (ret != 0)
        return ret;
    #ifdef WOLFSSL_HASH_FLAGS
        wc_Sha512SetFlags(&ssl->hsHashes->hashSha512, WC_HASH_FLAG_WILLCOPY);
    #endif
#endif
#ifdef WOLFSSL_SM3
    ret = wc_InitSm3(&ssl->hsHashes->hashSm3, ssl->heap, ssl->devId);
    if (ret != 0)
        return ret;
    #ifdef WOLFSSL_HASH_FLAGS
        wc_Sm3SetFlags(&ssl->hsHashes->hashSm3, WC_HASH_FLAG_WILLCOPY);
    #endif
#endif

    return ret;
}

void Free_HS_Hashes(HS_Hashes* hsHashes, void* heap)
{
    if (hsHashes) {
    #if !defined(NO_MD5) && !defined(NO_OLD_TLS)
        wc_Md5Free(&hsHashes->hashMd5);
    #endif
    #if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                              defined(WOLFSSL_ALLOW_TLS_SHA1))
        wc_ShaFree(&hsHashes->hashSha);
    #endif
    #ifndef NO_SHA256
        wc_Sha256Free(&hsHashes->hashSha256);
    #endif
    #ifdef WOLFSSL_SHA384
        wc_Sha384Free(&hsHashes->hashSha384);
    #endif
    #ifdef WOLFSSL_SHA512
        wc_Sha512Free(&hsHashes->hashSha512);
    #endif
    #ifdef WOLFSSL_SM3
        wc_Sm3Free(&hsHashes->hashSm3);
    #endif
    #if (defined(HAVE_ED25519) || defined(HAVE_ED448) || \
         (defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3))) && \
        !defined(WOLFSSL_NO_CLIENT_AUTH)
        if (hsHashes->messages != NULL) {
            ForceZero(hsHashes->messages, (word32)hsHashes->length);
            XFREE(hsHashes->messages, heap, DYNAMIC_TYPE_HASHES);
            hsHashes->messages = NULL;
         }
    #endif

        XFREE(hsHashes, heap, DYNAMIC_TYPE_HASHES);
        hsHashes = NULL;
    }
}

void FreeHandshakeHashes(WOLFSSL* ssl)
{
    Free_HS_Hashes(ssl->hsHashes, ssl->heap);
    ssl->hsHashes = NULL;
}

/* copy the hashes from source to a newly made destination return status */
int InitHandshakeHashesAndCopy(WOLFSSL* ssl, HS_Hashes* source,
  HS_Hashes** destination)
{
    int ret;

    if ((ssl == NULL) || (source == NULL) || (destination == NULL))
        return BAD_FUNC_ARG;

    /* If *destination is already allocated, its constituent hashes need to be
     * freed, else they would leak. */
    Free_HS_Hashes(*destination, ssl->heap);

    /* allocate handshake hashes */
    *destination = (HS_Hashes*)XMALLOC(sizeof(HS_Hashes), ssl->heap,
                                       DYNAMIC_TYPE_HASHES);
    if (*destination == NULL) {
        WOLFSSL_MSG("HS_Hashes Memory error");
        return MEMORY_E;
    }

    /* Note we can't call InitHandshakeHashes() here, because the copy methods
     * overwrite the entire dest low level hash struct.  With some hashes and
     * settings (e.g. SHA-2 hashes with WOLFSSL_SMALL_STACK_CACHE), internal
     * scratch buffers are preallocated at init and will leak if overwritten.
     */
    XMEMSET(*destination, 0, sizeof(HS_Hashes));

  /* now copy the source contents to the destination */
    ret = 0;
#ifndef NO_OLD_TLS
    #ifndef NO_SHA
    if (ret == 0)
        ret = wc_ShaCopy(&source->hashSha, &(*destination)->hashSha);
    #endif
    #ifndef NO_MD5
    if (ret == 0)
        ret = wc_Md5Copy(&source->hashMd5, &(*destination)->hashMd5);
    #endif
#endif /* !NO_OLD_TLS */
    #ifndef NO_SHA256
    if (ret == 0)
        ret = wc_Sha256Copy(&source->hashSha256,
            &(*destination)->hashSha256);
    #endif
    #ifdef WOLFSSL_SHA384
    if (ret == 0)
        ret = wc_Sha384Copy(&source->hashSha384,
            &(*destination)->hashSha384);
    #endif
    #ifdef WOLFSSL_SHA512
    if (ret == 0)
        ret = wc_Sha512Copy(&source->hashSha512,
            &(*destination)->hashSha512);
    #endif
    #ifdef WOLFSSL_SM3
    if (ret == 0)
        ret = wc_Sm3Copy(&source->hashSm3,
            &(*destination)->hashSm3);
    #endif
    #if (defined(HAVE_ED25519) || defined(HAVE_ED448) || \
         (defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3))) && \
        !defined(WOLFSSL_NO_CLIENT_AUTH)
    if (ret == 0 && source->messages != NULL) {
        (*destination)->messages = (byte*)XMALLOC((size_t)source->length,
        ssl->heap,
        (int)DYNAMIC_TYPE_HASHES);
        (*destination)->length = source->length;
        (*destination)->prevLen = source->prevLen;

        if ((*destination)->messages == NULL) {
            ret = MEMORY_E;
        }
        else {
            XMEMCPY((*destination)->messages, source->messages,
                (size_t)source->length);
        }
    }
    #endif

    return ret;
}

/* called if user attempts to reuse WOLFSSL object for a new session.
 * For example wolfSSL_clear() is called then wolfSSL_connect or accept */
int ReinitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
{
    int ret = 0;

    WOLFSSL_ENTER("ReinitSSL");

    /* arrays */
    if (!writeDup && ssl->arrays == NULL) {
        ssl->arrays = (Arrays*)XMALLOC(sizeof(Arrays), ssl->heap,
                                                           DYNAMIC_TYPE_ARRAYS);
        if (ssl->arrays == NULL) {
            WOLFSSL_MSG("Arrays Memory error");
            return MEMORY_E;
        }
#ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Add("SSL Arrays", ssl->arrays, sizeof(*ssl->arrays));
#endif
        XMEMSET(ssl->arrays, 0, sizeof(Arrays));
#if defined(WOLFSSL_TLS13) || defined(WOLFSSL_SNIFFER)
        ssl->arrays->preMasterSz = ENCRYPT_LEN;
        ssl->arrays->preMasterSecret = (byte*)XMALLOC(ENCRYPT_LEN, ssl->heap,
            DYNAMIC_TYPE_SECRET);
        if (ssl->arrays->preMasterSecret == NULL) {
            WOLFSSL_MSG("preMasterSecret Memory error");
            return MEMORY_E;
        }
#ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Add("SSL Arrays", ssl->arrays->preMasterSecret, ENCRYPT_LEN);
#endif
        XMEMSET(ssl->arrays->preMasterSecret, 0, ENCRYPT_LEN);
#endif
    }

    /* RNG */
#ifdef SINGLE_THREADED
    if (ssl->rng == NULL) {
        ssl->rng = ctx->rng; /* CTX may have one, if so use it */
    }
#endif
    if (ssl->rng == NULL) {
        ssl->rng = (WC_RNG*)XMALLOC(sizeof(WC_RNG), ssl->heap,DYNAMIC_TYPE_RNG);
        if (ssl->rng == NULL) {
            WOLFSSL_MSG("RNG Memory error");
            return MEMORY_E;
        }
        XMEMSET(ssl->rng, 0, sizeof(WC_RNG));
        ssl->options.weOwnRng = 1;

        /* FIPS RNG API does not accept a heap hint */
#ifndef HAVE_FIPS
        if ( (ret = wc_InitRng_ex(ssl->rng, ssl->heap, ssl->devId)) != 0) {
            WOLFSSL_MSG("RNG Init error");
            return ret;
        }
#else
        if ( (ret = wc_InitRng(ssl->rng)) != 0) {
            WOLFSSL_MSG("RNG Init error");
            return ret;
        }
#endif
    }
    (void)ctx;

    ssl->options.shutdownDone = 0;
    if (ssl->session != NULL)
        ssl->session->side = (byte)ssl->options.side;

    return ret;
}

/* init everything to 0, NULL, default values before calling anything that may
   fail so that destructor has a "good" state to cleanup

   ssl      object to initialize
   ctx      parent factory
   writeDup flag indicating this is a write dup only

   0 on success */
int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
{
    int  ret;

    XMEMSET(ssl, 0, sizeof(WOLFSSL));
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Add("SSL Keys", &ssl->keys, sizeof(ssl->keys));
#ifdef WOLFSSL_TLS13
    wc_MemZero_Add("SSL client secret", &ssl->clientSecret,
        sizeof(ssl->clientSecret));
    wc_MemZero_Add("SSL client secret", &ssl->serverSecret,
        sizeof(ssl->serverSecret));
#endif
#ifdef WOLFSSL_HAVE_TLS_UNIQUE
    wc_MemZero_Add("ClientFinished hash", &ssl->clientFinished,
        TLS_FINISHED_SZ_MAX);
    wc_MemZero_Add("ServerFinished hash", &ssl->serverFinished,
        TLS_FINISHED_SZ_MAX);
#endif
#endif /* WOLFSSL_CHECK_MEM_ZERO */

#if defined(WOLFSSL_STATIC_MEMORY)
    if (ctx->heap != NULL) {
        WOLFSSL_HEAP_HINT* ssl_hint;
        WOLFSSL_HEAP_HINT* ctx_hint;

        /* avoid dereferencing a test value */
    #ifdef WOLFSSL_HEAP_TEST
        if (ctx->heap == (void*)WOLFSSL_HEAP_TEST) {
            ssl->heap = ctx->heap;
        }
        else {
    #endif
        ssl->heap = (WOLFSSL_HEAP_HINT*)XMALLOC(sizeof(WOLFSSL_HEAP_HINT),
                                               ctx->heap, DYNAMIC_TYPE_SSL);
        if (ssl->heap == NULL) {
            return MEMORY_E;
        }
        XMEMSET(ssl->heap, 0, sizeof(WOLFSSL_HEAP_HINT));
        ssl_hint = ((WOLFSSL_HEAP_HINT*)(ssl->heap));
        ctx_hint = ((WOLFSSL_HEAP_HINT*)(ctx->heap));

        ssl_hint->memory = ctx_hint->memory;
    #ifndef WOLFSSL_STATIC_MEMORY_LEAN
    #ifndef SINGLE_THREADED
        /* lock and check IO count / handshake count */
        if (wc_LockMutex(&(ctx_hint->memory->memory_mutex)) != 0) {
            WOLFSSL_MSG("Bad memory_mutex lock");
            XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL);
            ssl->heap = NULL; /* free and set to NULL for IO counter */
            WOLFSSL_ERROR_VERBOSE(BAD_MUTEX_E);
            return BAD_MUTEX_E;
        }
    #endif
        if (ctx_hint->memory->maxHa > 0 &&
                           ctx_hint->memory->maxHa <= ctx_hint->memory->curHa) {
            WOLFSSL_MSG("At max number of handshakes for static memory");
        #ifndef SINGLE_THREADED
            wc_UnLockMutex(&(ctx_hint->memory->memory_mutex));
        #endif
            XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL);
            ssl->heap = NULL; /* free and set to NULL for IO counter */
            return MEMORY_E;
        }

        if (ctx_hint->memory->maxIO > 0 &&
                           ctx_hint->memory->maxIO <= ctx_hint->memory->curIO) {
            WOLFSSL_MSG("At max number of IO allowed for static memory");
        #ifndef SINGLE_THREADED
            wc_UnLockMutex(&(ctx_hint->memory->memory_mutex));
        #endif
            XFREE(ssl->heap, ctx->heap, DYNAMIC_TYPE_SSL);
            ssl->heap = NULL; /* free and set to NULL for IO counter */
            return MEMORY_E;
        }
        ctx_hint->memory->curIO++;
        ctx_hint->memory->curHa++;
        ssl_hint->haFlag = 1;
    #ifndef SINGLE_THREADED
        wc_UnLockMutex(&(ctx_hint->memory->memory_mutex));
    #endif

        /* check if tracking stats */
        if (ctx_hint->memory->flag & WOLFMEM_TRACK_STATS) {
            ssl_hint->stats = (WOLFSSL_MEM_CONN_STATS*)XMALLOC(
               sizeof(WOLFSSL_MEM_CONN_STATS), ctx->heap, DYNAMIC_TYPE_SSL);
            if (ssl_hint->stats == NULL) {
                return MEMORY_E;
            }
            XMEMSET(ssl_hint->stats, 0, sizeof(WOLFSSL_MEM_CONN_STATS));
        }

        /* check if using fixed IO buffers */
        if (ctx_hint->memory->flag & WOLFMEM_IO_POOL_FIXED) {
        #ifndef SINGLE_THREADED
            if (wc_LockMutex(&(ctx_hint->memory->memory_mutex)) != 0) {
                WOLFSSL_MSG("Bad memory_mutex lock");
                WOLFSSL_ERROR_VERBOSE(BAD_MUTEX_E);
                return BAD_MUTEX_E;
            }
        #endif
            if (SetFixedIO(ctx_hint->memory, &(ssl_hint->inBuf)) != 1) {
            #ifndef SINGLE_THREADED
                wc_UnLockMutex(&(ctx_hint->memory->memory_mutex));
            #endif
                return MEMORY_E;
            }
            if (SetFixedIO(ctx_hint->memory, &(ssl_hint->outBuf)) != 1) {
            #ifndef SINGLE_THREADED
                wc_UnLockMutex(&(ctx_hint->memory->memory_mutex));
            #endif
                return MEMORY_E;
            }
            if (ssl_hint->outBuf == NULL || ssl_hint->inBuf == NULL) {
                WOLFSSL_MSG("Not enough memory to create fixed IO buffers");
            #ifndef SINGLE_THREADED
                wc_UnLockMutex(&(ctx_hint->memory->memory_mutex));
            #endif
                return MEMORY_E;
            }
       #ifndef SINGLE_THREADED
            wc_UnLockMutex(&(ctx_hint->memory->memory_mutex));
        #endif
        }
    #endif /* !WOLFSSL_STATIC_MEMORY_LEAN */
    #ifdef WOLFSSL_HEAP_TEST
        }
    #endif
    }
    else {
        ssl->heap = ctx->heap;
    }
#else
    ssl->heap = ctx->heap; /* carry over user heap without static memory */
#endif /* WOLFSSL_STATIC_MEMORY */

    ssl->buffers.inputBuffer.buffer = ssl->buffers.inputBuffer.staticBuffer;
    ssl->buffers.inputBuffer.bufferSize  = STATIC_BUFFER_LEN;

    ssl->buffers.outputBuffer.buffer = ssl->buffers.outputBuffer.staticBuffer;
    ssl->buffers.outputBuffer.bufferSize  = STATIC_BUFFER_LEN;

#ifdef WOLFSSL_THREADED_CRYPT
    {
        int i;
        for (i = 0; i < WOLFSSL_THREADED_CRYPT_CNT; i++) {
            ssl->buffers.encrypt[i].avail = 1;
        }
    }
#endif

#ifdef KEEP_PEER_CERT
    InitX509(&ssl->peerCert, 0, ssl->heap);
#endif

    ssl->rfd = -1;   /* set to invalid descriptor */
    ssl->wfd = -1;
    ssl->devId = ctx->devId; /* device for async HW (from wolfAsync_DevOpen) */

    /* initialize states */
    ssl->options.serverState = NULL_STATE;
    ssl->options.clientState = NULL_STATE;
    ssl->options.connectState = CONNECT_BEGIN;
    ssl->options.acceptState  = ACCEPT_BEGIN;
    ssl->options.handShakeState  = NULL_STATE;
    ssl->options.processReply = doProcessInit;
    ssl->options.asyncState = TLS_ASYNC_BEGIN;
    ssl->options.buildMsgState = BUILD_MSG_BEGIN;
    ssl->encrypt.state = CIPHER_STATE_BEGIN;
    ssl->decrypt.state = CIPHER_STATE_BEGIN;
#ifndef NO_DH
    #if !defined(WOLFSSL_OLD_PRIME_CHECK) && !defined(HAVE_FIPS) && \
        !defined(HAVE_SELFTEST)
        ssl->options.dhDoKeyTest = 1;
    #endif
#endif /* !NO_DH */

#ifdef WOLFSSL_DTLS
    #ifdef WOLFSSL_SCTP
        ssl->options.dtlsSctp           = ctx->dtlsSctp;
    #endif
    #ifdef WOLFSSL_SRTP
        ssl->dtlsSrtpProfiles           = ctx->dtlsSrtpProfiles;
    #endif
    #if defined(WOLFSSL_SCTP) || defined(WOLFSSL_DTLS_MTU)
        ssl->dtlsMtuSz                  = ctx->dtlsMtuSz;
    #endif
    ssl->dtls_timeout_init              = DTLS_TIMEOUT_INIT;
    ssl->dtls_timeout_max               = DTLS_TIMEOUT_MAX;
    ssl->dtls_timeout                   = ssl->dtls_timeout_init;

    ssl->buffers.dtlsCtx.rfd            = -1;
    ssl->buffers.dtlsCtx.wfd            = -1;

#ifdef WOLFSSL_RW_THREADED
    if (wc_InitRwLock(&ssl->buffers.dtlsCtx.peerLock) != 0)
        return BAD_MUTEX_E;
#endif

    ssl->IOCB_ReadCtx  = &ssl->buffers.dtlsCtx;  /* prevent invalid pointer access if not */
    ssl->IOCB_WriteCtx = &ssl->buffers.dtlsCtx;  /* correctly set */
#else
#ifdef HAVE_NETX
    ssl->IOCB_ReadCtx  = &ssl->nxCtx;  /* default NetX IO ctx, same for read */
    ssl->IOCB_WriteCtx = &ssl->nxCtx;  /* and write */
#elif defined(WOLFSSL_APACHE_MYNEWT) && !defined(WOLFSSL_LWIP)
    ssl->mnCtx = mynewt_ctx_new();
    if(!ssl->mnCtx) {
        return MEMORY_E;
    }
    ssl->IOCB_ReadCtx  = ssl->mnCtx;  /* default Mynewt IO ctx, same for read */
    ssl->IOCB_WriteCtx = ssl->mnCtx;  /* and write */
#elif defined (WOLFSSL_GNRC)
    ssl->IOCB_ReadCtx = ssl->gnrcCtx;
    ssl->IOCB_WriteCtx = ssl->gnrcCtx;
#else
    ssl->IOCB_ReadCtx  = &ssl->rfd;  /* prevent invalid pointer access if not */
    ssl->IOCB_WriteCtx = &ssl->wfd;  /* correctly set */
#endif
#endif /* WOLFSSL_DTLS */


#ifndef WOLFSSL_AEAD_ONLY
    #ifndef NO_OLD_TLS
        ssl->hmac = SSL_hmac; /* default to SSLv3 */
    #elif !defined(WOLFSSL_NO_TLS12) && !defined(NO_TLS)
      #if !defined(WOLFSSL_RENESAS_FSPSM_TLS) && \
          !defined(WOLFSSL_RENESAS_TSIP_TLS)
        ssl->hmac = TLS_hmac;
      #else
        ssl->hmac = Renesas_cmn_TLS_hmac;
      #endif
    #endif
#endif /* WOLFSSL_AEAD_ONLY */

#if defined(WOLFSSL_OPENVPN) && defined(HAVE_KEYING_MATERIAL)
    /* Save arrays by default for OpenVPN */
    ssl->options.saveArrays = 1;
#endif

    ssl->cipher.ssl = ssl;

#ifdef HAVE_EXTENDED_MASTER
    ssl->options.haveEMS = ctx->haveEMS;
#endif
    ssl->options.useClientOrder = ctx->useClientOrder;
    ssl->options.mutualAuth = ctx->mutualAuth;

#ifdef WOLFSSL_TLS13
    #if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_SERVER)
        ssl->options.maxTicketTls13 = ctx->maxTicketTls13;
    #endif
    #ifdef HAVE_SESSION_TICKET
        ssl->options.noTicketTls13  = ctx->noTicketTls13;
    #endif
    #if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
        ssl->options.noPskDheKe = ctx->noPskDheKe;
    #ifdef HAVE_SUPPORTED_CURVES
        ssl->options.onlyPskDheKe = ctx->onlyPskDheKe;
    #endif /* HAVE_SUPPORTED_CURVES */
    #endif /* HAVE_SESSION_TICKET || !NO_PSK */
    #if defined(WOLFSSL_POST_HANDSHAKE_AUTH)
        ssl->options.postHandshakeAuth = ctx->postHandshakeAuth;
        ssl->options.verifyPostHandshake = ctx->verifyPostHandshake;
    #endif

    if (ctx->numGroups > 0) {
        XMEMCPY(ssl->group, ctx->group, sizeof(*ctx->group) * ctx->numGroups);
        ssl->numGroups = ctx->numGroups;
    }

    #ifdef WOLFSSL_TLS13_MIDDLEBOX_COMPAT
        ssl->options.tls13MiddleBoxCompat = 1;
    #endif
#endif /* WOLFSSL_TLS13 */

#ifdef HAVE_TLS_EXTENSIONS
#ifdef HAVE_MAX_FRAGMENT
    ssl->max_fragment = MAX_RECORD_SIZE;
#endif
#ifdef HAVE_ALPN
    ssl->alpn_peer_requested = NULL;
    ssl->alpn_peer_requested_length = 0;
    #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)
        ssl->alpnSelect    = ctx->alpnSelect;
        ssl->alpnSelectArg = ctx->alpnSelectArg;
    #endif
    #if !defined(NO_BIO) && defined(OPENSSL_EXTRA)
        if (ctx->alpn_cli_protos != NULL && ctx->alpn_cli_protos_len > 0) {
            ret = wolfSSL_set_alpn_protos(ssl, ctx->alpn_cli_protos,
                                            ctx->alpn_cli_protos_len);
        #if defined(WOLFSSL_ERROR_CODE_OPENSSL)
            if (ret) {
        #else
            if (!ret) {
        #endif
                WOLFSSL_MSG("failed to set alpn protos to ssl object");
                return ret;
            }
        }
    #endif
#endif
#ifdef HAVE_SUPPORTED_CURVES
    ssl->options.userCurves = ctx->userCurves;
#endif
#endif /* HAVE_TLS_EXTENSIONS */

#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
    ssl->options.disallowEncThenMac = ctx->disallowEncThenMac;
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
    ssl->options.disableECH         = ctx->disableECH;
#endif

    /* default alert state (none) */
    ssl->alert_history.last_rx.code  = -1;
    ssl->alert_history.last_rx.level = -1;
    ssl->alert_history.last_tx.code  = -1;
    ssl->alert_history.last_tx.level = -1;

#ifdef WOLFSSL_SESSION_ID_CTX
    /* copy over application session context ID */
    ssl->sessionCtxSz = ctx->sessionCtxSz;
    XMEMCPY(ssl->sessionCtx, ctx->sessionCtx, ctx->sessionCtxSz);
#endif
#ifdef OPENSSL_EXTRA
    ssl->cbioFlag = ctx->cbioFlag;

    ssl->protoMsgCb  = ctx->protoMsgCb;
    ssl->protoMsgCtx = ctx->protoMsgCtx;

    /* follow default behavior of setting toInfoOn similar to
     * wolfSSL_set_msg_callback when the callback is set */
    if (ctx->protoMsgCb != NULL) {
        ssl->toInfoOn = 1;
    }

    ssl->disabledCurves = ctx->disabledCurves;
#endif
#if !defined(NO_WOLFSSL_CLIENT) && !defined(WOLFSSL_NO_TLS12) && \
    defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK)
    ssl->scr_check_enabled = 1;
#endif

    InitCiphers(ssl);
    InitCipherSpecs(&ssl->specs);

    /* all done with init, now can return errors, call other stuff */
    if ((ret = ReinitSSL(ssl, ctx, writeDup)) != 0) {
        WOLFSSL_MSG_EX("ReinitSSL failed. err = %d", ret);
        return ret;
    }

    if (!writeDup) {
#ifdef OPENSSL_EXTRA
        if ((ssl->param = (WOLFSSL_X509_VERIFY_PARAM*)XMALLOC(
                                    sizeof(WOLFSSL_X509_VERIFY_PARAM),
                                    ssl->heap, DYNAMIC_TYPE_OPENSSL)) == NULL) {
            WOLFSSL_MSG("ssl->param memory error");
            return MEMORY_E;
        }
        XMEMSET(ssl->param, 0, sizeof(WOLFSSL_X509_VERIFY_PARAM));

        /* pass on PARAM flags value from ctx to ssl */
        if (wolfSSL_X509_VERIFY_PARAM_set_flags(wolfSSL_get0_param(ssl),
            (unsigned long)wolfSSL_X509_VERIFY_PARAM_get_flags(
            wolfSSL_CTX_get0_param(ctx))) != WOLFSSL_SUCCESS) {
            WOLFSSL_MSG("ssl->param set flags error");
            return BAD_STATE_E;
        }
#endif

        if (ctx->suites == NULL) {
            /* suites */
            ret = AllocateCtxSuites(ctx);
            if (ret != 0)
                return ret;
            InitSSL_CTX_Suites(ctx);
        }
#ifdef OPENSSL_ALL
        ssl->suitesStack = NULL;
#endif
    } /* !writeDup */

    /* Initialize SSL with the appropriate fields from it's ctx */
    /* requires valid arrays and suites unless writeDup ing */
    if ((ret = SetSSL_CTX(ssl, ctx, writeDup)) != WOLFSSL_SUCCESS
#ifdef WOLFSSL_NO_INIT_CTX_KEY
        && ret != WC_NO_ERR_TRACE(NO_PRIVATE_KEY)
#endif
        ) {
        WOLFSSL_MSG_EX("SetSSL_CTX failed. err = %d", ret);
        return ret;
    }
    ssl->options.dtls = ssl->version.major == DTLS_MAJOR;


#ifdef WOLFSSL_DTLS13
    /* setup 0 (un-protected) epoch */
    ssl->dtls13Epochs[0].isValid = 1;
    ssl->dtls13Epochs[0].side = ENCRYPT_AND_DECRYPT_SIDE;
    ssl->dtls13EncryptEpoch = &ssl->dtls13Epochs[0];
    ssl->dtls13DecryptEpoch = &ssl->dtls13Epochs[0];
    ssl->options.dtls13SendMoreAcks = WOLFSSL_DTLS13_SEND_MOREACK_DEFAULT;
    ssl->dtls13Rtx.rtxRecordTailPtr = &ssl->dtls13Rtx.rtxRecords;

#ifdef WOLFSSL_RW_THREADED
    ret = wc_InitMutex(&ssl->dtls13Rtx.mutex);
    if (ret < 0) {
        return ret;
    }
#endif
#endif /* WOLFSSL_DTLS13 */

#ifdef HAVE_WRITE_DUP
    if (writeDup) {
        /* all done */
        return 0;
    }
#endif

    /* hsHashes */
    ret = InitHandshakeHashes(ssl);
    if (ret != 0) {
        WOLFSSL_MSG_EX("InitHandshakeHashes failed. err = %d", ret);
        return ret;
    }

#if defined(WOLFSSL_DTLS) && !defined(NO_WOLFSSL_SERVER)
    if (ssl->options.dtls && ssl->options.side == WOLFSSL_SERVER_END) {
        /* Initialize both in case we allow downgrading. */
        ret = wolfSSL_DTLS_SetCookieSecret(ssl, NULL, 0);
        if (ret != 0) {
            WOLFSSL_MSG("DTLS Cookie Secret error");
            return ret;
        }
    #if defined(WOLFSSL_DTLS13)
        if (IsAtLeastTLSv1_3(ssl->version)) {
        #if defined(WOLFSSL_SEND_HRR_COOKIE)
            ret = wolfSSL_send_hrr_cookie(ssl, NULL, 0);
            if (ret != WOLFSSL_SUCCESS) {
                WOLFSSL_MSG("DTLS1.3 Cookie secret error");
                return ret;
            }
        #endif /* WOLFSSL_SEND_HRR_COOKIE */
        #if defined(WOLFSSL_DTLS_CH_FRAG) && defined(WOLFSSL_HAVE_MLKEM)
            /* Allow fragmentation of the second ClientHello due to the
             * large PQC key share. */
            ret = wolfSSL_dtls13_allow_ch_frag(ssl, 1);
            if (ret != WOLFSSL_SUCCESS) {
                WOLFSSL_MSG("DTLS1.3 CH frag error");
                return ret;
            }
        #endif /* WOLFSSL_DTLS_CH_FRAG && WOLFSSL_HAVE_MLKEM */
        }
    #endif /* WOLFSSL_DTLS13 */
    }
#endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */

#ifdef HAVE_SECRET_CALLBACK
    ssl->sessionSecretCb  = NULL;
    ssl->sessionSecretCtx = NULL;
#ifdef WOLFSSL_TLS13
    ssl->tls13SecretCb  = NULL;
    ssl->tls13SecretCtx = NULL;
#endif
#endif
#if defined(OPENSSL_EXTRA) && defined(HAVE_SECRET_CALLBACK)
    if (ctx->keyLogCb != NULL) {
        ssl->keyLogCb = SessionSecret_callback;
#if defined(WOLFSSL_TLS13)
        ssl->tls13KeyLogCb = SessionSecret_callback_Tls13;
#endif /*WOLFSSL_TLS13*/
    }
#endif /*OPENSSL_EXTRA && HAVE_SECRET_CALLBACK */

    ssl->session = wolfSSL_NewSession(ssl->heap);
    if (ssl->session == NULL) {
        WOLFSSL_MSG_EX("SSL Session Memory error. wolfSSL_NewSession "
                       "err = %d", ret);
        return MEMORY_E;
    }
#ifdef HAVE_SESSION_TICKET
    ssl->options.noTicketTls12 = ctx->noTicketTls12;
#endif

#ifdef WOLFSSL_MULTICAST
    if (ctx->haveMcast) {
        int i;

        ssl->options.haveMcast = 1;
        ssl->options.mcastID = ctx->mcastID;

        /* Force the state to look like handshake has completed. */
        /* Keying material is supplied externally. */
        ssl->options.serverState = SERVER_FINISHED_COMPLETE;
        ssl->options.clientState = CLIENT_FINISHED_COMPLETE;
        ssl->options.connectState = SECOND_REPLY_DONE;
        ssl->options.acceptState = ACCEPT_THIRD_REPLY_DONE;
        ssl->options.handShakeState = HANDSHAKE_DONE;
        ssl->options.handShakeDone = 1;

        for (i = 0; i < WOLFSSL_DTLS_PEERSEQ_SZ; i++)
            ssl->keys.peerSeq[i].peerId = INVALID_PEER_ID;
    }
#endif

#if defined(HAVE_SECURE_RENEGOTIATION) || \
    defined(HAVE_SERVER_RENEGOTIATION_INFO)
    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        int useSecureReneg = ssl->ctx->useSecureReneg;
        /* use secure renegotiation by default (not recommend) */
    #if defined(WOLFSSL_SECURE_RENEGOTIATION_ON_BY_DEFAULT) || \
        (defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_NO_TLS12) && \
                !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK))
        useSecureReneg = 1;
    #endif
        if (useSecureReneg) {
            ret = wolfSSL_UseSecureRenegotiation(ssl);
            if (ret != WOLFSSL_SUCCESS)
                return ret;
        }
    }
#endif /* HAVE_SECURE_RENEGOTIATION */

#ifdef WOLFSSL_QUIC
    if (ctx->quic.method) {
        ret = wolfSSL_set_quic_method(ssl, ctx->quic.method);
        if (ret != WOLFSSL_SUCCESS)
            return WOLFSSL_FATAL_ERROR;
    }
#endif

#if defined(WOLFSSL_MAXQ10XX_TLS)
    ret = wolfSSL_maxq10xx_load_certificate(ssl);
    if (ret != WOLFSSL_SUCCESS)
        return ret;
#endif

#if defined(HAVE_SECRET_CALLBACK) && defined(SHOW_SECRETS) && \
    defined(WOLFSSL_SSLKEYLOGFILE) && defined(WOLFSSL_TLS13)
    (void)wolfSSL_set_tls13_secret_cb(ssl, tls13ShowSecrets, NULL);
#endif
#if defined(HAVE_SECRET_CALLBACK) && defined(SHOW_SECRETS)
    (void)wolfSSL_set_secret_cb(ssl, tlsShowSecrets, NULL);
#endif
#ifdef WOLFSSL_DUAL_ALG_CERTS
    ssl->sigSpec = ctx->sigSpec;
    ssl->sigSpecSz = ctx->sigSpecSz;
#endif /* WOLFSSL_DUAL_ALG_CERTS */
#ifdef HAVE_OCSP
#if defined(WOLFSSL_TLS13) && defined(HAVE_CERTIFICATE_STATUS_REQUEST)
    ssl->response_idx = 0;
#endif
#endif
#if defined(WOLFSSL_SYS_CRYPTO_POLICY)
    ssl->secLevel = ctx->secLevel;
#endif /* WOLFSSL_SYS_CRYPTO_POLICY */
    /* Returns 0 on success, not WOLFSSL_SUCCESS (1) */
    WOLFSSL_MSG("InitSSL done. return 0 (success)");
    return 0;
}


/* free use of temporary arrays */
void FreeArrays(WOLFSSL* ssl, int keep)
{
    if (ssl->arrays) {
        if (keep && !IsAtLeastTLSv1_3(ssl->version)) {
            /* keeps session id for user retrieval */
            XMEMCPY(ssl->session->sessionID, ssl->arrays->sessionID, ID_LEN);
            ssl->session->sessionIDSz = ssl->arrays->sessionIDSz;
        }
        if (ssl->arrays->preMasterSecret) {
            ForceZero(ssl->arrays->preMasterSecret, ENCRYPT_LEN);
            XFREE(ssl->arrays->preMasterSecret, ssl->heap, DYNAMIC_TYPE_SECRET);
            ssl->arrays->preMasterSecret = NULL;
        }
        XFREE(ssl->arrays->pendingMsg, ssl->heap, DYNAMIC_TYPE_ARRAYS);
        ssl->arrays->pendingMsg = NULL;
        ForceZero(ssl->arrays, sizeof(Arrays)); /* clear arrays struct */
    }
    XFREE(ssl->arrays, ssl->heap, DYNAMIC_TYPE_ARRAYS);
    ssl->arrays = NULL;
}

void FreeKey(WOLFSSL* ssl, int type, void** pKey)
{
    if (ssl && pKey && *pKey) {
        switch (type) {
        #ifndef NO_RSA
            case DYNAMIC_TYPE_RSA:
                wc_FreeRsaKey((RsaKey*)*pKey);
                break;
        #endif /* ! NO_RSA */
        #ifdef HAVE_ECC
            case DYNAMIC_TYPE_ECC:
            #if defined(WC_ECC_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
                defined(WC_ASYNC_ENABLE_ECC)
                if (((ecc_key*)*pKey)->nb_ctx != NULL) {
                    XFREE(((ecc_key*)*pKey)->nb_ctx, ((ecc_key*)*pKey)->heap,
                          DYNAMIC_TYPE_TMP_BUFFER);
                }
            #endif /* WC_ECC_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
                      WC_ASYNC_ENABLE_ECC */
                wc_ecc_free((ecc_key*)*pKey);
                break;
        #endif /* HAVE_ECC */
        #ifdef HAVE_ED25519
            case DYNAMIC_TYPE_ED25519:
                wc_ed25519_free((ed25519_key*)*pKey);
                break;
        #endif /* HAVE_ED25519 */
        #ifdef HAVE_CURVE25519
            case DYNAMIC_TYPE_CURVE25519:
            #if defined(WC_X25519_NONBLOCK) && \
                defined(WOLFSSL_ASYNC_CRYPT_SW)
                if (((curve25519_key*)*pKey)->nb_ctx != NULL) {
                    XFREE(((curve25519_key*)*pKey)->nb_ctx, ssl->heap,
                          DYNAMIC_TYPE_TMP_BUFFER);
                }
            #endif
                wc_curve25519_free((curve25519_key*)*pKey);
                break;
        #endif /* HAVE_CURVE25519 */
        #ifdef HAVE_ED448
            case DYNAMIC_TYPE_ED448:
                wc_ed448_free((ed448_key*)*pKey);
                break;
        #endif /* HAVE_ED448 */
        #ifdef HAVE_CURVE448
            case DYNAMIC_TYPE_CURVE448:
                wc_curve448_free((curve448_key*)*pKey);
                break;
        #endif /* HAVE_CURVE448 */
        #if defined(HAVE_FALCON)
            case DYNAMIC_TYPE_FALCON:
                wc_falcon_free((falcon_key*)*pKey);
                break;
        #endif /* HAVE_FALCON */
        #if defined(HAVE_DILITHIUM)
            case DYNAMIC_TYPE_DILITHIUM:
                wc_dilithium_free((dilithium_key*)*pKey);
                break;
        #endif /* HAVE_DILITHIUM */
        #ifndef NO_DH
            case DYNAMIC_TYPE_DH:
                wc_FreeDhKey((DhKey*)*pKey);
                break;
        #endif /* !NO_DH */
            default:
                break;
        }
        XFREE(*pKey, ssl->heap, type);

        /* Reset pointer */
        *pKey = NULL;
    }
}

int AllocKey(WOLFSSL* ssl, int type, void** pKey)
{
    int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
    size_t sz = 0;
#ifdef HAVE_ECC
    ecc_key* eccKey;
#endif /* HAVE_ECC */
#if defined(WC_ECC_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
    defined(WC_ASYNC_ENABLE_ECC)
    ecc_nb_ctx_t* eccNbCtx;
#endif /* WC_ECC_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW && WC_ASYNC_ENABLE_ECC */
#ifdef HAVE_CURVE25519
    curve25519_key* x25519Key;
#endif /* HAVE_CURVE25519 */
#if defined(WC_X25519_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW)
    x25519_nb_ctx_t* x25519NbCtx;
#endif /* WC_X25519_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW */

    if (ssl == NULL || pKey == NULL) {
        return BAD_FUNC_ARG;
    }

    /* Sanity check key destination */
    if (*pKey != NULL) {
        WOLFSSL_MSG("Key already present!");
    #ifdef WOLFSSL_ASYNC_CRYPT
        /* allow calling this again for async reentry */
        if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            return 0;
        }
    #endif
        return BAD_STATE_E;
    }

    /* Determine size */
    switch (type) {
    #ifndef NO_RSA
        case DYNAMIC_TYPE_RSA:
            sz = sizeof(RsaKey);
            break;
    #endif /* ! NO_RSA */
    #ifdef HAVE_ECC
        case DYNAMIC_TYPE_ECC:
            sz = sizeof(ecc_key);
            break;
    #endif /* HAVE_ECC */
    #ifdef HAVE_ED25519
        case DYNAMIC_TYPE_ED25519:
            sz = sizeof(ed25519_key);
            break;
    #endif /* HAVE_ED25519 */
    #ifdef HAVE_CURVE25519
        case DYNAMIC_TYPE_CURVE25519:
            sz = sizeof(curve25519_key);
            break;
    #endif /* HAVE_CURVE25519 */
    #ifdef HAVE_ED448
        case DYNAMIC_TYPE_ED448:
            sz = sizeof(ed448_key);
            break;
    #endif /* HAVE_ED448 */
    #ifdef HAVE_CURVE448
        case DYNAMIC_TYPE_CURVE448:
            sz = sizeof(curve448_key);
            break;
    #endif /* HAVE_CURVE448 */
    #if defined(HAVE_FALCON)
        case DYNAMIC_TYPE_FALCON:
            sz = sizeof(falcon_key);
            break;
    #endif /* HAVE_FALCON */
    #if defined(HAVE_DILITHIUM)
        case DYNAMIC_TYPE_DILITHIUM:
            sz = sizeof(dilithium_key);
            break;
    #endif /* HAVE_DILITHIUM */
    #ifndef NO_DH
        case DYNAMIC_TYPE_DH:
            sz = sizeof(DhKey);
            break;
    #endif /* !NO_DH */
        default:
            return BAD_FUNC_ARG;
    }

    /* Allocate memory for key */
    *pKey = (void *)XMALLOC(sz, ssl->heap, type);
    if (*pKey == NULL) {
        return MEMORY_E;
    }

    /* Initialize key */
    switch (type) {
    #ifndef NO_RSA
        case DYNAMIC_TYPE_RSA:
            ret = wc_InitRsaKey_ex((RsaKey*)*pKey, ssl->heap, ssl->devId);
            break;
    #endif /* ! NO_RSA */
    #ifdef HAVE_ECC
        case DYNAMIC_TYPE_ECC:
            eccKey = (ecc_key*)*pKey;
            ret = wc_ecc_init_ex(eccKey, ssl->heap, ssl->devId);
        #if defined(WC_ECC_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
            defined(WC_ASYNC_ENABLE_ECC)
            /* Only set non-blocking context when async device is active. With
             * INVALID_DEVID there is no async loop to retry on FP_WOULDBLOCK, so
             * let the WC_ECC_NONBLOCK_ONLY blocking fallback handle it instead. */
            if (ret == 0 && ssl->devId != INVALID_DEVID) {
                eccNbCtx = (ecc_nb_ctx_t*)XMALLOC(sizeof(ecc_nb_ctx_t),
                               eccKey->heap, DYNAMIC_TYPE_TMP_BUFFER);
                if (eccNbCtx == NULL) {
                    ret = MEMORY_E;
                }
                else {
                    ret = wc_ecc_set_nonblock(eccKey, eccNbCtx);
                    if (ret != 0) {
                        XFREE(eccNbCtx, eccKey->heap, DYNAMIC_TYPE_TMP_BUFFER);
                    }
                }
            }
        #endif /* WC_ECC_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
                  WC_ASYNC_ENABLE_ECC */
            break;
    #endif /* HAVE_ECC */
    #ifdef HAVE_ED25519
        case DYNAMIC_TYPE_ED25519:
            wc_ed25519_init_ex((ed25519_key*)*pKey, ssl->heap, ssl->devId);
            ret = 0;
            break;
    #endif /* HAVE_CURVE25519 */
    #ifdef HAVE_CURVE25519
        case DYNAMIC_TYPE_CURVE25519:
            x25519Key = (curve25519_key*)*pKey;
            ret = wc_curve25519_init_ex(x25519Key, ssl->heap, ssl->devId);
        #if defined(WC_X25519_NONBLOCK) && defined(WOLFSSL_ASYNC_CRYPT_SW) && \
            defined(WC_ASYNC_ENABLE_X25519)
            /* Only set non-blocking context when async device is active. With
             * INVALID_DEVID there is no async loop to retry on FP_WOULDBLOCK, so
             * skip non-blocking setup and use blocking mode instead. */
            if (ret == 0 && ssl->devId != INVALID_DEVID) {
                x25519NbCtx = (x25519_nb_ctx_t*)XMALLOC(sizeof(x25519_nb_ctx_t),
                                  ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
                if (x25519NbCtx == NULL) {
                    ret = MEMORY_E;
                }
                else {
                    ret = wc_curve25519_set_nonblock(x25519Key, x25519NbCtx);
                    if (ret != 0) {
                        XFREE(x25519NbCtx, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
                    }
                }
            }
        #endif /* WC_X25519_NONBLOCK && WOLFSSL_ASYNC_CRYPT_SW &&
                  WC_ASYNC_ENABLE_X25519 */
            break;
    #endif /* HAVE_CURVE25519 */
    #ifdef HAVE_ED448
        case DYNAMIC_TYPE_ED448:
            wc_ed448_init_ex((ed448_key*)*pKey, ssl->heap, ssl->devId);
            ret = 0;
            break;
    #endif /* HAVE_CURVE448 */
    #if defined(HAVE_FALCON)
        case DYNAMIC_TYPE_FALCON:
            wc_falcon_init_ex((falcon_key*)*pKey, ssl->heap, ssl->devId);
            ret = 0;
            break;
    #endif /* HAVE_FALCON */
    #if defined(HAVE_DILITHIUM)
        case DYNAMIC_TYPE_DILITHIUM:
            wc_dilithium_init_ex((dilithium_key*)*pKey, ssl->heap, ssl->devId);
            ret = 0;
            break;
    #endif /* HAVE_DILITHIUM */
    #ifdef HAVE_CURVE448
        case DYNAMIC_TYPE_CURVE448:
            wc_curve448_init((curve448_key*)*pKey);
            ret = 0;
            break;
    #endif /* HAVE_CURVE448 */
    #ifndef NO_DH
        case DYNAMIC_TYPE_DH:
            ret = wc_InitDhKey_ex((DhKey*)*pKey, ssl->heap, ssl->devId);
            break;
    #endif /* !NO_DH */
        default:
            return BAD_FUNC_ARG;
    }

    /* On error free handshake key */
    if (ret != 0) {
        FreeKey(ssl, type, pKey);
    }

    return ret;
}

#if !defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \
    defined(HAVE_CURVE25519) || defined(HAVE_ED448) || \
    defined(HAVE_CURVE448) || defined(HAVE_FALCON) || defined(HAVE_DILITHIUM)
static int ReuseKey(WOLFSSL* ssl, int type, void* pKey)
{
    int ret = 0;

    (void)ssl;

    switch (type) {
    #ifndef NO_RSA
        case DYNAMIC_TYPE_RSA:
            wc_FreeRsaKey((RsaKey*)pKey);
            ret = wc_InitRsaKey_ex((RsaKey*)pKey, ssl->heap, ssl->devId);
            break;
    #endif /* ! NO_RSA */
    #ifdef HAVE_ECC
        case DYNAMIC_TYPE_ECC:
            wc_ecc_free((ecc_key*)pKey);
            ret = wc_ecc_init_ex((ecc_key*)pKey, ssl->heap, ssl->devId);
            break;
    #endif /* HAVE_ECC */
    #ifdef HAVE_ED25519
        case DYNAMIC_TYPE_ED25519:
            wc_ed25519_free((ed25519_key*)pKey);
            ret = wc_ed25519_init_ex((ed25519_key*)pKey, ssl->heap,
                ssl->devId);
            break;
    #endif /* HAVE_CURVE25519 */
    #ifdef HAVE_CURVE25519
        case DYNAMIC_TYPE_CURVE25519:
            wc_curve25519_free((curve25519_key*)pKey);
            ret = wc_curve25519_init_ex((curve25519_key*)pKey, ssl->heap,
                ssl->devId);
            break;
    #endif /* HAVE_CURVE25519 */
    #ifdef HAVE_ED448
        case DYNAMIC_TYPE_ED448:
            wc_ed448_free((ed448_key*)pKey);
            ret = wc_ed448_init_ex((ed448_key*)pKey, ssl->heap, ssl->devId);
            break;
    #endif /* HAVE_CURVE448 */
    #ifdef HAVE_CURVE448
        case DYNAMIC_TYPE_CURVE448:
            wc_curve448_free((curve448_key*)pKey);
            ret = wc_curve448_init((curve448_key*)pKey);
            break;
    #endif /* HAVE_CURVE448 */
    #if defined(HAVE_FALCON)
        case DYNAMIC_TYPE_FALCON:
            wc_falcon_free((falcon_key*)pKey);
            ret = wc_falcon_init((falcon_key*)pKey);
            break;
    #endif /* HAVE_FALCON */
    #if defined(HAVE_DILITHIUM)
        case DYNAMIC_TYPE_DILITHIUM:
            wc_dilithium_free((dilithium_key*)pKey);
            ret = wc_dilithium_init((dilithium_key*)pKey);
            break;
    #endif /* HAVE_DILITHIUM */
    #ifndef NO_DH
        case DYNAMIC_TYPE_DH:
            wc_FreeDhKey((DhKey*)pKey);
            ret = wc_InitDhKey_ex((DhKey*)pKey, ssl->heap, ssl->devId);
            break;
    #endif /* !NO_DH */
        default:
            return BAD_FUNC_ARG;
    }

    return ret;
}
#endif

#ifdef WOLFSSL_ASYNC_IO
void FreeAsyncCtx(WOLFSSL* ssl, byte freeAsync)
{
    if (ssl->async != NULL) {
        if (ssl->async->freeArgs != NULL) {
            ssl->async->freeArgs(ssl, ssl->async->args);
            ssl->async->freeArgs = NULL;
        }
#if defined(WOLFSSL_ASYNC_CRYPT) && !defined(WOLFSSL_NO_TLS12)
        if (ssl->options.buildArgsSet) {
            FreeBuildMsgArgs(ssl, &ssl->async->buildArgs);
            ssl->options.buildArgsSet = 0;
        }
#endif
        if (freeAsync) {
            XFREE(ssl->async, ssl->heap, DYNAMIC_TYPE_ASYNC);
            ssl->async = NULL;
        }
    }
}
#endif

void FreeKeyExchange(WOLFSSL* ssl)
{
    /* Cleanup signature buffer */
    if (ssl->buffers.sig.buffer) {
        XFREE(ssl->buffers.sig.buffer, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
        ssl->buffers.sig.buffer = NULL;
        ssl->buffers.sig.length = 0;
    }

    /* Cleanup digest buffer */
    if (ssl->buffers.digest.buffer) {
        /* Only free if digest buffer was not set using SetDigest */
        if (!ssl->options.dontFreeDigest) {
            XFREE(ssl->buffers.digest.buffer, ssl->heap, DYNAMIC_TYPE_DIGEST);
        }
        ssl->buffers.digest.buffer = NULL;
        ssl->buffers.digest.length = 0;
        ssl->options.dontFreeDigest = 0;
    }

    /* Free handshake key */
    FreeKey(ssl, (int)ssl->hsType, &ssl->hsKey);
#ifdef WOLFSSL_DUAL_ALG_CERTS
    FreeKey(ssl, ssl->hsAltType, &ssl->hsAltKey);
#endif /* WOLFSSL_DUAL_ALG_CERTS */

#ifndef NO_DH
    /* Free temp DH key */
    FreeKey(ssl, DYNAMIC_TYPE_DH, (void**)&ssl->buffers.serverDH_Key);
#endif
}


/* Free up all memory used by Suites structure from WOLFSSL */
void FreeSuites(WOLFSSL* ssl)
{
#ifdef OPENSSL_EXTRA
    if (ssl->suitesStack != NULL) {
        /* Enough to free stack structure since WOLFSSL_CIPHER
         * isn't allocated separately. */
        wolfSSL_sk_SSL_CIPHER_free(ssl->suitesStack);
        ssl->suitesStack = NULL;
    }
    if (ssl->clSuitesStack != NULL) {
        /* Enough to free stack structure since WOLFSSL_CIPHER
         * isn't allocated separately. */
        wolfSSL_sk_SSL_CIPHER_free(ssl->clSuitesStack);
        ssl->clSuitesStack = NULL;
    }
    XFREE(ssl->clSuites, ssl->heap, DYNAMIC_TYPE_SUITES);
    ssl->clSuites = NULL;
#endif
    XFREE(ssl->suites, ssl->heap, DYNAMIC_TYPE_SUITES);
    ssl->suites = NULL;
}


/* In case holding SSL object in array and don't want to free actual ssl */
void wolfSSL_ResourceFree(WOLFSSL* ssl)
{
    /* Note: any resources used during the handshake should be released in the
     * function FreeHandshakeResources(). Be careful with the special cases
     * like the RNG which may optionally be kept for the whole session. (For
     * example with the RNG, it isn't used beyond the handshake except when
     * using stream ciphers where it is retained. */

    if (ssl->options.side == WOLFSSL_SERVER_END) {
        WOLFSSL_MSG("Free'ing server ssl");
    }
    else {
        WOLFSSL_MSG("Free'ing client ssl");
    }

#ifdef HAVE_EX_DATA_CLEANUP_HOOKS
    wolfSSL_CRYPTO_cleanup_ex_data(&ssl->ex_data);
#endif

    FreeCiphers(ssl);
    FreeArrays(ssl, 0);
    FreeKeyExchange(ssl);
#ifdef WOLFSSL_ASYNC_IO
    /* Cleanup async */
    FreeAsyncCtx(ssl, 1);
#endif
    if (ssl->options.weOwnRng) {
        wc_FreeRng(ssl->rng);
        XFREE(ssl->rng, ssl->heap, DYNAMIC_TYPE_RNG);
        ssl->rng = NULL;
        ssl->options.weOwnRng = 0;
    }
    FreeSuites(ssl);
    FreeHandshakeHashes(ssl);
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
    /* try to free the ech hashes in case we errored out */
    ssl->hsHashes = ssl->hsHashesEch;
    FreeHandshakeHashes(ssl);
#endif
    XFREE(ssl->buffers.domainName.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN);
    XFREE(ssl->buffers.ipasc.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN);

    /* clear keys struct after session */
    ForceZero(&ssl->keys, sizeof(Keys));

#ifdef WOLFSSL_TLS13
    ForceZero(&ssl->clientSecret, sizeof(ssl->clientSecret));
    ForceZero(&ssl->serverSecret, sizeof(ssl->serverSecret));

#if defined(HAVE_ECH)
    if (ssl->echConfigs != NULL) {
        FreeEchConfigs(ssl->echConfigs, ssl->heap);
        ssl->echConfigs = NULL;
    }
#endif /* HAVE_ECH */
#endif /* WOLFSSL_TLS13 */
#ifdef WOLFSSL_HAVE_TLS_UNIQUE
    ForceZero(&ssl->clientFinished, TLS_FINISHED_SZ_MAX);
    ForceZero(&ssl->serverFinished, TLS_FINISHED_SZ_MAX);
    ssl->serverFinished_len = 0;
    ssl->clientFinished_len = 0;
#endif
#ifndef NO_DH
    if (ssl->buffers.serverDH_Priv.buffer != NULL) {
        ForceZero(ssl->buffers.serverDH_Priv.buffer,
                                             ssl->buffers.serverDH_Priv.length);
    }
    XFREE(ssl->buffers.serverDH_Priv.buffer, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY);
    XFREE(ssl->buffers.serverDH_Pub.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    /* parameters (p,g) may be owned by ctx */
    if (ssl->buffers.weOwnDH) {
        XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    }
#endif /* !NO_DH */
#ifndef NO_CERTS
    ssl->keepCert = 0; /* make sure certificate is free'd */
    wolfSSL_UnloadCertsKeys(ssl);
#endif
#ifndef NO_RSA
    FreeKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey);
    ssl->peerRsaKeyPresent = 0;
#endif
#if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS)
    XFREE(ssl->peerSceTsipEncRsaKeyIndex, ssl->heap, DYNAMIC_TYPE_RSA);
    Renesas_cmn_Cleanup(ssl);
#endif
#ifndef NO_TLS
    if (ssl->buffers.inputBuffer.dynamicFlag)
        ShrinkInputBuffer(ssl, FORCED_FREE);
    if (ssl->buffers.outputBuffer.dynamicFlag)
        ShrinkOutputBuffer(ssl);
#endif
#ifdef WOLFSSL_THREADED_CRYPT
    {
        int i;
        for (i = 0; i < WOLFSSL_THREADED_CRYPT_CNT; i++) {
            bufferStatic* buff = &ssl->buffers.encrypt[i].buffer;

            ssl->buffers.encrypt[i].stop = 1;
            FreeCiphersSide(&ssl->buffers.encrypt[i].encrypt, ssl->heap);
            if (buff->dynamicFlag) {
                XFREE(buff->buffer - buff->offset, ssl->heap,
                    DYNAMIC_TYPE_OUT_BUFFER);
                buff->buffer      = buff->staticBuffer;
                buff->bufferSize  = STATIC_BUFFER_LEN;
                buff->offset      = 0;
                buff->dynamicFlag = 0;
            }
        }
    }
#endif
#if defined(WOLFSSL_SEND_HRR_COOKIE) && !defined(NO_WOLFSSL_SERVER)
    if (ssl->buffers.tls13CookieSecret.buffer != NULL) {
        ForceZero(ssl->buffers.tls13CookieSecret.buffer,
            ssl->buffers.tls13CookieSecret.length);
    }
    XFREE(ssl->buffers.tls13CookieSecret.buffer, ssl->heap,
          DYNAMIC_TYPE_COOKIE_PWD);
#endif
#ifdef WOLFSSL_DTLS
    DtlsMsgPoolReset(ssl);
    if (ssl->dtls_rx_msg_list != NULL) {
        DtlsMsgListDelete(ssl->dtls_rx_msg_list, ssl->heap);
        ssl->dtls_rx_msg_list = NULL;
        ssl->dtls_rx_msg_list_sz = 0;
    }
    XFREE(ssl->buffers.dtlsCtx.peer.sa, ssl->heap, DYNAMIC_TYPE_SOCKADDR);
    ssl->buffers.dtlsCtx.peer.sa = NULL;
#ifdef WOLFSSL_RW_THREADED
    wc_FreeRwLock(&ssl->buffers.dtlsCtx.peerLock);
#endif
#ifdef WOLFSSL_DTLS_CID
    XFREE(ssl->buffers.dtlsCtx.pendingPeer.sa, ssl->heap,
            DYNAMIC_TYPE_SOCKADDR);
    ssl->buffers.dtlsCtx.pendingPeer.sa = NULL;
#endif
#ifndef NO_WOLFSSL_SERVER
    if (ssl->buffers.dtlsCookieSecret.buffer != NULL) {
        ForceZero(ssl->buffers.dtlsCookieSecret.buffer,
            ssl->buffers.dtlsCookieSecret.length);
    }
    XFREE(ssl->buffers.dtlsCookieSecret.buffer, ssl->heap,
          DYNAMIC_TYPE_COOKIE_PWD);
#endif

#ifdef WOLFSSL_DTLS13
    if (ssl->dtls13ClientHello != NULL) {
        XFREE(ssl->dtls13ClientHello, ssl->heap, DYNAMIC_TYPE_DTLS_MSG);
        ssl->dtls13ClientHello = NULL;
        ssl->dtls13ClientHelloSz = 0;
    }
#endif /* WOLFSSL_DTLS13 */

#endif /* WOLFSSL_DTLS */
#ifdef OPENSSL_EXTRA
#ifndef NO_BIO
    /* Don't free if there was/is a previous element in the chain.
     * This means that this BIO was part of a chain that will be
     * free'd separately. */
    if (ssl->biord != ssl->biowr)        /* only free write if different */
        if (ssl->biowr != NULL && ssl->biowr->prev == NULL)
            wolfSSL_BIO_free(ssl->biowr);
    if (ssl->biord != NULL && ssl->biord->prev == NULL)
        wolfSSL_BIO_free(ssl->biord);
    ssl->biowr = NULL;
    ssl->biord = NULL;
#endif
#endif
#ifdef HAVE_LIBZ
    FreeStreams(ssl);
#endif
#ifdef HAVE_ECC
    FreeKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccKey);
    ssl->peerEccKeyPresent = 0;
    FreeKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccDsaKey);
    ssl->peerEccDsaKeyPresent = 0;
#endif
#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) ||defined(HAVE_CURVE448)
    {
        int dtype = 0;
    #ifdef HAVE_ECC
        dtype = DYNAMIC_TYPE_ECC;
    #endif
    #ifdef HAVE_CURVE25519
        if (ssl->peerX25519KeyPresent
    #ifdef HAVE_ECC
                           || ssl->eccTempKeyPresent == DYNAMIC_TYPE_CURVE25519
    #endif /* HAVE_ECC */
           )
        {
            dtype = DYNAMIC_TYPE_CURVE25519;
        }
    #endif /* HAVE_CURVE25519 */
    #ifdef HAVE_CURVE448
        if (ssl->peerX448KeyPresent
    #ifdef HAVE_ECC
                             || ssl->eccTempKeyPresent == DYNAMIC_TYPE_CURVE448
    #endif /* HAVE_ECC */
           )
        {
            dtype = DYNAMIC_TYPE_CURVE448;
        }
    #endif /* HAVE_CURVE448 */
        FreeKey(ssl, dtype, (void**)&ssl->eccTempKey);
        ssl->eccTempKeyPresent = 0;
    }
#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
#ifdef HAVE_CURVE25519
    FreeKey(ssl, DYNAMIC_TYPE_CURVE25519, (void**)&ssl->peerX25519Key);
    ssl->peerX25519KeyPresent = 0;
#endif
#ifdef HAVE_ED25519
    FreeKey(ssl, DYNAMIC_TYPE_ED25519, (void**)&ssl->peerEd25519Key);
    ssl->peerEd25519KeyPresent = 0;
    #ifdef HAVE_PK_CALLBACKS
        if (ssl->buffers.peerEd25519Key.buffer != NULL) {
            XFREE(ssl->buffers.peerEd25519Key.buffer, ssl->heap,
                                                          DYNAMIC_TYPE_ED25519);
            ssl->buffers.peerEd25519Key.buffer = NULL;
        }
    #endif
#endif
#ifdef HAVE_CURVE448
    FreeKey(ssl, DYNAMIC_TYPE_CURVE448, (void**)&ssl->peerX448Key);
    ssl->peerX448KeyPresent = 0;
#endif
#ifdef HAVE_ED448
    FreeKey(ssl, DYNAMIC_TYPE_ED448, (void**)&ssl->peerEd448Key);
    ssl->peerEd448KeyPresent = 0;
    #ifdef HAVE_PK_CALLBACKS
        if (ssl->buffers.peerEd448Key.buffer != NULL) {
            XFREE(ssl->buffers.peerEd448Key.buffer, ssl->heap,
                                                            DYNAMIC_TYPE_ED448);
            ssl->buffers.peerEd448Key.buffer = NULL;
        }
    #endif
#endif
#if defined(HAVE_DILITHIUM)
    FreeKey(ssl, DYNAMIC_TYPE_DILITHIUM, (void**)&ssl->peerDilithiumKey);
    ssl->peerDilithiumKeyPresent = 0;
#endif
#if defined(HAVE_FALCON)
    FreeKey(ssl, DYNAMIC_TYPE_FALCON, (void**)&ssl->peerFalconKey);
    ssl->peerFalconKeyPresent = 0;
#endif
#ifdef HAVE_PK_CALLBACKS
    #ifdef HAVE_ECC
        XFREE(ssl->buffers.peerEccDsaKey.buffer, ssl->heap, DYNAMIC_TYPE_ECC);
    #endif /* HAVE_ECC */
    #ifndef NO_RSA
        XFREE(ssl->buffers.peerRsaKey.buffer, ssl->heap, DYNAMIC_TYPE_RSA);
    #endif /* NO_RSA */
#endif /* HAVE_PK_CALLBACKS */
#ifdef HAVE_TLS_EXTENSIONS
#if !defined(NO_TLS)
    TLSX_FreeAll(ssl->extensions, ssl->heap);
#endif /* !NO_TLS */
#ifdef HAVE_ALPN
    if (ssl->alpn_peer_requested != NULL) {
        XFREE(ssl->alpn_peer_requested, ssl->heap, DYNAMIC_TYPE_ALPN);
        ssl->alpn_peer_requested = NULL;
        ssl->alpn_peer_requested_length = 0;
    }
#endif
#endif /* HAVE_TLS_EXTENSIONS */
#if defined(WOLFSSL_APACHE_MYNEWT) && !defined(WOLFSSL_LWIP)
    if (ssl->mnCtx) {
        mynewt_ctx_clear(ssl->mnCtx);
        ssl->mnCtx = NULL;
    }
#endif
#ifdef HAVE_NETX
    if (ssl->nxCtx.nxPacket)
        nx_packet_release(ssl->nxCtx.nxPacket);
#endif
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL)
    if (ssl->x509_store_pt)
        wolfSSL_X509_STORE_free(ssl->x509_store_pt);
#endif
#ifdef KEEP_PEER_CERT
    FreeX509(&ssl->peerCert);
#endif

    if (ssl->session != NULL)
        wolfSSL_FreeSession(ssl->ctx, ssl->session);
#ifdef HAVE_WRITE_DUP
    if (ssl->dupWrite) {
        FreeWriteDup(ssl);
    }
#endif
#ifdef OPENSSL_EXTRA
    XFREE(ssl->param, ssl->heap, DYNAMIC_TYPE_OPENSSL);
#endif
#if defined(HAVE_OCSP)
    {
        size_t i;
        for (i = 0; i < XELEM_CNT(ssl->ocspCsrResp); i++)
            XFREE(ssl->ocspCsrResp[i].buffer, NULL, 0);
        XMEMSET(ssl->ocspCsrResp, 0, sizeof(ssl->ocspCsrResp));
    }
#endif /* defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)) */
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
    while (ssl->certReqCtx != NULL) {
        CertReqCtx* curr = ssl->certReqCtx;
        ssl->certReqCtx = curr->next;
        XFREE(curr, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
    }
#endif
#ifdef WOLFSSL_STATIC_EPHEMERAL
    #ifndef NO_DH
    FreeDer(&ssl->staticKE.dhKey);
    #endif
    #ifdef HAVE_ECC
    FreeDer(&ssl->staticKE.ecKey);
    #endif
    #ifdef HAVE_CURVE25519
    FreeDer(&ssl->staticKE.x25519Key);
    #endif
    #ifdef HAVE_CURVE448
    FreeDer(&ssl->staticKE.x448Key);
    #endif
#endif

#ifdef WOLFSSL_STATIC_MEMORY
    /* check if using fixed io buffers and free them */
    if (ssl->heap != NULL) {
    #ifdef WOLFSSL_HEAP_TEST
    /* avoid dereferencing a test value */
    if (ssl->heap != (void*)WOLFSSL_HEAP_TEST) {
    #endif
        void* heap = ssl->ctx ? ssl->ctx->heap : ssl->heap;
    #ifndef WOLFSSL_STATIC_MEMORY_LEAN
        WOLFSSL_HEAP_HINT* ssl_hint = (WOLFSSL_HEAP_HINT*)ssl->heap;
        WOLFSSL_HEAP*      ctx_heap;

        ctx_heap = ssl_hint->memory;
    #ifndef SINGLE_THREADED
        if (wc_LockMutex(&(ctx_heap->memory_mutex)) != 0) {
            WOLFSSL_MSG("Bad memory_mutex lock");
        }
    #endif
        ctx_heap->curIO--;
        if (FreeFixedIO(ctx_heap, &(ssl_hint->outBuf)) != 1) {
            WOLFSSL_MSG("Error freeing fixed output buffer");
        }
        if (FreeFixedIO(ctx_heap, &(ssl_hint->inBuf)) != 1) {
            WOLFSSL_MSG("Error freeing fixed output buffer");
        }

        /* check if handshake count has been decreased*/
        if (ssl_hint->haFlag && ctx_heap->curHa > 0) {
            ctx_heap->curHa--;
        }
    #ifndef SINGLE_THREADED
        wc_UnLockMutex(&(ctx_heap->memory_mutex));
    #endif

        /* check if tracking stats */
        if (ctx_heap->flag & WOLFMEM_TRACK_STATS) {
            XFREE(ssl_hint->stats, heap, DYNAMIC_TYPE_SSL);
        }
    #endif /* !WOLFSSL_STATIC_MEMORY_LEAN */
        XFREE(ssl->heap, heap, DYNAMIC_TYPE_SSL);
    #ifdef WOLFSSL_HEAP_TEST
    }
    #endif
    }
#endif /* WOLFSSL_STATIC_MEMORY */
#ifdef OPENSSL_EXTRA
    /* Enough to free stack structure since WOLFSSL_CIPHER
     * isn't allocated separately. */
    wolfSSL_sk_CIPHER_free(ssl->supportedCiphers);
    wolfSSL_sk_X509_pop_free(ssl->peerCertChain, NULL);
    wolfSSL_sk_X509_pop_free(ssl->verifiedChain, NULL);
    #ifdef KEEP_OUR_CERT
    wolfSSL_sk_X509_pop_free(ssl->ourCertChain, NULL);
    #endif
#endif
#ifndef WOLFSSL_NO_CA_NAMES
    wolfSSL_sk_X509_NAME_pop_free(ssl->client_ca_names, NULL);
    ssl->client_ca_names = NULL;
    wolfSSL_sk_X509_NAME_pop_free(ssl->ca_names, NULL);
    ssl->ca_names = NULL;
    wolfSSL_sk_X509_NAME_pop_free(ssl->peer_ca_names, NULL);
    ssl->peer_ca_names = NULL;
#endif
#ifdef WOLFSSL_DTLS13
    Dtls13FreeFsmResources(ssl);

#ifdef WOLFSSL_RW_THREADED
    wc_FreeMutex(&ssl->dtls13Rtx.mutex);
#endif
#endif /* WOLFSSL_DTLS13 */
#ifdef WOLFSSL_QUIC
    wolfSSL_quic_free(ssl);
#endif
#if defined(WOLFSSL_HAPROXY)
    wolfSSL_CTX_free(ssl->initial_ctx);
    ssl->initial_ctx = NULL;
#endif
#ifdef WOLFSSL_DUAL_ALG_CERTS
    XFREE(ssl->peerSigSpec, ssl->heap, DYNAMIC_TYPE_TLSX);
#endif
}

/* Free any handshake resources no longer needed */
void FreeHandshakeResources(WOLFSSL* ssl)
{
    WOLFSSL_ENTER("FreeHandshakeResources");

#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        /* DTLS_POOL (DTLSv1.3 flushes the queue autonomously) */
        if(!IsAtLeastTLSv1_3(ssl->version)) {
            DtlsMsgPoolReset(ssl);
            DtlsMsgListDelete(ssl->dtls_rx_msg_list, ssl->heap);
            ssl->dtls_rx_msg_list = NULL;
            ssl->dtls_rx_msg_list_sz = 0;
        }
#ifdef WOLFSSL_DTLS13
        if (ssl->dtls13ClientHello != NULL) {
            XFREE(ssl->dtls13ClientHello, ssl->heap, DYNAMIC_TYPE_DTLS_MSG);
            ssl->dtls13ClientHello = NULL;
            ssl->dtls13ClientHelloSz = 0;
        }
#endif /* WOLFSSL_DTLS13 */
    }
#endif

#ifdef HAVE_SECURE_RENEGOTIATION
    if (ssl->secure_renegotiation && ssl->secure_renegotiation->enabled) {
        WOLFSSL_MSG("Secure Renegotiation needs to retain handshake resources");
        return;
    }
#endif

#ifndef NO_TLS
    /* input buffer */
    if (ssl->buffers.inputBuffer.dynamicFlag)
        ShrinkInputBuffer(ssl, NO_FORCED_FREE);
#endif

#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
    if (!ssl->options.tls1_3)
#endif
    {
    #ifndef OPENSSL_EXTRA
        /* free suites unless using compatibility layer */
        FreeSuites(ssl);
    #endif
        /* hsHashes */
        FreeHandshakeHashes(ssl);
    }

    /* RNG */
    if (ssl->options.tls1_1 == 0
#ifndef WOLFSSL_AEAD_ONLY
        || ssl->specs.cipher_type == stream
#endif
#if defined(WOLFSSL_TLS13)
    /* Post-handshake auth requires random on client side for TLS 1.3.
     * Session ticket requires random on server side.
     */
    #if !defined(WOLFSSL_POST_HANDSHAKE_AUTH) && !defined(HAVE_SESSION_TICKET)
        || ssl->options.tls1_3
    #elif !defined(WOLFSSL_POST_HANDSHAKE_AUTH) && defined(HAVE_SESSION_TICKET)
        || (ssl->options.tls1_3 && ssl->options.side == WOLFSSL_CLIENT_END)
    #elif !defined(HAVE_SESSION_TICKET)
        || (ssl->options.tls1_3 && ssl->options.side == WOLFSSL_SERVER_END)
    #endif
#endif
    ) {
        if (ssl->options.weOwnRng) {
            wc_FreeRng(ssl->rng);
            XFREE(ssl->rng, ssl->heap, DYNAMIC_TYPE_RNG);
            ssl->rng = NULL;
            ssl->options.weOwnRng = 0;
        }
    }

#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH) && \
                                                    defined(HAVE_SESSION_TICKET)
    if (!ssl->options.tls1_3)
#endif
        /* arrays */
        if (ssl->options.saveArrays == 0)
            FreeArrays(ssl, 1);

#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
    if (!ssl->options.tls1_3 || ssl->options.side == WOLFSSL_CLIENT_END)
#endif
    {
#ifndef NO_RSA
        /* peerRsaKey */
        FreeKey(ssl, DYNAMIC_TYPE_RSA, (void**)&ssl->peerRsaKey);
        ssl->peerRsaKeyPresent = 0;
#endif
#ifdef HAVE_ECC
        FreeKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccDsaKey);
        ssl->peerEccDsaKeyPresent = 0;
#endif /* HAVE_ECC */
#ifdef HAVE_ED25519
        FreeKey(ssl, DYNAMIC_TYPE_ED25519, (void**)&ssl->peerEd25519Key);
        ssl->peerEd25519KeyPresent = 0;
#endif /* HAVE_ED25519 */
#ifdef HAVE_ED448
        FreeKey(ssl, DYNAMIC_TYPE_ED448, (void**)&ssl->peerEd448Key);
        ssl->peerEd448KeyPresent = 0;
#endif /* HAVE_ED448 */
#if defined(HAVE_FALCON)
        FreeKey(ssl, DYNAMIC_TYPE_FALCON, (void**)&ssl->peerFalconKey);
        ssl->peerFalconKeyPresent = 0;
#endif /* HAVE_FALCON */
#if defined(HAVE_DILITHIUM)
        FreeKey(ssl, DYNAMIC_TYPE_DILITHIUM, (void**)&ssl->peerDilithiumKey);
        ssl->peerDilithiumKeyPresent = 0;
#endif /* HAVE_DILITHIUM */
    }

#ifdef HAVE_ECC
    FreeKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccKey);
    ssl->peerEccKeyPresent = 0;
#endif
#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
    {
        int dtype;
    #ifdef HAVE_ECC
        dtype = DYNAMIC_TYPE_ECC;
    #elif defined(HAVE_CURVE25519)
        dtype = DYNAMIC_TYPE_CURVE25519;
    #else
        dtype = DYNAMIC_TYPE_CURVE448;
    #endif
    #if defined(HAVE_ECC) && defined(HAVE_CURVE25519)
        if (ssl->peerX25519KeyPresent ||
                              ssl->eccTempKeyPresent == DYNAMIC_TYPE_CURVE25519)
         {
            dtype = DYNAMIC_TYPE_CURVE25519;
         }
    #endif
    #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519)) && \
                                                          defined(HAVE_CURVE448)
        if (ssl->peerX448KeyPresent ||
                                ssl->eccTempKeyPresent == DYNAMIC_TYPE_CURVE448)
         {
            dtype = DYNAMIC_TYPE_CURVE448;
         }
    #endif
        FreeKey(ssl, dtype, (void**)&ssl->eccTempKey);
        ssl->eccTempKeyPresent = 0;
    }
#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
#ifdef HAVE_CURVE25519
    FreeKey(ssl, DYNAMIC_TYPE_CURVE25519, (void**)&ssl->peerX25519Key);
    ssl->peerX25519KeyPresent = 0;
#endif
#ifdef HAVE_CURVE448
    FreeKey(ssl, DYNAMIC_TYPE_CURVE448, (void**)&ssl->peerX448Key);
    ssl->peerX448KeyPresent = 0;
#endif

#ifndef NO_DH
    if (ssl->buffers.serverDH_Priv.buffer) {
        ForceZero(ssl->buffers.serverDH_Priv.buffer,
                                             ssl->buffers.serverDH_Priv.length);
    }
    XFREE(ssl->buffers.serverDH_Priv.buffer, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY);
    ssl->buffers.serverDH_Priv.buffer = NULL;
    XFREE(ssl->buffers.serverDH_Pub.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    ssl->buffers.serverDH_Pub.buffer = NULL;
    /* parameters (p,g) may be owned by ctx */
    if (ssl->buffers.weOwnDH) {
        XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_G.buffer = NULL;
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
    }
#endif /* !NO_DH */

#if !defined(NO_CERTS) && !defined(OPENSSL_EXTRA) && \
    !defined(WOLFSSL_WPAS_SMALL)
#ifndef WOLFSSL_POST_HANDSHAKE_AUTH
    if (ssl->options.side != WOLFSSL_CLIENT_END)
#endif
    {
        wolfSSL_UnloadCertsKeys(ssl);
    }
#endif
#ifdef HAVE_PK_CALLBACKS
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_POST_HANDSHAKE_AUTH)
    if (!ssl->options.tls1_3 || ssl->options.side == WOLFSSL_CLIENT_END)
#endif
    {
    #ifdef HAVE_ECC
        XFREE(ssl->buffers.peerEccDsaKey.buffer, ssl->heap, DYNAMIC_TYPE_ECC);
        ssl->buffers.peerEccDsaKey.buffer = NULL;
    #endif /* HAVE_ECC */
    #ifndef NO_RSA
        XFREE(ssl->buffers.peerRsaKey.buffer, ssl->heap, DYNAMIC_TYPE_RSA);
        ssl->buffers.peerRsaKey.buffer = NULL;
    #endif /* NO_RSA */
    #ifdef HAVE_ED25519
        XFREE(ssl->buffers.peerEd25519Key.buffer, ssl->heap,
                                                          DYNAMIC_TYPE_ED25519);
        ssl->buffers.peerEd25519Key.buffer = NULL;
    #endif
    #ifdef HAVE_ED448
        XFREE(ssl->buffers.peerEd448Key.buffer, ssl->heap, DYNAMIC_TYPE_ED448);
        ssl->buffers.peerEd448Key.buffer = NULL;
    #endif
    }
#endif /* HAVE_PK_CALLBACKS */

#if defined(HAVE_TLS_EXTENSIONS) && !defined(NO_TLS)
#if !defined(HAVE_SNI) && !defined(HAVE_ALPN) && !defined(WOLFSSL_DTLS_CID) && \
    !defined(WOLFSSL_POST_HANDSHAKE_AUTH)
    /* Some extensions need to be kept for post-handshake querying. */
    TLSX_FreeAll(ssl->extensions, ssl->heap);
    ssl->extensions = NULL;
#else
#if !defined(NO_CERTS) && !defined(WOLFSSL_NO_SIGALG)
    TLSX_Remove(&ssl->extensions, TLSX_SIGNATURE_ALGORITHMS, ssl->heap);
#endif
    TLSX_Remove(&ssl->extensions, TLSX_EC_POINT_FORMATS, ssl->heap);
    TLSX_Remove(&ssl->extensions, TLSX_SUPPORTED_GROUPS, ssl->heap);
#ifdef WOLFSSL_TLS13
    TLSX_Remove(&ssl->extensions, TLSX_SUPPORTED_VERSIONS, ssl->heap);
    TLSX_Remove(&ssl->extensions, TLSX_KEY_SHARE, ssl->heap);
#endif
#endif /* !HAVE_SNI && && !HAVE_ALPN && !WOLFSSL_DTLS_CID &&
        * !WOLFSSL_POST_HANDSHAKE_AUTH */
#endif /* HAVE_TLS_EXTENSIONS && !NO_TLS */

#if defined(HAVE_OCSP)
    {
        size_t i;
        for (i = 0; i < XELEM_CNT(ssl->ocspCsrResp); i++)
            XFREE(ssl->ocspCsrResp[i].buffer, NULL, 0);
        XMEMSET(ssl->ocspCsrResp, 0, sizeof(ssl->ocspCsrResp));
    }
#endif /* defined(HAVE_OCSP) && (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY)) */

#ifdef WOLFSSL_STATIC_MEMORY
    /* when done with handshake decrement current handshake count */
    if (ssl->heap != NULL) {
    #ifdef WOLFSSL_HEAP_TEST
    /* avoid dereferencing a test value */
    if (ssl->heap != (void*)WOLFSSL_HEAP_TEST) {
    #endif
        WOLFSSL_HEAP_HINT* ssl_hint = (WOLFSSL_HEAP_HINT*)ssl->heap;
        WOLFSSL_HEAP*      ctx_heap;

        ctx_heap = ssl_hint->memory;
    #ifndef SINGLE_THREADED
        if (wc_LockMutex(&(ctx_heap->memory_mutex)) != 0) {
            WOLFSSL_MSG("Bad memory_mutex lock");
        }
    #endif
    #ifndef WOLFSSL_STATIC_MEMORY_LEAN
        if (ctx_heap->curHa > 0) {
            ctx_heap->curHa--;
        }
        ssl_hint->haFlag = 0; /* set to zero since handshake has been dec */
    #endif
    #ifndef SINGLE_THREADED
        wc_UnLockMutex(&(ctx_heap->memory_mutex));
    #endif
    #ifdef WOLFSSL_HEAP_TEST
    }
    #endif
    }
#endif /* WOLFSSL_STATIC_MEMORY */
}


/* heap argument is the heap hint used when creating SSL */
void FreeSSL(WOLFSSL* ssl, void* heap)
{
    WOLFSSL_CTX* ctx = ssl->ctx;
    wolfSSL_ResourceFree(ssl);
    XFREE(ssl, heap, DYNAMIC_TYPE_SSL);
    if (ctx)
        FreeSSL_Ctx(ctx); /* will decrement and free underlying CTX if 0 */
    (void)heap;
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Check(ssl, sizeof(*ssl));
#endif
}

#if !defined(NO_OLD_TLS) || defined(WOLFSSL_DTLS) || \
    !defined(WOLFSSL_NO_TLS12) || \
    ((defined(HAVE_CHACHA) || defined(HAVE_AESCCM) || defined(HAVE_AESGCM) || \
     defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)) \
     && defined(HAVE_AEAD))

#if defined(WOLFSSL_DTLS) || !defined(WOLFSSL_NO_TLS12)
static WC_INLINE void GetSEQIncrement(WOLFSSL* ssl, int verify, word32 seq[2])
{
    if (verify) {
        seq[0] = ssl->keys.peer_sequence_number_hi;
        seq[1] = ssl->keys.peer_sequence_number_lo++;
        if (seq[1] > ssl->keys.peer_sequence_number_lo) {
            /* handle rollover */
            ssl->keys.peer_sequence_number_hi++;
        }
    }
    else {
        seq[0] = ssl->keys.sequence_number_hi;
        seq[1] = ssl->keys.sequence_number_lo++;
        if (seq[1] > ssl->keys.sequence_number_lo) {
            /* handle rollover */
            ssl->keys.sequence_number_hi++;
        }
    }
}
#endif /* WOLFSSL_DTLS || !WOLFSSL_NO_TLS12 */


#ifdef WOLFSSL_DTLS
static WC_INLINE void DtlsGetSEQ(WOLFSSL* ssl, int order, word32 seq[2])
{
#ifdef HAVE_SECURE_RENEGOTIATION
    order = DtlsCheckOrder(ssl, order);
#endif
    if (order == PREV_ORDER) {
        /* Previous epoch case */
        if (ssl->options.haveMcast) {
        #ifdef WOLFSSL_MULTICAST
            seq[0] = (((word32)ssl->keys.dtls_epoch - 1) << 16) |
                     (ssl->options.mcastID << 8) |
                     (ssl->keys.dtls_prev_sequence_number_hi & 0xFF);
        #endif
        }
        else
            seq[0] = (((word32)ssl->keys.dtls_epoch - 1) << 16) |
                     (ssl->keys.dtls_prev_sequence_number_hi & 0xFFFF);
        seq[1] = ssl->keys.dtls_prev_sequence_number_lo;
    }
    else if (order == PEER_ORDER) {
        if (ssl->options.haveMcast) {
        #ifdef WOLFSSL_MULTICAST
            seq[0] = ((word32)ssl->keys.curEpoch << 16) |
                     (ssl->keys.curPeerId << 8) |
                     (ssl->keys.curSeq_hi & 0xFF);
        #endif
        }
        else
            seq[0] = ((word32)ssl->keys.curEpoch << 16) |
                     (ssl->keys.curSeq_hi & 0xFFFF);
        seq[1] = ssl->keys.curSeq_lo; /* explicit from peer */
    }
    else {
        if (ssl->options.haveMcast) {
        #ifdef WOLFSSL_MULTICAST
            seq[0] = ((word32)ssl->keys.dtls_epoch << 16) |
                     (ssl->options.mcastID << 8) |
                     (ssl->keys.dtls_sequence_number_hi & 0xFF);
        #endif
        }
        else
            seq[0] = ((word32)ssl->keys.dtls_epoch << 16) |
                     (ssl->keys.dtls_sequence_number_hi & 0xFFFF);
        seq[1] = ssl->keys.dtls_sequence_number_lo;
    }
}

static WC_INLINE void DtlsSEQIncrement(WOLFSSL* ssl, int order)
{
    word32 seq;
#ifdef HAVE_SECURE_RENEGOTIATION
    order = DtlsCheckOrder(ssl, order);
#endif

    if (order == PREV_ORDER) {
        seq = ssl->keys.dtls_prev_sequence_number_lo++;
        if (seq > ssl->keys.dtls_prev_sequence_number_lo) {
            /* handle rollover */
            ssl->keys.dtls_prev_sequence_number_hi++;
        }
    }
    else if (order == PEER_ORDER) {
        seq = ssl->keys.peer_sequence_number_lo++;
        if (seq > ssl->keys.peer_sequence_number_lo) {
            /* handle rollover */
            ssl->keys.peer_sequence_number_hi++;
        }
    }
    else {
        seq = ssl->keys.dtls_sequence_number_lo++;
        if (seq > ssl->keys.dtls_sequence_number_lo) {
            /* handle rollover */
            ssl->keys.dtls_sequence_number_hi++;
        }
    }
}
#endif /* WOLFSSL_DTLS */

#if defined(WOLFSSL_DTLS) || !defined(WOLFSSL_NO_TLS12)
void WriteSEQ(WOLFSSL* ssl, int verifyOrder, byte* out)
{
    word32 seq[2] = {0, 0};

    if (!ssl->options.dtls) {
        GetSEQIncrement(ssl, verifyOrder, seq);
    }
    else {
#ifdef WOLFSSL_DTLS
        DtlsGetSEQ(ssl, verifyOrder, seq);
#endif
    }

    c32toa(seq[0], out);
    c32toa(seq[1], out + OPAQUE32_LEN);
}
#endif /* WOLFSSL_DTLS || !WOLFSSL_NO_TLS12 */
#endif /* !NO_OLD_TLS || WOLFSSL_DTLS || !WOLFSSL_NO_TLS12 ||
        *     ((HAVE_CHACHA || HAVE_AESCCM || HAVE_AESGCM || WOLFSSL_SM4_GCM ||
        *       WOLFSSL_SM4_CCM) && HAVE_AEAD) */

#ifdef WOLFSSL_DTLS

/* functions for managing DTLS datagram reordering */

/* Need to allocate space for the handshake message header. The hashing
 * routines assume the message pointer is still within the buffer that
 * has the headers, and will include those headers in the hash. The store
 * routines need to take that into account as well. New will allocate
 * extra space for the headers. */
DtlsMsg* DtlsMsgNew(word32 sz, byte tx, void* heap)
{
    DtlsMsg* msg;
    WOLFSSL_ENTER("DtlsMsgNew");

    (void)heap;
    msg = (DtlsMsg*)XMALLOC(sizeof(DtlsMsg), heap, DYNAMIC_TYPE_DTLS_MSG);

    if (msg != NULL) {
        XMEMSET(msg, 0, sizeof(DtlsMsg));
        msg->sz = sz;
        msg->type = no_shake;
        if (tx) {
            msg->raw = msg->fullMsg =
                    (byte*)XMALLOC(sz + DTLS_HANDSHAKE_HEADER_SZ, heap,
                            DYNAMIC_TYPE_DTLS_FRAG);
            msg->ready = 1;
            if (msg->raw == NULL) {
                DtlsMsgDelete(msg, heap);
                msg = NULL;
            }
        }
    }

    return msg;
}

void DtlsMsgDelete(DtlsMsg* item, void* heap)
{
    (void)heap;
    WOLFSSL_ENTER("DtlsMsgDelete");

    if (item != NULL) {
        while (item->fragBucketList != NULL) {
            DtlsFragBucket* next = item->fragBucketList->m.m.next;
            DtlsMsgDestroyFragBucket(item->fragBucketList, heap);
            item->fragBucketList = next;
        }
        XFREE(item->raw, heap, DYNAMIC_TYPE_DTLS_FRAG);
        XFREE(item, heap, DYNAMIC_TYPE_DTLS_MSG);
    }
}


void DtlsMsgListDelete(DtlsMsg* head, void* heap)
{
    DtlsMsg* next;
    WOLFSSL_ENTER("DtlsMsgListDelete");
    while (head) {
        next = head->next;
        DtlsMsgDelete(head, heap);
        head = next;
    }
}

/**
 * Drop messages when they are no longer going to be retransmitted
 */
void DtlsTxMsgListClean(WOLFSSL* ssl)
{
    DtlsMsg* head = ssl->dtls_tx_msg_list;
    DtlsMsg* next;
    WOLFSSL_ENTER("DtlsTxMsgListClean");
    while (head) {
        next = head->next;
        if (VerifyForTxDtlsMsgDelete(ssl, head))
            DtlsMsgDelete(head, ssl->heap);
        else
            /* Stored packets should be in order so break on first failed
             * verify */
            break;
        ssl->dtls_tx_msg_list_sz--;
        head = next;
    }
    ssl->dtls_tx_msg_list = head;
}

static DtlsFragBucket* DtlsMsgCreateFragBucket(word32 offset, const byte* data,
                                               word32 dataSz, void* heap)
{
    DtlsFragBucket* bucket =
            (DtlsFragBucket*)XMALLOC(sizeof(DtlsFragBucket) + dataSz, heap,
                                     DYNAMIC_TYPE_DTLS_FRAG);
    if (bucket != NULL) {
        XMEMSET(bucket, 0, sizeof(*bucket));
        bucket->m.m.next = NULL;
        bucket->m.m.offset = offset;
        bucket->m.m.sz = dataSz;
        if (data != NULL)
            XMEMCPY(bucket->buf, data, dataSz);
    }
    (void)heap;
    return bucket;
}

void DtlsMsgDestroyFragBucket(DtlsFragBucket* fragBucket, void* heap)
{
    (void)heap;
    XFREE(fragBucket, heap, DYNAMIC_TYPE_DTLS_FRAG);
}

/*
 * data overlaps with cur but is before next.
 * data + dataSz has to end before or inside next. next can be NULL.
 */
static DtlsFragBucket* DtlsMsgCombineFragBuckets(DtlsMsg* msg,
        DtlsFragBucket* cur, DtlsFragBucket* next, word32 offset,
        const byte* data, word32 dataSz, void* heap)
{
    word32 offsetEnd = offset + dataSz;
    word32 newOffset = min(cur->m.m.offset, offset);
    word32 newOffsetEnd;
    word32 newSz;
    word32 overlapSz = cur->m.m.sz;
    DtlsFragBucket** chosenBucket;
    DtlsFragBucket* newBucket;
    DtlsFragBucket* otherBucket;
    byte combineNext = FALSE;

    if (next != NULL && offsetEnd >= next->m.m.offset)
        combineNext = TRUE;

    if (combineNext)
        newOffsetEnd = next->m.m.offset + next->m.m.sz;
    else
        newOffsetEnd = max(cur->m.m.offset + cur->m.m.sz, offsetEnd);

    newSz = newOffsetEnd - newOffset;

    /* Expand the larger bucket if data bridges the gap between cur and next */
    if (!combineNext || cur->m.m.sz >= next->m.m.sz) {
        chosenBucket = &cur;
        otherBucket = next;
    }
    else {
        chosenBucket = &next;
        otherBucket = cur;
    }

    {
#ifdef XREALLOC
        DtlsFragBucket* tmp = (DtlsFragBucket*)XREALLOC(*chosenBucket,
                sizeof(DtlsFragBucket) + newSz, heap, DYNAMIC_TYPE_DTLS_FRAG);
#else
        DtlsFragBucket* tmp = (DtlsFragBucket*)XMALLOC(
                sizeof(DtlsFragBucket) + newSz, heap, DYNAMIC_TYPE_DTLS_FRAG);
#endif
        if (tmp == NULL)
            return NULL;
#ifndef XREALLOC
        XMEMCPY(tmp, *chosenBucket, sizeof(DtlsFragBucket) +
                (*chosenBucket)->m.m.sz);
#endif
        if (chosenBucket == &next) {
            /* Update the link */
            DtlsFragBucket* beforeNext = cur;
            while (beforeNext->m.m.next != next)
                beforeNext = beforeNext->m.m.next;
            beforeNext->m.m.next = tmp;
        }
#ifndef XREALLOC
        XFREE(*chosenBucket, heap, DYNAMIC_TYPE_DTLS_FRAG);
#endif
        newBucket = *chosenBucket = tmp;
    }

    if (combineNext) {
        /* Put next first since it will always be at the end. Use memmove since
         * newBucket may be next. */
        XMEMMOVE(newBucket->buf + (next->m.m.offset - newOffset), next->buf,
                next->m.m.sz);
        /* memory after newOffsetEnd is already copied. Don't do extra work. */
        newOffsetEnd = next->m.m.offset;
    }

    if (newOffset == offset) {
        /* data comes first */
        if (newOffsetEnd <= offsetEnd) {
            /* data encompasses cur. only copy data */
            XMEMCPY(newBucket->buf, data,
                    min(dataSz, newOffsetEnd - newOffset));
        }
        else {
            /* data -> cur. memcpy as much possible as its faster. */
            word32 skipInCur = offsetEnd - cur->m.m.offset;
            XMEMMOVE(newBucket->buf + dataSz, cur->buf + skipInCur,
                    cur->m.m.sz - skipInCur);
            XMEMCPY(newBucket->buf, data, dataSz);
        }
    }
    else {
        /* cur -> data */
        word32 curOffsetEnd = cur->m.m.offset + cur->m.m.sz;
        if (newBucket != cur)
            XMEMCPY(newBucket->buf, cur->buf, cur->m.m.sz);
        XMEMCPY(newBucket->buf + cur->m.m.sz,
                data + (curOffsetEnd - offset),
                newOffsetEnd - curOffsetEnd);
    }
    /* FINALLY the newBucket is populated correctly */

    /* All buckets up to and including next (if combining) have to be free'd */
    {
        DtlsFragBucket* toFree = cur->m.m.next;
        while (toFree != next) {
            DtlsFragBucket* n = toFree->m.m.next;
            overlapSz += toFree->m.m.sz;
            DtlsMsgDestroyFragBucket(toFree, heap);
            msg->fragBucketListCount--;
            toFree = n;
        }
        if (combineNext) {
            newBucket->m.m.next = next->m.m.next;
            overlapSz += next->m.m.sz;
            DtlsMsgDestroyFragBucket(otherBucket, heap);
            msg->fragBucketListCount--;
        }
        else {
            newBucket->m.m.next = next;
        }
    }
    /* Adjust size in msg */
    msg->bytesReceived += newSz - overlapSz;
    newBucket->m.m.offset = newOffset;
    newBucket->m.m.sz = newSz;
    return newBucket;
}

static void DtlsMsgAssembleCompleteMessage(DtlsMsg* msg)
{
    DtlsHandShakeHeader* dtls;

    /* We have received all necessary fragments. Reconstruct the header. */
    if (msg->fragBucketListCount != 1 || msg->fragBucketList->m.m.offset != 0 ||
            msg->fragBucketList->m.m.sz != msg->sz) {
        WOLFSSL_MSG("Major error in fragment assembly logic");
        return;
    }

    /* Re-cycle the DtlsFragBucket as the buffer that holds the complete
     * handshake message and the header. */
    msg->raw = (byte*)msg->fragBucketList;
    msg->fullMsg = msg->fragBucketList->buf;
    msg->ready = 1;

    /* frag->padding makes sure we can fit the entire DTLS handshake header
     * before frag->buf */

    /* note the dtls pointer needs to be computed from msg->fragBucketList, not
     * from msg->fragBucketList->buf, to avoid a pointerOutOfBounds access
     * detected by cppcheck.
     *
     * also note, the (void *) intermediate cast is necessary to avoid a
     * potential -Wcast-align around alignment of DtlsHandShakeHeader exceeding
     * alignment of char.
     */
    dtls = (DtlsHandShakeHeader*)(void *)((char *)msg->fragBucketList
                                          + WC_OFFSETOF(DtlsFragBucket,buf)
                                          - DTLS_HANDSHAKE_HEADER_SZ);

    msg->fragBucketList = NULL;
    msg->fragBucketListCount = 0;

    dtls->type = msg->type;
    c32to24(msg->sz, dtls->length);
    c16toa((word16)msg->seq, dtls->message_seq);
    c32to24(0, dtls->fragment_offset);
    c32to24(msg->sz, dtls->fragment_length);
}

int DtlsMsgSet(DtlsMsg* msg, word32 seq, word16 epoch, const byte* data, byte type,
               word32 fragOffset, word32 fragSz, void* heap, word32 totalLen,
               byte encrypted)
{
    word32 fragOffsetEnd = fragOffset + fragSz;

    WOLFSSL_ENTER("DtlsMsgSet");

    if (msg == NULL || data == NULL || msg->sz != totalLen ||
            fragOffsetEnd > totalLen) {
        WOLFSSL_ERROR_VERBOSE(BAD_FUNC_ARG);
        return BAD_FUNC_ARG;
    }

    if (msg->ready)
        return 0; /* msg is already complete */

    if (msg->type != no_shake) {
        /* msg is already populated with the correct seq, epoch, and type */
        if (msg->type != type || msg->epoch != epoch || msg->seq != seq) {
            WOLFSSL_ERROR_VERBOSE(SEQUENCE_ERROR);
            return SEQUENCE_ERROR;
        }
        msg->encrypted = msg->encrypted && encrypted;
    }
    else {
        msg->type = type;
        msg->epoch = epoch;
        msg->seq = seq;
        msg->encrypted = encrypted;
    }

    if (msg->fragBucketList == NULL) {
        /* Clean list. Create first fragment. */
        msg->fragBucketList = DtlsMsgCreateFragBucket(fragOffset, data, fragSz, heap);
        if (msg->fragBucketList != NULL) {
            msg->bytesReceived = fragSz;
            msg->fragBucketListCount++;
        }
        else {
            return MEMORY_ERROR;
        }
    }
    else {
        /* See if we can expand any existing bucket to fit this new data into */
        DtlsFragBucket* prev = NULL;
        DtlsFragBucket* cur = msg->fragBucketList;
        byte done = 0;
        for (; cur != NULL; prev = cur, cur = cur->m.m.next) {
            word32 curOffset = cur->m.m.offset;
            word32 curEnd    = cur->m.m.offset + cur->m.m.sz;

            if (fragOffset >= curOffset && fragOffsetEnd <= curEnd) {
                /* We already have this fragment */
                done = 1;
                break;
            }
            else if (fragOffset <= curEnd) {
                /* found place to store fragment */
                break;
            }
        }
        if (!done) {
            if (cur == NULL) {
                /* We reached the end of the list. data is after and disjointed
                 * from anything we have received so far. */
                if (msg->fragBucketListCount >= DTLS_FRAG_POOL_SZ) {
                    WOLFSSL_ERROR_VERBOSE(DTLS_TOO_MANY_FRAGMENTS_E);
                    return DTLS_TOO_MANY_FRAGMENTS_E;
                }
                prev->m.m.next =
                        DtlsMsgCreateFragBucket(fragOffset, data, fragSz, heap);
                if (prev->m.m.next != NULL) {
                    msg->bytesReceived += fragSz;
                    msg->fragBucketListCount++;
                }
            }
            else if (fragOffsetEnd < cur->m.m.offset) {
                    /* Fragment is entirely before cur with a gap */
                    DtlsFragBucket** prev_next;
                    if (msg->fragBucketListCount >= DTLS_FRAG_POOL_SZ) {
                        WOLFSSL_ERROR_VERBOSE(DTLS_TOO_MANY_FRAGMENTS_E);
                        return DTLS_TOO_MANY_FRAGMENTS_E;
                    }
                    prev_next = prev != NULL
                            ? &prev->m.m.next : &msg->fragBucketList;
                    *prev_next = DtlsMsgCreateFragBucket(fragOffset, data,
                            fragSz, heap);
                    if (*prev_next != NULL) {
                        (*prev_next)->m.m.next = cur;
                        msg->bytesReceived += fragSz;
                        msg->fragBucketListCount++;
                    }
                    else {
                        /* reset on error */
                        *prev_next = cur;
                    }
            }
            else {
                /* Find if this fragment overlaps with any more */
                DtlsFragBucket* next = cur->m.m.next;
                DtlsFragBucket** prev_next = prev != NULL
                        ? &prev->m.m.next : &msg->fragBucketList;
                while (next != NULL &&
                        (next->m.m.offset + next->m.m.sz) <= fragOffsetEnd)
                    next = next->m.m.next;
                /* We can combine the buckets */
                *prev_next = DtlsMsgCombineFragBuckets(msg, cur, next,
                        fragOffset, data, fragSz, heap);
                if (*prev_next == NULL) /* reset on error */
                    *prev_next = cur;
            }
        }
    }

    if (msg->bytesReceived == msg->sz)
        DtlsMsgAssembleCompleteMessage(msg);

    return 0;
}


DtlsMsg* DtlsMsgFind(DtlsMsg* head, word16 epoch, word32 seq)
{
    WOLFSSL_ENTER("DtlsMsgFind");
    while (head != NULL && !(head->epoch == epoch && head->seq == seq)) {
        head = head->next;
    }
    return head;
}


void DtlsMsgStore(WOLFSSL* ssl, word16 epoch, word32 seq, const byte* data,
        word32 dataSz, byte type, word32 fragOffset, word32 fragSz, void* heap)
{
    /* See if seq exists in the list. If it isn't in the list, make
     * a new item of size dataSz, copy fragSz bytes from data to msg->msg
     * starting at offset fragOffset, and add fragSz to msg->fragSz. If
     * the seq is in the list and it isn't full, copy fragSz bytes from
     * data to msg->msg starting at offset fragOffset, and add fragSz to
     * msg->fragSz. Insertions take into account data already in the list
     * in case there are overlaps in the handshake message due to retransmit
     * messages. The new item should be inserted into the list in its
     * proper position.
     *
     * 1. Find seq in list, or where seq should go in list. If seq not in
     *    list, create new item and insert into list. Either case, keep
     *    pointer to item.
     * 2. Copy the data from the message to the stored message where it
     *    belongs without overlaps.
     */

    DtlsMsg* head = ssl->dtls_rx_msg_list;
    byte encrypted = ssl->keys.decryptedCur == 1;
    WOLFSSL_ENTER("DtlsMsgStore");

    if (head != NULL) {
        DtlsMsg* cur = DtlsMsgFind(head, epoch, seq);
        if (cur == NULL) {
            cur = DtlsMsgNew(dataSz, 0, heap);
            if (cur != NULL) {
                if (DtlsMsgSet(cur, seq, epoch, data, type,
                             fragOffset, fragSz, heap, dataSz, encrypted) < 0) {
                    DtlsMsgDelete(cur, heap);
                }
                else {
                    ssl->dtls_rx_msg_list_sz++;
                    head = DtlsMsgInsert(head, cur);
                }
            }
        }
        else {
            /* If this fails, the data is just dropped. */
            DtlsMsgSet(cur, seq, epoch, data, type, fragOffset,
                    fragSz, heap, dataSz, encrypted);
        }
    }
    else {
        head = DtlsMsgNew(dataSz, 0, heap);
        if (DtlsMsgSet(head, seq, epoch, data, type, fragOffset,
                    fragSz, heap, dataSz, encrypted) < 0) {
            DtlsMsgDelete(head, heap);
            head = NULL;
        }
        else {
            ssl->dtls_rx_msg_list_sz++;
        }
    }

    ssl->dtls_rx_msg_list = head;
}


/* DtlsMsgInsert() is an in-order insert. */
DtlsMsg* DtlsMsgInsert(DtlsMsg* head, DtlsMsg* item)
{
    WOLFSSL_ENTER("DtlsMsgInsert");
    if (head == NULL || (item->epoch <= head->epoch &&
                         item->seq   <  head->seq)) {
        item->next = head;
        head = item;
    }
    else if (head->next == NULL) {
        head->next = item;
    }
    else {
        DtlsMsg* cur = head->next;
        DtlsMsg* prev = head;
        while (cur) {
            if (item->epoch <= cur->epoch &&
                item->seq   <  cur->seq) {
                item->next = cur;
                prev->next = item;
                break;
            }
            prev = cur;
            cur = cur->next;
        }
        if (cur == NULL) {
            prev->next = item;
        }
    }

    return head;
}


/**
 * DtlsMsgPoolSave() adds the message to the end of the stored transmit
 * list. Must be called BEFORE BuildMessage or DtlsSEQIncrement or
 * anything else that increments ssl->keys.dtls_handshake_number.
 */
int DtlsMsgPoolSave(WOLFSSL* ssl, const byte* data, word32 dataSz,
                    enum HandShakeType type)
{
    DtlsMsg* item;
    int ret = 0;

    WOLFSSL_ENTER("DtlsMsgPoolSave");

    if (ssl->dtls_tx_msg_list_sz > DTLS_POOL_SZ) {
        WOLFSSL_ERROR(DTLS_POOL_SZ_E);
        return DTLS_POOL_SZ_E;
    }

    item = DtlsMsgNew(dataSz, 1, ssl->heap);

    if (item != NULL) {
        DtlsMsg* cur = ssl->dtls_tx_msg_list;

        XMEMCPY(item->raw, data, dataSz);
        item->epoch = ssl->keys.dtls_epoch;
        item->seq = ssl->keys.dtls_handshake_number;
        item->type = type;

        if (cur == NULL)
            ssl->dtls_tx_msg_list = item;
        else {
            while (cur->next)
                cur = cur->next;
            cur->next = item;
        }
        ssl->dtls_tx_msg_list_sz++;
    }
    else
        ret = MEMORY_E;

    WOLFSSL_LEAVE("DtlsMsgPoolSave()", ret);
    return ret;
}


/* DtlsMsgPoolTimeout() updates the timeout time. */
int DtlsMsgPoolTimeout(WOLFSSL* ssl)
{
    int result = -1;
    WOLFSSL_ENTER("DtlsMsgPoolTimeout");
    if (ssl->dtls_timeout <  ssl->dtls_timeout_max) {
        ssl->dtls_timeout *= DTLS_TIMEOUT_MULTIPLIER;
        result = 0;
    }
    WOLFSSL_LEAVE("DtlsMsgPoolTimeout()", result);
    return result;
}


/* DtlsMsgPoolReset() deletes the stored transmit list. */
void DtlsMsgPoolReset(WOLFSSL* ssl)
{
    WOLFSSL_ENTER("DtlsMsgPoolReset");
    if (ssl->dtls_tx_msg_list) {
        DtlsMsgListDelete(ssl->dtls_tx_msg_list, ssl->heap);
        ssl->dtls_tx_msg_list = NULL;
        ssl->dtls_tx_msg = NULL;
        ssl->dtls_tx_msg_list_sz = 0;
    }
#ifdef WOLFSSL_DTLS13
    /* Clear DTLS 1.3 buffer too */
    Dtls13RtxFlushBuffered(ssl, 1);
#endif
}


int VerifyForDtlsMsgPoolSend(WOLFSSL* ssl, byte type, word32 fragOffset)
{
    /**
     * only the first message from previous flight should be valid
     * to be used for triggering retransmission of whole DtlsMsgPool.
     * change cipher suite type is not verified here
     */
    if (fragOffset == 0) {
        if (ssl->options.side == WOLFSSL_SERVER_END) {
            if (type == client_hello)
                return 1;
            else if (ssl->options.verifyPeer && type == certificate)
                return 1;
            else if (!ssl->options.verifyPeer && type == client_key_exchange)
                return 1;
        }
        else {
            if (type == hello_request || type == server_hello)
                return 1;
        }
    }
    return 0;
}


/**
 * Verify if message `item` from `ssl->dtls_tx_msg_list` should be deleted
 * depending on the current state of the handshake negotiation.
 */
int VerifyForTxDtlsMsgDelete(WOLFSSL* ssl, DtlsMsg* item)
{
    WOLFSSL_ENTER("VerifyForTxDtlsMsgDelete");
    if (item->epoch < ssl->keys.dtls_epoch - 1)
        /* Messages not from current or previous epoch can be deleted */
        return 1;
    switch (ssl->options.side) {
    case WOLFSSL_CLIENT_END:
        if (item->type == client_hello &&
                ssl->options.serverState >= SERVER_HELLODONE_COMPLETE)
            return 1; /* client can forget first client_hello if received full
                       * flight of packets from server */
        else
            return 0;
    case WOLFSSL_SERVER_END:
        if (ssl->options.clientState >= CLIENT_HELLO_COMPLETE &&
                item->type == hello_request)
            return 1; /* Server can forget HelloRequest if client sent a valid
                       * ClientHello */
        if (ssl->options.clientState >= CLIENT_FINISHED_COMPLETE &&
                item->type <= server_hello_done)
            return 1; /* server can forget everything up to ServerHelloDone if
                       * a client finished message has been received and
                       * successfully processed */
        else
            return 0;
    default:
        return 0;
    }
}


/* DtlsMsgPoolSend() will send the stored transmit list. The stored list is
 * updated with new sequence numbers, and will be re-encrypted if needed. */
int DtlsMsgPoolSend(WOLFSSL* ssl, int sendOnlyFirstPacket)
{
    int ret = 0;
    DtlsMsg* pool;

    WOLFSSL_ENTER("DtlsMsgPoolSend");

    pool = ssl->dtls_tx_msg == NULL ? ssl->dtls_tx_msg_list : ssl->dtls_tx_msg;

    if (pool != NULL) {
        if ((ssl->options.side == WOLFSSL_SERVER_END &&
             !(ssl->options.acceptState == ACCEPT_BEGIN_RENEG ||
               ssl->options.acceptState == SERVER_HELLO_DONE ||
               ssl->options.acceptState == ACCEPT_FINISHED_DONE ||
               ssl->options.acceptState == ACCEPT_THIRD_REPLY_DONE)) ||
            (ssl->options.side == WOLFSSL_CLIENT_END &&
             !(ssl->options.connectState == CLIENT_HELLO_SENT ||
               ssl->options.connectState == HELLO_AGAIN_REPLY ||
               ssl->options.connectState == FINISHED_DONE ||
               ssl->options.connectState == SECOND_REPLY_DONE))) {

            WOLFSSL_ERROR(DTLS_RETX_OVER_TX);
            ssl->error = DTLS_RETX_OVER_TX;
            return WOLFSSL_FATAL_ERROR;
        }

        while (pool != NULL) {
            int epochOrder;

            if (pool->epoch == 0) {
                DtlsRecordLayerHeader* dtls;

                dtls = (DtlsRecordLayerHeader*)pool->raw;
                /* If the stored record's epoch is 0, and the currently set
                 * epoch is 0, use the "current order" sequence number.
                 * If the stored record's epoch is 0 and the currently set
                 * epoch is not 0, the stored record is considered a "previous
                 * order" sequence number. */
                epochOrder = (ssl->keys.dtls_epoch == 0) ?
                             CUR_ORDER : PREV_ORDER;

                WriteSEQ(ssl, epochOrder, dtls->sequence_number);
                DtlsSEQIncrement(ssl, epochOrder);
                if ((ret = CheckAvailableSize(ssl, (int)pool->sz)) != 0) {
                    WOLFSSL_ERROR(ret);
                    return ret;
                }

                XMEMCPY(GetOutputBuffer(ssl), pool->raw, pool->sz);
                ssl->buffers.outputBuffer.length += pool->sz;
            }
            else {
                /* Handle sending packets from previous epoch */
                byte*  input;
                byte*  output;
                int    inputSz, sendSz;

                input = pool->raw;
                inputSz = (int)pool->sz;
                sendSz = inputSz + cipherExtraData(ssl);

#ifdef HAVE_SECURE_RENEGOTIATION
                /*
                 * CUR_ORDER will use ssl->secure_renegotiation from epoch 2+.
                 * ssl->keys otherwise
                 * PREV_ORDER will always use ssl->keys
                 */
                if (DtlsSCRKeysSet(ssl)) {
                    if (pool->epoch == ssl->secure_renegotiation->tmp_keys.dtls_epoch)
                        epochOrder = CUR_ORDER;
                    else
                        epochOrder = PREV_ORDER;
                }
                else {
                    epochOrder = CUR_ORDER;
                }
#else
                epochOrder = CUR_ORDER;
#endif


                /* add back in record header space from saved pool size */
                sendSz += DTLS_RECORD_HEADER_SZ;

                if ((ret = CheckAvailableSize(ssl, sendSz)) != 0) {
                    WOLFSSL_ERROR(ret);
                    return ret;
                }

                output = GetOutputBuffer(ssl);
                if (inputSz != ENUM_LEN)
                    sendSz = BuildMessage(ssl, output, sendSz, input, inputSz,
                                          handshake, 0, 0, 0, epochOrder);
                else
                    /* inputSz == ENUM_LEN must mean that this is a change cipher
                     * spec message */
                    sendSz = BuildMessage(ssl, output, sendSz, input, inputSz,
                                          change_cipher_spec, 0, 0, 0, epochOrder);

                if (sendSz < 0) {
                    WOLFSSL_ERROR(BUILD_MSG_ERROR);
                    return BUILD_MSG_ERROR;
                }

                ssl->buffers.outputBuffer.length += sendSz;
            }


            if (!ssl->options.groupMessages)
                ret = SendBuffered(ssl);

            /**
             * on server side, retransmission is being triggered only by sending
             * first message of given flight, in order to trigger client
             * to retransmit its whole flight. Sending the whole previous flight
             * could lead to retransmission of previous client flight for each
             * server message from previous flight. Therefore one message should
             * be enough to do the trick.
             */
            if (sendOnlyFirstPacket &&
                ssl->options.side == WOLFSSL_SERVER_END)
                pool = NULL;
            else
                pool = pool->next;
            ssl->dtls_tx_msg = pool;
        }

        if (ret == 0 && ssl->options.groupMessages)
            ret = SendBuffered(ssl);
    }

    WOLFSSL_LEAVE("DtlsMsgPoolSend()", ret);
    return ret;
}

#endif /* WOLFSSL_DTLS */

#if defined(WOLFSSL_ALLOW_SSLV3) && !defined(NO_OLD_TLS)

ProtocolVersion MakeSSLv3(void)
{
    ProtocolVersion pv;
    pv.major = SSLv3_MAJOR;
    pv.minor = SSLv3_MINOR;

    return pv;
}

#endif /* WOLFSSL_ALLOW_SSLV3 && !NO_OLD_TLS */


#ifdef WOLFSSL_DTLS

ProtocolVersion MakeDTLSv1(void)
{
    ProtocolVersion pv;
    pv.major = DTLS_MAJOR;
    pv.minor = DTLS_MINOR;

    return pv;
}

#ifndef WOLFSSL_NO_TLS12

ProtocolVersion MakeDTLSv1_2(void)
{
    ProtocolVersion pv;
    pv.major = DTLS_MAJOR;
    pv.minor = DTLSv1_2_MINOR;

    return pv;
}

#endif /* !WOLFSSL_NO_TLS12 */

#ifdef WOLFSSL_DTLS13

ProtocolVersion MakeDTLSv1_3(void)
{
    ProtocolVersion pv;
    pv.major = DTLS_MAJOR;
    pv.minor = DTLSv1_3_MINOR;

    return pv;
}

#endif /* WOLFSSL_DTLS13 */
#endif /* WOLFSSL_DTLS */


#ifndef NO_ASN_TIME
#if defined(USER_TICKS)
#if 0
    word32 LowResTimer(void)
    {
        /*
        write your own clock tick function if don't want time(0)
        needs second accuracy but doesn't have to correlated to EPOCH
        */
    }
#endif

#elif defined(TIME_OVERRIDES)
#if !defined(NO_ASN) && !defined(NO_ASN_TIME)
    /* use same asn time overrides unless user wants tick override above */

    word32 LowResTimer(void)
    {
        return (word32) wc_Time(0);
    }
#else
    #ifndef HAVE_TIME_T_TYPE
        typedef long time_t;
    #endif
    extern time_t XTIME(time_t * timer);

    word32 LowResTimer(void)
    {
        return (word32) XTIME(0);
    }
#endif

#elif defined(USE_WINDOWS_API)

    word32 LowResTimer(void)
    {
        static int           init = 0;
        static LARGE_INTEGER freq;
        LARGE_INTEGER        count;

        if (!init) {
            QueryPerformanceFrequency(&freq);
            init = 1;
        }

        QueryPerformanceCounter(&count);

        return (word32)(count.QuadPart / freq.QuadPart);
    }

#elif defined(HAVE_RTP_SYS)

    #include "rtptime.h"

    word32 LowResTimer(void)
    {
        return (word32)rtp_get_system_sec();
    }

#elif defined(WOLFSSL_DEOS)

    word32 LowResTimer(void)
    {
        const word32 systemTickTimeInHz = 1000000 / systemTickInMicroseconds();
        const volatile word32 *systemTickPtr = systemTickPointer();

        return (word32) *systemTickPtr/systemTickTimeInHz;
    }

#elif defined(MICRIUM)

    word32 LowResTimer(void)
    {
        OS_TICK ticks = 0;
        OS_ERR  err;

        ticks = OSTimeGet(&err);

        return (word32) (ticks / OSCfg_TickRate_Hz);
    }


#elif defined(MICROCHIP_TCPIP_V5)

    word32 LowResTimer(void)
    {
        return (word32) (TickGet() / TICKS_PER_SECOND);
    }


#elif defined(MICROCHIP_TCPIP)

    #if defined(MICROCHIP_MPLAB_HARMONY)

        #include <system/tmr/sys_tmr.h>

        word32 LowResTimer(void)
        {
            return (word32) (SYS_TMR_TickCountGet() /
                             SYS_TMR_TickCounterFrequencyGet());
        }

    #else

        word32 LowResTimer(void)
        {
            return (word32) (SYS_TICK_Get() / SYS_TICK_TicksPerSecondGet());
        }

    #endif

#elif defined(FREESCALE_MQX) || defined(FREESCALE_KSDK_MQX)

    word32 LowResTimer(void)
    {
        TIME_STRUCT mqxTime;

        _time_get_elapsed(&mqxTime);

        return (word32) mqxTime.SECONDS;
    }
#elif defined(FREESCALE_FREE_RTOS) || defined(FREESCALE_KSDK_FREERTOS)

    #include "include/task.h"

    unsigned int LowResTimer(void)
    {
        return (unsigned int)(((float)xTaskGetTickCount())/configTICK_RATE_HZ);
    }

#elif defined(FREERTOS)

    #ifdef PLATFORMIO
        #include <freertos/FreeRTOS.h>
        #include <freertos/task.h>
    #else
        #include "task.h"
    #endif

    unsigned int LowResTimer(void)
    {
        return (unsigned int)(((float)xTaskGetTickCount())/configTICK_RATE_HZ);
    }

#elif defined(FREESCALE_KSDK_BM)

    #include "lwip/sys.h" /* lwIP */
    word32 LowResTimer(void)
    {
        return sys_now()/1000;
    }

#elif defined(WOLFSSL_CMSIS_RTOS) || defined(WOLFSSL_CMSIS_RTOSv2)

    word32 LowResTimer(void)
    {
        return (word32)osKernelGetTickCount() / 1000;
    }

#elif defined(WOLFSSL_TIRTOS)

    word32 LowResTimer(void)
    {
        return (word32) Seconds_get();
    }
#elif defined(WOLFSSL_XILINX)
    #include "xrtcpsu.h"

    word32 LowResTimer(void)
    {
        XRtcPsu_Config* con;
        XRtcPsu         rtc;

        con = XRtcPsu_LookupConfig(XPAR_XRTCPSU_0_DEVICE_ID);
        if (con != NULL) {
            if (XRtcPsu_CfgInitialize(&rtc, con, con->BaseAddr)
                    == XST_SUCCESS) {
                return (word32)XRtcPsu_GetCurrentTime(&rtc);
            }
            else {
                WOLFSSL_MSG("Unable to initialize RTC");
            }
        }

        return 0;
    }

#elif defined(WOLFSSL_UTASKER)

    word32 LowResTimer(void)
    {
        return (word32)(uTaskerSystemTick / TICK_RESOLUTION);
    }

#elif defined(WOLFSSL_NUCLEUS_1_2)

    #define NU_TICKS_PER_SECOND 100

    word32 LowResTimer(void)
    {
        /* returns number of 10ms ticks, so 100 ticks/sec */
        return NU_Retrieve_Clock() / NU_TICKS_PER_SECOND;
    }
#elif defined(WOLFSSL_APACHE_MYNEWT)

    #include "os/os_time.h"
    word32 LowResTimer(void)
    {
        word32 now;
        struct os_timeval tv;
        os_gettimeofday(&tv, NULL);
        now = (word32)tv.tv_sec;
        return now;
    }

#elif defined(WOLFSSL_ZEPHYR)

    word32 LowResTimer(void)
    {
        int64_t t;
    #if defined(CONFIG_ARCH_POSIX) && !defined(CONFIG_BOARD_NATIVE_POSIX)
        k_cpu_idle();
    #endif
        t = k_uptime_get(); /* returns current uptime in milliseconds */
        return (word32)(t / 1000);
    }

#elif defined(WOLFSSL_LINUXKM)
    word32 LowResTimer(void)
    {
        return (word32)time(NULL);
    }

#else
    /* Posix style time */
    #if !defined(USER_TIME) && !defined(USE_WOLF_TM)
    #include <time.h>
    #endif

    word32 LowResTimer(void)
    {
    #if !defined(NO_ASN) && !defined(NO_ASN_TIME)
        return (word32)wc_Time(0);
    #else
        return (word32)XTIME(0);
    #endif
    }
#endif
#else
    /* user must supply timer function to return elapsed seconds:
     *   word32 LowResTimer(void);
     */
#endif /* !NO_ASN_TIME */


#ifndef NO_TLS
#if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
               ((defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)) || \
                (defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \
                (defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)))
/* Store the message for use with CertificateVerify using EdDSA.
 *
 * ssl   SSL/TLS object.
 * data  Message to store.
 * sz    Size of message to store.
 * returns MEMORY_E if not able to reallocate, otherwise 0.
 */
static int EdDSA_Update(WOLFSSL* ssl, const byte* data, int sz)
{
    int   ret = 0;
    byte* msgs;

    if (ssl->options.cacheMessages) {
        msgs = (byte*)XMALLOC(ssl->hsHashes->length + sz, ssl->heap,
            DYNAMIC_TYPE_HASHES);
        if (msgs == NULL)
            ret = MEMORY_E;
        if ((ret == 0) && (ssl->hsHashes->messages != NULL)) {
            XMEMCPY(msgs, ssl->hsHashes->messages, ssl->hsHashes->length);
            ForceZero(ssl->hsHashes->messages, ssl->hsHashes->length);
            XFREE(ssl->hsHashes->messages, ssl->heap, DYNAMIC_TYPE_HASHES);
        }
        if (ret == 0) {
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Add("Handshake messages", msgs,
                ssl->hsHashes->length + sz);
        #endif
            ssl->hsHashes->messages = msgs;
            XMEMCPY(msgs + ssl->hsHashes->length, data, sz);
            ssl->hsHashes->prevLen = ssl->hsHashes->length;
            ssl->hsHashes->length += sz;
        }
    }

    return ret;
}
#endif /* (HAVE_ED25519 || HAVE_ED448) && !WOLFSSL_NO_CLIENT_AUTH */

int HashRaw(WOLFSSL* ssl, const byte* data, int sz)
{
    int ret = 0;
#ifdef WOLFSSL_DEBUG_TLS
    byte digest[WC_MAX_DIGEST_SIZE];

    WOLFSSL_MSG("HashRaw:");
    WOLFSSL_MSG("Data:");
    WOLFSSL_BUFFER(data, sz);
    WOLFSSL_MSG("Hashes:");
#endif

    (void)data;
    (void)sz;

    if (ssl->hsHashes == NULL) {
        return BAD_FUNC_ARG;
    }

#if defined(WOLFSSL_RENESAS_TSIP_TLS)
    ret = tsip_StoreMessage(ssl, data, sz);
    if (ret != 0 && ret != WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)) {
        return ret;
    }
#endif /* WOLFSSL_RENESAS_TSIP_TLS */

#if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                          defined(WOLFSSL_ALLOW_TLS_SHA1))
    wc_ShaUpdate(&ssl->hsHashes->hashSha, data, (word32)(sz));
#endif
#if !defined(NO_MD5) && !defined(NO_OLD_TLS)
    wc_Md5Update(&ssl->hsHashes->hashMd5, data, (word32)(sz));
#endif

    if (IsAtLeastTLSv1_2(ssl)) {
    #ifndef NO_SHA256
        ret = wc_Sha256Update(&ssl->hsHashes->hashSha256, data, (word32)sz);
        if (ret != 0)
            return ret;
    #ifdef WOLFSSL_DEBUG_TLS
        WOLFSSL_MSG("Sha256");
        wc_Sha256GetHash(&ssl->hsHashes->hashSha256, digest);
        WOLFSSL_BUFFER(digest, WC_SHA256_DIGEST_SIZE);
    #endif
    #endif
    #ifdef WOLFSSL_SHA384
        ret = wc_Sha384Update(&ssl->hsHashes->hashSha384, data, (word32)sz);
        if (ret != 0)
            return ret;
    #ifdef WOLFSSL_DEBUG_TLS
        WOLFSSL_MSG("Sha384");
        wc_Sha384GetHash(&ssl->hsHashes->hashSha384, digest);
        WOLFSSL_BUFFER(digest, WC_SHA384_DIGEST_SIZE);
    #endif
    #endif
    #ifdef WOLFSSL_SHA512
        ret = wc_Sha512Update(&ssl->hsHashes->hashSha512, data, (word32)sz);
        if (ret != 0)
            return ret;
    #ifdef WOLFSSL_DEBUG_TLS
        WOLFSSL_MSG("Sha512");
        wc_Sha512GetHash(&ssl->hsHashes->hashSha512, digest);
        WOLFSSL_BUFFER(digest, WC_SHA512_DIGEST_SIZE);
    #endif
    #endif
    #ifdef WOLFSSL_SM3
        ret = wc_Sm3Update(&ssl->hsHashes->hashSm3, data, sz);
        if (ret != 0)
            return ret;
    #ifdef WOLFSSL_DEBUG_TLS
        WOLFSSL_MSG("SM3");
        wc_Sm3GetHash(&ssl->hsHashes->hashSm3, digest);
        WOLFSSL_BUFFER(digest, WC_SM3_DIGEST_SIZE);
    #endif
    #endif
    #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
               ((defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)) || \
                (defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \
                (defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)))
        ret = EdDSA_Update(ssl, data, sz);
        if (ret != 0)
            return ret;
    #endif
    }

    return ret;
}

/* add output to md5 and sha handshake hashes, exclude record header */
int HashOutput(WOLFSSL* ssl, const byte* output, int sz, int ivSz)
{
    const byte* adj;

    if ((ssl->hsHashes == NULL) || (output == NULL))
        return BAD_FUNC_ARG;

    adj = output + RECORD_HEADER_SZ + ivSz;
    sz -= RECORD_HEADER_SZ;

#ifdef HAVE_FUZZER
    if (ssl->fuzzerCb)
        ssl->fuzzerCb(ssl, output, sz, FUZZ_HASH, ssl->fuzzerCtx);
#endif
#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        if (IsAtLeastTLSv1_3(ssl->version)) {
#ifdef WOLFSSL_DTLS13
            word16 dtls_record_extra;
            dtls_record_extra = Dtls13GetRlHeaderLength(ssl, (byte)IsEncryptionOn(ssl, 1));
            dtls_record_extra -= RECORD_HEADER_SZ;

            adj += dtls_record_extra;
            sz  -= dtls_record_extra;
#endif /* WOLFSSL_DTLS13 */
        } else {
#ifdef WOLFSSL_DTLS_CID
            byte cidSz = DtlsGetCidTxSize(ssl);
            if (IsEncryptionOn(ssl, 1) && cidSz > 0) {
                adj += cidSz;
                sz  -= cidSz + 1; /* +1 to not hash the real content type */
            }
#endif
            adj += DTLS_RECORD_EXTRA;
            sz  -= DTLS_RECORD_EXTRA;
        }
    }
#endif

    return HashRaw(ssl, adj, sz);
}


/* add input to md5 and sha handshake hashes, include handshake header */
int HashInput(WOLFSSL* ssl, const byte* input, int sz)
{
    const byte* adj;

    if (ssl->hsHashes == NULL) {
        return BAD_FUNC_ARG;
    }

    adj = input - HANDSHAKE_HEADER_SZ;
    sz += HANDSHAKE_HEADER_SZ;

#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        adj -= DTLS_HANDSHAKE_EXTRA;
        sz  += DTLS_HANDSHAKE_EXTRA;

#ifdef WOLFSSL_DTLS13
        if (IsAtLeastTLSv1_3(ssl->version))
            return Dtls13HashHandshake(ssl, adj, (word16)sz);
#endif /* WOLFSSL_DTLS13 */

    }
#endif

    return HashRaw(ssl, adj, sz);
}


/* add record layer header for message */
static void AddRecordHeader(byte* output, word32 length, byte type,
        WOLFSSL* ssl, int epochOrder)
{
    RecordLayerHeader* rl;

    (void)epochOrder;

    /* record layer header */
    rl = (RecordLayerHeader*)output;
    if (rl == NULL) {
        return;
    }
    rl->type    = type;
    rl->pvMajor = ssl->version.major;       /* type and version same in each */
#ifdef WOLFSSL_TLS13
    if (IsAtLeastTLSv1_3(ssl->version)) {
        rl->pvMinor = TLSv1_2_MINOR;
#ifdef WOLFSSL_DTLS
        if (ssl->options.dtls)
            rl->pvMinor = DTLSv1_2_MINOR;
#endif /* WOLFSSL_DTLS */
    }
    else
#endif
        rl->pvMinor = ssl->version.minor;

#ifdef WOLFSSL_ALTERNATIVE_DOWNGRADE
    if (ssl->options.side == WOLFSSL_CLIENT_END
    &&  ssl->options.connectState == CONNECT_BEGIN
    && !ssl->options.resuming) {
        rl->pvMinor = ssl->options.downgrade ? ssl->options.minDowngrade
                                             : ssl->version.minor;
    }
#endif

    if (!ssl->options.dtls) {
        c16toa((word16)length, rl->length);
    }
    else {
#ifdef WOLFSSL_DTLS
        /* dtls record layer header extensions */
        DtlsRecordLayerHeader* dtls = (DtlsRecordLayerHeader*)output;
#ifdef WOLFSSL_DTLS_CID
        byte cidSz = 0;
        if (type == dtls12_cid && (cidSz = DtlsGetCidTxSize(ssl)) > 0) {
            wolfSSL_dtls_cid_get_tx(ssl, output + DTLS12_CID_OFFSET, cidSz);
            c16toa((word16)length, output + DTLS12_CID_OFFSET + cidSz);
        }
        else
#endif
            c16toa((word16)length, dtls->length);
        WriteSEQ(ssl, epochOrder, dtls->sequence_number);
#endif
    }
}


#if !defined(WOLFSSL_NO_TLS12) || (defined(HAVE_SESSION_TICKET) && \
    !defined(NO_WOLFSSL_SERVER)) || \
    defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
    defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) || !defined(NO_CERTS)
/* add handshake header for message */
static void AddHandShakeHeader(byte* output, word32 length,
                               word32 fragOffset, word32 fragLength,
                               byte type, WOLFSSL* ssl)
{
    HandShakeHeader* hs;
    (void)fragOffset;
    (void)fragLength;
    (void)ssl;

    /* handshake header */
    hs = (HandShakeHeader*)output;
    if (hs == NULL)
        return;

    PRAGMA_GCC_DIAG_PUSH;
    PRAGMA_GCC("GCC diagnostic ignored \"-Wnull-dereference\"");
    hs->type = type;
    c32to24(length, hs->length);         /* type and length same for each */
    PRAGMA_GCC_DIAG_POP;
#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        DtlsHandShakeHeader* dtls;

        /* dtls handshake header extensions */
        dtls = (DtlsHandShakeHeader*)output;
        c16toa(ssl->keys.dtls_handshake_number++, dtls->message_seq);
        c32to24(fragOffset, dtls->fragment_offset);
        c32to24(fragLength, dtls->fragment_length);
    }
#endif
}

/* add both headers for handshake message */
WC_MAYBE_UNUSED static void AddHeaders(byte* output, word32 length, byte type, WOLFSSL* ssl)
{
    word32 lengthAdj = HANDSHAKE_HEADER_SZ;
    word32 outputAdj = RECORD_HEADER_SZ;

#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        lengthAdj += DTLS_HANDSHAKE_EXTRA;
        outputAdj += DTLS_RECORD_EXTRA;
    }
#endif

    AddRecordHeader(output, length + lengthAdj, handshake, ssl, CUR_ORDER);
    AddHandShakeHeader(output + outputAdj, length, 0, length, type, ssl);
}
#endif /* !WOLFSSL_NO_TLS12 || (HAVE_SESSION_TICKET && !NO_WOLFSSL_SERVER) ||
        * HAVE_CERTIFICATE_STATUS_REQUEST ||
        * HAVE_CERTIFICATE_STATUS_REQUEST_V2 || !NO_CERTS */


#if !defined(WOLFSSL_NO_TLS12) || (defined(WOLFSSL_DTLS) && defined(WOLFSSL_TLS13))
WC_MAYBE_UNUSED static void AddFragHeaders(byte* output, word32 fragSz,
        word32 fragOffset, word32 length, byte type, WOLFSSL* ssl)
{
    word32 lengthAdj = HANDSHAKE_HEADER_SZ;
    word32 outputAdj = RECORD_HEADER_SZ;
    (void)fragSz;

#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        lengthAdj += DTLS_HANDSHAKE_EXTRA;
        outputAdj += DTLS_RECORD_EXTRA;
    }
#endif

    AddRecordHeader(output, fragSz + lengthAdj, handshake, ssl, CUR_ORDER);
    AddHandShakeHeader(output + outputAdj, length, fragOffset, fragSz, type, ssl);
}
#endif /* !WOLFSSL_NO_TLS12 || (WOLFSSL_DTLS && WOLFSSL_TLS13) */

/**
 * Send the handshake message. This function handles fragmenting the message
 * so that it will fit into the desired MTU or the max fragment size.
 * @param ssl     Connection object
 * @param input   Input starting at the record layer header. This function
 *                assumes that the appropriate record and handshake headers
 *                are present. These headers must assume no fragmentation.
 *                That is handled here.
 * @param inputSz Length of message excluding headers (this is the total
 *                length of all fragments)
 * @param type    Type of message being sent
 * @return        0 on success and negative otherwise
 */
WC_MAYBE_UNUSED static int SendHandshakeMsg(WOLFSSL* ssl, byte* input,
        word32 inputSz, enum HandShakeType type, const char* packetName)
{
    int maxFrag;
    int ret = 0;
    int headerSz;
    int rHdrSz = 0; /* record header size */
    int hsHdrSz = 0; /* handshake header size */

    WOLFSSL_ENTER("SendHandshakeMsg");
    (void)type;
    (void)packetName;

    if (ssl == NULL || input == NULL)
        return BAD_FUNC_ARG;
#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        rHdrSz = DTLS_RECORD_HEADER_SZ;
        hsHdrSz = DTLS_HANDSHAKE_HEADER_SZ;
    }
    else
#endif
    {
        /* In TLS we send one handshake header in total, not one
         * per fragment like in DTLS. The handshake header should
         * already be in the input buffer. */
        inputSz += HANDSHAKE_HEADER_SZ;
        rHdrSz = RECORD_HEADER_SZ;
    }
    maxFrag = wolfssl_local_GetMaxPlaintextSize(ssl);
#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        /* In DTLS the handshake header is per fragment */
        maxFrag -= DTLS_HANDSHAKE_HEADER_SZ;
    }
#endif
    if (maxFrag <= 0 || maxFrag > MAX_RECORD_SIZE)
        return BUFFER_E;

    /* Make sure input is not the ssl output buffer as this
     * function doesn't handle that */
    if (input >= ssl->buffers.outputBuffer.buffer &&
            input < ssl->buffers.outputBuffer.buffer +
                     ssl->buffers.outputBuffer.bufferSize) {
        WOLFSSL_MSG("Can't use output buffer for input in SendHandshakeMsg");
        return BAD_FUNC_ARG;
    }
    if (!ssl->options.buildingMsg) {
        /* Hash it before the loop as we modify the input with
         * encryption on */
        ret = HashRaw(ssl, input + rHdrSz, (int)(inputSz) + hsHdrSz);
        if (ret != 0)
            return ret;
#ifdef WOLFSSL_DTLS
        /* Decrement msg number so that we continue to use the
         * same msg number for this msg */
        if (ssl->options.dtls)
            ssl->keys.dtls_handshake_number--;
#endif
    }
    headerSz = rHdrSz + hsHdrSz;
    while (ssl->fragOffset < inputSz) {
        byte* output;
        int outputSz;
        byte* data = input + ssl->fragOffset + headerSz;
        word32 fragSz = (word32)maxFrag;

        ssl->options.buildingMsg = 1;

        if (inputSz - ssl->fragOffset < fragSz)
            fragSz = inputSz - ssl->fragOffset;

        /* check for available size */
        if (fragSz > (word32)MAX_RECORD_SIZE)
            return BUFFER_E;
        outputSz = headerSz + (int)fragSz;
        if (IsEncryptionOn(ssl, 1))
            outputSz += cipherExtraData(ssl);
        if ((ret = CheckAvailableSize(ssl, outputSz)) != 0)
            return ret;
        if (ssl->buffers.outputBuffer.buffer == NULL)
            return MEMORY_E;
        output = GetOutputBuffer(ssl);

        if (IsEncryptionOn(ssl, 1)) {
            /* First we need to add the fragment header ourselves.
             * We do this in the input to minimize allocations */
            int dataSz = (int)fragSz;
#ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
                if (fragSz + DTLS_HANDSHAKE_HEADER_SZ > (word32)MAX_RECORD_SIZE)
                    return BUFFER_E;
                data   -= DTLS_HANDSHAKE_HEADER_SZ;
                dataSz += DTLS_HANDSHAKE_HEADER_SZ;
                AddHandShakeHeader(data, inputSz, ssl->fragOffset, fragSz,
                                   type, ssl);
                ssl->keys.dtls_handshake_number--;
            }
            if (IsDtlsNotSctpMode(ssl) &&
                (ret = DtlsMsgPoolSave(ssl, data,
                        fragSz + DTLS_HANDSHAKE_HEADER_SZ, type))
                    != 0)
                return ret;
#endif
            ret = BuildMessage(ssl, output, outputSz,
                        data, dataSz, handshake, 0, 0, 0, CUR_ORDER);
            if (ret >= 0)
                outputSz = ret;
            else
                return ret;
            ret = 0;
        }
        else {
#ifdef WOLFSSL_DTLS
            if (ssl->options.dtls)
                AddFragHeaders(output, fragSz, ssl->fragOffset,
                        inputSz, type, ssl);
            else
#endif
                AddRecordHeader(output, fragSz, handshake, ssl, CUR_ORDER);

            XMEMCPY(output + headerSz, data, fragSz);
#ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
                ssl->keys.dtls_handshake_number--;
                DtlsSEQIncrement(ssl, CUR_ORDER);
            }
            if (IsDtlsNotSctpMode(ssl)) {
                if ((ret = DtlsMsgPoolSave(ssl, output, headerSz + fragSz,
                        type)) != 0) {
                    return ret;
                }
            }
#endif
        }
        ssl->buffers.outputBuffer.length += (word32)outputSz;
#if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
        if (ssl->hsInfoOn) {
            AddPacketName(ssl, packetName);
        }
        if (ssl->toInfoOn) {
            ret = AddPacketInfo(ssl, packetName, handshake,
                output, outputSz, WRITE_PROTO, 0, ssl->heap);
            if (ret != 0)
                return ret;
        }
#endif
        ssl->fragOffset += fragSz;
        if (!ssl->options.groupMessages)
            ret = SendBuffered(ssl);
        if (ret != 0)
            return ret;
    }
#ifdef WOLFSSL_DTLS
    /* Increment msg number once we sent all fragments */
    if (ssl->options.dtls)
        ssl->keys.dtls_handshake_number++;
#endif
    ssl->fragOffset = 0;
    ssl->options.buildingMsg = 0;
    return ret;
}


/* return bytes received, WOLFSSL_FATAL_ERROR on error,
 * or BAD_FUNC_ARG if ssl is null */
static int wolfSSLReceive(WOLFSSL* ssl, byte* buf, word32 sz)
{
    int recvd;
    int retryLimit = WOLFSSL_MODE_AUTO_RETRY_ATTEMPTS;

    if (ssl == NULL) {
        return BAD_FUNC_ARG;
    }
#ifdef WOLFSSL_QUIC
    if (WOLFSSL_IS_QUIC(ssl)) {
        /* QUIC only "reads" from data provided by the application
         * via wolfSSL_provide_quic_data(). Transfer from there
         * into the inputBuffer. */
        return wolfSSL_quic_receive(ssl, buf, sz);
    }
#endif

    if (ssl->CBIORecv == NULL) {
        WOLFSSL_MSG("Your IO Recv callback is null, please set");
        return WOLFSSL_FATAL_ERROR;
    }

retry:
    recvd = ssl->CBIORecv(ssl, (char *)buf, (int)sz, ssl->IOCB_ReadCtx);
    if (recvd < 0) {
        switch (recvd) {
            case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_GENERAL):
                #ifdef WOLFSSL_APACHE_HTTPD
                #ifndef NO_BIO
                    if (ssl->biord) {
                        /* If retry and read flags are set, return WANT_READ */
                        if ((ssl->biord->flags & WOLFSSL_BIO_FLAG_READ) &&
                            (ssl->biord->flags & WOLFSSL_BIO_FLAG_RETRY)) {
                            return WANT_READ;
                        }
                    }
                #endif
                #endif
                return WOLFSSL_FATAL_ERROR;

            case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_READ):
                if (retryLimit > 0 && ssl->ctx->autoRetry &&
                        !ssl->options.handShakeDone && !ssl->options.dtls) {
                    retryLimit--;
                    goto retry;
                }
                return WC_NO_ERR_TRACE(WANT_READ);

            case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_CONN_RST):
                #ifdef USE_WINDOWS_API
                if (ssl->options.dtls) {
                    goto retry;
                }
                #endif
                ssl->options.connReset = 1;
                return WOLFSSL_FATAL_ERROR;

            case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_ISR): /* interrupt */
                /* see if we got our timeout */
                #ifdef WOLFSSL_CALLBACKS
                    if (ssl->toInfoOn) {
                        struct itimerval timeout;
                        getitimer(ITIMER_REAL, &timeout);
                        if (timeout.it_value.tv_sec == 0 &&
                                                timeout.it_value.tv_usec == 0) {
                            XSTRNCPY(ssl->timeoutInfo.timeoutName,
                                    "recv() timeout", MAX_TIMEOUT_NAME_SZ);
                            ssl->timeoutInfo.timeoutName[
                                MAX_TIMEOUT_NAME_SZ] = '\0';

                            WOLFSSL_MSG("Got our timeout");
                            return WANT_READ;
                        }
                    }
                #endif
                return WOLFSSL_FATAL_ERROR;

            case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_CONN_CLOSE):
                ssl->options.isClosed = 1;
                return WOLFSSL_FATAL_ERROR;

            case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_TIMEOUT):
            #ifdef WOLFSSL_DTLS
#ifdef WOLFSSL_DTLS13
                if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
                    /* TODO: support WANT_WRITE here */
                    if (Dtls13RtxTimeout(ssl) < 0) {
                        WOLFSSL_MSG(
                            "Error trying to retransmit DTLS buffered message");
                        return WOLFSSL_FATAL_ERROR;
                    }
                    goto retry;
                }
#endif /* WOLFSSL_DTLS13 */

                if (IsDtlsNotSctpMode(ssl) &&
                    ssl->options.handShakeState != HANDSHAKE_DONE &&
                    DtlsMsgPoolTimeout(ssl) == 0 &&
                    DtlsMsgPoolSend(ssl, 0) == 0) {

                    /* retry read for DTLS during handshake only */
                    goto retry;
                }
            #endif
                return WOLFSSL_FATAL_ERROR;

            default:
                WOLFSSL_MSG("Unexpected recv return code");
                return recvd;
        }
    }

    return recvd;
}


/* Switch dynamic output buffer back to static, buffer is assumed clear */
void ShrinkOutputBuffer(WOLFSSL* ssl)
{
    WOLFSSL_MSG("Shrinking output buffer");
    XFREE(ssl->buffers.outputBuffer.buffer - ssl->buffers.outputBuffer.offset,
          ssl->heap, DYNAMIC_TYPE_OUT_BUFFER);
    ssl->buffers.outputBuffer.buffer = ssl->buffers.outputBuffer.staticBuffer;
    ssl->buffers.outputBuffer.bufferSize  = STATIC_BUFFER_LEN;
    ssl->buffers.outputBuffer.dynamicFlag = 0;
    ssl->buffers.outputBuffer.offset      = 0;
    /* idx and length are assumed to be 0. */
}


/* Switch dynamic input buffer back to static, keep any remaining input */
/* forced free means cleaning up */
/* Be *CAREFUL* where this function is called. ProcessReply relies on
 * inputBuffer.idx *NOT* changing inside the ProcessReply function. ProcessReply
 * calls ShrinkInputBuffer itself when it is safe to do so. Don't overuse it. */
void ShrinkInputBuffer(WOLFSSL* ssl, int forcedFree)
{
    int usedLength = (int)(ssl->buffers.inputBuffer.length -
                     ssl->buffers.inputBuffer.idx);
    if (!forcedFree && (usedLength > STATIC_BUFFER_LEN ||
            ssl->buffers.clearOutputBuffer.length > 0))
        return;

    WOLFSSL_MSG("Shrinking input buffer");

    if (!forcedFree && usedLength > 0) {
        XMEMCPY(ssl->buffers.inputBuffer.staticBuffer,
               ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx,
               (size_t)(usedLength));
    }

    ForceZero(ssl->buffers.inputBuffer.buffer,
        ssl->buffers.inputBuffer.bufferSize);
    XFREE(ssl->buffers.inputBuffer.buffer - ssl->buffers.inputBuffer.offset,
          ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
    ssl->buffers.inputBuffer.buffer = ssl->buffers.inputBuffer.staticBuffer;
    ssl->buffers.inputBuffer.bufferSize  = STATIC_BUFFER_LEN;
    ssl->buffers.inputBuffer.dynamicFlag = 0;
    ssl->buffers.inputBuffer.offset      = 0;
    ssl->buffers.inputBuffer.idx = 0;
    ssl->buffers.inputBuffer.length = (word32)usedLength;
}

int SendBuffered(WOLFSSL* ssl)
{
    int retryLimit = WOLFSSL_MODE_AUTO_RETRY_ATTEMPTS;

    if (ssl == NULL) {
        WOLFSSL_MSG("ssl is null");
        return BAD_FUNC_ARG;
    }

    if (ssl->CBIOSend == NULL && !WOLFSSL_IS_QUIC(ssl)) {
        WOLFSSL_MSG("Your IO Send callback is null, please set");
        return SOCKET_ERROR_E;
    }

#ifdef WOLFSSL_DEBUG_TLS
    if (ssl->buffers.outputBuffer.idx == 0) {
        WOLFSSL_MSG("Data to send");
        WOLFSSL_BUFFER(ssl->buffers.outputBuffer.buffer,
                       ssl->buffers.outputBuffer.length);
    }
#endif

#ifdef WOLFSSL_QUIC
    if (WOLFSSL_IS_QUIC(ssl)) {
        return wolfSSL_quic_send(ssl);
    }
#endif

    while (ssl->buffers.outputBuffer.length > 0) {
        int sent = 0;
retry:
        sent = ssl->CBIOSend(ssl,
                             (char*)ssl->buffers.outputBuffer.buffer +
                             ssl->buffers.outputBuffer.idx,
                             (int)ssl->buffers.outputBuffer.length,
                             ssl->IOCB_WriteCtx);
        if (sent < 0) {
            switch (sent) {

                case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_WANT_WRITE):
                    if (retryLimit > 0 && ssl->ctx->autoRetry &&
                            !ssl->options.handShakeDone && !ssl->options.dtls) {
                        retryLimit--;
                        goto retry;
                    }
                    return WC_NO_ERR_TRACE(WANT_WRITE);

                case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_CONN_RST):
                    ssl->options.connReset = 1;
                    break;

                case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_ISR): /* interrupt */
                    /* see if we got our timeout */
                    #ifdef WOLFSSL_CALLBACKS
                        if (ssl->toInfoOn) {
                            struct itimerval timeout;
                            getitimer(ITIMER_REAL, &timeout);
                            if (timeout.it_value.tv_sec == 0 &&
                                                timeout.it_value.tv_usec == 0) {
                                XSTRNCPY(ssl->timeoutInfo.timeoutName,
                                        "send() timeout", MAX_TIMEOUT_NAME_SZ);
                                ssl->timeoutInfo.timeoutName[
                                    MAX_TIMEOUT_NAME_SZ] = '\0';

                                WOLFSSL_MSG("Got our timeout");
                                return WANT_WRITE;
                            }
                        }
                    #endif
                    continue;

                case WC_NO_ERR_TRACE(WOLFSSL_CBIO_ERR_CONN_CLOSE): /* epipe */
                    ssl->options.connReset = 1;  /* treat same as reset */
                    break;

                default:
                    return SOCKET_ERROR_E;
            }

            return SOCKET_ERROR_E;
        }

        if (sent > (int)ssl->buffers.outputBuffer.length) {
            WOLFSSL_MSG("SendBuffered() out of bounds read");
            return SEND_OOB_READ_E;
        }

        ssl->buffers.outputBuffer.idx += (word32)sent;
        ssl->buffers.outputBuffer.length -= (word32)sent;
    }

    ssl->buffers.outputBuffer.idx = 0;

    if (ssl->buffers.outputBuffer.dynamicFlag)
        ShrinkOutputBuffer(ssl);

    return 0;
}

#ifdef WOLFSSL_THREADED_CRYPT
static WC_INLINE int GrowAnOutputBuffer(WOLFSSL* ssl,
    bufferStatic* outputBuffer, int size)
{
    byte* tmp;
#if WOLFSSL_GENERAL_ALIGNMENT > 0
    byte  hdrSz = ssl->options.dtls ? DTLS_RECORD_HEADER_SZ :
                                      RECORD_HEADER_SZ;
    byte align = WOLFSSL_GENERAL_ALIGNMENT;
#else
    const byte align = WOLFSSL_GENERAL_ALIGNMENT;
#endif
    word32 newSz = 0;

#if WOLFSSL_GENERAL_ALIGNMENT > 0
    /* the encrypted data will be offset from the front of the buffer by
       the header, if the user wants encrypted alignment they need
       to define their alignment requirement */

    while (align < hdrSz)
        align *= 2;
#endif

    if (!WC_SAFE_SUM_WORD32(outputBuffer->length, (word32)size, newSz))
        return BUFFER_E;
#if WOLFSSL_GENERAL_ALIGNMENT > 0
    if (!WC_SAFE_SUM_WORD32(newSz, align, newSz))
        return BUFFER_E;
#endif
    tmp = (byte*)XMALLOC(newSz, ssl->heap, DYNAMIC_TYPE_OUT_BUFFER);
    WOLFSSL_MSG("growing output buffer");

    if (tmp == NULL)
        return MEMORY_E;

#if WOLFSSL_GENERAL_ALIGNMENT > 0
    tmp += align - hdrSz;
#endif

#ifdef WOLFSSL_STATIC_MEMORY
    /* can be from IO memory pool which does not need copy if same buffer */
    if (outputBuffer->length && tmp == outputBuffer->buffer) {
        outputBuffer->bufferSize = newSz - align;
        return 0;
    }
#endif

    if (outputBuffer->length)
        XMEMCPY(tmp, outputBuffer->buffer, outputBuffer->length);

    if (outputBuffer->dynamicFlag) {
        XFREE(outputBuffer->buffer - outputBuffer->offset, ssl->heap,
              DYNAMIC_TYPE_OUT_BUFFER);
    }

#if WOLFSSL_GENERAL_ALIGNMENT > 0
    outputBuffer->offset = align - hdrSz;
#else
    outputBuffer->offset = 0;
#endif

    outputBuffer->buffer = tmp;
    outputBuffer->dynamicFlag = 1;
    outputBuffer->bufferSize = newSz - align;
    return 0;
}
#endif

/* returns the current location in the output buffer to start writing to */
byte* GetOutputBuffer(WOLFSSL* ssl)
{
    return ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.idx +
             ssl->buffers.outputBuffer.length;
}


/* Grow the output buffer */
static WC_INLINE int GrowOutputBuffer(WOLFSSL* ssl, int size)
{
    byte* tmp;
#if WOLFSSL_GENERAL_ALIGNMENT > 0
    byte  hdrSz = ssl->options.dtls ? DTLS_RECORD_HEADER_SZ :
                                      RECORD_HEADER_SZ;
    byte align = WOLFSSL_GENERAL_ALIGNMENT;
#else
    const byte align = WOLFSSL_GENERAL_ALIGNMENT;
#endif
    word32 newSz = 0;

#if WOLFSSL_GENERAL_ALIGNMENT > 0
    /* the encrypted data will be offset from the front of the buffer by
       the header, if the user wants encrypted alignment they need
       to define their alignment requirement */

    while (align < hdrSz)
        align *= 2;
#endif

    if (! WC_SAFE_SUM_WORD32(ssl->buffers.outputBuffer.idx,
                             ssl->buffers.outputBuffer.length, newSz))
        return BUFFER_E;
    if (! WC_SAFE_SUM_WORD32(newSz, (word32)size, newSz))
        return BUFFER_E;
#if WOLFSSL_GENERAL_ALIGNMENT > 0
    if (! WC_SAFE_SUM_WORD32(newSz, align, newSz))
        return BUFFER_E;
#endif
    tmp = (byte*)XMALLOC(newSz, ssl->heap, DYNAMIC_TYPE_OUT_BUFFER);
    newSz -= align;
    WOLFSSL_MSG("growing output buffer");

    if (tmp == NULL)
        return MEMORY_E;

#if WOLFSSL_GENERAL_ALIGNMENT > 0
    tmp += align - hdrSz;
#endif

#ifdef WOLFSSL_STATIC_MEMORY
    /* can be from IO memory pool which does not need copy if same buffer */
    if (ssl->buffers.outputBuffer.length &&
            tmp == ssl->buffers.outputBuffer.buffer) {
        ssl->buffers.outputBuffer.bufferSize = newSz;
        return 0;
    }
#endif

    if (ssl->buffers.outputBuffer.length)
        XMEMCPY(tmp, ssl->buffers.outputBuffer.buffer,
               ssl->buffers.outputBuffer.idx +
               ssl->buffers.outputBuffer.length);

    if (ssl->buffers.outputBuffer.dynamicFlag) {
        XFREE(ssl->buffers.outputBuffer.buffer -
              ssl->buffers.outputBuffer.offset, ssl->heap,
              DYNAMIC_TYPE_OUT_BUFFER);
    }
    ssl->buffers.outputBuffer.dynamicFlag = 1;

#if WOLFSSL_GENERAL_ALIGNMENT > 0
    ssl->buffers.outputBuffer.offset = align - hdrSz;
#else
    ssl->buffers.outputBuffer.offset = 0;
#endif

    ssl->buffers.outputBuffer.buffer = tmp;
    ssl->buffers.outputBuffer.bufferSize = newSz;
    return 0;
}


/* Grow the input buffer, should only be to read cert or big app data */
int GrowInputBuffer(WOLFSSL* ssl, int size, int usedLength)
{
    byte* tmp;
#if defined(WOLFSSL_DTLS) || WOLFSSL_GENERAL_ALIGNMENT > 0
    byte  align = ssl->options.dtls ? WOLFSSL_GENERAL_ALIGNMENT : 0;
    byte  hdrSz = DTLS_RECORD_HEADER_SZ;
#else
    const byte align = WOLFSSL_GENERAL_ALIGNMENT;
#endif

#if defined(WOLFSSL_DTLS) || WOLFSSL_GENERAL_ALIGNMENT > 0
    /* the encrypted data will be offset from the front of the buffer by
       the dtls record header, if the user wants encrypted alignment they need
       to define their alignment requirement. in tls we read record header
       to get size of record and put actual data back at front, so don't need */

    if (align) {
       while (align < hdrSz)
           align *= 2;
    }
#endif

    if (usedLength < 0 || size < 0) {
        WOLFSSL_MSG("GrowInputBuffer() called with negative number");
        return BAD_FUNC_ARG;
    }

    tmp = (byte*)XMALLOC((size_t)(size + usedLength + align),
                             ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
    WOLFSSL_MSG("growing input buffer");

    if (tmp == NULL)
        return MEMORY_E;

#if defined(WOLFSSL_DTLS) || WOLFSSL_GENERAL_ALIGNMENT > 0
    if (align)
        tmp += align - hdrSz;
#endif

#ifdef WOLFSSL_STATIC_MEMORY
    /* can be from IO memory pool which does not need copy if same buffer */
    if (usedLength && tmp == ssl->buffers.inputBuffer.buffer) {
        ssl->buffers.inputBuffer.bufferSize = size + usedLength;
        ssl->buffers.inputBuffer.idx    = 0;
        ssl->buffers.inputBuffer.length = usedLength;
        return 0;
    }
#endif

    if (usedLength)
        XMEMCPY(tmp, ssl->buffers.inputBuffer.buffer +
                    ssl->buffers.inputBuffer.idx, (size_t)(usedLength));

    if (ssl->buffers.inputBuffer.dynamicFlag) {
        if (IsEncryptionOn(ssl, 1)) {
            ForceZero(ssl->buffers.inputBuffer.buffer,
                ssl->buffers.inputBuffer.length);
        }
        XFREE(ssl->buffers.inputBuffer.buffer - ssl->buffers.inputBuffer.offset,
              ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
    }

    ssl->buffers.inputBuffer.dynamicFlag = 1;
#if defined(WOLFSSL_DTLS) || WOLFSSL_GENERAL_ALIGNMENT > 0
    if (align)
        ssl->buffers.inputBuffer.offset = align - hdrSz;
    else
#endif
        ssl->buffers.inputBuffer.offset = 0;

    ssl->buffers.inputBuffer.buffer = tmp;
    ssl->buffers.inputBuffer.bufferSize = (word32)(size + usedLength);
    ssl->buffers.inputBuffer.idx    = 0;
    ssl->buffers.inputBuffer.length = (word32)usedLength;

    return 0;
}


/* Check available size into output buffer, make room if needed.
 * This function needs to be called before anything gets put
 * into the output buffers since it flushes pending data if it
 * predicts that the msg will exceed MTU. */
int CheckAvailableSize(WOLFSSL *ssl, int size)
{
    if (size < 0) {
        WOLFSSL_MSG("CheckAvailableSize() called with negative number");
        return BAD_FUNC_ARG;
    }

#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
#if defined(WOLFSSL_SCTP) || defined(WOLFSSL_DTLS_MTU)
        word32 mtu = (word32)ssl->dtlsMtuSz;
#else
        word32 mtu = MAX_MTU;
#endif
        if ((word32)size + ssl->buffers.outputBuffer.length > mtu) {
            int ret;
            WOLFSSL_MSG("CheckAvailableSize() flushing buffer "
                        "to make room for new message");
            if ((ret = SendBuffered(ssl)) != 0) {
                return ret;
            }
        }
        if ((word32)size > mtu
#ifdef WOLFSSL_DTLS13
            /* DTLS1.3 uses the output buffer to store the full message and deal
               with fragmentation later in dtls13HandshakeSend() */
            && !IsAtLeastTLSv1_3(ssl->version)
#endif /* WOLFSSL_DTLS13 */
            ) {
            WOLFSSL_MSG("CheckAvailableSize() called with size greater than MTU.");
            return DTLS_SIZE_ERROR;
        }
    }
#endif

    if ((ssl->buffers.outputBuffer.bufferSize -
             ssl->buffers.outputBuffer.length -
             ssl->buffers.outputBuffer.idx) < (word32)size) {
        if (GrowOutputBuffer(ssl, size) < 0)
            return MEMORY_E;
    }

    return 0;
}

#ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS

int MsgCheckEncryption(WOLFSSL* ssl, byte type, byte encrypted)
{
    if (ssl == NULL) {
        WOLFSSL_MSG("ssl is null");
        return BAD_FUNC_ARG;
    }
#ifdef WOLFSSL_QUIC
    /* QUIC protects messages outside of the TLS scope */
    if (WOLFSSL_IS_QUIC(ssl) && IsAtLeastTLSv1_3(ssl->version))
        return 0;
#endif
    /* Verify which messages always have to be encrypted */
    if (IsAtLeastTLSv1_3(ssl->version)) {
        switch ((enum HandShakeType)type) {
            case client_hello:
            case server_hello:
            case hello_verify_request:
            case hello_retry_request:
            case change_cipher_hs:
                if (encrypted) {
                    WOLFSSL_MSG("Message can not be encrypted");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                break;
            case hello_request:
            case session_ticket:
            case end_of_early_data:
            case encrypted_extensions:
            case certificate:
            case server_key_exchange:
            case certificate_request:
            case server_hello_done:
            case certificate_verify:
            case client_key_exchange:
            case finished:
            case certificate_status:
            case key_update:
                if (!encrypted) {
                    WOLFSSL_MSG("Message always has to be encrypted");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                break;
            case message_hash:
            case no_shake:
            default:
                WOLFSSL_MSG("Unknown message type");
                WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
                return SANITY_MSG_E;
        }
    }
    else {
        switch ((enum HandShakeType)type) {
            case client_hello:
                if ((IsSCR(ssl) || ssl->options.handShakeDone) && !encrypted) {
                    WOLFSSL_MSG("Message has to be encrypted for SCR");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                break;
            case server_hello:
            case hello_verify_request:
            case hello_retry_request:
            case certificate:
            case server_key_exchange:
            case certificate_request:
            case server_hello_done:
            case certificate_verify:
            case client_key_exchange:
            case certificate_status:
            case session_ticket:
            case change_cipher_hs:
                if (IsSCR(ssl)) {
                    if (!encrypted) {
                        WOLFSSL_MSG("Message has to be encrypted during SCR");
                        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                        return OUT_OF_ORDER_E;
                    }
                }
                else if (encrypted) {
                    WOLFSSL_MSG("Message can not be encrypted in regular "
                                "handshake");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                break;
            case hello_request:
            case finished:
                if (!encrypted) {
                    WOLFSSL_MSG("Message always has to be encrypted");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                break;
            case key_update:
            case encrypted_extensions:
            case end_of_early_data:
            case message_hash:
            case no_shake:
            default:
                WOLFSSL_MSG("Unknown message type");
                WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
                return SANITY_MSG_E;
        }
    }
    return 0;
}

static WC_INLINE int isLastMsg(const WOLFSSL* ssl, word32 msgSz)
{
    word32 extra = 0;
    if (IsEncryptionOn(ssl, 0))
        extra = ssl->keys.padSz;
    return (ssl->buffers.inputBuffer.idx - ssl->curStartIdx) + msgSz + extra
            == ssl->curSize;
}

/* Check if the msg is the last msg in a record. This is also an easy way
 * to check that a record doesn't span different key boundaries. */
static int MsgCheckBoundary(const WOLFSSL* ssl, byte type,
        byte version_negotiated, word32 msgSz)
{
    if (version_negotiated) {
        if (IsAtLeastTLSv1_3(ssl->version)) {
            switch ((enum HandShakeType)type) {
                case hello_request:
                case client_hello:
                case server_hello:
                case hello_verify_request:
                case hello_retry_request:
                case finished:
                case end_of_early_data:
                    if (!isLastMsg(ssl, msgSz)) {
                        WOLFSSL_MSG("Message type is not last in record");
                        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                        return OUT_OF_ORDER_E;
                    }
                    break;
                case session_ticket:
                case encrypted_extensions:
                case certificate:
                case server_key_exchange:
                case certificate_request:
                case certificate_verify:
                case client_key_exchange:
                case certificate_status:
                case key_update:
                case change_cipher_hs:
                    break;
                case server_hello_done:
                case message_hash:
                case no_shake:
                default:
                    WOLFSSL_MSG("Unknown message type");
                    WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
                    return SANITY_MSG_E;
            }
        }
        else {
            switch ((enum HandShakeType)type) {
                case hello_request:
                case client_hello:
                case hello_verify_request:
                    if (!isLastMsg(ssl, msgSz)) {
                        WOLFSSL_MSG("Message type is not last in record");
                        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                        return OUT_OF_ORDER_E;
                    }
                    break;
                case server_hello:
                case session_ticket:
                case end_of_early_data:
                case certificate:
                case server_key_exchange:
                case certificate_request:
                case server_hello_done:
                case certificate_verify:
                case client_key_exchange:
                case finished:
                case certificate_status:
                case change_cipher_hs:
                    break;
                case hello_retry_request:
                case encrypted_extensions:
                case key_update:
                case message_hash:
                case no_shake:
                default:
                    WOLFSSL_MSG("Unknown message type");
                    WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
                    return SANITY_MSG_E;
            }
        }
    }
    else {
        switch ((enum HandShakeType)type) {
            case hello_request:
            case client_hello:
            case hello_verify_request:
                if (!isLastMsg(ssl, msgSz)) {
                    WOLFSSL_MSG("Message type is not last in record");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                break;
            case server_hello:
            case session_ticket:
            case end_of_early_data:
            case hello_retry_request:
            case encrypted_extensions:
            case certificate:
            case server_key_exchange:
            case certificate_request:
            case server_hello_done:
            case certificate_verify:
            case client_key_exchange:
            case finished:
            case certificate_status:
            case key_update:
            case change_cipher_hs:
                break;
            case message_hash:
            case no_shake:
            default:
                WOLFSSL_MSG("Unknown message type");
                WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
                return SANITY_MSG_E;
        }
    }
    return 0;
}

#endif /* WOLFSSL_DISABLE_EARLY_SANITY_CHECKS */

/* Extract the handshake header information.
 *
 * ssl       The SSL/TLS object.
 * input     The buffer holding the message data.
 * inOutIdx  On entry, the index into the buffer of the handshake data.
 *           On exit, the start of the handshake data.
 * type      Type of handshake message.
 * size      The length of the handshake message data.
 * totalSz   The total size of data in the buffer.
 * returns BUFFER_E if there is not enough input data and 0 on success.
 */
int GetHandshakeHeader(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
                       byte* type, word32* size, word32 totalSz)
{
    const byte* ptr = input + *inOutIdx;
    (void)ssl;

    *inOutIdx += HANDSHAKE_HEADER_SZ;
    if (*inOutIdx > totalSz)
        return BUFFER_E;

    *type = ptr[0];
    c24to32(&ptr[1], size);

    return 0;
}

/**
 * This check is performed as soon as the handshake message type becomes known.
 * These checks can not be delayed and need to be performed when the msg is
 * received and not when it is processed (fragmentation may cause messages to
 * be processed at a later time). This function CAN NOT be called on stored
 * messages as it relies on the state of the WOLFSSL object right after
 * receiving the message.
 *
 * @param ssl   The current connection
 * @param type  The enum HandShakeType of the current message
 * @param msgSz Size of the current message
 * @return int (less than 0 on fail, 0 on success)
 */
int EarlySanityCheckMsgReceived(WOLFSSL* ssl, byte type, word32 msgSz)
{
    int ret = 0;
#ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
    /* Version has only been negotiated after we either send or process a
     * ServerHello message */
    byte version_negotiated = ssl->options.serverState >= SERVER_HELLO_COMPLETE;

    WOLFSSL_ENTER("EarlySanityCheckMsgReceived");

    if (version_negotiated)
        ret = MsgCheckEncryption(ssl, type, ssl->keys.decryptedCur == 1);

    if (ret == 0)
        ret = MsgCheckBoundary(ssl, type, version_negotiated, msgSz);

    if (ret != 0
#ifdef WOLFSSL_DTLS
            && ssl->options.dtls && ssl->options.dtlsStateful
#endif
            )
        SendAlert(ssl, alert_fatal, unexpected_message);

    WOLFSSL_LEAVE("EarlySanityCheckMsgReceived", ret);
#else
    (void)ssl;
    (void)type;
    (void)msgSz;
#endif

    return ret;
}

static int RecordsCanSpanReads(WOLFSSL *ssl)
{
#if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_DTLS_RECORDS_CAN_SPAN_DATAGRAMS)
    /* Only case where we return 0: DTLS mode (not SCTP) and can't span datagrams */
    if (IsDtlsNotSctpMode(ssl)) {
        return 0;
    }
#endif
    (void)ssl;
    return 1;
}

#ifdef WOLFSSL_DTLS13
static int GetInputData(WOLFSSL *ssl, word32 size);
static int GetDtls13RecordHeader(WOLFSSL* ssl, word32* inOutIdx,
    RecordLayerHeader* rh, word16* size)
{

    Dtls13UnifiedHdrInfo hdrInfo;
    w64wrapper epochNumber;
    byte epochBits;
    int readSize;
    int ret;

    readSize = ssl->buffers.inputBuffer.length - *inOutIdx;

    if (readSize < DTLS_UNIFIED_HEADER_MIN_SZ)
        return BUFFER_ERROR;

    epochBits = *(ssl->buffers.inputBuffer.buffer + *inOutIdx) & EE_MASK;
    ret = Dtls13ReconstructEpochNumber(ssl, epochBits, &epochNumber);
    if (ret != 0)
        return ret;

#ifdef WOLFSSL_DEBUG_TLS
    WOLFSSL_MSG_EX("reconstructed epoch number: %ld",
                   epochNumber);
#endif /* WOLFSSL_DEBUG_TLS */

    /* protected records always use unified_headers in DTLSv1.3 */
    if (w64IsZero(epochNumber))
        return SEQUENCE_ERROR;

    if (ssl->dtls13DecryptEpoch == NULL)
        return BAD_STATE_E;

#ifdef WOLFSSL_EARLY_DATA
    if (w64Equal(epochNumber, w64From32(0x0, DTLS13_EPOCH_EARLYDATA)) &&
            ssl->options.handShakeDone) {
        WOLFSSL_MSG("discarding early data after handshake");
        return SEQUENCE_ERROR;
    }
#endif /* WOLFSSL_DTLS13 */

    if (!w64Equal(ssl->dtls13DecryptEpoch->epochNumber, epochNumber)) {
        ret = Dtls13SetEpochKeys(ssl, epochNumber, DECRYPT_SIDE_ONLY);
        if (ret != 0)
            return SEQUENCE_ERROR;
    }

    ret = Dtls13GetUnifiedHeaderSize(ssl,
        *(ssl->buffers.inputBuffer.buffer+*inOutIdx), &ssl->dtls13CurRlLength);
    if (ret != 0)
        return ret;

    if (ssl->dtls13CurRlLength > sizeof(ssl->dtls13CurRL)) {
        WOLFSSL_MSG("Record header too long");
        return SEQUENCE_ERROR;
    }

    if (readSize < ssl->dtls13CurRlLength + DTLS13_RN_MASK_SIZE) {
        if (!RecordsCanSpanReads(ssl)) {
            WOLFSSL_MSG("Partial record received");
            return DTLS_PARTIAL_RECORD_READ;
        }
        /* when using DTLS over a medium that does not guarantee that a full
         * message is received in a single read, we may end up without the full
         * header and minimum ciphertext to decrypt record sequence numbers */
        ret = GetInputData(ssl, ssl->dtls13CurRlLength + DTLS13_RN_MASK_SIZE);
        if (ret != 0)
            return ret;

        readSize = ssl->buffers.inputBuffer.length - *inOutIdx;
    }

    ret = Dtls13ParseUnifiedRecordLayer(ssl,
        ssl->buffers.inputBuffer.buffer + *inOutIdx, (word16)readSize,
        &hdrInfo);

    if (ret != 0)
        return ret;

    *size = hdrInfo.recordLength;
    c16toa(*size, rh->length);

   /* type is implicit */
    rh->type = application_data;

    /* version is implicit */
    rh->pvMajor = ssl->version.major;
    rh->pvMinor = DTLSv1_2_MINOR;

    ssl->keys.curEpoch64 = epochNumber;

    ret = Dtls13ReconstructSeqNumber(ssl, &hdrInfo, &ssl->keys.curSeq);
    if (ret != 0)
        return ret;

#ifdef WOLFSSL_DEBUG_TLS
    WOLFSSL_MSG_EX("reconstructed seq number: %ld",
                   ssl->keys.curSeq);
#endif /* WOLFSSL_DEBUG_TLS */

    XMEMCPY(ssl->dtls13CurRL, ssl->buffers.inputBuffer.buffer + *inOutIdx,
            ssl->dtls13CurRlLength);
    *inOutIdx += ssl->dtls13CurRlLength;

    return 0;
}

#endif /* WOLFSSL_DTLS13 */

#ifdef WOLFSSL_DTLS
static int GetDtlsRecordHeader(WOLFSSL* ssl, word32* inOutIdx,
    RecordLayerHeader* rh, word16* size)
{
#ifdef WOLFSSL_DTLS_CID
    byte cidSz = 0;
#endif

#ifdef HAVE_FUZZER
    if (ssl->fuzzerCb)
        ssl->fuzzerCb(ssl, ssl->buffers.inputBuffer.buffer + *inOutIdx,
                      DTLS_RECORD_HEADER_SZ, FUZZ_HEAD, ssl->fuzzerCtx);
#endif

#ifdef WOLFSSL_DTLS13
    int ret;

    if (Dtls13IsUnifiedHeader(*(ssl->buffers.inputBuffer.buffer + *inOutIdx))) {
        ssl->options.seenUnifiedHdr = 1; /* We can send ACKs to the peer */

        /* version 1.3 already negotiated */
        if (ssl->options.tls1_3) {
            ret = GetDtls13RecordHeader(ssl, inOutIdx, rh, size);
            if (ret == 0 ||
                ((ret != WC_NO_ERR_TRACE(SEQUENCE_ERROR)) &&
                 (ret != WC_NO_ERR_TRACE(DTLS_CID_ERROR))))
                return ret;
        }

#ifndef NO_WOLFSSL_CLIENT
        if (ssl->options.side == WOLFSSL_CLIENT_END
            && ssl->options.serverState < SERVER_HELLO_COMPLETE
            && IsAtLeastTLSv1_3(ssl->version)
            && !ssl->options.handShakeDone) {
            /* we may have lost ServerHello. Try to send a empty ACK to shortcut
               Server retransmission timer */
            ssl->dtls13Rtx.sendAcks = 1;
        }
#endif
        return SEQUENCE_ERROR;
    }

    /* not a unified header, check that we have at least
     * DTLS_RECORD_HEADER_SZ */
    if (ssl->buffers.inputBuffer.length - *inOutIdx < DTLS_RECORD_HEADER_SZ) {
        if (!RecordsCanSpanReads(ssl)) {
            WOLFSSL_MSG("Partial record received");
            return DTLS_PARTIAL_RECORD_READ;
        }
        ret = GetInputData(ssl, DTLS_RECORD_HEADER_SZ);
        /* Check if Dtls13RtxTimeout(ssl) returned socket error */
        if (ret == WC_NO_ERR_TRACE(SOCKET_ERROR_E))
            return ret;
        if (ret != 0)
            return LENGTH_ERROR;
    }
#endif /* WOLFSSL_DTLS13 */

    /* type and version in same spot */
    XMEMCPY(rh, ssl->buffers.inputBuffer.buffer + *inOutIdx,
            ENUM_LEN + VERSION_SZ);
    *inOutIdx += ENUM_LEN + VERSION_SZ;
    ato16(ssl->buffers.inputBuffer.buffer + *inOutIdx, &ssl->keys.curEpoch);

    if (rh->pvMajor == DTLS_MAJOR && rh->pvMinor == DTLS_BOGUS_MINOR) {
        return SEQUENCE_ERROR;
    }

#ifdef WOLFSSL_DTLS_CID
    if (rh->type == dtls12_cid && (cidSz = DtlsGetCidRxSize(ssl)) == 0)
        return DTLS_CID_ERROR;
#endif

#ifdef WOLFSSL_DTLS13
    /* only non protected message can use the DTLSPlaintext record header */
    if (IsAtLeastTLSv1_3(ssl->version)) {
        if (ssl->keys.curEpoch != 0)
            return SEQUENCE_ERROR;

        w64Zero(&ssl->keys.curEpoch64);
        if (!w64IsZero(ssl->dtls13DecryptEpoch->epochNumber))
            Dtls13SetEpochKeys(ssl, ssl->keys.curEpoch64, DECRYPT_SIDE_ONLY);
    }
#endif /* WOLFSSL_DTLS13 */

    *inOutIdx += OPAQUE16_LEN;
    if (ssl->options.haveMcast) {
    #ifdef WOLFSSL_MULTICAST
        ssl->keys.curPeerId = ssl->buffers.inputBuffer.buffer[*inOutIdx];
        ssl->keys.curSeq_hi = ssl->buffers.inputBuffer.buffer[*inOutIdx+1];
    #endif
    }
    else
        ato16(ssl->buffers.inputBuffer.buffer + *inOutIdx, &ssl->keys.curSeq_hi);
    *inOutIdx += OPAQUE16_LEN;
    ato32(ssl->buffers.inputBuffer.buffer + *inOutIdx, &ssl->keys.curSeq_lo);
    *inOutIdx += OPAQUE32_LEN;  /* advance past rest of seq */

#ifdef WOLFSSL_DTLS13
    /* DTLSv1.3 PlainText records use DTLSv1.2 sequence number encoding. Update
       the DTLv1.3 word64 version as well */
    ssl->keys.curSeq = w64From32(ssl->keys.curSeq_hi, ssl->keys.curSeq_lo);
#endif /* WOLFSSL_DTLS13 */

#ifdef WOLFSSL_DTLS_CID
    if (rh->type == dtls12_cid) {
        byte* ourCid = NULL;
        if (ssl->buffers.inputBuffer.length - *inOutIdx <
                (word32)cidSz + LENGTH_SZ)
            return LENGTH_ERROR;
        if (wolfSSL_dtls_cid_get0_rx(ssl, &ourCid) != WOLFSSL_SUCCESS)
            return DTLS_CID_ERROR;
        if (XMEMCMP(ssl->buffers.inputBuffer.buffer + *inOutIdx, ourCid, cidSz)
                != 0)
            return DTLS_CID_ERROR;
        *inOutIdx += cidSz;
    }
#endif
    ato16(ssl->buffers.inputBuffer.buffer + *inOutIdx, size);
    *inOutIdx += LENGTH_SZ;

    return 0;
}
#endif /* WOLFSSL_DTLS */

/* do all verify and sanity checks on record header */
static int GetRecordHeader(WOLFSSL* ssl, word32* inOutIdx,
                           RecordLayerHeader* rh, word16 *size)
{
    byte tls12minor = 0;

#ifdef OPENSSL_ALL
    word32 start = *inOutIdx;
#endif

    (void)tls12minor;

    if (!ssl->options.dtls) {
#ifdef HAVE_FUZZER
        if (ssl->fuzzerCb)
            ssl->fuzzerCb(ssl, ssl->buffers.inputBuffer.buffer + *inOutIdx,
                          RECORD_HEADER_SZ, FUZZ_HEAD, ssl->fuzzerCtx);
#endif
        /* Set explicitly rather than make assumptions on struct layout */
        rh->type      = ssl->buffers.inputBuffer.buffer[*inOutIdx];
        rh->pvMajor   = ssl->buffers.inputBuffer.buffer[*inOutIdx + 1];
        rh->pvMinor   = ssl->buffers.inputBuffer.buffer[*inOutIdx + 2];
        rh->length[0] = ssl->buffers.inputBuffer.buffer[*inOutIdx + 3];
        rh->length[1] = ssl->buffers.inputBuffer.buffer[*inOutIdx + 4];

        *inOutIdx += RECORD_HEADER_SZ;
        ato16(rh->length, size);
    }
    else {
#ifdef WOLFSSL_DTLS
        int ret = GetDtlsRecordHeader(ssl, inOutIdx, rh, size);
        if (ret != 0)
            return ret;
#endif
    }

#ifdef WOLFSSL_DTLS
    /* DTLSv1.3 MUST check window after deprotecting to avoid timing channel
       (RFC9147 Section 4.5.1) */
    if (IsDtlsNotSctpMode(ssl) && !IsAtLeastTLSv1_3(ssl->version)) {
        byte needsEnc = rh->type == application_data; /* can't be epoch 0 */
#ifdef WOLFSSL_DTLS_CID
        needsEnc = needsEnc || rh->type == dtls12_cid;
#endif
        if (!_DtlsCheckWindow(ssl) ||
                (needsEnc && ssl->keys.curEpoch == 0) ||
                (rh->type == alert && ssl->options.handShakeDone &&
                        ssl->keys.curEpoch == 0 && ssl->keys.dtls_epoch != 0)) {
            WOLFSSL_LEAVE("GetRecordHeader()", SEQUENCE_ERROR);
            return SEQUENCE_ERROR;
        }
    }
#endif

#if defined(WOLFSSL_DTLS13) || defined(WOLFSSL_TLS13)
    tls12minor = TLSv1_2_MINOR;
#endif
#ifdef WOLFSSL_DTLS13
    if (ssl->options.dtls)
        tls12minor = DTLSv1_2_MINOR;
#endif /* WOLFSSL_DTLS13 */
    /* catch version mismatch */
#ifndef WOLFSSL_TLS13
    if (rh->pvMajor != ssl->version.major || rh->pvMinor != ssl->version.minor)
#else
    if (rh->pvMajor != ssl->version.major ||
        (rh->pvMinor != ssl->version.minor &&
         (!IsAtLeastTLSv1_3(ssl->version) || rh->pvMinor != tls12minor)
        ))
#endif
    {
        if (ssl->options.side == WOLFSSL_SERVER_END &&
            ssl->options.acceptState < ACCEPT_FIRST_REPLY_DONE)

            WOLFSSL_MSG("Client attempting to connect with different version");
        else if (ssl->options.side == WOLFSSL_CLIENT_END &&
                                 ssl->options.downgrade &&
                                 ssl->options.connectState < FIRST_REPLY_DONE)
            WOLFSSL_MSG("Server attempting to accept with different version");
        else if (ssl->options.dtls && rh->type == handshake)
            /* Check the DTLS handshake message RH version later. */
            WOLFSSL_MSG("DTLS handshake, skip RH version number check");
#ifdef WOLFSSL_DTLS13
        else if (ssl->options.dtls && !ssl->options.handShakeDone) {
            /* we may have lost the ServerHello and this is a unified record
               before version been negotiated */
            if (Dtls13IsUnifiedHeader(*ssl->buffers.inputBuffer.buffer)) {
                return SEQUENCE_ERROR;
            }
        }
#endif /* WOLFSSL_DTLS13 */
        /* Don't care about protocol version being lower than expected on alerts
         * sent back before version negotiation. */
        else if (!(ssl->options.side == WOLFSSL_CLIENT_END &&
                            ssl->options.connectState == CLIENT_HELLO_SENT &&
                            rh->type == alert &&
                            rh->pvMajor == ssl->version.major &&
        #ifdef WOLFSSL_DTLS
                            ((ssl->options.dtls && rh->pvMinor == DTLS_MINOR) ||
                             (!ssl->options.dtls &&
                              rh->pvMinor < ssl->version.minor))
        #else
                            (rh->pvMinor < ssl->version.minor)
        #endif
                   )) {
            WOLFSSL_MSG("SSL version error");
            WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
            return VERSION_ERROR;              /* only use requested version */
        }
    }

    /* record layer length check */
#ifdef HAVE_MAX_FRAGMENT
    if (*size > (ssl->max_fragment + MAX_MSG_EXTRA +
            (ssl->options.usingCompression ? MAX_COMP_EXTRA : 0))) {
        WOLFSSL_MSG_EX("Record length %d exceeds max fragment size", *size);
        WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR);
        return LENGTH_ERROR;
    }
#else
    if (*size > (MAX_RECORD_SIZE + MAX_MSG_EXTRA +
            (ssl->options.usingCompression ? MAX_COMP_EXTRA : 0))) {
        WOLFSSL_MSG_EX("Record length %d exceeds max record size", *size);
        WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR);
        return LENGTH_ERROR;
    }
#endif

    if (*size == 0 && rh->type != application_data) {
        WOLFSSL_MSG("0 length, non-app data record.");
        WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR);
        return LENGTH_ERROR;
    }

    /* verify record type here as well */
    switch (rh->type) {
        case handshake:
        case change_cipher_spec:
        case application_data:
        case alert:
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
        case dtls12_cid:
#endif
#ifdef WOLFSSL_DTLS13
        case ack:
#endif /* WOLFSSL_DTLS13 */
            break;
        case no_type:
        default:
#ifdef OPENSSL_ALL
            if (!ssl->options.dtls) {
                char *method = (char*)ssl->buffers.inputBuffer.buffer + start;
                /* Attempt to identify if this is a plain HTTP request.
                 * No size checks because this function assumes at least
                 * RECORD_HEADER_SZ size of data has been read which is
                 * also the longest string comparison in this if. */
                if (XSTRNCMP(method, "GET ", XSTR_SIZEOF("GET ")) == 0 ||
                    XSTRNCMP(method, "POST ", XSTR_SIZEOF("POST ")) == 0 ||
                    XSTRNCMP(method, "HEAD ", XSTR_SIZEOF("HEAD ")) == 0 ||
                    XSTRNCMP(method, "PUT ", XSTR_SIZEOF("PUT ")) == 0) {
                    WOLFSSL_MSG("Plain HTTP request detected");
                    return SSL_R_HTTP_REQUEST;
                }
            }
#endif
            WOLFSSL_MSG("Unknown Record Type");
            WOLFSSL_ERROR_VERBOSE(UNKNOWN_RECORD_TYPE);
            return UNKNOWN_RECORD_TYPE;
    }

    /* haven't decrypted this record yet */
    ssl->keys.decryptedCur = 0;

    return 0;
}

#ifdef WOLFSSL_DTLS
int GetDtlsHandShakeHeader(WOLFSSL* ssl, const byte* input,
                           word32* inOutIdx, byte *type, word32 *size,
                           word32 *fragOffset, word32 *fragSz,
                           word32 totalSz)
{
    word32 idx = *inOutIdx;

    *inOutIdx += DTLS_HANDSHAKE_HEADER_SZ;
    if (*inOutIdx > totalSz) {
        WOLFSSL_ERROR(BUFFER_E);
        return BUFFER_E;
    }

    *type = input[idx++];
    c24to32(input + idx, size);
    idx += OPAQUE24_LEN;

    ato16(input + idx, &ssl->keys.dtls_peer_handshake_number);
    idx += DTLS_HANDSHAKE_SEQ_SZ;

    c24to32(input + idx, fragOffset);
    idx += DTLS_HANDSHAKE_FRAG_SZ;
    c24to32(input + idx, fragSz);

    if ((ssl->curRL.pvMajor != ssl->version.major) ||
        (!IsAtLeastTLSv1_3(ssl->version) && ssl->curRL.pvMinor != ssl->version.minor) ||
        (IsAtLeastTLSv1_3(ssl->version) && ssl->curRL.pvMinor != DTLSv1_2_MINOR)
        ) {
        if (*type != client_hello && *type != hello_verify_request && *type != server_hello) {
            WOLFSSL_ERROR(VERSION_ERROR);
            return VERSION_ERROR;
        }
        else {
            WOLFSSL_MSG("DTLS Handshake ignoring hello or verify version");
        }
    }
    return 0;
}
#endif


#if !defined(NO_OLD_TLS) || \
    (defined(NO_OLD_TLS) && defined(WOLFSSL_ALLOW_TLS_SHA1))
/* fill with MD5 pad size since biggest required */
static const byte PAD1[PAD_MD5] =
                              { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
                              };
static const byte PAD2[PAD_MD5] =
                              { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c
                              };
#endif /* !NO_OLD_TLS || (NO_OLD_TLS && WOLFSSL_ALLOW_TLS_SHA1) */

#ifndef NO_OLD_TLS

/* calculate MD5 hash for finished */
#ifdef WOLFSSL_TI_HASH
#include <wolfssl/wolfcrypt/hash.h>
#endif

static int BuildMD5(WOLFSSL* ssl, Hashes* hashes, const byte* sender)
{
    int ret;
    byte md5_result[WC_MD5_DIGEST_SIZE];
#ifdef WOLFSSL_SMALL_STACK
    wc_Md5* md5 = (wc_Md5*)XMALLOC(sizeof(wc_Md5), ssl->heap, DYNAMIC_TYPE_HASHCTX);
    if (md5 == NULL)
        return MEMORY_E;
#else
    wc_Md5  md5[1];
#endif
    XMEMSET(md5, 0, sizeof(wc_Md5));

    /* make md5 inner */
    ret = wc_Md5Copy(&ssl->hsHashes->hashMd5, md5);
    if (ret == 0)
        ret = wc_Md5Update(md5, sender, SIZEOF_SENDER);
    if (ret == 0)
        ret = wc_Md5Update(md5, ssl->arrays->masterSecret, SECRET_LEN);
    if (ret == 0)
        ret = wc_Md5Update(md5, PAD1, PAD_MD5);
    if (ret == 0)
        ret = wc_Md5Final(md5, md5_result);

    /* make md5 outer */
    if (ret == 0) {
        ret = wc_InitMd5_ex(md5, ssl->heap, ssl->devId);
        if (ret == 0) {
            ret = wc_Md5Update(md5, ssl->arrays->masterSecret, SECRET_LEN);
            if (ret == 0)
                ret = wc_Md5Update(md5, PAD2, PAD_MD5);
            if (ret == 0)
                ret = wc_Md5Update(md5, md5_result, WC_MD5_DIGEST_SIZE);
            if (ret == 0)
                ret = wc_Md5Final(md5, hashes->md5);
            wc_Md5Free(md5);
        }
    }

    WC_FREE_VAR_EX(md5, ssl->heap, DYNAMIC_TYPE_HASHCTX);

    return ret;
}


/* calculate SHA hash for finished */
static int BuildSHA(WOLFSSL* ssl, Hashes* hashes, const byte* sender)
{
    int ret;
    byte sha_result[WC_SHA_DIGEST_SIZE];
#ifdef WOLFSSL_SMALL_STACK
    wc_Sha* sha = (wc_Sha*)XMALLOC(sizeof(wc_Sha), ssl->heap, DYNAMIC_TYPE_HASHCTX);
    if (sha == NULL)
        return MEMORY_E;
#else
    wc_Sha  sha[1];
#endif
    XMEMSET(sha, 0, sizeof(wc_Sha));
    /* make sha inner */
    ret = wc_ShaCopy(&ssl->hsHashes->hashSha, sha); /* Save current position */
    if (ret == 0)
        ret = wc_ShaUpdate(sha, sender, SIZEOF_SENDER);
    if (ret == 0)
        ret = wc_ShaUpdate(sha, ssl->arrays->masterSecret, SECRET_LEN);
    if (ret == 0)
        ret = wc_ShaUpdate(sha, PAD1, PAD_SHA);
    if (ret == 0)
        ret = wc_ShaFinal(sha, sha_result);

    /* make sha outer */
    if (ret == 0) {
        ret = wc_InitSha_ex(sha, ssl->heap, ssl->devId);
        if (ret == 0) {
            ret = wc_ShaUpdate(sha, ssl->arrays->masterSecret, SECRET_LEN);
            if (ret == 0)
                ret = wc_ShaUpdate(sha, PAD2, PAD_SHA);
            if (ret == 0)
                ret = wc_ShaUpdate(sha, sha_result, WC_SHA_DIGEST_SIZE);
            if (ret == 0)
                ret = wc_ShaFinal(sha, hashes->sha);
            wc_ShaFree(sha);
        }
    }

    WC_FREE_VAR_EX(sha, ssl->heap, DYNAMIC_TYPE_HASHCTX);

    return ret;
}
#endif

#ifndef WOLFSSL_NO_TLS12

/* Finished doesn't support SHA512, not SHA512 cipher suites yet */
static int BuildFinished(WOLFSSL* ssl, Hashes* hashes, const byte* sender)
{
    int ret = 0;

    if (ssl == NULL)
        return BAD_FUNC_ARG;

    if (ssl->options.tls) {
        ret = BuildTlsFinished(ssl, hashes, sender);
    }
#ifndef NO_OLD_TLS
    if (!ssl->options.tls) {
        ret = BuildMD5(ssl, hashes, sender);
        if (ret == 0) {
            ret = BuildSHA(ssl, hashes, sender);
        }
    }
#endif

    return ret;
}

#endif /* WOLFSSL_NO_TLS12 */

#if !defined(NO_WOLFSSL_SERVER) || !defined(NO_WOLFSSL_CLIENT)

/* Does this cipher suite (first, second) have the requirement
   an ephemeral key exchange will still require the key for signing
   the key exchange so ECDHE_RSA requires an rsa key thus rsa_kea */
int CipherRequires(byte first, byte second, int requirement)
{

    (void)requirement;
    (void)first;
    (void)second;

#ifndef WOLFSSL_NO_TLS12

#ifdef HAVE_CHACHA
    if (first == CHACHA_BYTE) {

        switch (second) {
            case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 :
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;

            case TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;

            case TLS_ECDHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_ECDHE_ECDSA_WITH_CHACHA20_OLD_POLY1305_SHA256 :
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;

            case TLS_DHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;


            case TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 :
                if (requirement == REQUIRES_PSK)
                    return 1;
                break;

            case TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 :
                if (requirement == REQUIRES_PSK)
                    return 1;
                break;

            case TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 :
                if (requirement == REQUIRES_PSK)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;

            default:
                WOLFSSL_MSG("Unsupported cipher suite, CipherRequires CHACHA");
                return 0;
        }

        if (requirement == REQUIRES_AEAD)
            return 1;
    }
#endif /* HAVE_CHACHA */

    /* ECC extensions */
    if (first == ECC_BYTE) {

        switch (second) {
#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
    #ifndef NO_RSA
            case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                break;

        #ifndef NO_DES3
            case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                break;
        #endif /* !NO_DES3 */

        #ifndef NO_RC4
            case TLS_ECDHE_RSA_WITH_RC4_128_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_ECDH_RSA_WITH_RC4_128_SHA :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                break;
        #endif /* !NO_RC4 */
    #endif /* NO_RSA */

        #ifndef NO_DES3
            case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA :
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;

            case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                break;
        #endif /* !NO_DES3  */
        #ifndef NO_RC4
            case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA :
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;

            case TLS_ECDH_ECDSA_WITH_RC4_128_SHA :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                break;
        #endif /* !NO_RC4 */
        #ifndef NO_RSA
            case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                break;
        #endif /* !NO_RSA */

            case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA :
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;

            case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                break;

            case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA :
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;

            case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                break;

            case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 :
                if (requirement == REQUIRES_ECC)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 :
                if (requirement == REQUIRES_ECC)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;
#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

#ifndef NO_RSA
    #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
            case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 :
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;
    #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
    #ifdef HAVE_AESCCM
            case TLS_RSA_WITH_AES_128_CCM_8 :
            case TLS_RSA_WITH_AES_256_CCM_8 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;
    #endif /* HAVE_AESCCM */
    #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)

            case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 :
            case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 :
            case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 :
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                break;
    #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
#endif /* !NO_RSA */

#ifdef HAVE_ARIA
        case TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 :
        case TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 :
            if (requirement == REQUIRES_ECC)
                return 1;
            break;
#endif /* HAVE_ARIA */

#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
        case TLS_ECDHE_ECDSA_WITH_AES_128_CCM :
        case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 :
        case TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 :
            if (requirement == REQUIRES_ECC)
                return 1;
            if (requirement == REQUIRES_AEAD)
                return 1;
            break;

            case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 :
            case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 :
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;

            case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 :
            case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 :
                if (requirement == REQUIRES_ECC)
                    return 1;
                if (requirement == REQUIRES_ECC_STATIC)
                    return 1;
                break;
    #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

    #ifndef NO_PSK
            case TLS_PSK_WITH_AES_128_CCM:
            case TLS_PSK_WITH_AES_256_CCM:
            case TLS_PSK_WITH_AES_128_CCM_8:
            case TLS_PSK_WITH_AES_256_CCM_8:
                if (requirement == REQUIRES_PSK)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_DHE_PSK_WITH_AES_128_CCM:
            case TLS_DHE_PSK_WITH_AES_256_CCM:
                if (requirement == REQUIRES_PSK)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;
    #endif /* !NO_PSK */
    #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
            case TLS_ECDHE_ECDSA_WITH_NULL_SHA :
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;

            case TLS_ECDHE_PSK_WITH_NULL_SHA256 :
                if (requirement == REQUIRES_PSK)
                    return 1;
                break;

            case TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 :
                if (requirement == REQUIRES_PSK)
                    return 1;
                break;
    #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

    #if defined(WOLFSSL_TLS13) && defined(HAVE_NULL_CIPHER)
            case TLS_SHA256_SHA256:
                break;
            case TLS_SHA384_SHA384:
                break;
    #endif

            default:
                WOLFSSL_MSG("Unsupported cipher suite, CipherRequires ECC");
                return 0;
        }   /* switch */
    }   /* if */

    /* ECC extensions */
    if (first == ECDHE_PSK_BYTE) {

        switch (second) {
    #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
            case TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 :
                if (requirement == REQUIRES_PSK)
                    return 1;
                break;
    #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
            default:
                WOLFSSL_MSG("Unsupported cipher suite, CipherRequires ECC PSK");
                return 0;
        }   /* switch */
    }   /* if     */
#endif /* !WOLFSSL_NO_TLS12 */

#ifdef WOLFSSL_TLS13
    /* Distinct TLS v1.3 cipher suites with cipher and digest only. */
    if (first == TLS13_BYTE) {

        switch (second) {
            case TLS_AES_128_GCM_SHA256:
            case TLS_AES_256_GCM_SHA384:
            case TLS_CHACHA20_POLY1305_SHA256:
            case TLS_AES_128_CCM_SHA256:
            case TLS_AES_128_CCM_8_SHA256:
                if (requirement == REQUIRES_AEAD)
                    return 1;
                return 0;

            default:
                WOLFSSL_MSG("Unsupported cipher suite, CipherRequires "
                            "TLS v1.3");
                return 0;
        }
    }

#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && defined(WOLFSSL_SM4)
    if (first == CIPHER_BYTE) {
        /* Other cipher suites for TLS 1.2 below.  */
        switch (second) {
        #if defined(WOLFSSL_SM4_GCM)
            case TLS_SM4_GCM_SM3:
                return 0;
                break;
        #endif
        #if defined(WOLFSSL_SM4_CCM)
            case TLS_SM4_CCM_SM3:
                return 0;
                break;
        #endif
        }
    }
#endif /* WOLFSSL_SM2 && WOLFSSL_SM3 && WOLFSSL_SM4 */
#endif /* WOLFSSL_TLS13 */

#ifndef WOLFSSL_NO_TLS12

#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && defined(WOLFSSL_SM4)
    if (first == SM_BYTE) {
        switch (second) {
        #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
            case TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3:
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;
        #endif
        #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
            case TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3:
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;
        #endif
        #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
            case TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3:
                if (requirement == REQUIRES_ECC)
                    return 1;
                break;
        #endif

            default:
                WOLFSSL_MSG("Unsupported cipher suite, CipherRequires SM");
                return 0;
        }
    }
#endif

    if (first == CIPHER_BYTE) {
        /* normal suites */
        switch (second) {

    #ifndef NO_RSA
        #ifndef NO_RC4
            case SSL_RSA_WITH_RC4_128_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case SSL_RSA_WITH_RC4_128_MD5 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;
        #endif /* NO_RC4 */

            case SSL_RSA_WITH_3DES_EDE_CBC_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_RSA_WITH_AES_128_CBC_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_RSA_WITH_AES_128_CBC_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_RSA_WITH_AES_256_CBC_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_RSA_WITH_AES_256_CBC_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_RSA_WITH_NULL_MD5 :
            case TLS_RSA_WITH_NULL_SHA :
            case TLS_RSA_WITH_NULL_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

    #endif /* !NO_RSA */

    #ifndef NO_PSK
            case TLS_PSK_WITH_AES_128_GCM_SHA256 :
                if (requirement == REQUIRES_PSK)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_PSK_WITH_AES_256_GCM_SHA384 :
                if (requirement == REQUIRES_PSK)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_PSK_WITH_AES_128_CBC_SHA256 :
            case TLS_PSK_WITH_AES_256_CBC_SHA384 :
            case TLS_PSK_WITH_AES_128_CBC_SHA :
            case TLS_PSK_WITH_AES_256_CBC_SHA :
            case TLS_PSK_WITH_NULL_SHA384 :
            case TLS_PSK_WITH_NULL_SHA256 :
            case TLS_PSK_WITH_NULL_SHA :
                if (requirement == REQUIRES_PSK)
                    return 1;
                break;

            case TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 :
            case TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 :
                if (requirement == REQUIRES_DHE)
                    return 1;
                if (requirement == REQUIRES_PSK)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 :
            case TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 :
            case TLS_DHE_PSK_WITH_NULL_SHA384 :
            case TLS_DHE_PSK_WITH_NULL_SHA256 :
                if (requirement == REQUIRES_DHE)
                    return 1;
                if (requirement == REQUIRES_PSK)
                    return 1;
                break;
    #endif /* NO_PSK */

    #ifndef NO_RSA
            case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;

            case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;

            case TLS_DHE_RSA_WITH_AES_128_CBC_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;

            case TLS_DHE_RSA_WITH_AES_256_CBC_SHA :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;

            case TLS_RSA_WITH_AES_128_GCM_SHA256 :
            case TLS_RSA_WITH_AES_256_GCM_SHA384 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

            case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 :
            case TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;

        #ifdef HAVE_CAMELLIA
            case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA :
            case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA :
            case TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 :
            case TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                break;

            case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA :
            case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA :
            case TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 :
            case TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 :
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;
        #endif /* HAVE_CAMELLIA */

            case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
                if (requirement == REQUIRES_RSA)
                    return 1;
                if (requirement == REQUIRES_RSA_SIG)
                    return 1;
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;
    #endif /* !NO_RSA */
    #ifdef HAVE_ANON
            case TLS_DH_anon_WITH_AES_128_CBC_SHA :
                if (requirement == REQUIRES_DHE)
                    return 1;
                break;
            case TLS_DH_anon_WITH_AES_256_GCM_SHA384:
                if (requirement == REQUIRES_DHE)
                    return 1;
                if (requirement == REQUIRES_AEAD)
                    return 1;
                break;
    #endif
    #ifdef WOLFSSL_MULTICAST
            case WDM_WITH_NULL_SHA256 :
                break;
    #endif

            default:
                WOLFSSL_MSG("Unsupported cipher suite, CipherRequires");
                return 0;
        }  /* switch */
    }  /* if ECC / Normal suites else */

#endif /* !WOLFSSL_NO_TLS12 */

    return 0;
}

#endif /* !NO_WOLFSSL_SERVER && !NO_WOLFSSL_CLIENT */
#endif /* !NO_TLS */

#ifndef NO_CERTS
#if defined(WOLFSSL_IP_ALT_NAME) && !defined(WOLFSSL_USER_IO)
static int MatchIPv6(const char* pattern, int patternLen,
                     const char* str, word32 strLen)
{
    WOLFSSL_SOCKADDR_IN6 addr1, addr2;
    char patBuf[WOLFSSL_MAX_IPSTR];
    char strBuf[WOLFSSL_MAX_IPSTR];

    if ((word32)patternLen >= sizeof(patBuf) || strLen >= sizeof(strBuf))
        return 0;

    /* Make sure strings are null-terminated and safely copied */
    XMEMCPY(patBuf, pattern, patternLen);
    patBuf[patternLen] = '\0';
    XMEMCPY(strBuf, str, strLen);
    strBuf[strLen] = '\0';

    XMEMSET(&addr1, 0, sizeof(addr1));
    XMEMSET(&addr2, 0, sizeof(addr2));

    /* Try parsing both as IPv6 */
    if (XINET_PTON(WOLFSSL_IP6, patBuf, &addr1) != 1)
        return 0;
    if (XINET_PTON(WOLFSSL_IP6, strBuf, &addr2) != 1)
        return 0;

    /* Compare raw address bytes */
    return XMEMCMP(&addr1, &addr2, sizeof(WOLFSSL_SOCKADDR_IN6)) == 0;
}
#endif /* WOLFSSL_IP_ALT_NAME && !WOLFSSL_USER_IO */

/* Match names with wildcards, each wildcard can represent a single name
   component or fragment but not multiple names, i.e.,
   *.z.com matches y.z.com but not x.y.z.com

   If flags contains WOLFSSL_LEFT_MOST_WILDCARD_ONLY, wildcard only applies
   to left-most name component, compatible with RFC 2830 identity checking.

   return 1 on success */
int MatchDomainName(const char* pattern, int patternLen, const char* str,
                    word32 strLen, unsigned int flags)
{
    int ret = 0;
    byte wildcardEligible = 1;
    byte leftWildcardOnly = flags & WOLFSSL_LEFT_MOST_WILDCARD_ONLY;

    if (pattern == NULL || str == NULL || patternLen <= 0 || strLen == 0)
        return 0;

#if defined(WOLFSSL_IP_ALT_NAME) && !defined(WOLFSSL_USER_IO)
    /* First try to match IPv6 addresses */
    if (MatchIPv6(pattern, patternLen, str, strLen))
        return 1;
#endif

    while (patternLen > 0) {
        /* Get the next pattern char to evaluate */
        char p = (char)XTOLOWER((unsigned char)*pattern);
        if (p == '\0')
            break;

        pattern++;

        if ((p == '*') && wildcardEligible) {
            char s;
            /* We will always match '*' */
            patternLen--;

            /* Only single wildcard allowed with strict left only */
            if (leftWildcardOnly) {
                /* For the left wildcard only case, disallow wildcards with
                 * the pattern *domain.com and only allow *.domain.com. */
                if (patternLen > 0 && *pattern != '.')
                    return 0;

                wildcardEligible = 0;
            }

            /* Consume any extra '*' chars until the next non '*' char. */
            while (patternLen > 0) {
                p = (char)XTOLOWER((unsigned char)*pattern);
                pattern++;
                if (p == '\0' && patternLen > 0)
                    return 0;
                if (p != '*')
                    break;
                if (leftWildcardOnly && (p == '*')) {
                    /* RFC2830 only allows single left-most wildcard */
                    return 0;
                }

                patternLen--;
            }

            /* Consume str until we reach next char in pattern after '*' or
             * end of string */
            while (strLen > 0) {
                s = (char)XTOLOWER((unsigned char) *str);
                str++;
                strLen--;

                /* p is next char in pattern after '*', or '*' if '*' is the
                 * last char in the pattern (in which case patternLen is 1) */
                if ( ((s == p) && (patternLen > 0))) {
                    /* We had already counted the '*' as matched, this means
                     * we also matched the next non '*' char in pattern */
                    patternLen--;
                    break;
                }

                /* If strlen is 0, we have consumed the entire string. Count that
                 * as a match of '*' */
                if (strLen == 0) {
                    break;
                }

                if (s == '.')
                    return 0;
            }
        }
        else {
            /* Past left-most wildcard location, not eligible if flag set*/
            if (leftWildcardOnly && wildcardEligible) {
                wildcardEligible = 0;
            }

            if (strLen == 0)
                return 0;

            /* Simple case, pattern match exactly */
            if (p != (char)XTOLOWER((unsigned char) *str))
                return 0;

            str++;
            strLen--;
            patternLen--;
        }
    }

    if (strLen == 0 && patternLen == 0) {
        ret = 1; /* success */
    }

    return ret;
}


/* Check that alternative names, if they exists, match the domain.
 * Fail if there are wild patterns and they didn't match.
 * Check the common name if no alternative names matched.
 *
 * dCert     Decoded cert to get the alternative names from.
 * domain    Domain name to compare against.
 * domainLen Length of the domain name.
 * checkCN   Whether to check the common name.
 * flags     Matching flags.
 * isIP      Whether the domain is an IP address.
 * returns   1 : match was found.
 *           0 : no match found.
 *          -1 : No matches and wild pattern match failed.
 */
int CheckForAltNames(DecodedCert* dCert, const char* domain, word32 domainLen,
                     int* checkCN, unsigned int flags, byte isIP)
{
    int match = 0;
    DNS_entry* altName = NULL;
    const char *buf;
    word32 len;

    WOLFSSL_MSG("Checking AltNames");

    if (dCert != NULL)
        altName = dCert->altNames;

    if (checkCN != NULL)
        *checkCN = (altName == NULL) ? 1 : 0;

    for (; altName != NULL; altName = altName->next) {
        WOLFSSL_MSG("\tindividual AltName check");

#ifdef WOLFSSL_IP_ALT_NAME
        if (altName->type == ASN_IP_TYPE) {
            buf = altName->ipString;
            len = (word32)XSTRLEN(buf);
        }
        else
#endif /* WOLFSSL_IP_ALT_NAME */
        {
            buf = altName->name;
            len = (word32)altName->len;
        }

        if ((isIP && (altName->type != ASN_IP_TYPE)) ||
                (!isIP && (altName->type == ASN_IP_TYPE))) {
            WOLFSSL_MSG("\tAltName type mismatch, continue");
            continue;
        }

        if (MatchDomainName(buf, (int)len, domain, domainLen, flags)) {
            match = 1;
            if (checkCN != NULL) {
                *checkCN = 0;
            }
            WOLFSSL_MSG("\tmatch found");
            break;
        }
        /* No matches and wild pattern match failed. */
        else if (buf && (len >=1) && (buf[0] == '*')) {
            match = -1;
            WOLFSSL_MSG("\twildcard match failed");
        }
    }

    return match;
}


/* Check the domain name matches the subject alternative name or the subject
 * name.
 *
 * dcert          Decoded certificate.
 * domainName     The domain name.
 * domainNameLen  The length of the domain name.
 * flags          Matching flags.
 * isIP           Whether the domain name is an IP address.
 * returns DOMAIN_NAME_MISMATCH when no match found and 0 on success.
 */
int CheckHostName(DecodedCert* dCert, const char *domainName,
                  size_t domainNameLen, unsigned int flags, byte isIP)
{
    int checkCN;
    int ret = WC_NO_ERR_TRACE(DOMAIN_NAME_MISMATCH);

    if (dCert == NULL) {
        return BAD_FUNC_ARG;
    }

    if (CheckForAltNames(dCert, domainName, (word32)domainNameLen,
                                            &checkCN, flags, isIP) != 1) {
        ret = DOMAIN_NAME_MISMATCH;
        WOLFSSL_MSG("DomainName match on alt names failed");
    }
    else {
        ret = 0;
    }

#ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
    if (checkCN == 1) {
        if (MatchDomainName(dCert->subjectCN, dCert->subjectCNLen,
                            domainName, (word32)domainNameLen, flags) == 1) {
            ret = 0;
        }
        else {
            ret = DOMAIN_NAME_MISMATCH;
            WOLFSSL_MSG("DomainName match on common name failed");
        }
    }
#endif /* !WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY */

    return ret;
}

int CheckIPAddr(DecodedCert* dCert, const char* ipasc)
{
    WOLFSSL_MSG("Checking IPAddr");

    return CheckHostName(dCert, ipasc, (size_t)XSTRLEN(ipasc), 0, 1);
}


WC_MAYBE_UNUSED static void AddSessionCertToChain(WOLFSSL_X509_CHAIN* chain,
    byte* certBuf, word32 certSz)
{
   if (chain->count < MAX_CHAIN_DEPTH &&
                               certSz < MAX_X509_SIZE) {
        chain->certs[chain->count].length = (int)certSz;
        XMEMCPY(chain->certs[chain->count].buffer, certBuf, certSz);
        chain->count++;
    }
    else {
        WOLFSSL_MSG("Couldn't store chain cert for session");
    }
}

#if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) || \
    defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \
    defined(WOLFSSL_ACERT)
    static int CopyAltNames(DNS_entry** to, DNS_entry* from, int type, void* heap)
{
    /* Copy from to the beginning of to */
    DNS_entry** prev_next = to;
    DNS_entry* next;

    if (to == NULL) {
        return BAD_FUNC_ARG;
    }

    next = *to;

    for (; from != NULL; from = from->next) {
        DNS_entry* dnsEntry;

        if (type != -1 && from->type != type)
            continue;

        dnsEntry = AltNameDup(from, heap);
        if (dnsEntry == NULL) {
            WOLFSSL_MSG("\tOut of Memory");
            return MEMORY_E;
        }

        dnsEntry->next = next;
        *prev_next = dnsEntry;
        prev_next = &dnsEntry->next;
    }

    return 0;
}
#endif /* KEEP_PEER_CERT || SESSION_CERTS ||
        * OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL ||
        * WOLFSSL_ACERT */

#if (defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) || \
     defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
    !defined(IGNORE_NAME_CONSTRAINTS)
/* Duplicate a Base_entry */
static Base_entry* BaseEntryDup(Base_entry* from, void* heap)
{
    Base_entry* entry;

    if (from == NULL) {
        return NULL;
    }

    entry = (Base_entry*)XMALLOC(sizeof(Base_entry), heap,
                                 DYNAMIC_TYPE_ALTNAME);
    if (entry == NULL) {
        return NULL;
    }
    XMEMSET(entry, 0, sizeof(Base_entry));

    entry->name = (char*)XMALLOC((word32)from->nameSz + 1, heap,
                                 DYNAMIC_TYPE_ALTNAME);
    if (entry->name == NULL) {
        XFREE(entry, heap, DYNAMIC_TYPE_ALTNAME);
        return NULL;
    }
    XMEMCPY(entry->name, from->name, (word32)from->nameSz);
    entry->name[from->nameSz] = '\0';
    entry->nameSz = from->nameSz;
    entry->type = from->type;

    return entry;
}

/* Copy a Base_entry list */
static int CopyBaseEntry(Base_entry** to, Base_entry* from, void* heap)
{
    Base_entry** next = to;

    if (to == NULL) {
        return BAD_FUNC_ARG;
    }

    for (; from != NULL; from = from->next) {
        Base_entry* entry = BaseEntryDup(from, heap);
        if (entry == NULL) {
            WOLFSSL_MSG("BaseEntryDup failed");
            return MEMORY_E;
        }
        *next = entry;
        next = &entry->next;
    }

    return 0;
}
#endif /* (KEEP_PEER_CERT || SESSION_CERTS || OPENSSL_EXTRA ||
        *  OPENSSL_EXTRA_X509_SMALL) && !IGNORE_NAME_CONSTRAINTS */

#if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) || \
    defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \
    defined(WOLFSSL_ACERT)
/* Copy an ASN-encoded date (type + length + data) into a WOLFSSL_ASN1_TIME.
 * srcDate: ASN date buffer where [0]=type, [1]=length, [2..]=date bytes.
 * srcDateLen: total length of srcDate (0 means no date present). */
static void CopyDateToASN1_TIME(const byte* srcDate, int srcDateLen,
                                WOLFSSL_ASN1_TIME* dst)
{
    if (srcDateLen >= 2) {
        /* Clamp the date length to the maximum allowed size.
         * This needs to match the size of WOLFSSL_ASN1_TIME minus the
         * the type and length fields. */
        const int maxSz = CTC_DATE_SIZE - 2;
        const int copySz = (int)min(srcDate[1], maxSz);
        dst->type = srcDate[0];
        dst->length = copySz;
        XMEMCPY(dst->data, &srcDate[2], copySz);
    }
    else {
        dst->length = 0;
    }
}
#endif /* KEEP_PEER_CERT || SESSION_CERTS || OPENSSL_EXTRA ||
        *  OPENSSL_EXTRA_X509_SMALL || WOLFSSL_ACERT */

#if defined(KEEP_PEER_CERT) || defined(SESSION_CERTS) || \
    defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
void CopyDecodedName(WOLFSSL_X509_NAME* name, DecodedCert* dCert, int nameType)
{
    if (name->dynamicName) {
        XFREE(name->name, name->heap, DYNAMIC_TYPE_X509);
        name->name = name->staticName;
        name->dynamicName = 0;
    }

    if (nameType == ASN_SUBJECT) {
        XSTRNCPY(name->name, dCert->subject, ASN_NAME_MAX);
        name->name[ASN_NAME_MAX - 1] = '\0';
        name->sz = (int)XSTRLEN(name->name) + 1;
#if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(HAVE_LIGHTY)
        name->rawLen = min(dCert->subjectRawLen, ASN_NAME_MAX);
        if (name->rawLen > 0)
            XMEMCPY(name->raw, dCert->subjectRaw, name->rawLen);
#endif
    }
    else {
        XSTRNCPY(name->name, dCert->issuer, ASN_NAME_MAX);
        name->name[ASN_NAME_MAX - 1] = '\0';
        name->sz = (int)XSTRLEN(name->name) + 1;
#if (defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(HAVE_LIGHTY)) \
    && (defined(HAVE_PKCS7) || defined(WOLFSSL_CERT_EXT))
        name->rawLen = min(dCert->issuerRawLen, ASN_NAME_MAX);
        if (name->rawLen > 0) {
            XMEMCPY(name->raw, dCert->issuerRaw, name->rawLen);
        }
#endif
    }
}

#ifdef WOLFSSL_CERT_REQ
static int CopyREQAttributes(WOLFSSL_X509* x509, DecodedCert* dCert)
{
    int ret = 0;

    if (dCert->cPwd) {
        if (dCert->cPwdLen < CTC_NAME_SIZE) {
            XMEMCPY(x509->challengePw, dCert->cPwd, dCert->cPwdLen);
            x509->challengePw[dCert->cPwdLen] = '\0';
        #if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN)
            if (wolfSSL_X509_REQ_add1_attr_by_NID(x509,
                                        WC_NID_pkcs9_challengePassword,
                                        MBSTRING_ASC,
                                        (const byte*)dCert->cPwd,
                                        dCert->cPwdLen) != WOLFSSL_SUCCESS) {
                ret = REQ_ATTRIBUTE_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
        #endif
        }
        else {
            WOLFSSL_MSG("Challenge password too long");
            ret = MEMORY_E;
        }
    }

    if (dCert->contentType) {
        if (dCert->contentTypeLen < CTC_NAME_SIZE) {
            XMEMCPY(x509->contentType, dCert->contentType, dCert->contentTypeLen);
            x509->contentType[dCert->contentTypeLen] = '\0';
        }
    #if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN)
        if (wolfSSL_X509_REQ_add1_attr_by_NID(x509,
                                        WC_NID_pkcs9_contentType,
                                        MBSTRING_ASC,
                                        (const byte*)dCert->contentType,
                                        dCert->contentTypeLen) !=
                WOLFSSL_SUCCESS) {
            ret = REQ_ATTRIBUTE_E;
            WOLFSSL_ERROR_VERBOSE(ret);
        }
    #endif
    }

    #if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN)
    if (dCert->sNum) {
        if (wolfSSL_X509_REQ_add1_attr_by_NID(x509,
                                        WC_NID_serialNumber,
                                        MBSTRING_ASC,
                                        (const byte*)dCert->sNum,
                                        dCert->sNumLen) != WOLFSSL_SUCCESS) {
            ret = REQ_ATTRIBUTE_E;
            WOLFSSL_ERROR_VERBOSE(ret);
        }
    }
    if (dCert->unstructuredName) {
        if (wolfSSL_X509_REQ_add1_attr_by_NID(x509,
                                        WC_NID_pkcs9_unstructuredName,
                                        MBSTRING_ASC,
                                        (const byte*)dCert->unstructuredName,
                                        dCert->unstructuredNameLen)
                != WOLFSSL_SUCCESS) {
            ret = REQ_ATTRIBUTE_E;
            WOLFSSL_ERROR_VERBOSE(ret);
        }
    }
    if (dCert->surname) {
        if (wolfSSL_X509_REQ_add1_attr_by_NID(x509,
                                        WC_NID_surname,
                                        MBSTRING_ASC,
                                        (const byte*)dCert->surname,
                                        dCert->surnameLen) != WOLFSSL_SUCCESS) {
            ret = REQ_ATTRIBUTE_E;
            WOLFSSL_ERROR_VERBOSE(ret);
        }
    }
    if (dCert->givenName) {
        if (wolfSSL_X509_REQ_add1_attr_by_NID(x509,
                                        WC_NID_givenName,
                                        MBSTRING_ASC,
                                        (const byte*)dCert->givenName,
                                        dCert->givenNameLen) != WOLFSSL_SUCCESS) {
            ret = REQ_ATTRIBUTE_E;
            WOLFSSL_ERROR_VERBOSE(ret);
        }
    }
    if (dCert->dnQualifier) {
        if (wolfSSL_X509_REQ_add1_attr_by_NID(x509,
                                        WC_NID_dnQualifier,
                                        MBSTRING_ASC,
                                        (const byte*)dCert->dnQualifier,
                                        dCert->dnQualifierLen) != WOLFSSL_SUCCESS) {
            ret = REQ_ATTRIBUTE_E;
            WOLFSSL_ERROR_VERBOSE(ret);
        }
    }
    if (dCert->initials) {
        if (wolfSSL_X509_REQ_add1_attr_by_NID(x509,
                                        WC_NID_initials,
                                        MBSTRING_ASC,
                                        (const byte*)dCert->initials,
                                        dCert->initialsLen) != WOLFSSL_SUCCESS) {
            ret = REQ_ATTRIBUTE_E;
            WOLFSSL_ERROR_VERBOSE(ret);
        }
    }
    #endif /* OPENSSL_ALL */

    return ret;
}
#endif /* WOLFSSL_CERT_REQ */

/* Copy parts X509 needs from Decoded cert, 0 on success */
int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert)
{
    int ret = 0;
#ifdef WOLFSSL_SEP
    int minSz;
#endif

    if (x509 == NULL || dCert == NULL ||
        dCert->subjectCNLen < 0)
        return BAD_FUNC_ARG;

    if (x509->issuer.name == NULL || x509->subject.name == NULL) {
        WOLFSSL_MSG("Either init was not called on X509 or programming error");
        WOLFSSL_ERROR_VERBOSE(BAD_FUNC_ARG);
        return BAD_FUNC_ARG;
    }

    x509->version = dCert->version + 1;

    CopyDecodedName(&x509->issuer, dCert, ASN_ISSUER);
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    if (dCert->issuerName != NULL) {
        wolfSSL_X509_set_issuer_name(x509,
                (WOLFSSL_X509_NAME*)dCert->issuerName);
        x509->issuer.x509 = x509;
    }
#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
    CopyDecodedName(&x509->subject, dCert, ASN_SUBJECT);
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    if (dCert->subjectName != NULL) {
        wolfSSL_X509_set_subject_name(x509,
                (WOLFSSL_X509_NAME*)dCert->subjectName);
        x509->subject.x509 = x509;
    }
#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */

    XMEMCPY(x509->serial, dCert->serial, EXTERNAL_SERIAL_SIZE);
    x509->serialSz = dCert->serialSz;
    if (dCert->subjectCN && dCert->subjectCNLen < ASN_NAME_MAX) {
        XMEMCPY(x509->subjectCN, dCert->subjectCN, dCert->subjectCNLen);
        x509->subjectCN[dCert->subjectCNLen] = '\0';
    }
    else
        x509->subjectCN[0] = '\0';

#ifdef WOLFSSL_CERT_REQ
    x509->isCSR = dCert->isCSR;

    /* CSR attributes */
    if (x509->isCSR) {
        ret = CopyREQAttributes(x509, dCert);
    }
#endif /* WOLFSSL_CERT_REQ */

#ifdef WOLFSSL_SEP
    minSz = min(dCert->deviceTypeSz, EXTERNAL_SERIAL_SIZE);
    if (minSz > 0) {
        x509->deviceTypeSz = minSz;
        XMEMCPY(x509->deviceType, dCert->deviceType, minSz);
    }
    else
        x509->deviceTypeSz = 0;
    minSz = min(dCert->hwTypeSz, EXTERNAL_SERIAL_SIZE);
    if (minSz > 0) {
        x509->hwTypeSz = minSz;
        XMEMCPY(x509->hwType, dCert->hwType, minSz);
    }
    else
        x509->hwTypeSz = 0;
    minSz = min(dCert->hwSerialNumSz, EXTERNAL_SERIAL_SIZE);
    if (minSz > 0) {
        x509->hwSerialNumSz = minSz;
        XMEMCPY(x509->hwSerialNum, dCert->hwSerialNum, minSz);
    }
    else
        x509->hwSerialNumSz = 0;
#endif /* WOLFSSL_SEP */

    CopyDateToASN1_TIME(dCert->beforeDate, dCert->beforeDateLen,
        &x509->notBefore);
    CopyDateToASN1_TIME(dCert->afterDate, dCert->afterDateLen,
        &x509->notAfter);

    if (dCert->publicKey != NULL && dCert->pubKeySize != 0) {
        x509->pubKey.buffer = (byte*)XMALLOC(
                        dCert->pubKeySize, x509->heap, DYNAMIC_TYPE_PUBLIC_KEY);
        if (x509->pubKey.buffer != NULL) {
            x509->pubKeyOID = (int)dCert->keyOID;
            x509->pubKey.length = dCert->pubKeySize;
            XMEMCPY(x509->pubKey.buffer, dCert->publicKey, dCert->pubKeySize);
        }
        else
            ret = MEMORY_E;
#if defined(OPENSSL_ALL)
        if (ret == 0) {
            x509->key.pubKeyOID = (int)dCert->keyOID;

            if (!x509->key.algor) {
                x509->key.algor = wolfSSL_X509_ALGOR_new();
            } else {
                wolfSSL_ASN1_OBJECT_free(x509->key.algor->algorithm);
            }
            if (!x509->key.algor) {
                ret = MEMORY_E;
            } else {
                if (!(x509->key.algor->algorithm =
                    wolfSSL_OBJ_nid2obj(oid2nid(dCert->keyOID, oidKeyType)))) {
                    ret = PUBLIC_KEY_E;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }

            wolfSSL_EVP_PKEY_free(x509->key.pkey);
            if (!(x509->key.pkey = wolfSSL_d2i_PUBKEY(NULL,
                                                      &dCert->publicKey,
                                                      dCert->pubKeySize))) {
                ret = PUBLIC_KEY_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
        }
#endif
    }

    if (dCert->signature != NULL && dCert->sigLength != 0 &&
            dCert->sigLength <= MAX_ENCODED_SIG_SZ) {
        x509->sig.buffer = (byte*)XMALLOC(
                          dCert->sigLength, x509->heap, DYNAMIC_TYPE_SIGNATURE);
        if (x509->sig.buffer == NULL) {
            ret = MEMORY_E;
        }
        else {
            XMEMCPY(x509->sig.buffer, dCert->signature, dCert->sigLength);
            x509->sig.length = dCert->sigLength;
            x509->sigOID = (int)dCert->signatureOID;
        }
#if defined(OPENSSL_ALL)
        wolfSSL_ASN1_OBJECT_free(x509->algor.algorithm);
        if (!(x509->algor.algorithm =
                wolfSSL_OBJ_nid2obj(oid2nid(dCert->signatureOID, oidSigType)))) {
            ret = PUBLIC_KEY_E;
            WOLFSSL_ERROR_VERBOSE(ret);
        }
#endif
    }

    /* if der contains original source buffer then store for potential
     * retrieval */
    if (dCert->source != NULL && dCert->maxIdx > 0) {
        if (AllocDer(&x509->derCert, dCert->maxIdx, CERT_TYPE, x509->heap)
                                                                         == 0) {
            XMEMCPY(x509->derCert->buffer, dCert->source, dCert->maxIdx);
        }
        else {
            ret = MEMORY_E;
        }
    }

    /* add alt names from dCert to X509 */
    if (CopyAltNames(&x509->altNames, dCert->altNames, -1, x509->heap) != 0) {
        return MEMORY_E;
    }
#if (defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)) && \
    !defined(IGNORE_NAME_CONSTRAINTS)
    /* add copies of email names from dCert to X509 */
    if (CopyAltNames(&x509->altNames, dCert->altEmailNames,
                ASN_RFC822_TYPE, x509->heap) != 0) {
        return MEMORY_E;
    }
#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
#if defined(OPENSSL_EXTRA) && !defined(IGNORE_NAME_CONSTRAINTS)
    /* add copies of alternate directory names from dCert to X509 */
    if (CopyAltNames(&x509->altNames, dCert->altDirNames,
                ASN_DIR_TYPE, x509->heap) != 0) {
        return MEMORY_E;
    }
#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
    x509->altNamesNext   = x509->altNames;  /* index hint */

#ifndef IGNORE_NAME_CONSTRAINTS
    /* copy name constraints from dCert to X509 */
    if (dCert->permittedNames != NULL) {
        if (CopyBaseEntry(&x509->permittedNames, dCert->permittedNames,
                          x509->heap) != 0) {
            return MEMORY_E;
        }
    }
    if (dCert->excludedNames != NULL) {
        if (CopyBaseEntry(&x509->excludedNames, dCert->excludedNames,
                          x509->heap) != 0) {
            return MEMORY_E;
        }
    }
    x509->nameConstraintCrit = dCert->extNameConstraintCrit;
#endif /* !IGNORE_NAME_CONSTRAINTS */

    x509->isCa = dCert->isCA;
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    x509->basicConstCrit = dCert->extBasicConstCrit;
    x509->pathLength = dCert->pathLength;
    x509->pathLengthSet = dCert->pathLengthSet;
    x509->keyUsage = dCert->extKeyUsage;

    x509->CRLdistSet = dCert->extCRLdistSet;
    x509->CRLdistCrit = dCert->extCRLdistCrit;
    if (dCert->extCrlInfoRaw != NULL && dCert->extCrlInfoRawSz > 0) {
        x509->rawCRLInfo = (byte*)XMALLOC(dCert->extCrlInfoRawSz, x509->heap,
            DYNAMIC_TYPE_X509_EXT);
        if (x509->rawCRLInfo != NULL) {
            XMEMCPY(x509->rawCRLInfo, dCert->extCrlInfoRaw, dCert->extCrlInfoRawSz);
            x509->rawCRLInfoSz = dCert->extCrlInfoRawSz;
        }
        else {
            ret = MEMORY_E;
        }
    }
    if (dCert->extCrlInfo != NULL && dCert->extCrlInfoSz > 0) {
        x509->CRLInfo = (byte*)XMALLOC(dCert->extCrlInfoSz, x509->heap,
            DYNAMIC_TYPE_X509_EXT);
        if (x509->CRLInfo != NULL) {
            XMEMCPY(x509->CRLInfo, dCert->extCrlInfo, dCert->extCrlInfoSz);
            x509->CRLInfoSz = dCert->extCrlInfoSz;
        }
        else {
            ret = MEMORY_E;
        }
    }
    x509->authInfoSet = dCert->extAuthInfoSet;
    x509->authInfoCrit = dCert->extAuthInfoCrit;
    x509->authInfoListSz = dCert->extAuthInfoListSz;
    x509->authInfoListOverflow = dCert->extAuthInfoListOverflow;
    if (x509->authInfoListSz > WOLFSSL_MAX_AIA_ENTRIES) {
        x509->authInfoListSz = WOLFSSL_MAX_AIA_ENTRIES;
        x509->authInfoListOverflow = 1;
    }
    if (x509->authInfoListSz > 0) {
        int i;
        for (i = 0; i < x509->authInfoListSz; i++) {
            x509->authInfoList[i].method = dCert->extAuthInfoList[i].method;
            x509->authInfoList[i].uriSz = dCert->extAuthInfoList[i].uriSz;
            x509->authInfoList[i].uri = NULL;

            if (dCert->extAuthInfoList[i].uri != NULL &&
                    dCert->source != NULL && dCert->maxIdx > 0 &&
                    x509->derCert != NULL && x509->derCert->buffer != NULL) {
                word32 offset = (word32)
                    (dCert->extAuthInfoList[i].uri - dCert->source);
                if (offset < (word32)dCert->maxIdx) {
                    x509->authInfoList[i].uri =
                        x509->derCert->buffer + offset;
                }
                else {
                    x509->authInfoList[i].uriSz = 0;
                }
            }
        }
    }
    if (dCert->extAuthInfo != NULL && dCert->extAuthInfoSz > 0) {
        x509->authInfo = (byte*)XMALLOC(dCert->extAuthInfoSz, x509->heap,
                DYNAMIC_TYPE_X509_EXT);
        if (x509->authInfo != NULL) {
            XMEMCPY(x509->authInfo, dCert->extAuthInfo, dCert->extAuthInfoSz);
            x509->authInfoSz = dCert->extAuthInfoSz;
        }
        else {
            ret = MEMORY_E;
        }
    }
    #ifdef WOLFSSL_ASN_CA_ISSUER
    if (dCert->extAuthInfoCaIssuer != NULL && dCert->extAuthInfoCaIssuerSz > 0) {
        x509->authInfoCaIssuer = (byte*)XMALLOC(dCert->extAuthInfoCaIssuerSz, x509->heap,
                DYNAMIC_TYPE_X509_EXT);
        if (x509->authInfoCaIssuer != NULL) {
            XMEMCPY(x509->authInfoCaIssuer, dCert->extAuthInfoCaIssuer, dCert->extAuthInfoCaIssuerSz);
            x509->authInfoCaIssuerSz = dCert->extAuthInfoCaIssuerSz;
        }
        else {
            ret = MEMORY_E;
        }
    }
    #endif
    x509->basicConstSet = dCert->extBasicConstSet;
    x509->basicConstPlSet = dCert->pathLengthSet;
    x509->subjAltNameSet = dCert->extSubjAltNameSet;
    x509->subjAltNameCrit = dCert->extSubjAltNameCrit;
    x509->authKeyIdSet = dCert->extAuthKeyIdSet;
    x509->authKeyIdCrit = dCert->extAuthKeyIdCrit;
    if (dCert->extAuthKeyIdSrc != NULL && dCert->extAuthKeyIdSz != 0) {
    #ifdef WOLFSSL_AKID_NAME
        if (dCert->extRawAuthKeyIdSrc != NULL &&
                dCert->extAuthKeyIdSrc > dCert->extRawAuthKeyIdSrc &&
                dCert->extAuthKeyIdSrc <
                    (dCert->extRawAuthKeyIdSrc + dCert->extRawAuthKeyIdSz)) {
            /* Confirmed: extAuthKeyIdSrc points inside extRawAuthKeyIdSrc */
            x509->authKeyIdSrc = (byte*)XMALLOC(dCert->extRawAuthKeyIdSz,
                    x509->heap, DYNAMIC_TYPE_X509_EXT);
            if (x509->authKeyIdSrc != NULL) {
                XMEMCPY(x509->authKeyIdSrc, dCert->extRawAuthKeyIdSrc,
                        dCert->extRawAuthKeyIdSz);
                x509->authKeyIdSrcSz = dCert->extRawAuthKeyIdSz;
                /* Set authKeyId to same offset inside authKeyIdSrc */
                x509->authKeyId = x509->authKeyIdSrc +
                        (dCert->extAuthKeyIdSrc - dCert->extRawAuthKeyIdSrc);
                x509->authKeyIdSz = dCert->extAuthKeyIdSz;
            }
            else
                ret = MEMORY_E;
        }
    #else
        x509->authKeyId = (byte*)XMALLOC(dCert->extAuthKeyIdSz, x509->heap,
                                         DYNAMIC_TYPE_X509_EXT);
        if (x509->authKeyId != NULL) {
            XMEMCPY(x509->authKeyId,
                                 dCert->extAuthKeyIdSrc, dCert->extAuthKeyIdSz);
            x509->authKeyIdSz = dCert->extAuthKeyIdSz;
        }
    #endif
        else
            ret = MEMORY_E;
    }
    x509->subjKeyIdSet = dCert->extSubjKeyIdSet;
    x509->subjKeyIdCrit = dCert->extSubjKeyIdCrit;
    if (dCert->extSubjKeyIdSrc != NULL && dCert->extSubjKeyIdSz != 0) {
        x509->subjKeyId = (byte*)XMALLOC(dCert->extSubjKeyIdSz, x509->heap,
                                         DYNAMIC_TYPE_X509_EXT);
        if (x509->subjKeyId != NULL) {
            XMEMCPY(x509->subjKeyId,
                                 dCert->extSubjKeyIdSrc, dCert->extSubjKeyIdSz);
            x509->subjKeyIdSz = dCert->extSubjKeyIdSz;
        }
        else
            ret = MEMORY_E;
    }
    x509->keyUsageSet = dCert->extKeyUsageSet;
    x509->keyUsageCrit = dCert->extKeyUsageCrit;
    if (dCert->extExtKeyUsageSrc != NULL && dCert->extExtKeyUsageSz > 0) {
        x509->extKeyUsageSrc = (byte*)XMALLOC(dCert->extExtKeyUsageSz,
                x509->heap, DYNAMIC_TYPE_X509_EXT);
        if (x509->extKeyUsageSrc != NULL) {
            XMEMCPY(x509->extKeyUsageSrc, dCert->extExtKeyUsageSrc,
                                                       dCert->extExtKeyUsageSz);
            x509->extKeyUsage      = dCert->extExtKeyUsage;
            x509->extKeyUsageSz    = dCert->extExtKeyUsageSz;
            x509->extKeyUsageCrit  = dCert->extExtKeyUsageCrit;
            x509->extKeyUsageCount = dCert->extExtKeyUsageCount;
        }
        else {
            ret = MEMORY_E;
        }
    }
    #ifndef IGNORE_NETSCAPE_CERT_TYPE
    x509->nsCertType = dCert->nsCertType;
    #endif
    #ifdef WOLFSSL_SEP
        x509->certPolicySet = dCert->extCertPolicySet;
        x509->certPolicyCrit = dCert->extCertPolicyCrit;
    #endif
    #ifdef WOLFSSL_CERT_EXT
        {
            int i;
            for (i = 0; i < dCert->extCertPoliciesNb && i < MAX_CERTPOL_NB; i++)
                XMEMCPY(x509->certPolicies[i], dCert->extCertPolicies[i],
                                                                MAX_CERTPOL_SZ);
            x509->certPoliciesNb = dCert->extCertPoliciesNb;
        }
    #endif /* WOLFSSL_CERT_EXT */
#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL */
#ifdef OPENSSL_ALL
    if (dCert->extSubjAltNameSrc != NULL && dCert->extSubjAltNameSz != 0) {
        x509->subjAltNameSrc = (byte*)XMALLOC(dCert->extSubjAltNameSz, x509->heap,
                                         DYNAMIC_TYPE_X509_EXT);
        if (x509->subjAltNameSrc != NULL) {
            XMEMCPY(x509->subjAltNameSrc,
                                 dCert->extSubjAltNameSrc, dCert->extSubjAltNameSz);
            x509->subjAltNameSz = dCert->extSubjAltNameSz;
        }
        else
            ret = MEMORY_E;
    }
#endif
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448)
    x509->pkCurveOID = dCert->pkCurveOID;
#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

#ifdef WOLFSSL_DUAL_ALG_CERTS
    /* Copy over alternative sig and pubkey. In this case we will allocate new
     * buffers for them as we have no knowledge of when the DecodedCert is
     * freed. */
    if (dCert->extSapkiSet) {
        x509->sapkiDer = (byte*)XMALLOC(dCert->sapkiLen, x509->heap,
                                        DYNAMIC_TYPE_X509_EXT);
        if (x509->sapkiDer != NULL) {
            XMEMCPY(x509->sapkiDer, dCert->sapkiDer, dCert->sapkiLen);
            x509->sapkiLen = dCert->sapkiLen;
            x509->sapkiCrit = dCert->extSapkiCrit;
        }
        else {
            ret = MEMORY_E;
        }
    }
    if (dCert->extAltSigAlgSet) {
        x509->altSigAlgDer = (byte*)XMALLOC(dCert->altSigAlgLen, x509->heap,
                                            DYNAMIC_TYPE_X509_EXT);
        if (x509->altSigAlgDer != NULL) {
            XMEMCPY(x509->altSigAlgDer, dCert->altSigAlgDer,
                    dCert->altSigAlgLen);
            x509->altSigAlgLen = dCert->altSigAlgLen;
            x509->altSigAlgCrit = dCert->extAltSigAlgCrit;
        }
        else {
            ret = MEMORY_E;
        }
    }
    if (dCert->extAltSigValSet) {
        x509->altSigValDer = (byte*)XMALLOC(dCert->altSigValLen, x509->heap,
                                            DYNAMIC_TYPE_X509_EXT);
        if (x509->altSigValDer != NULL) {
            XMEMCPY(x509->altSigValDer, dCert->altSigValDer,
                    dCert->altSigValLen);
            x509->altSigValLen = dCert->altSigValLen;
            x509->altSigValCrit = dCert->extAltSigValCrit;
        }
        else {
            ret = MEMORY_E;
        }
    }
#endif /* WOLFSSL_DUAL_ALG_CERTS */

    return ret;
}

#endif /* KEEP_PEER_CERT || SESSION_CERTS */

#if defined(WOLFSSL_ACERT)
/* Copy a DecodedAcert structure to an X509_ACERT.
 *
 * @param [out]     x509        the dst X509 acert structure
 * @param [in]      dAcert      the src decoded acert structure
 *
 * @return  0   on success
 * @return  < 0 on error
 * */
int CopyDecodedAcertToX509(WOLFSSL_X509_ACERT* x509, DecodedAcert* dAcert)
{
    int ret = 0;

    if (x509 == NULL || dAcert == NULL) {
        return BAD_FUNC_ARG;
    }

    /* Copy version and serial number. */
    x509->version = dAcert->version + 1;

    XMEMCPY(x509->serial, dAcert->serial, EXTERNAL_SERIAL_SIZE);
    x509->serialSz = dAcert->serialSz;

    if (dAcert->holderSerialSz > 0) {
        /* This ACERT Holder field had a serial number. Copy it. */
        XMEMCPY(x509->holderSerial, dAcert->holderSerial,
                dAcert->holderSerialSz);
        x509->holderSerialSz = dAcert->holderSerialSz;
    }

    /* Copy before and after dates. */
    CopyDateToASN1_TIME(dAcert->beforeDate, dAcert->beforeDateLen,
        &x509->notBefore);
    CopyDateToASN1_TIME(dAcert->afterDate, dAcert->afterDateLen,
        &x509->notAfter);

    /* Copy the signature. */
    if (dAcert->signature != NULL && dAcert->sigLength != 0 &&
            dAcert->sigLength <= MAX_ENCODED_SIG_SZ) {
        x509->sig.buffer = (byte*)XMALLOC(
                          dAcert->sigLength, x509->heap, DYNAMIC_TYPE_SIGNATURE);
        if (x509->sig.buffer == NULL) {
            ret = MEMORY_E;
        }
        else {
            XMEMCPY(x509->sig.buffer, dAcert->signature, dAcert->sigLength);
            x509->sig.length = dAcert->sigLength;
            x509->sigOID = (int)dAcert->signatureOID;
        }
    }

    /* if der contains original source buffer then store for potential
     * retrieval */
    if (dAcert->source != NULL && dAcert->maxIdx > 0) {
        if (AllocDer(&x509->derCert, dAcert->maxIdx, CERT_TYPE, x509->heap)
                                                                         == 0) {
            XMEMCPY(x509->derCert->buffer, dAcert->source, dAcert->maxIdx);
        }
        else {
            ret = MEMORY_E;
        }
    }

    /* Copy holder and att cert issuer names if present. */
    if (CopyAltNames(&x509->holderIssuerName, dAcert->holderIssuerName,
                ASN_DIR_TYPE, x509->heap) != 0) {
        return MEMORY_E;
    }

    if (CopyAltNames(&x509->holderEntityName, dAcert->holderEntityName,
                ASN_DIR_TYPE, x509->heap) != 0) {
        return MEMORY_E;
    }

    if (CopyAltNames(&x509->AttCertIssuerName, dAcert->AttCertIssuerName,
                ASN_DIR_TYPE, x509->heap) != 0) {
        return MEMORY_E;
    }

    if (dAcert->rawAttr && dAcert->rawAttrLen > 0) {
        /* Allocate space for the raw Attributes field, then copy it in. */
        x509->rawAttr = (byte*)XMALLOC(dAcert->rawAttrLen, x509->heap,
                                       DYNAMIC_TYPE_X509_EXT);
        if (x509->rawAttr != NULL) {
            XMEMCPY(x509->rawAttr, dAcert->rawAttr, dAcert->rawAttrLen);
            x509->rawAttrLen = dAcert->rawAttrLen;
        }
        else {
            ret = MEMORY_E;
        }
    }

    return ret;
}
#endif /* WOLFSSL_ACERT */

#if (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
     defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)) && !defined(WOLFSSL_NO_TLS12)
#if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)
#ifndef NO_WOLFSSL_SERVER
static int CsrDoStatusVerifyCb(WOLFSSL* ssl, byte* input, word32 inputSz, word32 idx,
        int ret)
{
    if (ssl->ctx->ocspStatusVerifyCb != NULL) {
        int verRet;
        WOLFSSL_MSG("Calling OCSP status verify callback");
        verRet = ssl->ctx->ocspStatusVerifyCb(ssl, ret,
                input, inputSz, idx, ssl->ctx->ocspStatusVerifyCbArg);
        if (verRet > 0) {
            WOLFSSL_MSG("\tInvalid OCSP status verify callback return");
            ret = BAD_CERTIFICATE_STATUS_ERROR;
        }
        else {
            if (ret == 0 && verRet < 0)
                WOLFSSL_MSG("\tforcing error");
            else if (ret < 0 && verRet == 0)
                WOLFSSL_MSG("\toverriding error");
            ret = verRet;
        }
    }
    return ret;
}
#endif

static int ProcessCSR_ex(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                      word32 status_length, int idx)
{
    int ret = 0;
    OcspRequest* request;
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST)
    TLSX* ext =  TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST);
    CertificateStatusRequest* csr;
#endif
    #ifdef WOLFSSL_SMALL_STACK
        CertStatus* status;
        OcspEntry* single;
        OcspResponse* response;
    #else
        CertStatus status[1];
        OcspEntry single[1];
        OcspResponse response[1];
    #endif

    WOLFSSL_ENTER("ProcessCSR");
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST)
    if (ext) {
        /* status request */
        csr = (CertificateStatusRequest*)ext->data;
        if (csr && !csr->ssl)
            csr->ssl = ssl;
    }
#endif
    do {
        #ifdef HAVE_CERTIFICATE_STATUS_REQUEST
            if (ssl->status_request) {
                request = (OcspRequest*)TLSX_CSR_GetRequest_ex(ssl->extensions,
                                idx);
                ssl->status_request = 0;
                break;
            }
        #else
            (void)idx;
        #endif

        #ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
            if (ssl->status_request_v2) {
                request = (OcspRequest*)TLSX_CSR2_GetRequest(ssl->extensions,
                                                          WOLFSSL_CSR2_OCSP, 0);
                ssl->status_request_v2 = 0;
                break;
            }
        #endif

        return BUFFER_ERROR;
    } while(0);

    #ifdef WOLFSSL_SMALL_STACK
        status = (CertStatus*)XMALLOC(sizeof(CertStatus), ssl->heap,
                                      DYNAMIC_TYPE_OCSP_STATUS);
        single = (OcspEntry*)XMALLOC(sizeof(OcspEntry), ssl->heap,
                                     DYNAMIC_TYPE_OCSP_ENTRY);
        response = (OcspResponse*)XMALLOC(sizeof(OcspResponse), ssl->heap,
                                          DYNAMIC_TYPE_OCSP_REQUEST);

        if (status == NULL || single == NULL || response == NULL) {
            if (status != NULL) {
                XFREE(status, ssl->heap, DYNAMIC_TYPE_OCSP_STATUS);
            }
            if (single != NULL) {
                XFREE(single, ssl->heap, DYNAMIC_TYPE_OCSP_ENTRY);
            }
            if (response != NULL) {
                XFREE(response, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);
            }

            return MEMORY_ERROR;
        }
    #endif

    /* InitOcspResponse sets single and status to response struct. */
    InitOcspResponse(response, single, status, input +*inOutIdx, status_length,
                     ssl->heap);

    /* Extract producedDate */
    if (OcspResponseDecode(response, SSL_CM(ssl), ssl->heap, 1, 1) != 0)
        ret = BAD_CERTIFICATE_STATUS_ERROR;
    if (ret == 0) {
        XMEMCPY(ssl->ocspProducedDate, response->producedDate,
                sizeof(ssl->ocspProducedDate));
        ssl->ocspProducedDateFormat = response->producedDateFormat;

        /* Reset response */
        FreeOcspResponse(response);
        InitOcspResponse(response, single, status, input + *inOutIdx,
                         status_length, ssl->heap);

        if (OcspResponseDecode(response, SSL_CM(ssl), ssl->heap, 0, 0) != 0)
            ret = BAD_CERTIFICATE_STATUS_ERROR;
        else if (CompareOcspReqResp(request, response) != 0)
            ret = BAD_CERTIFICATE_STATUS_ERROR;
        else if (response->responseStatus != OCSP_SUCCESSFUL)
            ret = BAD_CERTIFICATE_STATUS_ERROR;
        else if (response->single->status->status == CERT_REVOKED)
            ret = OCSP_CERT_REVOKED;
        else if (response->single->status->status != CERT_GOOD)
            ret = BAD_CERTIFICATE_STATUS_ERROR;

#ifndef NO_WOLFSSL_SERVER
        ret = CsrDoStatusVerifyCb(ssl, input + *inOutIdx, status_length, idx, ret);
#endif
    }

    *inOutIdx += status_length;

    /* FreeOcspResponse frees status and single only if
     * single->isDynamic is set. */
    FreeOcspResponse(response);

    WC_FREE_VAR_EX(status, ssl->heap, DYNAMIC_TYPE_OCSP_STATUS);
    WC_FREE_VAR_EX(single, ssl->heap, DYNAMIC_TYPE_OCSP_ENTRY);
    WC_FREE_VAR_EX(response, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);

    WOLFSSL_LEAVE("ProcessCSR", ret);
    return ret;
}

static int ProcessCSR(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                      word32 status_length)
{
    return ProcessCSR_ex(ssl, input, inOutIdx, status_length, 0);
}
#endif
#endif



#ifdef HAVE_PK_CALLBACKS

#ifdef HAVE_ECC
    static int SigPkCbEccVerify(const unsigned char* sig, unsigned int sigSz,
       const unsigned char* hash, unsigned int hashSz,
       const unsigned char* keyDer, unsigned int keySz,
       int* result, void* ctx)
    {
        int ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
        WOLFSSL* ssl = (WOLFSSL*)ctx;

        if (ssl && ssl->ctx->EccVerifyCb) {
            ret = ssl->ctx->EccVerifyCb(ssl, sig, sigSz, hash, hashSz,
                keyDer, keySz, result, ssl->EccVerifyCtx);
        }
        return ret;
    }
#endif
#ifndef NO_RSA
    static int SigPkCbRsaVerify(unsigned char* sig, unsigned int sigSz,
       unsigned char** out, const unsigned char* keyDer, unsigned int keySz,
       void* ctx)
    {
        int ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);
        WOLFSSL* ssl = (WOLFSSL*)ctx;

        if (ssl && ssl->ctx->RsaVerifyCb) {
            ret = ssl->ctx->RsaVerifyCb(ssl, sig, sigSz, out, keyDer, keySz,
                ssl->RsaVerifyCtx);
        }
        return ret;
    }
#endif

int InitSigPkCb(WOLFSSL* ssl, SignatureCtx* sigCtx)
{
    if (ssl == NULL || sigCtx == NULL)
        return BAD_FUNC_ARG;

    /* only setup the verify callback if a PK is set */
#ifdef HAVE_ECC
    #if defined(WOLFSSL_RENESAS_FSPSM_TLS) || defined(WOLFSSL_RENESAS_TSIP_TLS)
    sigCtx->pkCbEcc = Renesas_cmn_SigPkCbEccVerify;
    sigCtx->pkCtxEcc = (void*)&sigCtx->CertAtt;
    (void)SigPkCbEccVerify;
    #else
    if (ssl->ctx->EccVerifyCb) {
        sigCtx->pkCbEcc = SigPkCbEccVerify;
        sigCtx->pkCtxEcc = ssl;
    }
    #endif

#endif
#ifndef NO_RSA
    /* only setup the verify callback if a PK is set */
    #if defined(WOLFSSL_RENESAS_FSPSM_TLS) || defined(WOLFSSL_RENESAS_TSIP_TLS)
    sigCtx->pkCbRsa = Renesas_cmn_SigPkCbRsaVerify;
    sigCtx->pkCtxRsa = (void*)&sigCtx->CertAtt;
    (void)SigPkCbRsaVerify;
    #else
    if (ssl->ctx->RsaVerifyCb) {
        sigCtx->pkCbRsa = SigPkCbRsaVerify;
        sigCtx->pkCtxRsa = ssl;
    }
    #endif

#endif

    return 0;
}

#endif /* HAVE_PK_CALLBACKS */

#if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)
void DoCertFatalAlert(WOLFSSL* ssl, int ret)
{
    int alertWhy;
    if (ssl == NULL || ret == 0) {
        return;
    }
    WOLFSSL_ERROR(ret);

    /* Determine alert reason */
    alertWhy = bad_certificate;
    if (ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E) ||
        ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) {
        alertWhy = certificate_expired;
    }
    else if (ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E) ||
             ret == WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E) ||
             ret == WC_NO_ERR_TRACE(ASN_PATHLEN_SIZE_E)) {
        alertWhy = unknown_ca;
    }
#ifdef OPENSSL_EXTRA
    else if (ret == WC_NO_ERR_TRACE(CRL_CERT_REVOKED)) {
        alertWhy = certificate_revoked;
    }
#endif
#if defined(HAVE_RPK)
    else if (ret == WC_NO_ERR_TRACE(UNSUPPORTED_CERTIFICATE)) {
        alertWhy = unsupported_certificate;
    }
#endif /* HAVE_RPK */
    else if (ret == WC_NO_ERR_TRACE(NO_PEER_CERT)) {
#ifdef WOLFSSL_TLS13
        if (ssl->options.tls1_3) {
            alertWhy = certificate_required;
        }
        else
#endif
        {
            alertWhy = handshake_failure;
        }
    }

#ifndef NO_TLS
    /* send fatal alert and mark connection closed */
    SendAlert(ssl, alert_fatal, alertWhy); /* try to send */
#else
    (void)alertWhy;
#endif
    ssl->options.isClosed = 1;
}

int SetupStoreCtxCallback(WOLFSSL_X509_STORE_CTX** store_pt,
        WOLFSSL* ssl, WOLFSSL_CERT_MANAGER* cm, ProcPeerCertArgs* args,
        int cert_err, void* heap, int* x509Free)
{
    WOLFSSL_X509_STORE_CTX* store = NULL;
    char* domain = NULL;
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    WOLFSSL_X509* x509 = NULL;
#endif

    *x509Free = 0;

    store = wolfSSL_X509_STORE_CTX_new_ex(heap);
    if (store == NULL)
        goto mem_error;
    domain = (char*)XMALLOC(ASN_NAME_MAX, heap, DYNAMIC_TYPE_STRING);
    if (domain == NULL)
        goto mem_error;

    domain[0] = '\0';

    /* build subject CN as string to return in store */
    if (args->dCertInit && args->dCert && args->dCert->subjectCN) {
        int subjectCNLen = args->dCert->subjectCNLen;
        if (subjectCNLen > ASN_NAME_MAX-1)
            subjectCNLen = ASN_NAME_MAX-1;
        if (subjectCNLen > 0) {
            XMEMCPY(domain, args->dCert->subjectCN, (size_t)(subjectCNLen));
            domain[subjectCNLen] = '\0';
        }
    }

#ifndef OPENSSL_COMPATIBLE_DEFAULTS
    store->error = cert_err;
#else
    store->error = GetX509Error(cert_err);
#endif
    store->error_depth = args->certIdx;
    store->discardSessionCerts = 0;
    store->domain = domain;
    if (ssl != NULL) {
        if (ssl->verifyCbCtx != NULL) {
            /* Use the WOLFSSL user context if set */
            store->userCtx = ssl->verifyCbCtx;
        }
        else {
            /* Else use the WOLFSSL_CTX user context */
            store->userCtx = ssl->ctx->verifyCbCtx;
        }
    }
    else {
        store->userCtx = cm;
    }
    store->certs = args->certs;
    store->totalCerts = args->totalCerts;
#if defined(HAVE_EX_DATA) && \
    (defined(OPENSSL_EXTRA) || defined(WOLFSSL_WPAS_SMALL))
    if (wolfSSL_CRYPTO_set_ex_data(&store->ex_data, 0, ssl)
            != WOLFSSL_SUCCESS) {
        WOLFSSL_MSG("Failed to store ssl context in WOLFSSL_X509_STORE_CTX");
    }
#endif

    if (ssl != NULL) {
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER)
        store->store = SSL_STORE(ssl);
#if defined(OPENSSL_EXTRA)
        store->depth = args->count;
        /* Overwrite with non-default param values in SSL */
        if (ssl->param) {
            if (ssl->param->check_time)
                store->param->check_time = ssl->param->check_time;

            if (ssl->param->flags)
                store->param->flags = ssl->param->flags;
#ifdef WOLFSSL_LOCAL_X509_STORE
            else if (SSL_STORE(ssl) && SSL_STORE(ssl)->param &&
                    SSL_STORE(ssl)->param->flags)
                store->param->flags = SSL_STORE(ssl)->param->flags;
#endif


            if (ssl->param->hostName[0])
                XMEMCPY(store->param->hostName, ssl->param->hostName,
                        WOLFSSL_HOST_NAME_MAX);

        }
#endif /* defined(OPENSSL_EXTRA) */
#endif /* defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER)*/
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    #ifdef KEEP_PEER_CERT
        if (args->certIdx == 0) {
            FreeX509(&ssl->peerCert);
            InitX509(&ssl->peerCert, 0, ssl->heap);
            if (CopyDecodedToX509(&ssl->peerCert, args->dCert) == 0)
                WOLFSSL_MSG("Unable to copy to ssl->peerCert");
            store->current_cert = &ssl->peerCert; /* use existing X509 */
        }
        else
    #endif
        {
            x509 = wolfSSL_X509_new_ex(heap);
            if (x509 == NULL)
                goto mem_error;
            if (CopyDecodedToX509(x509, args->dCert) == 0) {
                store->current_cert = x509;
                *x509Free = 1;
            }
            else {
                goto mem_error;
            }
        }
#endif
#ifdef SESSION_CERTS
        store->sesChain = &ssl->session->chain;
#endif
    }
    *store_pt = store;
    return 0;
mem_error:
    if (store != NULL)
        wolfSSL_X509_STORE_CTX_free(store);
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    if (x509 != NULL) {
        wolfSSL_X509_free(x509);
        x509 = NULL;
    }
#endif
    XFREE(domain, heap, DYNAMIC_TYPE_STRING);
    return MEMORY_E;
}

void CleanupStoreCtxCallback(WOLFSSL_X509_STORE_CTX* store,
        WOLFSSL* ssl, void* heap, int x509Free)
{
    (void)ssl;
    (void)x509Free;

#if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA)
    wolfSSL_sk_X509_pop_free(store->chain, NULL);
    store->chain = NULL;
#endif
#ifdef SESSION_CERTS
    if ((ssl != NULL) && (store->discardSessionCerts)) {
        WOLFSSL_MSG("Verify callback requested discard sess certs");
        ssl->session->chain.count = 0;
    #ifdef WOLFSSL_ALT_CERT_CHAINS
        ssl->session->altChain.count = 0;
    #endif
    }
#endif /* SESSION_CERTS */
    XFREE(store->domain, heap, DYNAMIC_TYPE_STRING);
    store->domain = NULL;
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
    if (x509Free)
        wolfSSL_X509_free(store->current_cert);
    store->current_cert = NULL;
#endif
    wolfSSL_X509_STORE_CTX_free(store);
}

/* WOLFSSL_ALWAYS_VERIFY_CB: Use verify callback for success or failure cases */
/* WOLFSSL_VERIFY_CB_ALL_CERTS: Issue callback for all intermediate certificates */

/* Callback is issued for certificate presented in TLS Certificate (11) packet.
 * The intermediates are done first then peer leaf cert last. Use the
 * store->error_depth member to determine index (0=peer, >1 intermediates)
 */

int DoVerifyCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl, int cert_err,
                                                        ProcPeerCertArgs* args)
{
    int verify_ok = 0, use_cb = 0, ret = cert_err;
    void *heap;

    if (cm == NULL) {
        return BAD_FUNC_ARG;
    }

    heap = (ssl != NULL) ? ssl->heap : cm->heap;

    /* Determine if verify was okay */
    if (cert_err == 0) {
        verify_ok = 1;
    }

    /* Determine if verify callback should be used */
    if (cert_err != 0) {
        if ((ssl != NULL) && (!ssl->options.verifyNone)) {
            use_cb = 1; /* always report errors */
        }
    }
#ifdef WOLFSSL_ALWAYS_VERIFY_CB
    /* always use verify callback on peer leaf cert */
    if (args->certIdx == 0) {
        use_cb = 1;
    }
#endif
#ifdef WOLFSSL_VERIFY_CB_ALL_CERTS
    /* perform verify callback on other intermediate certs (not just peer) */
    if (args->certIdx > 0) {
        use_cb = 1;
    }
#endif
#if defined(OPENSSL_EXTRA)
    /* Perform domain and IP check only for the leaf certificate */
    if (args->certIdx == 0) {
        /* perform domain name check on the peer certificate */
        if (args->dCertInit && args->dCert && (ssl != NULL) &&
                ssl->param && ssl->param->hostName[0]) {
            /* If altNames names is present, then subject common name is ignored */
            if (args->dCert->altNames != NULL) {
                if (CheckForAltNames(args->dCert, ssl->param->hostName,
                    (word32)XSTRLEN(ssl->param->hostName), NULL, 0, 0) != 1) {
                    if (cert_err == 0) {
                        ret = DOMAIN_NAME_MISMATCH;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                }
            }
        #ifndef WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
            else {
                if (args->dCert->subjectCN) {
                    if (MatchDomainName(
                            args->dCert->subjectCN,
                            args->dCert->subjectCNLen,
                            ssl->param->hostName,
                            (word32)XSTRLEN(ssl->param->hostName), 0) == 0) {
                        if (cert_err == 0) {
                            ret = DOMAIN_NAME_MISMATCH;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                    }
                }
            }
        #else
            else {
                if (cert_err == 0) {
                    ret = DOMAIN_NAME_MISMATCH;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        #endif /* !WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY */
        }

        /* perform IP address check on the peer certificate */
        if ((args->dCertInit != 0) && (args->dCert != NULL) && (ssl != NULL) &&
            (ssl->param != NULL) && (XSTRLEN(ssl->param->ipasc) > 0)) {
            if (CheckIPAddr(args->dCert, ssl->param->ipasc) != 0) {
                if (cert_err == 0) {
                    ret = IPADDR_MISMATCH;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        }
    }
#endif
    /* if verify callback has been set */
    if ((use_cb && (ssl != NULL) && ((ssl->verifyCallback != NULL)
    #ifdef OPENSSL_ALL
        || (ssl->ctx->verifyCertCb != NULL)
    #endif
    #if defined(WOLFSSL_LOCAL_X509_STORE) && \
        (defined(OPENSSL_ALL) || defined(WOLFSSL_QT))
        || (SSL_STORE(ssl) != NULL && SSL_STORE(ssl)->verify_cb != NULL)
    #endif
        ))
    #ifndef NO_WOLFSSL_CM_VERIFY
        || (cm->verifyCallback != NULL)
    #endif
        ) {
        int verifyFail = 0;
        WOLFSSL_X509_STORE_CTX* store = NULL;
        int x509Free = 0;
        int setupRet = SetupStoreCtxCallback(&store, ssl, cm, args, cert_err,
                heap, &x509Free);

        if (setupRet != 0)
            return setupRet;

    #ifndef NO_WOLFSSL_CM_VERIFY
        /* non-zero return code indicates failure override */
        if (cm->verifyCallback != NULL) {
            store->userCtx = cm;
            if (cm->verifyCallback(verify_ok, store)) {
                if (cert_err != 0) {
                    WOLFSSL_MSG("Verify CM callback overriding error!");
                    ret = 0;
                }
            }
            else {
                verifyFail = 1;
            }
        }
    #endif

        if (ssl != NULL) {
    #ifdef OPENSSL_ALL
            /* non-zero return code indicates failure override */
            if (ssl->ctx->verifyCertCb) {
                if (ssl->ctx->verifyCertCb(store, ssl->ctx->verifyCertCbArg)) {
                    if (cert_err != 0) {
                        WOLFSSL_MSG("Verify Cert callback overriding error!");
                        ret = 0;
                    }
                }
                else {
                    verifyFail = 1;
                }
            }
    #endif
            /* non-zero return code indicates failure override */
            if (ssl->verifyCallback) {
                if (ssl->verifyCallback(verify_ok, store)) {
                    if (cert_err != 0) {
                        WOLFSSL_MSG("Verify callback overriding error!");
                        ret = 0;
                    }
                }
                else {
                    verifyFail = 1;
                }
            }
#if defined(WOLFSSL_LOCAL_X509_STORE) && \
    (defined(OPENSSL_ALL) || defined(WOLFSSL_QT))
            if (SSL_STORE(ssl) != NULL && SSL_STORE(ssl)->verify_cb != NULL) {
                if (SSL_STORE(ssl)->verify_cb(verify_ok, store)) {
                    if (cert_err != 0) {
                        WOLFSSL_MSG("Store Verify callback overriding error!");
                        ret = 0;
                    }
                }
                else {
                    verifyFail = 1;
                }
            }
#endif
        }

        if (verifyFail) {
            /* induce error if one not present */
            if (cert_err == 0) {
                ret = VERIFY_CERT_ERROR;
                WOLFSSL_ERROR_VERBOSE(ret);
            }

            /* mark as verify error */
            args->verifyErr = 1;
        }
        CleanupStoreCtxCallback(store, ssl, heap, x509Free);
    }

    (void)heap;

    return ret;
}

#ifdef HAVE_CRL
void DoCrlCallback(WOLFSSL_CERT_MANAGER* cm, WOLFSSL* ssl,
        ProcPeerCertArgs* args, int* outRet)
{
#if defined(WOLFSSL_LOCAL_X509_STORE) && \
    (defined(OPENSSL_ALL) || defined(WOLFSSL_QT))
    int ret = 0;
    void* heap = (ssl != NULL) ? ssl->heap : cm->heap;
    WOLFSSL_X509_STORE* cert_store = (ssl != NULL) ? SSL_STORE(ssl) : NULL;

    if (cert_store != NULL && cert_store->get_crl_cb != NULL) {
        WOLFSSL_CRL* userCrl = NULL;
        WOLFSSL_X509_STORE_CTX* store = NULL;
        int x509Free = 0;

        ret = SetupStoreCtxCallback(&store, ssl, cm, args, 0, heap,
                &x509Free);
        if (ret != 0) {
            *outRet = ret;
            return;
        }

        ret = cert_store->get_crl_cb(store, &userCrl, store->current_cert);
        if (ret == 1 && userCrl != NULL) {
            /* Point to current cm to be able to verify CRL */
            userCrl->cm = SSL_CM(ssl);
            *outRet = CheckCertCRL(userCrl, args->dCert);
        }
        else
            *outRet = CRL_MISSING;

        if (userCrl != NULL)
            wolfSSL_X509_CRL_free(userCrl);
        CleanupStoreCtxCallback(store, ssl, heap, x509Free);
    }
#else
    (void)cm;
    (void)ssl;
    (void)args;
    (void)outRet;
#endif
}
#endif

static void FreeProcPeerCertArgs(WOLFSSL* ssl, void* pArgs)
{
    ProcPeerCertArgs* args = (ProcPeerCertArgs*)pArgs;

    (void)ssl;

    XFREE(args->certs, ssl->heap, DYNAMIC_TYPE_DER);
    args->certs = NULL;
#ifdef WOLFSSL_TLS13
    XFREE(args->exts, ssl->heap, DYNAMIC_TYPE_CERT_EXT);
    args->exts = NULL;
#endif
    if (args->dCert) {
        if (args->dCertInit) {
            FreeDecodedCert(args->dCert);
            args->dCertInit = 0;
        }
        XFREE(args->dCert, ssl->heap, DYNAMIC_TYPE_DCERT);
        args->dCert = NULL;
    }
}
#if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN) && \
    (defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_EXT)) && \
    !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR) && \
    !defined(NO_STDIO_FILESYSTEM)
/* load certificate file which has the form <hash>.(r)N[0..N]       */
/* in the folder.                                                   */
/* (r), in the case of CRL file                                     */
/* @param store  a pointer to X509_STORE structure                  */
/* @param issuer a pointer to X509_NAME that presents an issuer     */
/* @param type   X509_LU_X509 or X509_LU_CRL                        */
/* @return WOLFSSL_SUCCESS on successful, otherwise WOLFSSL_FAILURE */
int LoadCertByIssuer(WOLFSSL_X509_STORE* store, X509_NAME* issuer, int type)
{
    const int MAX_SUFFIX = 10;/* The number comes from CA_TABLE_SIZE=10 */
    int ret = WOLFSSL_SUCCESS;
    WOLFSSL_X509_LOOKUP* lookup;
    WOLFSSL_BY_DIR_entry* entry;
    WOLFSSL_BY_DIR_HASH   hash_tmp;
    WOLFSSL_BY_DIR_HASH*  ph = NULL;
    WOLFSSL_X509* x509;
    unsigned long hash = 0;
    char*   filename = NULL;
    const char* post = "";
    byte*   pbuf = NULL;
    int     len, num, i, idx;
    int    suffix = 0;
    int retHash = NOT_COMPILED_IN;
    byte dgt[WC_MAX_DIGEST_SIZE];

    WOLFSSL_ENTER("LoadCertByIssuer");

    /* sanity check */
    if (store == NULL || issuer == NULL || (type != X509_LU_X509 && type != X509_LU_CRL)) {
        return WOLFSSL_FAILURE;
    }
    lookup = &store->lookup;
    if (lookup->dirs == NULL || lookup->type != 1) {
        return WOLFSSL_FAILURE;
    }

    len = wolfSSL_i2d_X509_NAME_canon(issuer, &pbuf);
    if (len > 0) {
    #if defined(NO_SHA) && !defined(NO_SHA256)
        retHash = wc_Sha256Hash((const byte*)pbuf, len, dgt);
    #elif !defined(NO_SHA)
        retHash = wc_ShaHash((const byte*)pbuf, (word32)len, dgt);
    #endif
        wolfSSL_OPENSSL_free(pbuf);
        if (retHash == 0) {
            /* 4 bytes in little endian as unsigned long */
            hash = (((unsigned long)dgt[3] << 24) |
                    ((unsigned long)dgt[2] << 16) |
                    ((unsigned long)dgt[1] <<  8) |
                    ((unsigned long)dgt[0]));
        } else {
            WOLFSSL_MSG("failed hash operation");
            return WOLFSSL_FAILURE;
        }
    }

    /* try to load each hashed name file in path */
#if !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR)

    if (type == X509_LU_CRL) {
        post = "r";
    }

    num = wolfSSL_sk_BY_DIR_entry_num(lookup->dirs->dir_entry);

    for (i=0; i<num; i++) {

        entry = wolfSSL_sk_BY_DIR_entry_value(lookup->dirs->dir_entry, i);

        if (type == X509_LU_CRL && entry->hashes != NULL &&
            wolfSSL_sk_BY_DIR_HASH_num(entry->hashes) > 0) {
            /* lock the list */
            if (wc_LockMutex(&lookup->dirs->lock) != 0) {
                WOLFSSL_MSG("wc_LockMutex cdir Lock error");
                return BAD_MUTEX_E;
            }

            hash_tmp.hash_value = hash;
            idx = wolfSSL_sk_BY_DIR_HASH_find(entry->hashes, &hash_tmp);
            if (idx >= 0) {
                WOLFSSL_MSG("find hashed CRL in list");
                ph = wolfSSL_sk_BY_DIR_HASH_value(entry->hashes, idx);
                if (ph)
                    suffix = ph->last_suffix;
                else
                    suffix = 0;
            } else {
                ph = NULL;
                suffix = 0;
            }

            wc_UnLockMutex(&lookup->dirs->lock);
        }

        /* Additional buffer length for file name memory allocation :   */
        /* / <hashvalue>.(r)N\0                                         */
        /*|1|     8    |1|1|1|1|           => 13                        */
        len = (int)XSTRLEN(entry->dir_name) + 13;
        XFREE(filename, NULL, DYNAMIC_TYPE_OPENSSL);

        filename = (char*)XMALLOC(len, NULL, DYNAMIC_TYPE_OPENSSL);
        if (filename == NULL) {
            WOLFSSL_MSG("memory allocation error");
            return MEMORY_E;
        }

        /* set as FAILURE, if successfully loading cert of CRL, this becomes */
        /* WOLFSSL_SUCCESS                                                  */
        ret = WOLFSSL_FAILURE;

        for (; suffix < MAX_SUFFIX; suffix++) {
            /* /folder-path/<hash>.(r)N[0..9] */
            if (XSNPRINTF(filename, (size_t)len, "%s/%08lx.%s%d", entry->dir_name,
                                                       hash, post, suffix)
                >= len)
            {
                WOLFSSL_MSG("buffer overrun in LoadCertByIssuer");
                ret = BUFFER_E;
                break;
            }

            if(wc_FileExists(filename) == 0/*0 file exists */) {

                if (type == X509_LU_X509) {
                    x509 = wolfSSL_X509_load_certificate_file(filename,
                                                        WOLFSSL_FILETYPE_PEM);
                    if (x509 != NULL) {
                       ret = wolfSSL_X509_STORE_add_cert(store, x509);
                       wolfSSL_X509_free(x509);
                       x509 = NULL;
                    } else {
                       WOLFSSL_MSG("failed to load certificate");
                       ret = WOLFSSL_FAILURE;
                       break;
                    }
                }
                else if (type == X509_LU_CRL) {
#if defined(HAVE_CRL)
                    ret = wolfSSL_X509_load_crl_file(&store->lookup, filename,
                                                     entry->dir_type);
                    if (ret != WOLFSSL_SUCCESS) {
                        WOLFSSL_MSG("failed to load CRL");
                        break;
                    }
#else
                    WOLFSSL_MSG("CRL is not supported");
                    ret = WOLFSSL_FAILURE;
                    break;
#endif /* HAVE_CRL  */
                }
            } else
                break;
        }

        if (ret != WOLFSSL_SUCCESS) {
            WOLFSSL_MSG("not found file");
            ret = WOLFSSL_FAILURE;
        } else {
            if (type == X509_LU_CRL) {
                if (wc_LockMutex(&lookup->dirs->lock) != 0) {
                    WOLFSSL_MSG("wc_LockMutex cdir Lock error");
                    XFREE(filename, NULL, DYNAMIC_TYPE_OPENSSL);
                    WOLFSSL_ERROR_VERBOSE(BAD_MUTEX_E);
                    return BAD_MUTEX_E;
                }
                if (ph == NULL) {
                    ph = wolfSSL_BY_DIR_HASH_new();
                    if (ph == NULL) {
                        WOLFSSL_MSG("failed to allocate hash stack");
                        ret = WOLFSSL_FAILURE;
                    } else {
                        ph->hash_value = hash;
                        ph->last_suffix = suffix;

                        ret = wolfSSL_sk_BY_DIR_HASH_push(entry->hashes, ph) > 0
                                ? WOLFSSL_SUCCESS : WOLFSSL_FAILURE;
                    }
                }
                wc_UnLockMutex(&lookup->dirs->lock);
            }
        }

        XFREE(filename, NULL, DYNAMIC_TYPE_OPENSSL);
        filename = NULL;
    }
#else
    (void) type;
    (void) ret;
    (void) x509;
    (void) filename;
    (void) suffix;
    (void) num;
    (void) i;
    ret = WOLFSSL_NOT_IMPLEMENTED;
#endif
    WOLFSSL_LEAVE("LoadCertByIssuer", ret);

    return ret;
}
#endif


static int ProcessPeerCertParse(WOLFSSL* ssl, ProcPeerCertArgs* args,
    int certType, int verify, byte** pSubjectHash, int* pAlreadySigner)
{
    int ret = 0;
    buffer* cert;
    byte* subjectHash = NULL;
    int alreadySigner = 0;
    Signer *extraSigners = NULL;
#if defined(HAVE_RPK)
    int cType;
#endif
#ifdef WOLFSSL_SMALL_CERT_VERIFY
    int sigRet = 0;
#endif

    if (ssl == NULL || args == NULL
    #ifndef WOLFSSL_SMALL_CERT_VERIFY
        || args->dCert == NULL
    #endif
    ) {
        return BAD_FUNC_ARG;
    }

PRAGMA_GCC_DIAG_PUSH
PRAGMA_GCC("GCC diagnostic ignored \"-Wstrict-overflow\"")
    /* Surrounded in gcc pragma to avoid -Werror=strict-overflow when the
     * compiler optimizes out the check and assumes no underflow. Keeping the
     * check in place to handle multiple build configurations and future
     * changes. */

    /* check to make sure certificate index is valid */
    if (args->certIdx > args->count)
        return BUFFER_E;
PRAGMA_GCC_DIAG_POP

    /* check if returning from non-blocking OCSP */
    /* skip this section because cert is already initialized and parsed */
#ifdef WOLFSSL_NONBLOCK_OCSP
    if (args->lastErr == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
        args->lastErr = 0; /* clear error */
        return 0;
    }
#endif

#ifdef WOLFSSL_TRUST_PEER_CERT
    /* we have trusted peer */
    if (args->haveTrustPeer) {
        return 0;
    }
#endif

    /* get certificate buffer */
    cert = &args->certs[args->certIdx];

#ifdef WOLFSSL_SMALL_CERT_VERIFY
    if (verify == VERIFY) {
        /* for small cert verify, release decoded cert during signature check to
            reduce peak memory usage */
        if (args->dCert != NULL) {
            if (args->dCertInit) {
                FreeDecodedCert(args->dCert);
                args->dCertInit = 0;
            }
            XFREE(args->dCert, ssl->heap, DYNAMIC_TYPE_DCERT);
            args->dCert = NULL;
        }

        /* perform cert parsing and signature check */
        sigRet = wc_CheckCertSignature(cert->buffer, cert->length,
                                         ssl->heap, SSL_CM(ssl));
        /* fail on errors here after the ParseCertRelative call, so dCert is populated */

        /* verify name only in ParseCertRelative below, signature check done */
        verify = VERIFY_NAME;
    }
#endif /* WOLFSSL_SMALL_CERT_VERIFY */

    /* make sure the decoded cert structure is allocated and initialized */
    if (!args->dCertInit
    #ifdef WOLFSSL_SMALL_CERT_VERIFY
        || args->dCert == NULL
    #endif
    ) {
    #ifdef WOLFSSL_SMALL_CERT_VERIFY
        if (args->dCert == NULL) {
            args->dCert = (DecodedCert*)XMALLOC(
                                 sizeof(DecodedCert), ssl->heap,
                                 DYNAMIC_TYPE_DCERT);
            if (args->dCert == NULL) {
                return MEMORY_E;
            }
        }
    #endif

        InitDecodedCert(args->dCert, cert->buffer, cert->length, ssl->heap);

        args->dCertInit = 1;
        args->dCert->sigCtx.devId = ssl->devId;
    #ifdef WOLFSSL_ASYNC_CRYPT
        args->dCert->sigCtx.asyncCtx = ssl;
    #endif

    #ifdef HAVE_PK_CALLBACKS
        /* setup the PK callback context */
        ret = InitSigPkCb(ssl, &args->dCert->sigCtx);
        if (ret != 0)
            return ret;
    #endif
    }
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2) && !defined(NO_TLS)
    if (verify != NO_VERIFY && TLSX_CSR2_IsMulti(ssl->extensions)) {
        extraSigners = TLSX_CSR2_GetPendingSigners(ssl->extensions);
    }
#endif
    /* Parse Certificate */
    ret = ParseCertRelative(args->dCert, certType, verify, SSL_CM(ssl), extraSigners);

#if defined(HAVE_RPK)
    /* if cert type has negotiated with peer, confirm the cert received has
     * the same type.
     */
    if (ret == 0 ) {
        if (ssl->options.side ==  WOLFSSL_CLIENT_END) {
            if (ssl->options.rpkState.received_ServerCertTypeCnt == 1) {
                cType = ssl->options.rpkState.received_ServerCertTypes[0];
                if ((cType == WOLFSSL_CERT_TYPE_RPK && !args->dCert->isRPK) ||
                    (cType == WOLFSSL_CERT_TYPE_X509 && args->dCert->isRPK)) {
                    /* cert type mismatch */
                    WOLFSSL_MSG("unsupported certificate type received");
                    ret = UNSUPPORTED_CERTIFICATE;
                }
            }
        }
        else if (ssl->options.side == WOLFSSL_SERVER_END) {
            if (ssl->options.rpkState.received_ClientCertTypeCnt == 1) {
                cType = ssl->options.rpkState.sending_ClientCertTypes[0];
                if ((cType == WOLFSSL_CERT_TYPE_RPK && !args->dCert->isRPK) ||
                    (cType == WOLFSSL_CERT_TYPE_X509 && args->dCert->isRPK)) {
                    /* cert type mismatch */
                    WOLFSSL_MSG("unsupported certificate type received");
                    ret = UNSUPPORTED_CERTIFICATE;
                }
            }
        }
    }
#endif /* HAVE_RPK */

    /* perform below checks for date failure cases */
    if (ret == 0 ||
        ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) ||
        ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) {
        /* get subject and determine if already loaded */
    #ifndef NO_SKID
        if (args->dCert->extAuthKeyIdSet)
            subjectHash = args->dCert->extSubjKeyId;
        else
    #endif
            subjectHash = args->dCert->subjectHash;
        alreadySigner = AlreadySigner(SSL_CM(ssl), subjectHash);
    }

#ifdef WOLFSSL_SMALL_CERT_VERIFY
    /* get signature check failures from above */
    if (ret == 0)
        ret = sigRet;
#endif

    if (pSubjectHash)
        *pSubjectHash = subjectHash;
    if (pAlreadySigner)
        *pAlreadySigner = alreadySigner;

#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ret = wolfSSL_AsyncPush(ssl,
            args->dCert->sigCtx.asyncDev);
    }
#endif

#if defined(WOLFSSL_PUBLIC_ASN) && defined(HAVE_PK_CALLBACKS)
    /* This block gives the callback a chance to process the peer cert.
     * If there is no callback set or it returns NOT_COMPILED_IN, then the
     * original return code is returned. */
    if (ssl->ctx && ssl->ctx->ProcessPeerCertCb) {
        int new_ret = ssl->ctx->ProcessPeerCertCb(ssl, args->dCert);
        if (new_ret != WC_NO_ERR_TRACE(NOT_COMPILED_IN)) {
            ret = new_ret;
        }
    }
#endif /* WOLFSSL_PUBLIC_ASN && HAVE_PK_CALLBACKS */

    return ret;
}

/* Check key sizes for certs. Is redundant check since
   ProcessBuffer also performs this check. */
static int ProcessPeerCertCheckKey(WOLFSSL* ssl, ProcPeerCertArgs* args)
{
    int ret = 0;

    if (ssl->options.verifyNone) {
        return ret;
    }

    switch (args->dCert->keyOID) {
    #ifndef NO_RSA
        #ifdef WC_RSA_PSS
        case RSAPSSk:
        #endif
        case RSAk:
            if (ssl->options.minRsaKeySz < 0 ||
                    args->dCert->pubKeySize <
                     (word16)ssl->options.minRsaKeySz) {
                WOLFSSL_MSG(
                    "RSA key size in cert chain error");
                ret = RSA_KEY_SIZE_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
            break;
    #endif /* !NO_RSA */
    #ifdef HAVE_ECC
        case ECDSAk:
            if (ssl->options.minEccKeySz < 0 ||
                    args->dCert->pubKeySize <
                     (word16)ssl->options.minEccKeySz) {
                WOLFSSL_MSG(
                    "ECC key size in cert chain error");
                ret = ECC_KEY_SIZE_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
            break;
    #endif /* HAVE_ECC */
    #ifdef HAVE_ED25519
        case ED25519k:
            if (ssl->options.minEccKeySz < 0 ||
                    ED25519_KEY_SIZE < (word16)ssl->options.minEccKeySz) {
                WOLFSSL_MSG(
                    "ECC key size in cert chain error");
                ret = ECC_KEY_SIZE_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
            break;
    #endif /* HAVE_ED25519 */
    #ifdef HAVE_ED448
        case ED448k:
            if (ssl->options.minEccKeySz < 0 ||
                    ED448_KEY_SIZE < (word16)ssl->options.minEccKeySz) {
                WOLFSSL_MSG(
                    "ECC key size in cert chain error");
                ret = ECC_KEY_SIZE_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
            break;
    #endif /* HAVE_ED448 */
    #if defined(HAVE_FALCON)
        case FALCON_LEVEL1k:
            if (ssl->options.minFalconKeySz < 0 ||
                FALCON_LEVEL1_KEY_SIZE < (word16)ssl->options.minFalconKeySz) {
                WOLFSSL_MSG("Falcon key size in cert chain error");
                ret = FALCON_KEY_SIZE_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
            break;
        case FALCON_LEVEL5k:
            if (ssl->options.minFalconKeySz < 0 ||
                FALCON_LEVEL5_KEY_SIZE < (word16)ssl->options.minFalconKeySz) {
                WOLFSSL_MSG("Falcon key size in cert chain error");
                ret = FALCON_KEY_SIZE_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
            break;
    #endif /* HAVE_FALCON */
    #if defined(HAVE_DILITHIUM)
        #ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
        case DILITHIUM_LEVEL2k:
            if (ssl->options.minDilithiumKeySz < 0 ||
                DILITHIUM_LEVEL2_KEY_SIZE
                < (word16)ssl->options.minDilithiumKeySz) {
                WOLFSSL_MSG("Dilithium key size in cert chain error");
                ret = DILITHIUM_KEY_SIZE_E;
            }
            break;
        case DILITHIUM_LEVEL3k:
            if (ssl->options.minDilithiumKeySz < 0 ||
                DILITHIUM_LEVEL3_KEY_SIZE
                < (word16)ssl->options.minDilithiumKeySz) {
                WOLFSSL_MSG( "Dilithium key size in cert chain error");
                ret = DILITHIUM_KEY_SIZE_E;
            }
            break;
        case DILITHIUM_LEVEL5k:
            if (ssl->options.minDilithiumKeySz < 0 ||
                DILITHIUM_LEVEL5_KEY_SIZE
                < (word16)ssl->options.minDilithiumKeySz) {
                WOLFSSL_MSG("Dilithium key size in cert chain error");
                ret = DILITHIUM_KEY_SIZE_E;
            }
            break;
        #endif /* WOLFSSL_DILITHIUM_FIPS204_DRAFT */
        case ML_DSA_LEVEL2k:
            if (ssl->options.minDilithiumKeySz < 0 ||
                ML_DSA_LEVEL2_KEY_SIZE
                < (word16)ssl->options.minDilithiumKeySz) {
                WOLFSSL_MSG("Dilithium key size in cert chain error");
                ret = DILITHIUM_KEY_SIZE_E;
            }
            break;
        case ML_DSA_LEVEL3k:
            if (ssl->options.minDilithiumKeySz < 0 ||
                ML_DSA_LEVEL3_KEY_SIZE
                < (word16)ssl->options.minDilithiumKeySz) {
                WOLFSSL_MSG( "Dilithium key size in cert chain error");
                ret = DILITHIUM_KEY_SIZE_E;
            }
            break;
        case ML_DSA_LEVEL5k:
            if (ssl->options.minDilithiumKeySz < 0 ||
                ML_DSA_LEVEL5_KEY_SIZE
                < (word16)ssl->options.minDilithiumKeySz) {
                WOLFSSL_MSG("Dilithium key size in cert chain error");
                ret = DILITHIUM_KEY_SIZE_E;
            }
            break;
    #endif /* HAVE_DILITHIUM */
        default:
            WOLFSSL_MSG("Key size not checked");
            /* key not being checked for size if not in
               switch */
            break;
    }

    return ret;
}

#if defined(HAVE_OCSP) && defined(WOLFSSL_TLS13) \
        && defined(HAVE_CERTIFICATE_STATUS_REQUEST)
static int ProcessPeerCertsChainOCSPStatusCheck(WOLFSSL* ssl)
{
    int ret = 0;
    word32 i;
    word32 idx = 0;
    TLSX* ext =  TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST);
    CertificateStatusRequest* csr;

    if (ext) {
        csr = (CertificateStatusRequest*)ext->data;
        if (csr == NULL) {
            return 0;
        }
    } else
        return 0;

    /* error when leaf cert doesn't have certificate status */
    if (csr->requests < 1 || csr->responses[0].length == 0) {
        WOLFSSL_MSG("Leaf cert doesn't have certificate status.");
        return BAD_CERTIFICATE_STATUS_ERROR;
    }

    for (i = 0; i < csr->requests; i++) {
        if (csr->responses[i].length != 0) {
            ssl->status_request = 1;
            idx = 0;
            ret = ProcessCSR_ex(ssl,
                    csr->responses[i].buffer,
                    &idx, csr->responses[i].length, i);
            if (ret < 0) {
                WOLFSSL_ERROR_VERBOSE(ret);
                break;
            }
        }
        else {
            WOLFSSL_MSG("Intermediate cert doesn't have certificate status.");
        }
    }

    return ret;
}

#endif

#ifdef HAVE_CRL
static int ProcessPeerCertsChainCRLCheck(WOLFSSL* ssl, ProcPeerCertArgs* args)
{
    Signer* prev = NULL;
    int ret = 0;
    WOLFSSL_CERT_MANAGER* cm = SSL_CM(ssl);
    Signer* ca = args->dCert->ca;
    /* End loop if no more issuers found or if we have
     * found a self signed cert (ca == prev) */
    for (; ret == 0 && ca != NULL && ca != prev;
            prev = ca, ca = GetCAByName(cm, ca->issuerNameHash)) {
        ret = CheckCertCRL_ex(cm->crl, ca->issuerNameHash, NULL, 0,
                ca->serialHash, NULL, 0, NULL);
        if (ret != 0)
            DoCrlCallback(cm, ssl, args, &ret);
        if (ret != 0){
            WOLFSSL_ERROR_VERBOSE(ret);
            WOLFSSL_MSG("\tCRL check not ok");
            break;
        }
    }
    return ret;
}
#endif

#ifdef OPENSSL_EXTRA
/* account for verify params flag set */
static int AdjustCMForParams(WOLFSSL* ssl)
{
    int flags;
    WOLFSSL_X509_VERIFY_PARAM* param;

    param = wolfSSL_get0_param(ssl);
    flags = wolfSSL_X509_VERIFY_PARAM_get_flags(param);

    /* For now there is a possible contradiction of PARAM flags and store flags.
     * Do not disable CRL support if it has already been enabled with store. */
    if (flags == 0) {
        return WOLFSSL_SUCCESS;
    }
    return wolfSSL_X509_STORE_set_flags(SSL_STORE(ssl), flags);
}
#endif

int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                     word32 totalSz)
{
    int ret = 0;
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
    ProcPeerCertArgs* args = NULL;
    WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
#elif defined(WOLFSSL_SMALL_STACK)
    ProcPeerCertArgs* args = NULL;
#else
    ProcPeerCertArgs  args[1];
#endif
    byte* subjectHash = NULL;
    int alreadySigner = 0;

#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    int addToPendingCAs = 0;
#endif
    WOLFSSL_ENTER("ProcessPeerCerts");

#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
    if (ssl->async == NULL) {
        ssl->async = (struct WOLFSSL_ASYNC*)
                XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
                        DYNAMIC_TYPE_ASYNC);
        if (ssl->async == NULL)
            ERROR_OUT(MEMORY_E, exit_ppc);
    }
    args = (ProcPeerCertArgs*)ssl->async->args;
#ifdef WOLFSSL_ASYNC_CRYPT
    ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState);
    if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
        /* Check for error */
        if (ret < 0)
            goto exit_ppc;
    }
    else
#endif /* WOLFSSL_ASYNC_CRYPT */
#ifdef WOLFSSL_NONBLOCK_OCSP
    if (ssl->error == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
        /* Re-entry after non-blocking OCSP */
    #ifdef WOLFSSL_ASYNC_CRYPT
        /* if async operations not pending, reset error code */
        if (ret == WC_NO_ERR_TRACE(WC_NO_PENDING_E))
            ret = 0;
    #endif
    }
    else
#endif /* WOLFSSL_NONBLOCK_OCSP */
#elif defined(WOLFSSL_SMALL_STACK)
    args = (ProcPeerCertArgs*)XMALLOC(
        sizeof(ProcPeerCertArgs), ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (args == NULL) {
        ERROR_OUT(MEMORY_E, exit_ppc);
    }
#endif /* WOLFSSL_ASYNC_CRYPT || WOLFSSL_NONBLOCK_OCSP */
    {
        /* Reset state */
        ret = 0;
        ssl->options.asyncState = TLS_ASYNC_BEGIN;
        XMEMSET(args, 0, sizeof(ProcPeerCertArgs));
        args->idx = *inOutIdx;
        args->begin = *inOutIdx;
    #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
        ssl->async->freeArgs = FreeProcPeerCertArgs;
    #endif
    }

#ifdef OPENSSL_EXTRA
    /* account for verify params flag set */
    if (AdjustCMForParams(ssl) != WOLFSSL_SUCCESS) {
        WOLFSSL_MSG("Issue with updating store flags from PARAMS set");
        ERROR_OUT(WOLFSSL_FAILURE, exit_ppc);
    }
#endif

    switch (ssl->options.asyncState)
    {
        case TLS_ASYNC_BEGIN:
        {
            word32 listSz;

        #ifdef WOLFSSL_CALLBACKS
            if (ssl->hsInfoOn)
                AddPacketName(ssl, "Certificate");
            if (ssl->toInfoOn)
                AddLateName("Certificate", &ssl->timeoutInfo);
        #endif

        #ifdef WOLFSSL_TLS13
            if (ssl->options.tls1_3) {
                byte ctxSz;

                /* Certificate Request Context */
                if ((args->idx - args->begin) + OPAQUE8_LEN > totalSz)
                    ERROR_OUT(BUFFER_ERROR, exit_ppc);
                ctxSz = *(input + args->idx);
                args->idx++;
                if ((args->idx - args->begin) + ctxSz > totalSz)
                    ERROR_OUT(BUFFER_ERROR, exit_ppc);
            #ifndef NO_WOLFSSL_CLIENT
                /* Must be empty when received from server. */
                if (ssl->options.side == WOLFSSL_CLIENT_END) {
                    if (ctxSz != 0) {
                        WOLFSSL_ERROR_VERBOSE(INVALID_CERT_CTX_E);
                        ERROR_OUT(INVALID_CERT_CTX_E, exit_ppc);
                    }
                }
            #endif
            #ifndef NO_WOLFSSL_SERVER
                /* Must contain value sent in request. */
                if (ssl->options.side == WOLFSSL_SERVER_END) {
                    if (ssl->options.handShakeState != HANDSHAKE_DONE &&
                                                                   ctxSz != 0) {
                        WOLFSSL_ERROR_VERBOSE(INVALID_CERT_CTX_E);
                        ERROR_OUT(INVALID_CERT_CTX_E, exit_ppc);
                    }
                    else if (ssl->options.handShakeState == HANDSHAKE_DONE) {
                #ifdef WOLFSSL_POST_HANDSHAKE_AUTH
                         CertReqCtx* curr = ssl->certReqCtx;
                         CertReqCtx* prev = NULL;
                         while (curr != NULL) {
                             if ((ctxSz == curr->len) &&
                                 XMEMCMP(&curr->ctx, input + args->idx, ctxSz)
                                                                         == 0) {
                                     if (prev != NULL)
                                         prev->next = curr->next;
                                     else
                                         ssl->certReqCtx = curr->next;
                                     XFREE(curr, ssl->heap,
                                           DYNAMIC_TYPE_TMP_BUFFER);
                                     break;
                             }
                             prev = curr;
                             curr = curr->next;
                        }
                        if (curr == NULL)
                #endif
                        {
                            WOLFSSL_ERROR_VERBOSE(INVALID_CERT_CTX_E);
                            ERROR_OUT(INVALID_CERT_CTX_E, exit_ppc);
                        }
                    }
                }
            #endif
                args->idx += ctxSz;

                /* allocate buffer for cert extensions */
                args->exts = (buffer*)XMALLOC(sizeof(buffer) *
                     MAX_CHAIN_DEPTH, ssl->heap, DYNAMIC_TYPE_CERT_EXT);
                if (args->exts == NULL) {
                    ERROR_OUT(MEMORY_E, exit_ppc);
                }
            }
        #endif

            /* allocate buffer for certs */
            args->certs = (buffer*)XMALLOC(sizeof(buffer) * MAX_CHAIN_DEPTH,
                                            ssl->heap, DYNAMIC_TYPE_DER);
            if (args->certs == NULL) {
                ERROR_OUT(MEMORY_E, exit_ppc);
            }
            XMEMSET(args->certs, 0, sizeof(buffer) * MAX_CHAIN_DEPTH);

            /* Certificate List */
            if ((args->idx - args->begin) + OPAQUE24_LEN > totalSz) {
                ERROR_OUT(BUFFER_ERROR, exit_ppc);
            }
            c24to32(input + args->idx, &listSz);
#ifdef HAVE_RPK
            /*
             * If this is RPK from the peer, then single cert (if TLS1.2).
             * So, ListSz location is same as CertSz location, so fake
             * we have just seen this ListSz.
             */
            if (!IsAtLeastTLSv1_3(ssl->version) &&
                ((ssl->options.side == WOLFSSL_SERVER_END &&
                  ssl->options.rpkState.received_ClientCertTypeCnt == 1 &&
                  ssl->options.rpkState.received_ClientCertTypes[0] == WOLFSSL_CERT_TYPE_RPK) ||
                 (ssl->options.side == WOLFSSL_CLIENT_END &&
                  ssl->options.rpkState.received_ServerCertTypeCnt == 1 &&
                  ssl->options.rpkState.received_ServerCertTypes[0] == WOLFSSL_CERT_TYPE_RPK))) {
                listSz += OPAQUE24_LEN;
            } else
#endif /* HAVE_RPK */
            {
                args->idx += OPAQUE24_LEN;
            }
            if (listSz > MAX_CERTIFICATE_SZ) {
                ERROR_OUT(BUFFER_ERROR, exit_ppc);
            }
            if ((args->idx - args->begin) + listSz != totalSz) {
                ERROR_OUT(BUFFER_ERROR, exit_ppc);
            }

            WOLFSSL_MSG("Loading peer's cert chain");
            /* first put cert chain into buffer so can verify top down
               we're sent bottom up */
            while (listSz) {
                word32 certSz;


            #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                if (args->totalCerts >= MAX_CHAIN_DEPTH) {
                    if (ssl->peerVerifyRet == 0) /* Return first cert error here */
                        ssl->peerVerifyRet =
                                        WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG;
                    ret = MAX_CHAIN_ERROR;
                    WOLFSSL_ERROR_VERBOSE(ret);
                    WOLFSSL_MSG("Too many certs for MAX_CHAIN_DEPTH");
                    break; /* break out to avoid reading more certs then buffer
                            * can hold */
                }
            #else
                if (args->totalCerts >= ssl->verifyDepth ||
                        args->totalCerts >= MAX_CHAIN_DEPTH) {
                    WOLFSSL_ERROR_VERBOSE(MAX_CHAIN_ERROR);
                    ERROR_OUT(MAX_CHAIN_ERROR, exit_ppc);
                }
            #endif

                if ((args->idx - args->begin) + OPAQUE24_LEN > totalSz) {
                    ERROR_OUT(BUFFER_ERROR, exit_ppc);
                }

                c24to32(input + args->idx, &certSz);
                args->idx += OPAQUE24_LEN;

                if ((args->idx - args->begin) + certSz > totalSz) {
                    ERROR_OUT(BUFFER_ERROR, exit_ppc);
                }

                args->certs[args->totalCerts].length = certSz;
                args->certs[args->totalCerts].buffer = input + args->idx;

            #ifdef SESSION_CERTS
                AddSessionCertToChain(&ssl->session->chain,
                    input + args->idx, certSz);
            #endif /* SESSION_CERTS */

                args->idx += certSz;
                listSz -= certSz + CERT_HEADER_SZ;

            #ifdef WOLFSSL_TLS13
                /* Extensions */
                if (ssl->options.tls1_3) {
                    word16 extSz;

                    if (args->exts == NULL) {
                        ERROR_OUT(BUFFER_ERROR, exit_ppc);
                    }
                    if ((args->idx - args->begin) + OPAQUE16_LEN > totalSz) {
                        ERROR_OUT(BUFFER_ERROR, exit_ppc);
                    }
                    ato16(input + args->idx, &extSz);
                    args->idx += OPAQUE16_LEN;
                    if ((args->idx - args->begin) + extSz > totalSz) {
                        ERROR_OUT(BUFFER_ERROR, exit_ppc);
                    }
                    /* Store extension data info for later processing. */
                    args->exts[args->totalCerts].length = extSz;
                    args->exts[args->totalCerts].buffer = input + args->idx;
                    args->idx += extSz;
                    listSz -= extSz + OPAQUE16_LEN;
                    WOLFSSL_MSG_EX("\tParsing %d bytes of cert extensions",
                        args->exts[args->totalCerts].length);
                    #if !defined(NO_TLS)
                    #if defined(HAVE_CERTIFICATE_STATUS_REQUEST)
                    ssl->response_idx = args->totalCerts;
                    #endif
                    ret = TLSX_Parse(ssl, args->exts[args->totalCerts].buffer,
                        (word16)args->exts[args->totalCerts].length,
                        certificate, NULL);
                    #endif /* !NO_TLS */
                    if (ret < 0) {
                        WOLFSSL_ERROR_VERBOSE(ret);
                        ERROR_OUT(ret, exit_ppc);
                    }
                }
            #endif

                args->totalCerts++;
                WOLFSSL_MSG("\tPut another cert into chain");
            } /* while (listSz) */

            args->count = args->totalCerts;
            args->certIdx = 0; /* select peer cert (first one) */

            if (args->count == 0) {
                /* Empty certificate message. */
                if ((ssl->options.side == WOLFSSL_SERVER_END) &&
                    (ssl->options.mutualAuth || (ssl->options.failNoCert &&
                                             IsAtLeastTLSv1_2(ssl)))) {
                    WOLFSSL_MSG("No peer cert from Client");
                    ret = NO_PEER_CERT;
                    WOLFSSL_ERROR_VERBOSE(ret);
                    DoCertFatalAlert(ssl, ret);
                }
                else if ((ssl->options.side == WOLFSSL_CLIENT_END) &&
                         IsAtLeastTLSv1_2(ssl)) {
                    WOLFSSL_MSG("No peer cert from Server");
                    ret = NO_PEER_CERT;
                    WOLFSSL_ERROR_VERBOSE(ret);
                    SendAlert(ssl, alert_fatal, decode_error);
                }
            }

            args->dCertInit = 0;
        #ifndef WOLFSSL_SMALL_CERT_VERIFY
            args->dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), ssl->heap,
                                                       DYNAMIC_TYPE_DCERT);
            if (args->dCert == NULL) {
                ERROR_OUT(MEMORY_E, exit_ppc);
            }
            XMEMSET(args->dCert, 0, sizeof(DecodedCert));
        #endif

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_BUILD;
        } /* case TLS_ASYNC_BEGIN */
        FALL_THROUGH;

        case TLS_ASYNC_BUILD:
        {
            if (args->count > 0) {

                /* check for trusted peer and get untrustedDepth */
            #if defined(WOLFSSL_TRUST_PEER_CERT) || defined(OPENSSL_EXTRA)
                if (args->certIdx == 0) {
                #ifdef WOLFSSL_TRUST_PEER_CERT
                    TrustedPeerCert* tp;
                #endif

                    ret = ProcessPeerCertParse(ssl, args, CERT_TYPE, NO_VERIFY,
                        &subjectHash, &alreadySigner);
                    if (ret != 0)
                        goto exit_ppc;

                #ifdef OPENSSL_EXTRA
                    /* Determine untrusted depth */
                    if (!alreadySigner && (!args->dCert ||
                            !args->dCertInit || !args->dCert->selfSigned)) {
                        args->untrustedDepth = 1;
                    }
                #endif

                #ifdef WOLFSSL_TRUST_PEER_CERT
                    tp = GetTrustedPeer(SSL_CM(ssl), args->dCert);
                    WOLFSSL_MSG("Checking for trusted peer cert");

                    if (tp && MatchTrustedPeer(tp, args->dCert)) {
                        WOLFSSL_MSG("Found matching trusted peer cert");
                        args->haveTrustPeer = 1;
                    }
                    else if (tp == NULL) {
                        /* no trusted peer cert */
                        WOLFSSL_MSG("No matching trusted peer cert. Checking CAs");
                    }
                    else {
                        WOLFSSL_MSG("Trusted peer cert did not match!");
                    }
                    if (!args->haveTrustPeer)
                #endif
                    {
                        /* free cert if not trusted peer */
                        FreeDecodedCert(args->dCert);
                        args->dCertInit = 0;
                    }
                }
            #endif /* WOLFSSL_TRUST_PEER_CERT || OPENSSL_EXTRA */

                /* check certificate up to peer's first */
                /* do not verify chain if trusted peer cert found */
                while (args->count > 1
                #ifdef WOLFSSL_TRUST_PEER_CERT
                    && !args->haveTrustPeer
                #endif /* WOLFSSL_TRUST_PEER_CERT */
                ) {
                    int skipAddCA = 0;

                    /* select last certificate */
                    args->certIdx = args->count - 1;

                    ret = ProcessPeerCertParse(ssl, args, CHAIN_CERT_TYPE,
                        !ssl->options.verifyNone ? VERIFY : NO_VERIFY,
                        &subjectHash, &alreadySigner);
#if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN) && \
    (defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_EXT)) && \
    !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR) && \
    !defined(NO_STDIO_FILESYSTEM)
                    if (ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E) ||
                        ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)) {
                        WOLFSSL_MSG("try to load certificate if hash dir is set");
                        ret = LoadCertByIssuer(SSL_STORE(ssl),
                           (WOLFSSL_X509_NAME*)args->dCert->issuerName,
                                                          X509_LU_X509);
                        if (ret == WOLFSSL_SUCCESS) {
                            FreeDecodedCert(args->dCert);
                            args->dCertInit = 0;
                            /* once again */
                            ret = ProcessPeerCertParse(ssl, args, CHAIN_CERT_TYPE,
                                !ssl->options.verifyNone ? VERIFY : NO_VERIFY,
                                &subjectHash, &alreadySigner);
                        }
                        else {
                            ret = ASN_NO_SIGNER_E;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                    }
#endif
                #ifdef WOLFSSL_ASYNC_CRYPT
                    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                        goto exit_ppc;
                #endif
                    if (ret == 0 || ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN)) {
                        ret = ProcessPeerCertCheckKey(ssl, args);
                    }
                    else if (ret == WC_NO_ERR_TRACE(ASN_PARSE_E) ||
                             ret == WC_NO_ERR_TRACE(BUFFER_E) ||
                             ret == WC_NO_ERR_TRACE(MEMORY_E)) {
                        WOLFSSL_MSG(
                            "Got Peer cert ASN PARSE_E, BUFFER E, MEMORY_E");
                        ERROR_OUT(ret, exit_ppc);
                    }

                    if (ret == 0 && args->dCert->isCA == 0) {
                        WOLFSSL_MSG("Chain cert is not a CA, not adding as one");
                    }
                    else if (ret == 0 && ssl->options.verifyNone) {
                        WOLFSSL_MSG("Chain cert not verified by option, "
                            "not adding as CA");
                    }
                    else if (ret == 0) {
                    #ifdef OPENSSL_EXTRA
                        if (args->certIdx > args->untrustedDepth) {
                            args->untrustedDepth = (char)args->certIdx + 1;
                        }
                    #endif

                        if (alreadySigner) {
                            WOLFSSL_MSG("Verified CA from chain and already had it");
                        }
                    }
                    else {
                        WOLFSSL_MSG("Failed to verify CA from chain");
                    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                        if (ssl->peerVerifyRet == 0) /* Return first cert error here */
                            ssl->peerVerifyRet = WOLFSSL_X509_V_ERR_INVALID_CA;
                    #endif
                    }

                #ifdef HAVE_OCSP
                    if (ret == 0 ||
                            /* Don't enter when args->dCert is potentially in
                             * a bad state. */
                            (ret != WC_NO_ERR_TRACE(ASN_PARSE_E) &&
                             ret != WC_NO_ERR_TRACE(BUFFER_E) &&
                             ret != WC_NO_ERR_TRACE(MEMORY_E) &&
                             ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG))) {
                        /* If we are processing OCSP staples then always
                         * initialize the corresponding request. */
                        int ocspRet = 0;
                    #ifndef NO_TLS
                    #ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
                        addToPendingCAs = 0;
                        if (ssl->options.side == WOLFSSL_CLIENT_END &&
                            ssl->status_request_v2 &&
                            TLSX_CSR2_IsMulti(ssl->extensions)) {
                            ocspRet = TLSX_CSR2_InitRequests(ssl->extensions,
                                                    args->dCert, 0, ssl->heap);
                            addToPendingCAs = 1;
                        }
                        else /* skips OCSP and force CRL check */
                    #endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
                    #if defined(HAVE_CERTIFICATE_STATUS_REQUEST)
                        if (IsAtLeastTLSv1_3(ssl->version) &&
                            ssl->options.side == WOLFSSL_CLIENT_END &&
                            ssl->status_request) {
                            /* We check CSR in Certificate message sent from
                             * Server. Server side will check client
                             * certificates by traditional OCSP if enabled
                             */
                            ocspRet = TLSX_CSR_InitRequest_ex(ssl->extensions,
                                    args->dCert, ssl->heap, args->certIdx);
                        }
                        else
                    #endif
                    #endif /* NO_TLS */
                        if (ret == 0 && SSL_CM(ssl)->ocspEnabled &&
                                            SSL_CM(ssl)->ocspCheckAll) {
                            WOLFSSL_MSG("Doing Non Leaf OCSP check");
                            ret = CheckCertOCSP_ex(SSL_CM(ssl)->ocsp,
                                                    args->dCert, ssl);
                        #ifdef WOLFSSL_NONBLOCK_OCSP
                            if (ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
                                args->lastErr = ret;
                                goto exit_ppc;
                            }
                        #endif
                            if (ret != 0) {
                                WOLFSSL_ERROR_VERBOSE(ret);
                                WOLFSSL_MSG("\tOCSP Lookup not ok");
                            }
                        }
                        if (ocspRet != 0) {
                            ret = ocspRet;
                            WOLFSSL_ERROR_VERBOSE(ret);
                            goto exit_ppc;
                        }
                    }
                #endif /* HAVE_OCSP */

                    if (ret == 0 || ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN)) {
                #ifdef HAVE_CRL
                        if (SSL_CM(ssl)->crlEnabled &&
                                SSL_CM(ssl)->crlCheckAll) {
                            int doCrlLookup = 1;

                        #ifdef HAVE_OCSP
                            if (SSL_CM(ssl)->ocspEnabled &&
                                    SSL_CM(ssl)->ocspCheckAll) {
                                /* If the cert status is unknown to the OCSP
                                   responder, do a CRL lookup. If any other
                                   error, skip the CRL lookup and fail the
                                   certificate. */
                                doCrlLookup = (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN));
                            }
                        #endif /* HAVE_OCSP */

                            if (doCrlLookup) {
                                WOLFSSL_MSG("Doing Non Leaf CRL check");
                                ret = CheckCertCRL(SSL_CM(ssl)->crl,
                                        args->dCert);
                            #ifdef WOLFSSL_NONBLOCK_OCSP
                                /* The CRL lookup I/O callback is using the
                                 * same WOULD_BLOCK error code as OCSP's I/O
                                 * callback, and it is enabling it using the
                                 * same flag. */
                                if (ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
                                    args->lastErr = ret;
                                    goto exit_ppc;
                                }
                            #endif
                                if (ret != 0)
                                    DoCrlCallback(SSL_CM(ssl), ssl, args, &ret);
                                if (ret != 0) {
                                    WOLFSSL_ERROR_VERBOSE(ret);
                                    WOLFSSL_MSG("\tCRL check not ok");
                                }
                                if (ret == 0 &&
                                        args->certIdx == args->totalCerts-1) {
                                    ret = ProcessPeerCertsChainCRLCheck(ssl,
                                            args);
                                    if (ret != 0) {
                                        WOLFSSL_ERROR_VERBOSE(ret);
                                        WOLFSSL_MSG("\tCRL chain check not ok");
                                        args->fatal = 0;
                                    }
                                }
                            }
                        }
                #endif /* HAVE_CRL */
                    }
            #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                    if (ret == 0 &&
                        /* extend the limit "+1" until reaching
                         * an ultimately trusted issuer.*/
                        args->count > (ssl->verifyDepth + 1)) {
                        if (ssl->peerVerifyRet == 0) /* Return first cert error here */
                            ssl->peerVerifyRet =
                                         WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG;
                        ret = MAX_CHAIN_ERROR;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
            #endif
                #ifdef WOLFSSL_ALT_CERT_CHAINS
                    /* For alternate cert chain, its okay for a CA cert to fail
                        with ASN_NO_SIGNER_E here. The "alternate" certificate
                        chain mode only requires that the peer certificate
                        validate to a trusted CA */
                    if (ret != 0 && args->dCert->isCA) {
                        if (ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E) ||
                            ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)) {
                            if (!ssl->options.usingAltCertChain) {
                                WOLFSSL_MSG_CERT_LOG(
                                                 "Trying alternate cert chain");
                                ssl->options.usingAltCertChain = 1;
                            }

                            ret = 0; /* clear errors and continue */
                    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                            ssl->peerVerifyRet = 0;
                    #endif
                            args->verifyErr = 0;

                            /* do not add to certificate manager */
                            skipAddCA = 1;
                        } /* ASN_NO_SIGNER_E || ASN_SELF_SIGNED_E */
                    } /* ret != 0 && isCA */
                #else
                    /* Not defined: WOLFSSL_ALT_CERT_CHAINS
                     * When WOLFSSL_DEBUG_CERTS enabled, suggest solution */
                    #ifdef WOLFSSL_DEBUG_CERTS
                    if (ret != 0 && args->dCert->isCA) {
                        if (ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)) {
                            WOLFSSL_MSG_CERT(
                                  "Consider enabling WOLFSSL_ALT_CERT_CHAINS"
                                  " to resolve ASN_NO_SIGNER_E");
                        }
                        if (ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)) {
                            WOLFSSL_MSG_CERT(
                                  "Consider enabling WOLFSSL_ALT_CERT_CHAINS"
                                  " to resolve ASN_SELF_SIGNED_E");
                        }
                    } /* check alt-cert possible fixable error codes */
                    #endif
                #endif /* WOLFSSL_ALT_CERT_CHAINS */

                #if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
                    /* If we are using native Apple CA validation, it is okay
                     * for a CA cert to fail validation here, as we will verify
                     * the entire chain when we hit the peer (leaf) cert */
                    if ((ssl->ctx->doAppleNativeCertValidationFlag)
                        && (ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E))) {

                        WOLFSSL_MSG("Bypassing errors to allow for Apple native"
                                    " CA validation");
                        ret = 0; /* clear errors and continue */
                        args->verifyErr = 0;
                        #if defined(OPENSSL_EXTRA) \
                            || defined(OPENSSL_EXTRA_X509_SMALL)
                        ssl->peerVerifyRet = 0;
                        #endif
                        /* do not add to certificate manager */
                        skipAddCA = 1;
                    }
                #endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */

                    /* Do verify callback */
                    ret = DoVerifyCallback(SSL_CM(ssl), ssl, ret, args);
                    if (ssl->options.verifyNone &&
                              (ret == WC_NO_ERR_TRACE(CRL_MISSING) ||
                               ret == WC_NO_ERR_TRACE(CRL_CERT_REVOKED) ||
                               ret == WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR))) {
                        WOLFSSL_MSG("Ignoring CRL problem based on verify setting");
                        ret = ssl->error = 0;
                    }

                #ifdef WOLFSSL_ALT_CERT_CHAINS
                    if (ret != 0 && args->dCert->isCA) {
                        /* do not add to certificate manager */
                        skipAddCA = 1;
                    }
                #endif
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
                    if (ret == 0 && addToPendingCAs && !alreadySigner) {
                        WC_DECLARE_VAR(dCertAdd, DecodedCert, 1, 0);
                        int dCertAdd_inited = 0;
                        DerBuffer *derBuffer = NULL;
                        buffer* cert = &args->certs[args->certIdx];
                        Signer *s = NULL;

                        WC_ALLOC_VAR_EX(dCertAdd, DecodedCert, 1, ssl->heap,
                            DYNAMIC_TYPE_TMP_BUFFER,
                        {
                            ret=MEMORY_E;
                            goto exit_req_v2;
                        });
                        InitDecodedCert(dCertAdd, cert->buffer, cert->length,
                                        ssl->heap);
                        dCertAdd_inited = 1;
                        ret = ParseCert(dCertAdd, CA_TYPE, NO_VERIFY,
                                        SSL_CM(ssl));
                        if (ret != 0) {
                            goto exit_req_v2;
                        }
                        ret = AllocDer(&derBuffer, cert->length, CA_TYPE, ssl->heap);
                        if (ret != 0 || derBuffer == NULL) {
                            goto exit_req_v2;
                        }
                        XMEMCPY(derBuffer->buffer, cert->buffer, cert->length);
                        s = MakeSigner(SSL_CM(ssl)->heap);
                        if (s == NULL) {
                            ret = MEMORY_E;
                            goto exit_req_v2;
                        }
                        ret = FillSigner(s, dCertAdd, CA_TYPE, derBuffer);
                        if (ret != 0) {
                            goto exit_req_v2;
                        }
                        skipAddCA = 1;
                        ret = TLSX_CSR2_AddPendingSigner(ssl->extensions, s);

                    exit_req_v2:
                        if (s && (ret != 0))
                            FreeSigner(s, SSL_CM(ssl)->heap);
                        if (derBuffer)
                            FreeDer(&derBuffer);
                        if (dCertAdd_inited)
                            FreeDecodedCert(dCertAdd);
                        WC_FREE_VAR_EX(dCertAdd, ssl->heap,
                            DYNAMIC_TYPE_TMP_BUFFER);
                        if (ret != 0)
                            goto exit_ppc;
                    }
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */

                    /* If valid CA then add to Certificate Manager */
                    if (ret == 0 && args->dCert->isCA &&
                            !ssl->options.verifyNone && !skipAddCA) {
                        buffer* cert = &args->certs[args->certIdx];

                        /* Is valid CA */
                    #if defined(SESSION_CERTS) && defined(WOLFSSL_ALT_CERT_CHAINS)
                        /* if using alternate chain, store the cert used */
                        if (ssl->options.usingAltCertChain) {
                            AddSessionCertToChain(&ssl->session->altChain,
                                cert->buffer, cert->length);
                        }
                    #endif /* SESSION_CERTS && WOLFSSL_ALT_CERT_CHAINS */
                        if (!alreadySigner) {
                            DerBuffer* add = NULL;
                            ret = AllocDer(&add, cert->length, CA_TYPE, ssl->heap);
                            if (ret < 0)
                                goto exit_ppc;

                            XMEMCPY(add->buffer, cert->buffer, cert->length);

                            /* CA already verified above in ParseCertRelative */
                            WOLFSSL_MSG("Adding CA from chain");
                            SSL_CM_WARNING(ssl);
                            ret = AddCA(SSL_CM(ssl), &add, WOLFSSL_CHAIN_CA,
                                NO_VERIFY);
                            if (ret == WOLFSSL_SUCCESS) {
                                ret = 0;
                            }
                        }
                    }

                    /* Handle error codes */
                    ssl->error = ret; /* Report SSL error or clear error if
                                       * callback overrides. */
                    if (ret != 0) {
                        if (!ssl->options.verifyNone) {
                            WOLFSSL_ERROR_VERBOSE(ret);
                            DoCertFatalAlert(ssl, ret);
                            args->lastErr = ret;
                            break; /* We sent a fatal alert.
                                    * No point continuing. */
                        }
                        if (args->lastErr == 0) {
                            args->lastErr = ret; /* save error from last time */
                            ret = 0; /* reset error */
                        }
                    }

                    FreeDecodedCert(args->dCert);
                    args->dCertInit = 0;
                    args->count--;
                } /* while (count > 1 && !args->haveTrustPeer) */
            } /* if (count > 0) */

            /* Check for error */
            if (ret != 0) {
                goto exit_ppc;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_DO;
        } /* case TLS_ASYNC_BUILD */
        FALL_THROUGH;

        case TLS_ASYNC_DO:
        {
            /* peer's, may not have one if blank client cert sent by TLSv1.2 */
            if (args->count > 0) {
                WOLFSSL_MSG("Verifying Peer's cert");

                /* select peer cert (first one) */
                args->certIdx = 0;

                ret = ProcessPeerCertParse(ssl, args, CERT_TYPE,
                        !ssl->options.verifyNone ? VERIFY : NO_VERIFY,
                        &subjectHash, &alreadySigner);
#if defined(OPENSSL_ALL) && defined(WOLFSSL_CERT_GEN) && \
    (defined(WOLFSSL_CERT_REQ) || defined(WOLFSSL_CERT_EXT)) && \
    !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR) && \
    !defined(NO_STDIO_FILESYSTEM)
                    if (ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E) ||
                        ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E)) {
                        int lastErr = ret; /* save error from last time */
                        WOLFSSL_MSG("try to load certificate if hash dir is set");
                        ret = LoadCertByIssuer(SSL_STORE(ssl),
                           (WOLFSSL_X509_NAME*)args->dCert->issuerName,
                                                          X509_LU_X509);
                        if (ret == WOLFSSL_SUCCESS) {
                            FreeDecodedCert(args->dCert);
                            args->dCertInit = 0;
                            /* once again */
                            ret = ProcessPeerCertParse(ssl, args, CERT_TYPE,
                                !ssl->options.verifyNone ? VERIFY : NO_VERIFY,
                                &subjectHash, &alreadySigner);
                        }
                        else {
                            ret = lastErr; /* restore error */
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                    }
#endif
            #ifdef WOLFSSL_ASYNC_CRYPT
                if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                    goto exit_ppc;
            #endif

                if (ret == 0) {
                    WOLFSSL_MSG("Verified Peer's cert");
                #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                    if (ssl->peerVerifyRet == 0) /* Return first cert error here */
                        ssl->peerVerifyRet = WOLFSSL_X509_V_OK;
                #endif
                #if defined(SESSION_CERTS) && defined(WOLFSSL_ALT_CERT_CHAINS)
                    /* if using alternate chain, store the cert used */
                    if (ssl->options.usingAltCertChain) {
                        buffer* cert = &args->certs[args->certIdx];
                        AddSessionCertToChain(&ssl->session->altChain,
                            cert->buffer, cert->length);
                    }
                #endif /* SESSION_CERTS && WOLFSSL_ALT_CERT_CHAINS */

                #ifndef OPENSSL_COMPATIBLE_DEFAULTS
                    /* Check peer's certificate version number. TLS 1.2 / 1.3
                     * requires the clients certificate be version 3 unless a
                     * different version has been negotiated using RFC 7250.
                     * OpenSSL doesn't appear to be performing this check.
                     * For TLS 1.3 see RFC8446 Section 4.4.2.3 */
                    if (ssl->options.side == WOLFSSL_SERVER_END) {
                #if defined(HAVE_RPK)
                        if (args->dCert->isRPK) {
                            /* to verify Raw Public Key cert, DANE(RFC6698)
                             * should be introduced. Without DANE, no
                             * authentication is performed.
                             */
                        #if defined(HAVE_DANE)
                            if (ssl->useDANE) {
                                /* DANE authentication should be added */
                            }
                        #endif /* HAVE_DANE */
                        }
                        else /* skip followingx509 version check */
                #endif  /* HAVE_RPK */
                        if (args->dCert->version != WOLFSSL_X509_V3) {
                            WOLFSSL_MSG("Peers certificate was not version 3!");
                            args->lastErr = ASN_VERSION_E;
                            /* setting last error but not considering it fatal
                             * giving the user a chance to override */
                        }
                    }
                #endif

                    /* check if fatal error */
                    if (args->verifyErr) {
                        args->fatal = 1;
                        ret = args->lastErr;
                    }
                    else {
                        args->fatal = 0;
                    }
                }
                else if (ret == WC_NO_ERR_TRACE(ASN_PARSE_E) ||
                         ret == WC_NO_ERR_TRACE(BUFFER_E) ||
                         ret == WC_NO_ERR_TRACE(MEMORY_E) ||
                         ret == WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
                    WOLFSSL_MSG("Got Peer cert ASN_PARSE_E, BUFFER_E, MEMORY_E,"
                                " BAD_FUNC_ARG");
                #if defined(WOLFSSL_EXTRA_ALERTS) || defined(OPENSSL_EXTRA) || \
                                               defined(OPENSSL_EXTRA_X509_SMALL)
                    DoCertFatalAlert(ssl, ret);
                #endif
                #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                    if (ssl->peerVerifyRet == 0) /* Return first cert error here */
                        ssl->peerVerifyRet = WOLFSSL_X509_V_ERR_CERT_REJECTED;
                #endif
                    args->fatal = 1;
                }
                else {
                    WOLFSSL_MSG("Failed to verify Peer's cert");

                    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                    if (ssl->peerVerifyRet == 0) { /* Return first cert error here */
                        if (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E)) {
                            ssl->peerVerifyRet =
                           (unsigned long)WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID;
                        }
                        else if (ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) {
                            ssl->peerVerifyRet =
                            (unsigned long)WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED;
                        }
                        else {
                            ssl->peerVerifyRet =
                                   (unsigned long)
                            WOLFSSL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE;
                        }
                    }
                    #endif

                    #if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
                    /* If we can't validate the peer cert chain against the CAs
                     * loaded into wolfSSL, try to validate against the system
                     * certificates using Apple's native trust APIs BEFORE
                     * calling the verify callback so the callback sees the
                     * correct validation result */
                    if ((ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)) &&
                        (ssl->ctx->doAppleNativeCertValidationFlag)) {
                        if (DoAppleNativeCertValidation(ssl, args->certs,
                                                             args->totalCerts)) {
                            WOLFSSL_MSG("Apple native cert chain validation "
                                        "SUCCESS");
                            ret = 0;
                        }
                        else {
                            WOLFSSL_MSG("Apple native cert chain validation "
                                        "FAIL");
                        }
                    }
                    #endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */

                    /* Do verify callback. */
                    args->leafVerifyErr = ret =
                            DoVerifyCallback(SSL_CM(ssl), ssl, ret, args);

                    if (ret != 0) {
                        WOLFSSL_MSG("\tfatal cert error");
                        args->fatal = 1;
                        DoCertFatalAlert(ssl, ret);
                    }
                }

            #ifdef HAVE_SECURE_RENEGOTIATION
                if (args->fatal == 0 && !IsAtLeastTLSv1_3(ssl->version)
                                     && ssl->secure_renegotiation
                                     && ssl->secure_renegotiation->enabled) {

                    if (IsEncryptionOn(ssl, 0)) {
                        /* compare against previous time */
                        if (ssl->secure_renegotiation->subject_hash_set) {
                            if (XMEMCMP(args->dCert->subjectHash,
                                    ssl->secure_renegotiation->subject_hash,
                                    KEYID_SIZE) != 0) {
                                WOLFSSL_MSG(
                                  "Peer sent different cert during scr, fatal");
                                args->fatal = 1;
                                ret = SCR_DIFFERENT_CERT_E;
                                WOLFSSL_ERROR_VERBOSE(ret);
                            }
                        }
                    }

                    /* cache peer's hash */
                    if (args->fatal == 0) {
                        XMEMCPY(ssl->secure_renegotiation->subject_hash,
                                args->dCert->subjectHash, KEYID_SIZE);
                        ssl->secure_renegotiation->subject_hash_set = 1;
                    }
                }
            #endif /* HAVE_SECURE_RENEGOTIATION */
            } /* if (count > 0) */

            /* Check for error */
            if (args->fatal && ret != 0) {
                goto exit_ppc;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_VERIFY;
        } /* case TLS_ASYNC_DO */
        FALL_THROUGH;

        case TLS_ASYNC_VERIFY:
        {
            if (args->count > 0) {
            #if defined(HAVE_OCSP) || defined(HAVE_CRL)
                /* only attempt to check OCSP or CRL if not previous error such
                 * as ASN_BEFORE_DATE_E or ASN_AFTER_DATE_E */
                if (args->fatal == 0 && ret == 0) {
                    int doLookup = 1;

                    WOLFSSL_MSG("Checking if ocsp needed");

                    if (ssl->options.side == WOLFSSL_CLIENT_END) {
                #ifndef NO_TLS
                #ifdef HAVE_CERTIFICATE_STATUS_REQUEST
                        if (ssl->status_request) {
                            args->fatal = (TLSX_CSR_InitRequest_ex(
                                                ssl->extensions, args->dCert,
                                                ssl->heap, args->certIdx) != 0);
                            doLookup = 0;
                            WOLFSSL_MSG("\tHave status request");
                        #if defined(WOLFSSL_TLS13)
                            if (ssl->options.tls1_3) {
                                ret = ProcessPeerCertsChainOCSPStatusCheck(ssl);
                                if (ret < 0) {
                                    WOLFSSL_ERROR_VERBOSE(ret);
                                    goto exit_ppc;
                                }
                            }
                        #endif
                        }
                        /* Ensure a stapling response was seen */
                        else if (ssl->options.tls1_3 &&
                                                 SSL_CM(ssl)->ocspMustStaple) {
                             ret = OCSP_CERT_UNKNOWN;
                             goto exit_ppc;
                        }
                #endif /* HAVE_CERTIFICATE_STATUS_REQUEST */
                #ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
                        if (ssl->status_request_v2) {
                            args->fatal = (TLSX_CSR2_InitRequests(ssl->extensions,
                                                 args->dCert, 1, ssl->heap) != 0);
                            doLookup = 0;
                            WOLFSSL_MSG("\tHave status request v2");
                        }
                #endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
                #endif /* !NO_TLS */
                    }

                #ifdef HAVE_OCSP
                    if (doLookup && SSL_CM(ssl)->ocspEnabled) {
                        WOLFSSL_MSG("Doing Leaf OCSP check");
                        ret = CheckCertOCSP_ex(SSL_CM(ssl)->ocsp,
                                                    args->dCert, ssl);
                    #ifdef WOLFSSL_NONBLOCK_OCSP
                        if (ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
                            goto exit_ppc;
                        }
                    #endif
                        doLookup = (ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN));
                        if (ret != 0) {
                            WOLFSSL_MSG("\tOCSP Lookup not ok");
                            args->fatal = 0;
                        #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                            if (ssl->peerVerifyRet == 0) {
                                /* Return first cert error here */
                                ssl->peerVerifyRet =
                                    ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED)
                                    ? WOLFSSL_X509_V_ERR_CERT_REVOKED
                                    : WOLFSSL_X509_V_ERR_CERT_REJECTED;
                            }
                        #endif
                        }
                    }
                #endif /* HAVE_OCSP */

                #ifdef HAVE_CRL
                    if ((ret == 0 || ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN))
                            && doLookup && SSL_CM(ssl)->crlEnabled) {
                        WOLFSSL_MSG("Doing Leaf CRL check");
                        ret = CheckCertCRL(SSL_CM(ssl)->crl, args->dCert);
                    #ifdef WOLFSSL_NONBLOCK_OCSP
                        /* The CRL lookup I/O callback is using the
                         * same WOULD_BLOCK error code as OCSP's I/O
                         * callback, and it is enabling it using the
                         * same flag. */
                        if (ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
                            goto exit_ppc;
                        }
                    #endif
                        if (ret != 0)
                            DoCrlCallback(SSL_CM(ssl), ssl, args, &ret);
                        if (ret != 0) {
                            WOLFSSL_MSG("\tCRL check not ok");
                            args->fatal = 0;
                        #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                            if (ssl->peerVerifyRet == 0) {
                                /* Return first cert error here */
                                ssl->peerVerifyRet =
                                        ret == WC_NO_ERR_TRACE(CRL_CERT_REVOKED)
                                            ? WOLFSSL_X509_V_ERR_CERT_REVOKED
                                            : WOLFSSL_X509_V_ERR_CERT_REJECTED;
                            }
                        #endif
                        }
                    }
                    if ((ret == 0 || ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN))
                            && doLookup && SSL_CM(ssl)->crlEnabled &&
                            SSL_CM(ssl)->crlCheckAll && args->totalCerts == 1) {
                        /* Check the entire cert chain */
                        if (args->dCert->ca != NULL) {
                            ret = ProcessPeerCertsChainCRLCheck(ssl, args);
                            if (ret != 0) {
                                WOLFSSL_ERROR_VERBOSE(ret);
                                WOLFSSL_MSG("\tCRL chain check not ok");
                                args->fatal = 0;
                            }
                        }
                        else {
                            WOLFSSL_MSG("No CA signer set");
                        }
                    }
                #endif /* HAVE_CRL */
                    (void)doLookup;
                }
            #endif /* HAVE_OCSP || HAVE_CRL */

            #ifdef KEEP_PEER_CERT
                if (args->fatal == 0) {
                    int copyRet = 0;

                    /* free old peer cert */
                    FreeX509(&ssl->peerCert);
                    InitX509(&ssl->peerCert, 0, ssl->heap);
                    copyRet = CopyDecodedToX509(&ssl->peerCert, args->dCert);
                    if (copyRet == WC_NO_ERR_TRACE(MEMORY_E)) {
                        args->fatal = 1;
                    }
                }
            #endif /* KEEP_PEER_CERT */

            #ifndef IGNORE_KEY_EXTENSIONS
                #if defined(OPENSSL_EXTRA)
                  /* when compatibility layer is turned on and no verify is
                   * set then ignore the certificate key extension */
                    if (args->dCert->extKeyUsageSet &&
                          args->dCert->extKeyUsageCrit == 0 &&
                          ssl->options.verifyNone) {
                        WOLFSSL_MSG("Not verifying certificate key usage");
                    }
                    else
                #endif
                if (args->dCert->extKeyUsageSet) {
                    if ((ssl->specs.kea == rsa_kea) &&
                        (ssl->options.side == WOLFSSL_CLIENT_END) &&
                        (args->dCert->extKeyUsage & KEYUSE_KEY_ENCIPHER) == 0) {
                        ret = KEYUSE_ENCIPHER_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                    if ((ssl->specs.kea != rsa_kea) &&
                        (ssl->specs.sig_algo == rsa_sa_algo ||
                            (ssl->specs.sig_algo == ecc_dsa_sa_algo &&
                                 !ssl->specs.static_ecdh)) &&
                        (args->dCert->extKeyUsage & KEYUSE_DIGITAL_SIG) == 0) {
                        WOLFSSL_MSG("KeyUse Digital Sig not set");
                        ret = KEYUSE_SIGNATURE_E;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                }

                #if defined(OPENSSL_EXTRA)
                    /* when compatibility layer is turned on and no verify is
                     * set then ignore the certificate key extension */
                    if (args->dCert->extExtKeyUsageSet &&
                            args->dCert->extExtKeyUsageCrit == 0 &&
                          ssl->options.verifyNone) {
                                WOLFSSL_MSG("Not verifying certificate ext key usage");
                    }
                    else
                #endif
                if (args->dCert->extExtKeyUsageSet) {
                    if (ssl->options.side == WOLFSSL_CLIENT_END) {
                        if ((args->dCert->extExtKeyUsage &
                                (EXTKEYUSE_ANY | EXTKEYUSE_SERVER_AUTH)) == 0) {
                            WOLFSSL_MSG("ExtKeyUse Server Auth not set");
                            ret = EXTKEYUSE_AUTH_E;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                    }
                    else {
                        if ((args->dCert->extExtKeyUsage &
                                (EXTKEYUSE_ANY | EXTKEYUSE_CLIENT_AUTH)) == 0) {
                            WOLFSSL_MSG("ExtKeyUse Client Auth not set");
                            ret = EXTKEYUSE_AUTH_E;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                    }
                }
            #endif /* IGNORE_KEY_EXTENSIONS */

                if (args->fatal) {
                    ssl->error = ret;
                #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
                    SendAlert(ssl, alert_fatal, bad_certificate);
                    if (ssl->peerVerifyRet == 0) /* Return first cert error here */
                        ssl->peerVerifyRet = WOLFSSL_X509_V_ERR_CERT_REJECTED;
                #endif
                    goto exit_ppc;
                }

                /* Certificate validated and stored. */
                ssl->options.havePeerCert = 1;
            #if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_RSA)
                if (ssl->options.side == WOLFSSL_CLIENT_END &&
                    ssl->specs.sig_algo == rsa_kea) {
                    /* CLIENT: No ServerKeyExchange message sent by server. */
                    ssl->options.peerAuthGood = 1;
                }
            #endif
            #if !defined(NO_WOLFSSL_CLIENT) && defined(HAVE_ECC)
                if (ssl->options.side == WOLFSSL_CLIENT_END &&
                    ssl->specs.static_ecdh) {
                    /* CLIENT: No ServerKeyExchange message sent by server. */
                    ssl->options.peerAuthGood = 1;
                }
            #endif

                if (!ssl->options.verifyNone && ssl->buffers.domainName.buffer) {
                #ifndef WOLFSSL_ALLOW_NO_CN_IN_SAN
                    /* Per RFC 5280 section 4.2.1.6, "Whenever such identities
                     * are to be bound into a certificate, the subject
                     * alternative name extension MUST be used." */
                    if (args->dCert->altNames) {
                        if (CheckForAltNames(args->dCert,
                                (char*)ssl->buffers.domainName.buffer,
                                (ssl->buffers.domainName.buffer == NULL ? 0 :
                                (word32)XSTRLEN(
                                (const char *)ssl->buffers.domainName.buffer)),
                                NULL, 0, 0) != 1) {
                            WOLFSSL_MSG("DomainName match on alt names failed");
                            /* try to get peer key still */
                            ret = DOMAIN_NAME_MISMATCH;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                    }
                    else {
                    #ifndef  WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
                        if (MatchDomainName(
                                args->dCert->subjectCN,
                                args->dCert->subjectCNLen,
                                (char*)ssl->buffers.domainName.buffer,
                                (ssl->buffers.domainName.buffer == NULL ? 0 :
                                (word32)XSTRLEN(
                                (const char *)ssl->buffers.domainName.buffer)
                                ), 0) == 0)
                    #endif
                        {
                            WOLFSSL_MSG("DomainName match failed");
                            ret = DOMAIN_NAME_MISMATCH;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                    }
                #else /* WOLFSSL_ALL_NO_CN_IN_SAN */
                    /* Old behavior. */
                #ifndef  WOLFSSL_HOSTNAME_VERIFY_ALT_NAME_ONLY
                    if (MatchDomainName(args->dCert->subjectCN,
                                args->dCert->subjectCNLen,
                                (char*)ssl->buffers.domainName.buffer,
                                (ssl->buffers.domainName.buffer == NULL ? 0 :
                                (word32)XSTRLEN(ssl->buffers.domainName.buffer)), 0) == 0)
                #endif
                    {
                        if (CheckForAltNames(args->dCert,
                                 (char*)ssl->buffers.domainName.buffer,
                                 (ssl->buffers.domainName.buffer == NULL ? 0 :
                                 (word32)XSTRLEN(ssl->buffers.domainName.buffer)),
                                 NULL, 0, 0) != 1) {
                            WOLFSSL_MSG("DomainName match failed");
                            /* try to get peer key still */
                            ret = DOMAIN_NAME_MISMATCH;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                    }
                #endif /* WOLFSSL_ALL_NO_CN_IN_SAN */
                }

#ifndef OPENSSL_EXTRA
                if (!ssl->options.verifyNone && ssl->buffers.ipasc.buffer) {
                    if (CheckIPAddr(args->dCert,
                            (const char*)ssl->buffers.ipasc.buffer) != 0) {
                        WOLFSSL_MSG("IPAddr match on alt names failed");
                        ret = IPADDR_MISMATCH;
                        WOLFSSL_ERROR_VERBOSE(ret);
                    }
                }
#endif

                /* decode peer key */
                switch (args->dCert->keyOID) {
                #ifndef NO_RSA
                    #ifdef WC_RSA_PSS
                    case RSAPSSk:
                    #endif
                    case RSAk:
                    {
                        word32 keyIdx = 0;
                        int keyRet = 0;

                        if (ssl->peerRsaKey == NULL) {
                            keyRet = AllocKey(ssl, DYNAMIC_TYPE_RSA,
                                                (void**)&ssl->peerRsaKey);
                        } else if (ssl->peerRsaKeyPresent) {
                            keyRet = ReuseKey(ssl, DYNAMIC_TYPE_RSA,
                                              ssl->peerRsaKey);
                            ssl->peerRsaKeyPresent = 0;
                        }

                        if (keyRet != 0 || wc_RsaPublicKeyDecode(
                               args->dCert->publicKey, &keyIdx, ssl->peerRsaKey,
                                                args->dCert->pubKeySize) != 0) {
                            ret = PEER_KEY_ERROR;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                        else {
                            ssl->peerRsaKeyPresent = 1;
                    #if defined(WOLFSSL_RENESAS_TSIP_TLS) || \
                                             defined(WOLFSSL_RENESAS_FSPSM_TLS)
                        /* copy encrypted tsip key index into ssl object */
                        if (args->dCert->sce_tsip_encRsaKeyIdx) {
                            if (!ssl->peerSceTsipEncRsaKeyIndex) {
                                ssl->peerSceTsipEncRsaKeyIndex = (byte*)XMALLOC(
                                    TSIP_TLS_ENCPUBKEY_SZ_BY_CERTVRFY,
                                    ssl->heap, DYNAMIC_TYPE_RSA);
                                if (!ssl->peerSceTsipEncRsaKeyIndex) {
                                    args->lastErr = MEMORY_E;
                                    goto exit_ppc;
                                }
                            }

                            XMEMCPY(ssl->peerSceTsipEncRsaKeyIndex,
                                        args->dCert->sce_tsip_encRsaKeyIdx,
                                        TSIP_TLS_ENCPUBKEY_SZ_BY_CERTVRFY);
                         }
                    #endif
                    #ifdef HAVE_PK_CALLBACKS
                        #if defined(HAVE_SECURE_RENEGOTIATION) || \
                                        defined(WOLFSSL_POST_HANDSHAKE_AUTH)
                        if (ssl->buffers.peerRsaKey.buffer) {
                            XFREE(ssl->buffers.peerRsaKey.buffer,
                                    ssl->heap, DYNAMIC_TYPE_RSA);
                            ssl->buffers.peerRsaKey.buffer = NULL;
                        }
                        #endif


                        ssl->buffers.peerRsaKey.buffer =
                               (byte*)XMALLOC(args->dCert->pubKeySize,
                                            ssl->heap, DYNAMIC_TYPE_RSA);
                        if (ssl->buffers.peerRsaKey.buffer == NULL) {
                            ret = MEMORY_ERROR;
                        }
                        else {
                            XMEMCPY(ssl->buffers.peerRsaKey.buffer,
                                    args->dCert->publicKey,
                                    args->dCert->pubKeySize);
                            ssl->buffers.peerRsaKey.length =
                                args->dCert->pubKeySize;
                        }
                    #endif /* HAVE_PK_CALLBACKS */
                        }

                        /* check size of peer RSA key */
                        if (ret == 0 && ssl->peerRsaKeyPresent &&
                                          !ssl->options.verifyNone &&
                                          wc_RsaEncryptSize(ssl->peerRsaKey)
                                              < ssl->options.minRsaKeySz) {
                            ret = RSA_KEY_SIZE_E;
                            WOLFSSL_ERROR_VERBOSE(ret);
                            WOLFSSL_MSG("Peer RSA key is too small");
                        }
                        break;
                    }
                #endif /* NO_RSA */
                #ifdef HAVE_ECC
                #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                    case SM2k:
                #endif
                    case ECDSAk:
                    {
                        int keyRet = 0;
                        word32 idx = 0;
                    #if defined(WOLFSSL_RENESAS_FSPSM_TLS) || \
                        defined(WOLFSSL_RENESAS_TSIP_TLS)
                        /* copy encrypted tsip/sce key index into ssl object */
                        if (args->dCert->sce_tsip_encRsaKeyIdx) {
                            if (!ssl->peerSceTsipEncRsaKeyIndex) {
                                ssl->peerSceTsipEncRsaKeyIndex = (byte*)XMALLOC(
                                    TSIP_TLS_ENCPUBKEY_SZ_BY_CERTVRFY,
                                    ssl->heap, DYNAMIC_TYPE_RSA);
                                if (!ssl->peerSceTsipEncRsaKeyIndex) {
                                    args->lastErr = MEMORY_E;
                                    ERROR_OUT(MEMORY_ERROR, exit_ppc);
                                }
                            }

                            XMEMCPY(ssl->peerSceTsipEncRsaKeyIndex,
                                        args->dCert->sce_tsip_encRsaKeyIdx,
                                        TSIP_TLS_ENCPUBKEY_SZ_BY_CERTVRFY);
                         }
                    #endif
                        if (ssl->peerEccDsaKey == NULL) {
                            /* alloc/init on demand */
                            keyRet = AllocKey(ssl, DYNAMIC_TYPE_ECC,
                                    (void**)&ssl->peerEccDsaKey);
                        } else if (ssl->peerEccDsaKeyPresent) {
                            keyRet = ReuseKey(ssl, DYNAMIC_TYPE_ECC,
                                              ssl->peerEccDsaKey);
                            ssl->peerEccDsaKeyPresent = 0;
                        }

                        if (keyRet != 0 ||
                            wc_EccPublicKeyDecode(args->dCert->publicKey, &idx,
                                                ssl->peerEccDsaKey,
                                                args->dCert->pubKeySize) != 0) {
                            ret = PEER_KEY_ERROR;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                        else {
                            ssl->peerEccDsaKeyPresent = 1;

                    #ifdef HAVE_PK_CALLBACKS
                            if (ssl->buffers.peerEccDsaKey.buffer)
                                XFREE(ssl->buffers.peerEccDsaKey.buffer,
                                      ssl->heap, DYNAMIC_TYPE_ECC);
                            ssl->buffers.peerEccDsaKey.buffer =
                                   (byte*)XMALLOC(args->dCert->pubKeySize,
                                           ssl->heap, DYNAMIC_TYPE_ECC);
                            if (ssl->buffers.peerEccDsaKey.buffer == NULL) {
                                ERROR_OUT(MEMORY_ERROR, exit_ppc);
                            }
                            else {
                                XMEMCPY(ssl->buffers.peerEccDsaKey.buffer,
                                        args->dCert->publicKey,
                                        args->dCert->pubKeySize);
                                ssl->buffers.peerEccDsaKey.length =
                                        args->dCert->pubKeySize;
                            }
                    #endif /* HAVE_PK_CALLBACKS */
                        }

                        /* check size of peer ECC key */
                        if (ret == 0 && ssl->peerEccDsaKeyPresent &&
                                              !ssl->options.verifyNone &&
                                              wc_ecc_size(ssl->peerEccDsaKey)
                                              < ssl->options.minEccKeySz) {
                            ret = ECC_KEY_SIZE_E;
                            WOLFSSL_ERROR_VERBOSE(ret);
                            WOLFSSL_MSG("Peer ECC key is too small");
                        }

                        /* populate curve oid - if missing */
                        if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->ecdhCurveOID == 0)
                            ssl->ecdhCurveOID = args->dCert->pkCurveOID;
                        break;
                    }
                #endif /* HAVE_ECC */
                #if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)
                    case ED25519k:
                    {
                        int keyRet = 0;
                        if (ssl->peerEd25519Key == NULL) {
                            /* alloc/init on demand */
                            keyRet = AllocKey(ssl, DYNAMIC_TYPE_ED25519,
                                    (void**)&ssl->peerEd25519Key);
                        } else if (ssl->peerEd25519KeyPresent) {
                            keyRet = ReuseKey(ssl, DYNAMIC_TYPE_ED25519,
                                              ssl->peerEd25519Key);
                            ssl->peerEd25519KeyPresent = 0;
                        }

                        if (keyRet != 0 ||
                            wc_ed25519_import_public(args->dCert->publicKey,
                                                     args->dCert->pubKeySize,
                                                     ssl->peerEd25519Key)
                                                                         != 0) {
                            ret = PEER_KEY_ERROR;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                        else {
                            ssl->peerEd25519KeyPresent = 1;
                    #ifdef HAVE_PK_CALLBACKS
                            ssl->buffers.peerEd25519Key.buffer =
                                   (byte*)XMALLOC(args->dCert->pubKeySize,
                                           ssl->heap, DYNAMIC_TYPE_ED25519);
                            if (ssl->buffers.peerEd25519Key.buffer == NULL) {
                                ERROR_OUT(MEMORY_ERROR, exit_ppc);
                            }
                            else {
                                XMEMCPY(ssl->buffers.peerEd25519Key.buffer,
                                        args->dCert->publicKey,
                                        args->dCert->pubKeySize);
                                ssl->buffers.peerEd25519Key.length =
                                        args->dCert->pubKeySize;
                            }
                    #endif /*HAVE_PK_CALLBACKS */
                        }

                        /* check size of peer ECC key */
                        if (ret == 0 && ssl->peerEd25519KeyPresent &&
                                  !ssl->options.verifyNone &&
                                  ED25519_KEY_SIZE < ssl->options.minEccKeySz) {
                            ret = ECC_KEY_SIZE_E;
                            WOLFSSL_ERROR_VERBOSE(ret);
                            WOLFSSL_MSG("Peer ECC key is too small");
                        }

                        /* populate curve oid - if missing */
                        if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->ecdhCurveOID == 0)
                            ssl->ecdhCurveOID = ECC_X25519_OID;
                        break;
                    }
                #endif /* HAVE_ED25519 && HAVE_ED25519_KEY_IMPORT */
                #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)
                    case ED448k:
                    {
                        int keyRet = 0;
                        if (ssl->peerEd448Key == NULL) {
                            /* alloc/init on demand */
                            keyRet = AllocKey(ssl, DYNAMIC_TYPE_ED448,
                                    (void**)&ssl->peerEd448Key);
                        } else if (ssl->peerEd448KeyPresent) {
                            keyRet = ReuseKey(ssl, DYNAMIC_TYPE_ED448,
                                    ssl->peerEd448Key);
                            ssl->peerEd448KeyPresent = 0;
                        }

                        if (keyRet != 0 ||
                            wc_ed448_import_public(args->dCert->publicKey,
                                    args->dCert->pubKeySize,
                                    ssl->peerEd448Key) != 0) {
                            ret = PEER_KEY_ERROR;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                        else {
                            ssl->peerEd448KeyPresent = 1;
                    #ifdef HAVE_PK_CALLBACKS
                            ssl->buffers.peerEd448Key.buffer =
                                   (byte*)XMALLOC(args->dCert->pubKeySize,
                                           ssl->heap, DYNAMIC_TYPE_ED448);
                            if (ssl->buffers.peerEd448Key.buffer == NULL) {
                                ERROR_OUT(MEMORY_ERROR, exit_ppc);
                            }
                            else {
                                XMEMCPY(ssl->buffers.peerEd448Key.buffer,
                                        args->dCert->publicKey,
                                        args->dCert->pubKeySize);
                                ssl->buffers.peerEd448Key.length =
                                        args->dCert->pubKeySize;
                            }
                    #endif /*HAVE_PK_CALLBACKS */
                        }

                        /* check size of peer ECC key */
                        if (ret == 0 && ssl->peerEd448KeyPresent &&
                               !ssl->options.verifyNone &&
                               ED448_KEY_SIZE < ssl->options.minEccKeySz) {
                            ret = ECC_KEY_SIZE_E;
                            WOLFSSL_ERROR_VERBOSE(ret);
                            WOLFSSL_MSG("Peer ECC key is too small");
                        }

                        /* populate curve oid - if missing */
                        if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->ecdhCurveOID == 0)
                            ssl->ecdhCurveOID = ECC_X448_OID;
                        break;
                    }
                #endif /* HAVE_ED448 && HAVE_ED448_KEY_IMPORT */
                #if defined(HAVE_FALCON)
                    case FALCON_LEVEL1k:
                    case FALCON_LEVEL5k:
                    {
                        int keyRet = 0;
                        if (ssl->peerFalconKey == NULL) {
                            /* alloc/init on demand */
                            keyRet = AllocKey(ssl, DYNAMIC_TYPE_FALCON,
                                    (void**)&ssl->peerFalconKey);
                        } else if (ssl->peerFalconKeyPresent) {
                            keyRet = ReuseKey(ssl, DYNAMIC_TYPE_FALCON,
                                    ssl->peerFalconKey);
                            ssl->peerFalconKeyPresent = 0;
                        }

                        if (keyRet == 0) {
                            if (args->dCert->keyOID == FALCON_LEVEL1k) {
                                keyRet = wc_falcon_set_level(ssl->peerFalconKey,
                                1);
                            }
                            else {
                                keyRet = wc_falcon_set_level(ssl->peerFalconKey,
                                5);
                            }
                        }

                        if (keyRet != 0 ||
                            wc_falcon_import_public(args->dCert->publicKey,
                                                    args->dCert->pubKeySize,
                                                    ssl->peerFalconKey) != 0) {
                            ret = PEER_KEY_ERROR;
                            WOLFSSL_ERROR_VERBOSE(ret);
                        }
                        else {
                            ssl->peerFalconKeyPresent = 1;
                        }

                        /* check size of peer Falcon key */
                        if (ret == 0 && ssl->peerFalconKeyPresent &&
                               !ssl->options.verifyNone &&
                               FALCON_MAX_KEY_SIZE <
                               ssl->options.minFalconKeySz) {
                            ret = FALCON_KEY_SIZE_E;
                            WOLFSSL_ERROR_VERBOSE(ret);
                            WOLFSSL_MSG("Peer Falcon key is too small");
                        }
                        break;
                    }
                #endif /* HAVE_FALCON */
                #if defined(HAVE_DILITHIUM) && \
                    !defined(WOLFSSL_DILITHIUM_NO_VERIFY)
                    case ML_DSA_LEVEL2k:
                    case ML_DSA_LEVEL3k:
                    case ML_DSA_LEVEL5k:
                    #ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
                    case DILITHIUM_LEVEL2k:
                    case DILITHIUM_LEVEL3k:
                    case DILITHIUM_LEVEL5k:
                    #endif
                    {
                        int keyRet = 0;
                        if (ssl->peerDilithiumKey == NULL) {
                            /* alloc/init on demand */
                            keyRet = AllocKey(ssl, DYNAMIC_TYPE_DILITHIUM,
                                    (void**)&ssl->peerDilithiumKey);
                        } else if (ssl->peerDilithiumKeyPresent) {
                            keyRet = ReuseKey(ssl, DYNAMIC_TYPE_DILITHIUM,
                                    ssl->peerDilithiumKey);
                            ssl->peerDilithiumKeyPresent = 0;
                        }

                        if (keyRet == 0) {
                            if (args->dCert->keyOID == ML_DSA_LEVEL2k) {
                                keyRet = wc_dilithium_set_level(
                                             ssl->peerDilithiumKey, WC_ML_DSA_44);
                            }
                            else if (args->dCert->keyOID == ML_DSA_LEVEL3k) {
                                keyRet = wc_dilithium_set_level(
                                             ssl->peerDilithiumKey, WC_ML_DSA_65);
                            }
                            else if (args->dCert->keyOID == ML_DSA_LEVEL5k) {
                                keyRet = wc_dilithium_set_level(
                                             ssl->peerDilithiumKey, WC_ML_DSA_87);
                            }
                            #ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
                            else if (args->dCert->keyOID == DILITHIUM_LEVEL2k) {
                                keyRet = wc_dilithium_set_level(
                                             ssl->peerDilithiumKey, WC_ML_DSA_44_DRAFT);
                            }
                            else if (args->dCert->keyOID == DILITHIUM_LEVEL3k) {
                                keyRet = wc_dilithium_set_level(
                                             ssl->peerDilithiumKey, WC_ML_DSA_65_DRAFT);
                            }
                            else if (args->dCert->keyOID == DILITHIUM_LEVEL5k) {
                                keyRet = wc_dilithium_set_level(
                                             ssl->peerDilithiumKey, WC_ML_DSA_87_DRAFT);
                            }
                            #endif
                        }

                        if (keyRet != 0 ||
                            wc_dilithium_import_public(args->dCert->publicKey,
                                                       args->dCert->pubKeySize,
                                                       ssl->peerDilithiumKey)
                            != 0) {
                            ret = PEER_KEY_ERROR;
                        }
                        else {
                            ssl->peerDilithiumKeyPresent = 1;
                        }

                        /* check size of peer Dilithium key */
                        if (ret == 0 && ssl->peerDilithiumKeyPresent &&
                               !ssl->options.verifyNone &&
                               DILITHIUM_MAX_KEY_SIZE <
                               ssl->options.minDilithiumKeySz) {
                            ret = DILITHIUM_KEY_SIZE_E;
                            WOLFSSL_MSG("Peer Dilithium key is too small");
                        }
                        break;
                    }
                #endif /* HAVE_DILITHIUM */
                    default:
                        break;
                }

                /* args->dCert free'd in function cleanup after callback */
            } /* if (count > 0) */

            /* Check for error */
            if (args->fatal && ret != 0) {
                goto exit_ppc;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_FINALIZE;
        } /* case TLS_ASYNC_VERIFY */
        FALL_THROUGH;

        case TLS_ASYNC_FINALIZE:
        {
            /* load last error */
            if (args->lastErr != 0 && ret == 0) {
                ret = args->lastErr;
            }

        #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL)
           /* limit compliant with OpenSSL verify Depth + 1
            * OpenSSL tries to expand the chain one longer than limit until
            * reaching an ultimately trusted issuer. Becoming failure if
            * we hit the limit, with WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG
            */
            if (args->untrustedDepth > (ssl->options.verifyDepth + 1)) {
                if (ssl->peerVerifyRet == 0) /* Return first cert error here */
                    ssl->peerVerifyRet = WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG;
                ret = MAX_CHAIN_ERROR;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
        #endif

            /* Do leaf verify callback when it wasn't called yet */
            if (ret == 0 || ret != args->leafVerifyErr)
                ret = DoVerifyCallback(SSL_CM(ssl), ssl, ret, args);

            if (ssl->options.verifyNone &&
                              (ret == WC_NO_ERR_TRACE(CRL_MISSING) ||
                               ret == WC_NO_ERR_TRACE(CRL_CERT_REVOKED) ||
                               ret == WC_NO_ERR_TRACE(CRL_CERT_DATE_ERR))) {
                WOLFSSL_MSG("Ignoring CRL problem based on verify setting");
                ret = ssl->error = 0;
            }

            if (ret != 0) {
                if (!ssl->options.verifyNone) {
                    DoCertFatalAlert(ssl, ret);
                }
                ssl->error = ret; /* Report SSL error */
            }

            if (ret == 0 && ssl->options.side == WOLFSSL_CLIENT_END) {
                ssl->options.serverState = SERVER_CERT_COMPLETE;
            }

            if (IsEncryptionOn(ssl, 0))
                args->idx += ssl->keys.padSz;

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_END;
        } /* case TLS_ASYNC_FINALIZE */
        FALL_THROUGH;

        case TLS_ASYNC_END:
        {
            /* Set final index */
            *inOutIdx = args->idx;

            break;
        }
        default:
            ret = INPUT_CASE_ERROR;
            break;
    } /* switch(ssl->options.asyncState) */

exit_ppc:

    WOLFSSL_LEAVE("ProcessPeerCerts", ret);


#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) ||
        ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
        /* Mark message as not received so it can process again */
        ssl->msgsReceived.got_certificate = 0;

        return ret;
    }
#endif /* WOLFSSL_ASYNC_CRYPT || WOLFSSL_NONBLOCK_OCSP */

#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
    /* Cleanup async */
    FreeAsyncCtx(ssl, 0);
#elif defined(WOLFSSL_SMALL_STACK)
    if (args)
    {
        FreeProcPeerCertArgs(ssl, args);
    }
#else
    FreeProcPeerCertArgs(ssl, args);
#endif /* WOLFSSL_ASYNC_CRYPT || WOLFSSL_NONBLOCK_OCSP || WOLFSSL_SMALL_STACK */

#if !defined(WOLFSSL_ASYNC_CRYPT) && defined(WOLFSSL_SMALL_STACK)
    XFREE(args, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif

    FreeKeyExchange(ssl);

    return ret;
}
#endif

#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
#if !defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH)

/* handle processing of certificate (11) */
static int DoCertificate(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                                                                word32 size)
{
    int ret;

    WOLFSSL_START(WC_FUNC_CERTIFICATE_DO);
    WOLFSSL_ENTER("DoCertificate");

#ifdef SESSION_CERTS
    /* Reset the session cert chain count in case the session resume failed,
     * do not reset if we are resuming after an async wait */
#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
    if (ssl->error != WC_NO_ERR_TRACE(OCSP_WANT_READ) &&
        ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E))
#endif
    {
        ssl->session->chain.count = 0;
#ifdef WOLFSSL_ALT_CERT_CHAINS
        ssl->session->altChain.count = 0;
#endif
    }
#endif /* SESSION_CERTS */

    ret = ProcessPeerCerts(ssl, input, inOutIdx, size);

#ifdef OPENSSL_EXTRA
    ssl->options.serverState = SERVER_CERT_COMPLETE;
#endif

    WOLFSSL_LEAVE("DoCertificate", ret);
    WOLFSSL_END(WC_FUNC_CERTIFICATE_DO);

    return ret;
}

/* handle processing of certificate_status (22) */
static int DoCertificateStatus(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                                                                    word32 size)
{
    int    ret = 0;
    byte   status_type;
    word32 status_length;
    int endCertificateOK = 0;

    WOLFSSL_START(WC_FUNC_CERTIFICATE_STATUS_DO);
    WOLFSSL_ENTER("DoCertificateStatus");

    if (size < ENUM_LEN + OPAQUE24_LEN)
        return BUFFER_ERROR;

    status_type = input[(*inOutIdx)++];

    c24to32(input + *inOutIdx, &status_length);
    *inOutIdx += OPAQUE24_LEN;

    if (size != ENUM_LEN + OPAQUE24_LEN + status_length)
        return BUFFER_ERROR;

    switch (status_type) {
    #ifndef NO_TLS
    #if (defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(NO_TLS)) \
     || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)

        /* WOLFSSL_CSR_OCSP overlaps with WOLFSSL_CSR2_OCSP */
        case WOLFSSL_CSR2_OCSP:
            ret = ProcessCSR(ssl, input, inOutIdx, status_length);
            endCertificateOK = (ret == 0);
            break;

    #endif

    #if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)

        case WOLFSSL_CSR2_OCSP_MULTI: {
            OcspRequest* request;
            word32 list_length = status_length;
            byte   idx = 0;
            Signer *pendingCAs = NULL;

            #ifdef WOLFSSL_SMALL_STACK
                CertStatus*   status;
                OcspEntry*    single;
                OcspResponse* response;
            #else
                CertStatus   status[1];
                OcspEntry    single[1];
                OcspResponse response[1];
            #endif

            if (!ssl->status_request_v2)
                return BUFFER_ERROR;

            ssl->status_request_v2 = 0;

            pendingCAs = TLSX_CSR2_GetPendingSigners(ssl->extensions);

            #ifdef WOLFSSL_SMALL_STACK
                status = (CertStatus*)XMALLOC(sizeof(CertStatus), ssl->heap,
                                                       DYNAMIC_TYPE_OCSP_STATUS);
                single = (OcspEntry*)XMALLOC(sizeof(OcspEntry), ssl->heap,
                                                              DYNAMIC_TYPE_OCSP_ENTRY);
                response = (OcspResponse*)XMALLOC(sizeof(OcspResponse), ssl->heap,
                                                             DYNAMIC_TYPE_OCSP_REQUEST);

                if (status == NULL || single == NULL || response == NULL) {
                    XFREE(status, ssl->heap, DYNAMIC_TYPE_OCSP_STATUS);
                    XFREE(single, ssl->heap, DYNAMIC_TYPE_OCSP_ENTRY);
                    XFREE(response, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);

                    return MEMORY_ERROR;
                }
            #endif

            while (list_length && ret == 0) {
                if (OPAQUE24_LEN > list_length) {
                    ret = BUFFER_ERROR;
                    break;
                }

                c24to32(input + *inOutIdx, &status_length);
                *inOutIdx   += OPAQUE24_LEN;
                list_length -= OPAQUE24_LEN;

                if (status_length > list_length) {
                    ret = BUFFER_ERROR;
                    break;
                }

                if (status_length) {
                    InitOcspResponse(response, single, status, input +*inOutIdx,
                                     status_length, ssl->heap);
                    response->pendingCAs = pendingCAs;
                    if ((OcspResponseDecode(response, SSL_CM(ssl), ssl->heap,
                            0, 0) != 0)
                    ||  (response->responseStatus != OCSP_SUCCESSFUL)
                    ||  (response->single->status->status != CERT_GOOD))
                        ret = BAD_CERTIFICATE_STATUS_ERROR;

                    if (ret == 0) {
                        request = (OcspRequest*)TLSX_CSR2_GetRequest(
                                ssl->extensions, status_type, idx);

                        if (request == NULL) {
                            ret = BAD_CERTIFICATE_STATUS_ERROR;
                        }
                        else if (CompareOcspReqResp(request, response) != 0) {
                            ret = BAD_CERTIFICATE_STATUS_ERROR;
                        }
                    }

                    /* only frees 'single' if single->isDynamic is set */
                    FreeOcspResponse(response);

               #ifndef NO_WOLFSSL_SERVER
                    ret = CsrDoStatusVerifyCb(ssl, input + *inOutIdx,
                                              status_length, idx, ret);
               #endif
                    if (ret == 0 && idx == 0) /* server cert must be OK */
                        endCertificateOK = 1;

                    *inOutIdx   += status_length;
                    list_length -= status_length;
                }
                if (idx >= MAX_CHAIN_DEPTH) {
                    ret = BUFFER_ERROR;
                    break;
                }
                idx++;
            }

            ssl->status_request_v2 = 0;

                WC_FREE_VAR_EX(status, NULL, DYNAMIC_TYPE_OCSP_STATUS);
                WC_FREE_VAR_EX(single, NULL, DYNAMIC_TYPE_OCSP_ENTRY);
                WC_FREE_VAR_EX(response, NULL, DYNAMIC_TYPE_OCSP_REQUEST);

        }
        break;

    #endif
    #endif /* !NO_TLS */

        default:
            ret = BUFFER_ERROR;
    }

    /* end certificate MUST be present */
    if (endCertificateOK == 0)
        ret = BAD_CERTIFICATE_STATUS_ERROR;
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    if (ret == 0) {
        if (TLSX_CSR2_MergePendingCA(ssl) < 0) {
            WOLFSSL_MSG("Failed to merge pending CAs");
        }
    }
    else {
        TLSX_CSR2_ClearPendingCA(ssl);
    }
#endif

    if (ret != 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
        SendAlert(ssl, alert_fatal, bad_certificate_status_response);
    }

    if (IsEncryptionOn(ssl, 0)) {
        if (*inOutIdx + ssl->keys.padSz > size)
            return BUFFER_E;
        *inOutIdx += ssl->keys.padSz;
    }

    WOLFSSL_LEAVE("DoCertificateStatus", ret);
    WOLFSSL_END(WC_FUNC_CERTIFICATE_STATUS_DO);

    return ret;
}

#endif

#endif /* !NO_TLS && !WOLFSSL_NO_TLS12 */

#endif /* !NO_CERTS */

#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)

static int DoHelloRequest(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
                                                    word32 size, word32 totalSz)
{
    (void)input;

    WOLFSSL_START(WC_FUNC_HELLO_REQUEST_DO);
    WOLFSSL_ENTER("DoHelloRequest");

    if (size) /* must be 0 */
        return BUFFER_ERROR;

    if (IsEncryptionOn(ssl, 0)) {
        /* If size == totalSz then we are in DtlsMsgDrain so no need to worry
         * about padding */
        /* access beyond input + size should be checked against totalSz */
        if (size != totalSz &&
                *inOutIdx + ssl->keys.padSz > totalSz)
            return BUFFER_E;

        *inOutIdx += ssl->keys.padSz;
    }

    if (ssl->options.side == WOLFSSL_SERVER_END) {
        SendAlert(ssl, alert_fatal, unexpected_message); /* try */
        WOLFSSL_ERROR_VERBOSE(FATAL_ERROR);
        return FATAL_ERROR;
    }
#ifdef HAVE_SECURE_RENEGOTIATION
    else if (ssl->secure_renegotiation && ssl->secure_renegotiation->enabled) {
        ssl->secure_renegotiation->startScr = 1;
        WOLFSSL_LEAVE("DoHelloRequest", 0);
        WOLFSSL_END(WC_FUNC_HELLO_REQUEST_DO);
        return 0;
    }
#endif
    else {
        return SendAlert(ssl, alert_warning, no_renegotiation);
    }
}


int DoFinished(WOLFSSL* ssl, const byte* input, word32* inOutIdx, word32 size,
                                                      word32 totalSz, int sniff)
{
    word32 finishedSz = (ssl->options.tls ? TLS_FINISHED_SZ : FINISHED_SZ);

    WOLFSSL_START(WC_FUNC_FINISHED_DO);
    WOLFSSL_ENTER("DoFinished");

    if (finishedSz != size)
        return BUFFER_ERROR;

    /* check against totalSz
     * If size == totalSz then we are in DtlsMsgDrain so no need to worry about
     * padding */
    if (size != totalSz) {
        if (*inOutIdx + size + ssl->keys.padSz > totalSz)
            return BUFFER_E;
    }

    #ifdef WOLFSSL_CALLBACKS
        if (ssl->hsInfoOn) AddPacketName(ssl, "Finished");
        if (ssl->toInfoOn) AddLateName("Finished", &ssl->timeoutInfo);
    #endif

    if (sniff == NO_SNIFF) {
        if (ConstantCompare(input + *inOutIdx,
                   (const byte*)&ssl->hsHashes->verifyHashes, (int)size) != 0) {
            WOLFSSL_MSG("Verify finished error on hashes");
            WOLFSSL_ERROR_VERBOSE(VERIFY_FINISHED_ERROR);
            return VERIFY_FINISHED_ERROR;
        }
    }

#ifdef HAVE_SECURE_RENEGOTIATION
    if (ssl->secure_renegotiation) {
        /* save peer's state */
        if (ssl->options.side == WOLFSSL_CLIENT_END)
            XMEMCPY(ssl->secure_renegotiation->server_verify_data,
                    input + *inOutIdx, TLS_FINISHED_SZ);
        else
            XMEMCPY(ssl->secure_renegotiation->client_verify_data,
                    input + *inOutIdx, TLS_FINISHED_SZ);
        ssl->secure_renegotiation->verifySet = 1;
    }
#endif
#ifdef WOLFSSL_HAVE_TLS_UNIQUE
    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        XMEMCPY(ssl->serverFinished,
                input + *inOutIdx, TLS_FINISHED_SZ);
        ssl->serverFinished_len = TLS_FINISHED_SZ;
    }
    else {
        XMEMCPY(ssl->clientFinished,
                input + *inOutIdx, TLS_FINISHED_SZ);
        ssl->clientFinished_len = TLS_FINISHED_SZ;
    }
#endif

    /* force input exhaustion at ProcessReply consuming padSz */
    *inOutIdx += size + ssl->keys.padSz;

    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        ssl->options.serverState = SERVER_FINISHED_COMPLETE;
#ifdef OPENSSL_EXTRA
        ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
        ssl->options.clientState = CLIENT_FINISHED_COMPLETE;
#endif
        if (!ssl->options.resuming) {
#ifdef OPENSSL_EXTRA
            if (ssl->CBIS != NULL) {
                ssl->CBIS(ssl, WOLFSSL_CB_CONNECT_LOOP, WOLFSSL_SUCCESS);
            }
#endif
            ssl->options.handShakeState = HANDSHAKE_DONE;
            ssl->options.handShakeDone  = 1;
#ifdef HAVE_SECURE_RENEGOTIATION
            ssl->options.resumed = ssl->options.resuming;
#endif
        }
    }
    else {
        ssl->options.clientState = CLIENT_FINISHED_COMPLETE;
#ifdef OPENSSL_EXTRA
        ssl->cbmode = WOLFSSL_CB_MODE_READ;
        ssl->options.serverState = SERVER_FINISHED_COMPLETE;
#endif
        if (ssl->options.resuming) {
#ifdef OPENSSL_EXTRA
            if (ssl->CBIS != NULL) {
                ssl->CBIS(ssl, WOLFSSL_CB_ACCEPT_LOOP, WOLFSSL_SUCCESS);
            }
#endif
            ssl->options.handShakeState = HANDSHAKE_DONE;
            ssl->options.handShakeDone  = 1;
#ifdef HAVE_SECURE_RENEGOTIATION
            ssl->options.resumed = ssl->options.resuming;
#endif
        }
    }
#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        if ((!ssl->options.resuming && ssl->options.side == WOLFSSL_CLIENT_END) ||
             (ssl->options.resuming && ssl->options.side == WOLFSSL_SERVER_END)){
            DtlsMsgPoolReset(ssl);
            ssl->keys.dtls_handshake_number = 0;
            ssl->keys.dtls_expected_peer_handshake_number = 0;
        }
    }
#endif

    WOLFSSL_LEAVE("DoFinished", 0);
    WOLFSSL_END(WC_FUNC_FINISHED_DO);

    return 0;
}

/* Make sure no duplicates, no fast forward, or other problems; 0 on success */
static int SanityCheckMsgReceived(WOLFSSL* ssl, byte type)
{
    /* verify not a duplicate, mark received, check state */
    switch (type) {

#ifndef NO_WOLFSSL_CLIENT
        case hello_request:
        #ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                WOLFSSL_MSG("HelloRequest received by server");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_hello_request) {
                WOLFSSL_MSG("Duplicate HelloRequest received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            ssl->msgsReceived.got_hello_request = 1;

            break;
#endif

#ifndef NO_WOLFSSL_SERVER
        case client_hello:
        #ifndef NO_WOLFSSL_CLIENT
            if (ssl->options.side == WOLFSSL_CLIENT_END) {
                WOLFSSL_MSG("ClientHello received by client");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_client_hello) {
                WOLFSSL_MSG("Duplicate ClientHello received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            ssl->msgsReceived.got_client_hello = 1;

            break;
#endif

#ifndef NO_WOLFSSL_CLIENT
        case server_hello:
        #ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                WOLFSSL_MSG("ServerHello received by server");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_server_hello) {
                WOLFSSL_MSG("Duplicate ServerHello received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            ssl->msgsReceived.got_server_hello = 1;

            break;
#endif

#ifndef NO_WOLFSSL_CLIENT
        case hello_verify_request:
        #ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                WOLFSSL_MSG("HelloVerifyRequest received by server");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_hello_verify_request) {
                WOLFSSL_MSG("Duplicate HelloVerifyRequest received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            if (ssl->msgsReceived.got_hello_retry_request) {
                WOLFSSL_MSG("Received HelloVerifyRequest after a "
                            "HelloRetryRequest");
                WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                return VERSION_ERROR;
            }
            ssl->msgsReceived.got_hello_verify_request = 1;

            break;
#endif

#ifndef NO_WOLFSSL_CLIENT
        case session_ticket:
        #ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                WOLFSSL_MSG("SessionTicket received by server");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_session_ticket) {
                WOLFSSL_MSG("Duplicate SessionTicket received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            if (!ssl->msgsReceived.got_server_hello ||
                    ssl->msgsReceived.got_change_cipher ||
                    ssl->msgsReceived.got_finished ||
                    (!ssl->options.resuming &&
                            !ssl->msgsReceived.got_server_hello_done)) {
                WOLFSSL_MSG("session_ticket received in wrong order");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            ssl->msgsReceived.got_session_ticket = 1;

            break;
#endif

        case certificate:
            if (ssl->msgsReceived.got_certificate) {
                WOLFSSL_MSG("Duplicate Certificate received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            ssl->msgsReceived.got_certificate = 1;

#ifndef NO_WOLFSSL_CLIENT
            if (ssl->options.side == WOLFSSL_CLIENT_END) {
                if (!ssl->msgsReceived.got_server_hello) {
                    WOLFSSL_MSG("No ServerHello before Cert");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                if (ssl->msgsReceived.got_certificate_status ||
                        ssl->msgsReceived.got_server_key_exchange ||
                        ssl->msgsReceived.got_certificate_request ||
                        ssl->msgsReceived.got_server_hello_done ||
                        ssl->msgsReceived.got_change_cipher ||
                        ssl->msgsReceived.got_finished) {
                    WOLFSSL_MSG("Cert received in wrong order");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
            }
#endif
#ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                if (!ssl->msgsReceived.got_client_hello) {
                    WOLFSSL_MSG("No ClientHello before Cert");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                if (ssl->msgsReceived.got_client_key_exchange ||
                        ssl->msgsReceived.got_certificate_verify ||
                        ssl->msgsReceived.got_change_cipher ||
                        ssl->msgsReceived.got_finished) {
                    WOLFSSL_MSG("Cert received in wrong order");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
            }
#endif
            break;

#ifndef NO_WOLFSSL_CLIENT
        case certificate_status:
        #ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                WOLFSSL_MSG("CertificateStatus received by server");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_certificate_status) {
                WOLFSSL_MSG("Duplicate CertificateStatus received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }

            if (!ssl->msgsReceived.got_certificate) {
                WOLFSSL_MSG("No Certificate before CertificateStatus");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            if (ssl->msgsReceived.got_server_key_exchange) {
                WOLFSSL_MSG("CertificateStatus after ServerKeyExchange");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            if (ssl->msgsReceived.got_server_key_exchange ||
                    ssl->msgsReceived.got_certificate_request ||
                    ssl->msgsReceived.got_server_hello_done ||
                    ssl->msgsReceived.got_change_cipher ||
                    ssl->msgsReceived.got_finished) {
                WOLFSSL_MSG("CertificateStatus received in wrong order");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }

            ssl->msgsReceived.got_certificate_status = 1;
            break;
#endif

#ifndef NO_WOLFSSL_CLIENT
        case server_key_exchange:
        #ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                WOLFSSL_MSG("ServerKeyExchange received by server");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_server_key_exchange) {
                WOLFSSL_MSG("Duplicate ServerKeyExchange received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            if (!ssl->msgsReceived.got_server_hello) {
                WOLFSSL_MSG("No ServerHello before ServerKeyExchange");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            if (!ssl->msgsReceived.got_certificate) {
                if (ssl->specs.kea != psk_kea &&
                        ssl->specs.kea != dhe_psk_kea &&
                        ssl->specs.kea != ecdhe_psk_kea &&
                        !ssl->options.usingAnon_cipher) {
                    WOLFSSL_MSG("No Certificate before ServerKeyExchange");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
            }
            if (ssl->msgsReceived.got_certificate_request ||
                    ssl->msgsReceived.got_server_hello_done ||
                    ssl->msgsReceived.got_change_cipher ||
                    ssl->msgsReceived.got_finished) {
                WOLFSSL_MSG("ServerKeyExchange received in wrong order");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }

            ssl->msgsReceived.got_server_key_exchange = 1;
            break;
#endif

#ifndef NO_WOLFSSL_CLIENT
        case certificate_request:
        #ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                WOLFSSL_MSG("CertificateRequest received by server");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_certificate_request) {
                WOLFSSL_MSG("Duplicate CertificateRequest received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            if (!ssl->msgsReceived.got_server_hello) {
                WOLFSSL_MSG("No ServerHello before CertificateRequest");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            if (!ssl->msgsReceived.got_certificate) {
                WOLFSSL_MSG("No Certificate before CertificateRequest");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            if (!ssl->options.resuming && ssl->specs.kea != rsa_kea &&
                    (ssl->specs.kea != ecc_diffie_hellman_kea ||
                     !ssl->specs.static_ecdh) &&
                    ssl->specs.kea != ecc_static_diffie_hellman_kea &&
                    !ssl->msgsReceived.got_server_key_exchange) {
                WOLFSSL_MSG("No ServerKeyExchange before CertificateRequest");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            if (ssl->msgsReceived.got_server_hello_done ||
                    ssl->msgsReceived.got_change_cipher ||
                    ssl->msgsReceived.got_finished) {
                WOLFSSL_MSG("CertificateRequest received in wrong order");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            ssl->msgsReceived.got_certificate_request = 1;

            break;
#endif

#ifndef NO_WOLFSSL_CLIENT
        case server_hello_done:
        #ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                WOLFSSL_MSG("ServerHelloDone received by server");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_server_hello_done) {
                WOLFSSL_MSG("Duplicate ServerHelloDone received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            ssl->msgsReceived.got_server_hello_done = 1;

            if (!ssl->msgsReceived.got_certificate) {
                if (ssl->specs.kea == psk_kea ||
                    ssl->specs.kea == dhe_psk_kea ||
                    ssl->specs.kea == ecdhe_psk_kea ||
                    ssl->options.usingAnon_cipher) {
                    WOLFSSL_MSG("No Cert required");
                }
                else {
                    WOLFSSL_MSG("No Certificate before ServerHelloDone");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
            }
            if (!ssl->msgsReceived.got_server_key_exchange) {
                int pskNoServerHint = 0;  /* not required in this case */

                #ifndef NO_PSK
                    if (ssl->specs.kea == psk_kea &&
                        ssl->arrays != NULL &&
                        ssl->arrays->server_hint[0] == 0)
                        pskNoServerHint = 1;
                #endif
                if (ssl->specs.static_ecdh == 1 ||
                    ssl->specs.kea == rsa_kea ||
                    pskNoServerHint) {
                    WOLFSSL_MSG("No KeyExchange required");
                }
                else {
                    WOLFSSL_MSG("No ServerKeyExchange before ServerDone");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
            }
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
                                     defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
            if (!ssl->msgsReceived.got_certificate_status) {
                int csrRet = 0;
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
                if (csrRet == 0 && ssl->status_request) {
                    WOLFSSL_MSG("No CertificateStatus before ServerHelloDone");
                    csrRet = TLSX_CSR_ForceRequest(ssl);
                }
#endif
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
                if (csrRet == 0 && ssl->status_request_v2) {
                    WOLFSSL_MSG("No CertificateStatus before ServerHelloDone");
                    csrRet = TLSX_CSR2_ForceRequest(ssl);
                }
                if (ssl->status_request_v2) {
                    if (csrRet == 0) {
                        if (TLSX_CSR2_MergePendingCA(ssl) < 0) {
                            WOLFSSL_MSG("Failed to merge pending CAs");
                        }
                    }
                    else {
                        TLSX_CSR2_ClearPendingCA(ssl);
                    }
                }
#endif
                if (csrRet != 0) {
                    /* Error out if OCSP lookups are enabled and failed or if
                     * the user requires stapling. */
                    if (SSL_CM(ssl)->ocspEnabled || SSL_CM(ssl)->ocspMustStaple)
                        return csrRet;
                }
                /* Check that a status request extension was seen as the
                 * CertificateStatus wasn't when an OCSP staple is required.
                 */
                if (
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
                     !ssl->status_request &&
#endif
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
                     !ssl->status_request_v2 &&
#endif
                                                 SSL_CM(ssl)->ocspMustStaple) {
                    WOLFSSL_ERROR_VERBOSE(OCSP_CERT_UNKNOWN);
                    return OCSP_CERT_UNKNOWN;
                }
            }
#endif
            if (ssl->msgsReceived.got_change_cipher ||
                    ssl->msgsReceived.got_finished) {
                WOLFSSL_MSG("ServerHelloDone received in wrong order");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            break;
#endif

#ifndef NO_WOLFSSL_SERVER
        case certificate_verify:
        #ifndef NO_WOLFSSL_CLIENT
            if (ssl->options.side == WOLFSSL_CLIENT_END) {
                WOLFSSL_MSG("CertificateVerify received by client");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_certificate_verify) {
                WOLFSSL_MSG("Duplicate CertificateVerify received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            if (!ssl->msgsReceived.got_client_key_exchange) {
                WOLFSSL_MSG("No ClientKeyExchange before CertVerify");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            if (!ssl->msgsReceived.got_certificate) {
                WOLFSSL_MSG("No Cert before CertVerify");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            if (ssl->msgsReceived.got_change_cipher ||
                    ssl->msgsReceived.got_finished) {
                WOLFSSL_MSG("CertVerify received in wrong order");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            ssl->msgsReceived.got_certificate_verify = 1;
            break;
#endif

#ifndef NO_WOLFSSL_SERVER
        case client_key_exchange:
        #ifndef NO_WOLFSSL_CLIENT
            if (ssl->options.side == WOLFSSL_CLIENT_END) {
                WOLFSSL_MSG("ClientKeyExchange received by client");
                WOLFSSL_ERROR_VERBOSE(SIDE_ERROR);
                return SIDE_ERROR;
            }
        #endif
            if (ssl->msgsReceived.got_client_key_exchange) {
                WOLFSSL_MSG("Duplicate ClientKeyExchange received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            if (!ssl->msgsReceived.got_client_hello) {
                WOLFSSL_MSG("No ClientHello before ClientKeyExchange");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
#ifndef WOLFSSL_NO_CLIENT_AUTH
            if (!ssl->options.resuming && ssl->options.verifyPeer &&
                    !ssl->options.usingPSK_cipher &&
                    !ssl->options.usingAnon_cipher &&
                    !ssl->msgsReceived.got_certificate) {
                return OUT_OF_ORDER_E;
            }
#endif
            if (ssl->msgsReceived.got_certificate_verify||
                    ssl->msgsReceived.got_change_cipher ||
                    ssl->msgsReceived.got_finished) {
                WOLFSSL_MSG("ClientKeyExchange received in wrong order");
                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                return OUT_OF_ORDER_E;
            }
            ssl->msgsReceived.got_client_key_exchange = 1;
            break;
#endif

        case finished:
            if (ssl->msgsReceived.got_finished) {
                WOLFSSL_MSG("Duplicate Finished received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
#ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
                if (ssl->keys.curEpoch == 0) {
                    WOLFSSL_MSG("Finished received with epoch 0");
                    WOLFSSL_ERROR_VERBOSE(SEQUENCE_ERROR);
                    return SEQUENCE_ERROR;
                }
            }
#endif
            if (!ssl->msgsReceived.got_change_cipher) {
                WOLFSSL_MSG("Finished received before ChangeCipher");
                WOLFSSL_ERROR_VERBOSE(NO_CHANGE_CIPHER_E);
                return NO_CHANGE_CIPHER_E;
            }
            ssl->msgsReceived.got_finished = 1;
            break;

        case change_cipher_hs:
            if (ssl->msgsReceived.got_change_cipher) {
                WOLFSSL_MSG("Duplicate ChangeCipher received");
                WOLFSSL_ERROR_VERBOSE(DUPLICATE_MSG_E);
                return DUPLICATE_MSG_E;
            }
            /* DTLS is going to ignore the CCS message if the client key
             * exchange message wasn't received yet. */
            if (!ssl->options.dtls)
                ssl->msgsReceived.got_change_cipher = 1;

#ifndef NO_WOLFSSL_CLIENT
            if (ssl->options.side == WOLFSSL_CLIENT_END) {
                if (!ssl->msgsReceived.got_server_hello) {
                    WOLFSSL_MSG("ChangeCipherSpec received in wrong order");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                if (!ssl->options.resuming) {
                   if (!ssl->msgsReceived.got_server_hello_done) {
                        WOLFSSL_MSG("No ServerHelloDone before ChangeCipher");
                        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                        return OUT_OF_ORDER_E;
                   }
                }
            #ifdef HAVE_SESSION_TICKET
                if (ssl->expect_session_ticket) {
                    WOLFSSL_MSG("Expected session ticket missing");
                    #ifdef WOLFSSL_DTLS
                        if (ssl->options.dtls) {
                            WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                            return OUT_OF_ORDER_E;
                        }
                    #endif
                    WOLFSSL_ERROR_VERBOSE(SESSION_TICKET_EXPECT_E);
                    return SESSION_TICKET_EXPECT_E;
                }
            #endif
            }
#endif
#ifndef NO_WOLFSSL_SERVER
            if (ssl->options.side == WOLFSSL_SERVER_END) {
                if (!ssl->msgsReceived.got_client_hello) {
                    WOLFSSL_MSG("ChangeCipherSpec received in wrong order");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
                if (!ssl->options.resuming &&
                                   !ssl->msgsReceived.got_client_key_exchange) {
                    WOLFSSL_MSG("No ClientKeyExchange before ChangeCipher");
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
            #ifndef NO_CERTS
                if (ssl->options.verifyPeer &&
                        ssl->options.havePeerCert) {
                    if (!ssl->options.havePeerVerify ||
                            !ssl->msgsReceived.got_certificate_verify) {
                        WOLFSSL_MSG("client didn't send cert verify");
                        #ifdef WOLFSSL_DTLS
                            if (ssl->options.dtls) {
                                WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                                return OUT_OF_ORDER_E;
                            }
                        #endif
                        WOLFSSL_ERROR_VERBOSE(NO_PEER_VERIFY);
                        return NO_PEER_VERIFY;
                    }
                }
            #endif
            }
#endif /* !NO_WOLFSSL_SERVER */
            if (ssl->options.dtls)
                ssl->msgsReceived.got_change_cipher = 1;
            break;

        default:
            WOLFSSL_MSG("Unknown message type");
            WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
            return SANITY_MSG_E;
    }

    return 0;
}

int DoHandShakeMsgType(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                          byte type, word32 size, word32 totalSz)
{
    int ret = 0;
    word32 expectedIdx;

    WOLFSSL_ENTER("DoHandShakeMsgType");

#ifdef WOLFSSL_TLS13
    if (type == hello_retry_request) {
        return DoTls13HandShakeMsgType(ssl, input, inOutIdx, type, size,
                                       totalSz);
    }
#endif

    /* make sure can read the message */
    if (*inOutIdx + size > totalSz) {
        WOLFSSL_MSG("Incomplete Data");
        WOLFSSL_ERROR_VERBOSE(INCOMPLETE_DATA);
        return INCOMPLETE_DATA;
    }

    expectedIdx = *inOutIdx + size +
                  (ssl->keys.encryptionOn ? ssl->keys.padSz : 0);

#if !defined(NO_WOLFSSL_SERVER) && \
    defined(HAVE_SECURE_RENEGOTIATION) && \
    defined(HAVE_SERVER_RENEGOTIATION_INFO)
    if (ssl->options.handShakeDone && type == client_hello &&
            ssl->secure_renegotiation &&
            ssl->secure_renegotiation->enabled)
    {
        WOLFSSL_MSG("Reset handshake state");
        XMEMSET(&ssl->msgsReceived, 0, sizeof(MsgsReceived));
        ssl->options.serverState = NULL_STATE;
        ssl->options.clientState = NULL_STATE;
        ssl->options.connectState = CONNECT_BEGIN;
        ssl->options.acceptState = ACCEPT_FIRST_REPLY_DONE;
        ssl->options.handShakeState = NULL_STATE;
        ssl->secure_renegotiation->cache_status = SCR_CACHE_NEEDED;

        ret = InitHandshakeHashes(ssl);
        if (ret != 0)
            return ret;
    }
#endif

    /* sanity check msg received */
    if ( (ret = SanityCheckMsgReceived(ssl, type)) != 0) {
        WOLFSSL_MSG("Sanity Check on handshake message type received failed");
        return ret;
    }

#if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
    /* add name later, add the handshake header part back on and record layer
     * header */
    if (ssl->toInfoOn) {
        ret = AddPacketInfo(ssl, 0, handshake, input + *inOutIdx -
            HANDSHAKE_HEADER_SZ, size + HANDSHAKE_HEADER_SZ, READ_PROTO,
            RECORD_HEADER_SZ, ssl->heap);
        if (ret != 0)
            return ret;
        #ifdef WOLFSSL_CALLBACKS
        AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo);
        #endif
    }
#endif

#if !defined(HAVE_SECURE_RENEGOTIATION)
    if (ssl->options.handShakeState == HANDSHAKE_DONE && type == client_hello &&
            ssl->options.side == WOLFSSL_SERVER_END) {
        WOLFSSL_MSG("Renegotiation request rejected");
        SendAlert(ssl, alert_fatal, no_renegotiation);
        WOLFSSL_ERROR_VERBOSE(SECURE_RENEGOTIATION_E);
        return SECURE_RENEGOTIATION_E;
    }
#endif

    if (ssl->options.handShakeState == HANDSHAKE_DONE && type != hello_request){
        WOLFSSL_MSG("HandShake message after handshake complete");
        SendAlert(ssl, alert_fatal, unexpected_message);
        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
        return OUT_OF_ORDER_E;
    }

    if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.dtls == 0 &&
               ssl->options.serverState == NULL_STATE && type != server_hello &&
               type != hello_request) {
        WOLFSSL_MSG("First server message not server hello or "
                    "hello request");
        SendAlert(ssl, alert_fatal, unexpected_message);
        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
        return OUT_OF_ORDER_E;
    }

    if (ssl->options.side == WOLFSSL_CLIENT_END && ssl->options.dtls &&
            type == server_hello_done &&
            ssl->options.serverState < SERVER_HELLO_COMPLETE) {
        WOLFSSL_MSG("Server hello done received before server hello in DTLS");
        SendAlert(ssl, alert_fatal, unexpected_message);
        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
        return OUT_OF_ORDER_E;
    }

    if (ssl->options.side == WOLFSSL_SERVER_END &&
               ssl->options.clientState == NULL_STATE && type != client_hello) {
        WOLFSSL_MSG("First client message not client hello");
        SendAlert(ssl, alert_fatal, unexpected_message);
        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
        return OUT_OF_ORDER_E;
    }

    /* above checks handshake state */
    /* hello_request not hashed */
    if (type != hello_request
    #ifdef WOLFSSL_ASYNC_CRYPT
            && ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E)
    #endif
    #ifdef WOLFSSL_NONBLOCK_OCSP
            && ssl->error != WC_NO_ERR_TRACE(OCSP_WANT_READ)
    #endif
    ) {
        ret = HashInput(ssl, input + *inOutIdx, (int)size);
        if (ret != 0) {
            WOLFSSL_MSG("Incomplete handshake hashes");
            return ret;
        }
    }

    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        switch (type) {
        case certificate:
        case server_key_exchange:
        case certificate_request:
        case server_hello_done:
            if (ssl->options.resuming) {
                /* Client requested resumption, but server is doing a
                 * full handshake */

                /* The server's decision to resume isn't known until after the
                 * "server_hello". If subsequent handshake messages like
                 * "certificate" or "server_key_exchange" are received then we
                 * are doing a full handshake */

                /* If the server included a session id then we
                 * treat this as a fatal error, since the server said it was
                 * doing resumption, but did not. */

                /* https://www.rfc-editor.org/rfc/rfc5077.html#section-3.4
                 *   Alternatively, the client MAY include an empty Session ID
                 *   in the ClientHello.  In this case, the client ignores the
                 *   Session ID sent in the ServerHello and determines if the
                 *   server is resuming a session by the subsequent handshake
                 *   messages.
                 */
#ifndef WOLFSSL_WPAS
                if (ssl->arrays->sessionIDSz != 0) {
                    /* Fatal error. Only try to send an alert. RFC 5246 does not
                     * allow for reverting back to a full handshake after the
                     * server has indicated the intention to do a resumption. */
                    (void)SendAlert(ssl, alert_fatal, unexpected_message);
                    WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
                    return OUT_OF_ORDER_E;
                }
#endif
                /* This can occur when ssl->sessionSecretCb is set. EAP-FAST
                 * (RFC 4851) allows for detecting server session resumption
                 * based on the msg received after the ServerHello. */
                WOLFSSL_MSG("Not resuming as thought");
                ssl->options.resuming = 0;
                /* No longer resuming, reset peer authentication state. */
                ssl->options.peerAuthGood = 0;
            }
        }
    }

#ifdef OPENSSL_EXTRA
    if (ssl->CBIS != NULL){
        ssl->cbmode = WOLFSSL_CB_MODE_READ;
        ssl->cbtype = type;
        ssl->CBIS(ssl, WOLFSSL_CB_ACCEPT_LOOP, WOLFSSL_SUCCESS);
    }
#endif

    switch (type) {

    case hello_request:
        WOLFSSL_MSG("processing hello request");
        ret = DoHelloRequest(ssl, input, inOutIdx, size, totalSz);
        break;

#ifndef NO_WOLFSSL_CLIENT
    case hello_verify_request:
        WOLFSSL_MSG("processing hello verify request");
        ret = DoHelloVerifyRequest(ssl, input,inOutIdx, size);
        if (IsEncryptionOn(ssl, 0)) {
            /* access beyond input + size should be checked against totalSz
             */
            if (*inOutIdx + ssl->keys.padSz > totalSz)
                return BUFFER_E;

            *inOutIdx += ssl->keys.padSz;
        }
        break;

    case server_hello:
        WOLFSSL_MSG("processing server hello");
        ret = DoServerHello(ssl, input, inOutIdx, size);
    #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
               ((defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)) || \
                (defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \
                (defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)))
        if (ssl->options.resuming || !IsAtLeastTLSv1_2(ssl) ||
                                               IsAtLeastTLSv1_3(ssl->version)) {

        #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
            if (ret != WC_NO_ERR_TRACE(WC_PENDING_E) &&
                ret != WC_NO_ERR_TRACE(OCSP_WANT_READ))
        #endif
            {
                ssl->options.cacheMessages = 0;
                if ((ssl->hsHashes != NULL) && (ssl->hsHashes->messages != NULL)) {
                    ForceZero(ssl->hsHashes->messages, ssl->hsHashes->length);
                    XFREE(ssl->hsHashes->messages, ssl->heap,
                        DYNAMIC_TYPE_HASHES);
                    ssl->hsHashes->messages = NULL;
                }
            }
        }
    #endif
        break;

#ifndef NO_CERTS
    case certificate_request:
        WOLFSSL_MSG("processing certificate request");
        ret = DoCertificateRequest(ssl, input, inOutIdx, size);
        break;
#endif

    case server_key_exchange:
        WOLFSSL_MSG("processing server key exchange");
        ret = DoServerKeyExchange(ssl, input, inOutIdx, size);
        break;

#ifdef HAVE_SESSION_TICKET
    case session_ticket:
        WOLFSSL_MSG("processing session ticket");
        ret = DoSessionTicket(ssl, input, inOutIdx, size);
        break;
#endif /* HAVE_SESSION_TICKET */
#endif

#if !defined(NO_CERTS) && (!defined(NO_WOLFSSL_CLIENT) || \
                                               !defined(WOLFSSL_NO_CLIENT_AUTH))
    case certificate:
        WOLFSSL_MSG("processing certificate");
        ret = DoCertificate(ssl, input, inOutIdx, size);
        break;

    case certificate_status:
        WOLFSSL_MSG("processing certificate status");
        ret = DoCertificateStatus(ssl, input, inOutIdx, size);
        break;
#endif

    case server_hello_done:
        WOLFSSL_MSG("processing server hello done");
    #ifdef WOLFSSL_CALLBACKS
        if (ssl->hsInfoOn)
            AddPacketName(ssl, "ServerHelloDone");
        if (ssl->toInfoOn)
            AddLateName("ServerHelloDone", &ssl->timeoutInfo);
    #endif
        ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
        if (IsEncryptionOn(ssl, 0))
            *inOutIdx += ssl->keys.padSz;
        break;

    case finished:
        WOLFSSL_MSG("processing finished");
        ret = DoFinished(ssl, input, inOutIdx, size, totalSz, NO_SNIFF);
        break;

#ifndef NO_WOLFSSL_SERVER
    case client_hello:
        WOLFSSL_MSG("processing client hello");
        ret = DoClientHello(ssl, input, inOutIdx, size);
    #if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
               ((defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)) || \
                (defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \
                (defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)))
        if (ssl->options.resuming || !ssl->options.verifyPeer || \
                     !IsAtLeastTLSv1_2(ssl) || IsAtLeastTLSv1_3(ssl->version)) {
        #if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
            if (ret != WC_NO_ERR_TRACE(WC_PENDING_E) &&
                ret != WC_NO_ERR_TRACE(OCSP_WANT_READ))
        #endif
            {
                ssl->options.cacheMessages = 0;
                if ((ssl->hsHashes != NULL) && (ssl->hsHashes->messages != NULL)) {
                    ForceZero(ssl->hsHashes->messages, ssl->hsHashes->length);
                    XFREE(ssl->hsHashes->messages, ssl->heap, DYNAMIC_TYPE_HASHES);
                    ssl->hsHashes->messages = NULL;
                }
            }
        }
    #endif
        /* If size == totalSz then we are in DtlsMsgDrain so no need to worry
         * about padding */
        if (IsEncryptionOn(ssl, 0)) {
            /* access beyond input + size should be checked against totalSz
             */
            if (size != totalSz &&
                    *inOutIdx + ssl->keys.padSz > totalSz)
                return BUFFER_E;
            *inOutIdx += ssl->keys.padSz;
        }
        break;

    case client_key_exchange:
        WOLFSSL_MSG("processing client key exchange");
        ret = DoClientKeyExchange(ssl, input, inOutIdx, size);
        break;

#if (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \
                        defined(HAVE_ED448)) && !defined(WOLFSSL_NO_CLIENT_AUTH)
    case certificate_verify:
        WOLFSSL_MSG("processing certificate verify");
        ret = DoCertificateVerify(ssl, input, inOutIdx, size);
        break;
#endif /* (!NO_RSA || ECC || ED25519 || ED448) && !WOLFSSL_NO_CLIENT_AUTH */

#endif /* !NO_WOLFSSL_SERVER */

    default:
        WOLFSSL_MSG("Unknown handshake message type");
        ret = UNKNOWN_HANDSHAKE_TYPE;
        break;
    }
    if (ret == 0 && expectedIdx != *inOutIdx) {
        WOLFSSL_MSG("Extra data in handshake message");
        if (!ssl->options.dtls)
            SendAlert(ssl, alert_fatal, decode_error);
        ret = DECODE_E;
        WOLFSSL_ERROR_VERBOSE(ret);
    }

#if defined(WOLFSSL_ASYNC_CRYPT) || defined(WOLFSSL_NONBLOCK_OCSP)
    /* if async, offset index so this msg will be processed again */
    if ((ret == WC_NO_ERR_TRACE(WC_PENDING_E) ||
         ret == WC_NO_ERR_TRACE(OCSP_WANT_READ)) && *inOutIdx > 0) {
        *inOutIdx -= HANDSHAKE_HEADER_SZ;
    #ifdef WOLFSSL_DTLS
        if (ssl->options.dtls) {
            *inOutIdx -= DTLS_HANDSHAKE_EXTRA;
        }
    #endif
    }

    /* make sure async error is cleared */
    if (ret == 0 && (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E) ||
                     ssl->error == WC_NO_ERR_TRACE(OCSP_WANT_READ))) {
        ssl->error = 0;
    }
#endif /* WOLFSSL_ASYNC_CRYPT || WOLFSSL_NONBLOCK_OCSP */

#ifdef WOLFSSL_DTLS
    if (ret == 0) {
        if (type == client_hello) {
            /* Advance expected number only if cookie exchange complete */
            if (ssl->msgsReceived.got_client_hello)
                ssl->keys.dtls_expected_peer_handshake_number =
                    ssl->keys.dtls_peer_handshake_number + 1;
        }
        else if (type != finished) {
            ssl->keys.dtls_expected_peer_handshake_number++;
        }
    }
#endif

    WOLFSSL_LEAVE("DoHandShakeMsgType()", ret);
    return ret;
}


static int DoHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                          word32 totalSz)
{
    int    ret = 0;
    word32 inputLength;

    WOLFSSL_ENTER("DoHandShakeMsg");

    if (ssl->arrays == NULL) {
        byte   type;
        word32 size;

        if (GetHandshakeHeader(ssl,input,inOutIdx,&type, &size, totalSz) != 0) {
            WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
            return PARSE_ERROR;
        }

        ret = EarlySanityCheckMsgReceived(ssl, type, size);
        if (ret != 0) {
            WOLFSSL_ERROR(ret);
            return ret;
        }

        if (size > MAX_HANDSHAKE_SZ) {
            WOLFSSL_MSG("Handshake message too large");
            WOLFSSL_ERROR_VERBOSE(HANDSHAKE_SIZE_ERROR);
            return HANDSHAKE_SIZE_ERROR;
        }

        return DoHandShakeMsgType(ssl, input, inOutIdx, type, size, totalSz);
    }

    inputLength = ssl->buffers.inputBuffer.length - *inOutIdx;

    /* If there is a pending fragmented handshake message,
     * pending message size will be non-zero. */
    if (ssl->arrays->pendingMsgSz == 0) {
        byte   type;
        word32 size;

        if (GetHandshakeHeader(ssl, input, inOutIdx, &type, &size,
                               totalSz) != 0) {
            WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
            return PARSE_ERROR;
        }

        ret = EarlySanityCheckMsgReceived(ssl, type,
                min(inputLength - HANDSHAKE_HEADER_SZ, size));
        if (ret != 0) {
            WOLFSSL_ERROR(ret);
            return ret;
        }

        /* Cap the maximum size of a handshake message to something reasonable.
         * By default is the maximum size of a certificate message assuming
         * nine 2048-bit RSA certificates in the chain. */
        if (size > MAX_HANDSHAKE_SZ) {
            WOLFSSL_MSG("Handshake message too large");
            WOLFSSL_ERROR_VERBOSE(HANDSHAKE_SIZE_ERROR);
            return HANDSHAKE_SIZE_ERROR;
        }

        /* size is the size of the certificate message payload */
        if (inputLength - HANDSHAKE_HEADER_SZ < size) {
            ssl->arrays->pendingMsgType = type;
            ssl->arrays->pendingMsgSz = size + HANDSHAKE_HEADER_SZ;
            ssl->arrays->pendingMsg = (byte*)XMALLOC(size + HANDSHAKE_HEADER_SZ,
                                                     ssl->heap,
                                                     DYNAMIC_TYPE_ARRAYS);
            if (ssl->arrays->pendingMsg == NULL)
                return MEMORY_E;
            XMEMCPY(ssl->arrays->pendingMsg,
                    input + *inOutIdx - HANDSHAKE_HEADER_SZ,
                    inputLength);
            ssl->arrays->pendingMsgOffset = inputLength;
            *inOutIdx += inputLength - HANDSHAKE_HEADER_SZ;
            return 0;
        }

        ret = DoHandShakeMsgType(ssl, input, inOutIdx, type, size, totalSz);
    }
    else {
        word32 pendSz =
            ssl->arrays->pendingMsgSz - ssl->arrays->pendingMsgOffset;

        /* Catch the case where there may be the remainder of a fragmented
         * handshake message and the next handshake message in the same
         * record. */
        if (inputLength > pendSz)
            inputLength = pendSz;

        ret = EarlySanityCheckMsgReceived(ssl, ssl->arrays->pendingMsgType,
                inputLength);
        if (ret != 0) {
            WOLFSSL_ERROR(ret);
            return ret;
        }

    #ifdef WOLFSSL_ASYNC_CRYPT
        if (ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E))
    #endif
        {
            /* for async this copy was already done, do not replace, since
             * contents may have been changed for inline operations */
            XMEMCPY(ssl->arrays->pendingMsg + ssl->arrays->pendingMsgOffset,
                    input + *inOutIdx, inputLength);
        }
        ssl->arrays->pendingMsgOffset += inputLength;
        *inOutIdx += inputLength;

        if (ssl->arrays->pendingMsgOffset == ssl->arrays->pendingMsgSz)
        {
            word32 idx = HANDSHAKE_HEADER_SZ;
            ret = DoHandShakeMsgType(ssl,
                                     ssl->arrays->pendingMsg,
                                     &idx, ssl->arrays->pendingMsgType,
                                     ssl->arrays->pendingMsgSz - idx,
                                     ssl->arrays->pendingMsgSz);
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                /* setup to process fragment again */
                ssl->arrays->pendingMsgOffset -= inputLength;
                *inOutIdx -= inputLength;
            }
            else
        #endif
            {
                XFREE(ssl->arrays->pendingMsg, ssl->heap, DYNAMIC_TYPE_ARRAYS);
                ssl->arrays->pendingMsg = NULL;
                ssl->arrays->pendingMsgSz = 0;
            }
        }
    }

    WOLFSSL_LEAVE("DoHandShakeMsg()", ret);
    return ret;
}
#endif /* !NO_TLS && !WOLFSSL_NO_TLS12 */

#ifdef WOLFSSL_EXTRA_ALERTS
int SendFatalAlertOnly(WOLFSSL *ssl, int error)
{
    int why;

    /* already sent a more specific fatal alert  */
    if (ssl->alert_history.last_tx.level == alert_fatal)
        return 0;

    switch (error) {
        /* not fatal errors */
    case WC_NO_ERR_TRACE(WANT_WRITE):
    case WC_NO_ERR_TRACE(WANT_READ):
    case WC_NO_ERR_TRACE(ZERO_RETURN):
#ifdef WOLFSSL_NONBLOCK_OCSP
    case WC_NO_ERR_TRACE(OCSP_WANT_READ):
#endif
#ifdef WOLFSSL_ASYNC_CRYPT
    case WC_NO_ERR_TRACE(WC_PENDING_E):
#endif
        return 0;

    /* peer already disconnected and ssl is possibly in bad state
     * don't try to send an alert */
    case WC_NO_ERR_TRACE(SOCKET_ERROR_E):
        return error;

    case WC_NO_ERR_TRACE(BUFFER_ERROR):
    case WC_NO_ERR_TRACE(ASN_PARSE_E):
    case WC_NO_ERR_TRACE(COMPRESSION_ERROR):
        why = decode_error;
        break;
    case WC_NO_ERR_TRACE(VERIFY_FINISHED_ERROR):
    case WC_NO_ERR_TRACE(SIG_VERIFY_E):
        why = decrypt_error;
        break;
    case WC_NO_ERR_TRACE(DUPLICATE_MSG_E):
    case WC_NO_ERR_TRACE(NO_CHANGE_CIPHER_E):
    case WC_NO_ERR_TRACE(OUT_OF_ORDER_E):
        why = unexpected_message;
        break;
    case WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E):
        why = bad_record_mac;
        break;
    case WC_NO_ERR_TRACE(MATCH_SUITE_ERROR):
    case WC_NO_ERR_TRACE(VERSION_ERROR):
    default:
        why = handshake_failure;
        break;
    }

    return SendAlert(ssl, alert_fatal, why);
}
#else
int SendFatalAlertOnly(WOLFSSL *ssl, int error)
{
    (void)ssl;
    (void)error;
    /* no op */
    return 0;
}
#endif /* WOLFSSL_EXTRA_ALERTS */

#ifdef WOLFSSL_DTLS

static int _DtlsCheckWindow(WOLFSSL* ssl)
{
    word32* window;
    word16 cur_hi, next_hi;
    word32 cur_lo, next_lo, diff;
    int curLT;
    WOLFSSL_DTLS_PEERSEQ* peerSeq = NULL;

    if (!ssl->options.haveMcast)
        peerSeq = ssl->keys.peerSeq;
    else {
#ifdef WOLFSSL_MULTICAST
        WOLFSSL_DTLS_PEERSEQ* p;
        int i;

        for (i = 0, p = ssl->keys.peerSeq;
             i < WOLFSSL_DTLS_PEERSEQ_SZ;
             i++, p++) {

            if (p->peerId == ssl->keys.curPeerId) {
                peerSeq = p;
                break;
            }
        }
#endif
    }

    if (peerSeq == NULL) {
        WOLFSSL_MSG("Could not find peer sequence");
        return 0;
    }

    if (ssl->keys.curEpoch == peerSeq->nextEpoch) {
        next_hi = peerSeq->nextSeq_hi;
        next_lo = peerSeq->nextSeq_lo;
        window = peerSeq->window;
    }
    else if (ssl->keys.curEpoch == peerSeq->nextEpoch - 1) {
        next_hi = peerSeq->prevSeq_hi;
        next_lo = peerSeq->prevSeq_lo;
        window = peerSeq->prevWindow;
    }
    else {
        return 0;
    }

    cur_hi = ssl->keys.curSeq_hi;
    cur_lo = ssl->keys.curSeq_lo;

    /* If the difference between next and cur is > 2^32, way outside window. */
    if ((cur_hi > next_hi + 1) || (next_hi > cur_hi + 1)) {
        WOLFSSL_MSG("Current record from way too far in the future.");
        return 0;
    }

    if (cur_hi == next_hi) {
        curLT = cur_lo < next_lo;
        diff = curLT ? next_lo - cur_lo : cur_lo - next_lo;
    }
    else {
        curLT = cur_hi < next_hi;
        diff = curLT ? cur_lo - next_lo : next_lo - cur_lo;
    }

    /* Check to see that the next value is greater than the number of messages
     * trackable in the window, and that the difference between the next
     * expected sequence number and the received sequence number is inside the
     * window. */
    if ((next_hi || next_lo > DTLS_SEQ_BITS) &&
        curLT && (diff > DTLS_SEQ_BITS)) {

        WOLFSSL_MSG("Current record sequence number from the past.");
        return 0;
    }
#ifdef WOLFSSL_DTLS_DISALLOW_FUTURE
    else if (!curLT && (diff > DTLS_SEQ_BITS)) {
        WOLFSSL_MSG("Rejecting message too far into the future.");
        return 0;
    }
#endif
    else if (curLT) {
        word32 idx;
        word32 newDiff;
        if (diff == 0) {
            WOLFSSL_MSG("DTLS sanity check failed");
            return 0;
        }
        diff--;
        idx = diff / DTLS_WORD_BITS;
        newDiff = diff % DTLS_WORD_BITS;

        /* verify idx is valid for window array */
        if (idx >= WOLFSSL_DTLS_WINDOW_WORDS) {
            WOLFSSL_MSG("Invalid DTLS windows index");
            return 0;
        }

        if (window[idx] & (1 << newDiff)) {
            WOLFSSL_MSG("Current record sequence number already received.");
            return 0;
        }
    }

    return 1;
}

#ifdef WOLFSSL_DTLS13
static WC_INLINE int Dtls13CheckWindow(WOLFSSL* ssl)
{
    w64wrapper nextSeq, seq;
    w64wrapper diff64;
    word32 *window;
    int wordOffset;
    int wordIndex;
    word32 diff;

    WOLFSSL_ENTER("Dtls13CheckWindow");

    if (ssl->dtls13DecryptEpoch == NULL) {
        WOLFSSL_MSG("Can't find decrypting epoch");
        return 0;
    }

    nextSeq = ssl->dtls13DecryptEpoch->nextPeerSeqNumber;
    window = ssl->dtls13DecryptEpoch->window;
    seq = ssl->keys.curSeq;

    if (w64GTE(seq, nextSeq))
        return 1;

    /* seq < nextSeq, nextSeq - seq */
    diff64 = w64Sub(nextSeq, seq);

    /* diff >= DTLS_SEQ_BITS, outside of the window */
    if (w64GT(diff64, w64From32(0, DTLS_SEQ_BITS)))
        return 0;

    /* we are assuming DTLS_SEQ_BITS <= 2**32 */
    diff = w64GetLow32(diff64);

    /* zero based index */
    diff--;

    wordIndex = ((int)diff) / DTLS_WORD_BITS;
    wordOffset = ((int)diff) % DTLS_WORD_BITS;

    if (window[wordIndex] & (1 << wordOffset))
        return 0;

    return 1;
}

#endif /* WOLFSSL_DTLS13 */

#ifdef WOLFSSL_MULTICAST
static WC_INLINE word32 UpdateHighwaterMark(word32 cur, word32 first,
                                         word32 second, word32 high)
{
    word32 newCur = 0;

    if (cur < first)
        newCur = first;
    else if (cur < second)
        newCur = second;
    else if (cur < high)
        newCur = high;

    return newCur;
}
#endif /* WOLFSSL_MULTICAST */

/* diff is the difference between the message sequence and the
 * expected sequence number. 0 is special where it is an overflow. */
static void _DtlsUpdateWindowGTSeq(word32 diff, word32* window)
{
    word32 oldWindow[WOLFSSL_DTLS_WINDOW_WORDS];

    if (diff == 0 || diff >= DTLS_SEQ_BITS)
        XMEMSET(window, 0, DTLS_SEQ_SZ);
    else {
        word32 i;
        word32 temp = 0;
        word32 idx = diff / DTLS_WORD_BITS;
        diff %= DTLS_WORD_BITS;

        XMEMCPY(oldWindow, window, sizeof(oldWindow));

        for (i = 0; i < WOLFSSL_DTLS_WINDOW_WORDS; i++) {
            if (i < idx)
                window[i] = 0;
            else {
                temp |= (oldWindow[i-idx] << diff);
                window[i] = temp;
                if (diff > 0)
                    temp = oldWindow[i-idx] >> (DTLS_WORD_BITS - diff);
                else
                    temp = 0;
            }
        }
    }
    window[0] |= 1;
}

int wolfSSL_DtlsUpdateWindow(word16 cur_hi, word32 cur_lo,
        word16* next_hi, word32* next_lo, word32 *window)
{
    word32 diff;
    int curLT;

    if (cur_hi == *next_hi) {
        curLT = cur_lo < *next_lo;
        diff = curLT ? *next_lo - cur_lo : cur_lo - *next_lo;
    }
    else {
        if (cur_hi > *next_hi + 1) {
            /* reset window */
            _DtlsUpdateWindowGTSeq(0, window);
            *next_lo = cur_lo + 1;
            if (*next_lo == 0)
                *next_hi = cur_hi + 1;
            else
                *next_hi = cur_hi;
            return 1;
        }
        else if (*next_hi > cur_hi + 1) {
            return 1;
        }
        else {
            curLT = cur_hi < *next_hi;
            if (curLT) {
                if (*next_lo < DTLS_SEQ_BITS &&
                        cur_lo >= (((word32)0xFFFFFFFF) - DTLS_SEQ_BITS)) {
                    /* diff here can still result in a difference that can not
                     * be stored in the window. The index is checked against
                     * WOLFSSL_DTLS_WINDOW_WORDS later. */
                    diff = *next_lo + ((word32)0xFFFFFFFF - cur_lo) + 1;
                }
                else {
                    /* Too far back to update */
                    return 1;
                }
            }
            else {
                if (*next_lo >= (((word32)0xFFFFFFFF) - DTLS_SEQ_BITS) &&
                        cur_lo < DTLS_SEQ_BITS) {
                    /* diff here can still result in a difference that can not
                     * be stored in the window. The index is checked against
                     * WOLFSSL_DTLS_WINDOW_WORDS later. */
                    diff = cur_lo - *next_lo;
                }
                else {
                    _DtlsUpdateWindowGTSeq(0, window);
                    *next_lo = cur_lo + 1;
                    if (*next_lo == 0)
                        *next_hi = cur_hi + 1;
                    else
                        *next_hi = cur_hi;
                    return 1;
                }
            }
        }
    }

    if (curLT) {
        word32 idx;

        diff--;
        idx = diff / DTLS_WORD_BITS;
        diff %= DTLS_WORD_BITS;

        if (idx < WOLFSSL_DTLS_WINDOW_WORDS)
            window[idx] |= (1U << diff);
    }
    else {
        _DtlsUpdateWindowGTSeq(diff + 1, window);
        *next_lo = cur_lo + 1;
        if (*next_lo == 0)
            *next_hi = cur_hi + 1;
        else
            *next_hi = cur_hi;
    }

    return 1;
}

int DtlsUpdateWindow(WOLFSSL* ssl)
{
    WOLFSSL_DTLS_PEERSEQ* peerSeq = ssl->keys.peerSeq;
    word16 *next_hi;
    word32 *next_lo;
    word32* window;

#ifdef WOLFSSL_MULTICAST
    word32 cur_lo = ssl->keys.curSeq_lo;

    if (ssl->options.haveMcast) {
        WOLFSSL_DTLS_PEERSEQ* p;
        int i;

        peerSeq = NULL;
        for (i = 0, p = ssl->keys.peerSeq;
             i < WOLFSSL_DTLS_PEERSEQ_SZ;
             i++, p++) {

            if (p->peerId == ssl->keys.curPeerId) {
                peerSeq = p;
                break;
            }
        }

        if (peerSeq == NULL) {
            WOLFSSL_MSG("Couldn't find that peer ID to update window.");
            return 0;
        }

        if (p->highwaterMark && cur_lo >= p->highwaterMark) {
            int cbError = 0;

            if (ssl->ctx->mcastHwCb)
                cbError = ssl->ctx->mcastHwCb(p->peerId,
                                              ssl->ctx->mcastMaxSeq,
                                              cur_lo, ssl->mcastHwCbCtx);
            if (cbError) {
                WOLFSSL_MSG("Multicast highwater callback returned an error.");
                return MCAST_HIGHWATER_CB_E;
            }

            p->highwaterMark = UpdateHighwaterMark(cur_lo,
                                                   ssl->ctx->mcastFirstSeq,
                                                   ssl->ctx->mcastSecondSeq,
                                                   ssl->ctx->mcastMaxSeq);
        }
    }
#endif

    if (ssl->keys.curEpoch == peerSeq->nextEpoch) {
        next_hi = &peerSeq->nextSeq_hi;
        next_lo = &peerSeq->nextSeq_lo;
        window = peerSeq->window;
    }
    else {
        next_hi = &peerSeq->prevSeq_hi;
        next_lo = &peerSeq->prevSeq_lo;
        window = peerSeq->prevWindow;
    }

    return wolfSSL_DtlsUpdateWindow(ssl->keys.curSeq_hi, ssl->keys.curSeq_lo,
            next_hi, next_lo, window);
}

#ifdef WOLFSSL_DTLS13

/* Update DTLS 1.3 window
 * Return
 *   0 on successful update
 *  <0 on error
 */
static int Dtls13UpdateWindow(WOLFSSL* ssl)
{
    w64wrapper nextSeq, seq;
    w64wrapper diff64;
    word32 *window;
    int wordOffset;
    int wordIndex;
    word32 diff;
    Dtls13Epoch* e = ssl->dtls13DecryptEpoch;

    WOLFSSL_ENTER("Dtls13UpdateWindow");

    if (ssl->dtls13DecryptEpoch == NULL) {
        WOLFSSL_MSG("Can't find decrypting Epoch");
        return BAD_STATE_E;
    }

    if (!w64Equal(ssl->keys.curEpoch64, ssl->dtls13DecryptEpoch->epochNumber)) {
        /* ssl->dtls13DecryptEpoch has been updated since we received the msg */
        e = Dtls13GetEpoch(ssl, ssl->keys.curEpoch64);
        if (e == NULL) {
            WOLFSSL_MSG("Can't find decrypting Epoch");
            return BAD_STATE_E;
        }
    }

    nextSeq = e->nextPeerSeqNumber;
    window = e->window;
    seq = ssl->keys.curSeq;

    /* seq < nextSeq */
    if (w64LT(seq, nextSeq)) {
        diff64 = w64Sub(nextSeq, seq);

        /* zero based index */
        w64Decrement(&diff64);

        /* FIXME: check that diff64 < DTLS_WORDS_BITS */
        diff = w64GetLow32(diff64);
        wordIndex = ((int)diff) / DTLS_WORD_BITS;
        wordOffset = ((int)diff) % DTLS_WORD_BITS;

        if (wordIndex >= WOLFSSL_DTLS_WINDOW_WORDS) {
            WOLFSSL_MSG("Invalid sequence number to Dtls13UpdateWindow");
            return BAD_STATE_E;
        }

        window[wordIndex] |= (1 << wordOffset);
        return 0;
    }

    /* seq >= nextSeq, seq - nextSeq */
    diff64 = w64Sub(seq, nextSeq);

    /* as we are considering nextSeq inside the window, we should add + 1 */
    w64Increment(&diff64);
    _DtlsUpdateWindowGTSeq(w64GetLow32(diff64), window);

    w64Increment(&seq);
    e->nextPeerSeqNumber = seq;

    return 0;
}

int Dtls13UpdateWindowRecordRecvd(WOLFSSL* ssl)
{
    int ret = Dtls13UpdateWindow(ssl);
    if (ret != 0)
        return ret;
    return Dtls13RecordRecvd(ssl);
}
#endif /* WOLFSSL_DTLS13 */

int DtlsMsgDrain(WOLFSSL* ssl)
{
    DtlsMsg* item = ssl->dtls_rx_msg_list;
    int ret = 0;

    WOLFSSL_ENTER("DtlsMsgDrain");

    /* While there is an item in the store list, and it is the expected
     * message, and it is complete, and there hasn't been an error in the
     * last message... */
    while (item != NULL &&
            ssl->keys.dtls_expected_peer_handshake_number == item->seq &&
            item->ready && ret == 0) {
        word32 idx = 0;

    #ifndef WOLFSSL_DISABLE_EARLY_SANITY_CHECKS
        ret = MsgCheckEncryption(ssl, item->type, item->encrypted);
        if (ret != 0) {
            SendAlert(ssl, alert_fatal, unexpected_message);
            break;
        }
    #endif

    #ifdef WOLFSSL_NO_TLS12
        ret = DoTls13HandShakeMsgType(ssl, item->fullMsg, &idx, item->type,
                                      item->sz, item->sz);
    #else
        ret = DoHandShakeMsgType(ssl, item->fullMsg, &idx, item->type,
                                      item->sz, item->sz);
    #endif
        if (ret == 0) {
            DtlsTxMsgListClean(ssl);
        }
        else if (!IsAtLeastTLSv1_3(ssl->version)) {
            if (SendFatalAlertOnly(ssl, ret) == WC_NO_ERR_TRACE(SOCKET_ERROR_E)) {
                ret = SOCKET_ERROR_E;
            }
        }
    #ifdef WOLFSSL_ASYNC_CRYPT
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            break;
        }
    #endif
        ssl->dtls_rx_msg_list = item->next;
        DtlsMsgDelete(item, ssl->heap);
        item = ssl->dtls_rx_msg_list;
        ssl->dtls_rx_msg_list_sz--;
    }

    WOLFSSL_LEAVE("DtlsMsgDrain()", ret);
    return ret;
}


static int DoDtlsHandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                          word32 totalSz)
{
    byte type;
    word32 size;
    word32 fragOffset, fragSz;
    int ret = 0;
    int ignoreFinished = 0;

    WOLFSSL_ENTER("DoDtlsHandShakeMsg");

    /* parse header */
    if (GetDtlsHandShakeHeader(ssl, input, inOutIdx, &type,
                               &size, &fragOffset, &fragSz, totalSz) != 0) {
        WOLFSSL_ERROR(PARSE_ERROR);
        return PARSE_ERROR;
    }

    ret = EarlySanityCheckMsgReceived(ssl, type, fragSz);
    if (ret != 0) {
        WOLFSSL_ERROR(ret);
        return ret;
    }

    /* Cap the maximum size of a handshake message to something reasonable.
     * By default is the maximum size of a certificate message assuming
     * nine 2048-bit RSA certificates in the chain. */
    if (size > MAX_HANDSHAKE_SZ) {
        WOLFSSL_MSG("Handshake message too large");
        return HANDSHAKE_SIZE_ERROR;
    }

    /* check that we have complete fragment */
    if (*inOutIdx + fragSz > totalSz) {
        WOLFSSL_ERROR(INCOMPLETE_DATA);
        return INCOMPLETE_DATA;
    }

    /* check that the fragment is contained in the message */
    if (fragOffset + fragSz > size) {
        WOLFSSL_ERROR(LENGTH_ERROR);
        return LENGTH_ERROR;
    }

    if (type == finished && ssl->keys.dtls_peer_handshake_number >=
                            ssl->keys.dtls_expected_peer_handshake_number &&
                            ssl->keys.curEpoch == ssl->keys.dtls_epoch &&
                            ssl->keys.curEpoch != 0) {
        /* finished msg should be ignored from the current epoch
         * if it comes from a previous handshake */
        if (ssl->options.side == WOLFSSL_CLIENT_END) {
            ignoreFinished = ssl->options.connectState < FINISHED_DONE;
        }
        else {
            ignoreFinished = ssl->options.acceptState < ACCEPT_FINISHED_DONE;
        }
    }

#if !defined(NO_WOLFSSL_SERVER)
    if (ssl->options.side == WOLFSSL_SERVER_END &&
            ssl->options.acceptState < ACCEPT_FIRST_REPLY_DONE &&
            type != client_hello) {
        WOLFSSL_MSG("Ignoring other messages before we verify a ClientHello");
        *inOutIdx = totalSz;
        return 0;
    }
#endif

    /* Check the handshake sequence number first. If out of order,
     * add the current message to the list. If the message is in order,
     * but it is a fragment, add the current message to the list, then
     * check the head of the list to see if it is complete, if so, pop
     * it out as the current message. If the message is complete and in
     * order, process it. Check the head of the list to see if it is in
     * order, if so, process it. (Repeat until list exhausted.) If the
     * head is out of order, return for more processing.
     */
    if (ssl->keys.dtls_peer_handshake_number >
            ssl->keys.dtls_expected_peer_handshake_number &&
            /* Only client_hello shouldn't be ignored if the handshake
             * num is greater */
            (type == client_hello ||
                    ssl->options.handShakeState != HANDSHAKE_DONE) &&
            !ignoreFinished) {
        /* Current message is out of order. It will get stored in the list.
         * Storing also takes care of defragmentation. If the messages is a
         * client hello, we need to process this out of order; the server
         * is not supposed to keep state, but the second client hello will
         * have a different handshake sequence number than is expected, and
         * the server shouldn't be expecting any particular handshake sequence
         * number. (If the cookie changes multiple times in quick succession,
         * the client could be sending multiple new client hello messages
         * with newer and newer cookies.) */
        if (type != client_hello) {
            WOLFSSL_MSG("Current message is out of order");
            if (ssl->dtls_rx_msg_list_sz >= DTLS_POOL_SZ) {
                WOLFSSL_MSG("Reached rx msg limit error");
                return DTLS_TOO_MANY_FRAGMENTS_E;
            }

            DtlsMsgStore(ssl, ssl->keys.curEpoch,
                         ssl->keys.dtls_peer_handshake_number,
                         input + *inOutIdx, size, type,
                         fragOffset, fragSz, ssl->heap);
            *inOutIdx += fragSz;
            if (*inOutIdx + ssl->keys.padSz > totalSz) {
                WOLFSSL_ERROR(BUFFER_E);
                return BUFFER_E;
            }
            *inOutIdx += ssl->keys.padSz;
            ret = 0;
            #ifndef WOLFSSL_DTLS_RESEND_ONLY_TIMEOUT
            /* If we receive an out of order last flight msg then retransmit */
            if (type == server_hello_done || type == finished) {
                ret = DtlsMsgPoolSend(ssl, 0);
            }
            #endif
        }
        else {
            if (fragSz < size) {
                /* a fragmented ClientHello, very probably forged or
                   erroneous. Even if the packet is valid, we don't want to save
                   state while processing a ClientHello to avoid DoS attacks */
                WOLFSSL_MSG("Ignoring datagram with fragmented ClientHello");
                *inOutIdx = totalSz;
            }
            else {
            #ifdef WOLFSSL_NO_TLS12
                ret = DoTls13HandShakeMsgType(ssl, input, inOutIdx, type, size,
                    totalSz);
            #else
                ret = DoHandShakeMsgType(ssl, input, inOutIdx, type, size,
                    totalSz);
            #endif
            }
        }
    }
    else if (ssl->keys.dtls_peer_handshake_number <
                ssl->keys.dtls_expected_peer_handshake_number ||
            /* ignore all handshake messages if we are done with the
             * handshake */
            (ssl->keys.dtls_peer_handshake_number >
                ssl->keys.dtls_expected_peer_handshake_number &&
                ssl->options.handShakeState == HANDSHAKE_DONE) ||
            ignoreFinished) {
        /* Already saw this message and processed it. It can be ignored. */
        WOLFSSL_MSG("Already saw this message and processed it");
        *inOutIdx += fragSz;
        if (*inOutIdx + ssl->keys.padSz > totalSz) {
            WOLFSSL_ERROR(BUFFER_E);
            return BUFFER_E;
        }
        #ifndef WOLFSSL_DTLS_RESEND_ONLY_TIMEOUT
        if (IsDtlsNotSctpMode(ssl) &&
            VerifyForDtlsMsgPoolSend(ssl, type, fragOffset)) {

            ret = DtlsMsgPoolSend(ssl, 0);
        }
        #endif
        *inOutIdx += ssl->keys.padSz;
    }
    else if (fragSz < size) {
        /* Since this branch is in order, but fragmented, dtls_rx_msg_list will
         * be pointing to the message with this fragment in it. Check it to see
         * if it is completed. */
        WOLFSSL_MSG("Branch is in order, but fragmented");

        if (type == client_hello) {
            WOLFSSL_MSG("Ignoring datagram with fragmented ClientHello");
            *inOutIdx = totalSz;
            return 0;
        }

        if (ssl->dtls_rx_msg_list_sz >= DTLS_POOL_SZ) {
            WOLFSSL_MSG("Reached rx msg limit error");
            WOLFSSL_ERROR(DTLS_TOO_MANY_FRAGMENTS_E);
            return DTLS_TOO_MANY_FRAGMENTS_E;
        }
        DtlsMsgStore(ssl, ssl->keys.curEpoch,
                     ssl->keys.dtls_peer_handshake_number,
                     input + *inOutIdx, size, type,
                     fragOffset, fragSz, ssl->heap);
        *inOutIdx += fragSz;
        if (*inOutIdx + ssl->keys.padSz > totalSz) {
            WOLFSSL_ERROR(BUFFER_E);
            return BUFFER_E;
        }
        *inOutIdx += ssl->keys.padSz;
        ret = 0;
        if (ssl->dtls_rx_msg_list != NULL && ssl->dtls_rx_msg_list->ready)
            ret = DtlsMsgDrain(ssl);
    }
    else {
        /* This branch is in order next, and a complete message. On success
         * clean the tx list. */
        WOLFSSL_MSG("Branch is in order and a complete message");

#ifdef WOLFSSL_ASYNC_CRYPT
        if (ssl->devId != INVALID_DEVID) {
            word32 idx = *inOutIdx;
            if (ssl->dtls_rx_msg_list_sz >= DTLS_POOL_SZ) {
                WOLFSSL_ERROR(BUFFER_ERROR);
                return BUFFER_ERROR;
            }
            if (idx + fragSz + ssl->keys.padSz > totalSz)
                return BUFFER_E;
            *inOutIdx = idx + fragSz + ssl->keys.padSz;
            /* In async mode always store the message and process it with
             * DtlsMsgDrain because in case of a WC_PENDING_E it will be
             * easier this way. */
            if (ssl->dtls_rx_msg_list_sz >= DTLS_POOL_SZ) {
                WOLFSSL_MSG("Reached rx msg limit error");
                return DTLS_TOO_MANY_FRAGMENTS_E;
            }
            DtlsMsgStore(ssl, ssl->keys.curEpoch,
                         ssl->keys.dtls_peer_handshake_number,
                         input + idx, size, type,
                         fragOffset, fragSz, ssl->heap);
            ret = DtlsMsgDrain(ssl);
        }
        else
#endif
        {
#ifdef WOLFSSL_NO_TLS12
            ret = DoTls13HandShakeMsgType(ssl, input, inOutIdx, type, size,
                                  totalSz);
#else
            ret = DoHandShakeMsgType(ssl, input, inOutIdx, type, size, totalSz);
#endif
            if (ret == 0) {
                DtlsTxMsgListClean(ssl);
                if (ssl->dtls_rx_msg_list != NULL) {
                    ret = DtlsMsgDrain(ssl);
                }
            }
        }
    }

    WOLFSSL_LEAVE("DoDtlsHandShakeMsg()", ret);
    return ret;
}
#endif /* WOLFSSL_DTLS13 */

#ifndef NO_TLS
#ifndef WOLFSSL_NO_TLS12

#ifdef HAVE_AEAD

#if (!defined(NO_PUBLIC_GCM_SET_IV) && \
    ((defined(HAVE_FIPS) || defined(HAVE_SELFTEST)) && \
    (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)))) || \
    (defined(HAVE_POLY1305) && defined(HAVE_CHACHA)) || \
    defined(HAVE_ARIA) || \
    defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)
static WC_INLINE void AeadIncrementExpIV(WOLFSSL* ssl)
{
    int i;
    for (i = AEAD_MAX_EXP_SZ-1; i >= 0; i--) {
        if (++ssl->keys.aead_exp_IV[i]) return;
    }
}
#endif


#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && !defined(NO_CHAPOL_AEAD)
/* Used for the older version of creating AEAD tags with Poly1305 */
static int Poly1305TagOld(WOLFSSL* ssl, byte* additional, int additionalSz,
        const byte* out, byte* cipher, word16 sz, byte* tag)
{
    int ret       = 0;
    int msglen    = (sz - ssl->specs.aead_mac_size);
    word32 keySz  = 32;
    byte padding[8]; /* used to temporarily store lengths */

#ifdef CHACHA_AEAD_TEST
      printf("Using old version of poly1305 input.\n");
#endif

    if (msglen < 0)
        return INPUT_CASE_ERROR;

    if ((ret = wc_Poly1305SetKey(ssl->auth.poly1305, cipher, keySz)) != 0)
        return ret;

    if ((ret = wc_Poly1305Update(ssl->auth.poly1305, additional,
            additionalSz)) != 0)
        return ret;

    /* length of additional input plus padding */
    XMEMSET(padding, 0, sizeof(padding));
    padding[0] = (byte)additionalSz;
    if ((ret = wc_Poly1305Update(ssl->auth.poly1305, padding,
                    sizeof(padding))) != 0)
        return ret;


    /* add cipher info and then its length */
    XMEMSET(padding, 0, sizeof(padding));
    if ((ret = wc_Poly1305Update(ssl->auth.poly1305, out, (word32)msglen)) != 0)
        return ret;

    /* 32 bit size of cipher to 64 bit endian */
    padding[0] =  msglen        & 0xff;
    padding[1] = (msglen >>  8) & 0xff;
    padding[2] = ((word32)msglen >> 16) & 0xff;
    padding[3] = ((word32)msglen >> 24) & 0xff;
    if ((ret = wc_Poly1305Update(ssl->auth.poly1305, padding, sizeof(padding)))
        != 0)
        return ret;

    /* generate tag */
    if ((ret = wc_Poly1305Final(ssl->auth.poly1305, tag)) != 0)
        return ret;

    return ret;
}


/* When the flag oldPoly is not set this follows RFC7905. When oldPoly is set
 * the implementation follows an older draft for creating the nonce and MAC.
 * The flag oldPoly gets set automatically depending on what cipher suite was
 * negotiated in the handshake. This is able to be done because the IDs for the
 * cipher suites was updated in RFC7905 giving unique values for the older
 * draft in comparison to the more recent RFC.
 *
 * ssl   WOLFSSL structure to get cipher and TLS state from
 * out   output buffer to hold encrypted data
 * input data to encrypt
 * sz    size of input
 *
 * Return 0 on success negative values in error case
 */
int ChachaAEADEncrypt(WOLFSSL* ssl, byte* out, const byte* input,
                              word16 sz, byte type)
{
    int ret       = 0;
    word32 msgLen = (sz - ssl->specs.aead_mac_size);
    byte tag[POLY1305_AUTH_SZ];
    byte add[AEAD_AUTH_DATA_SZ];
    int  addSz = 0;
    byte nonce[CHACHA20_NONCE_SZ];
    byte poly[CHACHA20_256_KEY_SIZE]; /* generated key for poly1305 */
    #ifdef CHACHA_AEAD_TEST
        int i;
    #endif
    Keys* keys = &ssl->keys;
    byte* seq = NULL;
    int verifyOrder = CUR_ORDER;

    XMEMSET(tag,   0, sizeof(tag));
    XMEMSET(nonce, 0, sizeof(nonce));
    XMEMSET(poly,  0, sizeof(poly));
    XMEMSET(add,   0, sizeof(add));

#if defined(WOLFSSL_DTLS) && defined(HAVE_SECURE_RENEGOTIATION)
    /*
     * For epochs 2+:
     * * use ssl->secure_renegotiation when encrypting the current epoch as it
     *   has the current epoch cipher material
     * * use PREV_ORDER if encrypting the epoch not in
     *   ssl->secure_renegotiation
     */
    /* opaque SEQ number stored for AD */
    if (ssl->options.dtls && DtlsSCRKeysSet(ssl)) {
        if (ssl->keys.dtls_epoch ==
                    ssl->secure_renegotiation->tmp_keys.dtls_epoch)
            keys = &ssl->secure_renegotiation->tmp_keys;
        else
            verifyOrder = PREV_ORDER;
    }
#endif

    addSz = writeAeadAuthData(ssl, (word16)msgLen, type, add, 0, &seq,
        verifyOrder);
    if (addSz < 0)
        return addSz;

    if (ssl->options.oldPoly != 0) {
        /* get nonce. SEQ should not be incremented again here */
        XMEMCPY(nonce + CHACHA20_OLD_OFFSET, seq, SEQ_SZ);
    }

    #ifdef CHACHA_AEAD_TEST
        printf("Encrypt Additional : ");
        for (i = 0; i < AEAD_AUTH_DATA_SZ; i++) {
            printf("%02x", add[i]);
        }
        printf("\n\n");
        printf("input before encryption :\n");
        for (i = 0; i < sz; i++) {
            printf("%02x", input[i]);
            if ((i + 1) % 16 == 0)
                printf("\n");
        }
        printf("\n");
    #endif

    if (ssl->options.oldPoly == 0) {
        /* nonce is formed by 4 0x00 byte padded to the left followed by 8 byte
         * record sequence number XORed with client_write_IV/server_write_IV */
        XMEMCPY(nonce + CHACHA20_OFFSET, seq, SEQ_SZ);
        xorbuf(nonce, keys->aead_enc_imp_IV, CHACHA20_IMP_IV_SZ);
    }
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Add("ChachaAEADEncrypt nonce", nonce, CHACHA20_NONCE_SZ);
#endif

    /* set the nonce for chacha and get poly1305 key */
    if ((ret = wc_Chacha_SetIV(ssl->encrypt.chacha, nonce, 0)) != 0) {
        ForceZero(nonce, CHACHA20_NONCE_SZ);
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
    #endif
        return ret;
    }

    /* create Poly1305 key using chacha20 keystream */
    if ((ret = wc_Chacha_Process(ssl->encrypt.chacha, poly,
                                                    poly, sizeof(poly))) != 0) {
        ForceZero(nonce, CHACHA20_NONCE_SZ);
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
    #endif
        return ret;
    }
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Add("ChachaAEADEncrypt poly", poly, CHACHA20_256_KEY_SIZE);
#endif

    /* set the counter after getting poly1305 key */
    if ((ret = wc_Chacha_SetIV(ssl->encrypt.chacha, nonce, 1)) != 0) {
        ForceZero(nonce, CHACHA20_NONCE_SZ);
        ForceZero(poly, sizeof(poly));
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
        wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
    #endif
        return ret;
    }
    ForceZero(nonce, CHACHA20_NONCE_SZ); /* done with nonce, clear it */
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
#endif

    /* encrypt the plain text */
    if ((ret = wc_Chacha_Process(ssl->encrypt.chacha, out,
                                                         input, msgLen)) != 0) {
        ForceZero(poly, sizeof(poly));
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
    #endif
        return ret;
    }

    /* get the poly1305 tag using either old padding scheme or more recent */
    if (ssl->options.oldPoly != 0) {
        if ((ret = Poly1305TagOld(ssl, add, addSz, (const byte* )out,
                                                         poly, sz, tag)) != 0) {
            ForceZero(poly, sizeof(poly));
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
        #endif
            return ret;
        }
    }
    else {
        if ((ret = wc_Poly1305SetKey(ssl->auth.poly1305, poly,
                                                          sizeof(poly))) != 0) {
            ForceZero(poly, sizeof(poly));
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
        #endif
            return ret;
        }
        if ((ret = wc_Poly1305_MAC(ssl->auth.poly1305, add, addSz, out, msgLen,
                tag, sizeof(tag))) != 0) {
            ForceZero(poly, sizeof(poly));
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
        #endif
            return ret;
        }
    }
    ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
#endif

    /* append tag to ciphertext */
    XMEMCPY(out + msgLen, tag, sizeof(tag));

    AeadIncrementExpIV(ssl);

    #ifdef CHACHA_AEAD_TEST
       printf("mac tag :\n");
        for (i = 0; i < 16; i++) {
           printf("%02x", tag[i]);
           if ((i + 1) % 16 == 0)
               printf("\n");
        }
       printf("\n\noutput after encrypt :\n");
        for (i = 0; i < sz; i++) {
           printf("%02x", out[i]);
           if ((i + 1) % 16 == 0)
               printf("\n");
        }
        printf("\n");
    #endif

    return ret;
}


/* When the flag oldPoly is not set this follows RFC7905. When oldPoly is set
 * the implementation follows an older draft for creating the nonce and MAC.
 * The flag oldPoly gets set automatically depending on what cipher suite was
 * negotiated in the handshake. This is able to be done because the IDs for the
 * cipher suites was updated in RFC7905 giving unique values for the older
 * draft in comparison to the more recent RFC.
 *
 * ssl   WOLFSSL structure to get cipher and TLS state from
 * plain output buffer to hold decrypted data
 * input data to decrypt
 * sz    size of input
 *
 * Return 0 on success negative values in error case
 */
int ChachaAEADDecrypt(WOLFSSL* ssl, byte* plain, const byte* input,
                           word16 sz)
{
    byte add[AEAD_AUTH_DATA_SZ];
    int  addSz = 0;
    byte nonce[CHACHA20_NONCE_SZ];
    byte tag[POLY1305_AUTH_SZ];
    byte poly[CHACHA20_256_KEY_SIZE]; /* generated key for mac */
    int ret    = 0;
    int msgLen = 0;
    Keys* keys = &ssl->keys;
    byte* seq = NULL;

    if (sz < ssl->specs.aead_mac_size) {
        return BAD_FUNC_ARG;
    }
    msgLen = (sz - ssl->specs.aead_mac_size);

    #ifdef CHACHA_AEAD_TEST
       int i;
       printf("input before decrypt :\n");
        for (i = 0; i < sz; i++) {
           printf("%02x", input[i]);
           if ((i + 1) % 16 == 0)
               printf("\n");
        }
        printf("\n");
    #endif

    XMEMSET(tag,   0, sizeof(tag));
    XMEMSET(poly,  0, sizeof(poly));
    XMEMSET(nonce, 0, sizeof(nonce));
    XMEMSET(add,   0, sizeof(add));

#if defined(WOLFSSL_DTLS) && defined(HAVE_SECURE_RENEGOTIATION)
    /*
     * For epochs 2+:
     * * use ssl->secure_renegotiation when decrypting the latest epoch as it
     *   has the latest epoch cipher material
     */
    if (ssl->options.dtls && DtlsSCRKeysSet(ssl) &&
        ssl->keys.curEpoch == ssl->secure_renegotiation->tmp_keys.dtls_epoch)
        keys = &ssl->secure_renegotiation->tmp_keys;
#endif


    addSz = writeAeadAuthData(ssl, (word16)msgLen, no_type, add, 1, &seq,
        PEER_ORDER);
    if (addSz < 0)
        return addSz;

    if (ssl->options.oldPoly != 0) {
        /* get nonce, SEQ should not be incremented again here */
        XMEMCPY(nonce + CHACHA20_OLD_OFFSET, seq, SEQ_SZ);
    }

    #ifdef CHACHA_AEAD_TEST
        printf("Decrypt Additional : ");
        for (i = 0; i < AEAD_AUTH_DATA_SZ; i++) {
            printf("%02x", add[i]);
        }
        printf("\n\n");
    #endif

    if (ssl->options.oldPoly == 0) {
        /* nonce is formed by 4 0x00 byte padded to the left followed by 8 byte
         * record sequence number XORed with client_write_IV/server_write_IV */
        XMEMCPY(nonce + CHACHA20_OFFSET, seq, SEQ_SZ);
        xorbuf(nonce, keys->aead_dec_imp_IV, CHACHA20_IMP_IV_SZ);
    }
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Add("ChachaAEADEncrypt nonce", nonce, CHACHA20_NONCE_SZ);
#endif

    /* set nonce and get poly1305 key */
    if ((ret = wc_Chacha_SetIV(ssl->decrypt.chacha, nonce, 0)) != 0) {
        ForceZero(nonce, CHACHA20_NONCE_SZ);
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
    #endif
        return ret;
    }

    /* use chacha20 keystream to get poly1305 key for tag */
    if ((ret = wc_Chacha_Process(ssl->decrypt.chacha, poly,
                                                    poly, sizeof(poly))) != 0) {
        ForceZero(nonce, CHACHA20_NONCE_SZ);
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
    #endif
        return ret;
    }
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Add("ChachaAEADEncrypt poly", poly, CHACHA20_256_KEY_SIZE);
#endif

    /* set counter after getting poly1305 key */
    if ((ret = wc_Chacha_SetIV(ssl->decrypt.chacha, nonce, 1)) != 0) {
        ForceZero(nonce, CHACHA20_NONCE_SZ);
        ForceZero(poly, sizeof(poly));
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
        wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
    #endif
        return ret;
    }
    ForceZero(nonce, CHACHA20_NONCE_SZ); /* done with nonce, clear it */
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Check(nonce, CHACHA20_NONCE_SZ);
#endif

    /* get the tag using Poly1305 */
    if (ssl->options.oldPoly != 0) {
        if ((ret = Poly1305TagOld(ssl, add, addSz, input, poly, sz, tag))
                != 0) {
            ForceZero(poly, sizeof(poly));
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
        #endif
            return ret;
        }
    }
    else {
        if ((ret = wc_Poly1305SetKey(ssl->auth.poly1305, poly,
                                                          sizeof(poly))) != 0) {
            ForceZero(poly, sizeof(poly));
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
        #endif
            return ret;
        }
        if ((ret = wc_Poly1305_MAC(ssl->auth.poly1305, add, addSz, input,
                (word32)msgLen, tag, sizeof(tag))) != 0) {
            ForceZero(poly, sizeof(poly));
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
        #endif
            return ret;
        }
    }
    ForceZero(poly, sizeof(poly)); /* done with poly1305 key, clear it */
#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Check(poly, CHACHA20_256_KEY_SIZE);
#endif

    /* check tag sent along with packet */
    if (ConstantCompare(input + msgLen, tag, ssl->specs.aead_mac_size) != 0) {
        WOLFSSL_MSG("MAC did not match");
        if (!ssl->options.dtls)
            SendAlert(ssl, alert_fatal, bad_record_mac);
        WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
        return VERIFY_MAC_ERROR;
    }

    /* if the tag was good decrypt message */
    if ((ret = wc_Chacha_Process(ssl->decrypt.chacha, plain,
                                                           input, (word32)msgLen)) != 0)
        return ret;

    #ifdef CHACHA_AEAD_TEST
       printf("plain after decrypt :\n");
        for (i = 0; i < sz; i++) {
           printf("%02x", plain[i]);
           if ((i + 1) % 16 == 0)
               printf("\n");
        }
        printf("\n");
    #endif

    return ret;
}
#endif /* HAVE_CHACHA && HAVE_POLY1305 && !NO_CHAPOL_AEAD*/
#endif /* HAVE_AEAD */


#if defined(BUILD_AESGCM) || defined(HAVE_AESCCM)

#if !defined(NO_GCM_ENCRYPT_EXTRA) && \
    ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) || \
    (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)))
/* The following type is used to share code between AES-GCM and AES-CCM. */
    typedef int (*AesAuthEncryptFunc)(Aes* aes, byte* out,
                                       const byte* in, word32 sz,
                                       byte* iv, word32 ivSz,
                                       byte* authTag, word32 authTagSz,
                                       const byte* authIn, word32 authInSz);
    #define AES_AUTH_ENCRYPT_FUNC AesAuthEncryptFunc
    #define AES_GCM_ENCRYPT wc_AesGcmEncrypt_ex
    #define AES_CCM_ENCRYPT wc_AesCcmEncrypt_ex
#else
    #define AES_AUTH_ENCRYPT_FUNC wc_AesAuthEncryptFunc
    #define AES_GCM_ENCRYPT wc_AesGcmEncrypt
    #define AES_CCM_ENCRYPT wc_AesCcmEncrypt
#endif

#endif

#if defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)

/* The following type is used to share code between SM4-GCM and SM4-CCM. */
typedef int (*Sm4AuthEncryptFunc)(wc_Sm4* sm4, byte* out, const byte* in,
    word32 sz, const byte* nonce, word32 nonceSz, byte* tag, word32 tagSz,
    const byte* aad, word32 aadSz);
typedef int (*Sm4AuthDecryptFunc)(wc_Sm4* sm4, byte* out, const byte* in,
    word32 sz, const byte* nonce, word32 nonceSz, const byte* tag, word32 tagSz,
    const byte* aad, word32 aadSz);

#define SM4_AUTH_ENCRYPT_FUNC Sm4AuthEncryptFunc
#define SM4_AUTH_DECRYPT_FUNC Sm4AuthDecryptFunc
#define SM4_GCM_ENCRYPT_FUNC wc_Sm4GcmEncrypt
#define SM4_CCM_ENCRYPT_FUNC wc_Sm4CcmEncrypt
#define SM4_GCM_DECRYPT_FUNC wc_Sm4GcmDecrypt
#define SM4_CCM_DECRYPT_FUNC wc_Sm4CcmDecrypt

#endif

#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
#define TLS_AEAD_CID_SZ(s, dec) \
                ((dec) ? DtlsGetCidRxSize((s)) \
                       : DtlsGetCidTxSize((s)))
#define TLS_AEAD_CID(s, dec, b, c) \
                ((dec) ? wolfSSL_dtls_cid_get_rx((s), (b), (c)) \
                       : wolfSSL_dtls_cid_get_tx((s), (b), (c)))
#endif
/**
 *
 * @param ssl           WOLFSSL object
 * @param sz            Length of fragment
 * @param type          Record content type
 * @param additional    AAD output buffer. Assumed AEAD_AUTH_DATA_SZ length.
 * @param dec           Are we decrypting
 * @return >= 0         length of auth data
 *         < 0          error
 */
int writeAeadAuthData(WOLFSSL* ssl, word16 sz, byte type,
        byte* additional, byte dec, byte** seq, int verifyOrder)
{
    word32 idx = 0;
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
    byte cidSz = 0;
    if (ssl->options.dtls && (cidSz = TLS_AEAD_CID_SZ(ssl, dec)) > 0) {
        if (cidSz > DTLS_CID_MAX_SIZE) {
            WOLFSSL_MSG("DTLS CID too large");
            return DTLS_CID_ERROR;
        }

        XMEMSET(additional + idx, 0xFF, SEQ_SZ);
        idx += SEQ_SZ;
        additional[idx++] = dtls12_cid;
        additional[idx++] = cidSz;
        additional[idx++] = dtls12_cid;
        additional[idx++] = dec ? ssl->curRL.pvMajor : ssl->version.major;
        additional[idx++] = dec ? ssl->curRL.pvMinor : ssl->version.minor;
        WriteSEQ(ssl, verifyOrder, additional + idx);
        if (seq != NULL)
            *seq = additional + idx;
        idx += SEQ_SZ;
        if (TLS_AEAD_CID(ssl, dec, additional + idx, (unsigned int)cidSz)
                == WC_NO_ERR_TRACE(WOLFSSL_FAILURE)) {
            WOLFSSL_MSG("DTLS CID write failed");
            return DTLS_CID_ERROR;
        }
        idx += cidSz;
        c16toa(sz, additional + idx);
        idx += LENGTH_SZ;

        return (int)idx;
    }
#endif
    if (seq != NULL)
        *seq = additional + idx;
    WriteSEQ(ssl, verifyOrder, additional + idx);
    idx += SEQ_SZ;
    additional[idx++] = dec ? ssl->curRL.type : type;
    additional[idx++] = dec ? ssl->curRL.pvMajor : ssl->version.major;
    additional[idx++] = dec ? ssl->curRL.pvMinor : ssl->version.minor;
    c16toa(sz, additional + idx);
    idx += LENGTH_SZ;

    return (int)idx;
}

static WC_INLINE int EncryptDo(WOLFSSL* ssl, byte* out, const byte* input,
    word16 sz, int asyncOkay, byte type)
{
    int ret = 0;
#ifdef WOLFSSL_ASYNC_CRYPT
    WC_ASYNC_DEV* asyncDev = NULL;
    word32 event_flags = WC_ASYNC_FLAG_CALL_AGAIN;
#else
    (void)asyncOkay;
#endif

    (void)out;
    (void)sz;
    (void)type;

    if (input == NULL) {
        return BAD_FUNC_ARG;
    }

    switch (ssl->specs.bulk_cipher_algorithm) {
    #ifdef BUILD_ARC4
        case wolfssl_rc4:
            wc_Arc4Process(ssl->encrypt.arc4, out, input, sz);
            break;
    #endif

    #ifdef BUILD_DES3
        case wolfssl_triple_des:
        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            asyncDev = &ssl->encrypt.des3->asyncDev;
            ret = wolfSSL_AsyncInit(ssl, asyncDev, event_flags);
            if (ret != 0)
                break;
        #endif

            ret = wc_Des3_CbcEncrypt(ssl->encrypt.des3, out, input, sz);
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) && asyncOkay) {
                ret = wolfSSL_AsyncPush(ssl, asyncDev);
            }
        #endif
            break;
    #endif

    #if defined(BUILD_AES) && defined(HAVE_AES_CBC)
        case wolfssl_aes:
        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            asyncDev = &ssl->encrypt.aes->asyncDev;
            ret = wolfSSL_AsyncInit(ssl, asyncDev, event_flags);
            if (ret != 0)
                break;
        #endif
            ret = wc_AesCbcEncrypt(ssl->encrypt.aes, out, input, sz);
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) && asyncOkay) {
                ret = wolfSSL_AsyncPush(ssl, asyncDev);
            }
        #endif
            break;
    #endif

    #if defined(BUILD_AESGCM) || defined(HAVE_AESCCM)
        case wolfssl_aes_gcm:
        case wolfssl_aes_ccm:/* GCM AEAD macros use same size as CCM */
        {
            AES_AUTH_ENCRYPT_FUNC aes_auth_fn;
            int additionalSz;

        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            asyncDev = &ssl->encrypt.aes->asyncDev;
            ret = wolfSSL_AsyncInit(ssl, asyncDev, event_flags);
            if (ret != 0)
                break;
        #endif

        #if defined(BUILD_AESGCM) && defined(HAVE_AESCCM)
            aes_auth_fn = (ssl->specs.bulk_cipher_algorithm == wolfssl_aes_gcm)
                            ? AES_GCM_ENCRYPT : AES_CCM_ENCRYPT;
        #elif defined(BUILD_AESGCM)
            aes_auth_fn = AES_GCM_ENCRYPT;
        #else
            aes_auth_fn = AES_CCM_ENCRYPT;
        #endif

            additionalSz = writeAeadAuthData(ssl,
                    /* Length of the plain text minus the explicit
                     * IV length minus the authentication tag size. */
                    sz - (word16)(AESGCM_EXP_IV_SZ) - ssl->specs.aead_mac_size,
                    type, ssl->encrypt.additional, 0, NULL, CUR_ORDER);
            if (additionalSz < 0) {
                ret = additionalSz;
                break;
            }

#if !defined(NO_PUBLIC_GCM_SET_IV) && \
    ((defined(HAVE_FIPS) || defined(HAVE_SELFTEST)) && \
    (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)))
            XMEMCPY(ssl->encrypt.nonce,
                                ssl->keys.aead_enc_imp_IV, AESGCM_IMP_IV_SZ);
            XMEMCPY(ssl->encrypt.nonce + AESGCM_IMP_IV_SZ,
                                ssl->keys.aead_exp_IV, AESGCM_EXP_IV_SZ);
#endif
        #ifdef HAVE_PK_CALLBACKS
            ret = NOT_COMPILED_IN;
            if (ssl->ctx && ssl->ctx->PerformTlsRecordProcessingCb) {
                ret = ssl->ctx->PerformTlsRecordProcessingCb(ssl, 1,
                         out + AESGCM_EXP_IV_SZ, input + AESGCM_EXP_IV_SZ,
                         sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size,
                         ssl->encrypt.nonce, AESGCM_NONCE_SZ,
                         out + sz - ssl->specs.aead_mac_size,
                         ssl->specs.aead_mac_size,
                         ssl->encrypt.additional, (word32)(additionalSz));
            }

            if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN))
        #endif /* HAVE_PK_CALLBACKS */
            {
                ret = aes_auth_fn(ssl->encrypt.aes,
                    out + AESGCM_EXP_IV_SZ, input + AESGCM_EXP_IV_SZ,
                    sz - (word16)(AESGCM_EXP_IV_SZ) - ssl->specs.aead_mac_size,
                    ssl->encrypt.nonce, AESGCM_NONCE_SZ,
                    out + sz - ssl->specs.aead_mac_size,
                    ssl->specs.aead_mac_size,
                    ssl->encrypt.additional, (word32)(additionalSz));
            }

        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) && asyncOkay) {
                ret = wolfSSL_AsyncPush(ssl, asyncDev);
            }
        #endif
#if !defined(NO_PUBLIC_GCM_SET_IV) && \
    ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) || \
    (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)))
            XMEMCPY(out,
                    ssl->encrypt.nonce + AESGCM_IMP_IV_SZ, AESGCM_EXP_IV_SZ);
#endif
        }
        break;
    #endif /* BUILD_AESGCM || HAVE_AESCCM */

    #ifdef HAVE_ARIA
        case wolfssl_aria_gcm:
        {
            int additionalSz;
            byte *outBuf = NULL;

            additionalSz = ret = writeAeadAuthData(ssl,
                    /* Length of the plain text minus the explicit
                     * IV length minus the authentication tag size. */
                    sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size, type,
                    ssl->encrypt.additional, 0, NULL, CUR_ORDER);
            if (ret < 0)
                break;
            ret = 0;

            XMEMCPY(ssl->encrypt.nonce,
                                ssl->keys.aead_enc_imp_IV, AESGCM_IMP_IV_SZ);
            XMEMCPY(ssl->encrypt.nonce + AESGCM_IMP_IV_SZ,
                                ssl->keys.aead_exp_IV, AESGCM_EXP_IV_SZ);
            outBuf = (byte*)XMALLOC(sz - AESGCM_EXP_IV_SZ, ssl->heap,
                                    DYNAMIC_TYPE_TMP_BUFFER);
            if (outBuf == NULL) {
                ret = MEMORY_ERROR;
                break;
            }
            ret = wc_AriaEncrypt(ssl->encrypt.aria, outBuf,
                    (byte*) input + AESGCM_EXP_IV_SZ,
                    sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size,
                    ssl->encrypt.nonce, AESGCM_NONCE_SZ,
                    ssl->encrypt.additional, additionalSz,
                    out + sz - ssl->specs.aead_mac_size,
                    ssl->specs.aead_mac_size
                    );
            if (ret != 0) {
                XFREE(outBuf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
                break;
            }
            XMEMCPY(out,
                    ssl->encrypt.nonce + AESGCM_IMP_IV_SZ, AESGCM_EXP_IV_SZ);
            XMEMCPY(out + AESGCM_EXP_IV_SZ,outBuf,sz - AESGCM_EXP_IV_SZ);
            XFREE(outBuf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
            break;
        }
    #endif

    #ifdef HAVE_CAMELLIA
        case wolfssl_camellia:
            ret = wc_CamelliaCbcEncrypt(ssl->encrypt.cam, out, input, sz);
            break;
    #endif

    #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && \
        !defined(NO_CHAPOL_AEAD)
        case wolfssl_chacha:
            ret = ChachaAEADEncrypt(ssl, out, input, sz, type);
            break;
    #endif

    #ifdef WOLFSSL_SM4_CBC
        case wolfssl_sm4_cbc:
        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            asyncDev = &ssl->encrypt.sm4->asyncDev;
            ret = wolfSSL_AsyncInit(ssl, asyncDev, event_flags);
            if (ret != 0)
                break;
        #endif
            ret = wc_Sm4CbcEncrypt(ssl->encrypt.sm4, out, input, sz);
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) && asyncOkay) {
                ret = wolfSSL_AsyncPush(ssl, asyncDev);
            }
        #endif
            break;
    #endif

    #if defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)
        case wolfssl_sm4_gcm:
        case wolfssl_sm4_ccm:/* GCM AEAD macros use same size as CCM */
        {
            SM4_AUTH_ENCRYPT_FUNC sm4_auth_fn;
            const byte* additionalSrc;

        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            asyncDev = &ssl->encrypt.sm4->asyncDev;
            ret = wolfSSL_AsyncInit(ssl, asyncDev, event_flags);
            if (ret != 0)
                break;
        #endif

        #if defined(WOLFSSL_SM4_GCM) && defined(WOLFSSL_SM4_CCM)
            sm4_auth_fn = (ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_gcm)
                            ? SM4_GCM_ENCRYPT_FUNC : SM4_CCM_ENCRYPT_FUNC;
        #elif defined(WOLFSSL_SM4_GCM)
            sm4_auth_fn = SM4_GCM_ENCRYPT_FUNC;
        #else
            sm4_auth_fn = SM4_CCM_ENCRYPT_FUNC;
        #endif
            additionalSrc = input - 5;

            XMEMSET(ssl->encrypt.additional, 0, AEAD_AUTH_DATA_SZ);

            /* sequence number field is 64-bits */
            WriteSEQ(ssl, CUR_ORDER, ssl->encrypt.additional);

            /* Store the type, version. Unfortunately, they are in
             * the input buffer ahead of the plaintext. */
        #ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
                additionalSrc -= DTLS_HANDSHAKE_EXTRA;
            }
        #endif
            XMEMCPY(ssl->encrypt.additional + AEAD_TYPE_OFFSET,
                                                        additionalSrc, 3);

            /* Store the length of the plain text minus the explicit
             * IV length minus the authentication tag size. */
            c16toa(sz - GCM_EXP_IV_SZ - ssl->specs.aead_mac_size,
                                ssl->encrypt.additional + AEAD_LEN_OFFSET);
            XMEMCPY(ssl->encrypt.nonce,
                                ssl->keys.aead_enc_imp_IV, GCM_IMP_IV_SZ);
            XMEMCPY(ssl->encrypt.nonce + GCM_IMP_IV_SZ,
                                ssl->keys.aead_exp_IV, GCM_EXP_IV_SZ);
            ret = sm4_auth_fn(ssl->encrypt.sm4,
                    out + GCM_EXP_IV_SZ, input + GCM_EXP_IV_SZ,
                    sz - GCM_EXP_IV_SZ - ssl->specs.aead_mac_size,
                    ssl->encrypt.nonce, GCM_NONCE_SZ,
                    out + sz - ssl->specs.aead_mac_size,
                    ssl->specs.aead_mac_size,
                    ssl->encrypt.additional, AEAD_AUTH_DATA_SZ);

        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) && asyncOkay) {
                ret = wolfSSL_AsyncPush(ssl, asyncDev);
            }
        #endif
#if !defined(NO_PUBLIC_GCM_SET_IV) && \
    ((!defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)) || \
    (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION >= 2)))
            XMEMCPY(out,
                    ssl->encrypt.nonce + GCM_IMP_IV_SZ, GCM_EXP_IV_SZ);
#endif
        }
        break;
    #endif /* WOLFSSL_SM4_GCM || WOLFSSL_SM4_CCM */

    #ifdef HAVE_NULL_CIPHER
        case wolfssl_cipher_null:
            if (input != out) {
                XMEMMOVE(out, input, sz);
            }
            break;
    #endif

        default:
            WOLFSSL_MSG("wolfSSL Encrypt programming error");
            ret = ENCRYPT_ERROR;
            WOLFSSL_ERROR_VERBOSE(ret);
    }

#ifdef WOLFSSL_ASYNC_CRYPT
    /* if async is not okay, then block */
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) && !asyncOkay) {
        ret = wc_AsyncWait(ret, asyncDev, event_flags);
    }
#endif

    return ret;
}

static WC_INLINE int Encrypt(WOLFSSL* ssl, byte* out, const byte* input,
    word16 sz, int asyncOkay, byte type)
{
    int ret = 0;

#ifdef WOLFSSL_ASYNC_CRYPT
    if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        ssl->error = 0; /* clear async */
    }
#endif

    switch (ssl->encrypt.state) {
        case CIPHER_STATE_BEGIN:
        {
            if (ssl->encrypt.setup == 0) {
                WOLFSSL_MSG("Encrypt ciphers not setup");
                WOLFSSL_ERROR_VERBOSE(ENCRYPT_ERROR);
                return ENCRYPT_ERROR;
            }

        #ifdef WOLFSSL_CIPHER_TEXT_CHECK
            if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null) {
                XMEMCPY(ssl->encrypt.sanityCheck, input,
                    min(sz, sizeof(ssl->encrypt.sanityCheck)));
            }
        #endif

        #ifdef HAVE_FUZZER
            if (ssl->fuzzerCb)
                ssl->fuzzerCb(ssl, input, sz, FUZZ_ENCRYPT, ssl->fuzzerCtx);
        #endif

        #if defined(BUILD_AESGCM) || defined(HAVE_AESCCM) || defined(HAVE_ARIA)
            /* make sure AES GCM/CCM memory is allocated */
            /* free for these happens in FreeCiphers */
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_aes_ccm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_aes_gcm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_aria_gcm) {
                /* make sure auth iv and auth are allocated */
                if (ssl->encrypt.additional == NULL)
                    ssl->encrypt.additional = (byte*)XMALLOC(AEAD_AUTH_DATA_SZ,
                                                ssl->heap, DYNAMIC_TYPE_CIPHER);
                if (ssl->encrypt.nonce == NULL) {
                    ssl->encrypt.nonce = (byte*)XMALLOC(AESGCM_NONCE_SZ,
                                                ssl->heap, DYNAMIC_TYPE_CIPHER);
                #ifdef WOLFSSL_CHECK_MEM_ZERO
                    if (ssl->encrypt.nonce != NULL) {
                        wc_MemZero_Add("Encrypt nonce", ssl->encrypt.nonce,
                            AESGCM_NONCE_SZ);
                    }
                #endif
                }
                if (ssl->encrypt.additional == NULL ||
                         ssl->encrypt.nonce == NULL) {
                    return MEMORY_E;
                }
            }
        #endif /* BUILD_AESGCM || HAVE_AESCCM || HAVE_ARIA */

        #if defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)
            /* make sure SM4 GCM/CCM memory is allocated */
            /* free for these happens in FreeCiphers */
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_ccm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_gcm) {
                /* make sure auth iv and auth are allocated */
                if (ssl->encrypt.additional == NULL)
                    ssl->encrypt.additional = (byte*)XMALLOC(AEAD_AUTH_DATA_SZ,
                                                ssl->heap, DYNAMIC_TYPE_CIPHER);
                if (ssl->encrypt.nonce == NULL) {
                    ssl->encrypt.nonce = (byte*)XMALLOC(GCM_NONCE_SZ,
                                                ssl->heap, DYNAMIC_TYPE_CIPHER);
                #ifdef WOLFSSL_CHECK_MEM_ZERO
                    if (ssl->encrypt.nonce != NULL) {
                        wc_MemZero_Add("Encrypt nonce", ssl->encrypt.nonce,
                            GCM_NONCE_SZ);
                    }
                #endif
                }
                if (ssl->encrypt.additional == NULL ||
                         ssl->encrypt.nonce == NULL) {
                    return MEMORY_E;
                }
            }
        #endif /* WOLFSSL_SM4_GCM || WOLFSSL_SM4_CCM */

            /* Advance state and proceed */
            ssl->encrypt.state = CIPHER_STATE_DO;
        }
        FALL_THROUGH;

        case CIPHER_STATE_DO:
        {
            ret = EncryptDo(ssl, out, input, sz, asyncOkay, type);

            /* Advance state */
            ssl->encrypt.state = CIPHER_STATE_END;

        #ifdef WOLFSSL_ASYNC_CRYPT
            /* If pending, then leave and return will resume below */
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                return ret;
            }
        #endif
        }
        FALL_THROUGH;

        case CIPHER_STATE_END:
        {
        #ifdef WOLFSSL_CIPHER_TEXT_CHECK
            if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null &&
                XMEMCMP(out, ssl->encrypt.sanityCheck,
                    min(sz, sizeof(ssl->encrypt.sanityCheck))) == 0) {

                WOLFSSL_MSG("Encrypt sanity check failed! Glitch?");
                WOLFSSL_ERROR_VERBOSE(ENCRYPT_ERROR);
                return ENCRYPT_ERROR;
            }
            ForceZero(ssl->encrypt.sanityCheck,
                sizeof(ssl->encrypt.sanityCheck));
        #endif

        #if defined(BUILD_AESGCM) || defined(HAVE_AESCCM)
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_aes_ccm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_aes_gcm)
            {
                /* finalize authentication cipher */
#if !defined(NO_PUBLIC_GCM_SET_IV) && \
    ((defined(HAVE_FIPS) || defined(HAVE_SELFTEST)) && \
    (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)))
                AeadIncrementExpIV(ssl);
#endif
                if (ssl->encrypt.nonce)
                    ForceZero(ssl->encrypt.nonce, AESGCM_NONCE_SZ);
            }
        #endif /* BUILD_AESGCM || HAVE_AESCCM */
        #ifdef HAVE_ARIA
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_aria_gcm)
            {
                /* finalize authentication cipher -- wc_AriaEncrypt is
                 * stateless, so the explicit IV must always advance */
                AeadIncrementExpIV(ssl);
                if (ssl->encrypt.nonce)
                    ForceZero(ssl->encrypt.nonce, AESGCM_NONCE_SZ);
            }
        #endif /* HAVE_ARIA */
        #if defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_ccm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_gcm)
            {
                /* finalize authentication cipher */
                AeadIncrementExpIV(ssl);
                if (ssl->encrypt.nonce)
                    ForceZero(ssl->encrypt.nonce, GCM_NONCE_SZ);
            }
        #endif /* WOLFSSL_SM4_GCM || WOLFSSL_SM4_CCM */
        #ifdef WOLFSSL_CHECK_MEM_ZERO
            if ((ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null) &&
                    (out != input) && (ret == 0)) {
                wc_MemZero_Add("TLS Encrypt plaintext", input, sz);
            }
        #endif
            break;
        }

        default:
            break;
    }

    /* Reset state */
    ssl->encrypt.state = CIPHER_STATE_BEGIN;

    return ret;
}


static WC_INLINE int DecryptDo(WOLFSSL* ssl, byte* plain, const byte* input,
                           word16 sz)
{
    int ret = 0;

    (void)plain;
    (void)input;
    (void)sz;

    switch (ssl->specs.bulk_cipher_algorithm)
    {
    #ifdef BUILD_ARC4
        case wolfssl_rc4:
            wc_Arc4Process(ssl->decrypt.arc4, plain, input, sz);
            break;
    #endif

    #ifdef BUILD_DES3
        case wolfssl_triple_des:
        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            ret = wolfSSL_AsyncInit(ssl, &ssl->decrypt.des3->asyncDev,
                WC_ASYNC_FLAG_CALL_AGAIN);
            if (ret != 0)
                break;
        #endif

            ret = wc_Des3_CbcDecrypt(ssl->decrypt.des3, plain, input, sz);
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                ret = wolfSSL_AsyncPush(ssl, &ssl->decrypt.des3->asyncDev);
            }
        #endif
            break;
    #endif

    #if defined(BUILD_AES) && defined(HAVE_AES_CBC)
        case wolfssl_aes:
        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            ret = wolfSSL_AsyncInit(ssl, &ssl->decrypt.aes->asyncDev,
                WC_ASYNC_FLAG_CALL_AGAIN);
            if (ret != 0)
                break;
        #endif
            ret = wc_AesCbcDecrypt(ssl->decrypt.aes, plain, input, sz);
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                ret = wolfSSL_AsyncPush(ssl, &ssl->decrypt.aes->asyncDev);
            }
        #endif
            break;
    #endif

    #if defined(BUILD_AESGCM) || defined(HAVE_AESCCM)
        case wolfssl_aes_gcm:
        case wolfssl_aes_ccm: /* GCM AEAD macros use same size as CCM */
        {
            wc_AesAuthDecryptFunc aes_auth_fn;
            int additionalSz;

        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            ret = wolfSSL_AsyncInit(ssl, &ssl->decrypt.aes->asyncDev,
                WC_ASYNC_FLAG_CALL_AGAIN);
            if (ret != 0)
                break;
        #endif

        #if defined(BUILD_AESGCM) && defined(HAVE_AESCCM)
            aes_auth_fn = (ssl->specs.bulk_cipher_algorithm == wolfssl_aes_gcm)
                            ? wc_AesGcmDecrypt : wc_AesCcmDecrypt;
        #elif defined(BUILD_AESGCM)
            aes_auth_fn = wc_AesGcmDecrypt;
        #else
            aes_auth_fn = wc_AesCcmDecrypt;
        #endif

            additionalSz = writeAeadAuthData(ssl,
                    sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size, no_type,
                    ssl->decrypt.additional, 1, NULL, PEER_ORDER);
            if (additionalSz < 0) {
                ret = additionalSz;
                break;
            }

        #if defined(WOLFSSL_DTLS) && defined(HAVE_SECURE_RENEGOTIATION)
            if (ssl->options.dtls && IsDtlsMsgSCRKeys(ssl))
                XMEMCPY(ssl->decrypt.nonce,
                        ssl->secure_renegotiation->tmp_keys.aead_dec_imp_IV,
                        AESGCM_IMP_IV_SZ);
            else
        #endif
                XMEMCPY(ssl->decrypt.nonce, ssl->keys.aead_dec_imp_IV,
                        AESGCM_IMP_IV_SZ);
            XMEMCPY(ssl->decrypt.nonce + AESGCM_IMP_IV_SZ, input,
                                                            AESGCM_EXP_IV_SZ);
        #ifdef HAVE_PK_CALLBACKS
            ret = NOT_COMPILED_IN;
            if (ssl->ctx && ssl->ctx->PerformTlsRecordProcessingCb) {
                ret = ssl->ctx->PerformTlsRecordProcessingCb(ssl, 0,
                        plain + AESGCM_EXP_IV_SZ,
                        input + AESGCM_EXP_IV_SZ,
                        sz - (word16)(AESGCM_EXP_IV_SZ) - ssl->specs.aead_mac_size,
                        ssl->decrypt.nonce, AESGCM_NONCE_SZ,
                        (byte *)(input + sz - ssl->specs.aead_mac_size),
                        ssl->specs.aead_mac_size,
                        ssl->decrypt.additional, (word32)(additionalSz));
            }

            if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN))
        #endif /* HAVE_PK_CALLBACKS */
            {
                if ((ret = aes_auth_fn(ssl->decrypt.aes,
                    plain + AESGCM_EXP_IV_SZ,
                    input + AESGCM_EXP_IV_SZ,
                    sz - (word16)(AESGCM_EXP_IV_SZ) - ssl->specs.aead_mac_size,
                    ssl->decrypt.nonce, AESGCM_NONCE_SZ,
                    input + sz - ssl->specs.aead_mac_size,
                    ssl->specs.aead_mac_size,
                    ssl->decrypt.additional, (word32)(additionalSz))) < 0) {
                #ifdef WOLFSSL_ASYNC_CRYPT
                    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                        ret = wolfSSL_AsyncPush(ssl,
                                                &ssl->decrypt.aes->asyncDev);
                    }
                #endif
                }
            }
        }
        break;
    #endif /* BUILD_AESGCM || HAVE_AESCCM */

    #ifdef HAVE_ARIA
        case wolfssl_aria_gcm:
        {
            byte *outBuf = NULL;
            int additionalSz;

            additionalSz = ret = writeAeadAuthData(ssl,
                    sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size, no_type,
                    ssl->decrypt.additional, 1, NULL, PEER_ORDER);
            if (ret < 0)
                break;
            ret = 0;

        #if defined(WOLFSSL_DTLS) && defined(HAVE_SECURE_RENEGOTIATION)
            if (ssl->options.dtls && IsDtlsMsgSCRKeys(ssl))
                XMEMCPY(ssl->decrypt.nonce,
                        ssl->secure_renegotiation->tmp_keys.aead_dec_imp_IV,
                        AESGCM_IMP_IV_SZ);
            else
        #endif
                XMEMCPY(ssl->decrypt.nonce, ssl->keys.aead_dec_imp_IV,
                        AESGCM_IMP_IV_SZ);
            XMEMCPY(ssl->decrypt.nonce + AESGCM_IMP_IV_SZ, input,
                                                            AESGCM_EXP_IV_SZ);
            outBuf = (byte*)XMALLOC(sz - AESGCM_EXP_IV_SZ, ssl->heap,
                                                       DYNAMIC_TYPE_TMP_BUFFER);
            if (outBuf == NULL) {
                ret = MEMORY_ERROR;
                break;
            }
            ret = wc_AriaDecrypt(ssl->decrypt.aria, outBuf,
                                (byte *)input + AESGCM_EXP_IV_SZ,
                                sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size,
                                ssl->decrypt.nonce, AESGCM_NONCE_SZ,
                                ssl->decrypt.additional, additionalSz,
                                (byte *)input + sz - ssl->specs.aead_mac_size,
                                ssl->specs.aead_mac_size
                                );
            if (ret != 0) {
                XFREE(outBuf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
                break;
            }
            XMEMCPY(plain + AESGCM_EXP_IV_SZ,
                    outBuf,
                    sz - AESGCM_EXP_IV_SZ - ssl->specs.aead_mac_size);
            XFREE(outBuf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
            break;
        }
    #endif /* HAVE_ARIA */

    #ifdef HAVE_CAMELLIA
        case wolfssl_camellia:
            ret = wc_CamelliaCbcDecrypt(ssl->decrypt.cam, plain, input, sz);
            break;
    #endif

    #if defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && \
        !defined(NO_CHAPOL_AEAD)
        case wolfssl_chacha:
            ret = ChachaAEADDecrypt(ssl, plain, input, sz);
            break;
    #endif

    #ifdef WOLFSSL_SM4_CBC
        case wolfssl_sm4_cbc:
        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            ret = wolfSSL_AsyncInit(ssl, &ssl->decrypt.sm4->asyncDev,
                WC_ASYNC_FLAG_CALL_AGAIN);
            if (ret != 0)
                break;
        #endif
            ret = wc_Sm4CbcDecrypt(ssl->decrypt.sm4, plain, input, sz);
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                ret = wolfSSL_AsyncPush(ssl, &ssl->decrypt.sm4->asyncDev);
            }
        #endif
            break;
    #endif

    #if defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)
        case wolfssl_sm4_gcm:
        case wolfssl_sm4_ccm: /* GCM AEAD macros use same size as CCM */
        {
            SM4_AUTH_DECRYPT_FUNC sm4_auth_fn;

        #ifdef WOLFSSL_ASYNC_CRYPT
            /* initialize event */
            ret = wolfSSL_AsyncInit(ssl, &ssl->decrypt.sm4->asyncDev,
                WC_ASYNC_FLAG_CALL_AGAIN);
            if (ret != 0)
                break;
        #endif

        #if defined(WOLFSSL_SM4_GCM) && defined(WOLFSSL_SM4_CCM)
            sm4_auth_fn = (ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_gcm)
                            ? SM4_GCM_DECRYPT_FUNC : SM4_CCM_DECRYPT_FUNC;
        #elif defined(WOLFSSL_SM4_GCM)
            sm4_auth_fn = SM4_GCM_DECRYPT_FUNC;
        #else
            sm4_auth_fn = SM4_CCM_DECRYPT_FUNC;
        #endif

            XMEMSET(ssl->decrypt.additional, 0, AEAD_AUTH_DATA_SZ);

            /* sequence number field is 64-bits */
            WriteSEQ(ssl, PEER_ORDER, ssl->decrypt.additional);

            ssl->decrypt.additional[AEAD_TYPE_OFFSET] = ssl->curRL.type;
            ssl->decrypt.additional[AEAD_VMAJ_OFFSET] = ssl->curRL.pvMajor;
            ssl->decrypt.additional[AEAD_VMIN_OFFSET] = ssl->curRL.pvMinor;

            c16toa(sz - GCM_EXP_IV_SZ - ssl->specs.aead_mac_size,
                                    ssl->decrypt.additional + AEAD_LEN_OFFSET);

        #if defined(WOLFSSL_DTLS) && defined(HAVE_SECURE_RENEGOTIATION)
            if (ssl->options.dtls && IsDtlsMsgSCRKeys(ssl))
                XMEMCPY(ssl->decrypt.nonce,
                        ssl->secure_renegotiation->tmp_keys.aead_dec_imp_IV,
                        GCM_IMP_IV_SZ);
            else
        #endif
                XMEMCPY(ssl->decrypt.nonce, ssl->keys.aead_dec_imp_IV,
                        GCM_IMP_IV_SZ);
            XMEMCPY(ssl->decrypt.nonce + GCM_IMP_IV_SZ, input, GCM_EXP_IV_SZ);
            if ((ret = sm4_auth_fn(ssl->decrypt.sm4,
                        plain + GCM_EXP_IV_SZ,
                        input + GCM_EXP_IV_SZ,
                        sz - GCM_EXP_IV_SZ - ssl->specs.aead_mac_size,
                        ssl->decrypt.nonce, GCM_NONCE_SZ,
                        input + sz - ssl->specs.aead_mac_size,
                        ssl->specs.aead_mac_size,
                        ssl->decrypt.additional, AEAD_AUTH_DATA_SZ)) < 0) {
            #ifdef WOLFSSL_ASYNC_CRYPT
                if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                    ret = wolfSSL_AsyncPush(ssl,
                                            &ssl->decrypt.sm4->asyncDev);
                }
            #endif
            }
        }
        break;
    #endif /* WOLFSSL_SM4_GCM || WOLFSSL_SM4_CCM */

    #ifdef HAVE_NULL_CIPHER
        case wolfssl_cipher_null:
            if (input != plain) {
                XMEMMOVE(plain, input, sz);
            }
            break;
    #endif

        default:
            WOLFSSL_MSG("wolfSSL Decrypt programming error");
            WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR);
            ret = DECRYPT_ERROR;
    }

#ifdef WOLFSSL_CHECK_MEM_ZERO
    if ((ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null) &&
            (ret == 0)) {
        wc_MemZero_Add("Decrypted data", plain, sz);
    }
#endif

    return ret;
}

static int DecryptTls(WOLFSSL* ssl, byte* plain, const byte* input, word16 sz)
{
    int ret = 0;

#ifdef WOLFSSL_ASYNC_CRYPT
    ret = wolfSSL_AsyncPop(ssl, &ssl->decrypt.state);
    if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
        /* check for still pending */
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
            return ret;

        ssl->error = 0; /* clear async */

        /* let failures through so CIPHER_STATE_END logic is run */
    }
    else
#endif
    {
        /* Reset state */
        ret = 0;
        ssl->decrypt.state = CIPHER_STATE_BEGIN;
    }

    switch (ssl->decrypt.state) {
        case CIPHER_STATE_BEGIN:
        {
            if (ssl->decrypt.setup == 0) {
                WOLFSSL_MSG("Decrypt ciphers not setup");
                WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR);
                return DECRYPT_ERROR;
            }

        #if defined(BUILD_AESGCM) || defined(HAVE_AESCCM) || defined(HAVE_ARIA)
            /* make sure AES GCM/CCM memory is allocated */
            /* free for these happens in FreeCiphers */
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_aes_ccm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_aes_gcm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_aria_gcm) {
                /* make sure auth iv and auth are allocated */
                if (ssl->decrypt.additional == NULL)
                    ssl->decrypt.additional = (byte*)XMALLOC(AEAD_AUTH_DATA_SZ,
                                                ssl->heap, DYNAMIC_TYPE_CIPHER);
                if (ssl->decrypt.nonce == NULL) {
                    ssl->decrypt.nonce = (byte*)XMALLOC(AESGCM_NONCE_SZ,
                                                ssl->heap, DYNAMIC_TYPE_CIPHER);
                #ifdef WOLFSSL_CHECK_MEM_ZERO
                    if (ssl->decrypt.nonce != NULL) {
                        wc_MemZero_Add("DecryptTls nonce", ssl->decrypt.nonce,
                            AESGCM_NONCE_SZ);
                    }
                #endif
                }
                if (ssl->decrypt.additional == NULL ||
                         ssl->decrypt.nonce == NULL) {
                    return MEMORY_E;
                }
            }
        #endif /* BUILD_AESGCM || HAVE_AESCCM || HAVE_ARIA */

        #if defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)
            /* make sure SM4 GCM/CCM memory is allocated */
            /* free for these happens in FreeCiphers */
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_ccm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_gcm) {
                /* make sure auth iv and auth are allocated */
                if (ssl->decrypt.additional == NULL)
                    ssl->decrypt.additional = (byte*)XMALLOC(AEAD_AUTH_DATA_SZ,
                                                ssl->heap, DYNAMIC_TYPE_CIPHER);
                if (ssl->decrypt.nonce == NULL) {
                    ssl->decrypt.nonce = (byte*)XMALLOC(GCM_NONCE_SZ,
                                                ssl->heap, DYNAMIC_TYPE_CIPHER);
                #ifdef WOLFSSL_CHECK_MEM_ZERO
                    if (ssl->decrypt.nonce != NULL) {
                        wc_MemZero_Add("DecryptTls nonce", ssl->decrypt.nonce,
                            GCM_NONCE_SZ);
                    }
                #endif
                }
                if (ssl->decrypt.additional == NULL ||
                         ssl->decrypt.nonce == NULL) {
                    return MEMORY_E;
                }
            }
        #endif /* WOLFSSL_SM4_GCM || WOLFSSL_SM4_CCM */

            /* Advance state and proceed */
            ssl->decrypt.state = CIPHER_STATE_DO;
        }
        FALL_THROUGH;
        case CIPHER_STATE_DO:
        {
        #if defined(WOLFSSL_DTLS) && defined(HAVE_SECURE_RENEGOTIATION)
            if (ssl->options.dtls && DtlsSCRKeysSet(ssl)) {
                /* For epochs >1 the current cipher parameters are located in
                 * ssl->secure_renegotiation->tmp_keys. Previous cipher
                 * parameters and for epoch 1 use ssl->keys */
                if (ssl->keys.curEpoch ==
                        ssl->secure_renegotiation->tmp_keys.dtls_epoch) {
                    if (ssl->decrypt.src != SCR) {
                        ssl->secure_renegotiation->cache_status =
                                SCR_CACHE_NEEDED;
                        if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0)
                            break;
                    }
                }
                else {
                    if (ssl->decrypt.src != KEYS) {
                        ssl->secure_renegotiation->cache_status =
                                SCR_CACHE_NULL;
                        if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0)
                            break;
                    }
                }
            }
        #endif

            ret = DecryptDo(ssl, plain, input, sz);

            /* Advance state */
            ssl->decrypt.state = CIPHER_STATE_END;

        #ifdef WOLFSSL_ASYNC_CRYPT
            /* If pending, leave and return below */
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                return ret;
            }
        #endif
        }
        FALL_THROUGH;
        case CIPHER_STATE_END:
        {
        #if defined(BUILD_AESGCM) || defined(HAVE_AESCCM) || defined(HAVE_ARIA)
            /* make sure AES GCM/CCM nonce is cleared */
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_aes_ccm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_aes_gcm) {
                if (ssl->decrypt.nonce)
                    ForceZero(ssl->decrypt.nonce, AESGCM_NONCE_SZ);

                if (ret < 0) {
                    ret = VERIFY_MAC_ERROR;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        #endif /* BUILD_AESGCM || HAVE_AESCCM || HAVE_ARIA */
        #if defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)
            /* make sure SM4 GCM/CCM nonce is cleared */
            if (ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_ccm ||
                ssl->specs.bulk_cipher_algorithm == wolfssl_sm4_gcm) {
                if (ssl->decrypt.nonce)
                    ForceZero(ssl->decrypt.nonce, GCM_NONCE_SZ);

                if (ret < 0) {
                    ret = VERIFY_MAC_ERROR;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
        #endif /* BUILD_AESGCM || HAVE_AESCCM */
            break;
        }

        default:
            break;
    }

    /* Reset state */
    ssl->decrypt.state = CIPHER_STATE_BEGIN;

    return ret;
}

#endif /* !WOLFSSL_NO_TLS12 */

/* Check conditions for a cipher to have an explicit IV.
 *
 * ssl  The SSL/TLS object.
 * returns 1 if the cipher in use has an explicit IV and 0 otherwise.
 */
static WC_INLINE int CipherHasExpIV(WOLFSSL *ssl)
{
#ifdef WOLFSSL_TLS13
    if (ssl->options.tls1_3)
        return 0;
#endif
    return (ssl->specs.cipher_type == aead) &&
            (ssl->specs.bulk_cipher_algorithm != wolfssl_chacha);
}

/* check cipher text size for sanity */
static int SanityCheckCipherText(WOLFSSL* ssl, word32 encryptSz)
{
    word32 minLength = MacSize(ssl);

#ifndef WOLFSSL_AEAD_ONLY
    if (ssl->specs.cipher_type == block) {
#ifdef HAVE_ENCRYPT_THEN_MAC
        if (ssl->options.startedETMRead) {
            if (encryptSz < minLength ||
                (encryptSz - MacSize(ssl)) % ssl->specs.block_size) {
                WOLFSSL_MSG("Block ciphertext not block size");
                WOLFSSL_ERROR_VERBOSE(SANITY_CIPHER_E);
                return SANITY_CIPHER_E;
            }
        }
        else
#endif
        if (encryptSz % ssl->specs.block_size) {
            WOLFSSL_MSG("Block ciphertext not block size");
            WOLFSSL_ERROR_VERBOSE(SANITY_CIPHER_E);
            return SANITY_CIPHER_E;
        }

        minLength++;  /* pad byte */

        if (ssl->specs.block_size > minLength)
            minLength = ssl->specs.block_size;

        if (ssl->options.tls1_1)
            minLength += ssl->specs.block_size;  /* explicit IV */
    }
    else
#endif
    if (ssl->specs.cipher_type == aead) {
        minLength = ssl->specs.aead_mac_size;    /* authTag size */
        if (CipherHasExpIV(ssl))
            minLength += AESGCM_EXP_IV_SZ;       /* explicit IV  */
    }

    if (encryptSz < minLength) {
        WOLFSSL_MSG("Ciphertext not minimum size");
        WOLFSSL_ERROR_VERBOSE(SANITY_CIPHER_E);
        return SANITY_CIPHER_E;
    }

    return 0;
}


#ifndef WOLFSSL_AEAD_ONLY
#ifdef WOLFSSL_OLD_TIMINGPADVERIFY
#define COMPRESS_LOWER      64
#define COMPRESS_UPPER      55
#define COMPRESS_CONSTANT   13

#ifndef NO_OLD_TLS

static WC_INLINE void Md5Rounds(int rounds, const byte* data, int sz)
{
    wc_Md5 md5;
    int i;

    wc_InitMd5(&md5);   /* no error check on purpose, dummy round */

    for (i = 0; i < rounds; i++)
        wc_Md5Update(&md5, data, sz);
    wc_Md5Free(&md5); /* in case needed to release resources */
}



/* do a dummy sha round */
static WC_INLINE void ShaRounds(int rounds, const byte* data, int sz)
{
    wc_Sha sha;
    int i;

    wc_InitSha(&sha);  /* no error check on purpose, dummy round */

    for (i = 0; i < rounds; i++)
        wc_ShaUpdate(&sha, data, sz);
    wc_ShaFree(&sha); /* in case needed to release resources */
}
#endif


#ifndef NO_SHA256

static WC_INLINE void Sha256Rounds(int rounds, const byte* data, int sz)
{
    wc_Sha256 sha256;
    int i;

    wc_InitSha256(&sha256);  /* no error check on purpose, dummy round */

    for (i = 0; i < rounds; i++) {
        wc_Sha256Update(&sha256, data, sz);
        /* no error check on purpose, dummy round */
    }
    wc_Sha256Free(&sha256); /* in case needed to release resources */
}

#endif


#ifdef WOLFSSL_SHA384

static WC_INLINE void Sha384Rounds(int rounds, const byte* data, int sz)
{
    wc_Sha384 sha384;
    int i;

    wc_InitSha384(&sha384);  /* no error check on purpose, dummy round */

    for (i = 0; i < rounds; i++) {
        wc_Sha384Update(&sha384, data, sz);
        /* no error check on purpose, dummy round */
    }
    wc_Sha384Free(&sha384); /* in case needed to release resources */
}

#endif


#ifdef WOLFSSL_SHA512
static WC_INLINE void Sha512Rounds(int rounds, const byte* data, int sz)
{
    wc_Sha512 sha512;
    int i;

    wc_InitSha512(&sha512);  /* no error check on purpose, dummy round */

    for (i = 0; i < rounds; i++) {
        wc_Sha512Update(&sha512, data, sz);
        /* no error check on purpose, dummy round */
    }
    wc_Sha512Free(&sha512); /* in case needed to release resources */
}

#endif


#ifdef WOLFSSL_RIPEMD

static WC_INLINE void RmdRounds(int rounds, const byte* data, int sz)
{
    RipeMd ripemd;
    int i;

    wc_InitRipeMd(&ripemd);

    for (i = 0; i < rounds; i++)
        wc_RipeMdUpdate(&ripemd, data, sz);
}

#endif


/* Do dummy rounds */
static WC_INLINE void DoRounds(int type, int rounds, const byte* data, int sz)
{
    (void)rounds;
    (void)data;
    (void)sz;

    switch (type) {
        case no_mac :
            break;

#ifndef NO_OLD_TLS
#ifndef NO_MD5
        case md5_mac :
            Md5Rounds(rounds, data, sz);
            break;
#endif

#ifndef NO_SHA
        case sha_mac :
            ShaRounds(rounds, data, sz);
            break;
#endif
#endif

#ifndef NO_SHA256
        case sha256_mac :
            Sha256Rounds(rounds, data, sz);
            break;
#endif

#ifdef WOLFSSL_SHA384
        case sha384_mac :
            Sha384Rounds(rounds, data, sz);
            break;
#endif

#ifdef WOLFSSL_SHA512
        case sha512_mac :
            Sha512Rounds(rounds, data, sz);
            break;
#endif

#ifdef WOLFSSL_RIPEMD
        case rmd_mac :
            RmdRounds(rounds, data, sz);
            break;
#endif

        default:
            WOLFSSL_MSG("Bad round type");
            break;
    }
}


/* do number of compression rounds on dummy data */
static WC_INLINE void CompressRounds(WOLFSSL* ssl, int rounds, const byte* dummy)
{
    if (rounds)
        DoRounds(ssl->specs.mac_algorithm, rounds, dummy, COMPRESS_LOWER);
}


/* check all length bytes for the pad value, return 0 on success */
static int PadCheck(const byte* a, byte pad, int length)
{
    int i;
    int compareSum = 0;

    for (i = 0; i < length; i++) {
        compareSum |= a[i] ^ pad;
    }

    return compareSum;
}


/* get compression extra rounds */
static WC_INLINE int GetRounds(int pLen, int padLen, int t)
{
    int  roundL1 = 1;  /* round up flags */
    int  roundL2 = 1;

    int L1 = COMPRESS_CONSTANT + pLen - t;
    int L2 = COMPRESS_CONSTANT + pLen - padLen - 1 - t;

    L1 -= COMPRESS_UPPER;
    L2 -= COMPRESS_UPPER;

    if ( (L1 % COMPRESS_LOWER) == 0)
        roundL1 = 0;
    if ( (L2 % COMPRESS_LOWER) == 0)
        roundL2 = 0;

    L1 /= COMPRESS_LOWER;
    L2 /= COMPRESS_LOWER;

    L1 += roundL1;
    L2 += roundL2;

    return L1 - L2;
}


/* timing resistant pad/verify check, return 0 on success */
 int TimingPadVerify(WOLFSSL* ssl, const byte* input, int padLen, int t,
                     int pLen, int content)
{
    byte verify[WC_MAX_DIGEST_SIZE];
    byte dmy[sizeof(WOLFSSL) >= MAX_PAD_SIZE ? 1 : MAX_PAD_SIZE] = {0};
    byte* dummy = sizeof(dmy) < MAX_PAD_SIZE ? (byte*) ssl : dmy;
    int  ret = 0;

    (void)dmy;

    if ( (t + padLen + 1) > pLen) {
        WOLFSSL_MSG("Plain Len not long enough for pad/mac");
        PadCheck(dummy, (byte)padLen, MAX_PAD_SIZE);
        /* still compare */
        ssl->hmac(ssl, verify, input, pLen - t, -1, content, 1, PEER_ORDER);
        ConstantCompare(verify, input + pLen - t, t);
        WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
        return VERIFY_MAC_ERROR;
    }

    if (PadCheck(input + pLen - (padLen + 1), (byte)padLen, padLen + 1) != 0) {
        WOLFSSL_MSG("PadCheck failed");
        PadCheck(dummy, (byte)padLen, MAX_PAD_SIZE - padLen - 1);
        /* still compare */
        ssl->hmac(ssl, verify, input, pLen - t, -1, content, 1, PEER_ORDER);
        ConstantCompare(verify, input + pLen - t, t);
        WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
        return VERIFY_MAC_ERROR;
    }

    PadCheck(dummy, (byte)padLen, MAX_PAD_SIZE - padLen - 1);
    ret = ssl->hmac(ssl, verify, input, pLen - padLen - 1 - t, -1, content,
                                                                 1, PEER_ORDER);

    CompressRounds(ssl, GetRounds(pLen, padLen, t), dummy);

    if (ConstantCompare(verify, input + (pLen - padLen - 1 - t), t) != 0) {
        WOLFSSL_MSG("Verify MAC compare failed");
        WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
        return VERIFY_MAC_ERROR;
    }

    /* treat any failure as verify MAC error */
    if (ret != 0) {
        ret = VERIFY_MAC_ERROR;
        WOLFSSL_ERROR_VERBOSE(ret);
    }

    return ret;
}
#else

#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
/* check all length bytes for the pad value, return 0 on success */
static int PadCheck(const byte* a, byte pad, int length)
{
    int i;
    int compareSum = 0;

    for (i = 0; i < length; i++) {
        compareSum |= a[i] ^ pad;
    }

    return compareSum;
}


/* Mask the padding bytes with the expected values.
 * Constant time implementation - does maximum pad size possible.
 *
 * data   Message data.
 * sz     Size of the message including MAC and padding and padding length.
 * macSz  Size of the MAC.
 * returns 0 on success, otherwise failure.
 */
static byte MaskPadding(const byte* data, int sz, int macSz)
{
    int i;
    int checkSz = sz - 1;
    byte paddingSz = data[sz - 1];
    byte good = ctMaskGT(paddingSz, sz - 1 - macSz);

    if (checkSz > TLS_MAX_PAD_SZ)
        checkSz = TLS_MAX_PAD_SZ;

    for (i = 0; i < checkSz; i++) {
        volatile byte mask = ctMaskLTE(i, paddingSz);
        good |= mask & (data[sz - 1 - i] ^ paddingSz);
    }

    return good;
}

/* Mask the MAC in the message with the MAC calculated.
 * Constant time implementation - starts looking for MAC where maximum padding
 * size has it.
 *
 * data    Message data.
 * sz      Size of the message including MAC and padding and padding length.
 * macSz   Size of the MAC data.
 * expMac  Expected MAC value.
 * returns 0 on success, otherwise failure.
 */
static byte MaskMac(const byte* data, int sz, int macSz, byte* expMac)
{
    int i, j;
    int r = 0;
    unsigned char mac[WC_MAX_DIGEST_SIZE];
    int scanStart = sz - 1 - TLS_MAX_PAD_SZ - macSz;
    volatile int macEnd = sz - 1 - data[sz - 1];
    int macStart = macEnd - macSz;
    volatile int maskScanStart;
    volatile int maskMacStart;
    unsigned char good = 0;

    maskScanStart = ctMaskIntGTE(scanStart, 0);
    maskMacStart = ctMaskIntGTE(macStart, 0);
    scanStart &= maskScanStart;
    macStart &= maskMacStart;

    /* Div on Intel has different speeds depending on value.
     * Use a bitwise AND or mod a specific value (converted to mul). */
    if ((macSz & (macSz - 1)) == 0) {
        r = macSz - scanStart;
        r += macStart;
        r &= (macSz - 1);
    }
#ifndef NO_SHA
    else if (macSz == WC_SHA_DIGEST_SIZE) {
        r = macSz - scanStart;
        r += macStart;
        r %= WC_SHA_DIGEST_SIZE;
    }
#endif
#ifdef WOLFSSL_SHA384
    else if (macSz == WC_SHA384_DIGEST_SIZE) {
        r = macSz - scanStart;
        r += macStart;
        r %= WC_SHA384_DIGEST_SIZE;
    }
#endif

    XMEMSET(mac, 0, (size_t)(macSz));
    for (i = scanStart; i < sz; i += macSz) {
        for (j = 0; j < macSz && j + i < sz; j++) {
            unsigned char started = ctMaskGTE(i + j, macStart);
            unsigned char notEnded = ctMaskLT(i + j, macEnd);
            mac[j] |= started & notEnded & data[i + j];
        }
    }

    if ((macSz & (macSz - 1)) == 0) {
        for (i = 0; i < macSz; i++)
            good |= expMac[i] ^ mac[(i + r) & (macSz - 1)];
    }
#ifndef NO_SHA
    else if (macSz == WC_SHA_DIGEST_SIZE) {
        for (i = 0; i < macSz; i++)
            good |= expMac[i] ^ mac[(i + r) % WC_SHA_DIGEST_SIZE];
    }
#endif
#ifdef WOLFSSL_SHA384
    else if (macSz == WC_SHA384_DIGEST_SIZE) {
        for (i = 0; i < macSz; i++)
            good |= expMac[i] ^ mac[(i + r) % WC_SHA384_DIGEST_SIZE];
    }
#endif

    return good;
}

/* timing resistant pad/verify check, return 0 on success */
int TimingPadVerify(WOLFSSL* ssl, const byte* input, int padLen, int macSz,
                    int pLen, int content)
{
    byte verify[WC_MAX_DIGEST_SIZE];
    byte good;
    int  ret = 0;

    XMEMSET(verify, 0, WC_MAX_DIGEST_SIZE);
    good = MaskPadding(input, pLen, macSz);
    /* 4th argument has potential to underflow, ssl->hmac function should
     * either increment the size by (macSz + padLen + 1) before use or check on
     * the size to make sure is valid. */
    ret = ssl->hmac(ssl, verify, input, (word32)(pLen - macSz - padLen - 1), padLen,
                                                        content, 1, PEER_ORDER);
    good |= MaskMac(input, pLen, ssl->specs.hash_size, verify);

    /* Non-zero on failure. */
    good = (byte)~(word32)good;
    good &= good >> 4;
    good &= good >> 2;
    good &= good >> 1;
    /* Make ret negative on masking failure. */
    ret -= 1 - good;

    /* Treat any failure as verify MAC error. */
    if (ret != 0) {
        ret = VERIFY_MAC_ERROR;
        WOLFSSL_ERROR_VERBOSE(ret);
    }

    return ret;
}
#endif /* !WOLFSSL_NO_TLS12 && !WOLFSSL_AEAD_ONLY */
#endif /* WOLFSSL_OLD_TIMINGPADVERIFY */
#endif /* WOLFSSL_AEAD_ONLY */

int DoApplicationData(WOLFSSL* ssl, byte* input, word32* inOutIdx, int sniff)
{
    word32 msgSz   = ssl->curSize;
    word32 idx     = *inOutIdx;
    int    dataSz;
    byte*  rawData = input + idx;  /* keep current  for hmac */
#ifdef HAVE_LIBZ
    byte   decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
#endif
#ifdef WOLFSSL_EARLY_DATA
    int    isEarlyData = ssl->options.tls1_3 &&
                         ssl->options.handShakeDone == 0 &&
                         ssl->options.side == WOLFSSL_SERVER_END;
    int    acceptEarlyData = ssl->earlyData != no_early_data &&
                         ssl->options.clientState == CLIENT_HELLO_COMPLETE;
#endif

#if defined(WOLFSSL_EARLY_DATA) && defined(WOLFSSL_DTLS13)
    if (ssl->options.tls1_3 && ssl->options.dtls)
        isEarlyData = isEarlyData && w64Equal(ssl->keys.curEpoch64,
                w64From32(0x0, DTLS13_EPOCH_EARLYDATA));
#endif
#ifdef WOLFSSL_DTLS13
    /* Application data should never appear in epoch 0 or 2 */
    if (ssl->options.tls1_3 && ssl->options.dtls &&
        (w64Equal(ssl->keys.curEpoch64, w64From32(0x0, DTLS13_EPOCH_HANDSHAKE))
                || w64Equal(ssl->keys.curEpoch64, w64From32(0x0, 0x0))))
    {
        WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
        return SANITY_MSG_E;
    }
#endif

#ifdef WOLFSSL_EARLY_DATA
    if (isEarlyData && acceptEarlyData) {
        WOLFSSL_MSG("Processing EarlyData");
    }
    else if (isEarlyData && !acceptEarlyData) {
        WOLFSSL_MSG("Ignoring EarlyData!");
        *inOutIdx += ssl->curSize;
        if (*inOutIdx > ssl->buffers.inputBuffer.length)
            return BUFFER_E;
#ifdef WOLFSSL_DTLS13
        /* Receiving app data from the traffic epoch before the handshake is
         * done means that there was a disruption. */
        if (ssl->options.dtls && !w64Equal(ssl->keys.curEpoch64,
                                        w64From32(0x0, DTLS13_EPOCH_EARLYDATA)))
            ssl->dtls13Rtx.sendAcks = 1;
#endif
        return 0;
    }
    else
#endif
#ifdef WOLFSSL_DTLS
    if (ssl->options.handShakeDone == 0 && ssl->options.dtls) {
        WOLFSSL_MSG("Dropping app data received before handshake complete");
        *inOutIdx += ssl->curSize;
        if (*inOutIdx > ssl->buffers.inputBuffer.length)
            return BUFFER_E;
#ifdef WOLFSSL_DTLS13
        /* Receiving app data from the traffic epoch before the handshake is
         * done means that there was a disruption. */
        if (ssl->options.tls1_3 && !w64Equal(ssl->keys.curEpoch64,
                                        w64From32(0x0, DTLS13_EPOCH_EARLYDATA)))
            ssl->dtls13Rtx.sendAcks = 1;
#endif
        return 0;
    }
    else
#endif
    if (ssl->options.handShakeDone == 0) {
        WOLFSSL_MSG("Received App data before a handshake completed");
        if (sniff == NO_SNIFF) {
            SendAlert(ssl, alert_fatal, unexpected_message);
        }
        WOLFSSL_ERROR_VERBOSE(OUT_OF_ORDER_E);
        return OUT_OF_ORDER_E;
    }


#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)
    /* Check if we want to invalidate old epochs. If
     * ssl->dtls13InvalidateBefore is set then we want to mark all old
     * epochs as encrypt only. This is done when we detect too many failed
     * decryptions. We do this here to confirm that the peer has updated its
     * keys and we can stop using the old keys. */
    if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
        if (!w64IsZero(ssl->dtls13InvalidateBefore) &&
                w64Equal(ssl->keys.curEpoch64, ssl->dtls13InvalidateBefore)) {
            Dtls13SetOlderEpochSide(ssl, ssl->dtls13InvalidateBefore,
                                    ENCRYPT_SIDE_ONLY);
            w64Zero(&ssl->dtls13InvalidateBefore);
        }
    }
#endif

    dataSz = (int)(msgSz - ssl->keys.padSz);
    if (dataSz < 0) {
        WOLFSSL_MSG("App data buffer error, malicious input?");
        if (sniff == NO_SNIFF) {
            SendAlert(ssl, alert_fatal, unexpected_message);
        }
        WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR);
        return BUFFER_ERROR;
    }
#ifdef WOLFSSL_EARLY_DATA
    if (ssl->options.side == WOLFSSL_SERVER_END &&
            ssl->earlyData > early_data_ext) {
        if (ssl->earlyDataSz + dataSz > ssl->options.maxEarlyDataSz) {
            if (sniff == NO_SNIFF) {
                SendAlert(ssl, alert_fatal, unexpected_message);
            }
            return WOLFSSL_FATAL_ERROR;
        }
        ssl->earlyDataSz += dataSz;
    }
#endif

    /* read data */
    if (dataSz) {
        int rawSz = dataSz;       /* keep raw size for idx adjustment */

#ifdef HAVE_LIBZ
        if (ssl->options.usingCompression) {
            dataSz = myDeCompress(ssl, rawData, dataSz, decomp, sizeof(decomp));
            if (dataSz < 0) return dataSz;
        }
#endif
        idx += (word32)rawSz;

        ssl->buffers.clearOutputBuffer.buffer = rawData;
        ssl->buffers.clearOutputBuffer.length = (unsigned int)dataSz;
    }

    idx += ssl->keys.padSz;

#ifdef HAVE_LIBZ
    /* decompress could be bigger, overwrite after verify */
    if (ssl->options.usingCompression)
        XMEMMOVE(rawData, decomp, dataSz);
#endif

    *inOutIdx = idx;
#ifdef WOLFSSL_DTLS13
    if (ssl->options.connectState == WAIT_FINISHED_ACK) {
        /* DTLS 1.3 is waiting for an ACK but we can still return app data. */
        return APP_DATA_READY;
    }
#endif
#ifdef HAVE_SECURE_RENEGOTIATION
    if (IsSCR(ssl)) {
        /* If we are in a secure renegotiation then APP DATA is treated
         * differently */
        return APP_DATA_READY;
    }
#endif
    return 0;
}

const char* AlertTypeToString(int type)
{
    switch (type) {
        case close_notify:
            {
                static const char close_notify_str[] =
                    "close_notify";
                return close_notify_str;
            }

        case unexpected_message:
            {
                static const char unexpected_message_str[] =
                    "unexpected_message";
                return unexpected_message_str;
            }

        case bad_record_mac:
            {
                static const char bad_record_mac_str[] =
                    "bad_record_mac";
                return bad_record_mac_str;
            }

        case record_overflow:
            {
                static const char record_overflow_str[] =
                    "record_overflow";
                return record_overflow_str;
            }

        case decompression_failure:
            {
                static const char decompression_failure_str[] =
                    "decompression_failure";
                return decompression_failure_str;
            }

        case handshake_failure:
            {
                static const char handshake_failure_str[] =
                    "handshake_failure";
                return handshake_failure_str;
            }

        case no_certificate:
            {
                static const char no_certificate_str[] =
                    "no_certificate";
                return no_certificate_str;
            }

        case bad_certificate:
            {
                static const char bad_certificate_str[] =
                    "bad_certificate";
                return bad_certificate_str;
            }

        case unsupported_certificate:
            {
                static const char unsupported_certificate_str[] =
                    "unsupported_certificate";
                return unsupported_certificate_str;
            }

        case certificate_revoked:
            {
                static const char certificate_revoked_str[] =
                    "certificate_revoked";
                return certificate_revoked_str;
            }

        case certificate_expired:
            {
                static const char certificate_expired_str[] =
                    "certificate_expired";
                return certificate_expired_str;
            }

        case certificate_unknown:
            {
                static const char certificate_unknown_str[] =
                    "certificate_unknown";
                return certificate_unknown_str;
            }

        case illegal_parameter:
            {
                static const char illegal_parameter_str[] =
                    "illegal_parameter";
                return illegal_parameter_str;
            }

        case unknown_ca:
            {
                static const char unknown_ca_str[] =
                    "unknown_ca";
                return unknown_ca_str;
            }

        case access_denied:
            {
                static const char access_denied_str[] =
                    "access_denied";
                return access_denied_str;
            }

        case decode_error:
            {
                static const char decode_error_str[] =
                    "decode_error";
                return decode_error_str;
            }

        case decrypt_error:
            {
                static const char decrypt_error_str[] =
                    "decrypt_error";
                return decrypt_error_str;
            }

        case wolfssl_alert_protocol_version:
            {
                static const char protocol_version_str[] =
                    "protocol_version";
                return protocol_version_str;
            }
        case insufficient_security:
            {
                static const char insufficient_security_str[] =
                    "insufficient_security";
                return insufficient_security_str;
            }

        case internal_error:
            {
                static const char internal_error_str[] =
                    "internal_error";
                return internal_error_str;
            }

        case inappropriate_fallback:
            {
                static const char inappropriate_fallback_str[] =
                    "inappropriate_fallback";
                return inappropriate_fallback_str;
            }

        case user_canceled:
            {
                static const char user_canceled_str[] =
                    "user_canceled";
                return user_canceled_str;
            }

        case no_renegotiation:
            {
                static const char no_renegotiation_str[] =
                    "no_renegotiation";
                return no_renegotiation_str;
            }

        case missing_extension:
            {
                static const char missing_extension_str[] =
                    "missing_extension";
                return missing_extension_str;
            }

        case unsupported_extension:
            {
                static const char unsupported_extension_str[] =
                    "unsupported_extension";
                return unsupported_extension_str;
            }

        case unrecognized_name:
            {
                static const char unrecognized_name_str[] =
                    "unrecognized_name";
                return unrecognized_name_str;
            }

        case bad_certificate_status_response:
            {
                static const char bad_certificate_status_response_str[] =
                    "bad_certificate_status_response";
                return bad_certificate_status_response_str;
            }

        case unknown_psk_identity:
            {
                static const char unknown_psk_identity_str[] =
                    "unknown_psk_identity";
                return unknown_psk_identity_str;
            }

        case certificate_required:
            {
                static const char certificate_required_str[] =
                    "certificate_required";
                return certificate_required_str;
            }

        case no_application_protocol:
            {
                static const char no_application_protocol_str[] =
                    "no_application_protocol";
                return no_application_protocol_str;
            }

        default:
            WOLFSSL_MSG("Unknown Alert");
            return NULL;
    }
}

static void LogAlert(int type)
{
#ifdef DEBUG_WOLFSSL
    const char* typeStr;

    typeStr = AlertTypeToString(type);
    if (typeStr != NULL) {
        char buff[60];
        if (XSNPRINTF(buff, sizeof(buff), "Alert type: %s", typeStr)
            >= (int)sizeof(buff))
        {
            buff[sizeof(buff) - 1] = 0;
        }
        WOLFSSL_MSG(buff);
    }
#else
    (void)type;
#endif /* DEBUG_WOLFSSL */
}

/* process alert, return level */
static int DoAlert(WOLFSSL* ssl, byte* input, word32* inOutIdx, int* type)
{
    byte level;
    byte code;
    word32 dataSz = (word32)ssl->curSize;

#if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
    if (ssl->hsInfoOn)
        AddPacketName(ssl, "Alert");
    if (ssl->toInfoOn) {
        /* add record header back on to info + alert bytes level/code */
        int ret = AddPacketInfo(ssl, "Alert", alert, input + *inOutIdx,
                           ALERT_SIZE, READ_PROTO, RECORD_HEADER_SZ, ssl->heap);
        if (ret != 0)
            return ret;
        #ifdef WOLFSSL_CALLBACKS
        AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo);
        #endif
    }
#endif

    if (IsEncryptionOn(ssl, 0))
        dataSz -= ssl->keys.padSz;

    /* make sure can read the message */
    if (dataSz != ALERT_SIZE) {
#ifdef WOLFSSL_EXTRA_ALERTS
        SendAlert(ssl, alert_fatal, unexpected_message);
#endif
        return BUFFER_E;
    }

    level = input[(*inOutIdx)++];
    code  = input[(*inOutIdx)++];
    *type = code;
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
    /* Don't process alert when TLS 1.3 and encrypting but plaintext alert. */
    if (!IsAtLeastTLSv1_3(ssl->version) || !IsEncryptionOn(ssl, 0) ||
                                                       ssl->keys.decryptedCur)
#endif
    {
        ssl->alert_history.last_rx.code = code;
        ssl->alert_history.last_rx.level = level;
        if (level == alert_fatal) {
            ssl->options.isClosed = 1;  /* Don't send close_notify */
        }
        /* RFC 8446 Section 6.2: In TLS 1.3, all error alerts are implicitly
         * fatal regardless of the AlertLevel byte. */
        if (IsAtLeastTLSv1_3(ssl->version) &&
                code != close_notify && code != user_canceled) {
            ssl->options.isClosed = 1;
        }
    }

    if (++ssl->options.alertCount >= WOLFSSL_ALERT_COUNT_MAX) {
        WOLFSSL_MSG("Alert count exceeded");
#ifdef WOLFSSL_EXTRA_ALERTS
        if (level != alert_warning || code != close_notify)
            SendAlert(ssl, alert_fatal, unexpected_message);
#endif
        WOLFSSL_ERROR_VERBOSE(ALERT_COUNT_E);
        return ALERT_COUNT_E;
    }

    LogAlert(*type);
    if (IsAtLeastTLSv1_3(ssl->version) && IsEncryptionOn(ssl, 0) &&
                                                      !ssl->keys.decryptedCur)
    {
#ifdef WOLFSSL_TLS13_IGNORE_PT_ALERT_ON_ENC
        /* Ignore alert if TLS 1.3 and encrypting but was plaintext alert. */
        *type = invalid_alert;
        level = alert_none;

#else
        /* Unexpected message when encryption is on and alert not encrypted. */
        SendAlert(ssl, alert_fatal, unexpected_message);
        WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
        return PARSE_ERROR;
#endif
    }
    else {
        if (*type == close_notify) {
            ssl->options.closeNotify = 1;
        }
        else {
            /*
             * A close_notify alert doesn't mean there's been an error, so we
             * only add other types of alerts to the error queue
             */
            WOLFSSL_ERROR(*type);
        }
    }
    if (IsEncryptionOn(ssl, 0))
        *inOutIdx += ssl->keys.padSz;

    return level;
}

static int GetInputData(WOLFSSL *ssl, word32 size)
{
    int inSz;
    int maxLength;
    int usedLength;
    int dtlsExtra = 0;

    if (ssl->options.disableRead)
        return WC_NO_ERR_TRACE(WANT_READ);

    /* check max input length */
    usedLength = (int)(ssl->buffers.inputBuffer.length -
                       ssl->buffers.inputBuffer.idx);
    maxLength  = (int)(ssl->buffers.inputBuffer.bufferSize -
                       (word32)usedLength);

#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls && IsDtlsNotSctpMode(ssl)) {
        /* Add DTLS_MTU_ADDITIONAL_READ_BUFFER bytes so that we can operate with
         * slight difference in set MTU size on each peer */
#ifdef WOLFSSL_DTLS_MTU
        inSz = (word32)ssl->dtlsMtuSz + DTLS_MTU_ADDITIONAL_READ_BUFFER;
#else
        inSz = MAX_MTU + DTLS_MTU_ADDITIONAL_READ_BUFFER;
#endif
        if (size < (word32)inSz)
            dtlsExtra = (int)(inSz - size);
    }
    else
#endif
    {
        /* check that no lengths or size values are negative */
        if (usedLength < 0 || maxLength < 0) {
            return BUFFER_ERROR;
        }

        /* Return if we have enough data already in the buffer */
        if (size <= (word32)usedLength) {
            return 0;
        }

        inSz = (int)(size - (word32)usedLength); /* from last partial read */
    }

    if (inSz > maxLength) {
        if (GrowInputBuffer(ssl, (int)(size + (word32)dtlsExtra), usedLength) < 0)
            return MEMORY_E;
    }

    /* Put buffer data at start if not there */
    if (usedLength > 0 && ssl->buffers.inputBuffer.idx != 0)
        XMEMMOVE(ssl->buffers.inputBuffer.buffer,
                ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx,
                (size_t)(usedLength));

    /* remove processed data */
    ssl->buffers.inputBuffer.idx    = 0;
    ssl->buffers.inputBuffer.length = (word32)usedLength;

    /* read data from network */
    do {
        int in = wolfSSLReceive(ssl,
                     ssl->buffers.inputBuffer.buffer +
                     ssl->buffers.inputBuffer.length,
                     (word32)inSz);
        if (in == WC_NO_ERR_TRACE(WANT_READ))
            return WC_NO_ERR_TRACE(WANT_READ);

        if (in < 0) {
            WOLFSSL_ERROR_VERBOSE(SOCKET_ERROR_E);
            return SOCKET_ERROR_E;
        }

        if (in > inSz) {
            WOLFSSL_ERROR_VERBOSE(RECV_OVERFLOW_E);
            return RECV_OVERFLOW_E;
        }

        if ((word32)in < size) {
            if (!RecordsCanSpanReads(ssl)) {
                WOLFSSL_MSG("DTLS: Received partial record, ignoring");
#ifdef WOLFSSL_DTLS_DROP_STATS
                ssl->replayDropCount++;
#endif /* WOLFSSL_DTLS_DROP_STATS */
                continue;
            }
        }

        ssl->buffers.inputBuffer.length += (word32)in;
        inSz -= in;
    } while (ssl->buffers.inputBuffer.length < size);

#ifdef WOLFSSL_DEBUG_TLS
    if (ssl->buffers.inputBuffer.idx == 0) {
        WOLFSSL_MSG("Data received");
        WOLFSSL_BUFFER(ssl->buffers.inputBuffer.buffer,
                       ssl->buffers.inputBuffer.length);
    }
#endif

    return 0;
}

#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
static WC_INLINE int VerifyMacEnc(WOLFSSL* ssl, const byte* input, word32 msgSz,
                                  int content)
{
    int    ret;
#ifdef HAVE_TRUNCATED_HMAC
    word32 digestSz = ssl->truncated_hmac ? (byte)TRUNCATED_HMAC_SZ
                                          : ssl->specs.hash_size;
#else
    word32 digestSz = ssl->specs.hash_size;
#endif
    byte   verify[WC_MAX_DIGEST_SIZE];

    WOLFSSL_MSG("Verify MAC of Encrypted Data");

    if (msgSz < digestSz) {
        WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
        return VERIFY_MAC_ERROR;
    }
    XMEMSET(verify, 0, WC_MAX_DIGEST_SIZE);

    ret  = ssl->hmac(ssl, verify, input, msgSz - digestSz, -1, content, 1, PEER_ORDER);
    ret |= ConstantCompare(verify, input + msgSz - digestSz, (int)digestSz);
    if (ret != 0) {
        WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
        return VERIFY_MAC_ERROR;
    }

    return 0;
}
#endif

static WC_INLINE int VerifyMac(WOLFSSL* ssl, const byte* input, word32 msgSz,
                            int content, word32* padSz)
{
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
    int    ret;
    word32 pad     = 0;
    word32 padByte = 0;
    word32 digestSz = MacSize(ssl);
    byte   verify[WC_MAX_DIGEST_SIZE];

    XMEMSET(verify, 0, WC_MAX_DIGEST_SIZE);

    if (ssl->specs.cipher_type == block) {
        pad = input[msgSz - 1];
        padByte = 1;

        if (ssl->options.tls) {
#if !defined(NO_CERTS) && defined(HAVE_PK_CALLBACKS)
            ret = PROTOCOLCB_UNAVAILABLE;
            if(ssl->ctx->VerifyMacCb) {
                void* ctx = wolfSSL_GetVerifyMacCtx(ssl);
                ret = ssl->ctx->VerifyMacCb(ssl, input,
                                            msgSz - digestSz - pad - 1,
                                            digestSz, (word32)content, ctx);
                if (ret != 0 &&
                    ret != WC_NO_ERR_TRACE(PROTOCOLCB_UNAVAILABLE)) {
                    return ret;
                }
            }
            if (!ssl->ctx->VerifyMacCb ||
                ret == WC_NO_ERR_TRACE(PROTOCOLCB_UNAVAILABLE))
#endif
            ret = TimingPadVerify(ssl, input, (int)pad, (int)digestSz,
                                  (int)msgSz, content);
            if (ret != 0)
                return ret;
        }
        else {  /* sslv3, some implementations have bad padding, but don't
                 * allow bad read */
            int  badPadLen = 0;
            byte dmy[sizeof(WOLFSSL) >= MAX_PAD_SIZE ? 1 : MAX_PAD_SIZE];
            byte* dummy = sizeof(dmy) < MAX_PAD_SIZE ? (byte*) ssl : dmy;
            XMEMSET(dmy, 0, sizeof(dmy));

            if (pad > (msgSz - digestSz - 1)) {
                WOLFSSL_MSG("Plain Len not long enough for pad/mac");
                pad       = 0;  /* no bad read */
                badPadLen = 1;
            }
            (void)PadCheck(dummy, (byte)pad, MAX_PAD_SIZE);  /* timing only */
            ret = ssl->hmac(ssl, verify, input, msgSz - digestSz - pad - 1,
                            (int)pad, content, 1, PEER_ORDER);
            if (ConstantCompare(verify, input + msgSz - digestSz - pad - 1,
                                (int)digestSz) != 0) {
                WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
                return VERIFY_MAC_ERROR;
            }
            if (ret != 0 || badPadLen) {
                WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
                return VERIFY_MAC_ERROR;
            }
        }
    }
    else if (ssl->specs.cipher_type == stream) {
        ret = ssl->hmac(ssl, verify, input, msgSz - digestSz, -1, content, 1,
                        PEER_ORDER);
        if (ConstantCompare(verify, input + msgSz - digestSz, (int)digestSz) != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }
    }
#endif /* !WOLFSSL_NO_TLS12 && !WOLFSSL_AEAD_ONLY */

    if (ssl->specs.cipher_type == aead) {
        *padSz = ssl->specs.aead_mac_size;
    }
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_AEAD_ONLY)
    else {
        *padSz = pad + padByte;
    }
#endif /* !WOLFSSL_NO_TLS12 && !WOLFSSL_AEAD_ONLY */

    (void)input;
    (void)msgSz;
    (void)content;

    return 0;
}

#ifdef WOLFSSL_DTLS
static int HandleDTLSDecryptFailed(WOLFSSL* ssl)
{
    int ret = 0;
#ifdef WOLFSSL_DTLS_DROP_STATS
    ssl->macDropCount++;
#endif

#if defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)
    /* Handle AEAD limits specified by the RFC for failed decryption */
    if (IsAtLeastTLSv1_3(ssl->version))
        ret = Dtls13CheckAEADFailLimit(ssl);
#endif

    (void)ssl;
    WOLFSSL_MSG("DTLS: Ignoring failed decryption");
    return ret;
}

static int DtlsShouldDrop(WOLFSSL* ssl, int retcode)
{
    if (ssl->options.handShakeDone && !IsEncryptionOn(ssl, 0) &&
            !ssl->options.dtlsHsRetain) {
        WOLFSSL_MSG("Silently dropping plaintext DTLS message "
                    "on established connection when we have nothing to send.");
        return 1;
    }

    if ((ssl->options.handShakeDone && retcode != 0)
        || retcode == WC_NO_ERR_TRACE(SEQUENCE_ERROR)
        || retcode == WC_NO_ERR_TRACE(DTLS_CID_ERROR)
        || retcode == WC_NO_ERR_TRACE(DTLS_PARTIAL_RECORD_READ)) {
        WOLFSSL_MSG_EX("Silently dropping DTLS message: %d", retcode);
        return 1;
    }

#ifdef WOLFSSL_DTLS13
    if (IsAtLeastTLSv1_3(ssl->version) && !w64IsZero(ssl->dtls13Epoch)
            && w64IsZero(ssl->keys.curEpoch64) && ssl->curRL.type != ack) {
        WOLFSSL_MSG("Silently dropping plaintext DTLS message "
                    "during encrypted handshake.");
        return 1;
    }
#endif /* WOLFSSL_DTLS13 */

#ifndef NO_WOLFSSL_SERVER
    if (ssl->options.side == WOLFSSL_SERVER_END
            && ssl->curRL.type != handshake && !IsSCR(ssl)) {
        if (!ssl->options.dtlsStateful) {
            WOLFSSL_MSG("Drop non-handshake record when not stateful");
            return 1;
        }
    }
#endif /* NO_WOLFSSL_SERVER */

    return 0;
}
#endif /* WOLFSSL_DTLS */

#if defined(WOLFSSL_TLS13) || \
    (defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID))
static int removeMsgInnerPadding(WOLFSSL* ssl)
{
    word32 i = ssl->buffers.inputBuffer.idx +
        ssl->curSize;
    if (ssl->specs.cipher_type == aead)
        i -= ssl->specs.aead_mac_size;
    else
        i -= ssl->keys.padSz + MacSize(ssl);

    /* check that the end of the logical length doesn't extend
     * past the real buffer */
    if (i > ssl->buffers.inputBuffer.length || i == 0) {
        WOLFSSL_ERROR(BUFFER_ERROR);
        return BUFFER_ERROR;
    }

    /* Remove padding from end of plain text. */
    for (--i; i > ssl->buffers.inputBuffer.idx; i--) {
        if (ssl->buffers.inputBuffer.buffer[i] != 0)
            break;
    }

    /* Get the real content type from the end of the data. */
    ssl->curRL.type = ssl->buffers.inputBuffer.buffer[i];
    if (ssl->curRL.type == 0) {
        SendAlert(ssl, alert_fatal, unexpected_message);
        WOLFSSL_ERROR(PARSE_ERROR);
        return PARSE_ERROR;
    }
    /* consider both contentType byte and MAC as padding */
    ssl->keys.padSz = ssl->buffers.inputBuffer.idx
        + ssl->curSize - i;
    return 0;
}
#endif

#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
static void dtlsClearPeer(WOLFSSL_SOCKADDR* peer)
{
    XFREE(peer->sa, NULL, DYNAMIC_TYPE_SOCKADDR);
    peer->sa = NULL;
    peer->sz = 0;
    peer->bufSz = 0;
}


/**
 * @brief Handle pending peer during record processing.
 * @param ssl           WOLFSSL object.
 * @param deprotected   0 when we have not decrypted the record yet
 *                      1 when we have decrypted and verified the record
 */
static void dtlsProcessPendingPeer(WOLFSSL* ssl, int deprotected)
{
    if (ssl->buffers.dtlsCtx.pendingPeer.sa != NULL) {
        if (!deprotected) {
            /* Here we have just read an entire record from the network. It is
             * still encrypted. If processingPendingRecord is set then that
             * means that an error occurred when processing the previous record.
             * In that case we should clear the pendingPeer because we only
             * want to allow it to be valid for one record. */
            if (ssl->buffers.dtlsCtx.processingPendingRecord) {
                /* Clear the pending peer. */
                dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer);
            }
            ssl->buffers.dtlsCtx.processingPendingRecord =
                    !ssl->buffers.dtlsCtx.processingPendingRecord;
        }
        else {
            /* Pending peer present and record deprotected. Update the peer. */
            (void)wolfSSL_dtls_set_peer(ssl,
                    ssl->buffers.dtlsCtx.pendingPeer.sa,
                    ssl->buffers.dtlsCtx.pendingPeer.sz);
            ssl->buffers.dtlsCtx.processingPendingRecord = 0;
            dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer);
        }
    }
    else {
        ssl->buffers.dtlsCtx.processingPendingRecord = 0;
    }
}
#endif
static int DoDecrypt(WOLFSSL *ssl)
{
    int ret;
    int atomicUser = 0;
    bufferStatic* in = &ssl->buffers.inputBuffer;

#ifdef ATOMIC_USER
    if (ssl->ctx->DecryptVerifyCb)
        atomicUser = 1;
#endif

    ret = SanityCheckCipherText(ssl, ssl->curSize);
    if (ret < 0) {
        return ret;
    }

    if (atomicUser) {
#ifdef ATOMIC_USER
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
        if (ssl->options.startedETMRead) {
            ret = ssl->ctx->VerifyDecryptCb(ssl,
                            in->buffer + in->idx, in->buffer + in->idx,
                            ssl->curSize - MacSize(ssl),
                            ssl->curRL.type, 1, &ssl->keys.padSz,
                            ssl->DecryptVerifyCtx);
        }
        else
#endif
        {
            ret = ssl->ctx->DecryptVerifyCb(ssl,
                            in->buffer + in->idx,
                            in->buffer + in->idx,
                            ssl->curSize, ssl->curRL.type, 1,
                            &ssl->keys.padSz, ssl->DecryptVerifyCtx);
        }
#endif /* ATOMIC_USER */
    }
    else {
        if (!ssl->options.tls1_3) {
#ifndef WOLFSSL_NO_TLS12
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
        if (ssl->options.startedETMRead) {
            word32 digestSz = MacSize(ssl);
            ret = DecryptTls(ssl,
                            in->buffer + in->idx,
                            in->buffer + in->idx,
                            ssl->curSize - (word16)digestSz);
            if (ret == 0) {
                byte invalid = 0;
                byte padding = (byte)-1;
                word32 i;
                word32 off = in->idx + ssl->curSize - digestSz - 1;

                /* Last of padding bytes - indicates length. */
                ssl->keys.padSz = in->buffer[off];
                /* Constant time checking of padding - don't leak the length of
                 * the data. */
                /* Compare max pad bytes or at most data + pad. */
                for (i = 1; i < MAX_PAD_SIZE && off >= i; i++) {
                    /* Mask on indicates this is expected to be a padding byte.
                     */
                    volatile byte maskPadByte = ctMaskLTE((int)i,
                                                          (int)ssl->keys.padSz);
                    padding &= maskPadByte;
                    /* When this is a padding byte and not equal to length then
                     * mask is set. */
                    invalid |= padding & ctMaskNotEq(in->buffer[off - i],
                                                     (int)ssl->keys.padSz);
                }
                /* If mask is set then there was an error. */
                if (invalid) {
                    ret = DECRYPT_ERROR;
                }
                ssl->keys.padSz += 1;
                ssl->keys.decryptedCur = 1;
            }
        }
        else
#endif
        {
            ret = DecryptTls(ssl,
                            in->buffer + in->idx,
                            in->buffer + in->idx,
                            ssl->curSize);
        }
#else
            ret = DECRYPT_ERROR;
#endif
        }
        else
        {
    #ifdef WOLFSSL_TLS13
            byte *aad = (byte*)&ssl->curRL;
            word16 aad_size = RECORD_HEADER_SZ;
        #ifdef WOLFSSL_DTLS13
            if (ssl->options.dtls) {
                /* aad now points to the record header */
                aad = ssl->dtls13CurRL;
                aad_size = ssl->dtls13CurRlLength;
            }
        #endif /* WOLFSSL_DTLS13 */
            /* Don't send an alert for DTLS. We will just drop it
                * silently later. */
            ret = DecryptTls13(ssl,
                            in->buffer + in->idx,
                            in->buffer + in->idx,
                            ssl->curSize,
                            aad, aad_size);
    #else
            ret = DECRYPT_ERROR;
    #endif /* WOLFSSL_TLS13 */
        }
        (void)in;
    }
    return ret;
}

#ifdef WOLFSSL_DTLS
static void DropAndRestartProcessReply(WOLFSSL* ssl)
{
    ssl->options.processReply = doProcessInit;
    ssl->buffers.inputBuffer.length = 0;
    ssl->buffers.inputBuffer.idx = 0;
#ifdef WOLFSSL_DTLS_DROP_STATS
    if (ssl->options.dtls)
        ssl->replayDropCount++;
#endif /* WOLFSSL_DTLS_DROP_STATS */
}
#endif /* WOLFSSL_DTLS */
/* Process input requests. Return 0 is done, 1 is call again to complete, and
   negative number is error. If allowSocketErr is set, SOCKET_ERROR_E in
   ssl->error will be whitelisted. This is useful when the connection has been
   closed and the endpoint wants to check for an alert sent by the other end. */
static int DoProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
{
    int    ret = 0, type = internal_error, readSz;
    int    atomicUser = 0;
#if defined(WOLFSSL_DTLS)
    int    used;
#endif

#ifdef ATOMIC_USER
    if (ssl->ctx->DecryptVerifyCb)
        atomicUser = 1;
#endif

    if (ssl->error != 0 &&
        ssl->error != WC_NO_ERR_TRACE(WANT_READ) &&
        ssl->error != WC_NO_ERR_TRACE(WANT_WRITE)
    #if defined(HAVE_SECURE_RENEGOTIATION) || defined(WOLFSSL_DTLS13)
        && ssl->error != WC_NO_ERR_TRACE(APP_DATA_READY)
    #endif
    #ifdef WOLFSSL_ASYNC_CRYPT
        && ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E)
    #endif
    #ifdef WOLFSSL_NONBLOCK_OCSP
        && ssl->error != WC_NO_ERR_TRACE(OCSP_WANT_READ)
    #endif
        && (allowSocketErr != 1 ||
            ssl->error != WC_NO_ERR_TRACE(SOCKET_ERROR_E))
    ) {
        WOLFSSL_MSG("ProcessReply retry in error state, not allowed");
        return ssl->error;
    }

#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_ASYNC_CRYPT)
    /* process any pending DTLS messages - this flow can happen with async */
    if (ssl->dtls_rx_msg_list != NULL) {
        word32 pendingMsg = ssl->dtls_rx_msg_list_sz;
        if(IsAtLeastTLSv1_3(ssl->version)) {
#ifdef WOLFSSL_DTLS13
            ret = Dtls13ProcessBufferedMessages(ssl);
#else
            ret = NOT_COMPILED_IN;
#endif /* WOLFSSL_DTLS13 */
        }
        else {
            ret = DtlsMsgDrain(ssl);
        }
        if (ret != 0) {
            WOLFSSL_ERROR(ret);
            return ret;
        }
        /* we processed some messages, return so connect/accept can make
           progress */
        if (ssl->dtls_rx_msg_list_sz != pendingMsg)
            return ret;
    }
#endif

    ret = RetrySendAlert(ssl);
    if (ret != 0) {
        WOLFSSL_MSG_EX("RetrySendAlert failed, giving up. err = %d", ret);
        return ret;
    }

    for (;;) {
        switch (ssl->options.processReply) {

        /* in the WOLFSSL_SERVER case, get the first byte for detecting
         * old client hello */
        case doProcessInit:

            readSz = RECORD_HEADER_SZ;

        #ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
                readSz = DTLS_RECORD_HEADER_SZ;
#ifdef WOLFSSL_DTLS13
                if (ssl->options.tls1_3) {
                    /* dtls1.3 unified header can be as little as 2 bytes */
                    readSz = DTLS_UNIFIED_HEADER_MIN_SZ;
                }
#endif /* WOLFSSL_DTLS13 */
            }
        #endif

            /* get header or return error */
            if (!ssl->options.dtls) {
                if ((ret = GetInputData(ssl, (word32)readSz)) < 0)
                    return ret;
            } else {
            #ifdef WOLFSSL_DTLS
                /* read ahead may already have header */
                used = ssl->buffers.inputBuffer.length -
                       ssl->buffers.inputBuffer.idx;
                if (used < readSz) {
                    if (used > 0 && !RecordsCanSpanReads(ssl)) {
                        WOLFSSL_MSG("DTLS: Partial record in buffer, dropping");
                        DropAndRestartProcessReply(ssl);
                        continue;
                    }
                    if ((ret = GetInputData(ssl, (word32)readSz)) < 0)
                        return ret;
                }
            #endif
            }

#ifdef OLD_HELLO_ALLOWED

            /* see if sending SSLv2 client hello */
            if ( ssl->options.side == WOLFSSL_SERVER_END &&
                 ssl->options.clientState == NULL_STATE &&
                 ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx]
                         != handshake &&
                 /* change_cipher_spec here is an error but we want to handle
                  * it correctly later */
                 ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx]
                         != change_cipher_spec) {
                byte b0, b1;

                ssl->options.processReply = runProcessOldClientHello;

                /* sanity checks before getting size at front */
                if (ssl->buffers.inputBuffer.buffer[
                          ssl->buffers.inputBuffer.idx + OPAQUE16_LEN] != OLD_HELLO_ID) {
                    WOLFSSL_MSG("Not a valid old client hello");
                    WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
                    return PARSE_ERROR;
                }

                if (ssl->buffers.inputBuffer.buffer[
                          ssl->buffers.inputBuffer.idx + OPAQUE24_LEN] != SSLv3_MAJOR &&
                    ssl->buffers.inputBuffer.buffer[
                          ssl->buffers.inputBuffer.idx + OPAQUE24_LEN] != DTLS_MAJOR) {
                    WOLFSSL_MSG("Not a valid version in old client hello");
                    WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
                    return PARSE_ERROR;
                }

                /* how many bytes need ProcessOldClientHello */
                b0 =
                ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx++];
                b1 =
                ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx++];
                ssl->curSize = (word16)(((b0 & 0x7f) << 8) | b1);
            }
            else {
                ssl->options.processReply = getRecordLayerHeader;
                continue;
            }
            FALL_THROUGH;

        /* in the WOLFSSL_SERVER case, run the old client hello */
        case runProcessOldClientHello:

            /* get sz bytes or return error */
            if (!ssl->options.dtls) {
                if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
                    return ret;
            } else {
            #ifdef WOLFSSL_DTLS
                /* read ahead may already have */
                used = ssl->buffers.inputBuffer.length -
                       ssl->buffers.inputBuffer.idx;
                if (used < ssl->curSize)
                    if ((ret = GetInputData(ssl, ssl->curSize - used)) < 0)
                        return ret;
            #endif  /* WOLFSSL_DTLS */
            }

            ret = ProcessOldClientHello(ssl, ssl->buffers.inputBuffer.buffer,
                                        &ssl->buffers.inputBuffer.idx,
                                        ssl->buffers.inputBuffer.length -
                                        ssl->buffers.inputBuffer.idx,
                                        ssl->curSize);
            if (ret < 0)
                return ret;

            else if (ssl->buffers.inputBuffer.idx ==
                     ssl->buffers.inputBuffer.length) {
                ssl->options.processReply = doProcessInit;
                return 0;
            }

#endif  /* OLD_HELLO_ALLOWED */
            FALL_THROUGH;

        /* get the record layer header */
        case getRecordLayerHeader:

            /* DTLSv1.3 record numbers in the header are encrypted, and AAD
             * uses the unencrypted form. Because of this we need to modify the
             * header, decrypting the numbers inside
             * DtlsParseUnifiedRecordLayer(). This violates the const attribute
             * of the buffer parameter of GetRecordHeader() used here. */
            ret = GetRecordHeader(ssl, &ssl->buffers.inputBuffer.idx,
                                       &ssl->curRL, &ssl->curSize);

#ifdef WOLFSSL_DTLS
#ifdef WOLFSSL_DTLS_CID
            if (ssl->options.dtls)
                dtlsProcessPendingPeer(ssl, 0);
#endif
            if (ssl->options.dtls && DtlsShouldDrop(ssl, ret)) {
                DropAndRestartProcessReply(ssl);
#ifdef WOLFSSL_DTLS13
                    /* return to send ACKS and shortcut rtx timer */
                    if (IsAtLeastTLSv1_3(ssl->version)
                        && ssl->dtls13Rtx.sendAcks)
                        return 0;
#endif /* WOLFSSL_DTLS13 */

                    continue;
            }
#endif
            if (ret != 0) {
                switch (ret) {
                case WC_NO_ERR_TRACE(VERSION_ERROR):
                    /* send alert per RFC5246 Appendix E. Backward
                     * Compatibility */
                    if (ssl->options.side == WOLFSSL_CLIENT_END)
                        SendAlert(ssl, alert_fatal,
                            wolfssl_alert_protocol_version);
                    break;
#ifdef HAVE_MAX_FRAGMENT
                case WC_NO_ERR_TRACE(LENGTH_ERROR):
                    SendAlert(ssl, alert_fatal, record_overflow);
                    break;
#endif /* HAVE_MAX_FRAGMENT */
default:
                    break;
                }
                return ret;
            }

#ifdef WOLFSSL_TLS13
            if (IsAtLeastTLSv1_3(ssl->version) && IsEncryptionOn(ssl, 0) &&
                                        ssl->curRL.type != application_data &&
                                        ssl->curRL.type != change_cipher_spec &&
                                        ssl->curRL.type != alert) {
                SendAlert(ssl, alert_fatal, unexpected_message);
                WOLFSSL_ERROR_VERBOSE(PARSE_ERROR);
                return PARSE_ERROR;
            }
#endif

            ssl->options.processReply = getData;
            FALL_THROUGH;

        /* retrieve record layer data */
        case getData:

            /* get sz bytes or return error */
            if (!ssl->options.dtls) {
                if ((ret = GetInputData(ssl, ssl->curSize)) < 0) {
#ifdef WOLFSSL_EXTRA_ALERTS
                    if (ret != WC_NO_ERR_TRACE(WANT_READ))
                        SendAlert(ssl, alert_fatal, bad_record_mac);
#endif
                    return ret;
                }
            }
            else {
#ifdef WOLFSSL_DTLS
                /* read ahead may already have */
                used = ssl->buffers.inputBuffer.length -
                       ssl->buffers.inputBuffer.idx;
                if (used < ssl->curSize) {
                    if (!RecordsCanSpanReads(ssl)) {
                        WOLFSSL_MSG("Partial record received, dropping");
                        DropAndRestartProcessReply(ssl);
                        continue;
                    }
                    if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
                        return ret;
                }
#endif
            }

            if (IsEncryptionOn(ssl, 0)) {
                int tooLong = 0;

#ifdef WOLFSSL_TLS13
                if (IsAtLeastTLSv1_3(ssl->version)) {
                    tooLong  = ssl->curSize > MAX_TLS13_ENC_SZ;
                    if (ssl->specs.aead_mac_size < ssl->curSize) {
                        tooLong |= ssl->curSize - ssl->specs.aead_mac_size >
                                                             MAX_TLS13_PLAIN_SZ;
                    }
                }
                else
#endif
                {
                    tooLong = ssl->curSize > MAX_TLS_CIPHER_SZ;
                }
                if (tooLong) {
                    WOLFSSL_MSG("Encrypted data too long");
                    SendAlert(ssl, alert_fatal, record_overflow);
                    return BUFFER_ERROR;
                }
            }
            ssl->keys.padSz = 0;

            ssl->options.processReply = verifyEncryptedMessage;
            FALL_THROUGH;

        /* verify digest of encrypted message */
        case verifyEncryptedMessage:
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
            if (IsEncryptionOn(ssl, 0) && ssl->keys.decryptedCur == 0 &&
                                   !atomicUser && ssl->options.startedETMRead) {
                ret = VerifyMacEnc(ssl, ssl->buffers.inputBuffer.buffer +
                                   ssl->buffers.inputBuffer.idx,
                                   ssl->curSize, ssl->curRL.type);
            #ifdef WOLFSSL_ASYNC_CRYPT
                if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                    return ret;
            #endif
                if (ret < 0) {
                    WOLFSSL_MSG("VerifyMacEnc failed");
                #ifdef WOLFSSL_DTLS
                    /* If in DTLS mode, if the decrypt fails for any
                     * reason, pretend the datagram never happened. */
                    if (ssl->options.dtls) {
                        DropAndRestartProcessReply(ssl);
                        return HandleDTLSDecryptFailed(ssl);
                    }
                #endif /* WOLFSSL_DTLS */
                #ifdef WOLFSSL_EXTRA_ALERTS
                    if (!ssl->options.dtls)
                        SendAlert(ssl, alert_fatal, bad_record_mac);
                #endif
                    WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR);
                    return DECRYPT_ERROR;
                }
                ssl->keys.encryptSz    = ssl->curSize;
            }
#endif
            ssl->options.processReply = decryptMessage;
            FALL_THROUGH;

        /* decrypt message */
        case decryptMessage:

            if (IsEncryptionOn(ssl, 0) && ssl->keys.decryptedCur == 0 &&
                                      (!IsAtLeastTLSv1_3(ssl->version) ||
                                       (ssl->curRL.type != change_cipher_spec &&
                                        ssl->curRL.type != alert))) {
                ret = DoDecrypt(ssl);
            #ifdef WOLFSSL_ASYNC_CRYPT
                if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                    return ret;
            #endif

                if (ret >= 0) {
            #ifndef WOLFSSL_NO_TLS12
                    /* handle success */
                #ifndef WOLFSSL_AEAD_ONLY
                    if (ssl->options.tls1_1 &&
                            ssl->specs.cipher_type == block) {
                        ssl->buffers.inputBuffer.idx += ssl->specs.block_size;
                        ssl->curSize -= ssl->specs.block_size;
                    }
                #endif
                    /* go past TLSv1.1 IV */
                    if (CipherHasExpIV(ssl)) {
                        ssl->buffers.inputBuffer.idx += AESGCM_EXP_IV_SZ;
                        ssl->curSize -= AESGCM_EXP_IV_SZ;
                    }
            #endif
                }
                else {
                    WOLFSSL_MSG("Decrypt failed");
                #ifdef WOLFSSL_DTLS
                    /* If in DTLS mode, if the decrypt fails for any
                     * reason, pretend the datagram never happened. */
                    if (ssl->options.dtls) {
                        DropAndRestartProcessReply(ssl);
                        return HandleDTLSDecryptFailed(ssl);
                    }
                #endif /* WOLFSSL_DTLS */
                #ifdef WOLFSSL_EARLY_DATA
                    if (ssl->options.tls1_3) {
                         if (ssl->options.side == WOLFSSL_SERVER_END &&
                                 ssl->earlyData != no_early_data &&
                                 ssl->options.clientState <
                                                     CLIENT_FINISHED_COMPLETE) {
                            ssl->earlyDataSz += ssl->curSize;
                            if (ssl->earlyDataSz <=
                                                  ssl->options.maxEarlyDataSz) {
                                WOLFSSL_MSG("Ignoring EarlyData!");
                                if (ssl->keys.peer_sequence_number_lo-- == 0)
                                    ssl->keys.peer_sequence_number_hi--;
                                ssl->options.processReply = doProcessInit;
                                ssl->buffers.inputBuffer.idx += ssl->curSize;
                                if (ssl->buffers.inputBuffer.idx >
                                    ssl->buffers.inputBuffer.length) {
                                    WOLFSSL_ERROR(BUFFER_E);
                                    return BUFFER_E;
                                }

                                return 0;
                            }
                            WOLFSSL_MSG("Too much EarlyData!");
                            SendAlert(ssl, alert_fatal, unexpected_message);
                            WOLFSSL_ERROR(TOO_MUCH_EARLY_DATA);
                            return TOO_MUCH_EARLY_DATA;
                        }
                    }
                #endif
                    SendAlert(ssl, alert_fatal, bad_record_mac);
                    /* Push error once we know that we will error out here */
                    WOLFSSL_ERROR(ret);
                    return ret;
                }
            }

            ssl->options.processReply = verifyMessage;
            FALL_THROUGH;

        /* verify digest of message */
        case verifyMessage:

            if (IsEncryptionOn(ssl, 0) && ssl->keys.decryptedCur == 0 &&
                                      (!IsAtLeastTLSv1_3(ssl->version) ||
                                       (ssl->curRL.type != change_cipher_spec &&
                                        ssl->curRL.type != alert))) {
                if (!atomicUser
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
                                && !ssl->options.startedETMRead
#endif
                    ) {
                    ret = VerifyMac(ssl, ssl->buffers.inputBuffer.buffer +
                                    ssl->buffers.inputBuffer.idx,
                                    ssl->curSize, ssl->curRL.type,
                                    &ssl->keys.padSz);
                #ifdef WOLFSSL_ASYNC_CRYPT
                    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                        return ret;
                #endif
                    if (ret < 0) {
                    #ifdef WOLFSSL_DTLS
                        /* If in DTLS mode, if the decrypt fails for any
                         * reason, pretend the datagram never happened. */
                        if (ssl->options.dtls) {
                            DropAndRestartProcessReply(ssl);
                            return HandleDTLSDecryptFailed(ssl);
                        }
                    #endif /* WOLFSSL_DTLS */
                    #if defined(WOLFSSL_EXTRA_ALERTS) && !defined(WOLFSSL_NO_ETM_ALERT)
                        if (!ssl->options.dtls)
                            SendAlert(ssl, alert_fatal, bad_record_mac);
                    #endif
                        WOLFSSL_MSG("VerifyMac failed");
                        WOLFSSL_ERROR_VERBOSE(DECRYPT_ERROR);
                        return DECRYPT_ERROR;
                    }
                }

                ssl->keys.encryptSz    = ssl->curSize;
                ssl->keys.decryptedCur = 1;
            }

            if (IsEncryptionOn(ssl, 0) && ssl->keys.decryptedCur == 1) {
#if defined(WOLFSSL_TLS13) || \
    (defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID))
                int removePadding = 0;
                if (ssl->options.tls1_3)
                    removePadding = 1;
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
                if (!ssl->options.tls1_3 && ssl->options.dtls &&
                        ssl->curRL.type == dtls12_cid)
                    removePadding = 1;
#endif
                if (removePadding) {
                    ret = removeMsgInnerPadding(ssl);
                    if (ret != 0)
                        return ret;
                }
                else
#endif
                {
                    /* With atomicUser the callback should have already included
                     * the mac in the padding size. The ETM callback doesn't do
                     * this for some reason. */
                    if (ssl->specs.cipher_type != aead
#ifdef ATOMIC_USER
                             && (!atomicUser
#ifdef HAVE_ENCRYPT_THEN_MAC
                                 || ssl->options.startedETMRead
#endif /* HAVE_ENCRYPT_THEN_MAC */
                                )
#endif /* !ATOMIC_USER */
                       )
                    {
                        /* consider MAC as padding */
                        ssl->keys.padSz += MacSize(ssl);
                    }
                }

            }

            /* in case > 1 msg per record */
            ssl->curStartIdx = ssl->buffers.inputBuffer.idx;

            ssl->options.processReply = runProcessingOneRecord;
            FALL_THROUGH;

        /* the record layer is here */
        case runProcessingOneRecord:
#ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
#ifdef WOLFSSL_DTLS13
                if (IsAtLeastTLSv1_3(ssl->version)) {
                    if (!Dtls13CheckWindow(ssl)) {
                        /* drop packet */
                        WOLFSSL_MSG("Dropping DTLS record outside receiving "
                                    "window");
                        ssl->options.processReply = doProcessInit;
                        ssl->buffers.inputBuffer.idx += ssl->curSize;
                        if (ssl->buffers.inputBuffer.idx >
                                ssl->buffers.inputBuffer.length)
                            return BUFFER_E;

                        continue;
                    }

                    /* Only update the window once we enter stateful parsing */
                    if (ssl->options.dtlsStateful) {
                        ret = Dtls13UpdateWindowRecordRecvd(ssl);
                        if (ret != 0) {
                            WOLFSSL_ERROR(ret);
                            return ret;
                        }
                    }
                }
                else
#endif /* WOLFSSL_DTLS13 */
                if (IsDtlsNotSctpMode(ssl)) {
                    DtlsUpdateWindow(ssl);
                }
#ifdef WOLFSSL_DTLS_CID
                /* Update the peer if we were able to de-protect the message */
                if (IsEncryptionOn(ssl, 0))
                    dtlsProcessPendingPeer(ssl, 1);
#endif
            }
#endif /* WOLFSSL_DTLS */
            ssl->options.processReply = runProcessingOneMessage;
            FALL_THROUGH;

        case runProcessingOneMessage:
            /* can't process a message if we have no data.  */
            if (ssl->buffers.inputBuffer.idx
                    >= ssl->buffers.inputBuffer.length) {
                return BUFFER_ERROR;
            }
       #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
            if (IsEncryptionOn(ssl, 0) && ssl->options.startedETMRead) {
                if ((ssl->curSize - ssl->keys.padSz > MAX_PLAINTEXT_SZ)
#ifdef WOLFSSL_ASYNC_CRYPT
                        && ssl->buffers.inputBuffer.length !=
                                ssl->buffers.inputBuffer.idx
#endif
                                ) {
                    WOLFSSL_MSG("Plaintext too long - Encrypt-Then-MAC");
            #if defined(WOLFSSL_EXTRA_ALERTS) && !defined(WOLFSSL_NO_ETM_ALERT)
                    SendAlert(ssl, alert_fatal, record_overflow);
            #endif
                    WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR);
                    return BUFFER_ERROR;
                }
            }
            else
       #endif
            /* TLS13 plaintext limit is checked earlier before decryption */
            if (!IsAtLeastTLSv1_3(ssl->version)
                    && ssl->curSize - ssl->keys.padSz > MAX_PLAINTEXT_SZ
#ifdef WOLFSSL_ASYNC_CRYPT
                    && ssl->buffers.inputBuffer.length !=
                            ssl->buffers.inputBuffer.idx
#endif
                                ) {
                WOLFSSL_MSG("Plaintext too long");
#if defined(WOLFSSL_TLS13) || defined(WOLFSSL_EXTRA_ALERTS)
                SendAlert(ssl, alert_fatal, record_overflow);
#endif
                WOLFSSL_ERROR_VERBOSE(BUFFER_ERROR);
                return BUFFER_ERROR;
            }

            WOLFSSL_MSG("received record layer msg");

            switch (ssl->curRL.type) {
                case handshake :
                    WOLFSSL_MSG("got HANDSHAKE");
                    /* debugging in DoHandShakeMsg */
                    if (ssl->options.dtls) {
#ifdef WOLFSSL_DTLS
                        if (!IsAtLeastTLSv1_3(ssl->version)) {
                            ret = DoDtlsHandShakeMsg(ssl,
                                ssl->buffers.inputBuffer.buffer,
                                &ssl->buffers.inputBuffer.idx,
                                ssl->buffers.inputBuffer.length);
                            if (ret == 0 ||
                                ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                                /* Reset timeout as we have received a valid
                                 * DTLS handshake message */
                                ssl->dtls_timeout = ssl->dtls_timeout_init;
                            }
                            else {
                                if (SendFatalAlertOnly(ssl, ret)
                                        == WC_NO_ERR_TRACE(SOCKET_ERROR_E)) {
                                    ret = SOCKET_ERROR_E;
                                }
                            }
                        }
#endif
#ifdef WOLFSSL_DTLS13
                        if (IsAtLeastTLSv1_3(ssl->version)) {
                            ret = Dtls13HandshakeRecv(ssl,
                                ssl->buffers.inputBuffer.buffer,
                                &ssl->buffers.inputBuffer.idx,
                                ssl->buffers.inputBuffer.length);
#ifdef WOLFSSL_EARLY_DATA
                            if (ret == 0 &&
                                ssl->options.side == WOLFSSL_SERVER_END &&
                                ssl->earlyData > early_data_ext &&
                                ssl->options.handShakeState == HANDSHAKE_DONE) {

                                /* return so wolfSSL_read_early_data can return
                                   exit */
                                ssl->earlyData = no_early_data;
                                ssl->options.processReply = doProcessInit;
                                if (ssl->options.clientInEarlyData)
                                    return APP_DATA_READY;
                            }
#endif /* WOLFSSL_EARLY_DATA */
                            if (ret == 0 ||
                                ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                                /* Reset timeout as we have received a valid
                                 * DTLS handshake message */
                                ssl->dtls_timeout = ssl->dtls_timeout_init;
                            }
                        }
#endif /* WOLFSSL_DTLS13 */
                    }
                    else if (!IsAtLeastTLSv1_3(ssl->version)
#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12)
                            || !TLSv1_3_Capable(ssl)
#endif
                            ) {
#ifndef WOLFSSL_NO_TLS12
                        ret = DoHandShakeMsg(ssl,
                                            ssl->buffers.inputBuffer.buffer,
                                            &ssl->buffers.inputBuffer.idx,
                                            ssl->buffers.inputBuffer.length);
                        if (ret != 0) {
                            if (SendFatalAlertOnly(ssl, ret) ==
                                WC_NO_ERR_TRACE(SOCKET_ERROR_E))
                                ret = SOCKET_ERROR_E;
                        }
#else
                        ret = BUFFER_ERROR;
#endif
                    }
                    else {
#ifdef WOLFSSL_TLS13
                        ssl->msgsReceived.got_change_cipher = 0;
                        ret = DoTls13HandShakeMsg(ssl,
                                            ssl->buffers.inputBuffer.buffer,
                                            &ssl->buffers.inputBuffer.idx,
                                            ssl->buffers.inputBuffer.length);
    #ifdef WOLFSSL_EARLY_DATA
                        if (ret != 0)
                            return ret;
                        if (ssl->options.side == WOLFSSL_SERVER_END &&
                                ssl->earlyData > early_data_ext &&
                                ssl->options.handShakeState == HANDSHAKE_DONE) {
                            ssl->earlyData = no_early_data;
                            ssl->options.processReply = doProcessInit;
                            if (ssl->options.clientInEarlyData)
                                return APP_DATA_READY;
                        }
    #endif
#else
                        ret = BUFFER_ERROR;
#endif
                    }
                    if (ret != 0
                            /* DoDtlsHandShakeMsg can return a WANT_WRITE when
                             * calling DtlsMsgPoolSend. This msg is done
                             * processing so let's move on. */
                        && (!ssl->options.dtls
                            || ret != WC_NO_ERR_TRACE(WANT_WRITE))
#ifdef WOLFSSL_ASYNC_CRYPT
                    /* In async case, on pending, move onto next message.
                     * Current message should have been DtlsMsgStore'ed and
                     * should be processed with DtlsMsgDrain */
                            && (!ssl->options.dtls
                                || ret != WC_NO_ERR_TRACE(WC_PENDING_E))
#endif
                    ) {
                        WOLFSSL_ERROR(ret);
                        return ret;
                    }
                    break;

                case change_cipher_spec:
                    WOLFSSL_MSG("got CHANGE CIPHER SPEC");
                    #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
                        if (ssl->hsInfoOn)
                            AddPacketName(ssl, "ChangeCipher");
                        /* add record header back on info */
                        if (ssl->toInfoOn) {
                            ret = AddPacketInfo(ssl, "ChangeCipher",
                                change_cipher_spec,
                                ssl->buffers.inputBuffer.buffer +
                                ssl->buffers.inputBuffer.idx,
                                1, READ_PROTO, RECORD_HEADER_SZ, ssl->heap);
                            if (ret != 0)
                                return ret;
                            #ifdef WOLFSSL_CALLBACKS
                            AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo);
                            #endif
                        }
                    #endif

#ifdef WOLFSSL_TLS13
                    if (IsAtLeastTLSv1_3(ssl->version)) {
                        word32 i = ssl->buffers.inputBuffer.idx;
                        if (ssl->options.handShakeState == HANDSHAKE_DONE) {
                            SendAlert(ssl, alert_fatal, unexpected_message);
                            WOLFSSL_ERROR_VERBOSE(UNKNOWN_RECORD_TYPE);
                            return UNKNOWN_RECORD_TYPE;
                        }
                        if (ssl->curSize != 1 ||
                                      ssl->buffers.inputBuffer.buffer[i] != 1) {
                            SendAlert(ssl, alert_fatal, unexpected_message);
                            WOLFSSL_ERROR_VERBOSE(UNKNOWN_RECORD_TYPE);
                            return UNKNOWN_RECORD_TYPE;
                        }
                        ssl->buffers.inputBuffer.idx++;
                        if (ssl->options.side == WOLFSSL_SERVER_END &&
                                !ssl->msgsReceived.got_client_hello) {
                            /* Can't appear before CH */
                            SendAlert(ssl, alert_fatal, unexpected_message);
                            WOLFSSL_ERROR_VERBOSE(UNKNOWN_RECORD_TYPE);
                            return UNKNOWN_RECORD_TYPE;
                        }
                        if (!ssl->msgsReceived.got_change_cipher) {
                            ssl->msgsReceived.got_change_cipher = 1;
                        }
                        else {
                            SendAlert(ssl, alert_fatal, illegal_parameter);
                            WOLFSSL_ERROR_VERBOSE(UNKNOWN_RECORD_TYPE);
                            return UNKNOWN_RECORD_TYPE;
                        }
                        if (ssl->keys.decryptedCur == 1) {
                            SendAlert(ssl, alert_fatal, unexpected_message);
                            WOLFSSL_ERROR_VERBOSE(UNKNOWN_RECORD_TYPE);
                            return UNKNOWN_RECORD_TYPE;
                        }
                        break;
                    }
#endif

#ifndef WOLFSSL_NO_TLS12
                    if (ssl->buffers.inputBuffer.idx >=
                            ssl->buffers.inputBuffer.length ||
                            ssl->curSize < 1) {
                        WOLFSSL_MSG("ChangeCipher msg too short");
                        WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR);
                        return LENGTH_ERROR;
                    }
                    if (ssl->buffers.inputBuffer.buffer[
                            ssl->buffers.inputBuffer.idx] != 1) {
                        WOLFSSL_MSG("ChangeCipher msg wrong value");
                        WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR);
                        return LENGTH_ERROR;
                    }

                    if (IsEncryptionOn(ssl, 0) && ssl->options.handShakeDone) {
                        ssl->buffers.inputBuffer.idx += ssl->keys.padSz;
                        ssl->curSize -= (word16)ssl->keys.padSz;
                    }

                    if (ssl->curSize != 1) {
                        WOLFSSL_MSG("Malicious or corrupted ChangeCipher msg");
                        WOLFSSL_ERROR_VERBOSE(LENGTH_ERROR);
                        return LENGTH_ERROR;
                    }

                    ssl->buffers.inputBuffer.idx++;

                    ret = SanityCheckMsgReceived(ssl, change_cipher_hs);
                    if (ret != 0) {
                        if (!ssl->options.dtls) {
                            return ret;
                        }
                        else {
                        #ifdef WOLFSSL_DTLS
                        /* Check for duplicate CCS message in DTLS mode.
                         * DTLS allows for duplicate messages, and it should be
                         * skipped. Also skip if out of order. */
                            if (ret != WC_NO_ERR_TRACE(DUPLICATE_MSG_E) &&
                                ret != WC_NO_ERR_TRACE(OUT_OF_ORDER_E))
                                return ret;
                            /* Reset error */
                            ret = 0;
                            break;
                        #endif /* WOLFSSL_DTLS */
                        }
                    }

                    ssl->keys.encryptionOn = 1;

                    /* setup decrypt keys for following messages */
                    /* XXX This might not be what we want to do when
                     * receiving a CCS with multicast. We update the
                     * key when the application updates them. */
                    if ((ret = SetKeysSide(ssl, DECRYPT_SIDE_ONLY)) != 0)
                        return ret;

            #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
                    ssl->options.startedETMRead = ssl->options.encThenMac;
            #endif

                    #ifdef WOLFSSL_DTLS
                        if (ssl->options.dtls) {
                            WOLFSSL_DTLS_PEERSEQ* peerSeq = ssl->keys.peerSeq;
#ifdef WOLFSSL_MULTICAST
                            if (ssl->options.haveMcast) {
                                peerSeq += ssl->keys.curPeerId;
                                peerSeq->highwaterMark = UpdateHighwaterMark(0,
                                        ssl->ctx->mcastFirstSeq,
                                        ssl->ctx->mcastSecondSeq,
                                        ssl->ctx->mcastMaxSeq);
                            }
#endif
                            peerSeq->nextEpoch++;
                            peerSeq->prevSeq_lo = peerSeq->nextSeq_lo;
                            peerSeq->prevSeq_hi = peerSeq->nextSeq_hi;
                            peerSeq->nextSeq_lo = 0;
                            peerSeq->nextSeq_hi = 0;
                            XMEMCPY(peerSeq->prevWindow, peerSeq->window,
                                    DTLS_SEQ_SZ);
                            XMEMSET(peerSeq->window, 0, DTLS_SEQ_SZ);
                        }
                    #endif

                    #ifdef HAVE_LIBZ
                        if (ssl->options.usingCompression)
                            if ( (ret = InitStreams(ssl)) != 0)
                                return ret;
                    #endif
                    ret = BuildFinished(ssl, &ssl->hsHashes->verifyHashes,
                                       ssl->options.side == WOLFSSL_CLIENT_END ?
                                       kTlsServerStr : kTlsClientStr);
                    if (ret != 0)
                        return ret;
#endif /* !WOLFSSL_NO_TLS12 */
                    break;

                case application_data:
                    WOLFSSL_MSG("got app DATA");
                    #ifdef WOLFSSL_DTLS
                        if (ssl->options.dtls && ssl->options.dtlsHsRetain) {
                        #ifdef HAVE_SECURE_RENEGOTIATION
                            /*
                             * Only free HS resources when not in the process of a
                             * secure renegotiation and we have received APP DATA
                             * from the current epoch
                             */
                            if (!IsSCR(ssl) && (DtlsUseSCRKeys(ssl)
                                    || !DtlsSCRKeysSet(ssl))) {
                                FreeHandshakeResources(ssl);
                                ssl->options.dtlsHsRetain = 0;
                            }
                        #else
                            FreeHandshakeResources(ssl);
                            ssl->options.dtlsHsRetain = 0;
                        #endif
                        }
                    #endif
                #ifndef WOLFSSL_RW_THREADED
                    #ifdef WOLFSSL_TLS13
                        if (ssl->keys.keyUpdateRespond) {
                            WOLFSSL_MSG("No KeyUpdate from peer seen");
                            WOLFSSL_ERROR_VERBOSE(SANITY_MSG_E);
                            return SANITY_MSG_E;
                        }
                    #endif
                #endif
                    if ((ret = DoApplicationData(ssl,
                                                ssl->buffers.inputBuffer.buffer,
                                                &ssl->buffers.inputBuffer.idx,
                                                              NO_SNIFF)) != 0) {
                        WOLFSSL_ERROR(ret);
                    #if defined(WOLFSSL_DTLS13) || \
                        defined(HAVE_SECURE_RENEGOTIATION)
                        /* Not really an error. We will return after cleaning
                         * up the processReply state. */
                        if (ret != WC_NO_ERR_TRACE(APP_DATA_READY))
                    #endif
                            return ret;
                    }
                    break;

                case alert:
                    WOLFSSL_MSG("got ALERT!");
                    ret = DoAlert(ssl, ssl->buffers.inputBuffer.buffer,
                                  &ssl->buffers.inputBuffer.idx, &type);
                    if (ret == alert_fatal)
                        return FATAL_ERROR;
                    else if (ret < 0)
                        return ret;

                    /* catch warnings that are handled as errors */
                    if (type == close_notify) {
                        ssl->buffers.inputBuffer.idx =
                            ssl->buffers.inputBuffer.length;
                        ssl->options.processReply = doProcessInit;
                        return ssl->error = ZERO_RETURN;
                    }

                    if (type == decrypt_error)
                        return FATAL_ERROR;

                    /* RFC 8446 Section 6.2: In TLS 1.3, all error alerts MUST
                     * be treated as fatal regardless of the AlertLevel byte.
                     * Only close_notify (handled above) and user_canceled
                     * are exempt. */
                    if (IsAtLeastTLSv1_3(ssl->version) &&
                            type != user_canceled && type != invalid_alert) {
                        return FATAL_ERROR;
                    }

                    /* Reset error if we got an alert level in ret */
                    if (ret > 0)
                        ret = 0;
                    break;

#ifdef WOLFSSL_DTLS13
            case ack:
                WOLFSSL_MSG("got ACK");
                if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
                    word32 processedSize = 0;
                    ret = DoDtls13Ack(ssl, ssl->buffers.inputBuffer.buffer +
                                             ssl->buffers.inputBuffer.idx,
                                             ssl->buffers.inputBuffer.length -
                                             ssl->buffers.inputBuffer.idx -
                                             ssl->keys.padSz, &processedSize);
                    ssl->buffers.inputBuffer.idx += processedSize;
                    ssl->buffers.inputBuffer.idx += ssl->keys.padSz;
                    if (ret != 0)
                        return ret;
                    break;
                }
                FALL_THROUGH;
#endif /* WOLFSSL_DTLS13 */
                default:
                    WOLFSSL_ERROR(UNKNOWN_RECORD_TYPE);
                    return UNKNOWN_RECORD_TYPE;
            }

            ssl->options.processReply = doProcessInit;

            /* input exhausted */
            if (ssl->buffers.inputBuffer.idx >= ssl->buffers.inputBuffer.length
#ifdef WOLFSSL_DTLS
                || (ssl->options.dtls &&
                    /* If app data was processed then return now to avoid
                     * dropping any app data. */
                    (ssl->curRL.type == application_data ||
                    /* client: if we processed a finished message, return to
                     *         allow higher layers to establish the crypto
                     *         parameters of the connection. The remaining data
                     *         may be app data that we would drop without the
                     *         crypto setup. */
                    (ssl->options.side == WOLFSSL_CLIENT_END &&
                        ssl->options.serverState == SERVER_FINISHED_COMPLETE &&
                        ssl->options.handShakeState != HANDSHAKE_DONE)))
#endif
                ) {
                /* Shrink input buffer when we successfully finish record
                 * processing */
                if ((ret == 0) && ssl->buffers.inputBuffer.dynamicFlag)
                    ShrinkInputBuffer(ssl, NO_FORCED_FREE);
                return ret;
            }
            /* more messages per record */
            else if ((ssl->buffers.inputBuffer.idx - ssl->curStartIdx)
                    < ssl->curSize) {
                WOLFSSL_MSG("More messages in record");

                ssl->options.processReply = runProcessingOneMessage;

                if (IsEncryptionOn(ssl, 0)) {
                    /* With encryption on, we advance the index by the value
                     * of ssl->keys.padSz. Since padding only appears once, we
                     * only can do this at the end of record parsing. We have to
                     * reset the index to the start of the next message here. */
                    if (ssl->buffers.inputBuffer.idx >= ssl->keys.padSz) {
                        ssl->buffers.inputBuffer.idx -= ssl->keys.padSz;
                    }
                    else {
                        WOLFSSL_MSG("\tBuffer advanced not enough error");
                        WOLFSSL_ERROR_VERBOSE(FATAL_ERROR);
                        return FATAL_ERROR;
                    }
                }
            }
            /* more records */
            else {
                WOLFSSL_MSG("More records in input");
            }
#ifdef WOLFSSL_ASYNC_CRYPT
            /* We are setup to read next message/record but we had an error
             * (probably WC_PENDING_E) so return that so it can be handled
             * by higher layers. */
            if (ret != 0)
                return ret;
#endif
#if defined(WOLFSSL_DTLS13) || defined(HAVE_SECURE_RENEGOTIATION)
            /* Signal to user that we have application data ready to read */
            if (ret == WC_NO_ERR_TRACE(APP_DATA_READY))
                return ret;
#endif
            /* It is safe to shrink the input buffer here now. local vars will
             * be reset to the new starting value. */
            if (ret == 0 && ssl->buffers.inputBuffer.dynamicFlag)
                ShrinkInputBuffer(ssl, NO_FORCED_FREE);
            continue;
        default:
            WOLFSSL_MSG("Bad process input state, programming error");
            WOLFSSL_ERROR_VERBOSE(INPUT_CASE_ERROR);
            return INPUT_CASE_ERROR;
        }
    }
}

int ProcessReply(WOLFSSL* ssl)
{
    return ProcessReplyEx(ssl, 0);
}

int ProcessReplyEx(WOLFSSL* ssl, int allowSocketErr)
{
    int ret;

    ret = DoProcessReplyEx(ssl, allowSocketErr);

#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
    if (ssl->options.dtls) {
        /* Don't clear pending peer if we are going to re-enter
         * DoProcessReplyEx */
        if (ret != WC_NO_ERR_TRACE(WANT_READ)
#ifdef WOLFSSL_ASYNC_CRYPT
                && ret != WC_NO_ERR_TRACE(WC_PENDING_E)
#endif
            ) {
            dtlsClearPeer(&ssl->buffers.dtlsCtx.pendingPeer);
            ssl->buffers.dtlsCtx.processingPendingRecord = 0;
        }
    }
#endif

    return ret;
}

#if !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS) || \
             (defined(WOLFSSL_TLS13) && defined(WOLFSSL_TLS13_MIDDLEBOX_COMPAT))
int SendChangeCipher(WOLFSSL* ssl)
{
    byte              *output;
    int                sendSz = RECORD_HEADER_SZ + ENUM_LEN;
    int                idx    = RECORD_HEADER_SZ;
    int                ret;

    #ifdef OPENSSL_EXTRA
    ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
    if (ssl->options.side == WOLFSSL_SERVER_END){
        ssl->options.serverState = SERVER_CHANGECIPHERSPEC_COMPLETE;
        if (ssl->CBIS != NULL)
            ssl->CBIS(ssl, WOLFSSL_CB_ACCEPT_LOOP, WOLFSSL_SUCCESS);
    }
    else {
        ssl->options.clientState =
            CLIENT_CHANGECIPHERSPEC_COMPLETE;
        if (ssl->CBIS != NULL)
            ssl->CBIS(ssl, WOLFSSL_CB_CONNECT_LOOP, WOLFSSL_SUCCESS);
    }
    #endif

    #ifdef WOLFSSL_DTLS
        if (ssl->options.dtls) {
            sendSz += DTLS_RECORD_EXTRA;
            idx    += DTLS_RECORD_EXTRA;
        }
    #endif

    /* are we in scr */
    if (IsEncryptionOn(ssl, 1) && ssl->options.handShakeDone) {
        sendSz += MAX_MSG_EXTRA;
    }

    /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
     * is not advanced yet */
    ssl->options.buildingMsg = 1;

    /* check for available size */
    if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
        return ret;

    /* get output buffer */
    output = GetOutputBuffer(ssl);
    if (output == NULL)
        return BUFFER_E;

    AddRecordHeader(output, 1, change_cipher_spec, ssl, CUR_ORDER);

    output[idx] = 1;             /* turn it on */

    if (IsEncryptionOn(ssl, 1) && ssl->options.handShakeDone) {
        byte input[ENUM_LEN];
        int  inputSz = ENUM_LEN;

        input[0] = 1;  /* turn it on */
    #ifdef WOLFSSL_DTLS
        if (IsDtlsNotSctpMode(ssl) &&
                (ret = DtlsMsgPoolSave(ssl, input, (word32)inputSz, change_cipher_hs)) != 0) {
            return ret;
        }
    #endif
        sendSz = BuildMessage(ssl, output, sendSz, input, inputSz,
                              change_cipher_spec, 0, 0, 0, CUR_ORDER);
        if (sendSz < 0) {
            return sendSz;
        }
    }
    #ifdef WOLFSSL_DTLS
    else {
        if (IsDtlsNotSctpMode(ssl)) {
            if ((ret = DtlsMsgPoolSave(ssl, output, (word32)sendSz, change_cipher_hs)) != 0)
                return ret;
            DtlsSEQIncrement(ssl, CUR_ORDER);
        }
    }
    #endif
    #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
        if (ssl->hsInfoOn) AddPacketName(ssl, "ChangeCipher");
        if (ssl->toInfoOn) {
            ret = AddPacketInfo(ssl, "ChangeCipher", change_cipher_spec, output,
                    sendSz, WRITE_PROTO, 0, ssl->heap);
            if (ret != 0)
                return ret;
        }
    #endif
    ssl->buffers.outputBuffer.length += (word32)sendSz;

#ifdef WOLFSSL_TLS13
    if (!ssl->options.tls1_3)
#endif
    {
        /* setup encrypt keys */
        if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0)
            return ret;

    #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
        ssl->options.startedETMWrite = ssl->options.encThenMac;
    #endif
    }

    ssl->options.buildingMsg = 0;

    if (ssl->options.groupMessages)
        return 0;
    #if defined(WOLFSSL_DTLS) && !defined(WOLFSSL_DEBUG_DTLS)
    else if (ssl->options.dtls) {
        /* If using DTLS, force the ChangeCipherSpec message to be in the
         * same datagram as the finished message. */
        return 0;
    }
    #endif
    else
        return SendBuffered(ssl);
}
#endif /* !WOLFSSL_NO_TLS12 || !NO_OLD_TLS ||
        * (WOLFSSL_TLS13 && WOLFSSL_TLS13_MIDDLEBOX_COMPAT) */


#if !defined(NO_OLD_TLS) && !defined(WOLFSSL_AEAD_ONLY)
static int SSL_hmac(WOLFSSL* ssl, byte* digest, const byte* in, word32 sz,
                    int padLen, int content, int verify, int epochOrder)
{
    byte   result[WC_MAX_DIGEST_SIZE];
    word32 digestSz = ssl->specs.hash_size;            /* actual sizes */
    word32 padSz    = ssl->specs.pad_size;
    int    ret      = 0;

    wc_Md5 md5;
    wc_Sha sha;

    /* data */
    byte seq[SEQ_SZ];
    byte conLen[ENUM_LEN + LENGTH_SZ];     /* content & length */
    const byte* macSecret = NULL;

    (void)padLen;

#ifdef HAVE_FUZZER
    if (ssl->fuzzerCb)
        ssl->fuzzerCb(ssl, in, sz, FUZZ_HMAC, ssl->fuzzerCtx);
#endif

#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls)
        macSecret = wolfSSL_GetDtlsMacSecret(ssl, verify, epochOrder);
    else
        macSecret = wolfSSL_GetMacSecret(ssl, verify);
#else
    macSecret = wolfSSL_GetMacSecret(ssl, verify);
#endif

    XMEMSET(seq, 0, SEQ_SZ);
    conLen[0] = (byte)content;
    c16toa((word16)sz, &conLen[ENUM_LEN]);
    WriteSEQ(ssl, epochOrder, seq);

    if (ssl->specs.mac_algorithm == md5_mac) {
        ret =  wc_InitMd5_ex(&md5, ssl->heap, ssl->devId);
        if (ret != 0)
            return ret;

        /* inner */
        ret =  wc_Md5Update(&md5, macSecret, digestSz);
        ret |= wc_Md5Update(&md5, PAD1, padSz);
        ret |= wc_Md5Update(&md5, seq, SEQ_SZ);
        ret |= wc_Md5Update(&md5, conLen, sizeof(conLen));
        /* in buffer */
        ret |= wc_Md5Update(&md5, in, sz);
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }
        ret = wc_Md5Final(&md5, result);
    #ifdef WOLFSSL_ASYNC_CRYPT
        /* TODO: Make non-blocking */
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            ret = wc_AsyncWait(ret, &md5.asyncDev, WC_ASYNC_FLAG_NONE);
        }
    #endif
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }

        /* outer */
        ret =  wc_Md5Update(&md5, macSecret, digestSz);
        ret |= wc_Md5Update(&md5, PAD2, padSz);
        ret |= wc_Md5Update(&md5, result, digestSz);
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }
        ret =  wc_Md5Final(&md5, digest);
    #ifdef WOLFSSL_ASYNC_CRYPT
        /* TODO: Make non-blocking */
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            ret = wc_AsyncWait(ret, &md5.asyncDev, WC_ASYNC_FLAG_NONE);
        }
    #endif
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }

        wc_Md5Free(&md5);
    }
    else if (ssl->specs.mac_algorithm == sha_mac) {
        ret =  wc_InitSha_ex(&sha, ssl->heap, ssl->devId);
        if (ret != 0)
            return ret;

        /* inner */
        ret =  wc_ShaUpdate(&sha, macSecret, digestSz);
        ret |= wc_ShaUpdate(&sha, PAD1, padSz);
        ret |= wc_ShaUpdate(&sha, seq, SEQ_SZ);
        ret |= wc_ShaUpdate(&sha, conLen, sizeof(conLen));
        /* in buffer */
        ret |= wc_ShaUpdate(&sha, in, sz);
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }
        ret = wc_ShaFinal(&sha, result);
    #ifdef WOLFSSL_ASYNC_CRYPT
        /* TODO: Make non-blocking */
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            ret = wc_AsyncWait(ret, &sha.asyncDev, WC_ASYNC_FLAG_NONE);
        }
    #endif
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }

        /* outer */
        ret =  wc_ShaUpdate(&sha, macSecret, digestSz);
        ret |= wc_ShaUpdate(&sha, PAD2, padSz);
        ret |= wc_ShaUpdate(&sha, result, digestSz);
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }
        ret =  wc_ShaFinal(&sha, digest);
    #ifdef WOLFSSL_ASYNC_CRYPT
        /* TODO: Make non-blocking */
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            ret = wc_AsyncWait(ret, &sha.asyncDev, WC_ASYNC_FLAG_NONE);
        }
    #endif
        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
            return VERIFY_MAC_ERROR;
        }

        wc_ShaFree(&sha);
    }
    else {
        WOLFSSL_ERROR_VERBOSE(VERIFY_MAC_ERROR);
        return VERIFY_MAC_ERROR;
    }
    return 0;
}
#endif /* !NO_OLD_TLS && !WOLFSSL_AEAD_ONLY */

#if !defined(NO_MD5) && !defined(NO_OLD_TLS)
static int BuildMD5_CertVerify(const WOLFSSL* ssl, byte* digest)
{
    int ret;
    byte md5_result[WC_MD5_DIGEST_SIZE];
#ifdef WOLFSSL_SMALL_STACK
    wc_Md5* md5 = (wc_Md5*)XMALLOC(sizeof(wc_Md5), ssl->heap,
        DYNAMIC_TYPE_HASHCTX);
#else
    wc_Md5  md5[1];
#endif
    XMEMSET(md5, 0, sizeof(wc_Md5));

    /* make md5 inner */
    ret = wc_Md5Copy(&ssl->hsHashes->hashMd5, md5); /* Save current position */
    if (ret == 0)
        ret = wc_Md5Update(md5, ssl->arrays->masterSecret, SECRET_LEN);
    if (ret == 0)
        ret = wc_Md5Update(md5, PAD1, PAD_MD5);
    if (ret == 0)
        ret = wc_Md5Final(md5, md5_result);

    /* make md5 outer */
    if (ret == 0) {
        ret = wc_InitMd5_ex(md5, ssl->heap, ssl->devId);
        if (ret == 0) {
            ret = wc_Md5Update(md5, ssl->arrays->masterSecret, SECRET_LEN);
            if (ret == 0)
                ret = wc_Md5Update(md5, PAD2, PAD_MD5);
            if (ret == 0)
                ret = wc_Md5Update(md5, md5_result, WC_MD5_DIGEST_SIZE);
            if (ret == 0)
                ret = wc_Md5Final(md5, digest);
            wc_Md5Free(md5);
        }
    }

    WC_FREE_VAR_EX(md5, ssl->heap, DYNAMIC_TYPE_HASHCTX);

    return ret;
}
#endif /* !NO_MD5 && !NO_OLD_TLS */

#if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                          defined(WOLFSSL_ALLOW_TLS_SHA1))
static int BuildSHA_CertVerify(const WOLFSSL* ssl, byte* digest)
{
    int ret;
    byte sha_result[WC_SHA_DIGEST_SIZE];
#ifdef WOLFSSL_SMALL_STACK
    wc_Sha* sha = (wc_Sha*)XMALLOC(sizeof(wc_Sha), ssl->heap,
        DYNAMIC_TYPE_HASHCTX);
#else
    wc_Sha  sha[1];
#endif
    XMEMSET(sha, 0, sizeof(wc_Sha));

    /* make sha inner */
    ret = wc_ShaCopy(&ssl->hsHashes->hashSha, sha); /* Save current position */
    if (ret == 0)
        ret = wc_ShaUpdate(sha, ssl->arrays->masterSecret, SECRET_LEN);
    if (ret == 0)
        ret = wc_ShaUpdate(sha, PAD1, PAD_SHA);
    if (ret == 0)
        ret = wc_ShaFinal(sha, sha_result);

    /* make sha outer */
    if (ret == 0) {
        ret = wc_InitSha_ex(sha, ssl->heap, ssl->devId);
        if (ret == 0) {
            ret = wc_ShaUpdate(sha, ssl->arrays->masterSecret, SECRET_LEN);
            if (ret == 0)
                ret = wc_ShaUpdate(sha, PAD2, PAD_SHA);
            if (ret == 0)
                ret = wc_ShaUpdate(sha, sha_result, WC_SHA_DIGEST_SIZE);
            if (ret == 0)
                ret = wc_ShaFinal(sha, digest);
            wc_ShaFree(sha);
        }
    }

    WC_FREE_VAR_EX(sha, ssl->heap, DYNAMIC_TYPE_HASHCTX);

    return ret;
}
#endif /* !NO_SHA && (!NO_OLD_TLS || WOLFSSL_ALLOW_TLS_SHA1) */

int BuildCertHashes(const WOLFSSL* ssl, Hashes* hashes)
{
    int ret = 0;

    (void)hashes;

    if (ssl->options.tls) {
    #if !defined(NO_MD5) && !defined(NO_OLD_TLS)
        ret = wc_Md5GetHash(&ssl->hsHashes->hashMd5, hashes->md5);
        if (ret != 0)
            return ret;
    #endif
    #if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                              defined(WOLFSSL_ALLOW_TLS_SHA1))
        ret = wc_ShaGetHash(&ssl->hsHashes->hashSha, hashes->sha);
        if (ret != 0)
            return ret;
    #endif
        if (IsAtLeastTLSv1_2(ssl)) {
            #ifndef NO_SHA256
                ret = wc_Sha256GetHash(&ssl->hsHashes->hashSha256,
                                       hashes->sha256);
                if (ret != 0)
                    return ret;
            #endif
            #ifdef WOLFSSL_SHA384
                ret = wc_Sha384GetHash(&ssl->hsHashes->hashSha384,
                                       hashes->sha384);
                if (ret != 0)
                    return ret;
            #endif
            #ifdef WOLFSSL_SHA512
                ret = wc_Sha512GetHash(&ssl->hsHashes->hashSha512,
                                       hashes->sha512);
                if (ret != 0)
                    return ret;
            #endif
            #ifdef WOLFSSL_SM3
                ret = wc_Sm3GetHash(&ssl->hsHashes->hashSm3,
                                       hashes->sm3);
                if (ret != 0)
                    return ret;
            #endif
        }
    }
    else {
    #if !defined(NO_MD5) && !defined(NO_OLD_TLS)
        ret = BuildMD5_CertVerify(ssl, hashes->md5);
        if (ret != 0)
            return ret;
    #endif
    #if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                              defined(WOLFSSL_ALLOW_TLS_SHA1))
        ret = BuildSHA_CertVerify(ssl, hashes->sha);
        if (ret != 0)
            return ret;
    #endif
    }

    return ret;
}

#ifndef WOLFSSL_NO_TLS12
void FreeBuildMsgArgs(WOLFSSL* ssl, BuildMsgArgs* args)
{
    (void)ssl;
    if (args
#ifdef WOLFSSL_ASYNC_CRYPT
            && ssl->options.buildArgsSet
#endif
        ) {
        /* only free the IV if it was dynamically allocated */
        if (args->iv && (args->iv != args->staticIvBuffer)) {
            XFREE(args->iv, ssl->heap, DYNAMIC_TYPE_SALT);
        }
    }
#ifdef WOLFSSL_ASYNC_CRYPT
    ssl->options.buildArgsSet = 0;
#endif
}
#endif

/* Build SSL Message, encrypted */
int BuildMessage(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
             int inSz, int type, int hashOutput, int sizeOnly, int asyncOkay,
             int epochOrder)
{
#ifndef WOLFSSL_NO_TLS12
    int ret;
    BuildMsgArgs* args;
    BuildMsgArgs  lcl_args;
#endif

#ifdef WOLFSSL_DTLS_CID
    byte cidSz = 0;
#endif

    WOLFSSL_ENTER("BuildMessage");

    if (ssl == NULL) {
        return BAD_FUNC_ARG;
    }
    /* catch mistaken sizeOnly parameter */
    if (!sizeOnly && (output == NULL || input == NULL) ) {
        return BAD_FUNC_ARG;
    }
    if (sizeOnly && (output || input) ) {
        return BAD_FUNC_ARG;
    }

    (void)epochOrder;

#if defined(WOLFSSL_NO_TLS12) && defined(WOLFSSL_TLS13)
    /* TLS v1.3 only */
    return BuildTls13Message(ssl, output, outSz, input, inSz, type,
                                               hashOutput, sizeOnly, asyncOkay);
#else
    /* TLS v1.2 or v1.3 */
#ifdef WOLFSSL_TLS13
    if (ssl->options.tls1_3) {
        return BuildTls13Message(ssl, output, outSz, input, inSz, type,
                                 hashOutput, sizeOnly, asyncOkay);
    }
#endif

#ifndef WOLFSSL_NO_TLS12
#ifdef WOLFSSL_ASYNC_CRYPT
    ret = WC_NO_PENDING_E;
    if (asyncOkay) {
        if (ssl->async == NULL) {
            return BAD_FUNC_ARG;
        }
        args = &ssl->async->buildArgs;

        ret = wolfSSL_AsyncPop(ssl, &ssl->options.buildMsgState);
        if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
            /* Check for error */
            if (ret < 0)
                goto exit_buildmsg;
        }
    }
    else
#endif
    {
        args = &lcl_args;
    }

    /* Reset state */
#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_NO_PENDING_E))
#endif
    {
        ret = 0;
#ifdef WOLFSSL_ASYNC_CRYPT
        ssl->options.buildArgsSet = 1;
#endif
        ssl->options.buildMsgState = BUILD_MSG_BEGIN;
        XMEMSET(args, 0, sizeof(BuildMsgArgs));

        args->sz = RECORD_HEADER_SZ + (word32)inSz;
        args->idx  = RECORD_HEADER_SZ;
        args->headerSz = RECORD_HEADER_SZ;
        args->type = (byte)type;
    }

    switch (ssl->options.buildMsgState) {
        case BUILD_MSG_BEGIN:
        {
        #if defined(WOLFSSL_DTLS) && defined(HAVE_SECURE_RENEGOTIATION)
            if (ssl->options.dtls && DtlsSCRKeysSet(ssl)) {
                /* For epochs >1 the current cipher parameters are located in
                 * ssl->secure_renegotiation->tmp_keys. Previous cipher
                 * parameters and for epoch 1 use ssl->keys */
                switch (epochOrder) {
                case PREV_ORDER:
                    if (ssl->encrypt.src != KEYS) {
                        ssl->secure_renegotiation->cache_status =
                                SCR_CACHE_NULL;
                        if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY)) != 0)
                            ERROR_OUT(ret, exit_buildmsg);
                    }
                    break;
                case CUR_ORDER:
                    if (ssl->keys.dtls_epoch ==
                            ssl->secure_renegotiation->tmp_keys.dtls_epoch) {
                        if (ssl->encrypt.src != SCR) {
                            ssl->secure_renegotiation->cache_status =
                                    SCR_CACHE_NEEDED;
                            if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY))
                                    != 0)
                                ERROR_OUT(ret, exit_buildmsg);
                        }
                    }
                    else {
                        if (ssl->encrypt.src != KEYS) {
                            ssl->secure_renegotiation->cache_status =
                                    SCR_CACHE_NULL;
                            if ((ret = SetKeysSide(ssl, ENCRYPT_SIDE_ONLY))
                                    != 0)
                                ERROR_OUT(ret, exit_buildmsg);
                        }
                    }
                    break;
                default:
                    WOLFSSL_MSG("BuildMessage only supports PREV_ORDER and "
                                "CUR_ORDER");
                    ERROR_OUT(BAD_FUNC_ARG, exit_buildmsg);
                }
            }
        #endif

            ssl->options.buildMsgState = BUILD_MSG_SIZE;
        }
        FALL_THROUGH;
        case BUILD_MSG_SIZE:
        {
            args->digestSz = ssl->specs.hash_size;
        #ifdef HAVE_TRUNCATED_HMAC
            if (ssl->truncated_hmac)
                args->digestSz = min(TRUNCATED_HMAC_SZ, args->digestSz);
        #endif
            args->sz += args->digestSz;

        #ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
                args->sz       += DTLS_RECORD_EXTRA;
                args->idx      += DTLS_RECORD_EXTRA;
                args->headerSz += DTLS_RECORD_EXTRA;
        #ifdef WOLFSSL_DTLS_CID
                if ((cidSz = DtlsGetCidTxSize(ssl)) > 0) {
                    args->sz       += cidSz;
                    args->idx      += cidSz;
                    args->headerSz += cidSz;
                    args->sz++; /* real_type. no padding. */
                }
        #endif
            }
        #endif

        #ifndef WOLFSSL_AEAD_ONLY
            if (ssl->specs.cipher_type == block) {
                word32 blockSz = ssl->specs.block_size;

                if (blockSz == 0) {
                    WOLFSSL_MSG("Invalid block size with block cipher type");
                    ERROR_OUT(BAD_STATE_E, exit_buildmsg);
                }

                if (ssl->options.tls1_1) {
                    args->ivSz = blockSz;
                    args->sz  += args->ivSz;

                    if (args->ivSz > MAX_IV_SZ)
                        ERROR_OUT(BUFFER_E, exit_buildmsg);
                }
                args->sz += 1;       /* pad byte */
            #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
                if (ssl->options.startedETMWrite) {
                    args->pad = (args->sz - args->headerSz -
                                                      args->digestSz) % blockSz;
                }
                else
            #endif
                {
                    args->pad = (args->sz - args->headerSz) % blockSz;
                }
                if (args->pad != 0)
                    args->pad = blockSz - args->pad;
                args->sz += args->pad;
            }
        #endif /* WOLFSSL_AEAD_ONLY */

        #ifdef HAVE_AEAD
            if (ssl->specs.cipher_type == aead) {
                if (ssl->specs.bulk_cipher_algorithm != wolfssl_chacha)
                    args->ivSz = AESGCM_EXP_IV_SZ;

                args->sz += (args->ivSz + ssl->specs.aead_mac_size - args->digestSz);
            }
        #endif

            /* done with size calculations */
            if (sizeOnly)
                goto exit_buildmsg;

            if (args->sz > (word32)outSz) {
                WOLFSSL_MSG("Oops, want to write past output buffer size");
                ERROR_OUT(BUFFER_E, exit_buildmsg);
            }

            if (args->ivSz > 0) {
                if (args->ivSz > sizeof(args->staticIvBuffer)) {
                    args->iv = (byte*)XMALLOC(args->ivSz, ssl->heap,
                                              DYNAMIC_TYPE_SALT);
                    if (args->iv == NULL) {
                        ERROR_OUT(MEMORY_E, exit_buildmsg);
                    }
                }
                else {
                    args->iv = args->staticIvBuffer;
                }

                ret = wc_RNG_GenerateBlock(ssl->rng, args->iv, args->ivSz);
                if (ret != 0)
                    goto exit_buildmsg;
            }
#if !defined(NO_PUBLIC_GCM_SET_IV) && \
    ((defined(HAVE_FIPS) || defined(HAVE_SELFTEST)) && \
    (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)) && \
    defined(HAVE_AEAD))
            if (ssl->specs.cipher_type == aead) {
                if (ssl->specs.bulk_cipher_algorithm != wolfssl_chacha)
                    XMEMCPY(args->iv, ssl->keys.aead_exp_IV, AESGCM_EXP_IV_SZ);
            }
#endif

            args->size = (word16)(args->sz - args->headerSz);    /* include mac and digest */

#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
            if (ssl->options.dtls && DtlsGetCidTxSize(ssl) > 0)
                args->type = dtls12_cid;
#endif
            AddRecordHeader(output, args->size, args->type, ssl, epochOrder);

            /* write to output */
            if (args->ivSz > 0) {
                XMEMCPY(output + args->idx, args->iv,
                                        min(args->ivSz, MAX_IV_SZ));
                args->idx += min(args->ivSz, MAX_IV_SZ);
            }
            XMEMCPY(output + args->idx, input, (size_t)(inSz));
            args->idx += (word32)inSz;
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
            if (ssl->options.dtls && DtlsGetCidTxSize(ssl) > 0) {
                output[args->idx++] = (byte)type; /* type goes after input */
                inSz++;
            }
#endif
            /* Make sure we don't access input anymore as inSz may have been
             * incremented */
            input = NULL;

            ssl->options.buildMsgState = BUILD_MSG_HASH;
        }
        FALL_THROUGH;
        case BUILD_MSG_HASH:
        {
            /* done with size calculations */
            if (sizeOnly)
                goto exit_buildmsg;

            if (type == handshake && hashOutput) {
                ret = HashOutput(ssl, output,
                    (int)(args->headerSz + (word32)inSz), (int)args->ivSz);
                if (ret != 0)
                    goto exit_buildmsg;
            }
        #ifndef WOLFSSL_AEAD_ONLY
            if (ssl->specs.cipher_type == block) {
                word32 tmpIdx;
                word32 i;

            #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
                if (ssl->options.startedETMWrite)
                    tmpIdx = args->idx;
                else
            #endif
                    tmpIdx = args->idx + args->digestSz;

                for (i = 0; i <= args->pad; i++)
                    output[tmpIdx++] = (byte)args->pad; /* pad byte gets pad value */
            }
        #endif

            ssl->options.buildMsgState = BUILD_MSG_VERIFY_MAC;
        }
        FALL_THROUGH;
        case BUILD_MSG_VERIFY_MAC:
        {
            /* done with size calculations */
            if (sizeOnly)
                goto exit_buildmsg;

            /* User Record Layer Callback handling */
    #ifdef ATOMIC_USER
        #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
            if (ssl->options.startedETMWrite) {
                if (ssl->ctx->EncryptMacCb) {
                    ret = ssl->ctx->EncryptMacCb(ssl, output + args->idx +
                                                 args->pad + 1, args->type, 0,
                                                 output + args->headerSz,
                                                 output + args->headerSz,
                                                 args->size - args->digestSz,
                                                 ssl->MacEncryptCtx);
                    goto exit_buildmsg;
                }
            }
            else
        #endif
            {
                if (ssl->ctx->MacEncryptCb) {
                    ret = ssl->ctx->MacEncryptCb(ssl, output + args->idx,
                                    output + args->headerSz + args->ivSz,
                                    (unsigned int)inSz, args->type, 0,
                                    output + args->headerSz,
                                    output + args->headerSz, args->size,
                                    ssl->MacEncryptCtx);
                    goto exit_buildmsg;
                }
            }
    #endif

        #ifndef WOLFSSL_AEAD_ONLY
            if (ssl->specs.cipher_type != aead
            #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
                                               && !ssl->options.startedETMWrite
            #endif
                ) {
            #ifdef HAVE_TRUNCATED_HMAC
                if (ssl->truncated_hmac &&
                                        ssl->specs.hash_size > args->digestSz) {
                    WC_DECLARE_VAR(hmac, byte, WC_MAX_DIGEST_SIZE, 0);

                    WC_ALLOC_VAR_EX(hmac, byte, WC_MAX_DIGEST_SIZE, ssl->heap,
                        DYNAMIC_TYPE_DIGEST,
                        ERROR_OUT(MEMORY_E,exit_buildmsg));

                    ret = ssl->hmac(ssl, hmac,
                                     output + args->headerSz + args->ivSz,
                                     (word32)inSz, -1, args->type, 0,
                                     epochOrder);
                    XMEMCPY(output + args->idx, hmac, args->digestSz);

                    WC_FREE_VAR_EX(hmac, ssl->heap, DYNAMIC_TYPE_DIGEST);
                }
                else
            #endif
                {
                    ret = ssl->hmac(ssl, output + args->idx, output +
                                args->headerSz + args->ivSz, (word32)inSz, -1,
                                args->type, 0, epochOrder);
                }
            }
        #endif /* WOLFSSL_AEAD_ONLY */
            if (ret != 0)
                goto exit_buildmsg;

            ssl->options.buildMsgState = BUILD_MSG_ENCRYPT;
        }
        FALL_THROUGH;
        case BUILD_MSG_ENCRYPT:
        {
            /* done with size calculations */
            if (sizeOnly)
                goto exit_buildmsg;

            {
    #if defined(HAVE_SECURE_RENEGOTIATION) && defined(WOLFSSL_DTLS)
            /* If we want the PREV_ORDER then modify CUR_ORDER sequence number
             * for all encryption algos that use it for encryption parameters */
            word16 dtls_epoch = 0;
            word16 dtls_sequence_number_hi = 0;
            word32 dtls_sequence_number_lo = 0;
            int swap_seq = ssl->options.dtls && epochOrder == PREV_ORDER &&
                    DtlsUseSCRKeys(ssl);
            if (swap_seq) {
                dtls_epoch = ssl->keys.dtls_epoch;
                dtls_sequence_number_hi = ssl->keys.dtls_sequence_number_hi;
                dtls_sequence_number_lo = ssl->keys.dtls_sequence_number_lo;
                ssl->keys.dtls_epoch--;
                ssl->keys.dtls_sequence_number_hi =
                        ssl->keys.dtls_prev_sequence_number_hi;
                ssl->keys.dtls_sequence_number_lo =
                        ssl->keys.dtls_prev_sequence_number_lo;
            }
    #endif

#ifdef WOLFSSL_THREADED_CRYPT
    if (asyncOkay) {
        WOLFSSL_MSG("Not encrypting\n");
        /* make sure build message state is reset */
        ssl->options.buildMsgState = BUILD_MSG_BEGIN;

        /* return sz on success */
        if (ret == 0) {
            ret = args->sz;
        }
        else {
            WOLFSSL_ERROR_VERBOSE(ret);
        }

        /* Final cleanup */
        FreeBuildMsgArgs(ssl, args);

        return ret;
    }
    else
#endif
    {
    #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
            if (ssl->options.startedETMWrite) {
                ret = Encrypt(ssl, output + args->headerSz,
                                          output + args->headerSz,
                                          (word16)(args->size - args->digestSz),
                                          asyncOkay, args->type);
            }
            else
    #endif
            {
                ret = Encrypt(ssl, output + args->headerSz,
                                output + args->headerSz, args->size, asyncOkay,
                                args->type);
            }
    #if defined(HAVE_SECURE_RENEGOTIATION) && defined(WOLFSSL_DTLS)
            /* Restore sequence numbers */
            if (swap_seq) {
                ssl->keys.dtls_epoch = dtls_epoch;
                ssl->keys.dtls_sequence_number_hi = dtls_sequence_number_hi;
                ssl->keys.dtls_sequence_number_lo = dtls_sequence_number_lo;
            }
    #endif
    }
            }

            if (ret != 0) {
            #ifdef WOLFSSL_ASYNC_CRYPT
                if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
            #endif
                {
                    /* Zeroize plaintext. */
            #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
                    if (ssl->options.startedETMWrite) {
                        ForceZero(output + args->headerSz,
                            (word16)(args->size - args->digestSz));
                    }
                    else
            #endif
                    {
                        ForceZero(output + args->headerSz, (word16)args->size);
                    }
                }
                goto exit_buildmsg;
            }
            ssl->options.buildMsgState = BUILD_MSG_ENCRYPTED_VERIFY_MAC;
        }
        FALL_THROUGH;
        case BUILD_MSG_ENCRYPTED_VERIFY_MAC:
        {
            /* done with size calculations */
            if (sizeOnly)
                goto exit_buildmsg;

        #if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
            if (ssl->options.startedETMWrite) {
                WOLFSSL_MSG("Calculate MAC of Encrypted Data");

            #ifdef HAVE_TRUNCATED_HMAC
                if (ssl->truncated_hmac &&
                                        ssl->specs.hash_size > args->digestSz) {
                    WC_DECLARE_VAR(hmac, byte, WC_MAX_DIGEST_SIZE, 0);

                    WC_ALLOC_VAR_EX(hmac, byte, WC_MAX_DIGEST_SIZE, ssl->heap,
                        DYNAMIC_TYPE_DIGEST,
                        ERROR_OUT(MEMORY_E,exit_buildmsg));

                    ret = ssl->hmac(ssl, hmac, output + args->headerSz,
                                    args->ivSz + inSz + args->pad + 1, -1,
                                    args->type, 0, epochOrder);
                    XMEMCPY(output + args->idx + args->pad + 1, hmac,
                                                                args->digestSz);

                    WC_FREE_VAR_EX(hmac, ssl->heap, DYNAMIC_TYPE_DIGEST);
                }
                else
            #endif
                {
                    ret = ssl->hmac(ssl, output + args->idx + args->pad + 1,
                                    output + args->headerSz,
                                    args->ivSz + (word32)inSz + args->pad + 1,
                                    -1, args->type, 0, epochOrder);
                }
            }
        #endif /* HAVE_ENCRYPT_THEN_MAC && !WOLFSSL_AEAD_ONLY */
        }
        FALL_THROUGH;
        default:
            break;
    }

exit_buildmsg:

    WOLFSSL_LEAVE("BuildMessage", ret);

#ifdef WOLFSSL_ASYNC_CRYPT
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        return ret;
    }
#endif

    /* make sure build message state is reset */
    ssl->options.buildMsgState = BUILD_MSG_BEGIN;

    #ifdef WOLFSSL_DTLS
        if (ret == 0 && ssl->options.dtls && !sizeOnly)
            DtlsSEQIncrement(ssl, epochOrder);
    #endif

    /* return sz on success */
    if (ret == 0) {
        ret = (int)args->sz;
    }
    else {
        WOLFSSL_ERROR_VERBOSE(ret);
    }

    /* Final cleanup */
    FreeBuildMsgArgs(ssl, args);
    return ret;
#else
    (void)outSz;
    (void)inSz;
    (void)type;
    (void)hashOutput;
    (void)asyncOkay;
    return NOT_COMPILED_IN;
#endif /* !WOLFSSL_NO_TLS12 */
#endif
}

#ifndef WOLFSSL_NO_TLS12

int SendFinished(WOLFSSL* ssl)
{
    int              sendSz,
                     finishedSz = ssl->options.tls ? TLS_FINISHED_SZ :
                                                     FINISHED_SZ;
    byte             input[FINISHED_SZ + DTLS_HANDSHAKE_HEADER_SZ];  /* max */
    byte            *output;
    Hashes*          hashes;
    int              ret;
    int              headerSz = HANDSHAKE_HEADER_SZ;
    int              outputSz;

    WOLFSSL_START(WC_FUNC_FINISHED_SEND);
    WOLFSSL_ENTER("SendFinished");

    /* check for available size */
    outputSz = sizeof(input) + MAX_MSG_EXTRA;
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
    if (ssl->options.dtls) {
        byte cidSz = 0;
        if ((cidSz = DtlsGetCidTxSize(ssl)) > 0)
            outputSz += cidSz + 1; /* +1 for inner content type */
    }
#endif

    /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
     * is not advanced yet */
    ssl->options.buildingMsg = 1;

    if ((ret = CheckAvailableSize(ssl, outputSz)) != 0)
        return ret;

    #ifdef WOLFSSL_DTLS
        if (ssl->options.dtls) {
            headerSz += DTLS_HANDSHAKE_EXTRA;
            ssl->keys.dtls_epoch++;
            ssl->keys.dtls_prev_sequence_number_hi =
                    ssl->keys.dtls_sequence_number_hi;
            ssl->keys.dtls_prev_sequence_number_lo =
                    ssl->keys.dtls_sequence_number_lo;
            ssl->keys.dtls_sequence_number_hi = 0;
            ssl->keys.dtls_sequence_number_lo = 0;
        }
    #endif

    /* get output buffer */
    output = GetOutputBuffer(ssl);
    AddHandShakeHeader(input, (word32)finishedSz, 0,
            (word32)finishedSz, finished, ssl);

    /* make finished hashes */
    hashes = (Hashes*)&input[headerSz];
    ret = BuildFinished(ssl, hashes, ssl->options.side == WOLFSSL_CLIENT_END ?
                                                 kTlsClientStr : kTlsServerStr);
    if (ret != 0) return ret;

#ifdef HAVE_SECURE_RENEGOTIATION
    if (ssl->secure_renegotiation) {
        if (ssl->options.side == WOLFSSL_CLIENT_END)
            XMEMCPY(ssl->secure_renegotiation->client_verify_data, hashes,
                    TLS_FINISHED_SZ);
        else
            XMEMCPY(ssl->secure_renegotiation->server_verify_data, hashes,
                    TLS_FINISHED_SZ);
    }
#endif
#ifdef WOLFSSL_HAVE_TLS_UNIQUE
    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        XMEMCPY(ssl->clientFinished,
                hashes, TLS_FINISHED_SZ);
        ssl->clientFinished_len = TLS_FINISHED_SZ;
    }
    else {
        XMEMCPY(ssl->serverFinished,
                hashes, TLS_FINISHED_SZ);
        ssl->serverFinished_len = TLS_FINISHED_SZ;
    }
#endif

    #ifdef WOLFSSL_DTLS
        if (IsDtlsNotSctpMode(ssl)) {
            if ((ret = DtlsMsgPoolSave(ssl, input, headerSz + finishedSz,
                                                              finished)) != 0) {
                return ret;
            }
        }
    #endif

    ssl->keys.encryptionOn = 1;
    sendSz = BuildMessage(ssl, output, outputSz, input, headerSz + finishedSz,
                                                 handshake, 1, 0, 0, CUR_ORDER);
    if (sendSz < 0)
        return BUILD_MSG_ERROR;

    if (!ssl->options.resuming) {
        SetupSession(ssl);
#ifndef NO_SESSION_CACHE
        AddSession(ssl);
#endif
        if (ssl->options.side == WOLFSSL_SERVER_END) {
        #ifdef OPENSSL_EXTRA
            ssl->options.serverState = SERVER_FINISHED_COMPLETE;
            ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
            if (ssl->CBIS != NULL)
                ssl->CBIS(ssl, WOLFSSL_CB_HANDSHAKE_DONE, WOLFSSL_SUCCESS);
        #endif
            ssl->options.handShakeState = HANDSHAKE_DONE;
            ssl->options.handShakeDone  = 1;
#ifdef HAVE_SECURE_RENEGOTIATION
            ssl->options.resumed = ssl->options.resuming;
#endif
        }
    }
    else {
        if (ssl->options.side == WOLFSSL_CLIENT_END) {
        #ifdef OPENSSL_EXTRA
            ssl->options.clientState = CLIENT_FINISHED_COMPLETE;
            ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
            if (ssl->CBIS != NULL)
                ssl->CBIS(ssl, WOLFSSL_CB_HANDSHAKE_DONE, WOLFSSL_SUCCESS);
        #endif
            ssl->options.handShakeState = HANDSHAKE_DONE;
            ssl->options.handShakeDone  = 1;
#ifdef HAVE_SECURE_RENEGOTIATION
            ssl->options.resumed = ssl->options.resuming;
#endif
        }
    }

    #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
        if (ssl->hsInfoOn) AddPacketName(ssl, "Finished");
        if (ssl->toInfoOn) {
            ret = AddPacketInfo(ssl, "Finished", handshake, output, sendSz,
                          WRITE_PROTO, 0, ssl->heap);
            if (ret != 0)
                return ret;
        }
    #endif

    ssl->buffers.outputBuffer.length += (word32)sendSz;

    ret = SendBuffered(ssl);

    ssl->options.buildingMsg = 0;

#ifdef WOLFSSL_DTLS
    if ((!ssl->options.resuming &&
            ssl->options.side == WOLFSSL_SERVER_END) ||
        (ssl->options.resuming &&
            ssl->options.side == WOLFSSL_CLIENT_END)) {
        ssl->keys.dtls_handshake_number = 0;
        ssl->keys.dtls_expected_peer_handshake_number = 0;
    }
#endif

    WOLFSSL_LEAVE("SendFinished", ret);
    WOLFSSL_END(WC_FUNC_FINISHED_SEND);

    return ret;
}
#endif /* WOLFSSL_NO_TLS12 */
#endif /* !NO_TLS */

#ifndef NO_WOLFSSL_SERVER
#if (!defined(WOLFSSL_NO_TLS12) && \
        (defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
         defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2))) || \
    (defined(WOLFSSL_TLS13) && defined(HAVE_CERTIFICATE_STATUS_REQUEST))
/* Parses and decodes the certificate then initializes "request". In the case
 * of !ssl->buffers.weOwnCert, ssl->ctx->certOcspRequest gets set to "request".
 *
 * Returns 0 on success
 */
int CreateOcspRequest(WOLFSSL* ssl, OcspRequest* request,
                             DecodedCert* cert, byte* certData, word32 length,
                             byte *ctxOwnsRequest)
{
    int ret;

    if (request != NULL)
        XMEMSET(request, 0, sizeof(OcspRequest));

    if (ctxOwnsRequest!= NULL)
        *ctxOwnsRequest = 0;

    InitDecodedCert(cert, certData, length, ssl->heap);
    /* TODO: Setup async support here */
    ret = ParseCertRelative(cert, CERT_TYPE, NO_VERIFY, SSL_CM(ssl), NULL);
    if (ret != 0)
        WOLFSSL_MSG("ParseCert failed");
    if (ret == 0)
        ret = InitOcspRequest(request, cert, 0, ssl->heap);
    if (ret == 0) {
        /* make sure ctx OCSP request is updated */
        if (!ssl->buffers.weOwnCert && SSL_CM(ssl) != NULL) {
            wolfSSL_Mutex* ocspLock = &SSL_CM(ssl)->ocsp_stapling->ocspLock;
            if (wc_LockMutex(ocspLock) == 0) {
                if (ssl->ctx->certOcspRequest == NULL) {
                    ssl->ctx->certOcspRequest = request;
                    if (ctxOwnsRequest!= NULL)
                        *ctxOwnsRequest = 1;
                }
                wc_UnLockMutex(ocspLock);
            }
        }
    }

    FreeDecodedCert(cert);

    return ret;
}


/* Creates OCSP response and places it in variable "response". Memory
 * management for "buffer* response" is up to the caller.
 *
 * Also creates an OcspRequest in the case that ocspRequest is null or that
 * ssl->buffers.weOwnCert is set. In those cases managing ocspRequest free'ing
 * is up to the caller. NOTE: in OcspCreateRequest ssl->ctx->certOcspRequest can
 * be set to point to "ocspRequest" and it then should not be free'd since
 * wolfSSL_CTX_free will take care of it.
 *
 * Returns 0 on success
 */
int CreateOcspResponse(WOLFSSL* ssl, OcspRequest** ocspRequest,
                       buffer* response)
{
    int          ret = 0;
    OcspRequest* request = NULL;
    byte createdRequest  = 0;
    byte ctxOwnsRequest = 0;

    if (ssl == NULL || ocspRequest == NULL || response == NULL)
        return BAD_FUNC_ARG;

    XMEMSET(response, 0, sizeof(*response));
    request = *ocspRequest;

    /* unable to fetch status. skip. */
    if (SSL_CM(ssl) == NULL || SSL_CM(ssl)->ocspStaplingEnabled == 0)
        return 0;

    if (request == NULL || ssl->buffers.weOwnCert) {
        DerBuffer* der = ssl->buffers.certificate;
            WC_DECLARE_VAR(cert, DecodedCert, 1, 0);

        /* unable to fetch status. skip. */
        if (der->buffer == NULL || der->length == 0)
            return 0;

        WC_ALLOC_VAR_EX(cert, DecodedCert, 1, ssl->heap, DYNAMIC_TYPE_DCERT,
            return MEMORY_E);
        request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), ssl->heap,
                                                     DYNAMIC_TYPE_OCSP_REQUEST);
        if (request == NULL)
            ret = MEMORY_E;

        createdRequest = 1;
        if (ret == 0) {
            ret = CreateOcspRequest(ssl, request, cert, der->buffer,
                      der->length, &ctxOwnsRequest);
        }

        if (ret != 0) {
            XFREE(request, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);
            request = NULL;
        }

        WC_FREE_VAR_EX(cert, ssl->heap, DYNAMIC_TYPE_DCERT);
    }

    if (ret == 0) {
        request->ssl = ssl;
        ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling, request, response,
                               ssl->heap);

        /* Suppressing, not critical */
        if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED) ||
            ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
            ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
            ret = 0;
        }
    }

    /* free request up if error case found otherwise return it */
    if (ret != 0 && createdRequest && !ctxOwnsRequest) {
        FreeOcspRequest(request);
        XFREE(request, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);
    }

    if (ret == 0)
        *ocspRequest = request;

    return ret;
}
#endif
#endif /* !NO_WOLFSSL_SERVER */

int cipherExtraData(WOLFSSL* ssl)
{
    int cipherExtra;
    /* Cipher data that may be added by BuildMessage */
    /* There is always an IV (expect for chacha). For AEAD ciphers,
     * there is the authentication tag (aead_mac_size). For block
     * ciphers we have the hash_size MAC on the message, and one
     * block size for possible padding. */
    if (ssl->specs.cipher_type == aead) {
        cipherExtra = ssl->specs.aead_mac_size;
        /* CHACHA does not have an explicit IV. */
        if (ssl->specs.bulk_cipher_algorithm != wolfssl_chacha) {
            cipherExtra += AESGCM_EXP_IV_SZ;
        }
    }
    else {
        cipherExtra = ssl->specs.iv_size + ssl->specs.block_size +
            ssl->specs.hash_size;
    }
    /* Add space needed for the CID */
#if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_CID)
    if (ssl->options.dtls) {
        byte cidSz = 0;
        if ((cidSz = DtlsGetCidTxSize(ssl)) > 0)
            cipherExtra += cidSz + 1; /* +1 for inner content type */
    }
#endif
    /* Sanity check so we don't ever return negative. */
    return cipherExtra > 0 ? cipherExtra : 0;
}

#ifndef WOLFSSL_NO_TLS12

static int BuildMsgOrHashOutput(WOLFSSL* ssl, byte* output, int* sendSz,
                                int inputSz, enum HandShakeType type,
                                const char *name)
{
    int ret = 0;
    if (IsEncryptionOn(ssl, 1)) {
        byte* input;
        int   recordHeaderSz = RECORD_HEADER_SZ;

        if (ssl->options.dtls)
            recordHeaderSz += DTLS_RECORD_EXTRA;
        inputSz -= recordHeaderSz;
        if (inputSz <= 0) {
            WOLFSSL_MSG("Bad inputSz");
            return BUFFER_E;
        }
        input = (byte*)XMALLOC((size_t)inputSz, ssl->heap,
                DYNAMIC_TYPE_IN_BUFFER);
        if (input == NULL)
            return MEMORY_E;

        XMEMCPY(input, output + recordHeaderSz, (size_t)(inputSz));
        #ifdef WOLFSSL_DTLS
        if (IsDtlsNotSctpMode(ssl) &&
                (ret = DtlsMsgPoolSave(ssl, input, (word32)inputSz,
                                      type)) != 0) {
            XFREE(input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
            return ret;
        }
        #else
        (void)type;
        #endif
        *sendSz = BuildMessage(ssl, output, *sendSz, input, inputSz,
                           handshake, 1, 0, 0, CUR_ORDER);
        XFREE(input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);

        if (*sendSz < 0)
            return *sendSz;
    } else {
        if (type == certificate_request)
            *sendSz = inputSz;
        #ifdef WOLFSSL_DTLS
            if (IsDtlsNotSctpMode(ssl)) {
                ret = DtlsMsgPoolSave(ssl, output, (word32)*sendSz, type);
                if (ret != 0)
                    return ret;
            }
            if (ssl->options.dtls)
                DtlsSEQIncrement(ssl, CUR_ORDER);
        #endif
        ret = HashOutput(ssl, output, *sendSz, 0);
        if (ret != 0)
            return ret;
    }
#if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
    if (ssl->hsInfoOn)
        AddPacketName(ssl, name);
    if (ssl->toInfoOn) {
        ret = AddPacketInfo(ssl, name, handshake, output,
                            *sendSz, WRITE_PROTO, 0, ssl->heap);
        if (ret != 0)
            return ret;
    }
#else
    (void)name;
#endif
    ssl->buffers.outputBuffer.length += (word32)*sendSz;
    ssl->options.buildingMsg = 0;
    return 0;
}

#ifndef NO_CERTS

#if (!defined(NO_WOLFSSL_SERVER) || !defined(WOLFSSL_NO_CLIENT_AUTH)) && \
    !defined(NO_TLS)
/* handle generation of certificate (11) */
int SendCertificate(WOLFSSL* ssl)
{
    int    ret = 0;
    word32 certSz, certChainSz, headerSz, listSz, payloadSz;
    word32 length, maxFragment;
#ifdef HAVE_RPK
    int    usingRpkTls12 = 0;
#endif /* HAVE_RPK */

    WOLFSSL_START(WC_FUNC_CERTIFICATE_SEND);
    WOLFSSL_ENTER("SendCertificate");

    if (ssl->options.usingPSK_cipher || ssl->options.usingAnon_cipher) {
        WOLFSSL_MSG("Not sending certificate msg. Using PSK or ANON cipher.");
        return 0;  /* not needed */
    }

#ifdef HAVE_RPK
    if (!IsAtLeastTLSv1_3(ssl->version)) {
        /* If this is (D)TLS1.2 and RPK, then single cert, not list. */
        if (ssl->options.side == WOLFSSL_SERVER_END) {
            if (ssl->options.rpkState.sending_ServerCertTypeCnt == 1 &&
                ssl->options.rpkState.sending_ServerCertTypes[0] == WOLFSSL_CERT_TYPE_RPK)
                usingRpkTls12 = 1;
        } else if (ssl->options.side == WOLFSSL_CLIENT_END) {
            if (ssl->options.rpkState.sending_ClientCertTypeCnt == 1 &&
                ssl->options.rpkState.sending_ClientCertTypes[0] == WOLFSSL_CERT_TYPE_RPK)
                usingRpkTls12 = 1;
        }
    }
#endif /* HAVE_RPK */

    if (ssl->options.sendVerify == SEND_BLANK_CERT) {
    #ifdef OPENSSL_EXTRA
        if (ssl->version.major == SSLv3_MAJOR
            && ssl->version.minor == SSLv3_MINOR){
            return SendAlert(ssl, alert_warning, no_certificate);
        } else {
    #endif
            certSz = 0;
            certChainSz = 0;
            headerSz = CERT_HEADER_SZ;
            length = CERT_HEADER_SZ;
            listSz = 0;
    #ifdef OPENSSL_EXTRA
        }
    #endif
    }
    else {
        if (!ssl->buffers.certificate) {
            WOLFSSL_MSG("Send Cert missing certificate buffer");
            return BUFFER_ERROR;
        }
        certSz = ssl->buffers.certificate->length;
#ifdef HAVE_RPK
        if (usingRpkTls12) {
            headerSz = 1 * CERT_HEADER_SZ;
            listSz = certSz;
        } else {
#endif /* HAVE_RPK */
            headerSz = 2 * CERT_HEADER_SZ;
            listSz = certSz + CERT_HEADER_SZ;
#ifdef HAVE_RPK
        }
#endif /* HAVE_RPK */
        /* list + cert size */
        length = certSz + headerSz;

        /* may need to send rest of chain, already has leading size(s) */
        if (certSz && ssl->buffers.certChain) {
            certChainSz = ssl->buffers.certChain->length;
            length += certChainSz;
            listSz += certChainSz;
        }
        else
            certChainSz = 0;
    }

    payloadSz = length;

    if (ssl->fragOffset != 0)
        length -= (ssl->fragOffset + headerSz);


    maxFragment = (word32)wolfssl_local_GetMaxPlaintextSize(ssl);
    if (ssl->options.dtls)
        maxFragment -= DTLS_HANDSHAKE_HEADER_SZ;
    else
        maxFragment -= HANDSHAKE_HEADER_SZ;

    while (length > 0 && ret == 0) {
        byte*  output = NULL;
        word32 fragSz = 0;
        word32 i = RECORD_HEADER_SZ;
        int    sendSz = RECORD_HEADER_SZ;

        ssl->options.buildingMsg = 1;

        if (!ssl->options.dtls) {
            if (ssl->fragOffset == 0)  {
                if (headerSz + certSz + certChainSz <=
                    maxFragment - HANDSHAKE_HEADER_SZ) {

                    fragSz = headerSz + certSz + certChainSz;
                }
                else {
                    fragSz = maxFragment - HANDSHAKE_HEADER_SZ;
                }
                sendSz += (int)(fragSz) + HANDSHAKE_HEADER_SZ;
                i += HANDSHAKE_HEADER_SZ;
            }
            else {
                fragSz = min(length, maxFragment);
                sendSz += (int)(fragSz);
            }

            if (IsEncryptionOn(ssl, 1))
                sendSz += MAX_MSG_EXTRA;
        }
        else {
        #ifdef WOLFSSL_DTLS
            fragSz = min(length, maxFragment);
            sendSz += fragSz + DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_HEADER_SZ;
            i      += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_HEADER_SZ;
        #endif
        }

        if (IsEncryptionOn(ssl, 1))
            sendSz += cipherExtraData(ssl);

        /* check for available size */
        if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
            return ret;

        /* get output buffer */
        output = GetOutputBuffer(ssl);

        /* Safe to use ssl->fragOffset since it will be incremented immediately
         * after this block. This block needs to be entered only once to not
         * hash the cert msg twice. */
        if (ssl->fragOffset == 0) {
            if (!ssl->options.dtls) {
                AddFragHeaders(output, fragSz, 0, payloadSz, certificate, ssl);
                if (!IsEncryptionOn(ssl, 1))
                    HashRaw(ssl, output + RECORD_HEADER_SZ,
                                  HANDSHAKE_HEADER_SZ);
            }
            else {
            #ifdef WOLFSSL_DTLS
                AddHeaders(output, payloadSz, certificate, ssl);
                HashRaw(ssl,
                        output + RECORD_HEADER_SZ + DTLS_RECORD_EXTRA,
                        HANDSHAKE_HEADER_SZ + DTLS_HANDSHAKE_EXTRA);
                /* Adding the headers increments these, decrement them for
                 * actual message header. */
                ssl->keys.dtls_handshake_number--;
                AddFragHeaders(output, fragSz, 0, payloadSz, certificate, ssl);
                ssl->keys.dtls_handshake_number--;
            #endif /* WOLFSSL_DTLS */
            }

            /* list total */
#ifdef HAVE_RPK
            if (!usingRpkTls12) {
#endif /* HAVE_RPK */
                c32to24(listSz, output + i);
                if (ssl->options.dtls || !IsEncryptionOn(ssl, 1))
                    HashRaw(ssl, output + i, CERT_HEADER_SZ);
                i += CERT_HEADER_SZ;
                length -= CERT_HEADER_SZ;
                fragSz -= CERT_HEADER_SZ;
#ifdef HAVE_RPK
            }
#endif /* HAVE_RPK */
            if (certSz) {
                c32to24(certSz, output + i);
                if (ssl->options.dtls || !IsEncryptionOn(ssl, 1))
                    HashRaw(ssl, output + i, CERT_HEADER_SZ);
                i += CERT_HEADER_SZ;
                length -= CERT_HEADER_SZ;
                fragSz -= CERT_HEADER_SZ;

                if (ssl->options.dtls || !IsEncryptionOn(ssl, 1)) {
                    HashRaw(ssl, ssl->buffers.certificate->buffer, (int)certSz);
                    if (certChainSz)
                        HashRaw(ssl, ssl->buffers.certChain->buffer,
                                      (int)certChainSz);
                }
            }
        }
        else {
            if (!ssl->options.dtls) {
                AddRecordHeader(output, fragSz, handshake, ssl, CUR_ORDER);
            }
            else {
            #ifdef WOLFSSL_DTLS
                AddFragHeaders(output, fragSz, ssl->fragOffset + headerSz,
                               payloadSz, certificate, ssl);
                ssl->keys.dtls_handshake_number--;
            #endif /* WOLFSSL_DTLS */
            }
        }

        /* member */
        if (certSz && ssl->fragOffset < certSz) {
            word32 copySz = min(certSz - ssl->fragOffset, fragSz);
            XMEMCPY(output + i,
                    ssl->buffers.certificate->buffer + ssl->fragOffset, copySz);
            i += copySz;
            ssl->fragOffset += copySz;
            length -= copySz;
            fragSz -= copySz;
        }
        if (certChainSz && fragSz) {
            word32 copySz = min(certChainSz + certSz - ssl->fragOffset, fragSz);
            XMEMCPY(output + i,
                    ssl->buffers.certChain->buffer + ssl->fragOffset - certSz,
                    copySz);
            i += copySz;
            ssl->fragOffset += copySz;
            length -= copySz;
        }

        if (IsEncryptionOn(ssl, 1)) {
            byte* input = NULL;
            int   inputSz = (int)i; /* build msg adds rec hdr */
            int   recordHeaderSz = RECORD_HEADER_SZ;

            if (ssl->options.dtls)
                recordHeaderSz += DTLS_RECORD_EXTRA;
            inputSz -= recordHeaderSz;

            if (inputSz < 0) {
                WOLFSSL_MSG("Send Cert bad inputSz");
                return BUFFER_E;
            }

            if (inputSz > 0) {  /* clang thinks could be zero, let's help */
                input = (byte*)XMALLOC((size_t)inputSz, ssl->heap,
                                       DYNAMIC_TYPE_IN_BUFFER);
                if (input == NULL)
                    return MEMORY_E;
                XMEMCPY(input, output + recordHeaderSz, (size_t)(inputSz));
            }

#ifndef WOLFSSL_DTLS
            sendSz = BuildMessage(ssl, output, sendSz, input, inputSz,
                                                          handshake, 1, 0, 0, CUR_ORDER);
#else
            if (!ssl->options.dtls)
                sendSz = BuildMessage(ssl, output, sendSz, input, inputSz,
                                                              handshake, 1, 0, 0, CUR_ORDER);
            else /* DTLS 1.2 has to ignore fragmentation in hashing so we need to
                  * calculate the hash ourselves above */ {
                if ((ret = DtlsMsgPoolSave(ssl, input, (word32)inputSz, certificate)) != 0) {
                    XFREE(input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
                    return ret;
                }
                sendSz = BuildMessage(ssl, output, sendSz, input, inputSz,
                                                              handshake, 0, 0, 0, CUR_ORDER);
            }
#endif

            XFREE(input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);

            if (sendSz < 0)
                return sendSz;
        }
        else {
            sendSz = (int)i;
        #ifdef WOLFSSL_DTLS
            if (IsDtlsNotSctpMode(ssl)) {
                if ((ret = DtlsMsgPoolSave(ssl, output, (word32)sendSz, certificate)) != 0)
                    return ret;
            }
            if (ssl->options.dtls)
                DtlsSEQIncrement(ssl, CUR_ORDER);
        #endif
        }

    #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
        if (ssl->hsInfoOn)
            AddPacketName(ssl, "Certificate");
        if (ssl->toInfoOn) {
            ret = AddPacketInfo(ssl, "Certificate", handshake, output, sendSz,
                           WRITE_PROTO, 0, ssl->heap);
            if (ret != 0)
                return ret;
        }
    #endif

        ssl->buffers.outputBuffer.length += (word32)sendSz;
        if (!ssl->options.groupMessages)
            ret = SendBuffered(ssl);
    }

    if (ret != WC_NO_ERR_TRACE(WANT_WRITE)) {
        /* Clean up the fragment offset. */
        ssl->options.buildingMsg = 0;
        ssl->fragOffset = 0;
        #ifdef WOLFSSL_DTLS
            if (ssl->options.dtls)
                ssl->keys.dtls_handshake_number++;
        #endif
        if (ssl->options.side == WOLFSSL_SERVER_END){
            ssl->options.serverState = SERVER_CERT_COMPLETE;
        }
    }

    WOLFSSL_LEAVE("SendCertificate", ret);
    WOLFSSL_END(WC_FUNC_CERTIFICATE_SEND);

    return ret;
}
#endif /* !NO_TLS && (!NO_WOLFSSL_SERVER || !WOLFSSL_NO_CLIENT_AUTH) */


#if !defined(NO_TLS)
/* handle generation of certificate_request (13) */
int SendCertificateRequest(WOLFSSL* ssl)
{
    byte   *output;
    int    ret;
    int    sendSz;
    word32 i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
    word32 dnLen = 0;
#ifndef WOLFSSL_NO_CA_NAMES
    WOLF_STACK_OF(WOLFSSL_X509_NAME)* names;
#endif
    const Suites* suites = WOLFSSL_SUITES(ssl);

    int  typeTotal = 1;  /* only 1 for now */
    int  reqSz = ENUM_LEN + typeTotal + REQ_HEADER_SZ;  /* add auth later */

    WOLFSSL_START(WC_FUNC_CERTIFICATE_REQUEST_SEND);
    WOLFSSL_ENTER("SendCertificateRequest");

    if (IsAtLeastTLSv1_2(ssl))
        reqSz += LENGTH_SZ + suites->hashSigAlgoSz;

#ifndef WOLFSSL_NO_CA_NAMES
    /* Certificate Authorities */
    names = SSL_PRIORITY_CA_NAMES(ssl);
    while (names != NULL) {
        byte seq[MAX_SEQ_SZ];
        WOLFSSL_X509_NAME* name = names->data.name;

        if (name != NULL) {
            /* 16-bit length | SEQ | Len | DER of name */
            dnLen += OPAQUE16_LEN + SetSequence(name->rawLen, seq) +
                        name->rawLen;
        }
        names = names->next;
    }
    reqSz += dnLen;
#endif

    if (ssl->options.usingPSK_cipher || ssl->options.usingAnon_cipher)
        return 0;  /* not needed */

    sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + reqSz;

    if (!ssl->options.dtls) {
        if (IsEncryptionOn(ssl, 1))
            sendSz += MAX_MSG_EXTRA;
    }
    else {
    #ifdef WOLFSSL_DTLS
        sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
        i      += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
    #endif
    }

    if (IsEncryptionOn(ssl, 1))
        sendSz += cipherExtraData(ssl);

    /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
     * is not advanced yet */
    ssl->options.buildingMsg = 1;

    /* check for available size */
    if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
        return ret;

    /* get output buffer */
    output = GetOutputBuffer(ssl);

    AddHeaders(output, (word32)reqSz, certificate_request, ssl);

    /* write to output */
    output[i++] = (byte)typeTotal;  /* # of types */
#ifdef HAVE_ECC
    if ((ssl->options.cipherSuite0 == ECC_BYTE ||
         ssl->options.cipherSuite0 == CHACHA_BYTE) &&
                     ssl->specs.sig_algo == ecc_dsa_sa_algo) {
        output[i++] = ecdsa_sign;
    }
    else
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && \
    (defined(WOLFSSL_SM4_CBC) || defined(WOLFSSL_SM4_GCM) || \
     defined(WOLFSSL_SM4_CCM))
    if (ssl->options.cipherSuite0 == SM_BYTE && (0
    #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
        || ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
    #endif
    #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
        || ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
    #endif
    #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
        || ssl->options.cipherSuite == TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
    #endif
        )) {
        output[i++] = ecdsa_sign;
    }
    else
#endif
#endif /* HAVE_ECC */
    {
        output[i++] = rsa_sign;
    }

    /* supported hash/sig */
    if (IsAtLeastTLSv1_2(ssl)) {
        c16toa(suites->hashSigAlgoSz, &output[i]);
        i += OPAQUE16_LEN;

        XMEMCPY(&output[i], suites->hashSigAlgo, suites->hashSigAlgoSz);
        i += suites->hashSigAlgoSz;
    }

    /* Certificate Authorities */
    c16toa((word16)dnLen, &output[i]);  /* auth's */
    i += REQ_HEADER_SZ;
#ifndef WOLFSSL_NO_CA_NAMES
    names = SSL_PRIORITY_CA_NAMES(ssl);
    while (names != NULL) {
        byte seq[MAX_SEQ_SZ];
        WOLFSSL_X509_NAME* name = names->data.name;

        if (name != NULL) {
            c16toa((word16)name->rawLen +
                   (word16)SetSequence(name->rawLen, seq), &output[i]);
            i += OPAQUE16_LEN;
            i += SetSequence(name->rawLen, output + i);
            XMEMCPY(output + i, name->raw, name->rawLen);
            i += name->rawLen;
        }
        names = names->next;
    }
#endif
    ret = BuildMsgOrHashOutput(ssl, output, &sendSz, i, certificate_request,
                               "CertificateRequest");
    if (ret != 0)
        return ret;

    if (ssl->options.groupMessages)
        ret = 0;
    else
        ret = SendBuffered(ssl);


    WOLFSSL_LEAVE("SendCertificateRequest", ret);
    WOLFSSL_END(WC_FUNC_CERTIFICATE_REQUEST_SEND);

    return ret;
}
#endif /* !NO_TLS */


#ifndef NO_WOLFSSL_SERVER

#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) \
 || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
static int BuildCertificateStatus(WOLFSSL* ssl, byte type, buffer* status,
                                                                   word32 count)
{
    byte*  output  = NULL;
    word32 idx     = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
    word32 length  = ENUM_LEN;
    word32 headerSz= idx;
    int    sendSz  = 0;
    int    ret     = 0;
    word32 i       = 0;

    WOLFSSL_ENTER("BuildCertificateStatus");

    switch (type) {
        case WOLFSSL_CSR2_OCSP_MULTI:
            length += OPAQUE24_LEN;
            FALL_THROUGH; /* followed by */

        case WOLFSSL_CSR2_OCSP:
            for (i = 0; i < count; i++)
                length += OPAQUE24_LEN + status[i].length;
        break;

        default:
            return 0;
    }
#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        headerSz = idx = DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ;
        sendSz  = idx + length;

    } else
#endif
    sendSz = (int)(idx + length);

    if (ssl->keys.encryptionOn)
        sendSz += MAX_MSG_EXTRA;

    output =(byte*)XMALLOC(sendSz, ssl->heap, DYNAMIC_TYPE_OCSP);
    if (output == NULL)
        return MEMORY_E;

    AddHeaders(output, length, certificate_status, ssl);

    output[idx++] = type;

    if (type == WOLFSSL_CSR2_OCSP_MULTI) {
        c32to24(length - (ENUM_LEN + OPAQUE24_LEN), output + idx);
        idx += OPAQUE24_LEN;
    }

    for (i = 0; i < count; i++) {
        c32to24(status[i].length, output + idx);
        idx += OPAQUE24_LEN;

        XMEMCPY(output + idx, status[i].buffer, status[i].length);
        idx += status[i].length;
    }
    /* Send Message. Handled message fragmentation in the function if needed */
    ret = SendHandshakeMsg(ssl, output, (sendSz - headerSz), certificate_status,
                "Certificate Status");
    XFREE(output, ssl->heap, DYNAMIC_TYPE_OCSP);

    WOLFSSL_LEAVE("BuildCertificateStatus", ret);
    return ret;
}
#endif

#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
    defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
static int BuildCertificateStatusWithStatusCB(WOLFSSL* ssl, byte status_type)
{
    WOLFSSL_OCSP *ocsp;
    int ret = 0;

    if (ssl == NULL)
        return BAD_FUNC_ARG;
    if (status_type != WOLFSSL_CSR2_OCSP &&
            status_type != WOLFSSL_CSR2_OCSP_MULTI)
        return 0;

    ocsp = SSL_CM(ssl)->ocsp_stapling;
    if (ocsp == NULL || ocsp->statusCb == NULL)
        return BAD_FUNC_ARG;
    WOLFSSL_MSG("Calling ocsp->statusCb");
    ret = ocsp->statusCb(ssl, ocsp->statusCbArg);
    switch (ret) {
        case WOLFSSL_OCSP_STATUS_CB_OK: {
            word32 cnt = 1;
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
            if (status_type == WOLFSSL_CSR2_OCSP_MULTI) {
                /* Find last response set */
                for (cnt = XELEM_CNT(ssl->ocspCsrResp);
                        cnt > 0 && ssl->ocspCsrResp[cnt-1].buffer == NULL;
                        cnt--);
                cnt = MIN(cnt, (word32)ssl->buffers.certChainCnt + 1);
            }
#endif
            ret = BuildCertificateStatus(ssl, status_type, ssl->ocspCsrResp,
                                         cnt);
            break;
        }
        case WOLFSSL_OCSP_STATUS_CB_NOACK:
            /* No OCSP response to send */
            ret = 0;
            break;
        case WOLFSSL_OCSP_STATUS_CB_ALERT_FATAL:
        /* fall through */
        default:
            ret = WOLFSSL_FATAL_ERROR;
            break;
    }

    return ret;
}
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST ||
        * HAVE_CERTIFICATE_STATUS_REQUEST_V2 */

#endif /* NO_WOLFSSL_SERVER */

/* handle generation of certificate_status (22) */
int SendCertificateStatus(WOLFSSL* ssl)
{
    int ret = 0;
    byte status_type = 0;

    WOLFSSL_START(WC_FUNC_CERTIFICATE_STATUS_SEND);
    WOLFSSL_ENTER("SendCertificateStatus");

    if (ssl == NULL || SSL_CM(ssl) == NULL) {
        WOLFSSL_MSG("SendCertificateStatus bad args");
        return BAD_FUNC_ARG;
    }

#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
    status_type = ssl->status_request;
#endif

#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
    status_type = status_type ? status_type : ssl->status_request_v2;
#endif

#ifndef NO_WOLFSSL_SERVER
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) || \
    defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
    if (SSL_CM(ssl)->ocsp_stapling != NULL &&
            SSL_CM(ssl)->ocsp_stapling->statusCb != NULL) {
        return BuildCertificateStatusWithStatusCB(ssl, status_type);
    }
#endif
#endif

    switch (status_type) {

    #ifndef NO_WOLFSSL_SERVER
    #if defined(HAVE_CERTIFICATE_STATUS_REQUEST) \
     || defined(HAVE_CERTIFICATE_STATUS_REQUEST_V2)
        /* case WOLFSSL_CSR_OCSP: */
        case WOLFSSL_CSR2_OCSP:
        {
            OcspRequest* request = ssl->ctx->certOcspRequest;
            buffer response;

            ret = CreateOcspResponse(ssl, &request, &response);

            /* if a request was successfully created and not stored in
             * ssl->ctx then free it */
            if (ret == 0 && request != ssl->ctx->certOcspRequest) {
                FreeOcspRequest(request);
                XFREE(request, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);
                request = NULL;
            }

            if (ret == 0 && response.buffer) {
                ret = BuildCertificateStatus(ssl, status_type, &response, 1);
            }

            /* Let's not error out the connection if we can't verify our cert */
            if (ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E) ||
                ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E))
                ret = 0;

            if (response.buffer) {
                XFREE(response.buffer, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);
                response.buffer = NULL;
            }
            break;
        }

    #endif /* HAVE_CERTIFICATE_STATUS_REQUEST    */
           /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */

    #if defined HAVE_CERTIFICATE_STATUS_REQUEST_V2
        case WOLFSSL_CSR2_OCSP_MULTI:
        {
            OcspRequest* request = ssl->ctx->certOcspRequest;
            buffer responses[1 + MAX_CHAIN_DEPTH];
            byte ctxOwnsRequest = 0;
            word32 i = 0;

            XMEMSET(responses, 0, sizeof(responses));

            ret = CreateOcspResponse(ssl, &request, &responses[0]);

            /* if a request was successfully created and not stored in
             * ssl->ctx then free it */
            if (ret == 0 && request != ssl->ctx->certOcspRequest) {
                FreeOcspRequest(request);
                XFREE(request, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);
                request = NULL;
            }

            if (ret == 0 && (!ssl->ctx->chainOcspRequest[0]
                                              || ssl->buffers.weOwnCertChain)) {
                buffer der;
                word32 idx = 0;
                WC_DECLARE_VAR(cert, DecodedCert, 1, 0);
                DerBuffer* chain;

                WC_ALLOC_VAR_EX(cert, DecodedCert, 1, ssl->heap,
                    DYNAMIC_TYPE_DCERT, return MEMORY_E);
                request = (OcspRequest*)XMALLOC(sizeof(OcspRequest), ssl->heap,
                                                     DYNAMIC_TYPE_OCSP_REQUEST);
                if (request == NULL) {
                    WC_FREE_VAR_EX(cert, ssl->heap, DYNAMIC_TYPE_DCERT);
                    return MEMORY_E;
                }

                /* use certChain if available, otherwise use certificate */
                chain = ssl->buffers.certChain;
                if (chain == NULL) {
                    chain = ssl->buffers.certificate;
                }

                if (chain && chain->buffer) {
                    while (ret == 0 && idx + OPAQUE24_LEN < chain->length) {
                        c24to32(chain->buffer + idx, &der.length);
                        idx += OPAQUE24_LEN;

                        der.buffer = chain->buffer + idx;
                        idx += der.length;

                        if (idx > chain->length)
                            break;
                        ret = CreateOcspRequest(ssl, request, cert, der.buffer,
                                                der.length, &ctxOwnsRequest);
                        if (ret == 0) {
                            request->ssl = ssl;
                            ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling,
                                        request, &responses[i + 1], ssl->heap);

                            /* Suppressing, not critical */
                            if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED) ||
                                ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
                                ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
                                ret = 0;
                            }


                            i++;
                            if (!ctxOwnsRequest)
                                FreeOcspRequest(request);
                        }
                    }
                }
                if (!ctxOwnsRequest)
                    XFREE(request, ssl->heap, DYNAMIC_TYPE_OCSP_REQUEST);
                WC_FREE_VAR_EX(cert, ssl->heap, DYNAMIC_TYPE_DCERT);
            }
            else {
                while (ret == 0 &&
                            NULL != (request = ssl->ctx->chainOcspRequest[i])) {
                    request->ssl = ssl;
                    ret = CheckOcspRequest(SSL_CM(ssl)->ocsp_stapling,
                                           request, &responses[++i], ssl->heap);

                    /* Suppressing, not critical */
                    if (ret == WC_NO_ERR_TRACE(OCSP_CERT_REVOKED) ||
                        ret == WC_NO_ERR_TRACE(OCSP_CERT_UNKNOWN) ||
                        ret == WC_NO_ERR_TRACE(OCSP_LOOKUP_FAIL)) {
                        ret = 0;
                    }
                }
            }

            if (responses[0].buffer) {
                if (ret == 0) {
                    ret = BuildCertificateStatus(ssl, status_type, responses,
                                                                         i + 1);
                }

                for (i = 0; i < 1 + MAX_CHAIN_DEPTH; i++) {
                    if (responses[i].buffer) {
                        XFREE(responses[i].buffer, ssl->heap,
                                                     DYNAMIC_TYPE_OCSP_REQUEST);
                    }
                }
            }

            /* Let's not error out the connection if we can't verify our cert */
            if (ret == WC_NO_ERR_TRACE(ASN_SELF_SIGNED_E) ||
                ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E))
                ret = 0;

            break;
        }
    #endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
    #endif /* NO_WOLFSSL_SERVER */

        default:
            break;
    }

    WOLFSSL_LEAVE("SendCertificateStatus", ret);
    WOLFSSL_END(WC_FUNC_CERTIFICATE_STATUS_SEND);

    return ret;
}

#endif /* !NO_CERTS */

#endif /* WOLFSSL_NO_TLS12 */



#if defined(HAVE_SECURE_RENEGOTIATION) && defined(WOLFSSL_DTLS)
/**
 * Check if the SCR keys are set in ssl->secure_renegotiation->tmp_keys.
 */
int DtlsSCRKeysSet(WOLFSSL* ssl)
{
    return ssl->secure_renegotiation &&
           ssl->secure_renegotiation->tmp_keys.dtls_epoch != 0;
}

/**
 * ssl->keys contains the current cipher parameters only for epoch 1. For
 * epochs >1 ssl->secure_renegotiation->tmp_keys contains the current
 * cipher parameters. This function checks if the message currently being
 * processed should use ssl->keys or ssl->secure_renegotiation->tmp_keys.
 */
int IsDtlsMsgSCRKeys(WOLFSSL* ssl)
{
    return DtlsSCRKeysSet(ssl) &&
           ssl->keys.curEpoch ==
                   ssl->secure_renegotiation->tmp_keys.dtls_epoch;
}

/**
 * ssl->keys contains the current cipher parameters only for epoch 1. For
 * epochs >1 ssl->secure_renegotiation->tmp_keys contains the current
 * cipher parameters. This function checks if the message currently being
 * built should use ssl->keys or ssl->secure_renegotiation->tmp_keys.
 */
int DtlsUseSCRKeys(WOLFSSL* ssl)
{
    return DtlsSCRKeysSet(ssl) &&
           ssl->secure_renegotiation->tmp_keys.dtls_epoch ==
                   ssl->keys.dtls_epoch;
}

/**
 * If ssl->secure_renegotiation->tmp_keys.dtls_epoch > ssl->keys.dtls_epoch
 * then PREV_ORDER refers to the current epoch.
 * */
int DtlsCheckOrder(WOLFSSL* ssl, int order)
{
    if (order == PREV_ORDER && ssl->secure_renegotiation &&
            ssl->secure_renegotiation->tmp_keys.dtls_epoch > ssl->keys.dtls_epoch) {
        return CUR_ORDER;
    }
    else {
        return order;
    }
}
#endif /* HAVE_SECURE_RENEGOTIATION && WOLFSSL_DTLS */

/* If secure renegotiation is disabled, this will always return false.
 * Otherwise it checks to see if we are currently renegotiating. */
int IsSCR(WOLFSSL* ssl)
{
#ifndef HAVE_SECURE_RENEGOTIATION
    (void)ssl;
#else /* HAVE_SECURE_RENEGOTIATION */
    if (ssl->secure_renegotiation &&
            ssl->secure_renegotiation->enabled &&  /* Is SCR enabled? */
            ssl->options.handShakeDone && /* At least one handshake done? */
            ssl->options.handShakeState != HANDSHAKE_DONE) /* Currently handshaking? */
        return 1;
#endif /* HAVE_SECURE_RENEGOTIATION */
    return 0;
}


#if !defined(NO_TLS) && defined(WOLFSSL_TLS13) && \
    !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)
/*
 * Enforce limits specified in
 * https://www.rfc-editor.org/rfc/rfc8446#section-5.5
 */
static int CheckTLS13AEADSendLimit(WOLFSSL* ssl)
{
    w64wrapper seq;
    w64wrapper limit;

    switch (ssl->specs.bulk_cipher_algorithm) {
#ifdef BUILD_AESGCM
        case wolfssl_aes_gcm:
            /* Limit is 2^24.5 */
            limit = AEAD_AES_LIMIT;
            break;
#endif
#if defined(HAVE_CHACHA) && defined(HAVE_POLY1305)
        case wolfssl_chacha:
            /* For ChaCha20/Poly1305, the record sequence number would wrap
             * before the safety limit is reached. */
            return 0;
#endif
#ifdef HAVE_AESCCM
        case wolfssl_aes_ccm:
            /* Use the limits calculated in the DTLS 1.3 spec
             * https://www.rfc-editor.org/rfc/rfc9147.html#name-analysis-of-limits-on-ccm-u */
#ifdef WOLFSSL_DTLS13
            if (ssl->options.dtls)
                limit = DTLS_AEAD_AES_CCM_LIMIT; /* Limit is 2^23 */
            else
#endif
                limit = AEAD_AES_LIMIT; /* Limit is 2^24.5 */
            break;
#endif
#ifdef WOLFSSL_SM4_GCM
        case wolfssl_sm4_gcm:
            /* Limit is 2^22 - 1 */
            limit = AEAD_SM4_GCM_LIMIT;
            break;
#endif
#ifdef WOLFSSL_SM4_CCM
        case wolfssl_sm4_ccm:
            /* Limit is 2^10 - 1 */
            limit = AEAD_SM4_CCM_LIMIT;
            break;
#endif
        case wolfssl_cipher_null:
            /* No encryption being done */
            return 0;
        default:
            WOLFSSL_MSG("Unrecognized ciphersuite for AEAD limit check");
            return BAD_STATE_E;

    }
#ifdef WOLFSSL_DTLS13
    if (ssl->options.dtls) {
        if (ssl->dtls13EncryptEpoch == NULL) {
            WOLFSSL_MSG("DTLS 1.3 encrypt epoch not set");
            return BAD_STATE_E;
        }
        seq = ssl->dtls13EncryptEpoch->nextSeqNumber;
    }
    else
#endif
    {
        seq = w64From32(ssl->keys.sequence_number_hi,
                ssl->keys.sequence_number_lo);
    }

    if (w64GTE(seq, limit)) { /* cppcheck-suppress uninitvar
                               * (false positive from cppcheck-2.13.0)
                               */
        return Tls13UpdateKeys(ssl); /* Need to generate new keys */
    }

    return 0;
}
#endif /* WOLFSSL_TLS13 && !WOLFSSL_TLS13_IGNORE_AEAD_LIMITS */

#ifdef WOLFSSL_THREADED_CRYPT
int SendAsyncData(WOLFSSL* ssl)
{
    int i;

    for (i = 0; i < WOLFSSL_THREADED_CRYPT_CNT; i++) {
        ThreadCrypt* encrypt = &ssl->buffers.encrypt[i];

        if (encrypt->done) {
            int error;

            GrowOutputBuffer(ssl, encrypt->buffer.length);
            XMEMCPY(ssl->buffers.outputBuffer.buffer, encrypt->buffer.buffer,
                encrypt->buffer.length);
            ssl->buffers.outputBuffer.length = encrypt->buffer.length;
            ssl->buffers.outputBuffer.idx = 0;
            encrypt->done = 0;
            encrypt->avail = 1;
            if ((error = SendBuffered(ssl)) < 0) {
                ssl->error = error;
                WOLFSSL_ERROR(ssl->error);
                /* store for next call if WANT_WRITE or user embedSend() that
                   doesn't present like WANT_WRITE */
                ssl->buffers.plainSz  = encrypt->buffer.length;
                ssl->buffers.prevSent = encrypt->buffer.length;
                if (ssl->error == WC_NO_ERR_TRACE(SOCKET_ERROR_E) &&
                        (ssl->options.connReset || ssl->options.isClosed)) {
                    return SOCKET_PEER_CLOSED_E;  /* peer reset or closed */
                }
                return ssl->error;
            }

            /* only one message per attempt */
            if (ssl->options.partialWrite == 1) {
                WOLFSSL_MSG("Partial Write on, only sending one record");
                break;
            }
        }
    }

    return 0;
}
#endif

#ifndef NO_TLS

/**
 * ssl_in_handshake():
 * Invoked in wolfSSL_read/wolfSSL_write to check if wolfSSL_negotiate() is
 * needed in the handshake.
 *
 * In TLSv1.2 negotiate until the end of the handshake, unless:
 * 1 in SCR and sending data or
 * 2 in SCR and we have plain data ready
 * Early data logic may bypass this logic in TLSv1.3 when appropriate.
 */
static int ssl_in_handshake(WOLFSSL *ssl, int sending_data)
{
int SendAsyncData = 1;
(void)SendAsyncData;
    if (IsSCR(ssl)) {
        if (sending_data) {
            /* allow sending data in SCR */
            return 0;
        } else {
            /* allow reading buffered data in SCR */
            if (ssl->buffers.clearOutputBuffer.length != 0)
                return 0;
        }
        return 1;
    }

    if (ssl->options.handShakeState != HANDSHAKE_DONE)
        return 1;

    if (ssl->options.side == WOLFSSL_SERVER_END) {
        if (IsAtLeastTLSv1_3(ssl->version))
            return ssl->options.acceptState < TLS13_TICKET_SENT;
        if (IsAtLeastTLSv1_2(ssl))
            return ssl->options.acceptState < ACCEPT_THIRD_REPLY_DONE;
        return 0;
    }

    if (ssl->options.side == WOLFSSL_CLIENT_END) {
        if (IsAtLeastTLSv1_3(ssl->version))
            return ssl->options.connectState < FINISHED_DONE;
        if (IsAtLeastTLSv1_2(ssl))
            return ssl->options.connectState < SECOND_REPLY_DONE;
        return 0;
    }

    return 0;
}

int SendData(WOLFSSL* ssl, const void* data, size_t sz)
{
    word32 sent = 0; /* plainText size */
    int sendSz,
        ret;
#if defined(WOLFSSL_EARLY_DATA) && defined(WOLFSSL_EARLY_DATA_GROUP)
    int groupMsgs = 0;
#endif
    int error = ssl->error;

    if (sz > INT_MAX) {
        WOLFSSL_MSG("SendData sz overflow");
        return WOLFSSL_FATAL_ERROR;
    }

    if (error == WC_NO_ERR_TRACE(WANT_WRITE)
    #ifdef WOLFSSL_ASYNC_CRYPT
        || error == WC_NO_ERR_TRACE(WC_PENDING_E)
    #endif
    ) {
        error = 0;
        ssl->error = 0;
    }

    /* don't allow write after decrypt or mac error */
    if (error == WC_NO_ERR_TRACE(VERIFY_MAC_ERROR) ||
        error == WC_NO_ERR_TRACE(DECRYPT_ERROR)) {
        /* For DTLS allow these possible errors and allow the session
            to continue despite them */
        if (ssl->options.dtls) {
            ssl->error = 0;
        }
        else {
            WOLFSSL_MSG("Not allowing write after decrypt or mac error");
            return WOLFSSL_FATAL_ERROR;
        }
    }

#ifdef WOLFSSL_EARLY_DATA
    if (ssl->options.side == WOLFSSL_CLIENT_END &&
            ssl->earlyData != no_early_data &&
            ssl->earlyData != done_early_data) {
        if (ssl->options.handShakeState == HANDSHAKE_DONE) {
            WOLFSSL_MSG("handshake complete, trying to send early data");
            ssl->error = BUILD_MSG_ERROR;
            return WOLFSSL_FATAL_ERROR;
        }
    #ifdef WOLFSSL_EARLY_DATA_GROUP
        groupMsgs = 1;
    #endif
    }
    else
#endif
    if (IsAtLeastTLSv1_3(ssl->version) &&
            ssl->options.side == WOLFSSL_SERVER_END &&
            ssl->options.acceptState >= TLS13_ACCEPT_FINISHED_SENT) {
        /* We can send data without waiting on peer finished msg */
        WOLFSSL_MSG("server sending data before receiving client finished");
    }
    else if (ssl_in_handshake(ssl, 1)) {
        int err;
        WOLFSSL_MSG("handshake not complete, trying to finish");
        if ( (err = wolfSSL_negotiate(ssl)) != WOLFSSL_SUCCESS) {
        #ifdef WOLFSSL_ASYNC_CRYPT
            /* if async would block return WANT_WRITE */
            if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                return WOLFSSL_CBIO_ERR_WANT_WRITE;
            }
        #endif
            return err;
        }
    }

#ifdef WOLFSSL_RW_THREADED
#ifdef WOLFSSL_DTLS13
    if (ssl->options.dtls) {
        /* Dtls13DoScheduledWork(ssl) may return WANT_WRITE */
        if ((error = Dtls13DoScheduledWork(ssl)) < 0) {
            ssl->error = error;
            WOLFSSL_ERROR(error);
            return error;
        }
    }
#endif /* WOLFSSL_DTLS13 */
#ifdef WOLFSSL_TLS13
    if (ssl->options.sendKeyUpdate) {
        ssl->options.sendKeyUpdate = 0;
        ret = SendTls13KeyUpdate(ssl);
        if (ret != 0) {
            ssl->error = BUILD_MSG_ERROR;
            return WOLFSSL_FATAL_ERROR;
        }
    }
#endif
#endif

    /* last time system socket output buffer was full, try again to send */
    if (ssl->buffers.outputBuffer.length > 0
    #if defined(WOLFSSL_EARLY_DATA) && defined(WOLFSSL_EARLY_DATA_GROUP)
        && !groupMsgs
    #endif
        ) {
        WOLFSSL_MSG("output buffer was full, trying to send again");
        if ( (error = SendBuffered(ssl)) < 0) {
            WOLFSSL_ERROR(error);
            if (error == WC_NO_ERR_TRACE(SOCKET_ERROR_E) &&
                    (ssl->options.connReset || ssl->options.isClosed)) {
                error = SOCKET_PEER_CLOSED_E;
                ssl->error = error;
                WOLFSSL_ERROR(error);
                return 0;  /* peer reset or closed */
            }
            return (ssl->error = error);
        }
        else {
            /* advance sent to previous sent + plain size just sent */
            sent = ssl->buffers.prevSent + ssl->buffers.plainSz;
            ssl->buffers.prevSent = 0;
            ssl->buffers.plainSz = 0;
            WOLFSSL_MSG("sent write buffered data");

            if (sent > (word32)sz) {
                WOLFSSL_MSG("error: write() after WANT_WRITE with short size");
                return (ssl->error = BAD_FUNC_ARG);
            }
        }
    }

    ret = RetrySendAlert(ssl);
    if (ret != 0) {
        ssl->error = ret;
        return WOLFSSL_FATAL_ERROR;
    }

#ifdef WOLFSSL_THREADED_CRYPT
    ret = SendAsyncData(ssl);
    if (ret != 0) {
        ssl->error = ret;
        return WOLFSSL_FATAL_ERROR;
    }
    if (ssl->dtls13WaitKeyUpdateAck) {
        ret = DoDtls13KeyUpdateAck(ssl);
        if (ret != 0)
            return ret;
    }
#endif

    for (;;) {
        byte* out;
        byte* sendBuffer = (byte*)data + sent;  /* may switch on comp */
        int   buffSz;                           /* may switch on comp */
        int   outputSz;
#ifdef HAVE_LIBZ
        byte  comp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
#endif
#ifdef WOLFSSL_THREADED_CRYPT
        int i;
        ThreadCrypt* encrypt = NULL;
#endif

#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_TLS13_IGNORE_AEAD_LIMITS)
        if (IsAtLeastTLSv1_3(ssl->version)) {
            ret = CheckTLS13AEADSendLimit(ssl);
            if (ret != 0) {
                ssl->error = ret;
                return WOLFSSL_FATAL_ERROR;
            }
        }
#endif

#ifdef WOLFSSL_DTLS13
        if (ssl->options.dtls && ssl->options.tls1_3) {
            byte isEarlyData = 0;

            if (ssl->dtls13EncryptEpoch == NULL)
                return ssl->error = BAD_STATE_E;

#ifdef WOLFSSL_EARLY_DATA
            isEarlyData = ssl->options.side == WOLFSSL_CLIENT_END &&
                    ssl->earlyData != no_early_data &&
                    ssl->earlyData != done_early_data;
#endif

            if (isEarlyData) {
#ifdef WOLFSSL_EARLY_DATA
                ret = Dtls13SetEpochKeys(ssl,
                    w64From32(0x0, DTLS13_EPOCH_EARLYDATA), ENCRYPT_SIDE_ONLY);
                if (ret != 0) {
                    WOLFSSL_MSG(
                        "trying to send early data without epoch 1");
                    ssl->error = BUILD_MSG_ERROR;
                    return WOLFSSL_FATAL_ERROR;
                }
#endif /* WOLFSSL_EARLY_DATA */
            }
            else if (!w64Equal(
                         ssl->dtls13EncryptEpoch->epochNumber,
                         ssl->dtls13Epoch)) {
                ret = Dtls13SetEpochKeys(
                    ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
                if (ret != 0) {
                    ssl->error = BUILD_MSG_ERROR;
                    return WOLFSSL_FATAL_ERROR;
                }
            }
        }
#endif /* WOLFSSL_DTLS13 */

        if (sent == (word32)sz) break;

        buffSz = (int)((word32)sz - sent);
        if (buffSz <= 0) {
            WOLFSSL_MSG("error: sent size exceeds input size");
            ssl->error = BAD_FUNC_ARG;
            return WOLFSSL_FATAL_ERROR;
        }
#if defined(WOLFSSL_DTLS)
        if (ssl->options.dtls) {
#if defined(WOLFSSL_DTLS_MTU)
            int mtu = ssl->dtlsMtuSz;
#else
            int mtu = MAX_MTU;
#endif
            outputSz = wolfssl_local_GetRecordSize(ssl, (word32)buffSz, 1);
            if (outputSz > mtu) {
#if defined(WOLFSSL_NO_DTLS_SIZE_CHECK)
                /* split instead of error out */
                buffSz = min(buffSz, wolfssl_local_GetMaxPlaintextSize(ssl));
                outputSz = wolfssl_local_GetRecordSize(ssl, (word32)buffSz, 1);
#else
                error = DTLS_SIZE_ERROR;
                ssl->error = error;
                WOLFSSL_ERROR(error);
                return error;
#endif /* WOLFSSL_NO_DTLS_SIZE_CHECK */
            }
        }
        else
#endif /* WOLFSSL_DTLS */
        {
            int maxFrag = wolfSSL_GetMaxFragSize(ssl);
            if (maxFrag > 0)
                buffSz = min((word32)buffSz, (word32)maxFrag);
            outputSz = wolfssl_local_GetRecordSize(ssl, (word32)buffSz, 1);
        }

        /* check for available size, it does also DTLS MTU checks */
        if ((ret = CheckAvailableSize(ssl, outputSz)) != 0)
            return (ssl->error = ret);

        /* get output buffer */
#ifndef WOLFSSL_THREADED_CRYPT
        out = GetOutputBuffer(ssl);
#else
        do {
            for (i = 0; i < WOLFSSL_THREADED_CRYPT_CNT; i++) {
                if (ssl->buffers.encrypt[i].avail) {
                    encrypt = &ssl->buffers.encrypt[i];
                    break;
                }
            }
            if (encrypt == NULL) {
                ret = SendAsyncData(ssl);
                if (ret != 0) {
                    ssl->error = ret;
                    return WOLFSSL_FATAL_ERROR;
                }
            }
        }
        while (encrypt == NULL);
        encrypt->done = 0;
        encrypt->avail = 0;
        GrowAnOutputBuffer(ssl, &encrypt->buffer, outputSz);
        out = encrypt->buffer.buffer;
#endif

#ifdef HAVE_LIBZ
        if (ssl->options.usingCompression) {
            buffSz = myCompress(ssl, sendBuffer, buffSz, comp, sizeof(comp));
            if (buffSz < 0) {
                return buffSz;
            }
            sendBuffer = comp;
        }
#endif
        if (!ssl->options.tls1_3) {
#ifdef WOLFSSL_ASYNC_CRYPT
            if (ssl->async == NULL) {
                ssl->async = (struct WOLFSSL_ASYNC*)
                        XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
                                DYNAMIC_TYPE_ASYNC);
                if (ssl->async == NULL)
                    return MEMORY_E;
                ssl->async->freeArgs = NULL;
            }
#endif
            sendSz = BuildMessage(ssl, out, outputSz, sendBuffer, buffSz,
                                  application_data, 0, 0, 1, CUR_ORDER);
        }
        else {
#ifdef WOLFSSL_TLS13
            sendSz = BuildTls13Message(ssl, out, outputSz, sendBuffer, buffSz,
                                       application_data, 0, 0, 1);
#else
            sendSz = BUFFER_ERROR;
#endif
        }
        if (sendSz < 0) {
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (sendSz == WC_NO_ERR_TRACE(WC_PENDING_E))
                ssl->error = sendSz;
        #endif
            return BUILD_MSG_ERROR;
        }

#ifdef WOLFSSL_ASYNC_CRYPT
        FreeAsyncCtx(ssl, 0);
#endif
#ifdef WOLFSSL_THREADED_CRYPT
        if (!encrypt->init) {
            SetKeys(&encrypt->encrypt, NULL, &ssl->keys, &ssl->specs,
                ssl->options.side, ssl->heap, ssl->devId, ssl->rng,
                ssl->options.tls1_3);
            encrypt->init = 1;
        }
        encrypt->buffer.length = sendSz;
        encrypt->offset = RECORD_HEADER_SZ;
        if (ssl->options.dtls) {
            encrypt->offset += DTLS_RECORD_EXTRA;
        }
        encrypt->cryptLen = outputSz - encrypt->offset;
    #ifdef HAVE_TRUNCATED_HMAC
        if (ssl->truncated_hmac) {
            encrypt->cryptLen -= min(TRUNCATED_HMAC_SZ, ssl->specs.hash_size);
        }
        else
    #endif
        {
            encrypt->cryptLen -= ssl->specs.hash_size;
        }

#if !defined(NO_PUBLIC_GCM_SET_IV) && \
    ((defined(HAVE_FIPS) || defined(HAVE_SELFTEST)) && \
    (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION < 2)))
        XMEMCPY(encrypt->nonce, ssl->keys.aead_enc_imp_IV, AESGCM_IMP_IV_SZ);
        XMEMCPY(encrypt->nonce + AESGCM_IMP_IV_SZ, ssl->keys.aead_exp_IV,
            AESGCM_EXP_IV_SZ);
#endif
        XMEMSET(encrypt->additional, 0, AEAD_AUTH_DATA_SZ);
        WriteSEQ(ssl, CUR_ORDER, encrypt->additional);
        XMEMCPY(encrypt->additional + AEAD_TYPE_OFFSET, encrypt->buffer.buffer,
            3);
        c16toa(sendSz - encrypt->offset - AESGCM_EXP_IV_SZ -
            ssl->specs.aead_mac_size, encrypt->additional + AEAD_LEN_OFFSET);

    #ifdef WOLFSSL_DTLS
        if (ssl->options.dtls)
            DtlsSEQIncrement(ssl, CUR_ORDER);
    #endif

        if (encrypt->signal != NULL) {
            encrypt->signal(encrypt->signalCtx, ssl);
        }
        return sendSz;
#else
        ssl->buffers.outputBuffer.length += (word32)sendSz;

        if ( (error = SendBuffered(ssl)) < 0) {
            ssl->error = error;
            WOLFSSL_ERROR(error);
            /* store for next call if WANT_WRITE or user embedSend() that
               doesn't present like WANT_WRITE */
            ssl->buffers.plainSz  = buffSz;
            ssl->buffers.prevSent = sent;
            if (error == WC_NO_ERR_TRACE(SOCKET_ERROR_E) &&
                    (ssl->options.connReset || ssl->options.isClosed)) {
                error = SOCKET_PEER_CLOSED_E;
                ssl->error = SOCKET_PEER_CLOSED_E;
                WOLFSSL_ERROR(error);
                return 0;  /* peer reset or closed */
            }
            return error;
        }
        else {
            ssl->error = 0; /* Clear any previous errors */
        }

        sent += buffSz;

        /* only one message per attempt */
        if (ssl->options.partialWrite == 1) {
            WOLFSSL_MSG("Partial Write on, only sending one record");
            break;
        }
#endif
    }

    return sent;
}

/* process input data */
int ReceiveData(WOLFSSL* ssl, byte* output, size_t sz, int peek)
{
    int size;
    int error = ssl->error;

    WOLFSSL_ENTER("ReceiveData");

    if (sz > INT_MAX) {
        WOLFSSL_MSG("ReceiveData sz overflow");
        return WOLFSSL_FATAL_ERROR;
    }

    /* reset error state */
    if (error == WC_NO_ERR_TRACE(WANT_READ) ||
        error == WOLFSSL_ERROR_WANT_READ) {
        error = 0;
        ssl->error = 0;
    }

#ifdef WOLFSSL_DTLS
    if (ssl->options.dtls) {
        /* In DTLS mode, we forgive some errors and allow the session
         * to continue despite them. */
        if (error == WC_NO_ERR_TRACE(VERIFY_MAC_ERROR) ||
            error == WC_NO_ERR_TRACE(DECRYPT_ERROR) ||
            error == WC_NO_ERR_TRACE(DTLS_SIZE_ERROR)) {

            error = 0;
            ssl->error = 0;
        }
    }
#endif /* WOLFSSL_DTLS */

    if (error != 0 && error != WC_NO_ERR_TRACE(WANT_WRITE)
#ifdef WOLFSSL_ASYNC_CRYPT
            && error != WC_NO_ERR_TRACE(WC_PENDING_E)
#endif
#if defined(HAVE_SECURE_RENEGOTIATION) || defined(WOLFSSL_DTLS13)
            && error != WC_NO_ERR_TRACE(APP_DATA_READY)
#endif
    ) {
        WOLFSSL_MSG("User calling wolfSSL_read in error state, not allowed");
        return error;
    }

#ifdef WOLFSSL_EARLY_DATA
    if (ssl->options.side == WOLFSSL_SERVER_END &&
          ssl->earlyData > early_data_ext && ssl->earlyData < done_early_data) {
    }
    else
#endif
    {
        if (ssl_in_handshake(ssl, 0)) {
            int err;
            WOLFSSL_MSG("Handshake not complete, trying to finish");
            if ( (err = wolfSSL_negotiate(ssl)) != WOLFSSL_SUCCESS) {
            #ifdef WOLFSSL_ASYNC_CRYPT
                /* if async would block return WANT_WRITE */
                if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                    return WOLFSSL_CBIO_ERR_WANT_READ;
                }
            #endif
                return err;
            }
        }
    }

#ifdef HAVE_SECURE_RENEGOTIATION
startScr:
    if (ssl->secure_renegotiation && ssl->secure_renegotiation->startScr) {
        int ret;
        WOLFSSL_MSG("Need to start scr, server requested");
        ret = wolfSSL_Rehandshake(ssl);
        ssl->secure_renegotiation->startScr = 0;  /* only start once */
        if (ret != WOLFSSL_SUCCESS)
            return ret;
    }
#endif

    while (ssl->buffers.clearOutputBuffer.length == 0) {
        if ( (error = ProcessReply(ssl)) < 0) {
            if (error == WC_NO_ERR_TRACE(ZERO_RETURN)) {
                ssl->error = error;
                WOLFSSL_MSG("Zero return, no more data coming");
                return 0; /* no more data coming */
            }
            if (error == WC_NO_ERR_TRACE(SOCKET_ERROR_E)) {
                if (ssl->options.connReset || ssl->options.isClosed) {
                    WOLFSSL_MSG("Peer reset or closed, connection done");
                    error = SOCKET_PEER_CLOSED_E;
                    ssl->error = error;
                    WOLFSSL_ERROR(error);
                    return 0; /* peer reset or closed */
                }
            }
            ssl->error = error;
            WOLFSSL_ERROR(error);
            return error;
        }

#ifndef WOLFSSL_RW_THREADED
    #ifdef WOLFSSL_DTLS13
        if (ssl->options.dtls) {
            /* Dtls13DoScheduledWork(ssl) may return WANT_WRITE */
            if ((error = Dtls13DoScheduledWork(ssl)) < 0) {
                ssl->error = error;
                WOLFSSL_ERROR(error);
                return error;
            }
        }
    #endif /* WOLFSSL_DTLS13 */
#endif

        #ifdef HAVE_SECURE_RENEGOTIATION
            if (ssl->secure_renegotiation &&
                ssl->secure_renegotiation->startScr) {
                goto startScr;
            }
            if (ssl->secure_renegotiation && ssl->secure_renegotiation->enabled &&
                    ssl->options.handShakeState != HANDSHAKE_DONE
                    && ssl->buffers.clearOutputBuffer.length == 0) {
                /* ProcessReply processed a handshake packet and not any APP DATA
                 * so let's move the handshake along */
                int err;
                WOLFSSL_MSG("Handshake not complete, trying to finish");
                if ( (err = wolfSSL_negotiate(ssl)) != WOLFSSL_SUCCESS) {
                #ifdef WOLFSSL_ASYNC_CRYPT
                    /* if async would block return WANT_WRITE */
                    if (ssl->error == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                        return WOLFSSL_CBIO_ERR_WANT_READ;
                    }
                #endif
                    return err;
                }
            }
        #endif

#ifdef WOLFSSL_DTLS13
            /* if wolfSSL_Peek() is invoked with sz == 0 it will not block (but
             *  it processes pending non-application records) */
            if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version) && peek &&
                sz == 0 && ssl->buffers.inputBuffer.idx
                - ssl->buffers.inputBuffer.length  == 0) {
                return 0;
            }
#endif /* WOLFSSL_DTLS13 */

#ifndef WOLFSSL_TLS13_NO_PEEK_HANDSHAKE_DONE
    #ifdef WOLFSSL_TLS13
        if (IsAtLeastTLSv1_3(ssl->version) && ssl->options.handShakeDone &&
                                         ssl->curRL.type == handshake && peek) {
            WOLFSSL_MSG("Got Handshake Message in APP data");
            if (ssl->buffers.inputBuffer.length == 0) {
                ssl->error = WOLFSSL_ERROR_WANT_READ;
                return 0;
            }
        }
    #endif
#endif
    }

    size = (sz < (size_t)ssl->buffers.clearOutputBuffer.length) ?
        (int)sz : (int)ssl->buffers.clearOutputBuffer.length;

    XMEMCPY(output, ssl->buffers.clearOutputBuffer.buffer, (size_t)(size));

    if (peek == 0) {
        ssl->buffers.clearOutputBuffer.length -= (word32)size;
        ssl->buffers.clearOutputBuffer.buffer += size;
    }

    if (ssl->buffers.inputBuffer.dynamicFlag)
       ShrinkInputBuffer(ssl, NO_FORCED_FREE);

    WOLFSSL_LEAVE("ReceiveData()", size);
    return size;
}

static int SendAlert_ex(WOLFSSL* ssl, int severity, int type)
{
    byte input[ALERT_SIZE];
    byte *output;
    int  sendSz;
    int  ret;
    int  outputSz;
    int  dtlsExtra = 0;
    const char* alert_str = NULL;

    WOLFSSL_ENTER("SendAlert");

    alert_str = AlertTypeToString(type);
    if (alert_str != NULL)
    {
        WOLFSSL_MSG_EX("SendAlert: %d %s", type, alert_str);
    }
    else
    {
        WOLFSSL_MSG_EX("SendAlert: %d", type);
    }

#ifdef WOLFSSL_QUIC
    if (WOLFSSL_IS_QUIC(ssl)) {
        ret = !ssl->quic.method->send_alert(ssl, ssl->quic.enc_level_write, (uint8_t)type);
        if (ret) {
            WOLFSSL_MSG("QUIC send_alert callback error");
        }
        return ret;
    }
#endif

#ifdef HAVE_WRITE_DUP
    if (ssl->dupWrite && ssl->dupSide == READ_DUP_SIDE) {
        int notifyErr = 0;

        WOLFSSL_MSG("Read dup side cannot write alerts, notifying sibling");

        if (type == close_notify) {
            notifyErr = ZERO_RETURN;
        } else if (severity == alert_fatal) {
            notifyErr = FATAL_ERROR;
        }

        if (notifyErr != 0) {
            return NotifyWriteSide(ssl, notifyErr);
        }

        return 0;
    }
#endif

    ssl->pendingAlert.code = type;
    ssl->pendingAlert.level = severity;

   #ifdef OPENSSL_EXTRA
        if (ssl->CBIS != NULL) {
            ssl->CBIS(ssl, WOLFSSL_CB_ALERT, type);
        }
   #endif
   #ifdef WOLFSSL_DTLS
        if (ssl->options.dtls)
           dtlsExtra = DTLS_RECORD_EXTRA;
   #endif

    /* check for available size */
    outputSz = ALERT_SIZE + MAX_MSG_EXTRA + dtlsExtra;
    if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) {
#ifdef WOLFSSL_DTLS
        /* If CheckAvailableSize returned WANT_WRITE due to a blocking write
         * then discard pending output and just send the alert. */
        if (ssl->options.dtls) {
            if (ret != WC_NO_ERR_TRACE(WANT_WRITE) || severity != alert_fatal)
                return ret;
            ShrinkOutputBuffer(ssl);
            if ((ret = CheckAvailableSize(ssl, outputSz)) != 0) {
                return ret;
            }
        }
        else {
            return ret;
        }
#else
        return ret;
#endif
    }

    /* Check output buffer */
    if (ssl->buffers.outputBuffer.buffer == NULL)
        return BUFFER_E;

    /* get output buffer */
    output = GetOutputBuffer(ssl);
    input[0] = (byte)severity;
    input[1] = (byte)type;
    ssl->alert_history.last_tx.code = type;
    ssl->alert_history.last_tx.level = severity;
    if (severity == alert_fatal) {
#ifdef WOLFSSL_DTLS
        /* Mark as closed in dtls only once we enter stateful mode. */
        if (!ssl->options.dtls || ssl->options.dtlsStateful)
#endif
            ssl->options.isClosed = 1;  /* Don't send close_notify */
    }

    /* send encrypted alert if encryption is on - can be a rehandshake over
     * an existing encrypted channel.
     * TLS 1.3 encrypts handshake packets after the ServerHello
     */
    if (IsEncryptionOn(ssl, 1)) {
#ifdef WOLFSSL_DTLS13
        if (ssl->options.dtls
            && IsAtLeastTLSv1_3(ssl->version)
            && !w64Equal(ssl->dtls13EncryptEpoch->epochNumber, ssl->dtls13Epoch)) {
            ret = Dtls13SetEpochKeys(ssl, ssl->dtls13Epoch, ENCRYPT_SIDE_ONLY);
            if (ret != 0)
                return ret;
        }
#endif /* WOLFSSL_DTLS13 */

        sendSz = BuildMessage(ssl, output, outputSz, input, ALERT_SIZE, alert,
                                                                       0, 0, 0, CUR_ORDER);
    }
    else {

#ifdef WOLFSSL_DTLS13
        if (ssl->options.dtls && IsAtLeastTLSv1_3(ssl->version)) {
            ret = Dtls13RlAddPlaintextHeader(ssl, output, alert, ALERT_SIZE);
            if (ret != 0)
                return ret;
        }
        else
#endif /* WOLFSSL_DTLS13 */
            {
                AddRecordHeader(output, ALERT_SIZE, alert, ssl, CUR_ORDER);
#ifdef WOLFSSL_DTLS
                /* AddRecordHeader doesn't increment the seq number */
                if (ssl->options.dtls)
                    DtlsSEQIncrement(ssl, CUR_ORDER);
#endif
            }

        output += RECORD_HEADER_SZ;
        #ifdef WOLFSSL_DTLS
            if (ssl->options.dtls)
                output += DTLS_RECORD_EXTRA;
        #endif
        XMEMCPY(output, input, ALERT_SIZE);

        sendSz = RECORD_HEADER_SZ + ALERT_SIZE;
        #ifdef WOLFSSL_DTLS
            if (ssl->options.dtls)
                sendSz += DTLS_RECORD_EXTRA;
        #endif
    }
    if (sendSz < 0)
        return BUILD_MSG_ERROR;

    #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
        if (ssl->hsInfoOn)
            AddPacketName(ssl, "Alert");
        if (ssl->toInfoOn) {
            ret = AddPacketInfo(ssl, "Alert", alert, output, sendSz,
                    WRITE_PROTO, 0, ssl->heap);
            if (ret != 0)
                return ret;
        }
    #endif

    ssl->buffers.outputBuffer.length += (word32)sendSz;

    ret = SendBuffered(ssl);

    ssl->pendingAlert.code = 0;
    ssl->pendingAlert.level = alert_none;

    WOLFSSL_LEAVE("SendAlert", ret);

    return ret;
}

#endif /* !NO_TLS */

int RetrySendAlert(WOLFSSL* ssl)
{
    int ret = 0;
    int type;
    int severity;
    WOLFSSL_ENTER("RetrySendAlert");

    if (ssl == NULL) {
        return BAD_FUNC_ARG;
    }

    type = ssl->pendingAlert.code;
    severity = ssl->pendingAlert.level;

    if (severity == alert_none)
        return 0;

    ssl->pendingAlert.code = 0;
    ssl->pendingAlert.level = alert_none;

#ifndef NO_TLS
    ret = SendAlert_ex(ssl, severity, type);
#else
    (void)type;
#endif
    return ret;
}

/* send alert message */
int SendAlert(WOLFSSL* ssl, int severity, int type)
{
    int ret = 0;
    WOLFSSL_ENTER("SendAlert");

    if (ssl == NULL) {
        return BAD_FUNC_ARG;
    }

    if (ssl->pendingAlert.level != alert_none) {
        ret = RetrySendAlert(ssl);
        if (ret != 0) {
            if (ssl->pendingAlert.level == alert_none ||
                    (ssl->pendingAlert.level != alert_fatal &&
                            severity == alert_fatal)) {
                /* Store current alert if pendingAlert is empty or if current
                 * is fatal and previous was not */
                ssl->pendingAlert.code = type;
                ssl->pendingAlert.level = severity;
            }
            return ret;
        }
    }
#ifndef NO_TLS
    ret = SendAlert_ex(ssl, severity, type);
#endif /* !NO_TLS */
    return ret;
}


#ifdef WOLFSSL_DEBUG_TRACE_ERROR_CODES_H
#include <wolfssl/debug-untrace-error-codes.h>
#endif

#if !defined(NO_ERROR_STRINGS) && (defined(OPENSSL_EXTRA) || \
    defined(OPENSSL_EXTRA_X509_SMALL) || \
    defined(HAVE_WEBSERVER) || defined(HAVE_MEMCACHED))
static const char* wolfSSL_ERR_reason_error_string_OpenSSL(unsigned long e)
{
    switch (e) {
    /* TODO: -WOLFSSL_X509_V_ERR_CERT_SIGNATURE_FAILURE. Conflicts with
     *       -WOLFSSL_ERROR_WANT_CONNECT.
     */
    case WOLFSSL_X509_V_ERR_CRL_HAS_EXPIRED:
        return "CRL has expired";

    case WOLFSSL_X509_V_ERR_UNABLE_TO_GET_CRL:
        return "unable to get CRL";

    case WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID:
        return "certificate not yet valid";

    case WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED:
        return "certificate has expired";

    case WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
        return "certificate signature failure";

    case WOLFSSL_X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
        return "format error in certificate's notAfter field";

    case WOLFSSL_X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
        return "self-signed certificate in certificate chain";

    case WOLFSSL_X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
        return "unable to get local issuer certificate";

    case WOLFSSL_X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
        return "unable to verify the first certificate";

    case WOLFSSL_X509_V_ERR_CERT_CHAIN_TOO_LONG:
        return "certificate chain too long";

    case WOLFSSL_X509_V_ERR_CERT_REVOKED:
        return "certificate revoked";

    case WOLFSSL_X509_V_ERR_INVALID_CA:
        return "invalid CA certificate";

    case WOLFSSL_X509_V_ERR_PATH_LENGTH_EXCEEDED:
        return "path length constraint exceeded";

    case WOLFSSL_X509_V_ERR_CERT_REJECTED:
        return "certificate rejected";

    case WOLFSSL_X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
        return "subject issuer mismatch";

    case WOLFSSL_X509_V_ERR_HOSTNAME_MISMATCH:
        return "hostname mismatch";

    case WOLFSSL_X509_V_ERR_IP_ADDRESS_MISMATCH:
        return "IP address mismatch";

    default:
        return NULL;
    }
}
#endif /* OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL || HAVE_WEBSERVER || HAVE_MEMCACHED */

const char* wolfSSL_ERR_reason_error_string(unsigned long e)
{
#ifdef NO_ERROR_STRINGS

    (void)e;
    return "no support for error strings built in";

#else

    int error = (int)e;

    if (error > 0) {
#if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) || \
    defined(HAVE_WEBSERVER) || defined(HAVE_MEMCACHED)
    /* Check the OpenSSL error strings first. */
        const char* ossl_err = wolfSSL_ERR_reason_error_string_OpenSSL(e);
        if (ossl_err != NULL) {
            return ossl_err;
        }
    /* try to find error strings from wolfSSL */
#endif
        error = -error;
    }
    /* pass to wolfCrypt */
    if ((error <= WC_SPAN1_FIRST_E && error >= WC_SPAN1_MIN_CODE_E) ||
        (error <= WC_SPAN2_FIRST_E && error >= WC_SPAN2_MIN_CODE_E))
    {
        return wc_GetErrorString(error);
    }

    if (error == 0) {
#ifdef OPENSSL_EXTRA
        return "ok";
#else
        return "unknown error number";
#endif
    }

    switch ((enum wolfSSL_ErrorCodes)error) { /* // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange) */

    case UNSUPPORTED_SUITE :
        return "unsupported cipher suite";

    case INPUT_CASE_ERROR :
        return "input state error";

    case PREFIX_ERROR :
        return "bad index to key rounds";

    case MEMORY_ERROR :
        return "out of memory";

    case VERIFY_FINISHED_ERROR :
        return "verify problem on finished";

    case VERIFY_MAC_ERROR :
        return "verify mac problem";

    case PARSE_ERROR :
        return "parse error on header";

    case SIDE_ERROR :
        return "wrong client/server type";

    case NO_PEER_CERT : /* OpenSSL compatibility expects this exact text */
        return "peer did not return a certificate";

    case UNKNOWN_HANDSHAKE_TYPE :
        return "weird handshake type";

    case SOCKET_ERROR_E :
        return "error state on socket";

    case SOCKET_NODATA :
        return "expected data, not there";

    case INCOMPLETE_DATA :
        return "don't have enough data to complete task";

    case UNKNOWN_RECORD_TYPE :
        return "unknown type in record hdr";

    case DECRYPT_ERROR :
        return "error during decryption";

    case FATAL_ERROR :
        return "received alert fatal error";

    case ENCRYPT_ERROR :
        return "error during encryption";

    case FREAD_ERROR :
        return "fread problem";

    case NO_PEER_KEY :
        return "need peer's key";

    case NO_PRIVATE_KEY :
        return "need the private key";

    case NO_DH_PARAMS :
        return "server missing DH params";

    case RSA_PRIVATE_ERROR :
        return "error during rsa priv op";

    case MATCH_SUITE_ERROR :
        return "can't match cipher suite";

    case COMPRESSION_ERROR :
        return "compression mismatch error";

    case BUILD_MSG_ERROR :
        return "build message failure";

    case BAD_HELLO :
        return "client hello malformed";

    case DOMAIN_NAME_MISMATCH :
        return "peer subject name mismatch";

    case IPADDR_MISMATCH :
        return "peer ip address mismatch";

    case WANT_READ :
    case WOLFSSL_ERROR_WANT_READ_E :
        return "non-blocking socket wants data to be read";

    case NOT_READY_ERROR :
        return "handshake layer not ready yet, complete first";

    case VERSION_ERROR :
        return "record layer version error";

    case WANT_WRITE :
    case WOLFSSL_ERROR_WANT_WRITE_E :
        return "non-blocking socket write buffer full";

    case WOLFSSL_ERROR_WANT_CONNECT_E :
    case WOLFSSL_ERROR_WANT_ACCEPT_E :
        return "The underlying BIO was not yet connected";

    case WOLFSSL_ERROR_SYSCALL_E :
        return "fatal I/O error in TLS layer";

    case WOLFSSL_ERROR_WANT_X509_LOOKUP_E :
        return "application client cert callback asked to be called again";

    case BUFFER_ERROR :
        return "malformed buffer input error";

    case VERIFY_CERT_ERROR :
        return "verify problem on certificate";

    case VERIFY_SIGN_ERROR :
        return "verify problem based on signature";

    case CLIENT_ID_ERROR :
        return "psk client identity error";

    case SERVER_HINT_ERROR:
        return "psk server hint error";

    case PSK_KEY_ERROR:
        return "psk key callback error";

    case DUPE_ENTRY_E:
        return "duplicate entry error";

    case GETTIME_ERROR:
        return "gettimeofday() error";

    case GETITIMER_ERROR:
        return "getitimer() error";

    case SIGACT_ERROR:
        return "sigaction() error";

    case SETITIMER_ERROR:
        return "setitimer() error";

    case LENGTH_ERROR:
        return "record layer length error";

    case PEER_KEY_ERROR:
        return "can't decode peer key";

    case ZERO_RETURN:
    case WOLFSSL_ERROR_ZERO_RETURN_E :
        return "peer sent close notify alert";

    case ECC_CURVETYPE_ERROR:
        return "Bad ECC Curve Type or unsupported";

    case ECC_CURVE_ERROR:
        return "Bad ECC Curve or unsupported";

    case ECC_PEERKEY_ERROR:
        return "Bad ECC Peer Key";

    case ECC_MAKEKEY_ERROR:
        return "ECC Make Key failure";

    case ECC_EXPORT_ERROR:
        return "ECC Export Key failure";

    case ECC_SHARED_ERROR:
        return "ECC DHE shared failure";

    case NOT_CA_ERROR:
        return "Not a CA by basic constraint error";

    case BAD_CERT_MANAGER_ERROR:
        return "Bad Cert Manager error";

    case OCSP_CERT_REVOKED:
        return "OCSP Cert revoked";

    case CRL_CERT_REVOKED:
#ifdef OPENSSL_EXTRA
        return "certificate revoked";
#else
        return "CRL Cert revoked";
#endif

    case CRL_MISSING:
        return "CRL missing, not loaded";

    case CRYPTO_POLICY_FORBIDDEN:
        return "Operation forbidden by system crypto-policy";

    case MONITOR_SETUP_E:
        return "CRL monitor setup error";

    case THREAD_CREATE_E:
        return "Thread creation problem";

    case OCSP_NEED_URL:
        return "OCSP need URL";

    case OCSP_CERT_UNKNOWN:
        return "OCSP Cert unknown";

    case OCSP_LOOKUP_FAIL:
        return "OCSP Responder lookup fail";

    case MAX_CHAIN_ERROR:
        return "Maximum Chain Depth Exceeded";

    case MAX_CERT_EXTENSIONS_ERR:
        return "Maximum Cert Extension Exceeded";

    case COOKIE_ERROR:
        return "DTLS Cookie Error";

    case SEQUENCE_ERROR:
        return "DTLS Sequence Error";

    case SUITES_ERROR:
        return "Suites Pointer Error";

    case OUT_OF_ORDER_E:
        return "Out of order message, fatal";

    case BAD_KEA_TYPE_E:
        return "Bad KEA type found";

    case SANITY_CIPHER_E:
        return "Sanity check on ciphertext failed";

    case RECV_OVERFLOW_E:
        return "Receive callback returned more than requested";

    case GEN_COOKIE_E:
        return "Generate Cookie Error";

    case NO_PEER_VERIFY:
        return "Need peer certificate verify Error";

    case FWRITE_ERROR:
        return "fwrite Error";

    case CACHE_MATCH_ERROR:
        return "Cache restore header match Error";

    case UNKNOWN_SNI_HOST_NAME_E:
        return "Unrecognized host name Error";

    case UNKNOWN_MAX_FRAG_LEN_E:
        return "Unrecognized max frag len Error";

    case KEYUSE_SIGNATURE_E:
        return "Key Use digitalSignature not set Error";

    case KEYUSE_ENCIPHER_E:
        return "Key Use keyEncipherment not set Error";

    case EXTKEYUSE_AUTH_E:
        return "Ext Key Use server/client auth not set Error";

    case SEND_OOB_READ_E:
        return "Send Callback Out of Bounds Read Error";

    case SECURE_RENEGOTIATION_E:
        return "Invalid Renegotiation Error";

    case SESSION_TICKET_LEN_E:
        return "Session Ticket Too Long Error";

    case SESSION_TICKET_EXPECT_E:
        return "Session Ticket Error";

    case SCR_DIFFERENT_CERT_E:
        return "SCR Different cert error";

    case SESSION_SECRET_CB_E:
        return "Session Secret Callback Error";

    case NO_CHANGE_CIPHER_E:
        return "Finished received from peer before Change Cipher Error";

    case SANITY_MSG_E:
        return "Sanity Check on message order Error";

    case DUPLICATE_MSG_E:
        return "Duplicate HandShake message Error";

    case SNI_UNSUPPORTED:
        return "Protocol version does not support SNI Error";

    case SOCKET_PEER_CLOSED_E:
        return "Peer closed underlying transport Error";

    case BAD_TICKET_KEY_CB_SZ:
        return "Bad user session ticket key callback Size Error";

    case BAD_TICKET_MSG_SZ:
        return "Bad session ticket message Size Error";

    case BAD_TICKET_ENCRYPT:
        return "Bad user ticket callback encrypt Error";

    case DH_KEY_SIZE_E:
        return "DH key too small Error";

    case SNI_ABSENT_ERROR:
        return "No Server Name Indication extension Error";

    case RSA_SIGN_FAULT:
        return "RSA Signature Fault Error";

    case HANDSHAKE_SIZE_ERROR:
        return "Handshake message too large Error";

    case UNKNOWN_ALPN_PROTOCOL_NAME_E:
        return "Unrecognized protocol name Error";

    case BAD_CERTIFICATE_STATUS_ERROR:
        return "Bad Certificate Status Message Error";

    case OCSP_INVALID_STATUS:
        return "Invalid OCSP Status Error";

    case OCSP_WANT_READ:
        return "OCSP nonblock wants read";

    case RSA_KEY_SIZE_E:
        return "RSA key too small";

    case ECC_KEY_SIZE_E:
        return "ECC key too small";

    case DTLS_EXPORT_VER_E:
        return "Version needs updated after code change or version mismatch";

    case INPUT_SIZE_E:
        return "Input size too large Error";

    case CTX_INIT_MUTEX_E:
        return "Initialize ctx mutex error";

    case EXT_MASTER_SECRET_NEEDED_E:
        return "Extended Master Secret must be enabled to resume EMS session";

    case DTLS_POOL_SZ_E:
        return "Maximum DTLS pool size exceeded";

    case DECODE_E:
        return "Decode handshake message error";

    case WRITE_DUP_READ_E:
        return "Write dup write side can't read error";

    case WRITE_DUP_WRITE_E:
        return "Write dup read side can't write error";

    case INVALID_CERT_CTX_E:
        return "Certificate context does not match request or not empty";

    case BAD_KEY_SHARE_DATA:
        return "The Key Share data contains a group which is invalid";

    case MISSING_HANDSHAKE_DATA:
        return "The handshake message is missing required data";

    case BAD_BINDER: /* OpenSSL compatibility expects this exact text */
        return "binder does not verify";

    case EXT_NOT_ALLOWED:
        return "Extension type not allowed in handshake message type";

    case INVALID_PARAMETER:
        return "The security parameter is invalid";

    case UNSUPPORTED_EXTENSION:
        return "TLS Extension not requested by the client";

    case PRF_MISSING:
        return "Pseudo-random function is not enabled";

    case KEY_SHARE_ERROR:
        return "Key share extension did not contain a valid named group";

    case POST_HAND_AUTH_ERROR:
        return "Client will not do post handshake authentication";

    case HRR_COOKIE_ERROR:
        return "Cookie does not match one sent in HelloRetryRequest";

    case MCAST_HIGHWATER_CB_E:
        return "Multicast highwater callback returned error";

    case ALERT_COUNT_E:
        return "Alert Count exceeded error";

    case EXT_MISSING:
        return "Required TLS extension missing";

    case DTLS_RETX_OVER_TX:
        return "DTLS interrupting flight transmit with retransmit";

    case DH_PARAMS_NOT_FFDHE_E:
        return "Server DH parameters were not from the FFDHE set as required";

    case TCA_INVALID_ID_TYPE:
        return "TLS Extension Trusted CA ID type invalid";

    case TCA_ABSENT_ERROR:
        return "TLS Extension Trusted CA ID response absent";

    case TSIP_MAC_DIGSZ_E:
        return "TSIP MAC size invalid, must be sized for SHA-1 or SHA-256";

    case CLIENT_CERT_CB_ERROR:
        return "Error importing client cert or key from callback";

    case SSL_SHUTDOWN_ALREADY_DONE_E:
        return "Shutdown has already occurred";

    case TLS13_SECRET_CB_E:
        return "TLS1.3 Secret Callback Error";

    case DTLS_SIZE_ERROR:
        return "DTLS trying to send too much in single datagram error";

    case NO_CERT_ERROR:
        return "TLS1.3 No Certificate Set Error";

    case APP_DATA_READY:
        return "Application data is available for reading";

    case TOO_MUCH_EARLY_DATA:
        return "Too much early data";

    case SOCKET_FILTERED_E:
        return "Session stopped by network filter";

    case UNSUPPORTED_CERTIFICATE:
        return "Unsupported certificate type";

    case HTTP_TIMEOUT:
        return "HTTP timeout for OCSP or CRL req";

    case HTTP_RECV_ERR:
        return "HTTP Receive error";

    case HTTP_HEADER_ERR:
        return "HTTP Header error";

    case HTTP_PROTO_ERR:
        return "HTTP Protocol error";

    case HTTP_STATUS_ERR:
        return "HTTP Status error";

    case HTTP_VERSION_ERR:
        return "HTTP Version error";

    case HTTP_APPSTR_ERR:
        return "HTTP Application string error";

    case UNSUPPORTED_PROTO_VERSION:
        #ifdef OPENSSL_EXTRA
        return "WRONG_SSL_VERSION";
        #else
        return "bad/unsupported protocol version";
        #endif

    case FALCON_KEY_SIZE_E:
        return "Wrong key size for Falcon.";

    case DILITHIUM_KEY_SIZE_E:
        return "Wrong key size for Dilithium.";

    case QUIC_TP_MISSING_E:
        return "QUIC transport parameter not set";

    case QUIC_WRONG_ENC_LEVEL:
        return "QUIC data received at wrong encryption level";

    case DTLS_CID_ERROR:
        return "DTLS ConnectionID mismatch or missing";

    case DTLS_TOO_MANY_FRAGMENTS_E:
        return "Received too many fragmented messages from peer error";

    case DUPLICATE_TLS_EXT_E:
        return "Duplicate TLS extension in message.";

    case WOLFSSL_ALPN_NOT_FOUND:
        return "TLS extension not found";

    case WOLFSSL_BAD_CERTTYPE:
        return "Certificate type not supported";

    case WOLFSSL_BAD_STAT:
        return "bad status";

    case WOLFSSL_BAD_PATH:
        return "No certificates found at designated path";

    case WOLFSSL_BAD_FILETYPE:
        return "Data format not supported";

    case WOLFSSL_BAD_FILE:
        return "Input/output error on file";

    case WOLFSSL_NOT_IMPLEMENTED:
        return "Function not implemented";

    case WOLFSSL_UNKNOWN:
        return "Unknown algorithm (EVP)";

    case WOLFSSL_FATAL_ERROR:
        return "fatal error";

    case WOLFSSL_PEM_R_NO_START_LINE_E:
        return "No more matching objects found (PEM)";

    case WOLFSSL_PEM_R_PROBLEMS_GETTING_PASSWORD_E:
        return "Error getting password (PEM)";

    case WOLFSSL_PEM_R_BAD_PASSWORD_READ_E:
        return "Bad password (PEM)";

    case WOLFSSL_PEM_R_BAD_DECRYPT_E :
        return "Decryption failed (PEM)";

    case WOLFSSL_ASN1_R_HEADER_TOO_LONG_E:
        return "ASN header too long (compat)";

    case WOLFSSL_EVP_R_BAD_DECRYPT_E :
        return "Decryption failed (EVP)";

    case WOLFSSL_EVP_R_BN_DECODE_ERROR:
        return "Bignum decode error (EVP)";

    case WOLFSSL_EVP_R_DECODE_ERROR  :
        return "Decode error (EVP)";

    case WOLFSSL_EVP_R_PRIVATE_KEY_DECODE_ERROR:
        return "Private key decode error (EVP)";

    case SESSION_TICKET_NONCE_OVERFLOW:
        return "Session ticket nonce overflow";
    }

    return "unknown error number";

#endif /* NO_ERROR_STRINGS */
}

#ifdef WOLFSSL_DEBUG_TRACE_ERROR_CODES
#include <wolfssl/debug-trace-error-codes.h>
#endif

const char* wolfSSL_ERR_func_error_string(unsigned long e)
{
    (void)e;
    WOLFSSL_MSG("wolfSSL_ERR_func_error_string does not return the name of "
                "the function that failed. Please inspect the wolfSSL debug "
                "logs to determine where the error occurred.");
    return "";
}

/* return library name
 * @param e error code
 * @return text library name,
 *    if there is no suitable library found, returns empty string
 */
const char* wolfSSL_ERR_lib_error_string(unsigned long e)
{
    int libe = 0;

    (void)libe;
    (void)e;

#if defined(OPENSSL_EXTRA)
    libe = wolfSSL_ERR_GET_LIB(e);
    switch (libe) {
    case WOLFSSL_ERR_LIB_PEM:
        return "wolfSSL PEM routines";
    case WOLFSSL_ERR_LIB_EVP:
        return "wolfSSL digital envelope routines";
    default:
        return "";
    }
#else
    return "";
#endif
}

void SetErrorString(int error, char* str)
{
    XSTRNCPY(str, wolfSSL_ERR_reason_error_string((unsigned long)error), WOLFSSL_MAX_ERROR_SZ);
    str[WOLFSSL_MAX_ERROR_SZ-1] = 0;
}

#ifdef NO_CIPHER_SUITE_ALIASES
    #ifndef NO_ERROR_STRINGS
        #if defined(OPENSSL_ALL) || defined(WOLFSSL_QT)
            #define SUITE_INFO(x,y,z,w,v,u) {(x),(y),(z),(w),(v),(u),WOLFSSL_CIPHER_SUITE_FLAG_NONE}
            #define SUITE_ALIAS(x,z,w,v,u) /* null expansion */
        #else
            #define SUITE_INFO(x,y,z,w,v,u) {(x),(y),(z),(w),WOLFSSL_CIPHER_SUITE_FLAG_NONE}
            #define SUITE_ALIAS(x,z,w,v,u) /* null expansion */
        #endif
    #else
        #if defined(OPENSSL_ALL) || defined(WOLFSSL_QT)
            #define SUITE_INFO(x,y,z,w,v,u) {(x),(z),(w),(v),(u),WOLFSSL_CIPHER_SUITE_FLAG_NONE}
            #define SUITE_ALIAS(x,z,w,v,u) /* null expansion */
        #else
            #define SUITE_INFO(x,y,z,w,v,u) {(x),(z),(w),WOLFSSL_CIPHER_SUITE_FLAG_NONE}
            #define SUITE_ALIAS(x,z,w,v,u) /* null expansion */
        #endif
    #endif
#else /* !NO_CIPHER_SUITE_ALIASES */

    /* note that the comma is included at the end of the SUITE_ALIAS() macro
     * definitions, to allow aliases to be gated out by the above null macros
     * in the NO_CIPHER_SUITE_ALIASES section.
     */

    #ifndef NO_ERROR_STRINGS
        #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_QT) || \
            defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_NGINX)
            #define SUITE_INFO(x,y,z,w,v,u) {(x),(y),(z),(w),(v),(u),WOLFSSL_CIPHER_SUITE_FLAG_NONE}
            #define SUITE_ALIAS(x,z,w,v,u) {(x),"",(z),(w),(v),(u),WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS},
        #else
            #define SUITE_INFO(x,y,z,w,v,u) {(x),(y),(z),(w),WOLFSSL_CIPHER_SUITE_FLAG_NONE}
            #define SUITE_ALIAS(x,z,w,v,u) {(x),"",(z),(w),WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS},
        #endif
    #else
        #if defined(OPENSSL_EXTRA) || defined(WOLFSSL_QT) || \
            defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_NGINX)
            #define SUITE_INFO(x,y,z,w,v,u) {(x),(z),(w),(v),(u),WOLFSSL_CIPHER_SUITE_FLAG_NONE}
            #define SUITE_ALIAS(x,z,w,v,u) {(x),(z),(w),(v),(u),WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS},
        #else
            #define SUITE_INFO(x,y,z,w,v,u) {(x),(z),(w),WOLFSSL_CIPHER_SUITE_FLAG_NONE}
            #define SUITE_ALIAS(x,z,w,v,u) {(x),(z),(w),WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS},
        #endif
    #endif
#endif /* NO_CIPHER_SUITE_ALIASES */

#ifndef NO_TLS
static const CipherSuiteInfo cipher_names[] =
{

#ifdef BUILD_TLS_AES_128_GCM_SHA256
    SUITE_INFO("TLS13-AES128-GCM-SHA256","TLS_AES_128_GCM_SHA256",TLS13_BYTE,TLS_AES_128_GCM_SHA256, TLSv1_3_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_AES_256_GCM_SHA384
    SUITE_INFO("TLS13-AES256-GCM-SHA384","TLS_AES_256_GCM_SHA384",TLS13_BYTE,TLS_AES_256_GCM_SHA384, TLSv1_3_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_CHACHA20_POLY1305_SHA256
    SUITE_INFO("TLS13-CHACHA20-POLY1305-SHA256","TLS_CHACHA20_POLY1305_SHA256",TLS13_BYTE,TLS_CHACHA20_POLY1305_SHA256, TLSv1_3_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_AES_128_CCM_SHA256
    SUITE_INFO("TLS13-AES128-CCM-SHA256","TLS_AES_128_CCM_SHA256",TLS13_BYTE,TLS_AES_128_CCM_SHA256, TLSv1_3_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_AES_128_CCM_8_SHA256
    SUITE_INFO("TLS13-AES128-CCM-8-SHA256","TLS_AES_128_CCM_8_SHA256",TLS13_BYTE,TLS_AES_128_CCM_8_SHA256,TLSv1_3_MINOR, SSLv3_MAJOR),
    SUITE_ALIAS("TLS13-AES128-CCM8-SHA256",TLS13_BYTE,TLS_AES_128_CCM_8_SHA256,TLSv1_3_MINOR, SSLv3_MAJOR)
#endif

#ifdef BUILD_TLS_SM4_GCM_SM3
    SUITE_INFO("TLS13-SM4-GCM-SM3","TLS_SM4_GCM_SM3",CIPHER_BYTE,TLS_SM4_GCM_SM3, TLSv1_3_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_SM4_CCM_SM3
    SUITE_INFO("TLS13-SM4-CCM-SM3","TLS_SM4_CCM_SM3",CIPHER_BYTE,TLS_SM4_CCM_SM3, TLSv1_3_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_SHA256_SHA256
    SUITE_INFO("TLS13-SHA256-SHA256","TLS_SHA256_SHA256",ECC_BYTE,TLS_SHA256_SHA256,TLSv1_3_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_SHA384_SHA384
    SUITE_INFO("TLS13-SHA384-SHA384","TLS_SHA384_SHA384",ECC_BYTE,TLS_SHA384_SHA384,TLSv1_3_MINOR, SSLv3_MAJOR),
#endif

#ifndef WOLFSSL_NO_TLS12

#ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA
    SUITE_INFO("RC4-SHA","SSL_RSA_WITH_RC4_128_SHA",CIPHER_BYTE,SSL_RSA_WITH_RC4_128_SHA,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5
    SUITE_INFO("RC4-MD5","SSL_RSA_WITH_RC4_128_MD5",CIPHER_BYTE,SSL_RSA_WITH_RC4_128_MD5,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA
    SUITE_INFO("DES-CBC3-SHA","SSL_RSA_WITH_3DES_EDE_CBC_SHA",CIPHER_BYTE,SSL_RSA_WITH_3DES_EDE_CBC_SHA,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA
    SUITE_INFO("AES128-SHA","TLS_RSA_WITH_AES_128_CBC_SHA",CIPHER_BYTE,TLS_RSA_WITH_AES_128_CBC_SHA,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA
    SUITE_INFO("AES256-SHA","TLS_RSA_WITH_AES_256_CBC_SHA",CIPHER_BYTE,TLS_RSA_WITH_AES_256_CBC_SHA,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_NULL_MD5
    SUITE_INFO("NULL-MD5","TLS_RSA_WITH_NULL_MD5",CIPHER_BYTE,TLS_RSA_WITH_NULL_MD5,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_NULL_SHA
    SUITE_INFO("NULL-SHA","TLS_RSA_WITH_NULL_SHA",CIPHER_BYTE,TLS_RSA_WITH_NULL_SHA,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_NULL_SHA256
    SUITE_INFO("NULL-SHA256","TLS_RSA_WITH_NULL_SHA256",CIPHER_BYTE,TLS_RSA_WITH_NULL_SHA256,TLSv1_2_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
    SUITE_INFO("DHE-RSA-AES128-SHA","TLS_DHE_RSA_WITH_AES_128_CBC_SHA",CIPHER_BYTE,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
    SUITE_INFO("DHE-RSA-AES256-SHA","TLS_DHE_RSA_WITH_AES_256_CBC_SHA",CIPHER_BYTE,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_256_GCM_SHA384
    SUITE_INFO("DHE-PSK-AES256-GCM-SHA384","TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",CIPHER_BYTE,TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,TLSv1_2_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_128_GCM_SHA256
    SUITE_INFO("DHE-PSK-AES128-GCM-SHA256","TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",CIPHER_BYTE,TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,TLSv1_2_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_256_GCM_SHA384
    SUITE_INFO("PSK-AES256-GCM-SHA384","TLS_PSK_WITH_AES_256_GCM_SHA384",CIPHER_BYTE,TLS_PSK_WITH_AES_256_GCM_SHA384,TLSv1_2_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_128_GCM_SHA256
    SUITE_INFO("PSK-AES128-GCM-SHA256","TLS_PSK_WITH_AES_128_GCM_SHA256",CIPHER_BYTE,TLS_PSK_WITH_AES_128_GCM_SHA256,TLSv1_2_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_256_CBC_SHA384
    SUITE_INFO("DHE-PSK-AES256-CBC-SHA384","TLS_DHE_PSK_WITH_AES_256_CBC_SHA384",CIPHER_BYTE,TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_128_CBC_SHA256
    SUITE_INFO("DHE-PSK-AES128-CBC-SHA256","TLS_DHE_PSK_WITH_AES_128_CBC_SHA256",CIPHER_BYTE,TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA384
    SUITE_INFO("PSK-AES256-CBC-SHA384","TLS_PSK_WITH_AES_256_CBC_SHA384",CIPHER_BYTE,TLS_PSK_WITH_AES_256_CBC_SHA384,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA256
    SUITE_INFO("PSK-AES128-CBC-SHA256","TLS_PSK_WITH_AES_128_CBC_SHA256",CIPHER_BYTE,TLS_PSK_WITH_AES_128_CBC_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA
    SUITE_INFO("PSK-AES128-CBC-SHA","TLS_PSK_WITH_AES_128_CBC_SHA",CIPHER_BYTE,TLS_PSK_WITH_AES_128_CBC_SHA,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA
    SUITE_INFO("PSK-AES256-CBC-SHA","TLS_PSK_WITH_AES_256_CBC_SHA",CIPHER_BYTE,TLS_PSK_WITH_AES_256_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_128_CCM
    SUITE_INFO("DHE-PSK-AES128-CCM","TLS_DHE_PSK_WITH_AES_128_CCM",ECC_BYTE,TLS_DHE_PSK_WITH_AES_128_CCM,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_AES_256_CCM
    SUITE_INFO("DHE-PSK-AES256-CCM","TLS_DHE_PSK_WITH_AES_256_CCM",ECC_BYTE,TLS_DHE_PSK_WITH_AES_256_CCM,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_128_CCM
    SUITE_INFO("PSK-AES128-CCM","TLS_PSK_WITH_AES_128_CCM",ECC_BYTE,TLS_PSK_WITH_AES_128_CCM,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_256_CCM
    SUITE_INFO("PSK-AES256-CCM","TLS_PSK_WITH_AES_256_CCM",ECC_BYTE,TLS_PSK_WITH_AES_256_CCM,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_128_CCM_8
    SUITE_INFO("PSK-AES128-CCM-8","TLS_PSK_WITH_AES_128_CCM_8",ECC_BYTE,TLS_PSK_WITH_AES_128_CCM_8,TLSv1_MINOR,SSLv3_MAJOR),
    SUITE_ALIAS("PSK-AES128-CCM8",ECC_BYTE,TLS_PSK_WITH_AES_128_CCM_8,TLSv1_MINOR,SSLv3_MAJOR)
#endif

#ifdef BUILD_TLS_PSK_WITH_AES_256_CCM_8
    SUITE_INFO("PSK-AES256-CCM-8","TLS_PSK_WITH_AES_256_CCM_8",ECC_BYTE,TLS_PSK_WITH_AES_256_CCM_8,TLSv1_MINOR,SSLv3_MAJOR),
    SUITE_ALIAS("PSK-AES256-CCM8",ECC_BYTE,TLS_PSK_WITH_AES_256_CCM_8,TLSv1_MINOR,SSLv3_MAJOR)
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_NULL_SHA384
    SUITE_INFO("DHE-PSK-NULL-SHA384","TLS_DHE_PSK_WITH_NULL_SHA384",CIPHER_BYTE,TLS_DHE_PSK_WITH_NULL_SHA384,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_NULL_SHA256
    SUITE_INFO("DHE-PSK-NULL-SHA256","TLS_DHE_PSK_WITH_NULL_SHA256",CIPHER_BYTE,TLS_DHE_PSK_WITH_NULL_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_NULL_SHA384
    SUITE_INFO("PSK-NULL-SHA384","TLS_PSK_WITH_NULL_SHA384",CIPHER_BYTE,TLS_PSK_WITH_NULL_SHA384,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_NULL_SHA256
    SUITE_INFO("PSK-NULL-SHA256","TLS_PSK_WITH_NULL_SHA256",CIPHER_BYTE,TLS_PSK_WITH_NULL_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_NULL_SHA
    SUITE_INFO("PSK-NULL-SHA","TLS_PSK_WITH_NULL_SHA",CIPHER_BYTE,TLS_PSK_WITH_NULL_SHA,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_128_CCM_8
    SUITE_INFO("AES128-CCM-8","TLS_RSA_WITH_AES_128_CCM_8",ECC_BYTE,TLS_RSA_WITH_AES_128_CCM_8, TLSv1_2_MINOR, SSLv3_MAJOR),
    SUITE_ALIAS("AES128-CCM8",ECC_BYTE,TLS_RSA_WITH_AES_128_CCM_8, TLSv1_2_MINOR, SSLv3_MAJOR)
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_256_CCM_8
    SUITE_INFO("AES256-CCM-8","TLS_RSA_WITH_AES_256_CCM_8",ECC_BYTE,TLS_RSA_WITH_AES_256_CCM_8, TLSv1_2_MINOR, SSLv3_MAJOR),
    SUITE_ALIAS("AES256-CCM8",ECC_BYTE,TLS_RSA_WITH_AES_256_CCM_8, TLSv1_2_MINOR, SSLv3_MAJOR)
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CCM
    SUITE_INFO("ECDHE-ECDSA-AES128-CCM","TLS_ECDHE_ECDSA_WITH_AES_128_CCM",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_128_CCM, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8
    SUITE_INFO("ECDHE-ECDSA-AES128-CCM-8","TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, TLSv1_2_MINOR, SSLv3_MAJOR),
    SUITE_ALIAS("ECDHE-ECDSA-AES128-CCM8",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, TLSv1_2_MINOR, SSLv3_MAJOR)
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8
    SUITE_INFO("ECDHE-ECDSA-AES256-CCM-8","TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, TLSv1_2_MINOR, SSLv3_MAJOR),
    SUITE_ALIAS("ECDHE-ECDSA-AES256-CCM8",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, TLSv1_2_MINOR, SSLv3_MAJOR)
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
    SUITE_INFO("ECDHE-RSA-AES128-SHA","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",ECC_BYTE,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
    SUITE_INFO("ECDHE-RSA-AES256-SHA","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",ECC_BYTE,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
    SUITE_INFO("ECDHE-ECDSA-AES128-SHA","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
    SUITE_INFO("ECDHE-ECDSA-AES256-SHA","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_RC4_128_SHA
    SUITE_INFO("ECDHE-RSA-RC4-SHA","TLS_ECDHE_RSA_WITH_RC4_128_SHA",ECC_BYTE,TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
    SUITE_INFO("ECDHE-RSA-DES-CBC3-SHA","TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",ECC_BYTE,TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
    SUITE_INFO("ECDHE-ECDSA-RC4-SHA","TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
    SUITE_INFO("ECDHE-ECDSA-DES-CBC3-SHA","TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA256
    SUITE_INFO("AES128-SHA256","TLS_RSA_WITH_AES_128_CBC_SHA256",CIPHER_BYTE,TLS_RSA_WITH_AES_128_CBC_SHA256, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA256
    SUITE_INFO("AES256-SHA256","TLS_RSA_WITH_AES_256_CBC_SHA256",CIPHER_BYTE,TLS_RSA_WITH_AES_256_CBC_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
    SUITE_INFO("DHE-RSA-AES128-SHA256","TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",CIPHER_BYTE,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
    SUITE_INFO("DHE-RSA-AES256-SHA256","TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",CIPHER_BYTE,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
    SUITE_INFO("ECDH-RSA-AES128-SHA","TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",ECC_BYTE,TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
    SUITE_INFO("ECDH-RSA-AES256-SHA","TLS_ECDH_RSA_WITH_AES_256_CBC_SHA",ECC_BYTE,TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
    SUITE_INFO("ECDH-ECDSA-AES128-SHA","TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",ECC_BYTE,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
    SUITE_INFO("ECDH-ECDSA-AES256-SHA","TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",ECC_BYTE,TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_RC4_128_SHA
    SUITE_INFO("ECDH-RSA-RC4-SHA","TLS_ECDH_RSA_WITH_RC4_128_SHA",ECC_BYTE,TLS_ECDH_RSA_WITH_RC4_128_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
    SUITE_INFO("ECDH-RSA-DES-CBC3-SHA","TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",ECC_BYTE,TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_RC4_128_SHA
    SUITE_INFO("ECDH-ECDSA-RC4-SHA","TLS_ECDH_ECDSA_WITH_RC4_128_SHA",ECC_BYTE,TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
    SUITE_INFO("ECDH-ECDSA-DES-CBC3-SHA","TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",ECC_BYTE,TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_128_GCM_SHA256
    SUITE_INFO("AES128-GCM-SHA256","TLS_RSA_WITH_AES_128_GCM_SHA256",CIPHER_BYTE,TLS_RSA_WITH_AES_128_GCM_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_AES_256_GCM_SHA384
    SUITE_INFO("AES256-GCM-SHA384","TLS_RSA_WITH_AES_256_GCM_SHA384",CIPHER_BYTE,TLS_RSA_WITH_AES_256_GCM_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
    SUITE_INFO("DHE-RSA-AES128-GCM-SHA256","TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",CIPHER_BYTE,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
    SUITE_INFO("DHE-RSA-AES256-GCM-SHA384","TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",CIPHER_BYTE,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    SUITE_INFO("ECDHE-RSA-AES128-GCM-SHA256","TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",ECC_BYTE,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    SUITE_INFO("ECDHE-RSA-AES256-GCM-SHA384","TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",ECC_BYTE,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    SUITE_INFO("ECDHE-ECDSA-AES128-GCM-SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    SUITE_INFO("ECDHE-ECDSA-AES256-GCM-SHA384","TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
    SUITE_INFO("ECDH-RSA-AES128-GCM-SHA256","TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256",ECC_BYTE,TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
    SUITE_INFO("ECDH-RSA-AES256-GCM-SHA384","TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384",ECC_BYTE,TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
    SUITE_INFO("ECDH-ECDSA-AES128-GCM-SHA256","TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256",ECC_BYTE,TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
    SUITE_INFO("ECDH-ECDSA-AES256-GCM-SHA384","TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384",ECC_BYTE,TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
    SUITE_INFO("CAMELLIA128-SHA","TLS_RSA_WITH_CAMELLIA_128_CBC_SHA",CIPHER_BYTE,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
    SUITE_INFO("DHE-RSA-CAMELLIA128-SHA","TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA",CIPHER_BYTE,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
    SUITE_INFO("CAMELLIA256-SHA","TLS_RSA_WITH_CAMELLIA_256_CBC_SHA",CIPHER_BYTE,TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
    SUITE_INFO("DHE-RSA-CAMELLIA256-SHA","TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA",CIPHER_BYTE,TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256
    SUITE_INFO("CAMELLIA128-SHA256","TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256",CIPHER_BYTE,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256
    SUITE_INFO("DHE-RSA-CAMELLIA128-SHA256","TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256",CIPHER_BYTE,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256
    SUITE_INFO("CAMELLIA256-SHA256","TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256",CIPHER_BYTE,TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256
    SUITE_INFO("DHE-RSA-CAMELLIA256-SHA256","TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256",CIPHER_BYTE,TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
    SUITE_INFO("ECDHE-RSA-AES128-SHA256","TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",ECC_BYTE,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
    SUITE_INFO("ECDHE-ECDSA-AES128-SHA256","TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
    SUITE_INFO("ECDH-RSA-AES128-SHA256","TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256",ECC_BYTE,TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
    SUITE_INFO("ECDH-ECDSA-AES128-SHA256","TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256",ECC_BYTE,TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
    SUITE_INFO("ECDHE-RSA-AES256-SHA384","TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",ECC_BYTE,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
    SUITE_INFO("ECDHE-ECDSA-AES256-SHA384","TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
    SUITE_INFO("ECDH-RSA-AES256-SHA384","TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384",ECC_BYTE,TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
    SUITE_INFO("ECDH-ECDSA-AES256-SHA384","TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384",ECC_BYTE,TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
    SUITE_INFO("ECDHE-RSA-CHACHA20-POLY1305","TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",CHACHA_BYTE,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
    SUITE_INFO("ECDHE-ECDSA-CHACHA20-POLY1305","TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",CHACHA_BYTE,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
    SUITE_INFO("DHE-RSA-CHACHA20-POLY1305","TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",CHACHA_BYTE,TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256
    SUITE_INFO("ECDHE-RSA-CHACHA20-POLY1305-OLD","TLS_ECDHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256",CHACHA_BYTE,TLS_ECDHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_CHACHA20_OLD_POLY1305_SHA256
    SUITE_INFO("ECDHE-ECDSA-CHACHA20-POLY1305-OLD","TLS_ECDHE_ECDSA_WITH_CHACHA20_OLD_POLY1305_SHA256",CHACHA_BYTE,TLS_ECDHE_ECDSA_WITH_CHACHA20_OLD_POLY1305_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256
    SUITE_INFO("DHE-RSA-CHACHA20-POLY1305-OLD","TLS_DHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256",CHACHA_BYTE,TLS_DHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
    SUITE_INFO("ECDHE-ECDSA-SM4-CBC-SM3","TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3",SM_BYTE,TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
    SUITE_INFO("ECDHE-ECDSA-SM4-GCM-SM3","TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3",SM_BYTE,TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
    SUITE_INFO("ECDHE-ECDSA-SM4-CCM-SM3","TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3",SM_BYTE,TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DH_anon_WITH_AES_128_CBC_SHA
    SUITE_INFO("ADH-AES128-SHA","TLS_DH_anon_WITH_AES_128_CBC_SHA",CIPHER_BYTE,TLS_DH_anon_WITH_AES_128_CBC_SHA, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DH_anon_WITH_AES_256_GCM_SHA384
    SUITE_INFO("ADH-AES256-GCM-SHA384","TLS_DH_anon_WITH_AES_256_GCM_SHA384",CIPHER_BYTE,TLS_DH_anon_WITH_AES_256_GCM_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef HAVE_RENEGOTIATION_INDICATION
    SUITE_INFO("RENEGOTIATION-INFO","TLS_EMPTY_RENEGOTIATION_INFO_SCSV",CIPHER_BYTE,TLS_EMPTY_RENEGOTIATION_INFO_SCSV,SSLv3_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_NULL_SHA
    SUITE_INFO("ECDHE-ECDSA-NULL-SHA","TLS_ECDHE_ECDSA_WITH_NULL_SHA",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_NULL_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_PSK_WITH_NULL_SHA256
    SUITE_INFO("ECDHE-PSK-NULL-SHA256","TLS_ECDHE_PSK_WITH_NULL_SHA256",ECC_BYTE,TLS_ECDHE_PSK_WITH_NULL_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256
    SUITE_INFO("ECDHE-PSK-AES128-CBC-SHA256","TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256",ECC_BYTE,TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256
    SUITE_INFO("ECDHE-PSK-AES128-GCM-SHA256","TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256",ECDHE_PSK_BYTE,TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,TLSv1_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256
    SUITE_INFO("PSK-CHACHA20-POLY1305","TLS_PSK_WITH_CHACHA20_POLY1305_SHA256",CHACHA_BYTE,TLS_PSK_WITH_CHACHA20_POLY1305_SHA256,TLSv1_2_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256
    SUITE_INFO("ECDHE-PSK-CHACHA20-POLY1305","TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",CHACHA_BYTE,TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,TLSv1_2_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256
    SUITE_INFO("DHE-PSK-CHACHA20-POLY1305","TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",CHACHA_BYTE,TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256,TLSv1_2_MINOR,SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
    SUITE_INFO("EDH-RSA-DES-CBC3-SHA","TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA",CIPHER_BYTE,TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLSv1_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256
    SUITE_INFO("ECDHE-ECDSA-ARIA128-GCM-SHA256","TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384
    SUITE_INFO("ECDHE-ECDSA-ARIA256-GCM-SHA384","TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384",ECC_BYTE,TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384, TLSv1_2_MINOR, SSLv3_MAJOR),
#endif

#ifdef BUILD_WDM_WITH_NULL_SHA256
    SUITE_INFO("WDM-NULL-SHA256","WDM_WITH_NULL_SHA256",CIPHER_BYTE,WDM_WITH_NULL_SHA256, TLSv1_3_MINOR, SSLv3_MAJOR)
#endif

#endif /* WOLFSSL_NO_TLS12 */
};

#else /* NO_TLS */

static const CipherSuiteInfo cipher_names[] =
{
    SUITE_INFO("NO-TLS","NO-TLS", 0, 0, 0, 0),
};

#endif /* NO_TLS */

/* returns the cipher_names array */
const CipherSuiteInfo* GetCipherNames(void)
{
    return cipher_names;
}


/* returns the number of elements in the cipher_names array */
int GetCipherNamesSize(void)
{
#ifdef NO_TLS
    return 0;
#else
    return (int)(sizeof(cipher_names) / sizeof(CipherSuiteInfo));
#endif
}


const char* GetCipherNameInternal(const byte cipherSuite0, const byte cipherSuite)
{
    int i;
    const char* nameInternal = "None";

    for (i = 0; i < GetCipherNamesSize(); i++) {
        if ((cipher_names[i].cipherSuite0 == cipherSuite0) &&
            (cipher_names[i].cipherSuite  == cipherSuite)
#ifndef NO_CIPHER_SUITE_ALIASES
            && (! (cipher_names[i].flags & WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS))
#endif
            ) {
            nameInternal = cipher_names[i].name;
            break;
        }
    }
    return nameInternal;
}

#if defined(WOLFSSL_QT) || defined(OPENSSL_ALL)
/* Segment cipher name into n[n0,n1,n2,n4]
 * @param cipher a pointer to WOLFSSL_CIPHER
 * @param n return segment cipher name
 * return cipher name if cipher is in the list,
 *        otherwise NULL
 */
const char* GetCipherSegment(const WOLFSSL_CIPHER* cipher, char n[][MAX_SEGMENT_SZ])
{
    int i,j,k;
    int strLen;
    unsigned long offset;
    const char* name;

    /* sanity check */
    if (cipher == NULL || n == NULL)
        return NULL;

    offset = cipher->offset;

    if (offset >= (unsigned long)GetCipherNamesSize())
        return NULL;

    name = cipher_names[offset].name;

    if (name == NULL)
        return NULL;

    /* Segment cipher name into n[n0,n1,n2,n4]
     * These are used later for comparisons to create:
     * keaStr, authStr, encStr, macStr
     *
     * If cipher_name = ECDHE-ECDSA-AES256-SHA
     * then n0 = "ECDHE", n1 = "ECDSA", n2 = "AES256", n3 = "SHA"
     * and n = [n0,n1,n2,n3,0]
     */
    strLen = (int)XSTRLEN(name);

    for (i = 0, j = 0, k = 0; i <= strLen; i++) {
        if (k >= MAX_SEGMENTS || j >= MAX_SEGMENT_SZ)
            break;

        if (name[i] != '-' && name[i] != '\0') {
            n[k][j] = name[i]; /* Fill kth segment string until '-' */
            j++;
        }
        else {
            n[k][j] = '\0';
            j = 0;
            k++;
        }
    }

    return name;
}

/* gcc-12 and later, building with ASAN at -O2 and higher, generate spurious
 * stringop-overread warnings on some (but not all...) reads of n[1] in
 * GetCipherKeaStr().
 */
#if defined(__GNUC__) && __GNUC__ > 11 && defined(__SANITIZE_ADDRESS__)
PRAGMA_GCC_DIAG_PUSH
PRAGMA_GCC("GCC diagnostic ignored \"-Wstringop-overread\"")
#endif

const char* GetCipherKeaStr(char n[][MAX_SEGMENT_SZ]) {
    const char* keaStr = NULL;

    if (XSTRCMP(n[0],"ECDHE") == 0 && XSTRCMP(n[1],"PSK") == 0)
        keaStr = "ECDHEPSK";
    else if ((XSTRCMP(n[0],"ECDH") == 0) || (XSTRCMP(n[0],"ECDHE") == 0))
        keaStr = "ECDH";
    else if (XSTRCMP(n[0],"DHE") == 0 && XSTRCMP(n[1],"PSK") == 0)
        keaStr = "DHEPSK";
    else if (XSTRCMP(n[0],"DHE") == 0)
        keaStr = "DH";
    else if (XSTRCMP(n[0],"RSA") == 0 && XSTRCMP(n[1],"PSK") == 0)
        keaStr = "RSAPSK";
    else if (XSTRCMP(n[0],"SRP") == 0)
        keaStr = "SRP";
    else if (XSTRCMP(n[0],"PSK") == 0)
        keaStr = "PSK";
    else if (XSTRCMP(n[0],"EDH") == 0)
        keaStr = "EDH";
    else if ((XSTRNCMP(n[1],"SHA", 3) == 0) || (XSTRNCMP(n[2],"SHA", 3) == 0) ||
             (XSTRNCMP(n[3],"SHA", 3) == 0) || (XSTRNCMP(n[4],"SHA", 3) == 0) ||
             (XSTRCMP(n[2],"RSA") == 0) || (XSTRCMP(n[0],"AES128") == 0) ||
             (XSTRCMP(n[0],"AES256") == 0) || (XSTRCMP(n[1],"MD5") == 0))
        keaStr = "RSA";
    else if (XSTRCMP(n[0],"NULL") == 0)
        keaStr = "None";
    else
        keaStr = "unknown";

    return keaStr;
}

#if defined(__GNUC__) && __GNUC__ > 11 && defined(__SANITIZE_ADDRESS__)
PRAGMA_GCC_DIAG_POP
#endif

const char* GetCipherAuthStr(char n[][MAX_SEGMENT_SZ]) {

    const char* authStr = NULL;

    if ((XSTRCMP(n[0],"AES128") == 0) || (XSTRCMP(n[0],"AES256") == 0)  ||
        ((XSTRCMP(n[0],"TLS13") == 0) && ((XSTRCMP(n[1],"AES128") == 0) ||
         (XSTRCMP(n[1],"AES256") == 0) || (XSTRCMP(n[1],"CHACHA20") == 0))) ||
        (XSTRCMP(n[0],"RSA") == 0) || (XSTRCMP(n[1],"RSA") == 0) ||
        (XSTRNCMP(n[1],"SHA", 3) == 0) || (XSTRNCMP(n[2],"SHA", 3) == 0) ||
        (XSTRCMP(n[1],"MD5") == 0))
        authStr = "RSA";
    else if (XSTRCMP(n[0],"PSK") == 0 || XSTRCMP(n[1],"PSK") == 0)
        authStr = "PSK";
    else if (XSTRCMP(n[0],"SRP") == 0 && XSTRCMP(n[1],"AES") == 0)
        authStr = "SRP";
    else if (XSTRCMP(n[1],"ECDSA") == 0)
        authStr = "ECDSA";
    else if (XSTRCMP(n[0],"ADH") == 0 || XSTRCMP(n[0],"NULL") == 0)
        authStr = "None";
    else
        authStr = "unknown";

    return authStr;
}

const char* GetCipherEncStr(char n[][MAX_SEGMENT_SZ]) {
    const char* encStr = NULL;

    if ((XSTRCMP(n[0],"AES256") == 0 && XSTRCMP(n[1],"GCM") == 0) ||
        (XSTRCMP(n[1],"AES256") == 0 && XSTRCMP(n[2],"GCM") == 0) ||
        (XSTRCMP(n[2],"AES256") == 0 && XSTRCMP(n[3],"GCM") == 0))
        encStr = "AESGCM(256)";

    else if ((XSTRCMP(n[0],"AES128") == 0 && XSTRCMP(n[1],"GCM") == 0) ||
             (XSTRCMP(n[1],"AES128") == 0 && XSTRCMP(n[2],"GCM") == 0) ||
             (XSTRCMP(n[2],"AES128") == 0 && XSTRCMP(n[3],"GCM") == 0))
        encStr = "AESGCM(128)";

    else if ((XSTRCMP(n[0],"AES128") == 0 && XSTRCMP(n[1],"CCM") == 0) ||
             (XSTRCMP(n[1],"AES128") == 0 && XSTRCMP(n[2],"CCM") == 0) ||
             (XSTRCMP(n[2],"AES128") == 0 && XSTRCMP(n[3],"CCM") == 0))
        encStr = "AESCCM(128)";

    else if ((XSTRCMP(n[0],"AES128") == 0) ||
             (XSTRCMP(n[1],"AES128") == 0) ||
             (XSTRCMP(n[2],"AES128") == 0) ||
             (XSTRCMP(n[1],"AES") == 0 && XSTRCMP(n[2],"128") == 0) ||
             (XSTRCMP(n[2],"AES") == 0 && XSTRCMP(n[3],"128") == 0))
        encStr = "AES(128)";

    else if ((XSTRCMP(n[0],"AES256") == 0) ||
             (XSTRCMP(n[1],"AES256") == 0) ||
             (XSTRCMP(n[2],"AES256") == 0) ||
             (XSTRCMP(n[1],"AES") == 0 && XSTRCMP(n[2],"256") == 0) ||
             (XSTRCMP(n[2],"AES") == 0 && XSTRCMP(n[3],"256") == 0))
        encStr = "AES(256)";

#ifdef HAVE_ARIA
    else if ((XSTRCMP(n[0],"ARIA256") == 0) ||
             (XSTRCMP(n[2],"ARIA256") == 0))
        encStr = "ARIA(256)";
    else if ((XSTRCMP(n[0],"ARIA128") == 0) ||
             (XSTRCMP(n[2],"ARIA128") == 0))
        encStr = "ARIA(128)";
#endif
    else if ((XSTRCMP(n[0],"CAMELLIA256") == 0) ||
             (XSTRCMP(n[2],"CAMELLIA256") == 0))
        encStr = "CAMELLIA(256)";
    else if ((XSTRCMP(n[0],"CAMELLIA128") == 0) ||
             (XSTRCMP(n[2],"CAMELLIA128") == 0))
        encStr = "CAMELLIA(128)";
#ifdef WOLFSSL_SM4_GCM
    else if ((XSTRCMP(n[0],"SM4") == 0 && XSTRCMP(n[1],"GCM") == 0) ||
             (XSTRCMP(n[1],"SM4") == 0 && XSTRCMP(n[2],"GCM") == 0) ||
             (XSTRCMP(n[2],"SM4") == 0 && XSTRCMP(n[3],"GCM") == 0))
        encStr = "SM4-GCM";
#endif
#ifdef WOLFSSL_SM4_CCM
    else if ((XSTRCMP(n[0],"SM4") == 0 && XSTRCMP(n[1],"CCM") == 0) ||
             (XSTRCMP(n[1],"SM4") == 0 && XSTRCMP(n[2],"CCM") == 0) ||
             (XSTRCMP(n[2],"SM4") == 0 && XSTRCMP(n[3],"CCM") == 0))
        encStr = "SM4-CCM";
#endif
#ifdef WOLFSSL_SM4_CBC
    else if ((XSTRCMP(n[0],"SM4") == 0) ||
             (XSTRCMP(n[2],"SM4") == 0))
        encStr = "SM4";
#endif
    else if ((XSTRCMP(n[0],"RC4") == 0) || (XSTRCMP(n[1],"RC4") == 0) ||
            (XSTRCMP(n[2],"RC4") == 0))
        encStr = "RC4";
    else if (((XSTRCMP(n[0],"DES") == 0)  || (XSTRCMP(n[1],"DES") == 0) ||
              (XSTRCMP(n[2],"DES") == 0)) &&
             ((XSTRCMP(n[1],"CBC3") == 0) || (XSTRCMP(n[2],"CBC3") == 0) ||
              (XSTRCMP(n[3],"CBC3") == 0)))
        encStr = "3DES";
    else if ((XSTRCMP(n[1],"CHACHA20") == 0 && XSTRCMP(n[2],"POLY1305") == 0) ||
             (XSTRCMP(n[2],"CHACHA20") == 0 && XSTRCMP(n[3],"POLY1305") == 0))
        encStr = "CHACHA20/POLY1305(256)";
    else if ((XSTRCMP(n[0],"NULL") == 0) || (XSTRCMP(n[1],"NULL") == 0) ||
             (XSTRCMP(n[2],"NULL") == 0) ||
             ((XSTRCMP(n[0],"TLS13") == 0) && (XSTRCMP(n[3],"") == 0)))
        encStr = "None";
    else
        encStr = "unknown";

    return encStr;
}

/* Check if a cipher is AEAD
 * @param n return segment cipher name
 * return 1 if the cipher is AEAD, otherwise 0
 */
int IsCipherAEAD(char n[][MAX_SEGMENT_SZ])
{
    WOLFSSL_ENTER("IsCipherAEAD");

    if (n == NULL) {
        WOLFSSL_MSG("bad function argument. n is NULL.");
        return 0;
    }

    if ((XSTRCMP(n[2],"GCM") == 0) || (XSTRCMP(n[3],"GCM") == 0) ||
        (XSTRCMP(n[1],"CCM") == 0) ||
        (XSTRCMP(n[2],"CCM") == 0) || (XSTRCMP(n[3],"CCM") == 0) ||
        (XSTRCMP(n[1],"CHACHA20") == 0 && XSTRCMP(n[2],"POLY1305") == 0) ||
        (XSTRCMP(n[2],"CHACHA20") == 0 && XSTRCMP(n[3],"POLY1305") == 0))
        return 1;
    return 0;
}

/* Returns the MAC string of a cipher or "unknown" on failure */
const char* GetCipherMacStr(char n[][MAX_SEGMENT_SZ]) {

    const char* macStr = NULL;

    if ((XSTRCMP(n[4],"SHA256") == 0) || (XSTRCMP(n[3],"SHA256") == 0) ||
        (XSTRCMP(n[2],"SHA256") == 0) || (XSTRCMP(n[1],"SHA256") == 0))
        macStr = "SHA256";
    else if ((XSTRCMP(n[4],"SHA384") == 0) ||
             (XSTRCMP(n[3],"SHA384") == 0) ||
             (XSTRCMP(n[2],"SHA384") == 0) ||
             (XSTRCMP(n[1],"SHA384") == 0))
        macStr = "SHA384";
#ifdef WOLFSSL_SM3
    else if ((XSTRCMP(n[4],"SM3") == 0) ||
             (XSTRCMP(n[3],"SM3") == 0) ||
             (XSTRCMP(n[2],"SM3") == 0) ||
             (XSTRCMP(n[1],"SM3") == 0))
        macStr = "SM3";
#endif
    else if ((XSTRCMP(n[4],"SHA") == 0) || (XSTRCMP(n[3],"SHA") == 0) ||
             (XSTRCMP(n[2],"SHA") == 0) || (XSTRCMP(n[1],"SHA") == 0) ||
             (XSTRCMP(n[1],"MD5") == 0))
        macStr = "SHA1";
    else if ((XSTRCMP(n[3],"GCM") == 0) ||
             (XSTRCMP(n[1],"CCM") == 0) ||
             (XSTRCMP(n[2],"CCM") == 0) || (XSTRCMP(n[3],"CCM") == 0) ||
             (XSTRCMP(n[1],"CHACHA20") == 0 && XSTRCMP(n[2],"POLY1305") == 0) ||
             (XSTRCMP(n[2],"CHACHA20") == 0 && XSTRCMP(n[3],"POLY1305") == 0))
        macStr = "AEAD";
    else
        macStr = "unknown";

    return macStr;
}

/* Returns the number of bits based on the cipher enc string, or 0 on failure */
int SetCipherBits(const char* enc) {
    int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE);

    if ((XSTRCMP(enc,"AESGCM(256)") == 0) ||
        (XSTRCMP(enc,"AES(256)") == 0) ||
        (XSTRCMP(enc,"CAMELLIA(256)") == 0) ||
        (XSTRCMP(enc,"CHACHA20/POLY1305(256)") == 0))
            ret = 256;
    else if
        ((XSTRCMP(enc,"3DES") == 0))
            ret = 168;
    else if
        ((XSTRCMP(enc,"AESGCM(128)") == 0) ||
         (XSTRCMP(enc,"AES(128)") == 0) ||
         (XSTRCMP(enc,"CAMELLIA(128)") == 0) ||
         (XSTRCMP(enc,"RC4") == 0))
            ret = 128;
   else if
        ((XSTRCMP(enc,"DES") == 0))
            ret = 56;

    return ret;
}
#endif /* WOLFSSL_QT || OPENSSL_ALL */

const char* GetCipherNameIana(const byte cipherSuite0, const byte cipherSuite)
{
#ifndef NO_ERROR_STRINGS
    int i;
    const char* nameIana = "NONE";

    for (i = 0; i < GetCipherNamesSize(); i++) {
        if ((cipher_names[i].cipherSuite0 == cipherSuite0) &&
            (cipher_names[i].cipherSuite  == cipherSuite)
#ifndef NO_CIPHER_SUITE_ALIASES
            && (! (cipher_names[i].flags & WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS))
#endif
            ) {
            nameIana = cipher_names[i].name_iana;
            break;
        }
    }
    return nameIana;
#else
    (void)cipherSuite0;
    (void)cipherSuite;
    return NULL;
#endif
}

const char* wolfSSL_get_cipher_name_internal(WOLFSSL* ssl)
{
    if (ssl == NULL) {
        return NULL;
    }

    return GetCipherNameInternal(ssl->options.cipherSuite0, ssl->options.cipherSuite);
}

const char* wolfSSL_get_cipher_name_iana(WOLFSSL* ssl)
{
    if (ssl == NULL) {
        return NULL;
    }

    return GetCipherNameIana(ssl->options.cipherSuite0, ssl->options.cipherSuite);
}

int GetCipherSuiteFromName(const char* name, byte* cipherSuite0,
                       byte* cipherSuite, byte* major, byte* minor, int* flags)
{
    int           ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
    int           i;
    unsigned long len;
    const char*   nameDelim;

    (void)major;
    (void)minor;

    /* Support trailing : */
    nameDelim = XSTRSTR(name, ":");
    if (nameDelim)
        len = (unsigned long)(nameDelim - name);
    else
        len = (unsigned long)XSTRLEN(name);

    for (i = 0; i < GetCipherNamesSize(); i++) {
        int found = (XSTRNCMP(name, cipher_names[i].name, len) == 0) &&
                    (cipher_names[i].name[len] == 0);
#ifndef NO_ERROR_STRINGS
        if (!found)
            found = (XSTRNCMP(name, cipher_names[i].name_iana, len) == 0) &&
                    (cipher_names[i].name_iana[len] == 0);
#endif

        if (found) {
            if (cipherSuite0 != NULL)
                *cipherSuite0 = cipher_names[i].cipherSuite0;
            if (cipherSuite != NULL)
                *cipherSuite  = cipher_names[i].cipherSuite;
#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_QT) || \
    defined(WOLFSSL_HAPROXY) || defined(WOLFSSL_NGINX)
            if (major != NULL)
                *major = cipher_names[i].major;
            if (minor != NULL)
                *minor = cipher_names[i].minor;
#endif
            if (flags != NULL)
                *flags = cipher_names[i].flags;
            ret = 0;
            break;
        }
    }

    return ret;
}

/**
Set the enabled cipher suites.

With OPENSSL_EXTRA we attempt to understand some of the available "bulk"
ciphersuites. We can not perfectly filter ciphersuites based on the "bulk"
names but we do what we can. Ciphersuites named explicitly take precedence to
ciphersuites introduced through the "bulk" ciphersuites.

@param [out] suites Suites structure.
@param [in]  list   List of cipher suites, only supports full name from
                    cipher_names[] delimited by ':'.

@return true on success, else false.
*/
static int ParseCipherList(Suites* suites,
        const char* list, ProtocolVersion version, int privateKeySz, byte side)
{
    int       ret              = 0;
    int       idx              = 0;
    int       haveSig          = 0;
    word16    haveRSA          = 0;
#ifdef OPENSSL_EXTRA
    word16    haveDH           = 0;
    word16    haveECC          = 0;
    word16    haveStaticRSA    = 1; /* allowed by default if compiled in */
    word16    haveStaticECC    = 0;
    word16    haveNull         = 1; /* allowed by default if compiled in */
    int       callInitSuites   = 0;
    word16    havePSK          = 0;
    word16    haveAES128       = 1; /* allowed by default if compiled in */
    word16    haveSHA1         = 1; /* allowed by default if compiled in */
    word16    haveRC4          = 1; /* allowed by default if compiled in */
#endif
    int       tls1_3        = 0;
    const int suiteSz       = GetCipherNamesSize();
    const char* next        = list;

    if (suites == NULL || list == NULL) {
        WOLFSSL_MSG("SetCipherList parameter error");
        return 0;
    }

    if (next[0] == '\0' ||
        XSTRCMP(next, "ALL") == 0 ||
        XSTRCMP(next, "DEFAULT") == 0 ||
        XSTRCMP(next, "HIGH") == 0)
    {
        /* Add all ciphersuites except anonymous and null ciphers. Prefer RSA */
#ifndef NO_RSA
        haveRSA = 1;
#endif
        InitSuites(suites, version,
#ifndef NO_CERTS
                privateKeySz,
#else
                0,
#endif
                haveRSA, 1, 1, !haveRSA, 1, haveRSA, !haveRSA, 0, 0, 1,
                1, 1, side
        );
        return 1; /* wolfSSL default */
    }

    do {
        const char* current = next;
        char   name[MAX_SUITE_NAME + 1];
        int    i;
        word32 length;
    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
        word16 allowing = 1;
    #endif

        next = XSTRSTR(next, ":");
        length = MAX_SUITE_NAME;
        if (next != NULL) {
            word32 currLen = (word32)(next - current);
            if (length > currLen) {
                length = currLen;
            }
            if (currLen == 0)
                break;
            ++next; /* increment to skip ':' */
        }

    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
        if (length > 1) {
            if (*current == '!') {
                allowing = 0;
                current++;
                length--;
            }
        }
    #endif

        XSTRNCPY(name, current, length);
        name[(length == sizeof(name)) ? length - 1 : length] = 0;


    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL)
        if (length > 1) {
            char* substr = NULL;
            char* substrCurrent = name;

            /* extract first public key type from a string like ECDHE+AESGCM */
            substr = XSTRSTR(substrCurrent, "+");
            if (substr != NULL) {
                do {
                    if (substr) {
                        length = (word32)(substr - substrCurrent);
                        substrCurrent[length] = '\0';
                    }
                    else {
                        length = (word32)XSTRLEN(substrCurrent);
                    }

                    /* check if is a public key type */
                    if (XSTRCMP(substrCurrent, "ECDHE") == 0 ||
                        XSTRCMP(substrCurrent, "RSA")   == 0 ||
                        XSTRCMP(substrCurrent, "DHE")   == 0) {
                        if (name != substrCurrent)
                            XMEMMOVE(name, substrCurrent, length);
                        name[length] = '\0';
                        break;
                    }

                    substrCurrent = substr;
                    if (substr) {
                        substrCurrent = substrCurrent + 1; /* +1 to skip over '+' */
                        substr  = XSTRSTR(substrCurrent, "+");
                    }
                } while (substrCurrent != NULL);
            }
        }

        if (XSTRCMP(name, "DEFAULT") == 0 || XSTRCMP(name, "ALL") == 0) {
            if (XSTRCMP(name, "ALL") == 0)
                haveSig |= SIG_ANON;
            else
                haveSig &= ~SIG_ANON;
            haveRSA = 1;
            haveDH = 1;
            haveECC = 1;

            /* having static ECC will disable all RSA use, do not set
             * static ECC suites here
             * haveStaticECC = 1; */
            haveStaticRSA = 1;
            haveSig |= SIG_RSA;
            havePSK = 1;
            haveNull = 0;

            callInitSuites = 1;
            ret = 1;
            continue;
        }

        /* We don't have a way to disallow high bit sizes. Only disable unsafe
         * ciphersuites. */
        if (XSTRCMP(name, "HIGH") == 0 && allowing) {
            /* Disable static, anonymous, and null ciphers */
            haveSig &= ~SIG_ANON;
            haveRSA = 1;
            haveDH = 1;
            haveECC = 1;
            haveStaticECC = 0;
            haveStaticRSA = 0;
            haveSig |= SIG_RSA;
            havePSK = 1;
            haveNull = 0;

            callInitSuites = 1;
            ret = 1;
            continue;
        }

        if (XSTRCMP(name, "aNULL") == 0) {
            if (allowing)
                haveSig |= SIG_ANON;
            else
                haveSig &= ~SIG_ANON;
            if (allowing) {
                /* Allow RSA by default. */
                if (!haveECC)
                    haveRSA = 1;
                if ((haveSig & SIG_ECDSA) == 0)
                    haveSig |= SIG_RSA;
                callInitSuites = 1;
                ret = 1;
            }
            continue;
        }

        if (XSTRCMP(name, "eNULL") == 0 || XSTRCMP(name, "NULL") == 0) {
            haveNull = allowing;
            if (allowing) {
                /* Allow RSA by default. */
                if (!haveECC)
                    haveRSA = 1;
                if ((haveSig & SIG_ECDSA) == 0)
                    haveSig |= SIG_RSA;
                callInitSuites = 1;
                ret = 1;
            }
            continue;
        }

        if (XSTRCMP(name, "kDH") == 0) {
            if (allowing) {
                haveDH = 1;
                callInitSuites = 1;
                ret = 1;
            }
            continue;
        }

        if (XSTRCMP(name, "DHE") == 0 || XSTRCMP(name, "EDH") == 0) {
            if (allowing) {
                haveDH = 1;
                callInitSuites = 1;
                ret = 1;
            }
            continue;
        }

        if (XSTRCMP(name, "ECDHE") == 0 || XSTRCMP(name, "EECDH") == 0) {
            if (allowing) {
                haveECC = 1;
                haveSig |= SIG_ECDSA;
                callInitSuites = 1;
                ret = 1;
            }
            continue;
        }

        if (XSTRCMP(name, "kRSA") == 0 || XSTRCMP(name, "RSA") == 0) {
            haveStaticRSA = allowing;
            if (allowing) {
                haveRSA = 1;
                haveSig |= SIG_RSA;
                callInitSuites = 1;
                ret = 1;
            }
            continue;
        }

        if (XSTRCMP(name, "PSK") == 0) {
            havePSK = allowing;
            haveSig |= SIG_RSA;
            if (allowing) {
                /* Allow RSA by default. */
                if (!haveECC)
                    haveRSA = 1;
                if ((haveSig & SIG_ECDSA) == 0)
                    haveSig |= SIG_RSA;
                callInitSuites = 1;
                ret = 1;
            }
            continue;
        }

        #if defined(WOLFSSL_SYS_CRYPTO_POLICY)
        if (XSTRCMP(name, "AES128") == 0) {
            haveAES128 = allowing;
            callInitSuites = 1;
            ret = 1;
            continue;
        }

        if (XSTRCMP(name, "SHA1") == 0) {
            haveSHA1 = allowing;
            callInitSuites = 1;
            ret = 1;
            continue;
        }

        if (XSTRCMP(name, "RC4") == 0) {
            haveRC4 = allowing;
            callInitSuites = 1;
            ret = 1;
            continue;
        }
        #endif /* WOLFSSL_SYS_CRYPTO_POLICY */

        if (XSTRCMP(name, "LOW") == 0 || XSTRCMP(name, "MEDIUM") == 0) {
            /* No way to limit or allow low bit sizes */
            if (allowing) {
                /* Allow RSA by default */
                haveRSA = 1;
                haveSig |= SIG_RSA;
                callInitSuites = 1;
                ret = 1;
            }
            continue;
        }

        if (XSTRCMP(name, "DSS") == 0) {
            /* No support for DSA ciphersuites */
            continue;
        }

        if (XSTRCMP(name, "EXP") == 0 || XSTRCMP(name, "EXPORT") == 0) {
            /* wolfSSL doesn't support "export" ciphers. We can skip this */
            continue;
        }

        #if defined(WOLFSSL_SYS_CRYPTO_POLICY)
        if (XSTRNCMP(name, WOLFSSL_SECLEVEL_STR,
                     strlen(WOLFSSL_SECLEVEL_STR)) == 0) {
            /* Skip the "@SECLEVEL=N" string, we'll process it elsewhere. */
            continue;
        }
        #endif /* WOLFSSL_SYS_CRYPTO_POLICY */
    #endif /* OPENSSL_EXTRA */

        for (i = 0; i < suiteSz; i++) {
            if (XSTRNCMP(name, cipher_names[i].name, sizeof(name)) == 0
            #ifndef NO_ERROR_STRINGS
                || XSTRNCMP(name, cipher_names[i].name_iana, sizeof(name)) == 0
            #endif
             ) {
                int j;

            #ifdef WOLFSSL_DTLS
                /* don't allow stream ciphers with DTLS */
                if (version.major == DTLS_MAJOR) {
                    if (XSTRSTR(name, "RC4"))
                    {
                        WOLFSSL_MSG("Stream ciphers not supported with DTLS");
                        continue;
                    }

                }
            #endif /* WOLFSSL_DTLS */

                for (j = 0; j < idx; j += 2) {
                    if ((suites->suites[j+0] == cipher_names[i].cipherSuite0) &&
                        (suites->suites[j+1] == cipher_names[i].cipherSuite)) {
                        break;
                    }
                }
                /* Silently drop duplicates from list. */
                if (j != idx) {
                    break;
                }

                if (idx + 1 >= WOLFSSL_MAX_SUITE_SZ) {
                    WOLFSSL_MSG("WOLFSSL_MAX_SUITE_SZ set too low");
                    return 0; /* suites buffer not large enough, error out */
                }

                suites->suites[idx++] = cipher_names[i].cipherSuite0;
                suites->suites[idx++] = cipher_names[i].cipherSuite;
                /* The suites are either ECDSA, RSA, PSK, or Anon. The RSA
                 * suites don't necessarily have RSA in the name. */
        #ifdef WOLFSSL_TLS13
                if (cipher_names[i].cipherSuite0 == TLS13_BYTE ||
                         (cipher_names[i].cipherSuite0 == ECC_BYTE &&
                          (cipher_names[i].cipherSuite == TLS_SHA256_SHA256 ||
                           cipher_names[i].cipherSuite == TLS_SHA384_SHA384))) {
                    tls1_3 = 1;
                #ifndef NO_RSA
                    haveSig |= SIG_RSA;
                #endif
                #if defined(HAVE_ECC) || defined(HAVE_ED25519) || \
                                                             defined(HAVE_ED448)
                    haveSig |= SIG_ECDSA;
                #endif
                #ifdef HAVE_FALCON
                    haveSig |= SIG_FALCON;
                #endif /* HAVE_FALCON */
                #ifdef HAVE_DILITHIUM
                    haveSig |= SIG_DILITHIUM;
                #endif /* HAVE_DILITHIUM */
                }
                else
            #ifdef BUILD_TLS_SM4_GCM_SM3
                if ((cipher_names[i].cipherSuite0 == CIPHER_BYTE) &&
                       (cipher_names[i].cipherSuite == TLS_SM4_GCM_SM3)) {
                    haveSig |= SIG_SM2;
                }
                else
            #endif
            #ifdef BUILD_TLS_SM4_CCM_SM3
                if ((cipher_names[i].cipherSuite0 == CIPHER_BYTE) &&
                       (cipher_names[i].cipherSuite == TLS_SM4_CCM_SM3)) {
                    haveSig |= SIG_SM2;
                }
                else
            #endif
        #endif /* WOLFSSL_TLS13 */
        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3) && \
            (defined(WOLFSSL_SM4_CBC) || defined(WOLFSSL_SM4_GCM) || \
             defined(WOLFSSL_SM4_CCM))
                if ((cipher_names[i].cipherSuite0 == SM_BYTE) && (0
                #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3
                     || (cipher_names[i].cipherSuite ==
                            TLS_ECDHE_ECDSA_WITH_SM4_CBC_SM3)
                #endif
                #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3
                     || (cipher_names[i].cipherSuite ==
                            TLS_ECDHE_ECDSA_WITH_SM4_GCM_SM3)
                #endif
                #ifdef BUILD_TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3
                     || (cipher_names[i].cipherSuite ==
                            TLS_ECDHE_ECDSA_WITH_SM4_CCM_SM3)
                #endif
                    )) {
                    haveSig |= SIG_SM2;
                }
                else
            #endif
            #if defined(HAVE_ECC) || defined(HAVE_ED25519) || \
                                                             defined(HAVE_ED448)
                if (XSTRSTR(name, "ECDSA"))
                    haveSig |= SIG_ECDSA;
                else
            #endif
            #ifdef HAVE_ANON
                if (XSTRSTR(name, "ADH"))
                    haveSig |= SIG_ANON;
                else
            #endif
            #ifndef NO_PSK
                if (XSTRSTR(name, "PSK") == NULL)
            #endif
                {
                    /* Fall back to RSA */
                    haveSig |= SIG_RSA;
                }

                ret = 1; /* found at least one */
                break;
            }
        }
    } while (next);

    if (ret) {
        int keySz = 0;
    #ifndef NO_CERTS
        keySz = privateKeySz;
    #endif
    #ifdef OPENSSL_EXTRA
        if (callInitSuites) {
            suites->setSuites = 0; /* Force InitSuites */
            suites->hashSigAlgoSz = 0; /* Force InitSuitesHashSigAlgo call
                                        * inside InitSuites */
            InitSuites(suites, version, keySz, (word16)haveRSA,
                       (word16)havePSK, (word16)haveDH,
                       (word16)((haveSig & SIG_ECDSA) != 0),
                       (word16)haveECC, (word16)haveStaticRSA,
                       (word16)haveStaticECC,
                       (word16)((haveSig & SIG_ANON) != 0),
                       (word16)haveNull, (word16)haveAES128,
                       (word16)haveSHA1, (word16)haveRC4, side);
            /* Restore user ciphers ahead of defaults */
            XMEMMOVE(suites->suites + idx, suites->suites,
                    min(suites->suiteSz, WOLFSSL_MAX_SUITE_SZ-idx));
            suites->suiteSz += (word16)idx;
        }
        else
    #endif
        {
            suites->suiteSz   = (word16)idx;
            InitSuitesHashSigAlgo(suites->hashSigAlgo, haveSig, 1, tls1_3,
                                  keySz, &suites->hashSigAlgoSz);
        }

#ifdef HAVE_RENEGOTIATION_INDICATION
        if (side == WOLFSSL_CLIENT_END) {
            if (suites->suiteSz > WOLFSSL_MAX_SUITE_SZ - 2) {
                WOLFSSL_MSG("Too many ciphersuites");
                return 0;
            }
            suites->suites[suites->suiteSz] = CIPHER_BYTE;
            suites->suites[suites->suiteSz+1] =
                TLS_EMPTY_RENEGOTIATION_INFO_SCSV;
            suites->suiteSz += 2;
        }
#endif
        suites->setSuites = 1;
    }

#ifdef NO_CERTS
    (void)privateKeySz;
#endif

    return ret;
}

int SetCipherList_ex(const WOLFSSL_CTX* ctx, const WOLFSSL* ssl,
        Suites* suites, const char* list)
{
    ProtocolVersion version;
    int privateKeySz = 0;
    byte side;

    if (ctx != NULL) {
        version = ctx->method->version;
#ifndef NO_CERTS
        privateKeySz = ctx->privateKeySz;
#endif
        side = ctx->method->side;
    }
    else if (ssl != NULL) {
        version = ssl->version;
#ifndef NO_CERTS
        privateKeySz = ssl->buffers.keySz;
#endif
        side = (byte)ssl->options.side;
    }
    else {
        WOLFSSL_MSG("SetCipherList_ex parameter error");
        return 0;
    }

    return ParseCipherList(suites, list, version, privateKeySz, side);
}

int SetCipherList(const WOLFSSL_CTX* ctx, Suites* suites,
                                 const char* list)
{
    return SetCipherList_ex(ctx, NULL, suites, list);
}

#if defined(OPENSSL_EXTRA) || defined(WOLFSSL_SET_CIPHER_BYTES)
int SetCipherListFromBytes(WOLFSSL_CTX* ctx, Suites* suites, const byte* list,
                           const int listSz)
{
    int ret = 0;
    int idx = 0;
    int i;

    int haveRSAsig       = 0;
    int haveECDSAsig     = 0;
    int haveFalconSig    = 0;
    int haveDilithiumSig = 0;
    int haveAnon         = 0;
    int tls1_3           = 0;

    if (suites == NULL || list == NULL) {
        WOLFSSL_MSG("SetCipherListFromBytes parameter error");
        return 0;
    }

    if ((listSz % 2) != 0) {
        return 0;
    }

    for (i = 0; (i + 1) < listSz; i += 2) {
        const byte firstByte = list[i];
        const byte secondByte = list[i + 1];
        const char* name = NULL;
        int j;

        name = GetCipherNameInternal(firstByte, secondByte);
        if (XSTRCMP(name, "None") == 0) {
            /* bytes don't match any known cipher */
            continue;
        }

    #ifdef WOLFSSL_DTLS
        /* don't allow stream ciphers with DTLS */
        if (ctx->method->version.major == DTLS_MAJOR) {
            if (XSTRSTR(name, "RC4")) {
                WOLFSSL_MSG("Stream ciphers not supported with DTLS");
                continue;
            }
        }
    #endif /* WOLFSSL_DTLS */

        for (j = 0; j < idx; j += 2) {
            if ((suites->suites[j+0] == firstByte) &&
                    (suites->suites[j+1] == secondByte)) {
                break;
            }
        }
        /* Silently drop duplicates from list. */
        if (j != idx) {
            continue;
        }

        if (idx + 1 >= WOLFSSL_MAX_SUITE_SZ) {
            WOLFSSL_MSG("WOLFSSL_MAX_SUITE_SZ set too low");
            return 0; /* suites buffer not large enough, error out */
        }

        suites->suites[idx++] = firstByte;
        suites->suites[idx++] = secondByte;

        /* The suites are either ECDSA, RSA, PSK, or Anon. The RSA
         * suites don't necessarily have RSA in the name. */
    #ifdef WOLFSSL_TLS13
        if (firstByte == TLS13_BYTE || (firstByte == ECC_BYTE &&
             (secondByte == TLS_SHA256_SHA256 ||
              secondByte == TLS_SHA384_SHA384)) ||
            (firstByte == CIPHER_BYTE && (secondByte == TLS_SM4_GCM_SM3 ||
              secondByte == TLS_SM4_CCM_SM3))) {
            tls1_3 = 1;
        #ifndef NO_RSA
            haveRSAsig = 1;
        #endif
        #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448)
            haveECDSAsig = 1;
        #endif
        #ifdef HAVE_FALCON
            haveFalconSig = 1;
        #endif /* HAVE_FALCON */
        #ifdef HAVE_DILITHIUM
            haveDilithiumSig = 1;
        #endif /* HAVE_DILITHIUM */
        }
        else
    #endif /* WOLFSSL_TLS13 */
    #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448)
        if ((haveECDSAsig == 0) && XSTRSTR(name, "ECDSA"))
            haveECDSAsig = 1;
        else
    #endif
    #ifdef HAVE_ANON
        if (XSTRSTR(name, "ADH"))
            haveAnon = 1;
        else
    #endif
        if (haveRSAsig == 0
        #ifndef NO_PSK
            && (XSTRSTR(name, "PSK") == NULL)
        #endif
        ) {
            haveRSAsig = 1;
        }

        ret = 1; /* found at least one */
    }

    if (ret) {
        int keySz = 0;
        int haveSig = 0;
    #ifndef NO_CERTS
        keySz = ctx->privateKeySz;
    #endif
        suites->suiteSz = (word16)idx;
        haveSig |= haveECDSAsig ? SIG_ECDSA : 0;
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        haveSig |= haveECDSAsig ? SIG_SM2 : 0;
    #endif
        haveSig |= haveRSAsig ? SIG_RSA : 0;
        haveSig |= haveFalconSig ? SIG_FALCON : 0;
        haveSig |= haveDilithiumSig ? SIG_DILITHIUM : 0;
        haveSig |= haveAnon ? SIG_ANON : 0;
        InitSuitesHashSigAlgo(suites->hashSigAlgo, haveSig, 1, tls1_3,
            keySz, &suites->hashSigAlgoSz);
#ifdef HAVE_RENEGOTIATION_INDICATION
        if (ctx->method->side == WOLFSSL_CLIENT_END) {
            if (suites->suiteSz > WOLFSSL_MAX_SUITE_SZ - 2) {
                WOLFSSL_MSG("Too many ciphersuites");
                return 0;
            }
            suites->suites[suites->suiteSz] = CIPHER_BYTE;
            suites->suites[suites->suiteSz+1] =
                TLS_EMPTY_RENEGOTIATION_INFO_SCSV;
            suites->suiteSz += 2;
        }
#endif
        suites->setSuites = 1;
    }

    (void)ctx;

    return ret;
}
#endif /* OPENSSL_EXTRA || WOLFSSL_SET_CIPHER_BYTES */


#ifdef OPENSSL_EXTRA

struct mac_algs {
    byte alg;
    const char* name;
} mac_names[] = {
#ifndef NO_SHA256
    { sha256_mac, "SHA256" },
#endif
#ifdef WOLFSSL_SHA384
    { sha384_mac, "SHA384" },
#endif
#ifdef WOLFSSL_SHA512
    { sha512_mac, "SHA512" },
#endif
#ifdef WOLFSSL_SHA224
    { sha224_mac, "SHA224" },
#endif
#ifdef WOLFSSL_SM3
    { sm3_mac,    "SM3" },
#endif
#if !defined(NO_SHA) && (!defined(NO_OLD_TLS) || \
                                                defined(WOLFSSL_ALLOW_TLS_SHA1))
    { sha_mac,    "SHA1" },
#endif
};
#define MAC_NAMES_SZ    (int)(sizeof(mac_names)/sizeof(*mac_names))

/* Convert the hash algorithm string to a TLS MAC algorithm num. */
static byte GetMacAlgFromName(const char* name, int len)
{
    byte alg = no_mac;
    int i;

    for (i = 0; i < MAC_NAMES_SZ; i++) {
        if (((int)XSTRLEN(mac_names[i].name) == len) &&
                                 (XMEMCMP(mac_names[i].name, name, len) == 0)) {
            alg = mac_names[i].alg;
            break;
        }
    }

    return alg;
}

struct sig_algs {
    byte alg;
    const char* name;
} sig_names[] = {
#ifndef NO_RSA
    { rsa_sa_algo,     "RSA" },
#ifdef WC_RSA_PSS
    { rsa_pss_sa_algo, "RSA-PSS" },
    { rsa_pss_sa_algo, "PSS" },
#endif
#endif
#ifdef HAVE_ECC
    { ecc_dsa_sa_algo, "ECDSA" },
#ifdef HAVE_ECC_BRAINPOOL
    { ecc_brainpool_sa_algo, "ECDSA-BRAINPOOL" },
#endif
#endif
#ifdef HAVE_ED25519
    { ed25519_sa_algo, "ED25519" },
#endif
#ifdef HAVE_ED448
    { ed448_sa_algo,   "ED448" },
#endif
#ifndef NO_DSA
    { dsa_sa_algo,     "DSA" },
#endif
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
    { sm2_sa_algo,     "SM2" },
#endif
};
#define SIG_NAMES_SZ    (int)(sizeof(sig_names)/sizeof(*sig_names))

/* Convert the signature algorithm string to a TLS signature algorithm num. */
static byte GetSigAlgFromName(const char* name, int len)
{
    byte alg = anonymous_sa_algo;
    int i;

    for (i = 0; i < SIG_NAMES_SZ; i++) {
        if (((int)XSTRLEN(sig_names[i].name) == len) &&
                                 (XMEMCMP(sig_names[i].name, name, len) == 0)) {
            alg = sig_names[i].alg;
            break;
        }
    }

    return alg;
}

/* Set the hash/signature algorithms that are supported for certificate signing.
 *
 * suites  [in,out]  Cipher suites and signature algorithms.
 * list    [in]      String representing hash/signature algorithms to set.
 * returns  0 on failure.
 *          1 on success.
 */
int SetSuitesHashSigAlgo(Suites* suites, const char* list)
{
    int ret = 1;
    word16 idx = 0;
    const char* s = list;
    byte sig_alg = 0;
    byte mac_alg = no_mac;

    /* Setting is destructive on error. */
    suites->hashSigAlgoSz = 0;

    do {
        if (*list == '+') {
            sig_alg = GetSigAlgFromName(s, (int)(list - s));
            if (sig_alg == 0) {
                ret = 0;
                break;
            }
            s = list + 1;
        }
        else if (*list == ':' || *list == '\0') {
            if (sig_alg == 0) {
                /* No signature algorithm set yet.
                 * Ed25519 and Ed448 have implied MAC algorithm.
                 */
                sig_alg = GetSigAlgFromName(s, (int)(list - s));
                if (sig_alg != ed25519_sa_algo && sig_alg != ed448_sa_algo) {
                    ret = 0;
                    break;
                }
            }
            else {
                mac_alg = GetMacAlgFromName(s, (int)(list - s));
                if (mac_alg == 0) {
                    ret = 0;
                    break;
                }
            }
            AddSuiteHashSigAlgo(suites->hashSigAlgo, mac_alg, sig_alg, 0, &idx);
            sig_alg = 0;
            mac_alg = no_mac;
            s = list + 1;
        }

        list++;
    }
    while (*(list-1) != '\0');

    if (s != list && (sig_alg != 0 || mac_alg != 0)) {
        ret = 0;
    }
    else {
        suites->hashSigAlgoSz = idx;
    }

    return ret;
}

#endif /* OPENSSL_EXTRA */

#if !defined(NO_TLS) && (!defined(NO_WOLFSSL_SERVER) || !defined(NO_CERTS))
static int MatchSigAlgo(WOLFSSL* ssl, int sigAlgo)
{
#ifdef HAVE_ED25519
    if (ssl->pkCurveOID == ECC_ED25519_OID) {
        /* Certificate has Ed25519 key, only match with Ed25519 sig alg  */
        return sigAlgo == ed25519_sa_algo;
    }
#endif
#ifdef HAVE_ED448
    if (ssl->pkCurveOID == ECC_ED448_OID) {
        /* Certificate has Ed448 key, only match with Ed448 sig alg  */
        return sigAlgo == ed448_sa_algo;
    }
#endif
#ifdef HAVE_FALCON
    if (ssl->pkCurveOID == CTC_FALCON_LEVEL1) {
        /* Certificate has Falcon level 1 key, only match with Falcon level 1
         * sig alg  */
        return sigAlgo == falcon_level1_sa_algo;
    }
    if (ssl->pkCurveOID == CTC_FALCON_LEVEL5) {
        /* Certificate has Falcon level 5 key, only match with Falcon level 5
         * sig alg  */
        return sigAlgo == falcon_level5_sa_algo;
    }
#endif /* HAVE_FALCON */
#ifdef HAVE_DILITHIUM
    #ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
    if (ssl->pkCurveOID == CTC_DILITHIUM_LEVEL2) {
        /* Certificate has Dilithium level 2 key, only match with it. */
        return sigAlgo == dilithium_level2_sa_algo;
    }
    if (ssl->pkCurveOID == CTC_DILITHIUM_LEVEL3) {
        /* Certificate has Dilithium level 3 key, only match with it. */
        return sigAlgo == dilithium_level3_sa_algo;
    }
    if (ssl->pkCurveOID == CTC_DILITHIUM_LEVEL5) {
        /* Certificate has Dilithium level 5 key, only match with it. */
        return sigAlgo == dilithium_level5_sa_algo;
    }
    #endif /* WOLFSSL_DILITHIUM_FIPS204_DRAFT */
    if (ssl->pkCurveOID == CTC_ML_DSA_LEVEL2) {
        /* Certificate has ML-DSA level 2 key, only match with it. */
        return sigAlgo == dilithium_level2_sa_algo;
    }
    if (ssl->pkCurveOID == CTC_ML_DSA_LEVEL3) {
        /* Certificate has ML-DSA level 3 key, only match with it. */
        return sigAlgo == dilithium_level3_sa_algo;
    }
    if (ssl->pkCurveOID == CTC_ML_DSA_LEVEL5) {
        /* Certificate has ML-DSA level 5 key, only match with it. */
        return sigAlgo == dilithium_level5_sa_algo;
    }
#endif /* HAVE_DILITHIUM */
#ifdef WC_RSA_PSS
    /* RSA certificate and PSS sig alg. */
    if (ssl->options.sigAlgo == rsa_sa_algo) {
    #if defined(WOLFSSL_TLS13)
        /* TLS 1.3 only supports RSA-PSS. */
        if (IsAtLeastTLSv1_3(ssl->version))
            return sigAlgo == rsa_pss_sa_algo;
    #endif
        /* TLS 1.2 and below - RSA-PSS allowed. */
        if (sigAlgo == rsa_pss_sa_algo)
            return 1;
    }
#endif
#ifdef HAVE_ECC_BRAINPOOL
    if ((ssl->pkCurveOID == ECC_BRAINPOOLP256R1_OID) ||
        (ssl->pkCurveOID == ECC_BRAINPOOLP384R1_OID) ||
        (ssl->pkCurveOID == ECC_BRAINPOOLP512R1_OID)) {
        if (IsAtLeastTLSv1_3(ssl->version)) {
            /* Certificate has an ECC Brainpool key, only match with the
             * specified ECDSA brainpool signature algorithms for TLS 1.3 */
            return sigAlgo == ecc_brainpool_sa_algo;
        }
        else {
            /* Certificate has an ECC Brainpool key, match with ECDSA in TLS 1.2
             * case, but only when the related Brainpool curve is present in
             * the supported_groups extension. */
            if (ssl->pkCurveOID == ECC_BRAINPOOLP256R1_OID &&
                    TLSX_SupportedCurve_IsSupported(ssl,
                                                 WOLFSSL_ECC_BRAINPOOLP256R1)) {
                return sigAlgo == ecc_dsa_sa_algo;
            }
            else if (ssl->pkCurveOID == ECC_BRAINPOOLP384R1_OID &&
                     TLSX_SupportedCurve_IsSupported(ssl,
                                                 WOLFSSL_ECC_BRAINPOOLP384R1)) {
                return sigAlgo == ecc_dsa_sa_algo;
            }
            else if (ssl->pkCurveOID == ECC_BRAINPOOLP512R1_OID &&
                     TLSX_SupportedCurve_IsSupported(ssl,
                                                 WOLFSSL_ECC_BRAINPOOLP512R1)) {
                return sigAlgo == ecc_dsa_sa_algo;
            }
            else {
                /* Curve not supported in supported_groups extension. */
                return 0;
            }
        }
    }
#endif
    /* Signature algorithm matches certificate. */
    return sigAlgo == ssl->options.sigAlgo;
}

#if defined(HAVE_ECC) && \
    (defined(WOLFSSL_TLS13) || defined(USE_ECDSA_KEYSZ_HASH_ALGO))
static int CmpEccStrength(int hashAlgo, int curveSz)
{
    int dgstSz = GetMacDigestSize((byte)hashAlgo);
    if (dgstSz <= 0)
        return WOLFSSL_FATAL_ERROR;
    return dgstSz - (curveSz & (~0x3));
}
#endif

static byte MinHashAlgo(WOLFSSL* ssl)
{
#ifdef WOLFSSL_TLS13
#ifndef NO_SHA256
    if (IsAtLeastTLSv1_3(ssl->version)) {
        return sha256_mac;
    }
#elif defined(WOLFSSL_SM3)
    if (IsAtLeastTLSv1_3(ssl->version)) {
        return sm3_mac;
    }
#endif
#endif
#if !defined(WOLFSSL_NO_TLS12) && !defined(WOLFSSL_ALLOW_TLS_SHA1)
    if (IsAtLeastTLSv1_2(ssl)) {
        return sha256_mac;
    }
#endif /* WOLFSSL_NO_TLS12 */
    (void)ssl;
    return sha_mac;
}

/* Check if a given peer hashSigAlgo is supported in our ssl->suites or
 * ssl->ctx->suites.
 *
 * Returns 1 on match.
 * Returns 0 otherwise.
 * */
static int SupportedHashSigAlgo(WOLFSSL* ssl, const byte * hashSigAlgo)
{
    const Suites * suites = NULL;
    word32         i = 0;

    if (ssl == NULL || hashSigAlgo == NULL) {
        return 0;
    }

    suites = WOLFSSL_SUITES(ssl);

    if (suites == NULL || suites->hashSigAlgoSz == 0) {
        return 0;
    }

    for (i = 0; (i+1) < suites->hashSigAlgoSz; i += HELLO_EXT_SIGALGO_SZ) {
        if (XMEMCMP(&suites->hashSigAlgo[i], hashSigAlgo,
                    HELLO_EXT_SIGALGO_SZ) == 0) {
            /* Match found. */
            return 1;
        }
    }

    return 0;
}

int PickHashSigAlgo(WOLFSSL* ssl, const byte* hashSigAlgo, word32 hashSigAlgoSz,
                    int matchSuites)
{
    word32 i;
    int ret = WC_NO_ERR_TRACE(MATCH_SUITE_ERROR);
    byte minHash;

    /* set defaults */
    if (IsAtLeastTLSv1_3(ssl->version)) {
    #ifndef NO_CERTS
        /* TLS 1.3 cipher suites don't have public key algorithms in them.
         * Using the one in the certificate - if any.
         */
        ssl->options.sigAlgo = ssl->buffers.keyType;
    #endif
    }
    else {
        ssl->options.sigAlgo = ssl->specs.sig_algo;
    }
    if (ssl->options.sigAlgo == anonymous_sa_algo) {
        /* PSK ciphersuite - get digest to use from cipher suite */
        ssl->options.hashAlgo = ssl->specs.mac_algorithm;
        return 0;
    }
    ssl->options.hashAlgo = minHash = MinHashAlgo(ssl);

    /* No list means go with the defaults. */
    if (hashSigAlgoSz == 0)
        return 0;

    /* i+1 since two bytes used to describe hash and signature algorithm */
    for (i = 0; (i+1) < hashSigAlgoSz; i += HELLO_EXT_SIGALGO_SZ) {
        byte hashAlgo = 0, sigAlgo = 0;

        DecodeSigAlg(&hashSigAlgo[i], &hashAlgo, &sigAlgo);
        /* Keep looking if hash algorithm not strong enough. */
        if (hashAlgo < minHash)
            continue;
        /* Keep looking if signature algorithm isn't supported by cert. */
        if (!MatchSigAlgo(ssl, sigAlgo))
            continue;

        if (matchSuites) {
            /* Keep looking if peer algorithm isn't supported in our ssl->suites
             * or ssl->ctx->suites. */
            if (!SupportedHashSigAlgo(ssl, &hashSigAlgo[i])) {
                continue;
            }
        }

    #ifdef HAVE_ED25519
        if (ssl->pkCurveOID == ECC_ED25519_OID) {
            /* Matched Ed25519 - set chosen and finished. */
            ssl->options.sigAlgo = sigAlgo;
            ssl->options.hashAlgo = hashAlgo;
            ret = 0;
            break;
        }
    #endif
    #ifdef HAVE_ED448
        if (ssl->pkCurveOID == ECC_ED448_OID) {
            /* Matched Ed448 - set chosen and finished. */
            ssl->options.sigAlgo = sigAlgo;
            ssl->options.hashAlgo = hashAlgo;
            ret = 0;
            break;
        }
    #endif
    #if defined(HAVE_FALCON)
        if (ssl->pkCurveOID == CTC_FALCON_LEVEL1 ||
            ssl->pkCurveOID == CTC_FALCON_LEVEL5 ) {
            /* Matched Falcon - set chosen and finished. */
            ssl->options.sigAlgo = sigAlgo;
            ssl->options.hashAlgo = hashAlgo;
            ret = 0;
            break;
        }
    #endif /* HAVE_FALCON */
    #if defined(HAVE_DILITHIUM)
        if (ssl->pkCurveOID == CTC_ML_DSA_LEVEL2 ||
            ssl->pkCurveOID == CTC_ML_DSA_LEVEL3 ||
            ssl->pkCurveOID == CTC_ML_DSA_LEVEL5
            #ifdef WOLFSSL_DILITHIUM_FIPS204_DRAFT
         || ssl->pkCurveOID == CTC_DILITHIUM_LEVEL2
         || ssl->pkCurveOID == CTC_DILITHIUM_LEVEL3
         || ssl->pkCurveOID == CTC_DILITHIUM_LEVEL5
            #endif /* WOLFSSL_DILITHIUM_FIPS204_DRAFT */
            ) {
            /* Matched ML-DSA or Dilithium - set chosen and finished. */
            ssl->options.sigAlgo = sigAlgo;
            ssl->options.hashAlgo = hashAlgo;
            ret = 0;
            break;
        }
    #endif /* HAVE_DILITHIUM */
    #if defined(HAVE_ECC_BRAINPOOL)
        if (ssl->pkCurveOID == ECC_BRAINPOOLP256R1_OID ||
            ssl->pkCurveOID == ECC_BRAINPOOLP384R1_OID ||
            ssl->pkCurveOID == ECC_BRAINPOOLP512R1_OID) {
            /* Matched ECC Brainpool. Set sigAlgo to "normal" ECDSA here
             * for compatibility with TLS 1.2. */
            sigAlgo = ecc_dsa_sa_algo;
        }
    #endif

    #if defined(WOLFSSL_ECDSA_MATCH_HASH) && defined(USE_ECDSA_KEYSZ_HASH_ALGO)
        #error "WOLFSSL_ECDSA_MATCH_HASH and USE_ECDSA_KEYSZ_HASH_ALGO cannot "
               "be used together"
    #endif

    #if defined(HAVE_ECC) && (defined(WOLFSSL_TLS13) || \
                                              defined(WOLFSSL_ECDSA_MATCH_HASH))
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
        if (sigAlgo == sm2_sa_algo && hashAlgo == sm3_mac
        #ifndef WOLFSSL_ECDSA_MATCH_HASH
            && IsAtLeastTLSv1_3(ssl->version)
        #endif
           ) {
            /* Must be exact match. */
            if (CmpEccStrength(hashAlgo, ssl->buffers.keySz) != 0)
                continue;
            /* Matched SM2-SM3 - set chosen and finished. */
            ssl->options.sigAlgo = sigAlgo;
            ssl->options.hashAlgo = hashAlgo;
            ret = 0;
            break;
        }
        else
    #endif
        if (sigAlgo == ecc_dsa_sa_algo
        #ifndef WOLFSSL_ECDSA_MATCH_HASH
            && IsAtLeastTLSv1_3(ssl->version)
        #endif
            ) {
            /* Must be exact match. */
            if (CmpEccStrength(hashAlgo, ssl->buffers.keySz) != 0)
                continue;

            /* Matched ECDSA exactly - set chosen and finished. */
            ssl->options.hashAlgo = hashAlgo;
            ssl->options.sigAlgo = sigAlgo;
            ret = 0;
            break;
        }
    #endif

    /* For ECDSA the `USE_ECDSA_KEYSZ_HASH_ALGO` build option will choose a hash
     * algorithm that matches the ephemeral ECDHE key size or the next highest
     * available. This workaround resolves issue with some peer's that do not
     * properly support scenarios such as a P-256 key hashed with SHA512.
     */
    #if defined(HAVE_ECC) && defined(USE_ECDSA_KEYSZ_HASH_ALGO)
        if (sigAlgo == ecc_dsa_sa_algo) {
            int cmp = CmpEccStrength(hashAlgo, ssl->eccTempKeySz);

            /* Keep looking if digest not strong enough. */
            if (cmp < 0)
                continue;

            /* Looking for exact match or next highest. */
            if (ret != 0 || hashAlgo <= ssl->options.hashAlgo) {
                ssl->options.hashAlgo = hashAlgo;
                ssl->options.sigAlgo = sigAlgo;
            #if defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE)
                ssl->namedGroup = 0;
            #endif
                ret = 0;
            }

            /* Continue looking if not the same strength. */
            if (cmp > 0)
                continue;
            /* Exact match - finished. */
            break;
        }
    #endif

        switch (hashAlgo) {
        #ifndef NO_SHA
            case sha_mac:
        #endif
        #ifdef WOLFSSL_SHA224
            case sha224_mac:
        #endif
        #ifndef NO_SHA256
            case sha256_mac:
        #endif
        #ifdef WOLFSSL_SHA384
            case sha384_mac:
        #endif
        #ifdef WOLFSSL_SHA512
            case sha512_mac:
        #endif
        #ifdef WOLFSSL_SM3
            case sm3_mac:
        #endif
            #ifdef WOLFSSL_STRONGEST_HASH_SIG
                /* Is hash algorithm weaker than chosen/min? */
                if (hashAlgo < ssl->options.hashAlgo)
                    break;
            #else
                /* Is hash algorithm stronger than last chosen? */
                if (ret == 0 && hashAlgo > ssl->options.hashAlgo)
                    break;
            #endif
                if (IsAtLeastTLSv1_2(ssl) && !IsAtLeastTLSv1_3(ssl->version) &&
                        (ssl->options.side == WOLFSSL_CLIENT_END)) {
                    /* TLS 1.2 client deciding hash algorithm for
                     * CertificateVerify. Hash must be one of the handshake
                     * hashes being maintained. */
                    if (1
                    #ifndef NO_SHA
                        && (hashAlgo != sha_mac)
                    #endif
                    #ifndef NO_SHA256
                        && (hashAlgo != sha256_mac)
                    #endif
                    #ifdef WOLFSSL_SHA384
                        && (hashAlgo != sha384_mac)
                    #endif
                    #ifdef WOLFSSL_SHA512
                        && (hashAlgo != sha512_mac)
                    #endif
                    #ifdef WOLFSSL_SM3
                        && (hashAlgo != sm3_mac)
                    #endif
                        )
                        {
                            break;
                        }
                }
                /* The chosen one - but keep looking. */
                ssl->options.hashAlgo = hashAlgo;
                ssl->options.sigAlgo = sigAlgo;
                ret = 0;
                break;
            default:
                /* Support for hash algorithm not compiled in. */
                break;
        }
    }

    return ret;
}
#endif /* !NO_TLS && (!defined(NO_WOLFSSL_SERVER) || !defined(NO_CERTS)) */

#if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)

    /* Initialize HandShakeInfo */
    void InitHandShakeInfo(HandShakeInfo* info, WOLFSSL* ssl)
    {
        int i;

        info->ssl = ssl;
        info->cipherName[0] = 0;
        for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++)
            info->packetNames[i][0] = 0;
        info->numberPackets = 0;
        info->negotiationError = 0;
    }

    /* Set Final HandShakeInfo parameters */
    void FinishHandShakeInfo(HandShakeInfo* info)
    {
        int i;
        int sz = GetCipherNamesSize();

        for (i = 0; i < sz; i++) {
#ifndef NO_CIPHER_SUITE_ALIASES
            if (cipher_names[i].flags & WOLFSSL_CIPHER_SUITE_FLAG_NAMEALIAS)
                continue;
#endif
            if (info->ssl->options.cipherSuite ==
                                            (byte)cipher_names[i].cipherSuite) {
                if (info->ssl->options.cipherSuite0 == ECC_BYTE)
                    continue;   /* ECC suites at end */
                XSTRNCPY(info->cipherName, cipher_names[i].name, MAX_CIPHERNAME_SZ);
                info->cipherName[MAX_CIPHERNAME_SZ] = '\0';
                break;
            }
        }

        /* error max and min are negative numbers */
        if (info->ssl->error <= MIN_PARAM_ERR && info->ssl->error >= MAX_PARAM_ERR)
            info->negotiationError = info->ssl->error;
    }


    /* Add name to info packet names, increase packet name count */
    void AddPacketName(WOLFSSL* ssl, const char* name)
    {
    #ifdef WOLFSSL_CALLBACKS
        HandShakeInfo* info = &ssl->handShakeInfo;
        if (info->numberPackets < MAX_PACKETS_HANDSHAKE) {
            char* packetName = info->packetNames[info->numberPackets];
            XSTRNCPY(packetName, name, MAX_PACKETNAME_SZ);
            packetName[MAX_PACKETNAME_SZ] = '\0';
            info->numberPackets++;
        }
    #endif
        (void)ssl;
        (void)name;
    }


    #ifdef WOLFSSL_CALLBACKS
    /* Initialize TimeoutInfo */
    void InitTimeoutInfo(TimeoutInfo* info)
    {
        XMEMSET(info, 0, sizeof(TimeoutInfo));
    }


    /* Free TimeoutInfo */
    void FreeTimeoutInfo(TimeoutInfo* info, void* heap)
    {
        int i;
        (void)heap;
        for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++) {
            if (info->packets[i].bufferValue) {
                XFREE(info->packets[i].bufferValue, heap, DYNAMIC_TYPE_INFO);
                info->packets[i].bufferValue = NULL;
            }
        }
    }

    /* Add packet name to previously added packet info */
    void AddLateName(const char* name, TimeoutInfo* info)
    {
        /* make sure we have a valid previous one */
        if (info->numberPackets > 0 && info->numberPackets <
                                                        MAX_PACKETS_HANDSHAKE) {
            char* packetName = info->packets[info->numberPackets-1].packetName;
            XSTRNCPY(packetName, name, MAX_PACKETNAME_SZ);
            packetName[MAX_PACKETNAME_SZ] = '\0';
        }
    }

    /* Add record header to previously added packet info */
    void AddLateRecordHeader(const RecordLayerHeader* rl, TimeoutInfo* info)
    {
        /* make sure we have a valid previous one */
        if (info->numberPackets > 0 && info->numberPackets <
                                                        MAX_PACKETS_HANDSHAKE) {
            if (info->packets[info->numberPackets - 1].bufferValue)
                XMEMCPY(info->packets[info->numberPackets - 1].bufferValue, rl,
                       RECORD_HEADER_SZ);
            else
                XMEMCPY(info->packets[info->numberPackets - 1].value, rl,
                       RECORD_HEADER_SZ);
        }
    }

    #endif /* WOLFSSL_CALLBACKS */


    /* Add PacketInfo to TimeoutInfo
     *
     * ssl   WOLFSSL structure sending or receiving packet
     * name  name of packet being sent
     * type  type of packet being sent
     * data  data bing sent with packet
     * sz    size of data buffer
     * lateRL  save space for record layer in TimoutInfo struct
     * written 1 if this packet is being written to wire, 0 if being read
     * heap  custom heap to use for mallocs/frees
     */
    int AddPacketInfo(WOLFSSL* ssl, const char* name, int type,
            const byte* data, int sz, int written, int lateRL, void* heap)
    {
    #ifdef WOLFSSL_CALLBACKS
        TimeoutInfo* info = &ssl->timeoutInfo;

        if (info->numberPackets < (MAX_PACKETS_HANDSHAKE - 1)) {
            WOLFSSL_TIMEVAL currTime;
            int totalSz;

            /* add in space for post record layer */
            totalSz = sz + lateRL;

            /* may add name after */
            if (name) {
                char* packetName = info->packets[info->numberPackets].packetName;
                XSTRNCPY(packetName, name, MAX_PACKETNAME_SZ);
                packetName[MAX_PACKETNAME_SZ] = '\0';
            }

            /* add data, put in buffer if bigger than static buffer */
            info->packets[info->numberPackets].valueSz = totalSz;
            if (totalSz < MAX_VALUE_SZ) {
                XMEMCPY(info->packets[info->numberPackets].value + lateRL, data,
                               sz);
            }
            else {
                info->packets[info->numberPackets].bufferValue =
                               (byte*)XMALLOC(totalSz, heap, DYNAMIC_TYPE_INFO);
                if (!info->packets[info->numberPackets].bufferValue) {
                    /* let next alloc catch, just don't fill, not fatal here  */
                    info->packets[info->numberPackets].valueSz = 0;
                }
                else {
                    /* copy over data (which has the handshake header), leaving
                     * room for post record layer header if set */
                    XMEMCPY(info->packets[info->numberPackets].bufferValue +
                            lateRL, data, sz);
                }
            }

            if (gettimeofday(&currTime, 0) < 0)
                return SYSLIB_FAILED_E;

            info->packets[info->numberPackets].timestamp.tv_sec  =
                                                             currTime.tv_sec;
            info->packets[info->numberPackets].timestamp.tv_usec =
                                                             currTime.tv_usec;
            info->numberPackets++;
        }
    #endif /* WOLFSSL_CALLBACKS */
    #ifdef OPENSSL_EXTRA
        if ((ssl->protoMsgCb != NULL) && (sz > 0) &&
            (ssl->keys.encryptionOn != 1)) {
            /* version from hex to dec  16 is 16^1, 256 from 16^2 and
               4096 from 16^3 */
            int version = (ssl->version.minor & 0x0F) +
                          ((ssl->version.minor & 0xF0) << 4) +
                          ((ssl->version.major & 0x0F) << 8) +
                          ((ssl->version.major & 0xF0) << 12);

            ssl->protoMsgCb(written, version, type,
                         (const void *)data, (size_t)sz,
                         ssl, ssl->protoMsgCtx);
        }
    #endif /* OPENSSL_EXTRA */
        (void)written;
        (void)name;
        (void)heap;
        (void)type;
        (void)ssl;
        (void)lateRL;

        return 0;
    }

#endif /* WOLFSSL_CALLBACKS || OPENSSL_EXTRA */

#if !defined(NO_CERTS)

#if defined(WOLF_PRIVATE_KEY_ID) && !defined(NO_CHECK_PRIVATE_KEY)
/* Create a private key for a device.
 *
 * pkey    Key object.
 * data    Data to identify key.
 * length  Length of data.
 * hsType  Type of the key to create.
 * heap    Custom heap to use for mallocs/frees
 * devId   Id for device.
 * return  0 on success.
 * return  NOT_COMPILED_IN if algorithm type not supported.
 * return  MEMORY_E on memory allocation failure.
 * return  other internal error
 */
int CreateDevPrivateKey(void** pkey, byte* data, word32 length, int hsType,
                        int label, int id, void* heap, int devId)
{
    int ret = WC_NO_ERR_TRACE(NOT_COMPILED_IN);

    if (hsType == DYNAMIC_TYPE_RSA) {
#ifndef NO_RSA
        RsaKey* rsaKey;

        rsaKey = (RsaKey*)XMALLOC(sizeof(RsaKey), heap, DYNAMIC_TYPE_RSA);
        if (rsaKey == NULL) {
            return MEMORY_E;
        }

        if (label) {
            ret = wc_InitRsaKey_Label(rsaKey, (char*)data, heap, devId);
        }
        else if (id) {
            ret = wc_InitRsaKey_Id(rsaKey, data, (int)length, heap, devId);
        }
        if (ret == 0) {
            *pkey = (void*)rsaKey;
        }
        else {
            XFREE(rsaKey, heap, DYNAMIC_TYPE_RSA);
        }
#endif
    }
    else if (hsType == DYNAMIC_TYPE_ECC) {
#ifdef HAVE_ECC
        ecc_key* ecKey;

        ecKey = (ecc_key*)XMALLOC(sizeof(ecc_key), heap, DYNAMIC_TYPE_ECC);
        if (ecKey == NULL) {
            return MEMORY_E;
        }

        if (label) {
            ret = wc_ecc_init_label(ecKey, (char*)data, heap, devId);
        }
        else if (id) {
            ret = wc_ecc_init_id(ecKey, data, (int)length, heap, devId);
        }
        if (ret == 0) {
            *pkey = (void*)ecKey;
        }
        else {
            XFREE(ecKey, heap, DYNAMIC_TYPE_ECC);
        }
#endif
    }
    else if (hsType == DYNAMIC_TYPE_DILITHIUM) {
#if defined(HAVE_DILITHIUM)
        dilithium_key* dilithiumKey;

        dilithiumKey = (dilithium_key*)XMALLOC(sizeof(dilithium_key), heap,
                                               DYNAMIC_TYPE_DILITHIUM);
        if (dilithiumKey == NULL) {
            return MEMORY_E;
        }

        if (label) {
            ret = wc_dilithium_init_label(dilithiumKey, (char*)data,
                                          heap, devId);
        }
        else if (id) {
            ret = wc_dilithium_init_id(dilithiumKey, data, length, heap, devId);
        }
        if (ret == 0) {
            *pkey = (void*)dilithiumKey;
        }
        else {
            XFREE(dilithiumKey, heap, DYNAMIC_TYPE_DILITHIUM);
        }
#endif
    }
    else if (hsType == DYNAMIC_TYPE_FALCON) {
#if defined(HAVE_FALCON)
        falcon_key* falconKey;

        falconKey = (falcon_key*)XMALLOC(sizeof(falcon_key), heap,
                                         DYNAMIC_TYPE_FALCON);
        if (falconKey == NULL) {
            return MEMORY_E;
        }

        if (label) {
            ret = wc_falcon_init_label(falconKey, (char*)data, heap, devId);
        }
        else if (id) {
            ret = wc_falcon_init_id(falconKey, data, length, heap, devId);
        }
        if (ret == 0) {
            *pkey = (void*)falconKey;
        }
        else {
            XFREE(falconKey, heap, DYNAMIC_TYPE_FALCON);
        }
#endif
    }

    return ret;
}
#endif /* WOLF_PRIVATE_KEY_ID && !NO_CHECK_PRIVATE_KEY */

/* Decode a private key - RSA/ECC/Ed25519/Ed448/Falcon/Dilithium - and
 * create a key object. The signature type is set as well.
 *
 * ssl          The SSL/TLS object.
 * keyType      The type of the key to decode.
 * key          The key to decode.
 * hsType       The handshake related type of the decoded key.
 * hsKey        The decoded key.
 * keyDevId     The devId of the key.
 * keyIdSet     The key data contains a key id.
 * keyLabelSet  The key data contains a key label.
 * keySz        The size of the key.
 * sigLen       The length of a signature.
 *
 * returns 0 on success, otherwise failure.
 */
static int DecodePrivateKey_ex(WOLFSSL *ssl, byte keyType, const DerBuffer* key,
                word32* hsType, void** hsKey, int keyDevId, byte keyIdSet,
                byte keyLabelSet, int keySz, word32* sigLen)
{
    int      ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG);
    int      keySzDecoded;
    word32   idx;

    /* make sure private key exists */
    if (key == NULL || key->buffer == NULL) {
        /* allow no private key if using external */
    #ifdef WOLF_PRIVATE_KEY_ID
        if (ssl->devId != INVALID_DEVID
        #ifdef HAVE_PK_CALLBACKS
            || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)
        #endif
        ) {
            *sigLen = (word32)GetPrivateKeySigSize(ssl);
            return 0;
        }
        else
    #endif
        {
            WOLFSSL_MSG("Private key missing!");
            ERROR_OUT(NO_PRIVATE_KEY, exit_dpk);
        }
    }

#if defined(WOLF_PRIVATE_KEY_ID) && !defined(NO_CHECK_PRIVATE_KEY)
    if (keyDevId != INVALID_DEVID && (keyIdSet || keyLabelSet)) {
        /* Set hsType */
        if (keyType == rsa_sa_algo)
            *hsType = DYNAMIC_TYPE_RSA;
        else if (keyType == ecc_dsa_sa_algo)
            *hsType = DYNAMIC_TYPE_ECC;
        else if ((keyType == falcon_level1_sa_algo) ||
                 (keyType == falcon_level5_sa_algo))
            *hsType = DYNAMIC_TYPE_FALCON;
        else if ((keyType == dilithium_level2_sa_algo) ||
                 (keyType == dilithium_level3_sa_algo) ||
                 (keyType == dilithium_level5_sa_algo))
            *hsType = DYNAMIC_TYPE_DILITHIUM;

        /* Create the private key */
        ret = CreateDevPrivateKey(hsKey, key->buffer,
                                  key->length, *hsType,
                                  keyLabelSet, keyIdSet, ssl->heap,
                                  keyDevId);
        if (ret != 0) {
            goto exit_dpk;
        }

        /* Check key size */
        if (*hsType == DYNAMIC_TYPE_RSA) {
    #ifndef NO_RSA
            if (keySz < ssl->options.minRsaKeySz) {
                WOLFSSL_MSG("RSA key size too small");
                ERROR_OUT(RSA_KEY_SIZE_E, exit_dpk);
            }

            /* Return the maximum signature length. */
            *sigLen = (word32)keySz;
    #else
            ret = NOT_COMPILED_IN;
    #endif
        }
        else if (*hsType == DYNAMIC_TYPE_ECC) {
    #ifdef HAVE_ECC
            if (keySz < ssl->options.minEccKeySz) {
                WOLFSSL_MSG("ECC key size too small");
                ERROR_OUT(ECC_KEY_SIZE_E, exit_dpk);
            }

            /* Return the maximum signature length. */
            *sigLen = (word32)wc_ecc_sig_size_calc(keySz);
    #else
            ret = NOT_COMPILED_IN;
    #endif
        }
        else if (*hsType == DYNAMIC_TYPE_FALCON) {
    #if defined(HAVE_FALCON)
            if (keyType == falcon_level1_sa_algo) {
                ret = wc_falcon_set_level((falcon_key*)*hsKey, 1);
            }
            else if (keyType == falcon_level5_sa_algo) {
                ret = wc_falcon_set_level((falcon_key*)*hsKey, 5);
            }

            if (ret == 0) {
                if (keySz < ssl->options.minFalconKeySz) {
                    WOLFSSL_MSG("Falcon key size too small");
                    ERROR_OUT(FALCON_KEY_SIZE_E, exit_dpk);
                }

                /* Return the maximum signature length. */
                *sigLen = wc_falcon_sig_size((falcon_key*)*hsKey);
            }
    #else
            ret = NOT_COMPILED_IN;
    #endif
        }
        else if (*hsType == DYNAMIC_TYPE_DILITHIUM) {
    #if defined(HAVE_DILITHIUM) && !defined(WOLFSSL_DILITHIUM_NO_SIGN)
            if (keyType == dilithium_level2_sa_algo) {
                ret = wc_dilithium_set_level((dilithium_key*)*hsKey,
                                             WC_ML_DSA_44);
            }
            else if (keyType == dilithium_level3_sa_algo) {
                ret = wc_dilithium_set_level((dilithium_key*)*hsKey,
                                             WC_ML_DSA_65);
            }
            else if (keyType == dilithium_level5_sa_algo) {
                ret = wc_dilithium_set_level((dilithium_key*)*hsKey,
                                             WC_ML_DSA_87);
            }

            if (ret == 0) {
                if (keySz < ssl->options.minDilithiumKeySz) {
                    WOLFSSL_MSG("Dilithium key size too small");
                    ERROR_OUT(DILITHIUM_KEY_SIZE_E, exit_dpk);
                }

                /* Return the maximum signature length. */
                *sigLen = wc_dilithium_sig_size((dilithium_key*)*hsKey);
            }
    #else
            ret = NOT_COMPILED_IN;
    #endif
        }
        goto exit_dpk;
    }
#endif /* WOLF_PRIVATE_KEY_ID */

#ifndef NO_RSA
    if (keyType == rsa_sa_algo || keyType == 0) {
        *hsType = DYNAMIC_TYPE_RSA;
        ret = AllocKey(ssl, (int)*hsType, hsKey);
        if (ret != 0) {
            goto exit_dpk;
        }

        WOLFSSL_MSG("Trying RSA private key");

        /* Set start of data to beginning of buffer. */
        idx = 0;
        /* Decode the key assuming it is an RSA private key. */
        ret = wc_RsaPrivateKeyDecode(key->buffer, &idx,
                    (RsaKey*)*hsKey, key->length);
    #ifdef WOLF_PRIVATE_KEY_ID
        /* if using external key then allow using a public key */
        if (ret != 0 && (ssl->devId != INVALID_DEVID
        #ifdef HAVE_PK_CALLBACKS
            || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)
        #endif
        )) {
            WOLFSSL_MSG("Trying RSA public key with crypto callbacks");
            idx = 0;
            ret = wc_RsaPublicKeyDecode(key->buffer, &idx,
                        (RsaKey*)*hsKey, key->length);
        }
    #endif
        if (ret == 0) {
            WOLFSSL_MSG("Using RSA private key");

            /* It worked so check it meets minimum key size requirements. */
            keySzDecoded = wc_RsaEncryptSize((RsaKey*)*hsKey);
            if (keySzDecoded < 0) { /* check if keySzDecoded has error case */
                ERROR_OUT(keySzDecoded, exit_dpk);
            }

            if (keySzDecoded < ssl->options.minRsaKeySz) {
                WOLFSSL_MSG("RSA key size too small");
                ERROR_OUT(RSA_KEY_SIZE_E, exit_dpk);
            }

            /* Return the maximum signature length. */
            *sigLen = (word32)keySzDecoded;

            goto exit_dpk;
        }
    }
#endif /* !NO_RSA */

#ifdef HAVE_ECC
#ifndef NO_RSA
    FreeKey(ssl, (int)*hsType, hsKey);
#endif /* !NO_RSA */

    if (keyType == ecc_dsa_sa_algo || keyType == 0
    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
         || keyType == sm2_sa_algo
    #endif
        ) {
        *hsType = DYNAMIC_TYPE_ECC;
        ret = AllocKey(ssl, (int)*hsType, hsKey);
        if (ret != 0) {
            goto exit_dpk;
        }

        WOLFSSL_MSG("Trying ECC private key");

        /* Set start of data to beginning of buffer. */
        idx = 0;
        /* Decode the key assuming it is an ECC private key. */
        ret = wc_EccPrivateKeyDecode(key->buffer, &idx,
                                     (ecc_key*)*hsKey,
                                     key->length);
    #ifdef WOLF_PRIVATE_KEY_ID
        /* if using external key then allow using a public key */
        if (ret != 0 && (ssl->devId != INVALID_DEVID
        #ifdef HAVE_PK_CALLBACKS
            || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)
        #endif
        )) {
            WOLFSSL_MSG("Trying ECC public key with crypto callbacks");
            idx = 0;
            ret = wc_EccPublicKeyDecode(key->buffer, &idx,
                                     (ecc_key*)*hsKey,
                                     key->length);
        }
    #endif
    #ifdef WOLFSSL_SM2
        if ((ret == 0) && (keyType == sm2_sa_algo)) {
            ret = wc_ecc_set_curve((ecc_key*)*hsKey,
                WOLFSSL_SM2_KEY_BITS / 8, ECC_SM2P256V1);
        }
    #endif
        if (ret == 0) {
            WOLFSSL_MSG("Using ECC private key");

            /* Check it meets the minimum ECC key size requirements. */
            keySzDecoded = wc_ecc_size((ecc_key*)*hsKey);
            if (keySzDecoded < ssl->options.minEccKeySz) {
                WOLFSSL_MSG("ECC key size too small");
                ERROR_OUT(ECC_KEY_SIZE_E, exit_dpk);
            }

            /* Return the maximum signature length. */
            *sigLen = (word32)wc_ecc_sig_size((ecc_key*)*hsKey);

            goto exit_dpk;
        }
    }
#endif
#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)
    #if !defined(NO_RSA) || defined(HAVE_ECC)
        FreeKey(ssl, *hsType, hsKey);
    #endif

    if (keyType == ed25519_sa_algo || keyType == 0) {
        *hsType = DYNAMIC_TYPE_ED25519;
        ret = AllocKey(ssl, *hsType, hsKey);
        if (ret != 0) {
            goto exit_dpk;
        }

        WOLFSSL_MSG("Trying ED25519 private key");

        /* Set start of data to beginning of buffer. */
        idx = 0;
        /* Decode the key assuming it is an ED25519 private key. */
        ret = wc_Ed25519PrivateKeyDecode(key->buffer, &idx,
                                         (ed25519_key*)*hsKey,
                                         key->length);
    #ifdef WOLF_PRIVATE_KEY_ID
        /* if using external key then allow using a public key */
        if (ret != 0 && (ssl->devId != INVALID_DEVID
        #ifdef HAVE_PK_CALLBACKS
            || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)
        #endif
        )) {
            WOLFSSL_MSG("Trying ED25519 public key with crypto callbacks");
            idx = 0;
            ret = wc_Ed25519PublicKeyDecode(key->buffer, &idx,
                                           (ed25519_key*)*hsKey,
                                            key->length);
        }
    #endif
        if (ret == 0) {
            WOLFSSL_MSG("Using ED25519 private key");

            /* Check it meets the minimum ECC key size requirements. */
            if (ED25519_KEY_SIZE < ssl->options.minEccKeySz) {
                WOLFSSL_MSG("ED25519 key size too small");
                ERROR_OUT(ECC_KEY_SIZE_E, exit_dpk);
            }

            /* Return the maximum signature length. */
            *sigLen = ED25519_SIG_SIZE;

            goto exit_dpk;
        }
    }
#endif /* HAVE_ED25519 && HAVE_ED25519_KEY_IMPORT */
#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)
    #if !defined(NO_RSA) || defined(HAVE_ECC)
        FreeKey(ssl, *hsType, hsKey);
    #endif

    if (keyType == ed448_sa_algo || keyType == 0) {
        *hsType = DYNAMIC_TYPE_ED448;
        ret = AllocKey(ssl, *hsType, hsKey);
        if (ret != 0) {
            goto exit_dpk;
        }

        WOLFSSL_MSG("Trying ED448 private key");

        /* Set start of data to beginning of buffer. */
        idx = 0;
        /* Decode the key assuming it is an ED448 private key. */
        ret = wc_Ed448PrivateKeyDecode(key->buffer, &idx,
                                       (ed448_key*)*hsKey,
                                       key->length);
    #ifdef WOLF_PRIVATE_KEY_ID
        /* if using external key then allow using a public key */
        if (ret != 0 && (ssl->devId != INVALID_DEVID
        #ifdef HAVE_PK_CALLBACKS
            || wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)
        #endif
        )) {
            WOLFSSL_MSG("Trying ED25519 public key with crypto callbacks");
            idx = 0;
            ret = wc_Ed448PublicKeyDecode(key->buffer, &idx,
                                          (ed448_key*)*hsKey,
                                          key->length);
        }
    #endif
        if (ret == 0) {
            WOLFSSL_MSG("Using ED448 private key");

            /* Check it meets the minimum ECC key size requirements. */
            if (ED448_KEY_SIZE < ssl->options.minEccKeySz) {
                WOLFSSL_MSG("ED448 key size too small");
                ERROR_OUT(ECC_KEY_SIZE_E, exit_dpk);
            }

            /* Return the maximum signature length. */
            *sigLen = ED448_SIG_SIZE;

            goto exit_dpk;
        }
    }
#endif /* HAVE_ED448 && HAVE_ED448_KEY_IMPORT */
#if defined(HAVE_FALCON)
    #if !defined(NO_RSA) || defined(HAVE_ECC)
        FreeKey(ssl, *hsType, hsKey);
    #endif

    if (keyType == falcon_level1_sa_algo ||
        keyType == falcon_level5_sa_algo ||
        keyType == 0) {

        *hsType = DYNAMIC_TYPE_FALCON;
        ret = AllocKey(ssl, *hsType, hsKey);
        if (ret != 0) {
            goto exit_dpk;
        }

        if (keyType == falcon_level1_sa_algo) {
            ret = wc_falcon_set_level((falcon_key*)*hsKey, 1);
        }
        else if (keyType == falcon_level5_sa_algo) {
            ret = wc_falcon_set_level((falcon_key*)*hsKey, 5);
        }
        else {
            /* What if keyType is 0? We might want to do something
             * more graceful here. */
            ret = ALGO_ID_E;
        }

        if (ret != 0) {
            goto exit_dpk;
        }

        WOLFSSL_MSG("Trying Falcon private key");

        /* Set start of data to beginning of buffer. */
        idx = 0;
        /* Decode the key assuming it is a Falcon private key. */
        ret = wc_falcon_import_private_only(key->buffer,
                                            key->length,
                                            (falcon_key*)*hsKey);
        if (ret == 0) {
            WOLFSSL_MSG("Using Falcon private key");

            /* Check it meets the minimum Falcon key size requirements. */
            keySzDecoded = wc_falcon_size((falcon_key*)*hsKey);
            if (keySzDecoded < ssl->options.minFalconKeySz) {
                WOLFSSL_MSG("Falcon key size too small");
                ERROR_OUT(FALCON_KEY_SIZE_E, exit_dpk);
            }

            /* Return the maximum signature length. */
            *sigLen = wc_falcon_sig_size((falcon_key*)*hsKey);

            goto exit_dpk;
        }
    }
#endif /* HAVE_FALCON */
#if defined(HAVE_DILITHIUM) && !defined(WOLFSSL_DILITHIUM_NO_SIGN) && \
    !defined(WOLFSSL_DILITHIUM_NO_ASN1)
    #if !defined(NO_RSA) || defined(HAVE_ECC)
        FreeKey(ssl, *hsType, hsKey);
    #endif

    if (keyType == dilithium_level2_sa_algo ||
        keyType == dilithium_level3_sa_algo ||
        keyType == dilithium_level5_sa_algo ||
        keyType == 0) {

        *hsType = DYNAMIC_TYPE_DILITHIUM;
        ret = AllocKey(ssl, *hsType, hsKey);
        if (ret != 0) {
            goto exit_dpk;
        }

        if (keyType == dilithium_level2_sa_algo) {
            ret = wc_dilithium_set_level((dilithium_key*)*hsKey, WC_ML_DSA_44);
        }
        else if (keyType == dilithium_level3_sa_algo) {
            ret = wc_dilithium_set_level((dilithium_key*)*hsKey, WC_ML_DSA_65);
        }
        else if (keyType == dilithium_level5_sa_algo) {
            ret = wc_dilithium_set_level((dilithium_key*)*hsKey, WC_ML_DSA_87);
        }
        else {
            /* What if keyType is 0? We might want to do something
             * more graceful here. */
            ret = ALGO_ID_E;
        }

        if (ret != 0) {
            goto exit_dpk;
        }

        WOLFSSL_MSG("Trying Dilithium private key");

        /* Set start of data to beginning of buffer. */
        idx = 0;
        /* Decode the key assuming it is a Dilithium private key. */
        ret = wc_Dilithium_PrivateKeyDecode(key->buffer,
                                            &idx,
                                            (dilithium_key*)*hsKey,
                                            key->length);
        if (ret == 0) {
            WOLFSSL_MSG("Using Dilithium private key");

            /* Check it meets the minimum Dilithium key size requirements. */
            keySzDecoded = wc_dilithium_size((dilithium_key*)*hsKey);
            if (keySzDecoded < ssl->options.minDilithiumKeySz) {
                WOLFSSL_MSG("Dilithium key size too small");
                ERROR_OUT(DILITHIUM_KEY_SIZE_E, exit_dpk);
            }

            /* Return the maximum signature length. */
            *sigLen = wc_dilithium_sig_size((dilithium_key*)*hsKey);

            goto exit_dpk;
        }
    }
#endif /* HAVE_DILITHIUM */

    (void)idx;
    (void)keySzDecoded;
    (void)keyType;
    (void)key;
    (void)hsType;
    (void)hsKey;
    (void)keyDevId;
    (void)keyIdSet;
    (void)keyLabelSet;
    (void)keySz;
    (void)sigLen;

exit_dpk:
    if (ret != 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
    }

    return ret;
}

/* Decode the private key - RSA/ECC/Ed25519/Ed448/Falcon/Dilithium - and
 * creates a key object.
 *
 * The signature type is set as well.
 * The maximum length of a signature is returned.
 *
 * ssl     The SSL/TLS object.
 * sigLen  The length of a signature.
 * returns 0 on success, otherwise failure.
 */
int DecodePrivateKey(WOLFSSL *ssl, word32* sigLen)
{
    int ret = 0;

    ret = DecodePrivateKey_ex(ssl, ssl->buffers.keyType, ssl->buffers.key,
                &ssl->hsType, &ssl->hsKey, ssl->buffers.keyDevId,
                ssl->buffers.keyId, ssl->buffers.keyLabel, ssl->buffers.keySz,
                sigLen);

    return ret;
}

#if defined(WOLFSSL_DUAL_ALG_CERTS)
/* This is just like the above, but uses the alternative key of the ssl object,
 * not the primary key.
 */
int DecodeAltPrivateKey(WOLFSSL *ssl, word32* sigLen)
{
    int ret = 0;

    ret = DecodePrivateKey_ex(ssl, ssl->buffers.altKeyType, ssl->buffers.altKey,
                &ssl->hsAltType, &ssl->hsAltKey, ssl->buffers.altKeyDevId,
                ssl->buffers.altKeyId, ssl->buffers.altKeyLabel,
                ssl->buffers.altKeySz, sigLen);

    return ret;
}
#endif /* WOLFSSL_DUAL_ALG_CERTS */

#endif /* !NO_CERTS */

#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12)
    /* returns 1 if able to do TLS 1.3 otherwise 0 */
    int TLSv1_3_Capable(WOLFSSL* ssl)
    {
    #ifndef WOLFSSL_TLS13
        return 0;
    #else
        int ret = 0;

        if (IsAtLeastTLSv1_3(ssl->ctx->method->version)) {
            ret = 1;
        }

        if ((wolfSSL_get_options(ssl) & WOLFSSL_OP_NO_TLSv1_3)) {
            /* option set at run time to disable TLS 1.3 */
            ret = 0;
        }

        return ret;
    #endif
    }
#endif /* WOLFSSL_TLS13 */

#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12)
#if (!defined(NO_WOLFSSL_CLIENT) && (!defined(NO_DH) || defined(HAVE_ECC) || \
      defined(HAVE_CURVE25519) || defined(HAVE_CURVE448))) || \
    (!defined(NO_WOLFSSL_SERVER) && (defined(HAVE_ECC) || \
      ((defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \
       (defined(HAVE_ED25519) || defined(HAVE_ED448) || !defined(NO_RSA)))) || \
     (!defined(NO_DH) && (!defined(NO_RSA) || defined(HAVE_ANON))))
/* Returns whether the signature algorithm requires caching of messages.
 *
 * @param [in] sigAlgo  Signature algorithm.
 * @return  1 when caching required.
 * @return  0 when caching not required.
 */
static int SigAlgoCachesMsgs(int sigAlgo)
{
    int ret;

    (void)sigAlgo;

#ifdef HAVE_ED25519
    if (sigAlgo == ed25519_sa_algo) {
        ret = 1;
    }
    else
#endif
#ifdef HAVE_ED448
    if (sigAlgo == ed448_sa_algo) {
        ret = 1;
    }
    else
#endif
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
    if (sigAlgo == sm2_sa_algo) {
        ret = 1;
    }
    else
#endif
    {
        ret = 0;
    }

    return ret;
}

static int HashSkeData(WOLFSSL* ssl, enum wc_HashType hashType,
    const byte* data, word32 sz, byte sigAlgo)
{
    int ret = 0;
    int digest_sz = wc_HashGetDigestSize(hashType);

    if (digest_sz <= 0) {
        ret = BUFFER_ERROR;
    }

    if (ret == 0) {
        word32 new_size = SEED_LEN;
        /* buffer for signature */
        if (! WC_SAFE_SUM_WORD32(new_size, sz, new_size))
            ret = MEMORY_E;
        else {
            ssl->buffers.sig.buffer = (byte*)XMALLOC(new_size, ssl->heap,
                                                        DYNAMIC_TYPE_SIGNATURE);
            if (ssl->buffers.sig.buffer == NULL) {
                ret = MEMORY_E;
            }
        }
    }
    if (ret == 0) {
        ssl->buffers.sig.length = SEED_LEN + sz;

        /* build message to hash */
        XMEMCPY(ssl->buffers.sig.buffer, ssl->arrays->clientRandom, RAN_LEN);
        XMEMCPY(&ssl->buffers.sig.buffer[RAN_LEN], ssl->arrays->serverRandom,
            RAN_LEN);
        /* message */
        XMEMCPY(&ssl->buffers.sig.buffer[RAN_LEN * 2], data, sz);
    }
    if (ret == 0 && !SigAlgoCachesMsgs(sigAlgo)) {
        ssl->buffers.digest.length = (unsigned int)digest_sz;

        /* buffer for hash */
        if (!ssl->options.dontFreeDigest) {
            XFREE(ssl->buffers.digest.buffer, ssl->heap,
                  DYNAMIC_TYPE_DIGEST);
        }
        ssl->options.dontFreeDigest = 0;

        ssl->buffers.digest.buffer = (byte*)XMALLOC(ssl->buffers.digest.length,
            ssl->heap, DYNAMIC_TYPE_DIGEST);
        if (ssl->buffers.digest.buffer == NULL) {
            ret = MEMORY_E;
        }
    }
    if (ret == 0 && !SigAlgoCachesMsgs(sigAlgo)) {
        /* Perform hash. Only wc_Hash supports MD5_SHA1. */
        ret = wc_Hash(hashType, ssl->buffers.sig.buffer,
                                ssl->buffers.sig.length,
                                ssl->buffers.digest.buffer,
                                ssl->buffers.digest.length);
#ifdef HAVE_PK_CALLBACKS
        if (ssl->ctx->ProcessServerSigKexCb == NULL)
#endif
        {
            /* No further processing will be done. It can be freed. */
            XFREE(ssl->buffers.sig.buffer, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
            ssl->buffers.sig.buffer = NULL;
        }
    }

    return ret;
}
#endif /* !NO_WOLFSSL_CLIENT [...etc] || !NO_WOLFSSL_SERVER [...etc] */
#endif /* !NO_TLS && !WOLFSSL_NO_TLS12 */

/* used in server and client */
#if !defined(WOLFSSL_NO_TLS12) && \
    (!defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS) && \
      (!defined(NO_DH) && (!defined(NO_PSK) || !defined(NO_RSA) \
       || defined(HAVE_ANON) && !defined(WOLFSSL_NO_TLS12))) \
     || !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && !defined(NO_DH))
static int DhSetKey(WOLFSSL* ssl)
{
    int ret;

    #if !defined(WOLFSSL_OLD_PRIME_CHECK) && \
        !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)
    if (ssl->options.dhDoKeyTest &&
        !ssl->options.dhKeyTested)
    {
        ret = wc_DhSetCheckKey(ssl->buffers.serverDH_Key,
                               ssl->buffers.serverDH_P.buffer,
                               ssl->buffers.serverDH_P.length,
                               ssl->buffers.serverDH_G.buffer,
                               ssl->buffers.serverDH_G.length,
                               NULL, 0, 0, ssl->rng);
        if (ret != 0)
            return ret;
        ssl->options.dhKeyTested = 1;
    }
    else
    #endif
    {
        ret = wc_DhSetKey(ssl->buffers.serverDH_Key,
                          ssl->buffers.serverDH_P.buffer,
                          ssl->buffers.serverDH_P.length,
                          ssl->buffers.serverDH_G.buffer,
                          ssl->buffers.serverDH_G.length);
        if (ret != 0)
            return ret;
    }
    return 0;
}
#endif /* !NO_DH */

#if !defined(NO_TLS) && !defined(WOLFSSL_NO_TLS12) && !defined(NO_PSK)
static void MakePSKPreMasterSecret(Arrays* arrays, byte use_psk_key)
{
    byte* pms = arrays->preMasterSecret;
    word16 sz = 0;

    /* sz + (use_psk_key ? sz 0s : sz unaltered) + length of psk + psk */
    if (!use_psk_key) {
        sz = (word16)arrays->preMasterSz;
        c16toa(sz, pms);
        pms += OPAQUE16_LEN + sz;
    }
    if ((int)arrays->psk_keySz > 0) {
        if (use_psk_key) {
            sz = (word16)arrays->psk_keySz;
            c16toa(sz, pms);
            pms += OPAQUE16_LEN;
            XMEMSET(pms, 0, sz);
            pms += sz;
        }
        c16toa((word16)arrays->psk_keySz, pms);
        pms += OPAQUE16_LEN;
        XMEMCPY(pms, arrays->psk_key, arrays->psk_keySz);
        arrays->preMasterSz = sz + arrays->psk_keySz + OPAQUE16_LEN * 2;
        ForceZero(arrays->psk_key, arrays->psk_keySz);
    }
    arrays->psk_keySz = 0; /* no further need */
}
#endif

/* client only parts */
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS)

    int HaveUniqueSessionObj(WOLFSSL* ssl)
    {
        if (ssl->session->ref.count > 1) {
            WOLFSSL_SESSION* newSession = wolfSSL_SESSION_dup(ssl->session);
            if (newSession == NULL) {
                WOLFSSL_MSG("Session duplicate failed");
                return 0;
            }
            wolfSSL_FreeSession(ssl->ctx, ssl->session);
            ssl->session = newSession;
        }
        return 1;
    }

#ifndef WOLFSSL_NO_TLS12

    /* handle generation of client_hello (1) */
    int SendClientHello(WOLFSSL* ssl)
    {
        byte              *output;
        word32             length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
        int                sendSz;
        int                idSz;
        int                ret;
        word32             extSz = 0;
        const Suites*      suites;

        if (ssl == NULL) {
            return BAD_FUNC_ARG;
        }

#ifdef WOLFSSL_TLS13
        if (IsAtLeastTLSv1_3(ssl->version))
            return SendTls13ClientHello(ssl);
#endif

#ifdef HAVE_SECURE_RENEGOTIATION
        /* We don't want to resume in SCR */
        if (IsSCR(ssl))
            ssl->options.resuming = 0;
#endif

        idSz = ssl->options.resuming ? ssl->session->sessionIDSz : 0;


        WOLFSSL_START(WC_FUNC_CLIENT_HELLO_SEND);
        WOLFSSL_ENTER("SendClientHello");

        suites = WOLFSSL_SUITES(ssl);

        if (suites == NULL) {
            WOLFSSL_MSG("Bad suites pointer in SendClientHello");
            return SUITES_ERROR;
        }

#ifdef HAVE_SESSION_TICKET
        if (ssl->options.resuming && ssl->session->ticketLen > 0) {
            SessionTicket* ticket;

            ticket = TLSX_SessionTicket_Create(0, ssl->session->ticket,
                                             ssl->session->ticketLen, ssl->heap);
            if (ticket == NULL) return MEMORY_E;

            ret = TLSX_UseSessionTicket(&ssl->extensions, ticket, ssl->heap);
            if (ret != WOLFSSL_SUCCESS) {
                TLSX_SessionTicket_Free(ticket, ssl->heap);
                return ret;
            }

            idSz = 0;
        }
#endif /* HAVE_SESSION_TICKET */
        length = VERSION_SZ + RAN_LEN
               + (word32)idSz + ENUM_LEN
               + SUITE_LEN
               + COMP_LEN + ENUM_LEN;
#ifndef NO_FORCE_SCR_SAME_SUITE
        if (IsSCR(ssl))
            length += SUITE_LEN;
        else
#endif
            length += suites->suiteSz;

#ifdef HAVE_TLS_EXTENSIONS
        /* auto populate extensions supported unless user defined */
        if ((ret = TLSX_PopulateExtensions(ssl, 0)) != 0)
            return ret;
        extSz = 0;
        ret = TLSX_GetRequestSize(ssl, client_hello, &extSz);
        if (ret != 0)
            return ret;
        length += extSz;
#else
        if (IsAtLeastTLSv1_2(ssl) && suites->hashSigAlgoSz)
            extSz += HELLO_EXT_SZ + HELLO_EXT_SIGALGO_SZ
                   + suites->hashSigAlgoSz;
#ifdef HAVE_EXTENDED_MASTER
        if (ssl->options.haveEMS)
            extSz += HELLO_EXT_SZ;
#endif
        if (extSz != 0)
            length += extSz + HELLO_EXT_SZ_SZ;
#endif /* HAVE_TLS_EXTENSIONS */
        sendSz = (int)length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;

        if (ssl->arrays == NULL) {
            return BAD_FUNC_ARG;
        }

#ifdef WOLFSSL_DTLS
        if (ssl->options.dtls) {
            length += ENUM_LEN;   /* cookie */
            if (ssl->arrays->cookieSz != 0) length += ssl->arrays->cookieSz;
            sendSz  = (int)length + DTLS_HANDSHAKE_HEADER_SZ + DTLS_RECORD_HEADER_SZ;
            idx    += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
        }
#endif

        if (IsEncryptionOn(ssl, 1))
            sendSz += MAX_MSG_EXTRA;

        /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
         * is not advanced yet */
        ssl->options.buildingMsg = 1;

        /* check for available size */
        if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
            return ret;

        /* get output buffer */
        output = GetOutputBuffer(ssl);

        AddHeaders(output, length, client_hello, ssl);

        /* client hello, first version */
        output[idx++] = ssl->version.major;
        output[idx++] = ssl->version.minor;
        ssl->chVersion = ssl->version;  /* store in case changed */

        /* then random */
        if (ssl->options.connectState == CONNECT_BEGIN) {
            ret = wc_RNG_GenerateBlock(ssl->rng, output + idx, RAN_LEN);
            if (ret != 0)
                return ret;

            /* store random */
            XMEMCPY(ssl->arrays->clientRandom, output + idx, RAN_LEN);
        } else {
#ifdef WOLFSSL_DTLS
            /* send same random on hello again */
            XMEMCPY(output + idx, ssl->arrays->clientRandom, RAN_LEN);
#endif
        }
        idx += RAN_LEN;

        /* then session id */
        output[idx++] = (byte)idSz;
        if (idSz) {
            XMEMCPY(output + idx, ssl->session->sessionID,
                                                      ssl->session->sessionIDSz);
            idx += ssl->session->sessionIDSz;
        }

        /* then DTLS cookie */
#ifdef WOLFSSL_DTLS
        if (ssl->options.dtls) {
            byte cookieSz = ssl->arrays->cookieSz;

            output[idx++] = cookieSz;
            if (cookieSz) {
                XMEMCPY(&output[idx], ssl->arrays->cookie, cookieSz);
                idx += cookieSz;
            }
        }
#endif /* WOLFSSL_DTLS */

#ifndef NO_FORCE_SCR_SAME_SUITE
        if (IsSCR(ssl)) {
            c16toa(SUITE_LEN, output + idx);
            idx += OPAQUE16_LEN;
            output[idx++] = ssl->options.cipherSuite0;
            output[idx++] = ssl->options.cipherSuite;
        }
        else
#endif /* NO_FORCE_SCR_SAME_SUITE */
        {
            /* then cipher suites */
            c16toa(suites->suiteSz, output + idx);
            idx += OPAQUE16_LEN;
            XMEMCPY(output + idx, &suites->suites, suites->suiteSz);
            idx += suites->suiteSz;
        }

        /* last, compression */
        output[idx++] = COMP_LEN;
        if (ssl->options.usingCompression)
            output[idx++] = ZLIB_COMPRESSION;
        else
            output[idx++] = NO_COMPRESSION;

#ifdef HAVE_TLS_EXTENSIONS
        extSz = 0;
        ret = TLSX_WriteRequest(ssl, output + idx, client_hello, &extSz);
        if (ret != 0)
            return ret;
        idx += extSz;

        (void)idx; /* suppress analyzer warning, keep idx current */
#else
        if (extSz != 0) {
            c16toa((word16)(extSz), output + idx);
            idx += HELLO_EXT_SZ_SZ;

            if (IsAtLeastTLSv1_2(ssl)) {
                if (suites->hashSigAlgoSz) {
                    word16 i;
                    /* extension type */
                    c16toa(HELLO_EXT_SIG_ALGO, output + idx);
                    idx += HELLO_EXT_TYPE_SZ;
                    /* extension data length */
                    c16toa(HELLO_EXT_SIGALGO_SZ + suites->hashSigAlgoSz,
                           output + idx);
                    idx += HELLO_EXT_SZ_SZ;
                    /* sig algos length */
                    c16toa(suites->hashSigAlgoSz, output + idx);
                    idx += HELLO_EXT_SIGALGO_SZ;
                    for (i=0; i < suites->hashSigAlgoSz; i++, idx++) {
                        output[idx] = suites->hashSigAlgo[i];
                    }
                }
            }
#ifdef HAVE_EXTENDED_MASTER
            if (ssl->options.haveEMS) {
                c16toa(HELLO_EXT_EXTMS, output + idx);
                idx += HELLO_EXT_TYPE_SZ;
                c16toa(0, output + idx);
                idx += HELLO_EXT_SZ_SZ;
            }
#endif
        }
#endif /* HAVE_TLS_EXTENSIONS */

        ret = BuildMsgOrHashOutput(ssl, output, &sendSz, idx, client_hello,
                                   "ClientHello");
        if (ret != 0)
            return ret;

        ssl->options.clientState = CLIENT_HELLO_COMPLETE;
#ifdef OPENSSL_EXTRA
        ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
        if (ssl->CBIS != NULL)
            ssl->CBIS(ssl, WOLFSSL_CB_CONNECT_LOOP, WOLFSSL_SUCCESS);
#endif

        ret = SendBuffered(ssl);

        WOLFSSL_LEAVE("SendClientHello", ret);
        WOLFSSL_END(WC_FUNC_CLIENT_HELLO_SEND);

        return ret;
    }


    /* handle processing of DTLS hello_verify_request (3) */
    int DoHelloVerifyRequest(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
        word32 size)
    {
        ProtocolVersion pv;
        byte            cookieSz;
        word32          begin = *inOutIdx;

#ifdef WOLFSSL_CALLBACKS
        if (ssl->hsInfoOn) AddPacketName(ssl, "HelloVerifyRequest");
        if (ssl->toInfoOn) AddLateName("HelloVerifyRequest", &ssl->timeoutInfo);
#endif

#ifdef WOLFSSL_DTLS
        if (ssl->options.dtls)
            DtlsMsgPoolReset(ssl);
#endif

        if (OPAQUE16_LEN + OPAQUE8_LEN > size)
            return BUFFER_ERROR;

        XMEMCPY(&pv, input + *inOutIdx, OPAQUE16_LEN);
        *inOutIdx += OPAQUE16_LEN;

        if (pv.major != DTLS_MAJOR ||
                         (pv.minor != DTLS_MINOR && pv.minor != DTLSv1_2_MINOR))
            return VERSION_ERROR;

        cookieSz = input[(*inOutIdx)++];

        if (cookieSz) {
            if ((*inOutIdx - begin) + cookieSz > size)
                return BUFFER_ERROR;

#ifdef WOLFSSL_DTLS
            if (cookieSz <= MAX_COOKIE_LEN) {
                XMEMCPY(ssl->arrays->cookie, input + *inOutIdx, cookieSz);
                ssl->arrays->cookieSz = cookieSz;
            }
#endif
            *inOutIdx += cookieSz;
        }

#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_TLS13)
        if (IsAtLeastTLSv1_3(ssl->version) && ssl->options.dtls) {
            /* we sent a TLSv1.3 ClientHello but received a
             * HELLO_VERIFY_REQUEST. We only check if DTLSv1_3_MINOR is the
             * min downgrade option as per the server_version field comments in
             * https://www.rfc-editor.org/rfc/rfc6347#section-4.2.1 */
            if (!ssl->options.downgrade ||
                    ssl->options.minDowngrade <= DTLSv1_3_MINOR)
                return VERSION_ERROR;

            /* Cannot be DTLS1.3 as HELLO_VERIFY_REQUEST */
            ssl->options.tls1_3 = 0;
        }
#endif /* defined(WOLFSSL_DTLS13) && defined(WOLFSSL_TLS13) */

        ssl->options.serverState = SERVER_HELLOVERIFYREQUEST_COMPLETE;

        return 0;
    }


    static WC_INLINE int DSH_CheckSessionId(WOLFSSL* ssl)
    {
        int ret = 0;

#ifdef HAVE_SECRET_CALLBACK
        /* If a session secret callback exists, we are using that
         * key instead of the saved session key. Requires a ticket. */
        ret = ret || (ssl->sessionSecretCb != NULL
#ifdef HAVE_SESSION_TICKET
                && ssl->session->ticketLen > 0
#endif
                );
#endif /* HAVE_SECRET_CALLBACK */

#ifdef HAVE_SESSION_TICKET
        /* server may send blank ticket which may not be expected to indicate
         * existing one ok but will also be sending a new one */
        ret = ret || (ssl->session->ticketLen > 0);
#endif

        ret = ret ||
              (ssl->options.haveSessionId && ssl->arrays->sessionIDSz == ID_LEN
                      && ssl->session->sessionIDSz == ID_LEN
                      && XMEMCMP(ssl->arrays->sessionID,
                              ssl->session->sessionID, ID_LEN) == 0);

        return ret;
    }

    /* Check the version in the received message is valid and set protocol
     * version to use.
     *
     * ssl  The SSL/TLS object.
     * pv   The protocol version from the packet.
     * returns 0 on success, otherwise failure.
     */
    int CheckVersion(WOLFSSL *ssl, ProtocolVersion pv)
    {
        byte lowerVersion, higherVersion;
    #ifdef WOLFSSL_TLS13_DRAFT
        if (pv.major == TLS_DRAFT_MAJOR) {
            pv.major = SSLv3_MAJOR;
            pv.minor = TLSv1_3_MINOR;
        }
    #endif

    #ifdef OPENSSL_EXTRA
        if (ssl->CBIS != NULL) {
            ssl->CBIS(ssl, WOLFSSL_CB_HANDSHAKE_START, WOLFSSL_SUCCESS);
        }
    #endif

        if (ssl->options.dtls) {
            if (pv.major != DTLS_MAJOR || pv.minor == DTLS_BOGUS_MINOR) {
                WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                return VERSION_ERROR;
            }
            lowerVersion = pv.minor > ssl->version.minor;
            higherVersion = pv.minor < ssl->version.minor;
        }
        else {
            if (pv.major != SSLv3_MAJOR) {
                WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                return VERSION_ERROR;
            }
            lowerVersion = pv.minor < ssl->version.minor;
            higherVersion = pv.minor > ssl->version.minor;
        }

        if (higherVersion) {
            WOLFSSL_MSG("Server using higher version, fatal error");
            WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
            return VERSION_ERROR;
        }
        if (lowerVersion) {
            WOLFSSL_MSG("server using lower version");

            /* Check for downgrade attack. */
            if (!ssl->options.downgrade) {
                WOLFSSL_MSG("\tno downgrade allowed, fatal error");
                WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                return VERSION_ERROR;
            }
            if ((!ssl->options.dtls && pv.minor < ssl->options.minDowngrade) ||
                (ssl->options.dtls && pv.minor > ssl->options.minDowngrade)) {
                WOLFSSL_MSG("\tversion below minimum allowed, fatal error");
                WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                return VERSION_ERROR;
            }

            #ifdef HAVE_SECURE_RENEGOTIATION
                if (ssl->secure_renegotiation &&
                                         ssl->secure_renegotiation->enabled &&
                                         ssl->options.handShakeDone) {
                    WOLFSSL_MSG("Server changed version during scr");
                    WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                    return VERSION_ERROR;
                }
            #endif /* HAVE_SECURE_RENEGOTIATION */

            /* Checks made - OK to downgrade. */
                ssl->version.minor = pv.minor;
                switch(pv.minor) {
                case SSLv3_MINOR:
                    /* turn off tls */
                    WOLFSSL_MSG("\tdowngrading to SSLv3");
                    ssl->options.tls    = 0;
                    ssl->options.tls1_1 = 0;
                    break;
                case TLSv1_MINOR:
                    /* turn off tls 1.1+ */
                    WOLFSSL_MSG("\tdowngrading to TLSv1");
                    ssl->options.tls1_1 = 0;
                    break;
                case TLSv1_1_MINOR:
                    WOLFSSL_MSG("\tdowngrading to TLSv1.1");
                    break;
                case DTLS_MINOR:
                    WOLFSSL_MSG("\tdowngrading to DTLSv1.1");
                    break;
                case TLSv1_2_MINOR:
                    WOLFSSL_MSG("\tdowngrading to TLSv1.2");
                    break;
                case DTLSv1_2_MINOR:
                    WOLFSSL_MSG("\tdowngrading to DTLSv1.2");
                    break;
                default:
                    WOLFSSL_MSG("\tbad minor version");
                    WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                    return VERSION_ERROR;
                }
        }

        /* check if option is set to not allow the current version
         * set from either wolfSSL_set_options or wolfSSL_CTX_set_options */
        if (!ssl->options.dtls && ssl->options.downgrade &&
            ssl->options.mask > 0) {

            if (ssl->version.minor == TLSv1_2_MINOR &&
               (ssl->options.mask & WOLFSSL_OP_NO_TLSv1_2) ==
                WOLFSSL_OP_NO_TLSv1_2) {
                WOLFSSL_MSG("\tOption set to not allow TLSv1.2, Downgrading");
                ssl->version.minor = TLSv1_1_MINOR;
            }

            if (ssl->version.minor == TLSv1_1_MINOR &&
               (ssl->options.mask & WOLFSSL_OP_NO_TLSv1_1) ==
                WOLFSSL_OP_NO_TLSv1_1) {
                WOLFSSL_MSG("\tOption set to not allow TLSv1.1, Downgrading");
                ssl->options.tls1_1 = 0;
                ssl->version.minor = TLSv1_MINOR;
            }

            if (ssl->version.minor == TLSv1_MINOR &&
                (ssl->options.mask & WOLFSSL_OP_NO_TLSv1) ==
                WOLFSSL_OP_NO_TLSv1) {
                WOLFSSL_MSG("\tOption set to not allow TLSv1, Downgrading");
                ssl->options.tls    = 0;
                ssl->options.tls1_1 = 0;
                ssl->version.minor = SSLv3_MINOR;
            }

            if (ssl->version.minor == SSLv3_MINOR &&
                (ssl->options.mask & WOLFSSL_OP_NO_SSLv3) ==
                WOLFSSL_OP_NO_SSLv3) {
                WOLFSSL_MSG("\tError, option set to not allow SSLv3");
                WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                return VERSION_ERROR;
            }

            if (ssl->version.minor < ssl->options.minDowngrade) {
                WOLFSSL_MSG("\tversion below minimum allowed, fatal error");
                WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                return VERSION_ERROR;
            }
        }

        return 0;
    }

    /* handle processing of server_hello (2) */
    int DoServerHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
                      word32 helloSz)
    {
        byte            cs0;   /* cipher suite bytes 0, 1 */
        byte            cs1;
        ProtocolVersion pv;
        byte            compression;
        word32          i = *inOutIdx;
        word32          begin = i;
        int             ret;

        WOLFSSL_START(WC_FUNC_SERVER_HELLO_DO);
        WOLFSSL_ENTER("DoServerHello");

#ifdef WOLFSSL_CALLBACKS
        if (ssl->hsInfoOn) AddPacketName(ssl, "ServerHello");
        if (ssl->toInfoOn) AddLateName("ServerHello", &ssl->timeoutInfo);
#endif

        /* protocol version, random and session id length check */
        if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz)
            return BUFFER_ERROR;

        /* protocol version */
        XMEMCPY(&pv, input + i, OPAQUE16_LEN);
        i += OPAQUE16_LEN;

        ret = CheckVersion(ssl, pv);
        if (ret != 0) {
            SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version);
            return ret;
        }

#ifdef WOLFSSL_TLS13
        if (IsAtLeastTLSv1_3(pv)) {
            byte type = server_hello;
            return DoTls13ServerHello(ssl, input, inOutIdx, helloSz, &type);
        }
#endif

        /* random */
        XMEMCPY(ssl->arrays->serverRandom, input + i, RAN_LEN);
        i += RAN_LEN;

        /* session id */
        ssl->arrays->sessionIDSz = input[i++];

        if (ssl->arrays->sessionIDSz > ID_LEN) {
            WOLFSSL_MSG("Invalid session ID size");
            ssl->arrays->sessionIDSz = 0;
            return BUFFER_ERROR;
        }
        else if (ssl->arrays->sessionIDSz) {
            if ((i - begin) + ssl->arrays->sessionIDSz > helloSz)
                return BUFFER_ERROR;

            XMEMCPY(ssl->arrays->sessionID, input + i,
                                                      ssl->arrays->sessionIDSz);
            i += ssl->arrays->sessionIDSz;
            ssl->options.haveSessionId = 1;
        }


        /* suite and compression */
        if ((i - begin) + OPAQUE16_LEN + OPAQUE8_LEN > helloSz)
            return BUFFER_ERROR;

        cs0 = input[i++];
        cs1 = input[i++];

#ifndef WOLFSSL_NO_STRICT_CIPHER_SUITE
#if defined(HAVE_SECURE_RENEGOTIATION) && !defined(NO_FORCE_SCR_SAME_SUITE)
        if (IsSCR(ssl)) {
            if (ssl->options.cipherSuite0 != cs0 ||
                ssl->options.cipherSuite  != cs1) {
                WOLFSSL_MSG("Server changed cipher suite during scr");
                WOLFSSL_ERROR_VERBOSE(MATCH_SUITE_ERROR);
                return MATCH_SUITE_ERROR;
            }
        }
        else
#endif /* HAVE_SECURE_RENEGOTIATION && !NO_FORCE_SCR_SAME_SUITE */
        {
            word32 idx, found = 0;
            const Suites* suites = WOLFSSL_SUITES(ssl);
            /* confirm server_hello cipher suite is one sent in client_hello */
            for (idx = 0; idx < suites->suiteSz; idx += 2) {
                if (suites->suites[idx]   == cs0 &&
                    suites->suites[idx+1] == cs1) {
                    found = 1;
                    break;
                }
            }
            if (!found) {
                WOLFSSL_MSG("ServerHello did not use cipher suite from ClientHello");
                WOLFSSL_ERROR_VERBOSE(MATCH_SUITE_ERROR);
                return MATCH_SUITE_ERROR;
            }
        }
#endif /* !WOLFSSL_NO_STRICT_CIPHER_SUITE */

        ssl->options.cipherSuite0 = cs0;
        ssl->options.cipherSuite  = cs1;
    #ifdef WOLFSSL_DEBUG_TLS
        WOLFSSL_MSG("Chosen cipher suite:");
        WOLFSSL_MSG(GetCipherNameInternal(ssl->options.cipherSuite0,
                                          ssl->options.cipherSuite));
    #endif

        compression = input[i++];

        if (compression != NO_COMPRESSION && !ssl->options.usingCompression) {
            WOLFSSL_MSG("Server forcing compression w/o support");
            WOLFSSL_ERROR_VERBOSE(COMPRESSION_ERROR);
            return COMPRESSION_ERROR;
        }

        if (compression != ZLIB_COMPRESSION && ssl->options.usingCompression) {
            WOLFSSL_MSG("Server refused compression, turning off");
            ssl->options.usingCompression = 0;  /* turn off if server refused */
        }

        *inOutIdx = i;

#ifdef HAVE_TLS_EXTENSIONS
        if ( (i - begin) < helloSz) {
            if (TLSX_SupportExtensions(ssl)) {
                word16 totalExtSz;

                if ((i - begin) + OPAQUE16_LEN > helloSz)
                    return BUFFER_ERROR;

                ato16(&input[i], &totalExtSz);
                i += OPAQUE16_LEN;

                if ((i - begin) + totalExtSz > helloSz)
                    return BUFFER_ERROR;

                if ((ret = TLSX_Parse(ssl, (byte *) input + i, totalExtSz,
                                                           server_hello, NULL)))
                    return ret;

                i += totalExtSz;
                *inOutIdx = i;
            }
            else
                *inOutIdx = begin + helloSz; /* skip extensions */
        }
        else
            ssl->options.haveEMS = 0; /* If no extensions, no EMS */
#else
        /* !HAVE_TLS_EXTENSIONS */
        {
            byte pendingEMS = 0;

            if ( (i - begin) < helloSz) {
                int allowExt = 0;

                if (ssl->version.major == SSLv3_MAJOR &&
                    ssl->version.minor >= TLSv1_MINOR) {

                    allowExt = 1;
                }
#ifdef WOLFSSL_DTLS
                if (ssl->version.major == DTLS_MAJOR)
                    allowExt = 1;
#endif

                if (allowExt) {
                    word16 totalExtSz;

                    if ((i - begin) + OPAQUE16_LEN > helloSz)
                        return BUFFER_ERROR;

                    ato16(&input[i], &totalExtSz);
                    i += OPAQUE16_LEN;

                    if ((i - begin) + totalExtSz > helloSz)
                        return BUFFER_ERROR;

                    while (totalExtSz) {
                        word16 extId, extSz;

                        if (OPAQUE16_LEN + OPAQUE16_LEN > totalExtSz)
                            return BUFFER_ERROR;

                        ato16(&input[i], &extId);
                        i += OPAQUE16_LEN;
                        ato16(&input[i], &extSz);
                        i += OPAQUE16_LEN;

                        if (OPAQUE16_LEN + OPAQUE16_LEN + extSz > totalExtSz)
                            return BUFFER_ERROR;

                        if (extId == HELLO_EXT_EXTMS)
                            pendingEMS = 1;
                        else
                            i += extSz;

                        totalExtSz -= (word16)(OPAQUE16_LEN) +
                                    (word16)(OPAQUE16_LEN) +
                                    extSz;
                    }

                    *inOutIdx = i;
                }
                else
                    *inOutIdx = begin + helloSz; /* skip extensions */
            }

            if (!pendingEMS && ssl->options.haveEMS)
                ssl->options.haveEMS = 0;
        }
#endif /* HAVE_TLS_EXTENSIONS */

#if defined(WOLFSSL_HARDEN_TLS) && !defined(WOLFSSL_HARDEN_TLS_NO_SCR_CHECK)
        if (ssl->scr_check_enabled && (ssl->secure_renegotiation == NULL ||
                !ssl->secure_renegotiation->enabled)) {
            /* If the server does not acknowledge the extension, the client
             * MUST generate a fatal handshake_failure alert prior to
             * terminating the connection.
             * https://www.rfc-editor.org/rfc/rfc9325#name-renegotiation-in-tls-12 */
            WOLFSSL_MSG("ServerHello did not contain SCR extension");
            return SECURE_RENEGOTIATION_E;
        }
#endif

        ssl->options.serverState = SERVER_HELLO_COMPLETE;

        if (IsEncryptionOn(ssl, 0))
            *inOutIdx += ssl->keys.padSz;

#ifdef HAVE_SECRET_CALLBACK
        if (ssl->sessionSecretCb != NULL
#ifdef HAVE_SESSION_TICKET
                && ssl->session->ticketLen > 0
#endif
                ) {
            int secretSz = SECRET_LEN;
            ret = ssl->sessionSecretCb(ssl, ssl->session->masterSecret,
                                              &secretSz, ssl->sessionSecretCtx);
            if (ret != 0 || secretSz != SECRET_LEN) {
                WOLFSSL_ERROR_VERBOSE(SESSION_SECRET_CB_E);
                return SESSION_SECRET_CB_E;
            }
        }
#endif /* HAVE_SECRET_CALLBACK */

        ret = CompleteServerHello(ssl);

        WOLFSSL_LEAVE("DoServerHello", ret);
        WOLFSSL_END(WC_FUNC_SERVER_HELLO_DO);

        return ret;
    }

    int CompleteServerHello(WOLFSSL* ssl)
    {
        int ret;

        if (!ssl->options.resuming) {
            byte* down = ssl->arrays->serverRandom + RAN_LEN -
                                                         TLS13_DOWNGRADE_SZ - 1;
            byte  vers = ssl->arrays->serverRandom[RAN_LEN - 1];
    #ifdef WOLFSSL_TLS13
            if (TLSv1_3_Capable(ssl)) {
                /* TLS v1.3 capable client not allowed to downgrade when
                 * connecting to TLS v1.3 capable server unless cipher suite
                 * demands it.
                 */
                if (XMEMCMP(down, tls13Downgrade, TLS13_DOWNGRADE_SZ) == 0 &&
                                                     (vers == 0 || vers == 1)) {
                    SendAlert(ssl, alert_fatal, illegal_parameter);
                    WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                    return VERSION_ERROR;
                }
            }
            else
    #endif /* WOLFSSL_TLS13 */
            if (ssl->ctx->method->version.major == SSLv3_MAJOR &&
                ssl->ctx->method->version.minor == TLSv1_2_MINOR &&
                (wolfSSL_get_options(ssl) & WOLFSSL_OP_NO_TLSv1_2) == 0) {
                /* TLS v1.2 capable client not allowed to downgrade when
                 * connecting to TLS v1.2 capable server.
                 */
                if (XMEMCMP(down, tls13Downgrade, TLS13_DOWNGRADE_SZ) == 0 &&
                                                                    vers == 0) {
                    SendAlert(ssl, alert_fatal, illegal_parameter);
                    WOLFSSL_ERROR_VERBOSE(VERSION_ERROR);
                    return VERSION_ERROR;
                }
            }
        }
        else {
            if (DSH_CheckSessionId(ssl)) {
                if (SetCipherSpecs(ssl) == 0) {
                    if (!HaveUniqueSessionObj(ssl)) {
                        WOLFSSL_MSG("Unable to have unique session object");
                        WOLFSSL_ERROR_VERBOSE(MEMORY_ERROR);
                        return MEMORY_ERROR;
                    }

                    XMEMCPY(ssl->arrays->masterSecret,
                            ssl->session->masterSecret, SECRET_LEN);
            #ifdef NO_OLD_TLS
                    ret = DeriveTlsKeys(ssl);
            #else
                    ret = -1; /* default value */
                #ifndef NO_TLS
                    if (ssl->options.tls)
                        ret = DeriveTlsKeys(ssl);
                #endif
                    if (!ssl->options.tls)
                        ret = DeriveKeys(ssl);
            #endif /* NO_OLD_TLS */
                    /* SERVER: peer auth based on session secret. */
                    ssl->options.peerAuthGood = (ret == 0);
                    ssl->options.serverState = SERVER_HELLODONE_COMPLETE;

                    return ret;
                }
                else {
                    WOLFSSL_MSG("Unsupported cipher suite, DoServerHello");
                    WOLFSSL_ERROR_VERBOSE(UNSUPPORTED_SUITE);
                    return UNSUPPORTED_SUITE;
                }
            }
            else {
                WOLFSSL_MSG("Server denied resumption attempt");
                ssl->options.resuming = 0; /* server denied resumption try */
            }
        }
        return SetCipherSpecs(ssl);
    }

#endif /* !WOLFSSL_NO_TLS12 */


    /* Make sure client setup is valid for this suite, true on success */
    int VerifyClientSuite(word16 havePSK, byte cipherSuite0, byte cipherSuite)
    {
        (void)havePSK;

        WOLFSSL_ENTER("VerifyClientSuite");

        if (CipherRequires(cipherSuite0, cipherSuite, REQUIRES_PSK)) {
            WOLFSSL_MSG("Requires PSK");
        #ifndef NO_PSK
            if (havePSK == 0)
        #endif
            {
                WOLFSSL_MSG("Don't have PSK");
                return 0;
            }
        }

        return 1;  /* success */
    }

#ifndef WOLFSSL_NO_TLS12

#ifndef NO_CERTS
    /* handle processing of certificate_request (13) */
    static int DoCertificateRequest(WOLFSSL* ssl, const byte* input, word32*
                                    inOutIdx, word32 size)
    {
        word16 len;
        word32 begin = *inOutIdx;
    #if defined(OPENSSL_EXTRA) || defined(OPENSSL_ALL) || \
        defined(WOLFSSL_NGINX) || defined(HAVE_LIGHTY) || \
        defined(WOLFSSL_CERT_SETUP_CB)
        int ret;
    #endif
    #ifdef OPENSSL_EXTRA
        WOLFSSL_X509* x509 = NULL;
        WOLFSSL_EVP_PKEY* pkey = NULL;
    #endif

        WOLFSSL_START(WC_FUNC_CERTIFICATE_REQUEST_DO);
        WOLFSSL_ENTER("DoCertificateRequest");

        #ifdef WOLFSSL_CALLBACKS
            if (ssl->hsInfoOn)
                AddPacketName(ssl, "CertificateRequest");
            if (ssl->toInfoOn)
                AddLateName("CertificateRequest", &ssl->timeoutInfo);
        #endif

        if (OPAQUE8_LEN > size)
            return BUFFER_ERROR;

        len = input[(*inOutIdx)++];

        if ((*inOutIdx - begin) + len > size)
            return BUFFER_ERROR;

        /* types, read in here */
        *inOutIdx += len;

        /* signature and hash signature algorithm */
        if (IsAtLeastTLSv1_2(ssl)) {
            if ((*inOutIdx - begin) + OPAQUE16_LEN > size)
                return BUFFER_ERROR;

            ato16(input + *inOutIdx, &len);
            *inOutIdx += OPAQUE16_LEN;

            if ((len > size) || ((*inOutIdx - begin) + len > size))
                return BUFFER_ERROR;

            if (PickHashSigAlgo(ssl, input + *inOutIdx, len, 0) != 0 &&
                                             ssl->buffers.certificate &&
                                             ssl->buffers.certificate->buffer) {
            #ifdef HAVE_PK_CALLBACKS
                if (wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)) {
                    WOLFSSL_MSG("Using PK for client private key");
                    WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
                    return INVALID_PARAMETER;
                }
            #endif
                if (ssl->buffers.key && ssl->buffers.key->buffer) {
                    WOLFSSL_ERROR_VERBOSE(INVALID_PARAMETER);
                    return INVALID_PARAMETER;
                }
            }
            *inOutIdx += len;
    #ifdef WC_RSA_PSS
            ssl->pssAlgo = 0;
            if (ssl->options.sigAlgo == rsa_pss_sa_algo)
                ssl->pssAlgo |= 1 << ssl->options.hashAlgo;
    #endif
        }

        /* authorities */
        if ((*inOutIdx - begin) + OPAQUE16_LEN > size)
            return BUFFER_ERROR;

        /* DN seq length */
        ato16(input + *inOutIdx, &len);
        *inOutIdx += OPAQUE16_LEN;

        if ((*inOutIdx - begin) + len > size)
            return BUFFER_ERROR;

    #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(HAVE_LIGHTY)
        wolfSSL_sk_X509_NAME_pop_free(ssl->peer_ca_names, NULL);
        ssl->peer_ca_names = wolfSSL_sk_X509_NAME_new(NULL);
        if (ssl->peer_ca_names == NULL) {
            return MEMORY_ERROR;
        }
    #endif

        while (len) {
            word16 dnSz;

            if ((*inOutIdx - begin) + OPAQUE16_LEN > size)
                return BUFFER_ERROR;

            ato16(input + *inOutIdx, &dnSz);
            *inOutIdx += OPAQUE16_LEN;

            if ((*inOutIdx - begin) + dnSz > size)
                return BUFFER_ERROR;

        #if defined(OPENSSL_ALL) || defined(WOLFSSL_NGINX) || defined(HAVE_LIGHTY)
            {
                WOLFSSL_X509_NAME* name = NULL;
                /* Use a DecodedCert struct to get access to GetName to
                 * parse DN name */
#ifdef WOLFSSL_SMALL_STACK
                DecodedCert *cert = (DecodedCert *)XMALLOC(
                    sizeof(*cert), ssl->heap, DYNAMIC_TYPE_DCERT);
                if (cert == NULL)
                    return MEMORY_ERROR;
#else
                DecodedCert cert[1];
#endif

                InitDecodedCert(cert, input + *inOutIdx, dnSz, ssl->heap);

                ret = GetName(cert, ASN_SUBJECT, dnSz);

                if (ret == 0) {
                    if ((name = wolfSSL_X509_NAME_new_ex(cert->heap)) == NULL)
                        ret = MEMORY_ERROR;
                }

                if (ret == 0) {
                    CopyDecodedName(name, cert, ASN_SUBJECT);
                }

                if (ret == 0) {
                    if (wolfSSL_sk_X509_NAME_push(ssl->peer_ca_names, name)
                        <= 0)
                    {
                        ret = MEMORY_ERROR;
                    }
                }

                FreeDecodedCert(cert);

                WC_FREE_VAR_EX(cert, ssl->heap, DYNAMIC_TYPE_DCERT);
                if (ret != 0) {
                    if (name != NULL)
                        wolfSSL_X509_NAME_free(name);
                    return ret;
                }
            }
        #endif

            *inOutIdx += dnSz;
            len -= (word16)(OPAQUE16_LEN) + dnSz;
        }

    #ifdef WOLFSSL_CERT_SETUP_CB
    #ifdef OPENSSL_EXTRA
        /* call client cert callback if no cert has been loaded */
        if ((ssl->ctx->CBClientCert != NULL) &&
            (!ssl->buffers.certificate || !ssl->buffers.certificate->buffer)) {

            ret = ssl->ctx->CBClientCert(ssl, &x509, &pkey);
            if (ret == 1) {
                if ((wolfSSL_use_certificate(ssl, x509) != WOLFSSL_SUCCESS) ||
                    (wolfSSL_use_PrivateKey(ssl, pkey) != WOLFSSL_SUCCESS)) {
                    WOLFSSL_ERROR_VERBOSE(CLIENT_CERT_CB_ERROR);
                    return CLIENT_CERT_CB_ERROR;
                }
                wolfSSL_X509_free(x509);
                x509 = NULL;
                wolfSSL_EVP_PKEY_free(pkey);

            }
            else if (ret < 0) {
                return WOLFSSL_ERROR_WANT_X509_LOOKUP;
            }
        }
    #endif
        if ((ret = CertSetupCbWrapper(ssl)) != 0)
            return ret;
    #endif

        /* don't send client cert or cert verify if user hasn't provided
           cert and private key */
        if (ssl->buffers.certificate && ssl->buffers.certificate->buffer) {
        #ifdef HAVE_PK_CALLBACKS
            if (wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)) {
                WOLFSSL_MSG("Using PK for client private key");
                ssl->options.sendVerify = SEND_CERT;
            }
        #endif
            if (ssl->buffers.key && ssl->buffers.key->buffer) {
                ssl->options.sendVerify = SEND_CERT;
            }
        }
    #ifdef OPENSSL_EXTRA
        else
    #else
        else if (IsTLS(ssl) || ssl->options.dtls)
    #endif
        {
            ssl->options.sendVerify = SEND_BLANK_CERT;
        }

        if (IsEncryptionOn(ssl, 0))
            *inOutIdx += ssl->keys.padSz;

        WOLFSSL_LEAVE("DoCertificateRequest", 0);
        WOLFSSL_END(WC_FUNC_CERTIFICATE_REQUEST_DO);

        return 0;
    }
#endif /* !NO_CERTS */


#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)

    static int CheckCurveId(int tlsCurveId)
    {
        int ret = WC_NO_ERR_TRACE(ECC_CURVE_ERROR);

        switch (tlsCurveId) {
    #if (defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 160
        #ifndef NO_ECC_SECP
            case WOLFSSL_ECC_SECP160R1: return ECC_SECP160R1_OID;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_SECPR2
            case WOLFSSL_ECC_SECP160R2: return ECC_SECP160R2_OID;
        #endif /* HAVE_ECC_SECPR2 */
        #ifdef HAVE_ECC_KOBLITZ
            case WOLFSSL_ECC_SECP160K1: return ECC_SECP160K1_OID;
        #endif /* HAVE_ECC_KOBLITZ */
    #endif
    #if (defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 192
        #ifndef NO_ECC_SECP
            case WOLFSSL_ECC_SECP192R1: return ECC_SECP192R1_OID;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_KOBLITZ
            case WOLFSSL_ECC_SECP192K1: return ECC_SECP192K1_OID;
        #endif /* HAVE_ECC_KOBLITZ */
    #endif
    #if (defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 224
        #ifndef NO_ECC_SECP
            case WOLFSSL_ECC_SECP224R1: return ECC_SECP224R1_OID;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_KOBLITZ
            case WOLFSSL_ECC_SECP224K1: return ECC_SECP224K1_OID;
        #endif /* HAVE_ECC_KOBLITZ */
    #endif
        #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256
            case WOLFSSL_ECC_X25519: return ECC_X25519_OID;
        #endif
    #if (!defined(NO_ECC256)  || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256
        #ifndef NO_ECC_SECP
            case WOLFSSL_ECC_SECP256R1: return ECC_SECP256R1_OID;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_KOBLITZ
            case WOLFSSL_ECC_SECP256K1: return ECC_SECP256K1_OID;
        #endif /* HAVE_ECC_KOBLITZ */
        #ifdef HAVE_ECC_BRAINPOOL
            case WOLFSSL_ECC_BRAINPOOLP256R1: return ECC_BRAINPOOLP256R1_OID;
        #endif /* HAVE_ECC_BRAINPOOL */
        #ifdef WOLFSSL_SM2
            case WOLFSSL_ECC_SM2P256V1: return ECC_SM2P256V1_OID;
        #endif /* WOLFSSL_SM2 */
    #endif
        #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448
            case WOLFSSL_ECC_X448: return ECC_X448_OID;
        #endif
    #if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384
        #ifndef NO_ECC_SECP
            case WOLFSSL_ECC_SECP384R1: return ECC_SECP384R1_OID;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_BRAINPOOL
            case WOLFSSL_ECC_BRAINPOOLP384R1: return ECC_BRAINPOOLP384R1_OID;
        #endif /* HAVE_ECC_BRAINPOOL */
    #endif
    #if (defined(HAVE_ECC512) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 512
        #ifdef HAVE_ECC_BRAINPOOL
            case WOLFSSL_ECC_BRAINPOOLP512R1: return ECC_BRAINPOOLP512R1_OID;
        #endif /* HAVE_ECC_BRAINPOOL */
    #endif
    #if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521
        #ifndef NO_ECC_SECP
            case WOLFSSL_ECC_SECP521R1: return ECC_SECP521R1_OID;
        #endif /* !NO_ECC_SECP */
    #endif
            default:
                ret = WC_NO_ERR_TRACE(ECC_CURVE_ERROR);
                break;
        }

        return ret;
    }

#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

/* Persistable DoServerKeyExchange arguments */
typedef struct DskeArgs {
    byte*  output; /* not allocated */
#if !defined(NO_DH) || defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
    byte*  verifySig;
#endif
    word32 idx;
    word32 begin;
#if !defined(NO_DH) || defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
    word16 verifySigSz;
#endif
    word16 sigSz;
#if !defined(NO_RSA) && defined(WC_RSA_PSS)
    int    bits;
#endif
} DskeArgs;

static void FreeDskeArgs(WOLFSSL* ssl, void* pArgs)
{
    DskeArgs* args = (DskeArgs*)pArgs;

    (void)ssl;
    (void)args;

#if !defined(NO_DH) || defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
    XFREE(args->verifySig, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
    args->verifySig = NULL;
#endif
}

#ifndef NO_DH
static int GetDhPublicKey(WOLFSSL* ssl, const byte* input, word32 size,
                          DskeArgs* args)
{
    int             ret = 0;
    word16          length;
#ifdef HAVE_FFDHE
#ifdef HAVE_PUBLIC_FFDHE
    const DhParams* params = NULL;
#endif
    word16          group = 0;
#endif

    if (ssl->buffers.weOwnDH) {
        if (ssl->buffers.serverDH_P.buffer) {
            XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                    DYNAMIC_TYPE_PUBLIC_KEY);
            ssl->buffers.serverDH_P.buffer = NULL;
        }

        if (ssl->buffers.serverDH_G.buffer) {
            XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap,
                    DYNAMIC_TYPE_PUBLIC_KEY);
            ssl->buffers.serverDH_G.buffer = NULL;
        }

    }

    if (ssl->buffers.serverDH_Pub.buffer) {
        XFREE(ssl->buffers.serverDH_Pub.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_Pub.buffer = NULL;
    }

    /* p */
    if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
        ERROR_OUT(BUFFER_ERROR, exit_gdpk);
    }

    ato16(input + args->idx, &length);
    args->idx += OPAQUE16_LEN;

    if ((args->idx - args->begin) + length > size) {
        ERROR_OUT(BUFFER_ERROR, exit_gdpk);
    }

    if (length < ssl->options.minDhKeySz) {
        WOLFSSL_MSG("Server using a DH key that is too small");
        SendAlert(ssl, alert_fatal, handshake_failure);
        ERROR_OUT(DH_KEY_SIZE_E, exit_gdpk);
    }
    if (length > ssl->options.maxDhKeySz) {
        WOLFSSL_MSG("Server using a DH key that is too big");
        SendAlert(ssl, alert_fatal, handshake_failure);
        ERROR_OUT(DH_KEY_SIZE_E, exit_gdpk);
    }

    ssl->buffers.serverDH_P.buffer =
        (byte*)XMALLOC(length, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    if (ssl->buffers.serverDH_P.buffer) {
        ssl->buffers.serverDH_P.length = length;
    }
    else {
        ERROR_OUT(MEMORY_ERROR, exit_gdpk);
    }

    XMEMCPY(ssl->buffers.serverDH_P.buffer, input + args->idx,
                                                        length);
    args->idx += length;

    ssl->options.dhKeySz = length;

    /* g */
    if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
        ERROR_OUT(BUFFER_ERROR, exit_gdpk);
    }

    ato16(input + args->idx, &length);
    args->idx += OPAQUE16_LEN;

    if ((args->idx - args->begin) + length > size) {
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
        ERROR_OUT(BUFFER_ERROR, exit_gdpk);
    }

    if (length > ssl->options.maxDhKeySz) {
        WOLFSSL_MSG("Server using a DH key generator that is too big");
        SendAlert(ssl, alert_fatal, handshake_failure);
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
        ERROR_OUT(DH_KEY_SIZE_E, exit_gdpk);
    }

    ssl->buffers.serverDH_G.buffer =
        (byte*)XMALLOC(length, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    if (ssl->buffers.serverDH_G.buffer) {
        ssl->buffers.serverDH_G.length = length;
    }
    else {
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
        ERROR_OUT(MEMORY_ERROR, exit_gdpk);
    }

    XMEMCPY(ssl->buffers.serverDH_G.buffer, input + args->idx,
                                                        length);
    args->idx += length;

    /* pub */
    if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
        XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_G.buffer = NULL;
        ERROR_OUT(BUFFER_ERROR, exit_gdpk);
    }

    ato16(input + args->idx, &length);
    args->idx += OPAQUE16_LEN;

    if ((args->idx - args->begin) + length > size) {
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
        XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_G.buffer = NULL;
        ERROR_OUT(BUFFER_ERROR, exit_gdpk);
    }

    if (length > ssl->options.maxDhKeySz) {
        WOLFSSL_MSG("Server using a public DH key that is too big");
        SendAlert(ssl, alert_fatal, handshake_failure);
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
        XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_G.buffer = NULL;
        ERROR_OUT(DH_KEY_SIZE_E, exit_gdpk);
    }

    ssl->buffers.serverDH_Pub.buffer =
        (byte*)XMALLOC(length, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
    if (ssl->buffers.serverDH_Pub.buffer) {
        ssl->buffers.serverDH_Pub.length = length;
    }
    else {
        XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_P.buffer = NULL;
        XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap,
                DYNAMIC_TYPE_PUBLIC_KEY);
        ssl->buffers.serverDH_G.buffer = NULL;
        ERROR_OUT(MEMORY_ERROR, exit_gdpk);
    }

    XMEMCPY(ssl->buffers.serverDH_Pub.buffer, input + args->idx,
                                                        length);
    ssl->buffers.weOwnDH = 1;
    args->idx += length;

#ifdef HAVE_FFDHE
    switch (ssl->options.dhKeySz) {
    #ifdef HAVE_FFDHE_2048
        case 2048/8:
            #ifdef HAVE_PUBLIC_FFDHE
            params = wc_Dh_ffdhe2048_Get();
            #endif
            group = WOLFSSL_FFDHE_2048;
            break;
    #endif
    #ifdef HAVE_FFDHE_3072
        case 3072/8:
            #ifdef HAVE_PUBLIC_FFDHE
            params = wc_Dh_ffdhe3072_Get();
            #endif
            group = WOLFSSL_FFDHE_3072;
            break;
    #endif
    #ifdef HAVE_FFDHE_4096
        case 4096/8:
            #ifdef HAVE_PUBLIC_FFDHE
            params = wc_Dh_ffdhe4096_Get();
            #endif
            group = WOLFSSL_FFDHE_4096;
            break;
    #endif
    #ifdef HAVE_FFDHE_6144
        case 6144/8:
            #ifdef HAVE_PUBLIC_FFDHE
            params = wc_Dh_ffdhe6144_Get();
            #endif
            group = WOLFSSL_FFDHE_6144;
            break;
    #endif
    #ifdef HAVE_FFDHE_8192
        case 8192/8:
            #ifdef HAVE_PUBLIC_FFDHE
            params = wc_Dh_ffdhe8192_Get();
            #endif
            group = WOLFSSL_FFDHE_8192;
            break;
    #endif
        default:
            break;
    }


#ifdef HAVE_PUBLIC_FFDHE
    if (params == NULL || params->g_len != ssl->buffers.serverDH_G.length ||
            (XMEMCMP(ssl->buffers.serverDH_G.buffer, params->g,
                    params->g_len) != 0) ||
            (XMEMCMP(ssl->buffers.serverDH_P.buffer, params->p,
                    params->p_len) != 0))
#else
    if (!wc_DhCmpNamedKey(group, 1,
            ssl->buffers.serverDH_P.buffer, ssl->buffers.serverDH_P.length,
            ssl->buffers.serverDH_G.buffer, ssl->buffers.serverDH_G.length,
            NULL, 0))
#endif
    {
        WOLFSSL_MSG("Server not using FFDHE parameters");
    #ifdef WOLFSSL_REQUIRE_FFDHE
        SendAlert(ssl, alert_fatal, handshake_failure);
        ERROR_OUT(DH_PARAMS_NOT_FFDHE_E, exit_gdpk);
    #endif
    }
    else {
        ssl->namedGroup = group;
    #if !defined(WOLFSSL_OLD_PRIME_CHECK) && !defined(HAVE_FIPS) && \
        !defined(HAVE_SELFTEST)
        ssl->options.dhDoKeyTest = 0;
    #endif
    }
#endif /* HAVE_FFDHE */

exit_gdpk:
    if (ret != 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
    }
    return ret;
}
#endif
#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
static int GetEcDiffieHellmanKea(WOLFSSL *ssl,
                                 const byte *input, word32 size, DskeArgs *args)
{
    int ret;
    byte b;
#ifdef HAVE_ECC
    int curveId;
#endif
    int curveOid;
    word16 length;

    if ((args->idx - args->begin) + ENUM_LEN + OPAQUE16_LEN +
        OPAQUE8_LEN > size) {
        return BUFFER_ERROR;
    }

    b = input[args->idx++];
    if (b != named_curve) {
        return ECC_CURVETYPE_ERROR;
    }

    args->idx += 1;     /* curve type, eat leading 0 */
    b = input[args->idx++];
    if ((curveOid = CheckCurveId(b)) < 0) {
        return ECC_CURVE_ERROR;
    }
    ssl->ecdhCurveOID = (word32) curveOid;
#if defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE)
    ssl->namedGroup = 0;
#endif

    length = input[args->idx++];
    if ((args->idx - args->begin) + length > size) {
        return BUFFER_ERROR;
    }
#ifdef HAVE_CURVE25519
    if (ssl->ecdhCurveOID == ECC_X25519_OID) {
        if (ssl->peerX25519Key == NULL) {
            ret = AllocKey(ssl, DYNAMIC_TYPE_CURVE25519,
                           (void **)&ssl->peerX25519Key);
            if (ret != 0) {
                return ret;
            }
        }
        else if (ssl->peerX25519KeyPresent) {
            ret = ReuseKey(ssl, DYNAMIC_TYPE_CURVE25519, ssl->peerX25519Key);
            ssl->peerX25519KeyPresent = 0;
            if (ret != 0) {
                return ret;
            }
        }

        if ((ret = wc_curve25519_check_public(input + args->idx, length,
                                              EC25519_LITTLE_ENDIAN)) != 0) {
#ifdef WOLFSSL_EXTRA_ALERTS
            if (ret == WC_NO_ERR_TRACE(BUFFER_E))
                SendAlert(ssl, alert_fatal, decode_error);
            else if (ret == WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E))
                SendAlert(ssl, alert_fatal, bad_record_mac);
            else {
                SendAlert(ssl, alert_fatal, illegal_parameter);
            }
#else
            (void)ret;
#endif
            return ECC_PEERKEY_ERROR;
        }

        if (wc_curve25519_import_public_ex(input + args->idx,
                                           length, ssl->peerX25519Key,
                                           EC25519_LITTLE_ENDIAN) != 0) {
            return ECC_PEERKEY_ERROR;
        }

        args->idx += length;
        ssl->peerX25519KeyPresent = 1;
        return 0;
    }
#endif
#ifdef HAVE_CURVE448
    if (ssl->ecdhCurveOID == ECC_X448_OID) {
        if (ssl->peerX448Key == NULL) {
            ret = AllocKey(ssl, DYNAMIC_TYPE_CURVE448,
                           (void **)&ssl->peerX448Key);
            if (ret != 0) {
                return ret;
            }
        }
        else if (ssl->peerX448KeyPresent) {
            ret = ReuseKey(ssl, DYNAMIC_TYPE_CURVE448, ssl->peerX448Key);
            ssl->peerX448KeyPresent = 0;
            if (ret != 0) {
                return ret;
            }
        }

        if ((ret = wc_curve448_check_public(input + args->idx, length,
                                            EC448_LITTLE_ENDIAN)) != 0) {
#ifdef WOLFSSL_EXTRA_ALERTS
            if (ret == WC_NO_ERR_TRACE(BUFFER_E))
                SendAlert(ssl, alert_fatal, decode_error);
            else if (ret == WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E))
                SendAlert(ssl, alert_fatal, bad_record_mac);
            else {
                SendAlert(ssl, alert_fatal, illegal_parameter);
            }
#else
            (void)ret;
#endif
            return ECC_PEERKEY_ERROR;
        }

        if (wc_curve448_import_public_ex(input + args->idx,
                                         length, ssl->peerX448Key,
                                         EC448_LITTLE_ENDIAN) != 0) {
            return ECC_PEERKEY_ERROR;
        }

        args->idx += length;
        ssl->peerX448KeyPresent = 1;
        return 0;
    }
#endif
#ifdef HAVE_ECC
    if (ssl->peerEccKey == NULL) {
        ret = AllocKey(ssl, DYNAMIC_TYPE_ECC, (void **)&ssl->peerEccKey);
        if (ret != 0) {
            return ret;
        }
    }
    else if (ssl->peerEccKeyPresent) {
        ret = ReuseKey(ssl, DYNAMIC_TYPE_ECC, ssl->peerEccKey);
        ssl->peerEccKeyPresent = 0;
        if (ret != 0) {
            return ret;
        }
    }

    curveId = wc_ecc_get_oid((word32) curveOid, NULL, NULL);
#if !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS)
    if (wc_ecc_import_x963_ex2(input + args->idx, length,
                               ssl->peerEccKey, curveId, 1) != 0)
#else
    /* FIPS has validation define on. */
    if (wc_ecc_import_x963_ex(input + args->idx, length,
                              ssl->peerEccKey, curveId) != 0)
#endif
    {
#ifdef WOLFSSL_EXTRA_ALERTS
        SendAlert(ssl, alert_fatal, illegal_parameter);
#endif
        return ECC_PEERKEY_ERROR;
    }

    args->idx += length;
    ssl->peerEccKeyPresent = 1;
#endif
    return 0;
}
#endif /* def(HAVE_ECC) || def(HAVE_CURVE25519) || def(HAVE_CURVE448)) */

#if !defined(NO_PSK)
static int GetPSKServerHint(WOLFSSL *ssl, const byte *input, word32 size,
                            DskeArgs *args)
{
    int srvHintLen;
    word16 length;

    if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
        return BUFFER_ERROR;
    }

    ato16(input + args->idx, &length);
    args->idx += OPAQUE16_LEN;

    if ((args->idx - args->begin) + length > size) {
        return BUFFER_ERROR;
    }

    /* get PSK server hint from the wire */
    srvHintLen = (int)min(length, MAX_PSK_ID_LEN);
    XMEMCPY(ssl->arrays->server_hint, input + args->idx, (size_t)(srvHintLen));
    ssl->arrays->server_hint[srvHintLen] = '\0';        /* null term */

    args->idx += length;
    return 0;
}
#endif /* !defined(NO_PSK) */

/* handle processing of server_key_exchange (12) */
static int DoServerKeyExchange(WOLFSSL* ssl, const byte* input,
                               word32* inOutIdx, word32 size)
{
    int ret = 0;
#ifdef WOLFSSL_ASYNC_CRYPT
    DskeArgs* args = NULL;
    WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
#else
    DskeArgs  args[1];
#endif

    (void)input;
    (void)size;

    WOLFSSL_START(WC_FUNC_SERVER_KEY_EXCHANGE_DO);
    WOLFSSL_ENTER("DoServerKeyExchange");

#ifdef WOLFSSL_ASYNC_CRYPT
    if (ssl->async == NULL) {
        ssl->async = (struct WOLFSSL_ASYNC*)
                XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
                        DYNAMIC_TYPE_ASYNC);
        if (ssl->async == NULL)
            ERROR_OUT(MEMORY_E, exit_dske);
    }
    args = (DskeArgs*)ssl->async->args;

    ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState);
    if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
        /* Check for error */
        if (ret < 0)
            goto exit_dske;
    }
    else
#endif
    {
        /* Reset state */
        ret = 0;
        ssl->options.asyncState = TLS_ASYNC_BEGIN;
        XMEMSET(args, 0, sizeof(DskeArgs));
        args->idx = *inOutIdx;
        args->begin = *inOutIdx;
        ssl->options.peerSigAlgo = ssl->specs.sig_algo;
        ssl->options.peerHashAlgo = sha_mac;
    #ifdef WOLFSSL_ASYNC_CRYPT
        ssl->async->freeArgs = FreeDskeArgs;
    #endif
    }

    switch(ssl->options.asyncState)
    {
        case TLS_ASYNC_BEGIN:
        {
        #ifdef WOLFSSL_CALLBACKS
            if (ssl->hsInfoOn)
                AddPacketName(ssl, "ServerKeyExchange");
            if (ssl->toInfoOn)
                AddLateName("ServerKeyExchange", &ssl->timeoutInfo);
        #endif

            switch(ssl->specs.kea)
            {
            #ifndef NO_PSK
                case psk_kea:
                {
                    ret = GetPSKServerHint(ssl, input, size, args);
                    break;
                }
            #endif /* !NO_PSK */
            #ifndef NO_DH
                case diffie_hellman_kea:
                {
                    ret = GetDhPublicKey(ssl, input, size, args);
                    break;
                }
            #endif /* !NO_DH */
            #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                case ecc_diffie_hellman_kea:
                {
                    ret = GetEcDiffieHellmanKea(ssl, input, size, args);
                    break;
                }
            #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
            #if !defined(NO_DH) && !defined(NO_PSK)
                case dhe_psk_kea:
                {
                    ret = GetPSKServerHint(ssl, input, size, args);
                    if (ret == 0)
                        ret = GetDhPublicKey(ssl, input, size, args);
                    break;
                }
            #endif /* !NO_DH && !NO_PSK */
            #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                case ecdhe_psk_kea:
                {
                    ret = GetPSKServerHint(ssl, input, size, args);
                    if (ret == 0)
                        ret = GetEcDiffieHellmanKea(ssl, input, size, args);
                    break;
                }
            #endif /* (HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448) && !NO_PSK */
                default:
                    ret = BAD_KEA_TYPE_E;
            } /* switch(ssl->specs.kea) */

            /* Check for error */
            if (ret != 0) {
                goto exit_dske;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_BUILD;
        } /* case TLS_ASYNC_BEGIN */
        FALL_THROUGH;

        case TLS_ASYNC_BUILD:
        {
            switch(ssl->specs.kea)
            {
                case psk_kea:
                case dhe_psk_kea:
                case ecdhe_psk_kea:
                {
                    /* Nothing to do in this sub-state */
                    break;
                }

                case diffie_hellman_kea:
                case ecc_diffie_hellman_kea:
                {
            #if defined(NO_DH) && !defined(HAVE_ECC) && \
                            !defined(HAVE_CURVE25519) && !defined(HAVE_CURVE448)
                    ERROR_OUT(NOT_COMPILED_IN, exit_dske);
            #else
                    enum wc_HashType hashType;
                    word32 verifySz;
                    byte sigAlgo;

                    if (ssl->options.usingAnon_cipher) {
                        break;
                    }

                    verifySz = (args->idx - args->begin);
                    if (verifySz > MAX_DH_SZ) {
                        ERROR_OUT(BUFFER_ERROR, exit_dske);
                    }

                    if (IsAtLeastTLSv1_2(ssl)) {
                        if ((args->idx - args->begin) + ENUM_LEN + ENUM_LEN >
                                                                        size) {
                            ERROR_OUT(BUFFER_ERROR, exit_dske);
                        }

                        /* Check if hashSigAlgo in Server Key Exchange is supported
                         * in our ssl->suites or ssl->ctx->suites. */
                        if (!SupportedHashSigAlgo(ssl, &input[args->idx])) {
                        #ifdef WOLFSSL_EXTRA_ALERTS
                            SendAlert(ssl, alert_fatal, handshake_failure);
                        #endif
                            ERROR_OUT(MATCH_SUITE_ERROR, exit_dske);
                        }

                        DecodeSigAlg(&input[args->idx], &ssl->options.peerHashAlgo,
                                     &sigAlgo);
                    #ifndef NO_RSA
                        if (sigAlgo == rsa_pss_sa_algo &&
                                                 ssl->options.peerSigAlgo == rsa_sa_algo) {
                            ssl->options.peerSigAlgo = sigAlgo;
                        }
                        else
                    #endif
                    #ifdef WC_RSA_PSS
                        if (sigAlgo == rsa_pss_pss_algo &&
                                ssl->options.peerSigAlgo == rsa_sa_algo) {
                            ssl->options.peerSigAlgo = sigAlgo;
                        }
                        else
                    #endif
                    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                        if (sigAlgo == sm2_sa_algo &&
                                             ssl->options.peerSigAlgo == ecc_dsa_sa_algo) {
                            ssl->options.peerSigAlgo = sigAlgo;
                        }
                        else
                    #endif
                    #ifdef HAVE_ED25519
                        if (sigAlgo == ed25519_sa_algo &&
                                             ssl->options.peerSigAlgo == ecc_dsa_sa_algo) {
                            ssl->options.peerSigAlgo = sigAlgo;
                        }
                        else
                    #endif
                    #ifdef HAVE_ED448
                        if (sigAlgo == ed448_sa_algo &&
                                             ssl->options.peerSigAlgo == ecc_dsa_sa_algo) {
                            ssl->options.peerSigAlgo = sigAlgo;
                        }
                        else
                    #endif
                        /* Signature algorithm from message must match signature
                         * algorithm in cipher suite. */
                        if (sigAlgo != ssl->options.peerSigAlgo) {
                            ERROR_OUT(ALGO_ID_E, exit_dske);
                        }
                        args->idx += 2;
                        hashType = HashAlgoToType(ssl->options.peerHashAlgo);
                        if (hashType == WC_HASH_TYPE_NONE) {
                            ERROR_OUT(ALGO_ID_E, exit_dske);
                        }
                    } else {
                        /* only using sha and md5 for rsa */
                        #ifndef NO_OLD_TLS
                            hashType = WC_HASH_TYPE_SHA;
                            if (ssl->options.peerSigAlgo == rsa_sa_algo) {
                                hashType = WC_HASH_TYPE_MD5_SHA;
                            }
                        #else
                            ERROR_OUT(ALGO_ID_E, exit_dske);
                        #endif
                    }

                    /* signature */
                    if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
                        ERROR_OUT(BUFFER_ERROR, exit_dske);
                    }

                    ato16(input + args->idx, &args->verifySigSz);
                    args->idx += OPAQUE16_LEN;

                    if ((args->idx - args->begin) + args->verifySigSz > size) {
                        ERROR_OUT(BUFFER_ERROR, exit_dske);
                    }

                    ret = HashSkeData(ssl, hashType, input + args->begin,
                        verifySz, ssl->options.peerSigAlgo);
                    if (ret != 0) {
                        goto exit_dske;
                    }

                    switch (ssl->options.peerSigAlgo)
                    {
                    #ifndef NO_RSA
                    #ifdef WC_RSA_PSS
                        case rsa_pss_sa_algo:
                        case rsa_pss_pss_algo:
                    #endif
                        case rsa_sa_algo:
                        {
                            if (ssl->peerRsaKey == NULL ||
                                                    !ssl->peerRsaKeyPresent) {
                                ERROR_OUT(NO_PEER_KEY, exit_dske);
                            }
                            break;
                        }
                    #endif /* !NO_RSA */
                    #ifdef HAVE_ECC
                    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                        case sm2_sa_algo:
                    #endif
                        case ecc_dsa_sa_algo:
                        {
                            if (!ssl->peerEccDsaKeyPresent) {
                                ERROR_OUT(NO_PEER_KEY, exit_dske);
                            }
                            break;
                        }
                    #endif /* HAVE_ECC */
                    #if defined(HAVE_ED25519)
                        case ed25519_sa_algo:
                        {
                            if (!ssl->peerEd25519KeyPresent) {
                                ERROR_OUT(NO_PEER_KEY, exit_dske);
                            }
                            break;
                        }
                    #endif /* HAVE_ED25519 */
                    #if defined(HAVE_ED448)
                        case ed448_sa_algo:
                        {
                            if (!ssl->peerEd448KeyPresent) {
                                ERROR_OUT(NO_PEER_KEY, exit_dske);
                            }
                            break;
                        }
                    #endif /* HAVE_ED448 */

                    default:
                        ret = ALGO_ID_E;
                    } /* switch (ssl->options.peerSigAlgo) */

            #endif /* NO_DH && !HAVE_ECC && !HAVE_ED25519 && !HAVE_ED448 */
                    break;
                }
                default:
                    ret = BAD_KEA_TYPE_E;
            } /* switch(ssl->specs.kea) */

            /* Check for error */
            if (ret != 0) {
                goto exit_dske;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_DO;
        } /* case TLS_ASYNC_BUILD */
        FALL_THROUGH;

        case TLS_ASYNC_DO:
        {
            switch(ssl->specs.kea)
            {
                case psk_kea:
                case dhe_psk_kea:
                case ecdhe_psk_kea:
                {
                    /* Nothing to do in this sub-state */
                    break;
                }

                case diffie_hellman_kea:
                case ecc_diffie_hellman_kea:
                {
            #if defined(NO_DH) && !defined(HAVE_ECC) && \
                            !defined(HAVE_CURVE25519) && !defined(HAVE_CURVE448)
                    ERROR_OUT(NOT_COMPILED_IN, exit_dske);
            #else
                    if (ssl->options.usingAnon_cipher) {
                        break;
                    }

                    if (args->verifySig == NULL) {
                        args->verifySig = (byte*)XMALLOC(args->verifySigSz,
                                            ssl->heap, DYNAMIC_TYPE_SIGNATURE);
                        if (args->verifySig == NULL) {
                            ERROR_OUT(MEMORY_E, exit_dske);
                        }
                        XMEMCPY(args->verifySig, input + args->idx,
                                                            args->verifySigSz);
                    }

                    switch (ssl->options.peerSigAlgo)
                    {
                    #ifndef NO_RSA
                    #ifdef WC_RSA_PSS
                        case rsa_pss_sa_algo:
                        case rsa_pss_pss_algo:
                    #endif
                        case rsa_sa_algo:
                        {
                            ret = RsaVerify(ssl,
                                args->verifySig, args->verifySigSz,
                                &args->output,
                                ssl->options.peerSigAlgo, ssl->options.peerHashAlgo,
                                ssl->peerRsaKey,
                            #ifdef HAVE_PK_CALLBACKS
                                &ssl->buffers.peerRsaKey
                            #else
                                NULL
                            #endif
                            );

                            if (ret >= 0) {
                                args->sigSz = (word16)ret;
                            #ifdef WC_RSA_PSS
                                args->bits = mp_count_bits(&ssl->peerRsaKey->n);
                            #endif
                                ret = 0;
                            }
                        #ifdef WOLFSSL_ASYNC_CRYPT
                            if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
                        #endif
                            {
                                /* peerRsaKey */
                                FreeKey(ssl, DYNAMIC_TYPE_RSA,
                                                      (void**)&ssl->peerRsaKey);
                                ssl->peerRsaKeyPresent = 0;
                            }
                            break;
                        }
                    #endif /* !NO_RSA */
                    #ifdef HAVE_ECC
                    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                        case sm2_sa_algo:
                    #endif
                        case ecc_dsa_sa_algo:
                        {
                            ret = NOT_COMPILED_IN;
                        #ifdef HAVE_PK_CALLBACKS
                            if (ssl->ctx && ssl->ctx->ProcessServerSigKexCb) {
                                ret = ssl->ctx->ProcessServerSigKexCb(ssl,
                                    ssl->options.peerSigAlgo,
                                    args->verifySig, args->verifySigSz,
                                    ssl->buffers.sig.buffer, SEED_LEN,
                                    &ssl->buffers.sig.buffer[SEED_LEN],
                                    (ssl->buffers.sig.length - SEED_LEN));
                            }
                        #endif /* HAVE_PK_CALLBACKS */
                            if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) {
                            #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                                if (ssl->options.peerSigAlgo == sm2_sa_algo) {
                                    ret = Sm2wSm3Verify(ssl,
                                        TLS12_SM2_SIG_ID, TLS12_SM2_SIG_ID_SZ,
                                        args->verifySig, args->verifySigSz,
                                        ssl->buffers.sig.buffer,
                                        ssl->buffers.sig.length,
                                        ssl->peerEccDsaKey,
                                    #ifdef HAVE_PK_CALLBACKS
                                        &ssl->buffers.peerEccDsaKey
                                    #else
                                        NULL
                                    #endif
                                    );
                                }
                                else
                            #endif
                                {
                                    ret = EccVerify(ssl,
                                        args->verifySig, args->verifySigSz,
                                        ssl->buffers.digest.buffer,
                                        ssl->buffers.digest.length,
                                        ssl->peerEccDsaKey,
                                    #ifdef HAVE_PK_CALLBACKS
                                        &ssl->buffers.peerEccDsaKey
                                    #else
                                        NULL
                                    #endif
                                    );
                                }
                            }

                        #ifdef WOLFSSL_ASYNC_CRYPT
                            if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
                        #endif
                            {
                                /* peerEccDsaKey */
                                FreeKey(ssl, DYNAMIC_TYPE_ECC,
                                                   (void**)&ssl->peerEccDsaKey);
                                ssl->peerEccDsaKeyPresent = 0;
                            }
                            /* CLIENT: Data verified with cert's public key. */
                            ssl->options.peerAuthGood =
                                ssl->options.havePeerCert && (ret == 0);
                            break;
                        }
                    #endif /* HAVE_ECC */
                    #if defined(HAVE_ED25519)
                        case ed25519_sa_algo:
                        {
                            ret = Ed25519Verify(ssl,
                                args->verifySig, args->verifySigSz,
                                ssl->buffers.sig.buffer,
                                ssl->buffers.sig.length,
                                ssl->peerEd25519Key,
                            #ifdef HAVE_PK_CALLBACKS
                                &ssl->buffers.peerEd25519Key
                            #else
                                NULL
                            #endif
                            );

                        #ifdef WOLFSSL_ASYNC_CRYPT
                            if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
                        #endif
                            {
                                /* peerEccDsaKey */
                                FreeKey(ssl, DYNAMIC_TYPE_ED25519,
                                                  (void**)&ssl->peerEd25519Key);
                                ssl->peerEd25519KeyPresent = 0;
                            }
                            /* CLIENT: Data verified with cert's public key. */
                            ssl->options.peerAuthGood =
                                ssl->options.havePeerCert && (ret == 0);
                            break;
                        }
                    #endif /* HAVE_ED25519 */
                    #if defined(HAVE_ED448)
                        case ed448_sa_algo:
                        {
                            ret = Ed448Verify(ssl,
                                args->verifySig, args->verifySigSz,
                                ssl->buffers.sig.buffer,
                                ssl->buffers.sig.length,
                                ssl->peerEd448Key,
                            #ifdef HAVE_PK_CALLBACKS
                                &ssl->buffers.peerEd448Key
                            #else
                                NULL
                            #endif
                            );

                        #ifdef WOLFSSL_ASYNC_CRYPT
                            if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
                        #endif
                            {
                                /* peerEccDsaKey */
                                FreeKey(ssl, DYNAMIC_TYPE_ED448,
                                                    (void**)&ssl->peerEd448Key);
                                ssl->peerEd448KeyPresent = 0;
                            }
                            /* CLIENT: Data verified with cert's public key. */
                            ssl->options.peerAuthGood =
                                ssl->options.havePeerCert && (ret == 0);
                            break;
                        }
                    #endif /* HAVE_ED448 */

                    default:
                        ret = ALGO_ID_E;
                    } /* switch (sigAlgo) */
            #endif /* NO_DH && !HAVE_ECC && !HAVE_ED25519 && !HAVE_ED448 */
                    break;
                }
                default:
                    ret = BAD_KEA_TYPE_E;
            } /* switch(ssl->specs.kea) */

            /* Check for error */
            if (ret != 0) {
                goto exit_dske;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_VERIFY;
        } /* case TLS_ASYNC_DO */
        FALL_THROUGH;

        case TLS_ASYNC_VERIFY:
        {
            switch(ssl->specs.kea)
            {
                case psk_kea:
                case dhe_psk_kea:
                case ecdhe_psk_kea:
                {
                    /* Nothing to do in this sub-state */
                    break;
                }

                case diffie_hellman_kea:
                case ecc_diffie_hellman_kea:
                {
            #if defined(NO_DH) && !defined(HAVE_ECC) && \
                            !defined(HAVE_CURVE25519) && !defined(HAVE_CURVE448)
                    ERROR_OUT(NOT_COMPILED_IN, exit_dske);
            #else
                    if (ssl->options.usingAnon_cipher) {
                        break;
                    }

                    /* increment index after verify is done */
                    args->idx += args->verifySigSz;

                    switch(ssl->options.peerSigAlgo)
                    {
                    #ifndef NO_RSA
                    #ifdef WC_RSA_PSS
                        case rsa_pss_sa_algo:
                        case rsa_pss_pss_algo:
                        #ifdef HAVE_SELFTEST
                            ret = wc_RsaPSS_CheckPadding(
                                             ssl->buffers.digest.buffer,
                                             ssl->buffers.digest.length,
                                             args->output, args->sigSz,
                                             HashAlgoToType(ssl->options.peerHashAlgo));
                        #else
                            ret = wc_RsaPSS_CheckPadding_ex(
                                             ssl->buffers.digest.buffer,
                                             ssl->buffers.digest.length,
                                             args->output, args->sigSz,
                                             HashAlgoToType(ssl->options.peerHashAlgo),
                                             -1, args->bits);
                        #endif
                            if (ret != 0)
                                goto exit_dske;
                            /* CLIENT: Data verified with cert's public key. */
                            ssl->options.peerAuthGood =
                                ssl->options.havePeerCert;
                            break;
                    #endif
                        case rsa_sa_algo:
                        {
                            #if (defined(WOLFSSL_RENESAS_FSPSM_TLS) && \
                                defined(WOLFSSL_RENESAS_FSPSM_ECC)) || \
                                defined(WOLFSSL_RENESAS_TSIP_TLS)
                            /* already checked signature result by SCE */
                            /* skip the sign checks below              */
                            if (Renesas_cmn_usable(ssl, 0)) {
                                break;
                             }
                            #endif
                            if (IsAtLeastTLSv1_2(ssl)) {
                                WC_DECLARE_VAR(encodedSig, byte,
                                    MAX_ENCODED_SIG_SZ, 0);
                                word32 encSigSz;

                                WC_ALLOC_VAR_EX(encodedSig, byte,
                                    MAX_ENCODED_SIG_SZ, ssl->heap,
                                    DYNAMIC_TYPE_SIGNATURE,
                                    ERROR_OUT(MEMORY_E,exit_dske));

                                encSigSz = wc_EncodeSignature(encodedSig,
                                    ssl->buffers.digest.buffer,
                                    ssl->buffers.digest.length,
                                    TypeHash(ssl->options.peerHashAlgo));
                                if (encSigSz != args->sigSz || !args->output ||
                                    XMEMCMP(args->output, encodedSig,
                                            min(encSigSz, MAX_ENCODED_SIG_SZ)) != 0) {
                                    ret = VERIFY_SIGN_ERROR;
                                }
                                WC_FREE_VAR_EX(encodedSig, ssl->heap,
                                    DYNAMIC_TYPE_SIGNATURE);
                                if (ret != 0) {
                                    goto exit_dske;
                                }
                            }
                            else if (args->sigSz != FINISHED_SZ ||
                                    !args->output ||
                                    XMEMCMP(args->output,
                                            ssl->buffers.digest.buffer,
                                            FINISHED_SZ) != 0) {
                                ERROR_OUT(VERIFY_SIGN_ERROR, exit_dske);
                            }
                            /* CLIENT: Data verified with cert's public key. */
                            ssl->options.peerAuthGood =
                                ssl->options.havePeerCert;
                            break;
                        }
                    #endif /* !NO_RSA */
                    #ifdef HAVE_ECC
                        case ecc_dsa_sa_algo:
                            /* Nothing to do in this algo */
                            break;
                    #endif /* HAVE_ECC */
                    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                        case sm2_sa_algo:
                            /* Nothing to do in this algo */
                            break;
                    #endif /* WOLFSSL_SM2 && WOLFSSL_SM3 */
                    #if defined(HAVE_ED25519)
                        case ed25519_sa_algo:
                            /* Nothing to do in this algo */
                            break;
                    #endif /* HAVE_ED25519 */
                    #if defined(HAVE_ED448)
                        case ed448_sa_algo:
                            /* Nothing to do in this algo */
                            break;
                    #endif /* HAVE_ED448 */
                        default:
                            ret = ALGO_ID_E;
                    } /* switch (sigAlgo) */
            #endif /* NO_DH && !HAVE_ECC && !HAVE_ED25519 && !HAVE_ED448 */
                    break;
                }
                default:
                    ret = BAD_KEA_TYPE_E;
            } /* switch(ssl->specs.kea) */

            /* Check for error */
            if (ret != 0) {
                goto exit_dske;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_FINALIZE;
        } /* case TLS_ASYNC_VERIFY */
        FALL_THROUGH;

        case TLS_ASYNC_FINALIZE:
        {
            if (IsEncryptionOn(ssl, 0))
                args->idx += ssl->keys.padSz;

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_END;
        } /* case TLS_ASYNC_FINALIZE */
        FALL_THROUGH;

        case TLS_ASYNC_END:
        {
            /* return index */
            *inOutIdx = args->idx;

            ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE;
            break;
        }
        default:
            ret = INPUT_CASE_ERROR;
    } /* switch(ssl->options.asyncState) */

exit_dske:

    WOLFSSL_LEAVE("DoServerKeyExchange", ret);
    WOLFSSL_END(WC_FUNC_SERVER_KEY_EXCHANGE_DO);

#ifdef WOLFSSL_ASYNC_CRYPT
    /* Handle async operation */
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        /* Mark message as not received so it can process again */
        ssl->msgsReceived.got_server_key_exchange = 0;

        return ret;
    }
    /* Cleanup async */
    FreeAsyncCtx(ssl, 0);
#else
    FreeDskeArgs(ssl, args);
#endif /* WOLFSSL_ASYNC_CRYPT */

    /* Final cleanup */
    FreeKeyExchange(ssl);

    if (ret != 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
    }
    return ret;
}

typedef struct SckeArgs {
    byte*  output; /* not allocated */
    byte*  encSecret;
    byte*  input;
    word32 encSz;
    word32 length;
    int    sendSz;
    int    inputSz;
} SckeArgs;

static void FreeSckeArgs(WOLFSSL* ssl, void* pArgs)
{
    SckeArgs* args = (SckeArgs*)pArgs;

    (void)ssl;

    XFREE(args->encSecret, ssl->heap, DYNAMIC_TYPE_SECRET);
    args->encSecret = NULL;
    XFREE(args->input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
    args->input = NULL;
}
#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
static int EcExportHsKey(WOLFSSL* ssl, byte* out, word32* len)
{
    int ret = 0;
#ifdef HAVE_CURVE25519
    if (ssl->ecdhCurveOID == ECC_X25519_OID) {
    #ifdef HAVE_PK_CALLBACKS
        /* if callback then use it for shared secret */
        if (ssl->ctx->X25519SharedSecretCb != NULL)
            return 0;
    #endif
        if (wc_curve25519_export_public_ex((curve25519_key*)ssl->hsKey,
                           out + OPAQUE8_LEN, len, EC25519_LITTLE_ENDIAN))
            ret = ECC_EXPORT_ERROR;
    } else
#endif
#ifdef HAVE_CURVE448
    if (ssl->ecdhCurveOID == ECC_X448_OID) {
    #ifdef HAVE_PK_CALLBACKS
        /* if callback then use it for shared secret */
        if (ssl->ctx->X448SharedSecretCb != NULL)
            return 0;
    #endif
        if (wc_curve448_export_public_ex((curve448_key*)ssl->hsKey,
                             out + OPAQUE8_LEN, len, EC448_LITTLE_ENDIAN))
            ret = ECC_EXPORT_ERROR;
    } else
#endif
    {
#if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT)
#ifdef HAVE_PK_CALLBACKS
        /* if callback then use it for shared secret */
        if (ssl->ctx->EccSharedSecretCb != NULL)
            return 0;
#endif
        /* Place ECC key in output buffer, leaving room for size */
        PRIVATE_KEY_UNLOCK();
        ret = wc_ecc_export_x963((ecc_key*)ssl->hsKey, out + OPAQUE8_LEN, len);
        PRIVATE_KEY_LOCK();
        if (ret != 0)
            ret = ECC_EXPORT_ERROR;
#endif
    }
    return ret;
}
#endif /*HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448*/

#if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
static int EcMakeKey(WOLFSSL* ssl)
{
    int ret = 0;
#ifdef HAVE_ECC
    int kea = ssl->specs.kea;
    ecc_key* peerKey;
#endif
#ifdef HAVE_CURVE25519
    if (ssl->peerX25519KeyPresent) {
        /* Check client ECC public key */
        if (!ssl->peerX25519Key || !ssl->peerX25519Key->dp) {
            return NO_PEER_KEY;
        }
        /* if callback then use it for shared secret */
    #ifdef HAVE_PK_CALLBACKS
        if (ssl->ecdhCurveOID == ECC_X25519_OID) {
            if (ssl->ctx->X25519SharedSecretCb != NULL)
                return 0;
        }
    #endif
        /* create private key */
        ssl->hsType = DYNAMIC_TYPE_CURVE25519;
        ret = AllocKey(ssl, (int)(ssl->hsType), &ssl->hsKey);
        if (ret != 0) {
            return ret;
        }
        ret = X25519MakeKey(ssl, (curve25519_key*)ssl->hsKey,
                            ssl->peerX25519Key);
        return ret;
    }
#endif /* HAVE_CURVE25519 */
#ifdef HAVE_CURVE448
    if (ssl->peerX448KeyPresent) {
        /* Check client ECC public key */
        if (!ssl->peerX448Key) {
            return NO_PEER_KEY;
        }
    #ifdef HAVE_PK_CALLBACKS
        if (ssl->ecdhCurveOID == ECC_X448_OID) {
            if (ssl->ctx->X448SharedSecretCb != NULL)
                return 0;
        }
    #endif
        /* create private key */
        ssl->hsType = DYNAMIC_TYPE_CURVE448;
        ret = AllocKey(ssl, ssl->hsType, &ssl->hsKey);
        if (ret != 0) {
            return ret;
        }
        ret = X448MakeKey(ssl, (curve448_key*)ssl->hsKey,
                          ssl->peerX448Key);
        return ret;
    }
#endif /* HAVE_CURVE448 */
#ifdef HAVE_ECC
    if (kea == ecc_diffie_hellman_kea && ssl->specs.static_ecdh) {
        /* Note: EccDsa is really fixed Ecc key here */
        if (!ssl->peerEccDsaKey || !ssl->peerEccDsaKeyPresent) {
            return NO_PEER_KEY;
        }
        peerKey = ssl->peerEccDsaKey;
    }
    else {
        /* Check client ECC public key */
        if (!ssl->peerEccKey || !ssl->peerEccKeyPresent ||
                                !ssl->peerEccKey->dp) {
            return NO_PEER_KEY;
        }
        peerKey = ssl->peerEccKey;
    }
    if (peerKey == NULL) {
        return NO_PEER_KEY;
    }
#ifdef HAVE_PK_CALLBACKS
    if (ssl->ctx->EccSharedSecretCb != NULL) {
        return 0;
    }
#endif
    /* create ephemeral private key */
    ssl->hsType = DYNAMIC_TYPE_ECC;
    ret = AllocKey(ssl, (int)(ssl->hsType), &ssl->hsKey);
    if (ret != 0) {
        return ret;
    }
    ret = EccMakeKey(ssl, (ecc_key*)ssl->hsKey, ssl->peerEccKey);
#endif /* HAVE_ECC */
    return ret;
}
#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

/* handle generation client_key_exchange (16) */
int SendClientKeyExchange(WOLFSSL* ssl)
{
    int ret = 0;
#ifdef WOLFSSL_ASYNC_IO
    SckeArgs* args = NULL;
    WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
#else
    SckeArgs  args[1];
#endif

    WOLFSSL_START(WC_FUNC_CLIENT_KEY_EXCHANGE_SEND);
    WOLFSSL_ENTER("SendClientKeyExchange");

#ifdef OPENSSL_EXTRA
    ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
    ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
    if (ssl->CBIS != NULL)
        ssl->CBIS(ssl, WOLFSSL_CB_CONNECT_LOOP, WOLFSSL_SUCCESS);
#endif

#ifdef WOLFSSL_ASYNC_IO
    if (ssl->async == NULL) {
        ssl->async = (struct WOLFSSL_ASYNC*)
                XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
                        DYNAMIC_TYPE_ASYNC);
        if (ssl->async == NULL)
            ERROR_OUT(MEMORY_E, exit_scke);
        XMEMSET(ssl->async, 0, sizeof(struct WOLFSSL_ASYNC));
    }
    args = (SckeArgs*)ssl->async->args;

#ifdef WOLFSSL_ASYNC_CRYPT
    ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState);
    if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
        /* Check for error */
        if (ret < 0)
            goto exit_scke;
    }
    else
#endif
    if (ssl->options.buildingMsg) {
        /* Continue building the message */
    }
    else
#endif
    {
        /* Reset state */
        ret = 0;
        ssl->options.asyncState = TLS_ASYNC_BEGIN;
        XMEMSET(args, 0, sizeof(SckeArgs));
        /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
         * is not advanced yet */
        ssl->options.buildingMsg = 1;
    #ifdef WOLFSSL_ASYNC_IO
        ssl->async->freeArgs = FreeSckeArgs;
    #endif
    }

    switch(ssl->options.asyncState)
    {
        case TLS_ASYNC_BEGIN:
        {
            switch (ssl->specs.kea) {
            #ifndef NO_RSA
                case rsa_kea:
                    if (ssl->peerRsaKey == NULL ||
                        ssl->peerRsaKeyPresent == 0) {
                        ERROR_OUT(NO_PEER_KEY, exit_scke);
                    }
                    break;
            #endif
            #ifndef NO_DH
                case diffie_hellman_kea:
                    if (ssl->buffers.serverDH_P.buffer == NULL ||
                        ssl->buffers.serverDH_G.buffer == NULL ||
                        ssl->buffers.serverDH_Pub.buffer == NULL) {
                        ERROR_OUT(NO_PEER_KEY, exit_scke);
                    }
                    break;
            #endif /* NO_DH */
            #ifndef NO_PSK
                case psk_kea:
                    /* sanity check that PSK client callback has been set */
                    if (ssl->options.client_psk_cb == NULL) {
                        WOLFSSL_MSG("No client PSK callback set");
                        ERROR_OUT(PSK_KEY_ERROR, exit_scke);
                    }
                    break;
            #endif /* NO_PSK */
            #if !defined(NO_DH) && !defined(NO_PSK)
                case dhe_psk_kea:
                    if (ssl->buffers.serverDH_P.buffer == NULL ||
                        ssl->buffers.serverDH_G.buffer == NULL ||
                        ssl->buffers.serverDH_Pub.buffer == NULL) {
                        ERROR_OUT(NO_PEER_KEY, exit_scke);
                    }

                    /* sanity check that PSK client callback has been set */
                    if (ssl->options.client_psk_cb == NULL) {
                        WOLFSSL_MSG("No client PSK callback set");
                        ERROR_OUT(PSK_KEY_ERROR, exit_scke);
                    }
                    break;
            #endif /* !NO_DH && !NO_PSK */
            #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                case ecdhe_psk_kea:
                    /* sanity check that PSK client callback has been set */
                    if (ssl->options.client_psk_cb == NULL) {
                        WOLFSSL_MSG("No client PSK callback set");
                        ERROR_OUT(PSK_KEY_ERROR, exit_scke);
                    }
                    ret = EcMakeKey(ssl);
                    if (ret)
                        ERROR_OUT(ret, exit_scke);
                    break;
            #endif /* (HAVE_ECC||HAVE_CURVE25519||HAVE_CURVE448) && !NO_PSK */
            #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                case ecc_diffie_hellman_kea:
                {
                    ret = EcMakeKey(ssl);
                    if (ret)
                        ERROR_OUT(ret, exit_scke);
                    break;
                }
            #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

                default:
                    ret = BAD_KEA_TYPE_E;
            } /* switch(ssl->specs.kea) */

            /* Check for error */
            if (ret != 0) {
                goto exit_scke;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_BUILD;
        } /* case TLS_ASYNC_BEGIN */
        FALL_THROUGH;

        case TLS_ASYNC_BUILD:
        {
            args->encSz = MAX_ENCRYPT_SZ;
            args->encSecret = (byte*)XMALLOC(MAX_ENCRYPT_SZ, ssl->heap,
                                                    DYNAMIC_TYPE_SECRET);
            if (args->encSecret == NULL) {
                ERROR_OUT(MEMORY_E, exit_scke);
            }
            if (ssl->arrays->preMasterSecret == NULL) {
                ssl->arrays->preMasterSz = ENCRYPT_LEN;
                ssl->arrays->preMasterSecret = (byte*)XMALLOC(ENCRYPT_LEN,
                                                ssl->heap, DYNAMIC_TYPE_SECRET);
                if (ssl->arrays->preMasterSecret == NULL) {
                    ERROR_OUT(MEMORY_E, exit_scke);
                }
                XMEMSET(ssl->arrays->preMasterSecret, 0, ENCRYPT_LEN);
            }

            switch(ssl->specs.kea)
            {
            #ifndef NO_RSA
                case rsa_kea:
                {
                    #ifdef HAVE_PK_CALLBACKS
                    if (ssl->ctx->GenPreMasterCb) {
                        void* ctx = wolfSSL_GetGenPreMasterCtx(ssl);
                        ret = ssl->ctx->GenPreMasterCb(ssl,
                            ssl->arrays->preMasterSecret, ENCRYPT_LEN, ctx);
                        if (ret != 0 &&
                            ret != WC_NO_ERR_TRACE(PROTOCOLCB_UNAVAILABLE)) {
                            goto exit_scke;
                        }
                    }
                    if (!ssl->ctx->GenPreMasterCb ||
                        ret == WC_NO_ERR_TRACE(PROTOCOLCB_UNAVAILABLE))
                    #endif
                    {
                        /* build PreMasterSecret with RNG data */
                        ret = wc_RNG_GenerateBlock(ssl->rng,
                            &ssl->arrays->preMasterSecret[VERSION_SZ],
                            SECRET_LEN - VERSION_SZ);
                        if (ret != 0) {
                            goto exit_scke;
                        }

                        ssl->arrays->preMasterSecret[0] = ssl->chVersion.major;
                        ssl->arrays->preMasterSecret[1] = ssl->chVersion.minor;

                        ssl->arrays->preMasterSz = SECRET_LEN;
                    }
                    break;
                }
            #endif /* !NO_RSA */
            #ifndef NO_DH
                case diffie_hellman_kea:
                {
                    ssl->buffers.sig.length = ENCRYPT_LEN;
                    ssl->buffers.sig.buffer = (byte*)XMALLOC(ENCRYPT_LEN,
                                            ssl->heap, DYNAMIC_TYPE_SIGNATURE);
                    if (ssl->buffers.sig.buffer == NULL) {
                        ERROR_OUT(MEMORY_E, exit_scke);
                    }

                    CHECK_RET(ret, AllocKey(ssl, DYNAMIC_TYPE_DH,
                                       (void**)&ssl->buffers.serverDH_Key),
                              exit_scke);
#if defined(HAVE_FFDHE) && !defined(HAVE_PUBLIC_FFDHE)
                    if (ssl->namedGroup) {
                        ret = wc_DhSetNamedKey(ssl->buffers.serverDH_Key,
                                ssl->namedGroup);
                        if (ret != 0) {
                            goto exit_scke;
                        }
                        ssl->buffers.sig.length =
                            wc_DhGetNamedKeyMinSize(ssl->namedGroup);
                    }
                    else
#endif
                    CHECK_RET(ret, DhSetKey(ssl), exit_scke);
                    /* for DH, encSecret is Yc, agree is pre-master */
                    ret = DhGenKeyPair(ssl, ssl->buffers.serverDH_Key,
                        ssl->buffers.sig.buffer, (word32*)&ssl->buffers.sig.length,
                        args->encSecret, &args->encSz);

                    /* set the max agree result size */
                    ssl->arrays->preMasterSz = ENCRYPT_LEN;
                    break;
                }
            #endif /* !NO_DH */
            #ifndef NO_PSK
                case psk_kea:
                {
                    ssl->arrays->psk_keySz = ssl->options.client_psk_cb(ssl,
                        ssl->arrays->server_hint, ssl->arrays->client_identity,
                        MAX_PSK_ID_LEN, ssl->arrays->psk_key, MAX_PSK_KEY_LEN);
                    if (ssl->arrays->psk_keySz == 0 ||
                            (ssl->arrays->psk_keySz > MAX_PSK_KEY_LEN &&
                        (int)ssl->arrays->psk_keySz != WC_NO_ERR_TRACE(USE_HW_PSK))) {
                        ERROR_OUT(PSK_KEY_ERROR, exit_scke);
                    }

                    /* Ensure the buffer is null-terminated. */
                    ssl->arrays->client_identity[MAX_PSK_ID_LEN] = '\0';
                    args->encSz = (word32)XSTRLEN(ssl->arrays->client_identity);
                    if (args->encSz > MAX_PSK_ID_LEN) {
                        ERROR_OUT(CLIENT_ID_ERROR, exit_scke);
                    }
                    XMEMCPY(args->encSecret, ssl->arrays->client_identity,
                            args->encSz);
                    ssl->options.peerAuthGood = 1;
                    MakePSKPreMasterSecret(ssl->arrays, 1);
                    break;
                }
            #endif /* !NO_PSK */
            #if !defined(NO_DH) && !defined(NO_PSK)
                case dhe_psk_kea:
                {
                    word32 esSz = 0;
                    args->output = args->encSecret;

                    ssl->arrays->psk_keySz = ssl->options.client_psk_cb(ssl,
                        ssl->arrays->server_hint, ssl->arrays->client_identity,
                        MAX_PSK_ID_LEN, ssl->arrays->psk_key, MAX_PSK_KEY_LEN);
                    if (ssl->arrays->psk_keySz == 0 ||
                            (ssl->arrays->psk_keySz > MAX_PSK_KEY_LEN &&
                        (int)ssl->arrays->psk_keySz != WC_NO_ERR_TRACE(USE_HW_PSK))) {
                        ERROR_OUT(PSK_KEY_ERROR, exit_scke);
                    }

                    ssl->arrays->client_identity[MAX_PSK_ID_LEN] = '\0'; /* null term */
                    esSz = (word32)XSTRLEN(ssl->arrays->client_identity);

                    if (esSz > MAX_PSK_ID_LEN) {
                        ERROR_OUT(CLIENT_ID_ERROR, exit_scke);
                    }
                    /* CLIENT: Pre-shared Key for peer authentication. */
                    ssl->options.peerAuthGood = 1;

                    ssl->buffers.sig.length = ENCRYPT_LEN;
                    ssl->buffers.sig.buffer = (byte*)XMALLOC(ENCRYPT_LEN,
                                            ssl->heap, DYNAMIC_TYPE_SIGNATURE);
                    if (ssl->buffers.sig.buffer == NULL) {
                        ERROR_OUT(MEMORY_E, exit_scke);
                    }

                    c16toa((word16)esSz, args->output);
                    args->output += OPAQUE16_LEN;
                    XMEMCPY(args->output, ssl->arrays->client_identity, esSz);
                    args->output += esSz;
                    args->length = args->encSz - esSz - OPAQUE16_LEN;
                    args->encSz = esSz + OPAQUE16_LEN;

                    CHECK_RET(ret, AllocKey(ssl, DYNAMIC_TYPE_DH,
                                       (void**)&ssl->buffers.serverDH_Key),
                              exit_scke);
                    CHECK_RET(ret, DhSetKey(ssl), exit_scke);
                    /* for DH, encSecret is Yc, agree is pre-master */
                    ret = DhGenKeyPair(ssl, ssl->buffers.serverDH_Key,
                            ssl->buffers.sig.buffer,
                            (word32*)&ssl->buffers.sig.length,
                            args->output + OPAQUE16_LEN, &args->length);
                    break;
                }
            #endif /* !NO_DH && !NO_PSK */
            #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                case ecdhe_psk_kea:
                {
                    word32 esSz = 0;
                    args->output = args->encSecret;

                    /* Send PSK client identity */
                    ssl->arrays->psk_keySz = ssl->options.client_psk_cb(ssl,
                        ssl->arrays->server_hint, ssl->arrays->client_identity,
                        MAX_PSK_ID_LEN, ssl->arrays->psk_key, MAX_PSK_KEY_LEN);
                    if (ssl->arrays->psk_keySz == 0 ||
                            (ssl->arrays->psk_keySz > MAX_PSK_KEY_LEN &&
                        (int)ssl->arrays->psk_keySz != WC_NO_ERR_TRACE(USE_HW_PSK))) {
                        ERROR_OUT(PSK_KEY_ERROR, exit_scke);
                    }

                    ssl->arrays->client_identity[MAX_PSK_ID_LEN] = '\0'; /* null term */
                    esSz = (word32)XSTRLEN(ssl->arrays->client_identity);
                    if (esSz > MAX_PSK_ID_LEN) {
                        ERROR_OUT(CLIENT_ID_ERROR, exit_scke);
                    }
                    /* CLIENT: Pre-shared Key for peer authentication. */
                    ssl->options.peerAuthGood = 1;

                    /* place size and identity in output buffer sz:identity */
                    c16toa((word16)esSz, args->output);
                    args->output += OPAQUE16_LEN;
                    XMEMCPY(args->output, ssl->arrays->client_identity, esSz);
                    args->output += esSz;
                    args->encSz = esSz + OPAQUE16_LEN;

                    /* length is used for public key size */
                    args->length = MAX_ENCRYPT_SZ;

                    /* Create shared ECC key leaving room at the beginning
                     * of buffer for size of shared key. */
                    ssl->arrays->preMasterSz = ENCRYPT_LEN - OPAQUE16_LEN;
                    ret = EcExportHsKey(ssl, args->output, &args->length);
                    break;
                }
            #endif /* (HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448) && !NO_PSK */
            #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                case ecc_diffie_hellman_kea:
                {
                    ssl->arrays->preMasterSz = ENCRYPT_LEN;
                    ret = EcExportHsKey(ssl, args->encSecret, &args->encSz);
                    break;
                }
            #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

                default:
                    ret = BAD_KEA_TYPE_E;
            } /* switch(ssl->specs.kea) */

            /* Check for error */
            if (ret != 0) {
                goto exit_scke;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_DO;
        } /* case TLS_ASYNC_BUILD */
        FALL_THROUGH;

        case TLS_ASYNC_DO:
        {
            switch(ssl->specs.kea)
            {
            #if !defined(NO_RSA) && !defined(WOLFSSL_RSA_VERIFY_ONLY)
                case rsa_kea:
                {
                        ret = RsaEnc(ssl,
                            ssl->arrays->preMasterSecret, SECRET_LEN,
                            args->encSecret, &args->encSz,
                            ssl->peerRsaKey,
                        #if defined(HAVE_PK_CALLBACKS)
                            &ssl->buffers.peerRsaKey
                        #else
                            NULL
                        #endif
                        );

                    break;
                }
            #endif /* !NO_RSA && !WOLFSSL_RSA_VERIFY_ONLY */
            #ifndef NO_DH
                case diffie_hellman_kea:
                {
                    ret = DhAgree(ssl, ssl->buffers.serverDH_Key,
                        ssl->buffers.sig.buffer, ssl->buffers.sig.length,
                        ssl->buffers.serverDH_Pub.buffer,
                        ssl->buffers.serverDH_Pub.length,
                        ssl->arrays->preMasterSecret,
                        &ssl->arrays->preMasterSz,
                        ssl->buffers.serverDH_P.buffer,
                        ssl->buffers.serverDH_P.length);
                    break;
                }
            #endif /* !NO_DH */
            #ifndef NO_PSK
                case psk_kea:
                {
                    break;
                }
            #endif /* !NO_PSK */
            #if !defined(NO_DH) && !defined(NO_PSK)
                case dhe_psk_kea:
                {
                    ret = DhAgree(ssl, ssl->buffers.serverDH_Key,
                        ssl->buffers.sig.buffer, ssl->buffers.sig.length,
                        ssl->buffers.serverDH_Pub.buffer,
                        ssl->buffers.serverDH_Pub.length,
                        ssl->arrays->preMasterSecret + OPAQUE16_LEN,
                        &ssl->arrays->preMasterSz,
                        ssl->buffers.serverDH_P.buffer,
                        ssl->buffers.serverDH_P.length);
                    break;
                }
            #endif /* !NO_DH && !NO_PSK */
            #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                case ecdhe_psk_kea:
                {
                #ifdef HAVE_CURVE25519
                    if (ssl->peerX25519KeyPresent) {
                        ret = X25519SharedSecret(
                            ssl,
                            (curve25519_key*)ssl->hsKey, ssl->peerX25519Key,
                            args->output ? args->output + OPAQUE8_LEN : NULL,
                            &args->length,
                            ssl->arrays->preMasterSecret + OPAQUE16_LEN,
                            &ssl->arrays->preMasterSz,
                            WOLFSSL_CLIENT_END
                        );
                        if (!ssl->specs.static_ecdh
                        #ifdef WOLFSSL_ASYNC_CRYPT
                            && ret != WC_NO_ERR_TRACE(WC_PENDING_E)
                        #endif
                        ) {
                            FreeKey(ssl, DYNAMIC_TYPE_CURVE25519,
                                                   (void**)&ssl->peerX25519Key);
                            ssl->peerX25519KeyPresent = 0;
                        }
                        break;
                    }
                #endif
                #ifdef HAVE_CURVE448
                    if (ssl->peerX448KeyPresent) {
                        ret = X448SharedSecret(
                            ssl,
                            (curve448_key*)ssl->hsKey, ssl->peerX448Key,
                            args->output ? args->output + OPAQUE8_LEN : NULL,
                            &args->length,
                            ssl->arrays->preMasterSecret + OPAQUE16_LEN,
                            &ssl->arrays->preMasterSz,
                            WOLFSSL_CLIENT_END
                        );
                        if (!ssl->specs.static_ecdh
                        #ifdef WOLFSSL_ASYNC_CRYPT
                            && ret != WC_NO_ERR_TRACE(WC_PENDING_E)
                        #endif
                        ) {
                            FreeKey(ssl, DYNAMIC_TYPE_CURVE448,
                                                     (void**)&ssl->peerX448Key);
                            ssl->peerX448KeyPresent = 0;
                        }
                        break;
                    }
                #endif
                    ret = EccSharedSecret(
                        ssl,
                        (ecc_key*)ssl->hsKey, ssl->peerEccKey,
                        args->output ? args->output + OPAQUE8_LEN : NULL,
                        &args->length,
                        ssl->arrays->preMasterSecret + OPAQUE16_LEN,
                        &ssl->arrays->preMasterSz,
                        WOLFSSL_CLIENT_END
                    );
                #ifdef WOLFSSL_ASYNC_CRYPT
                    if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
                #endif
                    {
                        FreeKey(ssl, DYNAMIC_TYPE_ECC,
                                                      (void**)&ssl->peerEccKey);
                        ssl->peerEccKeyPresent = 0;
                    }
                    break;
                }
            #endif /* (HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448) && !NO_PSK */
            #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                case ecc_diffie_hellman_kea:
                {
                #ifdef HAVE_ECC
                    ecc_key* peerKey;
                #endif

                #ifdef HAVE_CURVE25519
                    if (ssl->peerX25519KeyPresent) {
                        ret = X25519SharedSecret(
                            ssl,
                            (curve25519_key*)ssl->hsKey, ssl->peerX25519Key,
                            args->encSecret ? args->encSecret + OPAQUE8_LEN : NULL,
                            &args->encSz,
                            ssl->arrays->preMasterSecret,
                            &ssl->arrays->preMasterSz,
                            WOLFSSL_CLIENT_END
                        );
                        if (!ssl->specs.static_ecdh
                        #ifdef WOLFSSL_ASYNC_CRYPT
                            && ret != WC_NO_ERR_TRACE(WC_PENDING_E)
                        #endif
                        ) {
                            FreeKey(ssl, DYNAMIC_TYPE_CURVE25519,
                                                   (void**)&ssl->peerX25519Key);
                            ssl->peerX25519KeyPresent = 0;
                        }
                        break;
                    }
                #endif
                #ifdef HAVE_CURVE448
                    if (ssl->peerX448KeyPresent) {
                        ret = X448SharedSecret(
                            ssl,
                            (curve448_key*)ssl->hsKey, ssl->peerX448Key,
                            args->encSecret ? args->encSecret + OPAQUE8_LEN : NULL,
                            &args->encSz,
                            ssl->arrays->preMasterSecret,
                            &ssl->arrays->preMasterSz,
                            WOLFSSL_CLIENT_END
                        );
                        if (!ssl->specs.static_ecdh
                        #ifdef WOLFSSL_ASYNC_CRYPT
                            && ret != WC_NO_ERR_TRACE(WC_PENDING_E)
                        #endif
                        ) {
                            FreeKey(ssl, DYNAMIC_TYPE_CURVE448,
                                                     (void**)&ssl->peerX448Key);
                            ssl->peerX448KeyPresent = 0;
                        }
                        break;
                    }
                #endif
                #ifdef HAVE_ECC
                    peerKey = (ssl->specs.static_ecdh) ?
                              ssl->peerEccDsaKey : ssl->peerEccKey;

                    ret = EccSharedSecret(
                        ssl,
                        (ecc_key*)ssl->hsKey, peerKey,
                        args->encSecret ? args->encSecret + OPAQUE8_LEN : NULL,
                        &args->encSz,
                        ssl->arrays->preMasterSecret,
                        &ssl->arrays->preMasterSz,
                        WOLFSSL_CLIENT_END);

                    if (!ssl->specs.static_ecdh
                #ifdef WOLFSSL_ASYNC_CRYPT
                        && ret != WC_NO_ERR_TRACE(WC_PENDING_E)
                #endif
                     && !ssl->options.keepResources) {
                        FreeKey(ssl, DYNAMIC_TYPE_ECC,
                                                      (void**)&ssl->peerEccKey);
                        ssl->peerEccKeyPresent = 0;
                    }
                #endif

                    break;
                }
            #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

                default:
                    ret = BAD_KEA_TYPE_E;
            } /* switch(ssl->specs.kea) */

            /* Check for error */
            if (ret != 0) {
                goto exit_scke;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_VERIFY;
        } /* case TLS_ASYNC_DO */
        FALL_THROUGH;

        case TLS_ASYNC_VERIFY:
        {
            switch(ssl->specs.kea)
            {
            #ifndef NO_RSA
                case rsa_kea:
                {
                    break;
                }
            #endif /* !NO_RSA */
            #ifndef NO_DH
                case diffie_hellman_kea:
                {
                    break;
                }
            #endif /* !NO_DH */
            #ifndef NO_PSK
                case psk_kea:
                {
                    break;
                }
            #endif /* !NO_PSK */
            #if !defined(NO_DH) && !defined(NO_PSK)
                case dhe_psk_kea:
                {
                    /* validate args */
                    if (args->output == NULL || args->length == 0) {
                        ERROR_OUT(BAD_FUNC_ARG, exit_scke);
                    }

                    c16toa((word16)args->length, args->output);
                    args->encSz += args->length + OPAQUE16_LEN;

                    MakePSKPreMasterSecret(ssl->arrays, 0);
                    break;
                }
            #endif /* !NO_DH && !NO_PSK */
            #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                case ecdhe_psk_kea:
                {
                    /* validate args */
                    if (args->output == NULL || args->length > ENCRYPT_LEN) {
                        ERROR_OUT(BAD_FUNC_ARG, exit_scke);
                    }

                    /* place size of public key in output buffer */
                    *args->output = (byte)args->length;
                    args->encSz += args->length + OPAQUE8_LEN;

                    /* Create pre master secret is the concatenation of
                     * eccSize + eccSharedKey + pskSize + pskKey */
                    MakePSKPreMasterSecret(ssl->arrays, 0);
                    break;
                }
            #endif /* (HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448) && !NO_PSK */
            #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                case ecc_diffie_hellman_kea:
                {
                    if (args->encSecret == NULL) {
                        ret = BAD_STATE_E;
                        goto exit_scke;
                    }
                    else {
                        /* place size of public key in buffer */
                        *args->encSecret = (byte)args->encSz;
                        args->encSz += OPAQUE8_LEN;
                    }
                    break;
                }
            #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

                default:
                    ret = BAD_KEA_TYPE_E;
            } /* switch(ssl->specs.kea) */

            /* Check for error */
            if (ret != 0) {
                goto exit_scke;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_FINALIZE;
        } /* case TLS_ASYNC_VERIFY */
        FALL_THROUGH;

        case TLS_ASYNC_FINALIZE:
        {
            word32 tlsSz = 0;
            word32 idx = 0;

            if (ssl->options.tls || ssl->specs.kea == diffie_hellman_kea) {
                tlsSz = 2;
            }

            if (ssl->specs.kea == ecc_diffie_hellman_kea ||
                ssl->specs.kea == dhe_psk_kea ||
                ssl->specs.kea == ecdhe_psk_kea) { /* always off */
                tlsSz = 0;
            }

            idx = HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
            args->sendSz = (int)(args->encSz + tlsSz + idx);

        #ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
                idx    += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
                args->sendSz += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
            }
        #endif

            if (IsEncryptionOn(ssl, 1)) {
            #if defined(WOLFSSL_DTLS) && defined(WOLFSSL_DTLS_MTU)
                /* Use exact cipher overhead for the MTU pre-flight check.
                 * MAX_MSG_EXTRA is an upper bound that can exceed a small MTU,
                 * while the actual message fits within it. */
                if (ssl->options.dtls)
                    args->sendSz += cipherExtraData(ssl);
                else
            #endif
                    args->sendSz += MAX_MSG_EXTRA;
            }

            /* check for available size */
            if ((ret = CheckAvailableSize(ssl, args->sendSz)) != 0)
                goto exit_scke;

            /* get output buffer */
            args->output = GetOutputBuffer(ssl);

            AddHeaders(args->output, args->encSz + tlsSz, client_key_exchange, ssl);

            if (tlsSz) {
                c16toa((word16)args->encSz, &args->output[idx]);
                idx += OPAQUE16_LEN;
            }
            XMEMCPY(args->output + idx, args->encSecret, args->encSz);
            idx += args->encSz;

            if (IsEncryptionOn(ssl, 1)) {
                int recordHeaderSz = RECORD_HEADER_SZ;

                if (ssl->options.dtls)
                    recordHeaderSz += DTLS_RECORD_EXTRA;
                args->inputSz = (int)idx - recordHeaderSz; /* buildmsg adds rechdr */
                args->input = (byte*)XMALLOC((size_t)args->inputSz, ssl->heap,
                                                       DYNAMIC_TYPE_IN_BUFFER);
                if (args->input == NULL) {
                    ERROR_OUT(MEMORY_E, exit_scke);
                }

                XMEMCPY(args->input, args->output + recordHeaderSz,
                                                                args->inputSz);
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_END;
        } /* case TLS_ASYNC_FINALIZE */
        FALL_THROUGH;

        case TLS_ASYNC_END:
        {
            if (IsEncryptionOn(ssl, 1)) {
            #ifdef WOLFSSL_DTLS
                if (IsDtlsNotSctpMode(ssl) &&
                        (ret = DtlsMsgPoolSave(ssl, args->input, args->inputSz, client_key_exchange)) != 0) {
                    goto exit_scke;
                }
            #endif
                ret = BuildMessage(ssl, args->output, args->sendSz,
                            args->input, args->inputSz, handshake, 1, 0, 0, CUR_ORDER);
                XFREE(args->input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
                args->input = NULL; /* make sure its not double free'd on cleanup */

                if (ret >= 0) {
                    args->sendSz = ret;
                    ret = 0;
                }
            }
            else {
            #ifdef WOLFSSL_DTLS
                if (IsDtlsNotSctpMode(ssl)) {
                    if ((ret = DtlsMsgPoolSave(ssl, args->output, args->sendSz, client_key_exchange)) != 0) {
                        goto exit_scke;
                    }
                }
                if (ssl->options.dtls)
                    DtlsSEQIncrement(ssl, CUR_ORDER);
            #endif
                ret = HashOutput(ssl, args->output, args->sendSz, 0);
            }

            if (ret != 0) {
                goto exit_scke;
            }

        #if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
            if (ssl->hsInfoOn)
                AddPacketName(ssl, "ClientKeyExchange");
            if (ssl->toInfoOn) {
                ret = AddPacketInfo(ssl, "ClientKeyExchange", handshake,
                         args->output, args->sendSz, WRITE_PROTO, 0, ssl->heap);
                if (ret != 0) {
                    goto exit_scke;
                }
            }
        #endif

            ssl->buffers.outputBuffer.length += (word32)args->sendSz;

            if (!ssl->options.groupMessages) {
                ret = SendBuffered(ssl);
            }
            if (ret == 0 || ret == WC_NO_ERR_TRACE(WANT_WRITE)) {
                int tmpRet = MakeMasterSecret(ssl);
                if (tmpRet != 0) {
                    ret = tmpRet;   /* save WANT_WRITE unless more serious */
                }
                ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
                ssl->options.buildingMsg = 0;
            }
        #if defined(OPENSSL_EXTRA) && defined(HAVE_SECRET_CALLBACK)
            if (ssl->keyLogCb != NULL) {
                int secretSz = SECRET_LEN;
                ret = ssl->keyLogCb(ssl, ssl->arrays->masterSecret, &secretSz,
                                                                        NULL);
                if (ret != 0 || secretSz != SECRET_LEN)
                    return SESSION_SECRET_CB_E;
            }
        #endif /* OPENSSL_EXTRA && HAVE_SECRET_CALLBACK */
            break;
        }
        default:
            ret = INPUT_CASE_ERROR;
    } /* switch(ssl->options.asyncState) */

exit_scke:

    WOLFSSL_LEAVE("SendClientKeyExchange", ret);
    WOLFSSL_END(WC_FUNC_CLIENT_KEY_EXCHANGE_SEND);

#ifdef WOLFSSL_ASYNC_IO
    /* Handle async operation */
    if (ret == WC_NO_ERR_TRACE(WC_PENDING_E) ||
        ret == WC_NO_ERR_TRACE(WANT_WRITE))
    {
        if (ssl->options.buildingMsg)
            return ret;
        /* If we have completed all states then we will not enter this function
         * again. We need to do clean up now. */
    }
#endif

    /* No further need for PMS */
    if (ssl->arrays->preMasterSecret != NULL) {
        ForceZero(ssl->arrays->preMasterSecret, ssl->arrays->preMasterSz);
    }
    ssl->arrays->preMasterSz = 0;

    /* Final cleanup */
#ifdef WOLFSSL_ASYNC_IO
    /* Cleanup async */
    FreeAsyncCtx(ssl, 0);
#else
    FreeSckeArgs(ssl, args);
#endif
    FreeKeyExchange(ssl);

    if (ret != 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
    }
    return ret;
}

#endif /* !WOLFSSL_NO_TLS12 */

#ifndef NO_CERTS

#ifndef WOLFSSL_NO_TLS12

#ifndef WOLFSSL_NO_CLIENT_AUTH
typedef struct ScvArgs {
    byte*  output; /* not allocated */
#ifndef NO_RSA
    byte*  verifySig;
#endif
    byte*  verify; /* not allocated */
    byte*  input;
    word32 idx;
    word32 extraSz;
    word32 sigSz;
    int    sendSz;
    int    inputSz;
    word32 length;
    byte   sigAlgo;
} ScvArgs;

static void FreeScvArgs(WOLFSSL* ssl, void* pArgs)
{
    ScvArgs* args = (ScvArgs*)pArgs;

    (void)ssl;

#ifndef NO_RSA
    XFREE(args->verifySig, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
    args->verifySig = NULL;
#endif
    XFREE(args->input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
    args->input = NULL;
}

/* handle generation of certificate_verify (15) */
int SendCertificateVerify(WOLFSSL* ssl)
{
    int ret = 0;
#ifdef WOLFSSL_ASYNC_IO
    ScvArgs* args = NULL;
    WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
#else
    ScvArgs  args[1];
#endif

    WOLFSSL_START(WC_FUNC_CERTIFICATE_VERIFY_SEND);
    WOLFSSL_ENTER("SendCertificateVerify");

#ifdef WOLFSSL_BLIND_PRIVATE_KEY
    wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask);
#endif

#ifdef WOLFSSL_ASYNC_IO
    if (ssl->async == NULL) {
        ssl->async = (struct WOLFSSL_ASYNC*)
                XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
                        DYNAMIC_TYPE_ASYNC);
        if (ssl->async == NULL)
            ERROR_OUT(MEMORY_E, exit_scv);
        XMEMSET(ssl->async, 0, sizeof(struct WOLFSSL_ASYNC));
    }
    args = (ScvArgs*)ssl->async->args;
#ifdef WOLFSSL_ASYNC_CRYPT
    /* BuildMessage does its own Pop */
    if (ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E) ||
            ssl->options.asyncState != TLS_ASYNC_END)
        ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState);
    if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
        /* Check for error */
        if (ret < 0)
            goto exit_scv;
    }
    else
#endif
    if (ssl->options.buildingMsg) {
        /* We should be in the sending state. */
        if (ssl->options.asyncState != TLS_ASYNC_END) {
            ret = BAD_STATE_E;
            goto exit_scv;
        }
    }
    else
#endif
    {
        /* Reset state */
        ret = 0;
        ssl->options.asyncState = TLS_ASYNC_BEGIN;
        XMEMSET(args, 0, sizeof(ScvArgs));
    #ifdef WOLFSSL_ASYNC_IO
        ssl->async->freeArgs = FreeScvArgs;
    #endif
    }

    switch(ssl->options.asyncState)
    {
        case TLS_ASYNC_BEGIN:
        {
            if (ssl->options.sendVerify == SEND_BLANK_CERT) {
            #ifdef WOLFSSL_BLIND_PRIVATE_KEY
                wolfssl_priv_der_blind_toggle(ssl->buffers.key,
                    ssl->buffers.keyMask);
            #endif
                return 0;  /* sent blank cert, can't verify */
            }

            args->sendSz = WC_MAX_CERT_VERIFY_SZ + MAX_MSG_EXTRA;
            if (IsEncryptionOn(ssl, 1)) {
                args->sendSz += MAX_MSG_EXTRA;
            }

            /* Use tmp buffer */
            args->input = (byte*)XMALLOC((size_t)args->sendSz,
                    ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
            if (args->input == NULL)
                ERROR_OUT(MEMORY_E, exit_scv);
            args->output = args->input;

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_BUILD;
        } /* case TLS_ASYNC_BEGIN */
        FALL_THROUGH;

        case TLS_ASYNC_BUILD:
        {
            ret = BuildCertHashes(ssl, &ssl->hsHashes->certHashes);
            if (ret != 0) {
                goto exit_scv;
            }

            if (ssl->buffers.key == NULL) {
            #ifdef HAVE_PK_CALLBACKS
                if (wolfSSL_CTX_IsPrivatePkSet(ssl->ctx))
                    args->length = (word16)GetPrivateKeySigSize(ssl);
                else
            #endif
                    ERROR_OUT(NO_PRIVATE_KEY, exit_scv);
            }
            else {
                /* Decode private key. */
                ret = DecodePrivateKey(ssl, &args->length);
                if (ret != 0) {
                    goto exit_scv;
                }
            }

            if (args->length == 0) {
                ERROR_OUT(NO_PRIVATE_KEY, exit_scv);
            }

            /* idx is used to track verify pointer offset to output */
            args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
            args->verify = &args->output[RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ];
            args->extraSz = 0;  /* tls 1.2 hash/sig */

            /* build encoded signature buffer */
            ssl->buffers.sig.length = MAX_ENCODED_SIG_SZ;
            ssl->buffers.sig.buffer = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ,
                                        ssl->heap, DYNAMIC_TYPE_SIGNATURE);
            if (ssl->buffers.sig.buffer == NULL) {
                ERROR_OUT(MEMORY_E, exit_scv);
            }

        #ifdef WOLFSSL_DTLS
            if (ssl->options.dtls) {
                args->idx += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
                args->verify += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
            }
        #endif

            if (!IsAtLeastTLSv1_2(ssl)) {
        #ifndef NO_OLD_TLS
            #ifndef NO_SHA
                /* old tls default */
                SetDigest(ssl, sha_mac);
            #endif
        #else
            #ifndef NO_SHA256
                /* new tls default */
                SetDigest(ssl, sha256_mac);
            #endif
        #endif /* !NO_OLD_TLS */
            }
            else {
                SetDigest(ssl, ssl->options.hashAlgo);
            }

            if (ssl->hsType == DYNAMIC_TYPE_RSA) {
        #ifdef WC_RSA_PSS
                if (IsAtLeastTLSv1_2(ssl) &&
                                (ssl->pssAlgo & (1 << ssl->options.hashAlgo))) {
                    args->sigAlgo = rsa_pss_sa_algo;
                }
                else
        #endif
                    args->sigAlgo = rsa_sa_algo;
            }
            else if (ssl->hsType == DYNAMIC_TYPE_ECC)
        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                if (ssl->buffers.keyType == sm2_sa_algo) {
                    args->sigAlgo = sm2_sa_algo;
                }
                else
        #endif
                {
                    args->sigAlgo = ecc_dsa_sa_algo;
                }
            else if (ssl->hsType == DYNAMIC_TYPE_ED25519)
                args->sigAlgo = ed25519_sa_algo;
            else if (ssl->hsType == DYNAMIC_TYPE_ED448)
                args->sigAlgo = ed448_sa_algo;

            if (IsAtLeastTLSv1_2(ssl)) {
                EncodeSigAlg(ssl->options.hashAlgo, args->sigAlgo,
                             args->verify);
                args->extraSz = HASH_SIG_SIZE;
                SetDigest(ssl, ssl->options.hashAlgo);
            }
        #ifndef NO_OLD_TLS
            else {
                /* if old TLS load MD5 and SHA hash as value to sign
                 * MD5 and SHA must be first two buffers in structure */
                XMEMCPY(ssl->buffers.sig.buffer,
                                (byte*)&ssl->hsHashes->certHashes, FINISHED_SZ);
            }
        #endif

        #ifndef NO_RSA
            if (args->sigAlgo == rsa_sa_algo) {
                ssl->buffers.sig.length = FINISHED_SZ;
                args->sigSz = ENCRYPT_LEN;

                if (IsAtLeastTLSv1_2(ssl)) {
                    ssl->buffers.sig.length = wc_EncodeSignature(
                            ssl->buffers.sig.buffer, ssl->buffers.digest.buffer,
                            ssl->buffers.digest.length,
                            TypeHash(ssl->options.hashAlgo));
                }

                /* prepend hdr */
                c16toa((word16)args->length, args->verify + args->extraSz);
            }
            #ifdef WC_RSA_PSS
            else if (args->sigAlgo == rsa_pss_sa_algo) {
                XMEMCPY(ssl->buffers.sig.buffer, ssl->buffers.digest.buffer,
                        ssl->buffers.digest.length);
                ssl->buffers.sig.length = ssl->buffers.digest.length;
                args->sigSz = ENCRYPT_LEN;

                /* prepend hdr */
                c16toa((word16)args->length, args->verify + args->extraSz);
            }
            #endif
        #endif /* !NO_RSA */
        #if defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)
            if (args->sigAlgo == ed25519_sa_algo) {
                ret = Ed25519CheckPubKey(ssl);
                if (ret != 0)
                    goto exit_scv;
            }
        #endif /* HAVE_ED25519 && !NO_ED25519_CLIENT_AUTH */
        #if defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)
            if (args->sigAlgo == ed448_sa_algo) {
                ret = Ed448CheckPubKey(ssl);
                if (ret != 0)
                    goto exit_scv;
            }
        #endif /* HAVE_ED448 && !NO_ED448_CLIENT_AUTH */

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_DO;
        } /* case TLS_ASYNC_BUILD */
        FALL_THROUGH;

        case TLS_ASYNC_DO:
        {
        #ifdef HAVE_ECC
            if (ssl->hsType == DYNAMIC_TYPE_ECC) {
                ecc_key* key = (ecc_key*)ssl->hsKey;

            #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                if (args->sigAlgo == sm2_sa_algo) {
                    ret = Sm2wSm3Sign(ssl,
                        TLS12_SM2_SIG_ID, TLS12_SM2_SIG_ID_SZ,
                        ssl->hsHashes->messages, ssl->hsHashes->length,
                        ssl->buffers.sig.buffer,
                        (word32*)&ssl->buffers.sig.length,
                        key,
                #ifdef HAVE_PK_CALLBACKS
                        ssl->buffers.key
                #else
                        NULL
                #endif
                    );
                }
                else
            #endif
                {
                    ret = EccSign(ssl,
                        ssl->buffers.digest.buffer, ssl->buffers.digest.length,
                        ssl->buffers.sig.buffer,
                        (word32*)&ssl->buffers.sig.length,
                        key,
                #ifdef HAVE_PK_CALLBACKS
                        ssl->buffers.key
                #else
                        NULL
                #endif
                    );
                }
            }
        #endif /* HAVE_ECC */
        #if defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)
           if (ssl->hsType == DYNAMIC_TYPE_ED25519) {
                ed25519_key* key = (ed25519_key*)ssl->hsKey;

                ret = Ed25519Sign(ssl,
                    ssl->hsHashes->messages, ssl->hsHashes->length,
                    ssl->buffers.sig.buffer, (word32*)&ssl->buffers.sig.length,
                    key,
            #ifdef HAVE_PK_CALLBACKS
                    ssl->buffers.key
            #else
                    NULL
            #endif
                );
            }
        #endif /* HAVE_ED25519 && !NO_ED25519_CLIENT_AUTH */
        #if defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)
           if (ssl->hsType == DYNAMIC_TYPE_ED448) {
                ed448_key* key = (ed448_key*)ssl->hsKey;

                ret = Ed448Sign(ssl,
                    ssl->hsHashes->messages, ssl->hsHashes->length,
                    ssl->buffers.sig.buffer, (word32*)&ssl->buffers.sig.length,
                    key,
            #ifdef HAVE_PK_CALLBACKS
                    ssl->buffers.key
            #else
                    NULL
            #endif
                );
            }
        #endif /* HAVE_ED448 && !NO_ED448_CLIENT_AUTH */
        #if !defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
            !defined(WOLFSSL_RSA_VERIFY_ONLY)
            if (ssl->hsType == DYNAMIC_TYPE_RSA) {
                RsaKey* key = (RsaKey*)ssl->hsKey;

                /* restore verify pointer */
                args->verify = &args->output[args->idx];

                ret = RsaSign(ssl,
                    ssl->buffers.sig.buffer, ssl->buffers.sig.length,
                    args->verify + args->extraSz + VERIFY_HEADER, &args->sigSz,
                    args->sigAlgo, ssl->options.hashAlgo, key,
                    ssl->buffers.key
                );
            }
        #endif /* !NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY */

            /* Check for error */
            if (ret != 0) {
                goto exit_scv;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_VERIFY;
        } /* case TLS_ASYNC_DO */
        FALL_THROUGH;

        case TLS_ASYNC_VERIFY:
        {
            /* restore verify pointer */
            args->verify = &args->output[args->idx];

            switch (ssl->hsType) {
    #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448)
        #ifdef HAVE_ECC
                case DYNAMIC_TYPE_ECC:
            #ifdef WOLFSSL_CHECK_SIG_FAULTS
                {
                    ecc_key* key = (ecc_key*)ssl->hsKey;

                #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                    if (ssl->buffers.keyType == sm2_sa_algo) {
                    #ifdef HAVE_PK_CALLBACKS
                        buffer tmp;

                        tmp.length = ssl->buffers.key->length;
                        tmp.buffer = ssl->buffers.key->buffer;
                    #endif

                        ret = Sm3wSm2Verify(ssl,
                            TLS12_SM2_SIG_ID, TLS12_SM2_SIG_ID_SZ,
                            ssl->buffers.sig.buffer, ssl->buffers.sig.length,
                            ssl->buffers.digest.buffer,
                            ssl->buffers.digest.length, key,
                        #ifdef HAVE_PK_CALLBACKS
                            &tmp
                        #else
                            NULL
                        #endif
                        );
                    }
                    else
                #endif
                    {
                    #ifdef HAVE_PK_CALLBACKS
                        buffer tmp;

                        tmp.length = ssl->buffers.key->length;
                        tmp.buffer = ssl->buffers.key->buffer;
                    #endif

                        ret = EccVerify(ssl,
                            ssl->buffers.sig.buffer, ssl->buffers.sig.length,
                            ssl->buffers.digest.buffer,
                            ssl->buffers.digest.length, key,
                        #ifdef HAVE_PK_CALLBACKS
                            &tmp
                        #else
                            NULL
                        #endif
                        );
                    }
                    if (ret != 0) {
                        WOLFSSL_MSG("Failed to verify ECC signature");
                        goto exit_scv;
                    }
                }
                #if defined(HAVE_ED25519) || defined(HAVE_ED448)
                FALL_THROUGH;
                #endif
            #endif /* WOLFSSL_CHECK_SIG_FAULTS */
        #endif /* HAVE_ECC */
        #ifdef HAVE_ED25519
                case DYNAMIC_TYPE_ED25519:
        #endif
        #ifdef HAVE_ED448
                case DYNAMIC_TYPE_ED448:
        #endif
                    args->length = (word16)ssl->buffers.sig.length;
                    /* prepend hdr */
                    c16toa((word16)args->length, args->verify + args->extraSz);
                    XMEMCPY(args->verify + args->extraSz + VERIFY_HEADER,
                            ssl->buffers.sig.buffer, ssl->buffers.sig.length);
                    break;
    #endif /* HAVE_ECC || HAVE_ED25519 || HAVE_ED448 */
            #ifndef NO_RSA
                case DYNAMIC_TYPE_RSA:
                {
                    RsaKey* key = (RsaKey*)ssl->hsKey;

                    if (args->verifySig == NULL) {
                        args->verifySig = (byte*)XMALLOC(args->sigSz, ssl->heap,
                                          DYNAMIC_TYPE_SIGNATURE);
                        if (args->verifySig == NULL) {
                            ERROR_OUT(MEMORY_E, exit_scv);
                        }
                        XMEMCPY(args->verifySig, args->verify + args->extraSz +
                                                    VERIFY_HEADER, args->sigSz);
                    }

                    /* check for signature faults */
                    ret = VerifyRsaSign(ssl,
                        args->verifySig, args->sigSz,
                        ssl->buffers.sig.buffer, ssl->buffers.sig.length,
                        args->sigAlgo, ssl->options.hashAlgo, key,
                        ssl->buffers.key
                    );

                    /* free temporary buffer now */
                    if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
                        XFREE(args->verifySig, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
                        args->verifySig = NULL;
                    }
                    break;
                }
            #endif /* !NO_RSA */
                default:
                    break;
            }

            /* Check for error */
            if (ret != 0) {
                goto exit_scv;
            }

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_FINALIZE;
        } /* case TLS_ASYNC_VERIFY */
        FALL_THROUGH;

        case TLS_ASYNC_FINALIZE:
        {
            if (args->output == NULL) {
                ERROR_OUT(BUFFER_ERROR, exit_scv);
            }
            AddHeaders(args->output, (word32)args->length + args->extraSz +
                                        VERIFY_HEADER, certificate_verify, ssl);

            /* Advance state and proceed */
            ssl->options.asyncState = TLS_ASYNC_END;
        } /* case TLS_ASYNC_FINALIZE */
        FALL_THROUGH;

        case TLS_ASYNC_END:
        {
            ret = SendHandshakeMsg(ssl, args->output,
                (word32)args->length + args->extraSz + VERIFY_HEADER,
                certificate_verify, "CertificateVerify");
            if (ret != 0)
                goto exit_scv;

            break;
        }
        default:
            ret = INPUT_CASE_ERROR;
    } /* switch(ssl->options.asyncState) */

exit_scv:
#ifdef WOLFSSL_BLIND_PRIVATE_KEY
    if (ret == 0) {
        ret = wolfssl_priv_der_blind(ssl->rng, ssl->buffers.key,
            &ssl->buffers.keyMask);
    }
    else {
        wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask);
    }
#endif

    WOLFSSL_LEAVE("SendCertificateVerify", ret);
    WOLFSSL_END(WC_FUNC_CERTIFICATE_VERIFY_SEND);

#ifdef WOLFSSL_ASYNC_IO
    /* Handle async operation */
    if (ret == WC_NO_ERR_TRACE(WANT_WRITE)
#ifdef WOLFSSL_ASYNC_CRYPT
            || ret == WC_NO_ERR_TRACE(WC_PENDING_E)
#endif
            )
        return ret;
#endif /* WOLFSSL_ASYNC_IO */

    /* Digest is not allocated, so do this to prevent free */
    if(ssl->buffers.digest.buffer) {
        if (!ssl->options.dontFreeDigest) {
            /*This should not happen*/
            XFREE(ssl->buffers.digest.buffer,
                ssl->heap, DYNAMIC_TYPE_DIGEST);
        }
    }
    ssl->buffers.digest.buffer = NULL;
    ssl->buffers.digest.length = 0;
    ssl->options.dontFreeDigest = 0;

    /* Final cleanup */
#ifdef WOLFSSL_ASYNC_IO
    /* Cleanup async */
    FreeAsyncCtx(ssl, 0);
#else
    FreeScvArgs(ssl, args);
#endif
    FreeKeyExchange(ssl);

    if (ret != 0) {
        WOLFSSL_ERROR_VERBOSE(ret);
    }

    return ret;
}
#endif /* WOLFSSL_NO_CLIENT_AUTH */

#endif /* WOLFSSL_NO_TLS12 */

#endif /* NO_CERTS */


#ifdef HAVE_SESSION_TICKET
int SetTicket(WOLFSSL* ssl, const byte* ticket, word32 length)
{
    word32 sessIdLen = ID_LEN;

    if (!HaveUniqueSessionObj(ssl))
        return MEMORY_ERROR;

    /* Free old dynamic ticket if we already had one */
    if (ssl->session->ticketLenAlloc > 0) {
        XFREE(ssl->session->ticket, ssl->heap, DYNAMIC_TYPE_SESSION_TICK);
        ssl->session->ticket = ssl->session->staticTicket;
        ssl->session->ticketLenAlloc = 0;
    }

    if (length > sizeof(ssl->session->staticTicket)) {
        byte* sessionTicket =
                   (byte*)XMALLOC(length, ssl->heap, DYNAMIC_TYPE_SESSION_TICK);
        if (sessionTicket == NULL)
            return MEMORY_E;
        ssl->session->ticket = sessionTicket;
        ssl->session->ticketLenAlloc = (word16)length;
    }
    ssl->session->ticketLen = (word16)length;

    if (length > 0) {
        if (length < ID_LEN)
            sessIdLen = length;
        XMEMCPY(ssl->session->ticket, ticket, length);
        if (ssl->session_ticket_cb != NULL) {
            ssl->session_ticket_cb(ssl,
                                   ssl->session->ticket,
                                   ssl->session->ticketLen,
                                   ssl->session_ticket_ctx);
        }
        /* Create a fake sessionID based on the ticket, this will
         * supersede the existing session cache info. */
        ssl->options.haveSessionId = 1;
#ifdef WOLFSSL_TLS13
        if (ssl->options.tls1_3) {
            XMEMSET(ssl->session->sessionID, 0, ID_LEN);
            XMEMCPY(ssl->session->sessionID,
                                 ssl->session->ticket + length - sessIdLen,
                                 sessIdLen);
            ssl->session->sessionIDSz = ID_LEN;
        }
        else
#endif
        {
            XMEMSET(ssl->arrays->sessionID, 0, ID_LEN);
            XMEMCPY(ssl->arrays->sessionID,
                                 ssl->session->ticket + length - sessIdLen,
                                 sessIdLen);
            ssl->arrays->sessionIDSz = ID_LEN;
        }
    }

    return 0;
}

#ifndef WOLFSSL_NO_TLS12

/* handle processing of session_ticket (4) */
static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
    word32 size)
{
    word32 begin = *inOutIdx;
    word32 lifetime;
    word16 length;
    int    ret;

    if (ssl->expect_session_ticket == 0) {
        WOLFSSL_MSG("Unexpected session ticket");
        WOLFSSL_ERROR_VERBOSE(SESSION_TICKET_EXPECT_E);
        return SESSION_TICKET_EXPECT_E;
    }

    if (OPAQUE32_LEN > size)
        return BUFFER_ERROR;

    ato32(input + *inOutIdx, &lifetime);
    *inOutIdx += OPAQUE32_LEN;

    if ((*inOutIdx - begin) + OPAQUE16_LEN > size)
        return BUFFER_ERROR;

    ato16(input + *inOutIdx, &length);
    *inOutIdx += OPAQUE16_LEN;

    if ((*inOutIdx - begin) + length > size)
        return BUFFER_ERROR;

    if ((ret = SetTicket(ssl, input + *inOutIdx, length)) != 0)
        return ret;
    *inOutIdx += length;
    if (length > 0) {
        ssl->timeout = lifetime;
        SetupSession(ssl);
#ifndef NO_SESSION_CACHE
        AddSession(ssl);
#endif
    }

    if (IsEncryptionOn(ssl, 0))
        *inOutIdx += ssl->keys.padSz;

    ssl->expect_session_ticket = 0;

    return 0;
}

#endif /* !WOLFSSL_NO_TLS12 */

#endif /* HAVE_SESSION_TICKET */

#endif /* !NO_WOLFSSL_CLIENT && !NO_TLS */
/* end client only parts */


#ifndef NO_CERTS

#if defined(WOLF_PRIVATE_KEY_ID) || defined(HAVE_PK_CALLBACKS)
    int GetPrivateKeySigSize(WOLFSSL* ssl)
    {
        int sigSz = 0;

        if (ssl == NULL)
            return 0;

        switch (ssl->buffers.keyType) {
        #ifndef NO_RSA
        #ifdef WC_RSA_PSS
            case rsa_pss_sa_algo:
        #endif
            case rsa_sa_algo:
                sigSz = ssl->buffers.keySz;
                ssl->hsType = DYNAMIC_TYPE_RSA;
                break;
        #endif
        #ifdef HAVE_ECC
            case ecc_dsa_sa_algo:
                sigSz = wc_ecc_sig_size_calc(ssl->buffers.keySz);
                ssl->hsType = DYNAMIC_TYPE_ECC;
                break;
        #endif
        #ifdef HAVE_ED25519
            case ed25519_sa_algo:
                sigSz = ED25519_SIG_SIZE; /* fixed known value */
                ssl->hsType = DYNAMIC_TYPE_ED25519;
                break;
        #endif
        #ifdef HAVE_ED448
            case ed448_sa_algo:
                sigSz = ED448_SIG_SIZE; /* fixed known value */
                ssl->hsType = DYNAMIC_TYPE_ED448;
                break;
        #endif
            default:
                break;
        }
        return sigSz;
    }
#endif /* WOLF_PRIVATE_KEY_ID || HAVE_PK_CALLBACKS */

#endif /* !NO_CERTS */

#ifdef HAVE_ECC
    /* returns the WOLFSSL_* version of the curve from the OID sum */
    word16 GetCurveByOID(int oidSum) {
        switch(oidSum) {
    #if (defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 160
        #ifndef NO_ECC_SECP
            case ECC_SECP160R1_OID:
                return WOLFSSL_ECC_SECP160R1;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_SECPR2
            case ECC_SECP160R2_OID:
                return WOLFSSL_ECC_SECP160R2;
        #endif /* HAVE_ECC_SECPR2 */
        #ifdef HAVE_ECC_KOBLITZ
            case ECC_SECP160K1_OID:
                return WOLFSSL_ECC_SECP160K1;
        #endif /* HAVE_ECC_KOBLITZ */
    #endif
    #if (defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 192
        #ifndef NO_ECC_SECP
            case ECC_SECP192R1_OID:
                return WOLFSSL_ECC_SECP192R1;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_KOBLITZ
            case ECC_SECP192K1_OID:
                return WOLFSSL_ECC_SECP192K1;
        #endif /* HAVE_ECC_KOBLITZ */
    #endif
    #if (defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 224
        #ifndef NO_ECC_SECP
            case ECC_SECP224R1_OID:
                return WOLFSSL_ECC_SECP224R1;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_KOBLITZ
            case ECC_SECP224K1_OID:
                return WOLFSSL_ECC_SECP224K1;
        #endif /* HAVE_ECC_KOBLITZ */
    #endif
    #if (!defined(NO_ECC256)  || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256
        #ifndef NO_ECC_SECP
            case ECC_SECP256R1_OID:
                return WOLFSSL_ECC_SECP256R1;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_KOBLITZ
            case ECC_SECP256K1_OID:
                return WOLFSSL_ECC_SECP256K1;
        #endif /* HAVE_ECC_KOBLITZ */
        #ifdef HAVE_ECC_BRAINPOOL
            case ECC_BRAINPOOLP256R1_OID:
                return WOLFSSL_ECC_BRAINPOOLP256R1;
        #endif /* HAVE_ECC_BRAINPOOL */
        #ifdef WOLFSSL_SM2
            case ECC_SM2P256V1_OID:
                return WOLFSSL_ECC_SM2P256V1;
        #endif /* WOLFSSL_SM2 */
    #endif
    #if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384
        #ifndef NO_ECC_SECP
            case ECC_SECP384R1_OID:
                return WOLFSSL_ECC_SECP384R1;
        #endif /* !NO_ECC_SECP */
        #ifdef HAVE_ECC_BRAINPOOL
            case ECC_BRAINPOOLP384R1_OID:
                return WOLFSSL_ECC_BRAINPOOLP384R1;
        #endif /* HAVE_ECC_BRAINPOOL */
    #endif
    #if (defined(HAVE_ECC512) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 512
        #ifdef HAVE_ECC_BRAINPOOL
            case ECC_BRAINPOOLP512R1_OID:
                return WOLFSSL_ECC_BRAINPOOLP512R1;
        #endif /* HAVE_ECC_BRAINPOOL */
    #endif
    #if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521
        #ifndef NO_ECC_SECP
            case ECC_SECP521R1_OID:
                return WOLFSSL_ECC_SECP521R1;
        #endif /* !NO_ECC_SECP */
    #endif
            default:
                WOLFSSL_MSG("Curve OID not compiled in or implemented");
                return 0;
        }
    }
#endif /* HAVE_ECC */

#ifdef WOLFSSL_HAVE_MLKEM
    /* Returns 1 when the given group is a PQC group, 0 otherwise. */
    int NamedGroupIsPqc(int group)
    {
        switch (group) {
        #ifndef WOLFSSL_NO_ML_KEM
            case WOLFSSL_ML_KEM_512:
            case WOLFSSL_ML_KEM_768:
            case WOLFSSL_ML_KEM_1024:
        #endif
        #ifdef WOLFSSL_MLKEM_KYBER
            case WOLFSSL_KYBER_LEVEL1:
            case WOLFSSL_KYBER_LEVEL3:
            case WOLFSSL_KYBER_LEVEL5:
        #endif
                return 1;
            default:
                return 0;
        }
    }

    /* Returns 1 when the given group is a PQC hybrid group, 0 otherwise. */
    int NamedGroupIsPqcHybrid(int group)
    {
    #if defined(WOLFSSL_PQC_HYBRIDS) || defined(WOLFSSL_EXTRA_PQC_HYBRIDS) || \
        defined(WOLFSSL_MLKEM_KYBER)

        switch (group) {
        #ifndef WOLFSSL_NO_ML_KEM
            #ifdef WOLFSSL_PQC_HYBRIDS
            case WOLFSSL_SECP256R1MLKEM768:
            case WOLFSSL_X25519MLKEM768:
            case WOLFSSL_SECP384R1MLKEM1024:
            #endif /* WOLFSSL_PQC_HYBRIDS */
            #ifdef WOLFSSL_EXTRA_PQC_HYBRIDS
            case WOLFSSL_SECP256R1MLKEM512:
            case WOLFSSL_SECP384R1MLKEM768:
            case WOLFSSL_SECP521R1MLKEM1024:
            case WOLFSSL_X25519MLKEM512:
            case WOLFSSL_X448MLKEM768:
            #ifdef WOLFSSL_ML_KEM_USE_OLD_IDS
            case WOLFSSL_P256_ML_KEM_512_OLD:
            case WOLFSSL_P384_ML_KEM_768_OLD:
            case WOLFSSL_P521_ML_KEM_1024_OLD:
            #endif
            #endif /* WOLFSSL_EXTRA_PQC_HYBRIDS */
        #endif
        #ifdef WOLFSSL_MLKEM_KYBER
            case WOLFSSL_P256_KYBER_LEVEL3:
            case WOLFSSL_X25519_KYBER_LEVEL3:
            case WOLFSSL_P256_KYBER_LEVEL1:
            case WOLFSSL_P384_KYBER_LEVEL3:
            case WOLFSSL_P521_KYBER_LEVEL5:
            case WOLFSSL_X25519_KYBER_LEVEL1:
            case WOLFSSL_X448_KYBER_LEVEL3:
        #endif
                return 1;
            default:
                return 0;
        }
    #else
        (void)group;
        return 0;
    #endif
    }
#endif /* WOLFSSL_HAVE_MLKEM */

    int TranslateErrorToAlert(int err)
    {
        switch (err) {
            case WC_NO_ERR_TRACE(BUFFER_ERROR):
                return decode_error;
            case WC_NO_ERR_TRACE(EXT_NOT_ALLOWED):
            case WC_NO_ERR_TRACE(PEER_KEY_ERROR):
            case WC_NO_ERR_TRACE(ECC_PEERKEY_ERROR):
            case WC_NO_ERR_TRACE(BAD_KEY_SHARE_DATA):
            case WC_NO_ERR_TRACE(PSK_KEY_ERROR):
            case WC_NO_ERR_TRACE(INVALID_PARAMETER):
            case WC_NO_ERR_TRACE(HRR_COOKIE_ERROR):
            case WC_NO_ERR_TRACE(BAD_BINDER):
            case WC_NO_ERR_TRACE(DUPLICATE_TLS_EXT_E):
                return illegal_parameter;
            case WC_NO_ERR_TRACE(INCOMPLETE_DATA):
                return missing_extension;
            case WC_NO_ERR_TRACE(MATCH_SUITE_ERROR):
            case WC_NO_ERR_TRACE(MISSING_HANDSHAKE_DATA):
                return handshake_failure;
            case WC_NO_ERR_TRACE(VERSION_ERROR):
                return wolfssl_alert_protocol_version;
            case WC_NO_ERR_TRACE(BAD_CERTIFICATE_STATUS_ERROR):
                return bad_certificate_status_response;
            case WC_NO_ERR_TRACE(OUT_OF_ORDER_E):
                return unexpected_message;
            default:
                return invalid_alert;
        }
    }

    /* search suites for specific one, idx on success, negative on error */
    int FindSuite(const Suites* suites, byte first, byte second)
    {
        int i;

        if (suites == NULL || suites->suiteSz == 0) {
            WOLFSSL_MSG("Suites pointer error or suiteSz 0");
            return SUITES_ERROR;
        }

        for (i = 0; i < suites->suiteSz-1; i += SUITE_LEN) {
            if (suites->suites[i]   == first &&
                suites->suites[i+1] == second )
                return i;
        }

        return MATCH_SUITE_ERROR;
    }


#if !defined(NO_WOLFSSL_SERVER) && !defined(NO_TLS)

#ifndef WOLFSSL_NO_TLS12

#ifndef NO_PSK
static int AddPSKtoPreMasterSecret(WOLFSSL* ssl)
{
    int ret = 0;
    /* Use the PSK hint to look up the PSK and add it to the
     * preMasterSecret here. */
    ssl->arrays->psk_keySz = ssl->options.server_psk_cb(ssl,
        ssl->arrays->client_identity, ssl->arrays->psk_key,
        MAX_PSK_KEY_LEN);

    if (ssl->arrays->psk_keySz == 0 ||
        (ssl->arrays->psk_keySz > MAX_PSK_KEY_LEN &&
    (int)ssl->arrays->psk_keySz != WC_NO_ERR_TRACE(USE_HW_PSK))) {
    #if defined(WOLFSSL_EXTRA_ALERTS) || defined(WOLFSSL_PSK_IDENTITY_ALERT)
        SendAlert(ssl, alert_fatal, unknown_psk_identity);
    #endif
        ret = 1;
    }
    if (ret == 0)
        /* Pre-shared Key for peer authentication. */
        ssl->options.peerAuthGood = 1;
    return ret;
}
#endif /* NO_PSK */

    static int getSessionID(WOLFSSL* ssl)
    {
        int sessIdSz = 0;
        (void)ssl;
#ifndef NO_SESSION_CACHE
        /* if no session cache don't send a session ID */
        if (!ssl->options.sessionCacheOff)
            sessIdSz = ID_LEN;
#endif
#ifdef HAVE_SESSION_TICKET
        /* we may be echoing an ID as part of session tickets */
        if (ssl->options.useTicket) {
            /* echo session id sz can be 0,32 or bogus len in between */
            sessIdSz = ssl->arrays->sessionIDSz;
            if (sessIdSz > ID_LEN) {
                WOLFSSL_MSG("Bad bogus session id len");
                return BUFFER_ERROR;
            }
        }
#endif /* HAVE_SESSION_TICKET */
        return sessIdSz;
    }

    /* handle generation of server_hello (2) */
    int SendServerHello(WOLFSSL* ssl)
    {
        int    ret;
        byte   *output;
        word16 length;
        word32 idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
        int    sendSz;
        byte   sessIdSz;

        WOLFSSL_START(WC_FUNC_SERVER_HELLO_SEND);
        WOLFSSL_ENTER("SendServerHello");

        ret = getSessionID(ssl);
        if (ret < 0)
            return ret;
        sessIdSz = (byte)ret;

        length = VERSION_SZ + RAN_LEN
               + ENUM_LEN + sessIdSz
               + SUITE_LEN
               + ENUM_LEN;

#ifdef HAVE_TLS_EXTENSIONS
        ret = TLSX_GetResponseSize(ssl, server_hello, &length);
        if (ret != 0)
            return ret;
#else
        if (ssl->options.haveEMS) {
            length += HELLO_EXT_SZ_SZ + HELLO_EXT_SZ;
        }
#endif

        sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
        #ifdef WOLFSSL_DTLS
        if (ssl->options.dtls) {
            idx    += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
            sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
        }
        #endif /* WOLFSSL_DTLS */

        if (IsEncryptionOn(ssl, 1))
            sendSz += MAX_MSG_EXTRA;

        /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
         * is not advanced yet */
        ssl->options.buildingMsg = 1;

        /* check for available size */
        if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
            return ret;

        /* get output buffer */
        output = GetOutputBuffer(ssl);
        AddHeaders(output, length, server_hello, ssl);

        /* now write to output */
        /* first version */
        output[idx++] = (byte)ssl->version.major;
        output[idx++] = (byte)ssl->version.minor;

        /* then random and session id */
        if (!ssl->options.resuming) {
            word32 genRanLen = RAN_LEN;

#ifdef WOLFSSL_TLS13
            if (TLSv1_3_Capable(ssl)) {
                /* TLS v1.3 capable server downgraded. */
                XMEMCPY(output + idx + RAN_LEN - (TLS13_DOWNGRADE_SZ + 1),
                        tls13Downgrade, TLS13_DOWNGRADE_SZ);
                output[idx + RAN_LEN - 1] = (byte)IsAtLeastTLSv1_2(ssl);
                genRanLen -= TLS13_DOWNGRADE_SZ + 1;
            }
            else
#endif
            if (ssl->ctx->method->version.major == SSLv3_MAJOR &&
                ssl->ctx->method->version.minor == TLSv1_2_MINOR &&
                (wolfSSL_get_options(ssl) & WOLFSSL_OP_NO_TLSv1_2) == 0 &&
                !IsAtLeastTLSv1_2(ssl)) {
                /* TLS v1.2 capable server downgraded. */
                XMEMCPY(output + idx + RAN_LEN - (TLS13_DOWNGRADE_SZ + 1),
                        tls13Downgrade, TLS13_DOWNGRADE_SZ);
                output[idx + RAN_LEN - 1] = 0;
                genRanLen -= TLS13_DOWNGRADE_SZ + 1;
            }

            /* generate random part */
            ret = wc_RNG_GenerateBlock(ssl->rng, output + idx, genRanLen);
            if (ret != 0)
                return ret;
            XMEMCPY(ssl->arrays->serverRandom, output + idx, RAN_LEN);
            idx += RAN_LEN;

            /* generate session id */
            output[idx++] = sessIdSz;
            ret = wc_RNG_GenerateBlock(ssl->rng, output + idx, sessIdSz);
            if (ret != 0)
                return ret;
            XMEMCPY(ssl->arrays->sessionID, output + idx, sessIdSz);
            ssl->arrays->sessionIDSz = sessIdSz;
        }
        else {
            /* If resuming, use info from SSL */
            XMEMCPY(output + idx, ssl->arrays->serverRandom, RAN_LEN);
            idx += RAN_LEN;
            output[idx++] = sessIdSz;
            XMEMCPY(output + idx, ssl->arrays->sessionID, sessIdSz);
        }
        idx += sessIdSz;

#ifdef SHOW_SECRETS
        {
            int j;
            printf("server random: ");
            for (j = 0; j < RAN_LEN; j++)
                printf("%02x", ssl->arrays->serverRandom[j]);
            printf("\n");
        }
#endif

        /* then cipher suite */
        output[idx++] = ssl->options.cipherSuite0;
        output[idx++] = ssl->options.cipherSuite;

        /* then compression */
        if (ssl->options.usingCompression)
            output[idx++] = ZLIB_COMPRESSION;
        else
            output[idx++] = NO_COMPRESSION;

        /* last, extensions */
#ifdef HAVE_TLS_EXTENSIONS
        {
            word16 offset = 0;
            ret = TLSX_WriteResponse(ssl, output + idx, server_hello, &offset);
            if (ret != 0)
                return ret;
            idx += offset;
        }
#else
#ifdef HAVE_EXTENDED_MASTER
        if (ssl->options.haveEMS) {
            c16toa(HELLO_EXT_SZ, output + idx);
            idx += HELLO_EXT_SZ_SZ;

            c16toa(HELLO_EXT_EXTMS, output + idx);
            idx += HELLO_EXT_TYPE_SZ;
            c16toa(0, output + idx);
            /*idx += HELLO_EXT_SZ_SZ;*/
            /* idx is not used after this point. uncomment the line above
             * if adding any more extensions in the future. */
        }
#endif
#endif

        ret = BuildMsgOrHashOutput(ssl, output, &sendSz, idx, server_hello,
                                   "ServerHello");
        if (ret != 0)
            return ret;

        ssl->options.serverState = SERVER_HELLO_COMPLETE;

        if (ssl->options.groupMessages)
            ret = 0;
        else
            ret = SendBuffered(ssl);

        WOLFSSL_LEAVE("SendServerHello", ret);
        WOLFSSL_END(WC_FUNC_SERVER_HELLO_SEND);

        return ret;
    }


#if defined(HAVE_ECC)

    static byte SetCurveId(ecc_key* key)
    {
        if (key == NULL || key->dp == NULL) {
            WOLFSSL_MSG("SetCurveId: Invalid key!");
            return 0;
        }

        return (byte)GetCurveByOID((int)key->dp->oidSum);
    }

#endif /* HAVE_ECC */

    typedef struct SskeArgs {
        byte*  output; /* not allocated */
    #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
        byte*  exportBuf;
    #endif
    #ifndef NO_RSA
        byte*  verifySig;
    #endif
        byte*  input;
        word32 idx;
        word32 tmpSigSz;
        word32 length;
        word32 sigSz;
    #if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
                                                                !defined(NO_RSA)
        word32 sigDataSz;
    #endif
    #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
        word32 exportSz;
    #endif
        word32 sendSz;
        int    inputSz;
    } SskeArgs;

    static void FreeSskeArgs(WOLFSSL* ssl, void* pArgs)
    {
        SskeArgs* args = (SskeArgs*)pArgs;

        (void)ssl;

        if (args == NULL)
            return;

    #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
        XFREE(args->exportBuf, ssl->heap, DYNAMIC_TYPE_DER);
        args->exportBuf = NULL;
    #endif
    #ifndef NO_RSA
        XFREE(args->verifySig, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
        args->verifySig = NULL;
    #endif

        if (args->input != NULL) {
            XFREE(args->input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
            args->input = NULL;
        }
    }

#if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) \
    || !defined(NO_PSK) \
    || (!defined(NO_DH) && (!defined(NO_RSA) || \
                        (defined(HAVE_ANON) && !defined(WOLFSSL_NO_TLS12))))
    static void SetSendSzWithDtlsExtra(WOLFSSL* ssl, SskeArgs* args)
    {
        args->sendSz = args->length + HANDSHAKE_HEADER_SZ +
                                            RECORD_HEADER_SZ;
    #ifdef WOLFSSL_DTLS
        if (ssl->options.dtls) {
            args->sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
            args->idx    += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
        }
    #endif
        if (IsEncryptionOn(ssl, 1)) {
            args->sendSz += MAX_MSG_EXTRA;
        }
    }
    static int AllocArgsInput(WOLFSSL* ssl, SskeArgs* args)
    {   /* Use tmp buffer */
        args->input = (byte*)XMALLOC(args->sendSz, ssl->heap,
                                     DYNAMIC_TYPE_IN_BUFFER);
        if (args->input == NULL)
            return MEMORY_E;
        args->output = args->input;
        return 0;
    }
#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 || !NO_PSK */

#ifndef NO_PSK
    static int LengthPlusServerHint(WOLFSSL* ssl, SskeArgs* args,
        word32* hintLen)
    {   /* include size part */
        *hintLen = (word32)XSTRLEN(ssl->arrays->server_hint);
        if (*hintLen > MAX_PSK_ID_LEN)
            return SERVER_HINT_ERROR;
        args->length += HINT_LEN_SZ + *hintLen;
        return 0;
    }
    static void AddServerHint(WOLFSSL* ssl, SskeArgs* args, word32 hintLen)
    {   /* key data */
        c16toa((word16)hintLen, args->output + args->idx);
        args->idx += HINT_LEN_SZ;
        XMEMCPY(args->output + args->idx, ssl->arrays->server_hint, hintLen);
        args->idx += hintLen;
    }
#endif /* !NO_PSK */

#if ((defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                defined(HAVE_CURVE448)) && !defined(NO_PSK))\
  || \
    (defined(HAVE_ECC) || \
        ((defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \
         (defined(HAVE_ED25519) || defined(HAVE_ED448) || \
         !defined(NO_RSA))))
    static int ExportEccTempKey(WOLFSSL* ssl, SskeArgs* args)
    {
        int ret = 0;
        /* curve type, named curve, length(1) */
        args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
        args->length = ENUM_LEN + CURVE_LEN + ENUM_LEN;

        /* Export temp ECC key and add to length */
        args->exportSz = MAX_EXPORT_ECC_SZ;
        args->exportBuf = (byte*)XMALLOC(MAX_EXPORT_ECC_SZ,
                                         ssl->heap, DYNAMIC_TYPE_DER);
        if (args->exportBuf == NULL)
            return MEMORY_E;
    #ifdef HAVE_CURVE25519
        if (ssl->ecdhCurveOID == ECC_X25519_OID) {
            if (wc_curve25519_export_public_ex(
                    (curve25519_key*)ssl->eccTempKey,
                    args->exportBuf, &args->exportSz,
                    EC25519_LITTLE_ENDIAN) != 0) {
                ret = ECC_EXPORT_ERROR;
            }
        }
        else
    #endif
    #ifdef HAVE_CURVE448
        if (ssl->ecdhCurveOID == ECC_X448_OID) {
            if (wc_curve448_export_public_ex(
                    (curve448_key*)ssl->eccTempKey,
                    args->exportBuf, &args->exportSz,
                    EC448_LITTLE_ENDIAN) != 0) {
                ret = ECC_EXPORT_ERROR;
            }
        }
        else
    #endif
        {
    #if defined(HAVE_ECC) && defined(HAVE_ECC_KEY_EXPORT)
            PRIVATE_KEY_UNLOCK();
            ret = wc_ecc_export_x963(ssl->eccTempKey,
                       args->exportBuf, &args->exportSz);
            PRIVATE_KEY_LOCK();
            if (ret != 0) {
                ret = ECC_EXPORT_ERROR;
            }
    #endif
        }
        args->length += args->exportSz;
        return ret;
    }

    static int AddCurveId(WOLFSSL* ssl, SskeArgs* args)
    {   /* record and message headers will be added later, when we're sure
           of the sig length */
        /* ECC key exchange data */
        args->output[args->idx++] = named_curve;
        args->output[args->idx++] = 0x00;          /* leading zero */
    #ifdef HAVE_CURVE25519
        if (ssl->ecdhCurveOID == ECC_X25519_OID)
            args->output[args->idx++] = WOLFSSL_ECC_X25519;
        else
    #endif
    #ifdef HAVE_CURVE448
        if (ssl->ecdhCurveOID == ECC_X448_OID)
            args->output[args->idx++] = WOLFSSL_ECC_X448;
        else
    #endif
        {
    #ifdef HAVE_ECC
            args->output[args->idx++] = SetCurveId(ssl->eccTempKey);
    #endif
        }
        args->output[args->idx++] = (byte)args->exportSz;
        XMEMCPY(args->output + args->idx, args->exportBuf, args->exportSz);
        return 0;
    }
    #endif /*HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448)
         && (!NO_PSK || HAVE_ED25519 || HAVE_ED448 || !NO_RSA)*/

    #if   (    !defined(NO_RSA) \
           &&  (defined(HAVE_ECC) || \
                    ((defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \
                     (defined(HAVE_ED25519) || defined(HAVE_ED448) || \
                      !defined(NO_RSA)))) ) \
       || (    !defined(NO_RSA) \
           && (!defined(NO_DH) && (!defined(NO_RSA) || \
                        (defined(HAVE_ANON) && !defined(WOLFSSL_NO_TLS12)))) )
    static int ReEncodeSig(WOLFSSL* ssl)
    {    /* For TLS 1.2 re-encode signature */
        if (IsAtLeastTLSv1_2(ssl)) {
            byte* encodedSig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ, ssl->heap,
                                              DYNAMIC_TYPE_DIGEST);
            if (encodedSig == NULL)
                return MEMORY_E;
            ssl->buffers.digest.length =
                wc_EncodeSignature(encodedSig, ssl->buffers.digest.buffer,
                                   ssl->buffers.digest.length,
                                   TypeHash(ssl->options.hashAlgo));
            /* Replace sig buffer with new one */
            if (!ssl->options.dontFreeDigest)
                XFREE(ssl->buffers.digest.buffer,
                      ssl->heap, DYNAMIC_TYPE_DIGEST);
            ssl->options.dontFreeDigest = 0;
            ssl->buffers.digest.buffer = encodedSig;
        }
        return 0;
    }
    #endif

    #if defined(HAVE_ECC) || \
        ((defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \
         (defined(HAVE_ED25519) || defined(HAVE_ED448) || !defined(NO_RSA)))
    static int SetKeyTypeAndDecodePrivateKey(WOLFSSL* ssl, SskeArgs* args)
    {
        int ret;
        word32 keySz = 0;

        switch(ssl->options.sigAlgo) {
        #ifndef NO_RSA
        #ifdef WC_RSA_PSS
            case rsa_pss_sa_algo:
        #endif
            case rsa_sa_algo:
            {
                ssl->buffers.keyType = rsa_sa_algo;
                ret = DecodePrivateKey(ssl, &keySz);
                args->tmpSigSz = (word32)keySz;
                break;
            }
        #endif /* !NO_RSA */
        #ifdef HAVE_ECC
        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
            case sm2_sa_algo:
        #endif
            case ecc_dsa_sa_algo:
            {
                ssl->buffers.keyType = ssl->options.sigAlgo;
                ret = DecodePrivateKey(ssl, &keySz);
                /* worst case estimate */
                args->tmpSigSz = keySz;
                break;
            }
        #endif
        #ifdef HAVE_ED25519
            case ed25519_sa_algo:
            {
                ssl->buffers.keyType = ed25519_sa_algo;
                ret = DecodePrivateKey(ssl, &keySz);
                /* worst case estimate */
                args->tmpSigSz = ED25519_SIG_SIZE;
                break;
            }
        #endif /* HAVE_ED25519 */
        #ifdef HAVE_ED448
            case ed448_sa_algo:
            {
                ssl->buffers.keyType = ed448_sa_algo;
                ret = DecodePrivateKey(ssl, &keySz);
                /* worst case estimate */
                args->tmpSigSz = ED448_SIG_SIZE;
                break;
            }
        #endif /* HAVE_ED448 */
            default:
                ret = ALGO_ID_E;  /* unsupported type */
        } /* switch(ssl->specs.sig_algo) */
        return ret;
    }
    #endif /* HAVE_ECC || (HAVE_CURVE25519 || HAVE_CURVE448) &&
               (HAVE_ED25519 || HAVE_ED448 || !NO_RSA) */

    #if !defined(NO_DH)\
         && (!defined(NO_PSK) || !defined(NO_RSA) || \
                             (defined(HAVE_ANON) && !defined(WOLFSSL_NO_TLS12)))
    static int SKE_DhPubLength(WOLFSSL* ssl)
    {
        return LENGTH_SZ * 3 + /* p, g, pub */
                 ssl->buffers.serverDH_P.length +
                 ssl->buffers.serverDH_G.length +
                 ssl->buffers.serverDH_Pub.length;
    }

    static int SKE_AddDhPub(Buffers* buffers, byte* output, int idx)
    {   /* add p, g, pub */
        c16toa((word16)buffers->serverDH_P.length, output + idx);
        idx += LENGTH_SZ;
        XMEMCPY(output + idx, buffers->serverDH_P.buffer,
                              buffers->serverDH_P.length);
        idx += buffers->serverDH_P.length;
        /*  g */
        c16toa((word16)buffers->serverDH_G.length, output + idx);
        idx += LENGTH_SZ;
        XMEMCPY(output + idx, buffers->serverDH_G.buffer,
                              buffers->serverDH_G.length);
        idx += buffers->serverDH_G.length;
        /*  pub */
        c16toa((word16)buffers->serverDH_Pub.length, output + idx);
        idx += LENGTH_SZ;
        XMEMCPY(output + idx, buffers->serverDH_Pub.buffer,
                              buffers->serverDH_Pub.length);
        idx += buffers->serverDH_Pub.length;
        return idx;
    }
    #endif

    /*
     * this #if mess is a case for relaxing the requirement that
     * static functions that are defined must always be used.
     *
     * as it is, we have to be careful to only define the function
     * when it is used, which is the || of all the cases below
     *
     */
#if (!defined(NO_DH) && (!defined(NO_RSA) || \
     (defined(HAVE_ANON) && !defined(WOLFSSL_NO_TLS12)))) || \
    defined(HAVE_ECC) || \
    ((defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \
     (defined(HAVE_ED25519) || defined(HAVE_ED448) || !defined(NO_RSA)))
    static int EncodeSigAlgAndGetHashType(WOLFSSL* ssl, SskeArgs* args,
                                          enum wc_HashType* hashType)
    {   /* Determine hash type */
        if (IsAtLeastTLSv1_2(ssl)) {
            EncodeSigAlg(ssl->options.hashAlgo,
                         ssl->options.sigAlgo,
                         &args->output[args->idx]);
            args->idx += 2;
            *hashType = HashAlgoToType(ssl->options.hashAlgo);
            if (*hashType == WC_HASH_TYPE_NONE)
                return ALGO_ID_E;
        } else {
            /* only using sha and md5 for rsa */
        #ifndef NO_OLD_TLS
            *hashType = WC_HASH_TYPE_SHA;
            if (ssl->options.sigAlgo == rsa_sa_algo)
                *hashType = WC_HASH_TYPE_MD5_SHA;
        #else
            return ALGO_ID_E;
        #endif
        }
        return 0;
    }
#endif

    /* handle generation of server_key_exchange (12) */
    int SendServerKeyExchange(WOLFSSL* ssl)
    {
        int ret = 0;
    #ifdef WOLFSSL_ASYNC_IO
        SskeArgs* args = NULL;
        WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
    #else
        SskeArgs  args[1];
    #endif

        WOLFSSL_START(WC_FUNC_SERVER_KEY_EXCHANGE_SEND);
        WOLFSSL_ENTER("SendServerKeyExchange");

    #ifdef WOLFSSL_BLIND_PRIVATE_KEY
        wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask);
    #endif

    #ifdef WOLFSSL_ASYNC_IO
        if (ssl->async == NULL) {
            ssl->async = (struct WOLFSSL_ASYNC*)
                    XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
                            DYNAMIC_TYPE_ASYNC);
            if (ssl->async == NULL)
                ERROR_OUT(MEMORY_E, exit_sske);
            XMEMSET(ssl->async, 0, sizeof(struct WOLFSSL_ASYNC));
        }
        args = (SskeArgs*)ssl->async->args;
    #ifdef WOLFSSL_ASYNC_CRYPT
        ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState);
        if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
            /* Check for error */
            if (ret < 0)
                goto exit_sske;
        }
        else
    #endif
        if (ssl->options.buildingMsg) {
            /* We should be in the sending state. */
            if (ssl->options.asyncState != TLS_ASYNC_END) {
                ret = BAD_STATE_E;
                goto exit_sske;
            }
        }
        else
    #endif
        {
            /* Reset state */
            ret = 0;
            ssl->options.asyncState = TLS_ASYNC_BEGIN;
            XMEMSET(args, 0, sizeof(SskeArgs));
        #ifdef WOLFSSL_ASYNC_IO
            ssl->async->freeArgs = FreeSskeArgs;
        #endif
        }

        switch(ssl->options.asyncState)
        {
            case TLS_ASYNC_BEGIN:
            {
                /* Do some checks / debug msgs */
                switch(ssl->specs.kea)
                {
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                    {
                        WOLFSSL_MSG("Using ephemeral ECDH PSK");
                        break;
                    }
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                #if defined(HAVE_ECC)
                    case ecc_diffie_hellman_kea:
                    {
                        if (ssl->specs.static_ecdh) {
                            WOLFSSL_MSG("Using Static ECDH, not sending "
                                        "ServerKeyExchange");
                            ERROR_OUT(0, exit_sske);
                        }

                        WOLFSSL_MSG("Using ephemeral ECDH");
                        break;
                    }
                #endif /* HAVE_ECC */
                }

                /* Preparing keys */
                switch(ssl->specs.kea)
                {
                #ifndef NO_PSK
                    case psk_kea:
                    {
                        /* Nothing to do in this sub-state */
                        break;
                    }
                #endif /* !NO_PSK */
                #if !defined(NO_DH) && (!defined(NO_PSK) || !defined(NO_RSA) \
                          || (defined(HAVE_ANON) && !defined(WOLFSSL_NO_TLS12)))
                #if !defined(NO_PSK)
                    case dhe_psk_kea:
                #endif
                #if !defined(NO_RSA) || (defined(HAVE_ANON) && \
                                         !defined(WOLFSSL_NO_TLS12))
                    case diffie_hellman_kea:
                #endif
#if (defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE)) && !defined(HAVE_PUBLIC_FFDHE)
                    if (ssl->namedGroup) {
                        word32 pSz = 0;

                        ret = wc_DhGetNamedKeyParamSize(ssl->namedGroup, &pSz,
                                NULL, NULL);
                        if (ret != 0)
                            goto exit_sske;

                        if (ssl->buffers.serverDH_Pub.buffer == NULL) {
                            /* Free'd in wolfSSL_ResourceFree and
                             * FreeHandshakeResources */
                            ssl->buffers.serverDH_Pub.buffer = (byte*)XMALLOC(
                                    pSz, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
                            if (ssl->buffers.serverDH_Pub.buffer == NULL) {
                                ERROR_OUT(MEMORY_E, exit_sske);
                            }
                            ssl->buffers.serverDH_Pub.length = pSz;
                        }
                        ssl->options.dhKeySz =(word16)pSz;

                        pSz = wc_DhGetNamedKeyMinSize(ssl->namedGroup);

                        if (ssl->buffers.serverDH_Priv.buffer == NULL) {
                            /* Free'd in wolfSSL_ResourceFree and
                             * FreeHandshakeResources */
                            ssl->buffers.serverDH_Priv.buffer = (byte*)XMALLOC(
                                    pSz, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY);
                            if (ssl->buffers.serverDH_Priv.buffer == NULL) {
                                ERROR_OUT(MEMORY_E, exit_sske);
                            }
                            ssl->buffers.serverDH_Priv.length = pSz;
                        }

                        ret = AllocKey(ssl, DYNAMIC_TYPE_DH,
                                            (void**)&ssl->buffers.serverDH_Key);
                        if (ret != 0) {
                            goto exit_sske;
                        }

                        ret = wc_DhSetNamedKey(ssl->buffers.serverDH_Key,
                                ssl->namedGroup);
                        if (ret != 0) {
                            goto exit_sske;
                        }
    #if !defined(WOLFSSL_OLD_PRIME_CHECK) && \
        !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST)
                        ssl->options.dhKeyTested = 1;
    #endif

                #ifdef HAVE_SECURE_RENEGOTIATION
                        /* Check that the DH public key buffer is large
                         * enough to hold the key. This may occur on a
                         * renegotiation when the key generated in the
                         * initial handshake is shorter than the key
                         * generated in the renegotiation. */
                        if (ssl->buffers.serverDH_Pub.length <
                                ssl->buffers.serverDH_P.length) {
                            byte* tmp = (byte*)XREALLOC(
                                    ssl->buffers.serverDH_Pub.buffer,
                                    ssl->buffers.serverDH_P.length +
                                        OPAQUE16_LEN,
                                    ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
                            if (tmp == NULL)
                                ERROR_OUT(MEMORY_E, exit_sske);
                            ssl->buffers.serverDH_Pub.buffer = tmp;
                            ssl->buffers.serverDH_Pub.length =
                                ssl->buffers.serverDH_P.length + OPAQUE16_LEN;
                        }
                #endif

                        ret = DhGenKeyPair(ssl, ssl->buffers.serverDH_Key,
                            ssl->buffers.serverDH_Priv.buffer,
                            (word32*)&ssl->buffers.serverDH_Priv.length,
                            ssl->buffers.serverDH_Pub.buffer,
                            (word32*)&ssl->buffers.serverDH_Pub.length);
                    #ifdef WOLFSSL_CHECK_MEM_ZERO
                        wc_MemZero_Add("DH private key buffer",
                            ssl->buffers.serverDH_Priv.buffer,
                            ssl->buffers.serverDH_Priv.length);
                    #endif
                        break;
                    }
                    else
#endif
                    {
                        /* Allocate DH key buffers and generate key */
                        if (ssl->buffers.serverDH_P.buffer == NULL ||
                            ssl->buffers.serverDH_G.buffer == NULL) {
                            ERROR_OUT(NO_DH_PARAMS, exit_sske);
                        }

                        if (ssl->buffers.serverDH_Pub.buffer == NULL) {
                            /* Free'd in wolfSSL_ResourceFree
                             * and FreeHandshakeResources
                             */
                            ssl->buffers.serverDH_Pub.buffer = (byte*)XMALLOC(
                                    ssl->buffers.serverDH_P.length,
                                    ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
                            if (ssl->buffers.serverDH_Pub.buffer == NULL) {
                                ERROR_OUT(MEMORY_E, exit_sske);
                            }
                            ssl->buffers.serverDH_Pub.length =
                                ssl->buffers.serverDH_P.length;
                        }

                        if (ssl->buffers.serverDH_Priv.buffer == NULL) {
                            /* Free'd in wolfSSL_ResourceFree
                             * and FreeHandshakeResources
                             */
                            ssl->buffers.serverDH_Priv.buffer = (byte*)XMALLOC(
                                    ssl->buffers.serverDH_P.length,
                                    ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY);
                            if (ssl->buffers.serverDH_Priv.buffer == NULL) {
                                ERROR_OUT(MEMORY_E, exit_sske);
                            }
                            ssl->buffers.serverDH_Priv.length =
                                ssl->buffers.serverDH_P.length;
                        }

                        ssl->options.dhKeySz =
                                (word16)ssl->buffers.serverDH_P.length;
                        CHECK_RET(ret, AllocKey(ssl, DYNAMIC_TYPE_DH,
                                           (void**)&ssl->buffers.serverDH_Key),
                                  exit_sske);
                        CHECK_RET(ret, DhSetKey(ssl), exit_sske);
                #ifdef HAVE_SECURE_RENEGOTIATION
                        /* Check that the DH public key buffer is large
                         * enough to hold the key. This may occur on a
                         * renegotiation when the key generated in the
                         * initial handshake is shorter than the key
                         * generated in the renegotiation. */
                        if (ssl->buffers.serverDH_Pub.length <
                                ssl->buffers.serverDH_P.length) {
                            byte* tmp = (byte*)XREALLOC(
                                    ssl->buffers.serverDH_Pub.buffer,
                                    ssl->buffers.serverDH_P.length +
                                        OPAQUE16_LEN,
                                    ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
                            if (tmp == NULL)
                                ERROR_OUT(MEMORY_E, exit_sske);
                            ssl->buffers.serverDH_Pub.buffer = tmp;
                            ssl->buffers.serverDH_Pub.length =
                                ssl->buffers.serverDH_P.length + OPAQUE16_LEN;
                        }
                #endif
                        ret = DhGenKeyPair(ssl, ssl->buffers.serverDH_Key,
                            ssl->buffers.serverDH_Priv.buffer,
                            (word32*)&ssl->buffers.serverDH_Priv.length,
                            ssl->buffers.serverDH_Pub.buffer,
                            (word32*)&ssl->buffers.serverDH_Pub.length);
                    #ifdef WOLFSSL_CHECK_MEM_ZERO
                        wc_MemZero_Add("DH private key buffer",
                            ssl->buffers.serverDH_Priv.buffer,
                            ssl->buffers.serverDH_Priv.length);
                    #endif
                        break;
                    }
                #endif /* !NO_DH && (!NO_PSK || !NO_RSA) */
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                        /* Fall through to create temp ECC key */
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                #if defined(HAVE_ECC) || \
                    ((defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \
                     (defined(HAVE_ED25519) || defined(HAVE_ED448) || \
                      !defined(NO_RSA)))
                    case ecc_diffie_hellman_kea:
                    {
                    #ifdef HAVE_CURVE25519
                        if (ssl->ecdhCurveOID == ECC_X25519_OID) {
                            /* need ephemeral key now, create it if missing */
                            if (ssl->eccTempKey == NULL) {
                                /* alloc/init on demand */
                                ret = AllocKey(ssl, DYNAMIC_TYPE_CURVE25519,
                                    (void**)&ssl->eccTempKey);
                                if (ret != 0) {
                                    goto exit_sske;
                                }
                            }

                            if (ssl->eccTempKeyPresent == 0) {
                                ret = X25519MakeKey(ssl,
                                        (curve25519_key*)ssl->eccTempKey, NULL);
                                if (ret == 0 ||
                                    ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                                    ssl->eccTempKeyPresent =
                                        DYNAMIC_TYPE_CURVE25519;
                                }
                                else {
                                    FreeKey(ssl, DYNAMIC_TYPE_CURVE25519,
                                    (void**)&ssl->eccTempKey);
                                }
                            }
                            break;
                        }
                    #endif
                    #ifdef HAVE_CURVE448
                        if (ssl->ecdhCurveOID == ECC_X448_OID) {
                            /* need ephemeral key now, create it if missing */
                            if (ssl->eccTempKey == NULL) {
                                /* alloc/init on demand */
                                ret = AllocKey(ssl, DYNAMIC_TYPE_CURVE448,
                                    (void**)&ssl->eccTempKey);
                                if (ret != 0) {
                                    goto exit_sske;
                                }
                            }

                            if (ssl->eccTempKeyPresent == 0) {
                                ret = X448MakeKey(ssl,
                                          (curve448_key*)ssl->eccTempKey, NULL);
                                if (ret == 0 ||
                                    ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                                    ssl->eccTempKeyPresent =
                                        DYNAMIC_TYPE_CURVE448;
                                }
                                else {
                                    FreeKey(ssl, DYNAMIC_TYPE_CURVE448,
                                    (void**)&ssl->eccTempKey);
                                }
                            }
                            break;
                        }
                    #endif
                    #ifdef HAVE_ECC
                        /* need ephemeral key now, create it if missing */
                        if (ssl->eccTempKey == NULL) {
                            /* alloc/init on demand */
                            ret = AllocKey(ssl, DYNAMIC_TYPE_ECC,
                                (void**)&ssl->eccTempKey);
                            if (ret != 0) {
                                goto exit_sske;
                            }
                        }

                        if (ssl->eccTempKeyPresent == 0) {
                            ret = EccMakeKey(ssl, ssl->eccTempKey, NULL);
                            if (ret == 0 ||
                                ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                                ssl->eccTempKeyPresent = DYNAMIC_TYPE_ECC;
                            }
                        }
                    #endif
                        break;
                    }
                #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
                    default:
                        /* Skip ServerKeyExchange */
                        goto exit_sske;
                } /* switch(ssl->specs.kea) */

                /* Check for error */
                if (ret != 0) {
                    goto exit_sske;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_BUILD;
            } /* case TLS_ASYNC_BEGIN */
            FALL_THROUGH;

            case TLS_ASYNC_BUILD:
            {
                switch(ssl->specs.kea)
                {
                #ifndef NO_PSK
                    case psk_kea:
                    {
                        word32 hintLen;

                        args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
                        if (ssl->arrays->server_hint[0] == 0) {
                            ERROR_OUT(0, exit_sske); /* don't send */
                        }
                        CHECK_RET(ret,
                                  LengthPlusServerHint(ssl, args, &hintLen),
                                  exit_sske);
                        SetSendSzWithDtlsExtra(ssl, args);
                        CHECK_RET(ret, AllocArgsInput(ssl, args), exit_sske);
                        AddHeaders(args->output, args->length,
                                                    server_key_exchange, ssl);
                        AddServerHint(ssl, args, hintLen);
                        break;
                    }
                #endif /* !NO_PSK */
                #if !defined(NO_DH) && !defined(NO_PSK)
                    case dhe_psk_kea:
                    {
                        word32 hintLen;

                        args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
                        args->length = SKE_DhPubLength(ssl);
                        CHECK_RET(ret,
                                  LengthPlusServerHint(ssl, args, &hintLen),
                                  exit_sske);
                        SetSendSzWithDtlsExtra(ssl, args);
                        CHECK_RET(ret, AllocArgsInput(ssl, args), exit_sske);
                        AddHeaders(args->output, args->length,
                                                    server_key_exchange, ssl);
                        AddServerHint(ssl, args, hintLen);
                        args->idx = SKE_AddDhPub(&ssl->buffers, args->output,
                                                 args->idx);
                        break;
                    }
                #endif /* !defined(NO_DH) && !defined(NO_PSK) */
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                    {
                        word32 hintLen = 0;

                        CHECK_RET(ret, ExportEccTempKey(ssl, args), exit_sske);
                        CHECK_RET(ret,
                                  LengthPlusServerHint(ssl, args, &hintLen),
                                  exit_sske);
                        SetSendSzWithDtlsExtra(ssl, args);
                        CHECK_RET(ret, AllocArgsInput(ssl, args), exit_sske);
                        AddServerHint(ssl, args, hintLen);
                        CHECK_RET(ret, AddCurveId(ssl, args), exit_sske);
                        break;
                    }
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                #if defined(HAVE_ECC) || \
                    ((defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \
                     (defined(HAVE_ED25519) || defined(HAVE_ED448) || \
                      !defined(NO_RSA)))
                    case ecc_diffie_hellman_kea:
                    {
                        enum wc_HashType hashType;
                        word32 preSigSz, preSigIdx;

                        CHECK_RET(ret, ExportEccTempKey(ssl, args), exit_sske);
                        preSigSz  = args->length;

                        if (ssl->buffers.key == NULL) {
                        #ifdef HAVE_PK_CALLBACKS
                            if (wolfSSL_CTX_IsPrivatePkSet(ssl->ctx)) {
                                args->tmpSigSz = (word32)GetPrivateKeySigSize(ssl);
                                if (args->tmpSigSz == 0) {
                                    ERROR_OUT(NO_PRIVATE_KEY, exit_sske);
                                }
                            }
                            else
                        #endif
                                ERROR_OUT(NO_PRIVATE_KEY, exit_sske);
                        }
                        else {
                            CHECK_RET(ret,
                                      SetKeyTypeAndDecodePrivateKey(ssl, args),
                                      exit_sske);
                        }

                        /* sig length */
                        args->length += LENGTH_SZ;
                        args->length += args->tmpSigSz;

                        if (IsAtLeastTLSv1_2(ssl)) {
                            args->length += HASH_SIG_SIZE;
                        }

                        SetSendSzWithDtlsExtra(ssl, args);
                        preSigIdx = args->idx;
                        CHECK_RET(ret, AllocArgsInput(ssl, args), exit_sske);
                        CHECK_RET(ret, AddCurveId(ssl, args), exit_sske);
                        args->idx += args->exportSz;

                        CHECK_RET(ret,
                                  EncodeSigAlgAndGetHashType(ssl, args,
                                                             &hashType),
                                  exit_sske);
                        /* Signature length will be written later,
                         * when we're sure what it is */
                    #ifdef HAVE_FUZZER
                        if (ssl->fuzzerCb) {
                            ssl->fuzzerCb(ssl, args->output + preSigIdx,
                                preSigSz, FUZZ_SIGNATURE, ssl->fuzzerCtx);
                        }
                    #endif

                        CHECK_RET(ret, HashSkeData(ssl, hashType,
                                              args->output + preSigIdx,
                                              preSigSz, ssl->options.sigAlgo),
                                  exit_sske);
                        args->sigSz = args->tmpSigSz;

                        /* Sign hash to create signature */
                        switch (ssl->options.sigAlgo)
                        {
                        #ifndef NO_RSA
                            case rsa_sa_algo:
                            {
                                CHECK_RET(ret, ReEncodeSig(ssl), exit_sske);
                                /* write sig size here */
                                c16toa((word16)args->sigSz,
                                    args->output + args->idx);
                                args->idx += LENGTH_SZ;
                                break;
                            }
                        #ifdef WC_RSA_PSS
                            case rsa_pss_sa_algo:
                                /* write sig size here */
                                c16toa((word16)args->sigSz,
                                    args->output + args->idx);
                                args->idx += LENGTH_SZ;
                                break;
                        #endif
                        #endif /* !NO_RSA */
                        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                            case sm2_sa_algo:
                        #endif
                            case ecc_dsa_sa_algo:
                            {
                                break;
                            }
                        #ifdef  HAVE_ED25519
                            case ed25519_sa_algo:
                                ret = Ed25519CheckPubKey(ssl);
                                if (ret != 0)
                                    goto exit_sske;
                                break;
                        #endif /* HAVE_ED25519 */
                        #ifdef  HAVE_ED448
                            case ed448_sa_algo:
                                ret = Ed448CheckPubKey(ssl);
                                if (ret != 0)
                                    goto exit_sske;
                                break;
                        #endif /* HAVE_ED448 */
                            default:
                                break;
                        } /* switch(ssl->specs.sig_algo) */
                        break;
                    }
                #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
                #if !defined(NO_DH) && (!defined(NO_RSA) || \
                             (defined(HAVE_ANON) && !defined(WOLFSSL_NO_TLS12)))
                    case diffie_hellman_kea:
                    {
                        enum wc_HashType hashType;
                        word32 preSigSz, preSigIdx;

                        args->idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
                        args->length = SKE_DhPubLength(ssl);
                        preSigSz  = args->length;

                        if (!ssl->options.usingAnon_cipher) {
                            word32 keySz = 0;

                            /* sig length */
                            args->length += LENGTH_SZ;

                            if (ssl->buffers.key == NULL) {
                            #ifdef HAVE_PK_CALLBACKS
                                if (wolfSSL_CTX_IsPrivatePkSet(ssl->ctx))
                                    keySz = (word16)GetPrivateKeySigSize(ssl);
                                else
                            #endif
                                    ERROR_OUT(NO_PRIVATE_KEY, exit_sske);
                            }
                            else
                            {
                                if (ssl->buffers.keyType == 0)
                                    ssl->buffers.keyType = rsa_sa_algo;
                                ret = DecodePrivateKey(ssl, &keySz);
                                if (ret != 0) {
                                    goto exit_sske;
                                }
                            }

                            /* test if keySz has error */
                            if (keySz == 0) {
                                ERROR_OUT(keySz, exit_sske);
                            }

                            args->tmpSigSz = (word32)keySz;
                            args->length += args->tmpSigSz;

                            if (IsAtLeastTLSv1_2(ssl)) {
                                args->length += HASH_SIG_SIZE;
                            }
                        }

                        SetSendSzWithDtlsExtra(ssl, args);
                        preSigIdx = args->idx;
                        CHECK_RET(ret, AllocArgsInput(ssl, args), exit_sske);
                        AddHeaders(args->output, args->length,
                                                    server_key_exchange, ssl);
                        args->idx = SKE_AddDhPub(&ssl->buffers, args->output,
                                                 args->idx);

                    #ifdef HAVE_FUZZER
                        if (ssl->fuzzerCb) {
                            ssl->fuzzerCb(ssl, args->output + preSigIdx,
                                preSigSz, FUZZ_SIGNATURE, ssl->fuzzerCtx);
                        }
                    #endif

                        if (ssl->options.usingAnon_cipher) {
                            break;
                        }

                        CHECK_RET(ret,
                                  EncodeSigAlgAndGetHashType(ssl, args,
                                                             &hashType),
                                  exit_sske);
                        /* signature size */
                        c16toa((word16)args->tmpSigSz,
                               args->output + args->idx);
                        args->idx += LENGTH_SZ;
                        CHECK_RET(ret, HashSkeData(ssl, hashType,
                                              args->output + preSigIdx,
                                              preSigSz, ssl->options.sigAlgo),
                                  exit_sske);
                        args->sigSz = args->tmpSigSz;

                        #ifndef NO_RSA
                        /* Sign hash to create signature */
                        if (ssl->options.sigAlgo == rsa_sa_algo)
                            ret = ReEncodeSig(ssl);
                        #endif /* NO_RSA */
                        break;
                    }
                #endif /* !defined(NO_DH) && !defined(NO_RSA) */
                    default:
                        break;
                } /* switch(ssl->specs.kea) */

                /* Check for error */
                if (ret != 0) {
                    goto exit_sske;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_DO;
            } /* case TLS_ASYNC_BUILD */
            FALL_THROUGH;

            case TLS_ASYNC_DO:
            {
                switch(ssl->specs.kea)
                {
                #ifndef NO_PSK
                    case psk_kea:
                    {
                        break;
                    }
                #endif /* !NO_PSK */
                #if !defined(NO_DH) && !defined(NO_PSK)
                    case dhe_psk_kea:
                    {
                        break;
                    }
                #endif /* !defined(NO_DH) && !defined(NO_PSK) */
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                    {
                        break;
                    }
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                #if defined(HAVE_ECC)  || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                    case ecc_diffie_hellman_kea:
                    {
                        /* Sign hash to create signature */
                        switch (ssl->options.sigAlgo)
                        {
                        #if !defined(NO_RSA) && \
                            !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
                            !defined(WOLFSSL_RSA_VERIFY_ONLY)
                        #ifdef WC_RSA_PSS
                            case rsa_pss_sa_algo:
                        #endif
                            case rsa_sa_algo:
                            {
                                RsaKey* key = (RsaKey*)ssl->hsKey;

                                ret = RsaSign(ssl,
                                    ssl->buffers.digest.buffer,
                                    ssl->buffers.digest.length,
                                    args->output + args->idx,
                                    &args->sigSz,
                                    ssl->options.sigAlgo, ssl->options.hashAlgo,
                                    key,
                                    ssl->buffers.key
                                );
                                break;
                            }
                        #endif /* !NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY
                                  && !WOLFSSL_RSA_VERIFY_ONLY */
                        #ifdef HAVE_ECC
                        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                            case sm2_sa_algo:
                            {
                                ecc_key* key = (ecc_key*)ssl->hsKey;

                                ret = Sm2wSm3Sign(ssl,
                                    TLS12_SM2_SIG_ID, TLS12_SM2_SIG_ID_SZ,
                                    ssl->buffers.sig.buffer,
                                    ssl->buffers.sig.length,
                                    args->output + LENGTH_SZ + args->idx,
                                    &args->sigSz,
                                    key,
                            #ifdef HAVE_PK_CALLBACKS
                                    ssl->buffers.key
                            #else
                                    NULL
                            #endif
                                );
                                break;
                            }
                        #endif
                            case ecc_dsa_sa_algo:
                            {
                                ecc_key* key = (ecc_key*)ssl->hsKey;

                                ret = EccSign(ssl,
                                    ssl->buffers.digest.buffer,
                                    ssl->buffers.digest.length,
                                    args->output + LENGTH_SZ + args->idx,
                                    &args->sigSz,
                                    key,
                            #ifdef HAVE_PK_CALLBACKS
                                    ssl->buffers.key
                            #else
                                    NULL
                            #endif
                                );
                                break;
                            }
                        #endif /* HAVE_ECC */
                        #ifdef HAVE_ED25519
                            case ed25519_sa_algo:
                            {
                                ed25519_key* key = (ed25519_key*)ssl->hsKey;

                                ret = Ed25519Sign(ssl,
                                    ssl->buffers.sig.buffer,
                                    ssl->buffers.sig.length,
                                    args->output + LENGTH_SZ + args->idx,
                                    &args->sigSz,
                                    key,
                            #ifdef HAVE_PK_CALLBACKS
                                    ssl->buffers.key
                            #else
                                    NULL
                            #endif
                                );
                                break;
                            }
                        #endif
                        #ifdef HAVE_ED448
                            case ed448_sa_algo:
                            {
                                ed448_key* key = (ed448_key*)ssl->hsKey;

                                ret = Ed448Sign(ssl,
                                    ssl->buffers.sig.buffer,
                                    ssl->buffers.sig.length,
                                    args->output + LENGTH_SZ + args->idx,
                                    &args->sigSz,
                                    key,
                            #ifdef HAVE_PK_CALLBACKS
                                    ssl->buffers.key
                            #else
                                    NULL
                            #endif
                                );
                                break;
                            }
                        #endif
                            default:
                                ERROR_OUT(ALGO_ID_E, exit_sske);
                        } /* switch(ssl->specs.sig_algo) */
                        break;
                    }
                #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
                #if !defined(NO_DH) && !defined(NO_RSA)
                    case diffie_hellman_kea:
                    {
                        /* Sign hash to create signature */
                        switch (ssl->options.sigAlgo)
                        {
                        #if !defined(NO_RSA) && \
                            !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
                            !defined(WOLFSSL_RSA_VERIFY_ONLY)
                        #ifdef WC_RSA_PSS
                            case rsa_pss_sa_algo:
                        #endif
                            case rsa_sa_algo:
                            {
                                RsaKey* key = (RsaKey*)ssl->hsKey;

                                if (ssl->options.usingAnon_cipher) {
                                    break;
                                }

                                ret = RsaSign(ssl,
                                    ssl->buffers.digest.buffer,
                                    ssl->buffers.digest.length,
                                    args->output + args->idx,
                                    &args->sigSz,
                                    ssl->options.sigAlgo, ssl->options.hashAlgo,
                                    key,
                                    ssl->buffers.key
                                );
                                break;
                            }
                        #endif /* !NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY
                                  && !WOLFSSL_RSA_VERIFY_ONLY */
                            default:
                                break;
                        } /* switch (ssl->options.sigAlgo) */

                        break;
                    }
                #endif /* !defined(NO_DH) && !defined(NO_RSA) */
                    default:
                        break;
                } /* switch(ssl->specs.kea) */

                /* Check for error */
                if (ret != 0) {
                    goto exit_sske;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_VERIFY;
            } /* case TLS_ASYNC_DO */
            FALL_THROUGH;

            case TLS_ASYNC_VERIFY:
            {
                switch(ssl->specs.kea)
                {
                #ifndef NO_PSK
                    case psk_kea:
                    {
                        /* Nothing to do in this sub-state */
                        break;
                    }
                #endif /* !NO_PSK */
                #if !defined(NO_DH) && !defined(NO_PSK)
                    case dhe_psk_kea:
                    {
                        /* Nothing to do in this sub-state */
                        break;
                    }
                #endif /* !defined(NO_DH) && !defined(NO_PSK) */
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) ||  \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                    {
                        /* Nothing to do in this sub-state */
                        break;
                    }
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                    case ecc_diffie_hellman_kea:
                    {
                        switch(ssl->options.sigAlgo)
                        {
                        #ifndef NO_RSA
                        #ifdef WC_RSA_PSS
                            case rsa_pss_sa_algo:
                        #endif
                            case rsa_sa_algo:
                            {
                                RsaKey* key = (RsaKey*)ssl->hsKey;

                                if (args->verifySig == NULL) {
                                    if (args->sigSz == 0) {
                                        ERROR_OUT(BAD_COND_E, exit_sske);
                                    }
                                    args->verifySig = (byte*)XMALLOC(
                                                    args->sigSz, ssl->heap,
                                                    DYNAMIC_TYPE_SIGNATURE);
                                    if (!args->verifySig) {
                                        ERROR_OUT(MEMORY_E, exit_sske);
                                    }
                                    XMEMCPY(args->verifySig,
                                        args->output + args->idx, args->sigSz);
                                }

                                /* check for signature faults */
                                ret = VerifyRsaSign(ssl,
                                    args->verifySig, args->sigSz,
                                    ssl->buffers.digest.buffer,
                                    ssl->buffers.digest.length,
                                    ssl->options.sigAlgo, ssl->options.hashAlgo,
                                    key, ssl->buffers.key
                                );
                                break;
                            }
                        #endif
                        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                            case sm2_sa_algo:
                        #endif /* WOLFSSL_SM2 */
                            case ecc_dsa_sa_algo:
                        #ifdef WOLFSSL_CHECK_SIG_FAULTS
                            {
                                ecc_key* key = (ecc_key*)ssl->hsKey;

                            #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                                if (ssl->options.sigAlgo == sm2_sa_algo) {
                                    ret = Sm2wSm3Verify(ssl,
                                        TLS12_SM2_SIG_ID, TLS12_SM2_SIG_ID_SZ,
                                        args->output + LENGTH_SZ + args->idx,
                                        args->sigSz,
                                        ssl->buffers.sig.buffer,
                                        ssl->buffers.sig.length,
                                        key,
                                    #ifdef HAVE_PK_CALLBACKS
                                        ssl->buffers.key
                                    #else
                                        NULL
                                    #endif
                                    );
                                }
                                else
                            #endif /* WOLFSSL_SM2 */
                                {
                                #ifdef HAVE_PK_CALLBACKS
                                    buffer tmp;

                                    tmp.length = ssl->buffers.key->length;
                                    tmp.buffer = ssl->buffers.key->buffer;
                                #endif

                                    ret = EccVerify(ssl,
                                        args->output + LENGTH_SZ + args->idx,
                                        args->sigSz,
                                        ssl->buffers.digest.buffer,
                                        ssl->buffers.digest.length,
                                        key,
                                    #ifdef HAVE_PK_CALLBACKS
                                        &tmp
                                    #else
                                        NULL
                                    #endif
                                    );
                                }
                                if (ret != 0) {
                                    WOLFSSL_MSG(
                                        "Failed to verify ECC signature");
                                    goto exit_sske;
                                }
                            }
                            #if defined(HAVE_ED25519) || defined(HAVE_ED448)
                            FALL_THROUGH;
                            #endif
                        #endif /*  WOLFSSL_CHECK_SIG_FAULTS */
                        #ifdef HAVE_ED25519
                            case ed25519_sa_algo:
                        #endif
                        #ifdef HAVE_ED448
                            case ed448_sa_algo:
                        #endif
                            {
                                /* Now that we know the real sig size, write it. */
                                c16toa((word16)args->sigSz,
                                                    args->output + args->idx);

                                /* And adjust length and sendSz from estimates */
                                args->length += args->sigSz - args->tmpSigSz;
                                args->sendSz += args->sigSz - args->tmpSigSz;
                                break;
                            }
                            default:
                                ERROR_OUT(ALGO_ID_E, exit_sske);  /* unsupported type */
                        } /* switch(ssl->specs.sig_algo) */
                        break;
                    }
                #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
                #if !defined(NO_DH) && !defined(NO_RSA)
                    case diffie_hellman_kea:
                    {
                        switch (ssl->options.sigAlgo)
                        {
                        #ifndef NO_RSA
                        #ifndef WC_RSA_PSS
                            case rsa_pss_sa_algo:
                        #endif
                            case rsa_sa_algo:
                            {
                                RsaKey* key = (RsaKey*)ssl->hsKey;

                                if (ssl->options.usingAnon_cipher) {
                                    break;
                                }

                                if (args->verifySig == NULL) {
                                    if (args->sigSz == 0) {
                                        ERROR_OUT(BAD_COND_E, exit_sske);
                                    }
                                    args->verifySig = (byte*)XMALLOC(
                                                      args->sigSz, ssl->heap,
                                                      DYNAMIC_TYPE_SIGNATURE);
                                    if (!args->verifySig) {
                                        ERROR_OUT(MEMORY_E, exit_sske);
                                    }
                                    XMEMCPY(args->verifySig,
                                        args->output + args->idx, args->sigSz);
                                }

                                /* check for signature faults */
                                ret = VerifyRsaSign(ssl,
                                    args->verifySig, args->sigSz,
                                    ssl->buffers.digest.buffer,
                                    ssl->buffers.digest.length,
                                    ssl->options.sigAlgo, ssl->options.hashAlgo,
                                    key, ssl->buffers.key
                                );
                                break;
                            }
                        #endif
                        } /* switch (ssl->options.sigAlgo) */
                        break;
                    }
                #endif /* !defined(NO_DH) && !defined(NO_RSA) */
                    default:
                        break;
                } /* switch(ssl->specs.kea) */

                /* Check for error */
                if (ret != 0) {
                    goto exit_sske;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_FINALIZE;
            } /* case TLS_ASYNC_VERIFY */
            FALL_THROUGH;

            case TLS_ASYNC_FINALIZE:
            {
            #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                if (ssl->specs.kea == ecdhe_psk_kea ||
                    ssl->specs.kea == ecc_diffie_hellman_kea) {
                    /* Check output to make sure it was set */
                    if (args->output) {
                        AddHeaders(args->output, args->length,
                                                    server_key_exchange, ssl);
                    }
                    else {
                        ERROR_OUT(BUFFER_ERROR, exit_sske);
                    }
                }
            #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_END;
            } /* case TLS_ASYNC_FINALIZE */
            FALL_THROUGH;

            case TLS_ASYNC_END:
            {
                ret = SendHandshakeMsg(ssl, args->output, args->length,
                        server_key_exchange, "ServerKeyExchange");
                if (ret != 0)
                    goto exit_sske;
                ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE;
                break;
            }
            default:
                ret = INPUT_CASE_ERROR;
        } /* switch(ssl->options.asyncState) */

    exit_sske:

    #ifdef WOLFSSL_BLIND_PRIVATE_KEY
        if (ret == 0) {
            ret = wolfssl_priv_der_blind(ssl->rng, ssl->buffers.key,
                &ssl->buffers.keyMask);
        }
        else {
            wolfssl_priv_der_blind_toggle(ssl->buffers.key,
                                          ssl->buffers.keyMask);
        }
    #endif

        WOLFSSL_LEAVE("SendServerKeyExchange", ret);
        WOLFSSL_END(WC_FUNC_SERVER_KEY_EXCHANGE_SEND);

    #ifdef WOLFSSL_ASYNC_IO
        /* Handle async operation */
        if (ret == WC_NO_ERR_TRACE(WANT_WRITE)
        #ifdef WOLFSSL_ASYNC_CRYPT
                || ret == WC_NO_ERR_TRACE(WC_PENDING_E)
        #endif
                )
            return ret;
    #endif /* WOLFSSL_ASYNC_IO */

        /* Final cleanup */
        if (
        #ifdef WOLFSSL_ASYNC_IO
            args != NULL &&
        #endif
            args->input != NULL) {
            XFREE(args->input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
            args->input = NULL;
        }
    #ifdef WOLFSSL_ASYNC_IO
        /* Cleanup async */
        FreeAsyncCtx(ssl, 0);
    #else
        FreeSskeArgs(ssl, args);
    #endif
        FreeKeyExchange(ssl);

        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(ret);
        }

        return ret;
    }

#endif /* !WOLFSSL_NO_TLS12 */

#ifdef WOLFSSL_TLS13
    /* Check if a cipher suite is a TLS 1.3 cipher suite
     * Returns 1 if TLS 1.3 cipher suite, 0 otherwise
     */
    static WC_INLINE int IsTls13CipherSuite(byte first, byte second)
    {
        (void)second;  /* Suppress unused parameter warning */

        /* TLS 1.3 cipher suites use TLS13_BYTE (0x13) as first byte */
        if (first == TLS13_BYTE)
            return 1;

#ifdef HAVE_NULL_CIPHER
        /* Special cases for integrity-only cipher suites */
        if (first == ECC_BYTE && (second == TLS_SHA256_SHA256 ||
                                  second == TLS_SHA384_SHA384))
            return 1;
#endif

#if (defined(WOLFSSL_SM4_GCM) || defined(WOLFSSL_SM4_CCM)) && \
     defined(WOLFSSL_SM3)
        /* SM4 cipher suites for TLS 1.3 */
        if (first == CIPHER_BYTE && (second == TLS_SM4_GCM_SM3 ||
                                     second == TLS_SM4_CCM_SM3))
            return 1;
#endif

        return 0;
    }
#endif /* WOLFSSL_TLS13 */

    /* Make sure server cert/key are valid for this suite, true on success
     * Returns 1 for valid server suite or 0 if not found
     * For asynchronous this can return WC_PENDING_E
     */
    static int VerifyServerSuite(const WOLFSSL* ssl, const Suites* suites,
                                 word16 idx, CipherSuite* cs, TLSX* extensions)
    {
    #ifndef NO_PSK
        int  havePSK = ssl->options.havePSK;
    #endif
        byte first;
        byte second;

        (void)cs;
        (void)extensions;

        WOLFSSL_ENTER("VerifyServerSuite");

        if (suites == NULL) {
            WOLFSSL_MSG("Suites pointer error");
            return 0;
        }

        first   = suites->suites[idx];
        second  = suites->suites[idx+1];

#ifdef WOLFSSL_TLS13
        /* When negotiating TLS 1.3, reject non-TLS 1.3 cipher suites */
        if (IsAtLeastTLSv1_3(ssl->version) &&
            ssl->options.side == WOLFSSL_SERVER_END) {
            if (!IsTls13CipherSuite(first, second)) {
                WOLFSSL_MSG("TLS 1.2 cipher suite not valid for TLS 1.3");
                return 0;
            }
        }
#endif /* WOLFSSL_TLS13 */

        if (CipherRequires(first, second, REQUIRES_RSA)) {
            WOLFSSL_MSG("Requires RSA");
            if (ssl->options.haveRSA == 0) {
                WOLFSSL_MSG("Don't have RSA");
                return 0;
            }
        }

        if (CipherRequires(first, second, REQUIRES_DHE)) {
            WOLFSSL_MSG("Requires DHE");
            if (ssl->options.haveDH == 0) {
                WOLFSSL_MSG("Don't have DHE");
                return 0;
            }
        }

        if (CipherRequires(first, second, REQUIRES_ECC)) {
            WOLFSSL_MSG("Requires ECC");
            if (ssl->options.haveECC == 0) {
                WOLFSSL_MSG("Don't have ECC");
                return 0;
            }
        }

        if (CipherRequires(first, second, REQUIRES_ECC_STATIC)) {
            WOLFSSL_MSG("Requires static ECC");
            if (ssl->options.haveStaticECC == 0) {
                WOLFSSL_MSG("Don't have static ECC");
                return 0;
            }
        }

        if (CipherRequires(first, second, REQUIRES_PSK)) {
            WOLFSSL_MSG("Requires PSK");
        #ifndef NO_PSK
            if (havePSK == 0)
        #endif
            {
                WOLFSSL_MSG("Don't have PSK");
                return 0;
            }
        }

        if (CipherRequires(first, second, REQUIRES_RSA_SIG)) {
            WOLFSSL_MSG("Requires RSA Signature");
            if (ssl->options.side == WOLFSSL_SERVER_END &&
                                           ssl->options.haveECDSAsig == 1) {
                WOLFSSL_MSG("Don't have RSA Signature");
                return 0;
            }
        }

#if !defined(WOLFSSL_OLDTLS_AEAD_CIPHERSUITES)
        if (CipherRequires(first, second, REQUIRES_AEAD)) {
            WOLFSSL_MSG("Requires AEAD");
            if (ssl->version.major == SSLv3_MAJOR &&
                                           ssl->version.minor < TLSv1_2_MINOR) {
                WOLFSSL_MSG("Version of SSL does not support AEAD ciphers");
                return 0;
            }

        }
#endif

#if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                       defined(HAVE_CURVE448)) && defined(HAVE_SUPPORTED_CURVES)
        if (!TLSX_ValidateSupportedCurves(ssl, first, second,
                                          &cs->ecdhCurveOID)) {
            WOLFSSL_MSG("Don't have matching curves");
            return 0;
        }
#endif

#ifdef WOLFSSL_TLS13
        if (IsAtLeastTLSv1_3(ssl->version) &&
                                      ssl->options.side == WOLFSSL_SERVER_END) {
    #ifdef HAVE_SUPPORTED_CURVES
            byte searched = 0;
            int ret = TLSX_KeyShare_Choose(ssl, extensions, first, second,
                                           &cs->clientKSE, &searched);

            if (ret == WC_NO_ERR_TRACE(MEMORY_E)) {
                WOLFSSL_MSG("TLSX_KeyShare_Choose() failed in "
                            "VerifyServerSuite() with MEMORY_E");
                return 0;
            }
            if (cs->clientKSE == NULL && searched) {
            #ifdef WOLFSSL_SEND_HRR_COOKIE
                /* If the CH contains a cookie then we need to send an alert to
                 * start from scratch. */
                if (TLSX_Find(extensions, TLSX_COOKIE) != NULL)
                    return INVALID_PARAMETER;
            #endif
                cs->doHelloRetry = 1;
            }
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                return ret;
        #endif
            if (!cs->doHelloRetry && ret != 0)
                return 0; /* not found */
    #endif /* HAVE_SUPPORTED_CURVES */
        }
        else if ((first == TLS13_BYTE) || ((first == ECC_BYTE) &&
                 ((second == TLS_SHA256_SHA256) ||
                  (second == TLS_SHA384_SHA384))) ||
                 ((first == CIPHER_BYTE) && ((second == TLS_SM4_GCM_SM3) ||
                  (second == TLS_SM4_CCM_SM3)))) {
            /* Can't negotiate TLS 1.3 cipher suites with lower protocol
             * version. */
            return 0;
        }
#endif /* WOLFSSL_TLS13 */

        return 1;
    }

    void refineSuites(const Suites* sslSuites, const Suites* peerSuites,
            Suites* outSuites, byte useClientOrder)
    {
        byte   suites[WOLFSSL_MAX_SUITE_SZ];
        word16 suiteSz = 0;
        word16 i;
        word16 j;

        XMEMSET(suites, 0, sizeof(suites));

        if (!useClientOrder) {
            /* Server order refining. */
            for (i = 0; i < sslSuites->suiteSz; i += 2) {
                for (j = 0; j < peerSuites->suiteSz; j += 2) {
                    if ((sslSuites->suites[i+0] == peerSuites->suites[j+0]) &&
                        (sslSuites->suites[i+1] == peerSuites->suites[j+1])) {
                        suites[suiteSz++] = peerSuites->suites[j+0];
                        suites[suiteSz++] = peerSuites->suites[j+1];
                        break;
                    }
                }
                if (suiteSz == WOLFSSL_MAX_SUITE_SZ)
                    break;
            }
        }
        else {
            /* Client order refining. */
            for (j = 0; j < peerSuites->suiteSz; j += 2) {
                for (i = 0; i < sslSuites->suiteSz; i += 2) {
                    if ((sslSuites->suites[i+0] == peerSuites->suites[j+0]) &&
                        (sslSuites->suites[i+1] == peerSuites->suites[j+1])) {
                        suites[suiteSz++] = peerSuites->suites[j+0];
                        suites[suiteSz++] = peerSuites->suites[j+1];
                        break;
                    }
                }
                if (suiteSz == WOLFSSL_MAX_SUITE_SZ)
                    break;
            }
        }

        outSuites->suiteSz = suiteSz;
        XMEMCPY(outSuites->suites, &suites, sizeof(suites));
    #ifdef WOLFSSL_DEBUG_TLS
        {
            word16 ii;
            WOLFSSL_MSG("Refined Ciphers:");
            for (ii = 0 ; ii < outSuites->suiteSz; ii += 2) {
                WOLFSSL_MSG(GetCipherNameInternal(outSuites->suites[ii+0],
                                                  outSuites->suites[ii+1]));
            }
        }
    #endif
    }

    /* Refine list of supported cipher suites to those common to server and client.
     *
     * ssl         SSL/TLS object.
     * peerSuites  The peer's advertised list of supported cipher suites.
     */
    void sslRefineSuites(WOLFSSL* ssl, Suites* peerSuites)
    {
        if (AllocateSuites(ssl) != 0)
            return;
        refineSuites(ssl->suites, peerSuites, ssl->suites,
                (byte)ssl->options.useClientOrder);
    }

    static int CompareSuites(const WOLFSSL* ssl, const Suites* suites,
                             Suites* peerSuites, word16 i, word16 j,
                             CipherSuite* cs, TLSX* extensions)
    {
        if (suites->suites[i]   == peerSuites->suites[j] &&
            suites->suites[i+1] == peerSuites->suites[j+1] ) {

            int ret = VerifyServerSuite(ssl, suites, i, cs, extensions);
            if (ret < 0) {
                return ret;
            }
            if (ret) {
                WOLFSSL_MSG("Verified suite validity");
                cs->cipherSuite0 = suites->suites[i];
                cs->cipherSuite  = suites->suites[i+1];
                return 0;
            }
            else {
                WOLFSSL_MSG("Could not verify suite validity, continue");
            }
        }

        return MATCH_SUITE_ERROR;
    }

    int MatchSuite_ex(const WOLFSSL* ssl, Suites* peerSuites, CipherSuite* cs,
                      TLSX* extensions)
    {
        int ret;
        word16 i, j;
        const Suites* suites = WOLFSSL_SUITES(ssl);

        WOLFSSL_ENTER("MatchSuite");

        /* & 0x1 equivalent % 2 */
        if (peerSuites->suiteSz == 0 || peerSuites->suiteSz & 0x1)
            return BUFFER_ERROR;

        if (suites == NULL)
            return SUITES_ERROR;

        if (!ssl->options.useClientOrder) {
            /* Server order */
            for (i = 0; i < suites->suiteSz; i += 2) {
                for (j = 0; j < peerSuites->suiteSz; j += 2) {
                    ret = CompareSuites(ssl, suites, peerSuites, i, j, cs, extensions);
                    if (ret != WC_NO_ERR_TRACE(MATCH_SUITE_ERROR))
                        return ret;
                }
            }
        }
        else {
            /* Client order */
            for (j = 0; j < peerSuites->suiteSz; j += 2) {
                for (i = 0; i < suites->suiteSz; i += 2) {
                    ret = CompareSuites(ssl, suites, peerSuites, i, j, cs, extensions);
                    if (ret != WC_NO_ERR_TRACE(MATCH_SUITE_ERROR))
                        return ret;
                }
            }
        }

        WOLFSSL_ERROR_VERBOSE(MATCH_SUITE_ERROR);
        return MATCH_SUITE_ERROR;

    }

    int MatchSuite(WOLFSSL* ssl, Suites* peerSuites)
    {
        int ret;
        CipherSuite cs;

        XMEMSET(&cs, 0, sizeof(cs));

        ret = MatchSuite_ex(ssl, peerSuites, &cs,
#ifdef HAVE_TLS_EXTENSIONS
                ssl->extensions
#else
                NULL
#endif
                );
        if (ret != 0)
            return ret;

        ssl->options.cipherSuite0 = cs.cipherSuite0;
        ssl->options.cipherSuite  = cs.cipherSuite;
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_CURVE25519) || \
    defined(HAVE_ED448) || defined(HAVE_CURVE448)
        ssl->ecdhCurveOID = cs.ecdhCurveOID;
#endif

        ret = SetCipherSpecs(ssl);
        if (ret != 0)
            return ret;
        ret = PickHashSigAlgo(ssl, peerSuites->hashSigAlgo,
                              peerSuites->hashSigAlgoSz, 1);
        if (ret != 0)
            return ret;

#if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES)
        if (cs.doHelloRetry) {
            /* Make sure we don't send HRR twice */
            if (ssl->options.serverState == SERVER_HELLO_RETRY_REQUEST_COMPLETE)
                return INVALID_PARAMETER;
            ssl->options.serverState = SERVER_HELLO_RETRY_REQUEST_COMPLETE;
            return TLSX_KeyShare_SetSupported(ssl, &ssl->extensions);
        }
#endif

#if defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES)
        if (IsAtLeastTLSv1_3(ssl->version) &&
                                      ssl->options.side == WOLFSSL_SERVER_END) {
            ret = TLSX_KeyShare_Setup(ssl, cs.clientKSE);
            if (ret != 0)
                return ret;
        }
#endif
        return ret;
    }

#ifdef OLD_HELLO_ALLOWED

    /* process old style client hello, deprecate? */
    int ProcessOldClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
                              word32 inSz, word16 sz)
    {
        word32          idx = *inOutIdx;
        word16          sessionSz;
        word16          randomSz;
        word16          i, j;
        ProtocolVersion pv;
        Suites          clSuites;
        int ret = -1;

        (void)inSz;
        WOLFSSL_MSG("Got old format client hello");
#ifdef WOLFSSL_CALLBACKS
        if (ssl->hsInfoOn)
            AddPacketName(ssl, "ClientHello");
        if (ssl->toInfoOn)
            AddLateName("ClientHello", &ssl->timeoutInfo);
#endif

        /* manually hash input since different format */
#ifndef NO_OLD_TLS
#ifndef NO_MD5
        wc_Md5Update(&ssl->hsHashes->hashMd5, input + idx, sz);
#endif
#ifndef NO_SHA
        wc_ShaUpdate(&ssl->hsHashes->hashSha, input + idx, sz);
#endif
#endif /* !NO_OLD_TLS */
#ifndef NO_SHA256
        if (IsAtLeastTLSv1_2(ssl)) {
            int shaRet = wc_Sha256Update(&ssl->hsHashes->hashSha256,
                                         input + idx, sz);
            if (shaRet != 0)
                return shaRet;
        }
#endif

        /* does this value mean client_hello? */
        idx++;

        /* version */
        pv.major = input[idx++];
        pv.minor = input[idx++];
        ssl->chVersion = pv;  /* store */

        if (ssl->version.minor > pv.minor) {
            byte haveRSA = 0;
            byte havePSK = 0;
            int  keySz   = 0;

            if (!ssl->options.downgrade) {
                WOLFSSL_MSG("Client trying to connect with lesser version");
                return VERSION_ERROR;
            }
            if (pv.minor < ssl->options.minDowngrade) {
                WOLFSSL_MSG("\tversion below minimum allowed, fatal error");
                return VERSION_ERROR;
            }
            if (pv.minor == SSLv3_MINOR) {
                /* turn off tls */
                WOLFSSL_MSG("\tdowngrading to SSLv3");
                ssl->options.tls    = 0;
                ssl->options.tls1_1 = 0;
                ssl->version.minor  = SSLv3_MINOR;
            }
            else if (pv.minor == TLSv1_MINOR) {
                WOLFSSL_MSG("\tdowngrading to TLSv1");
                /* turn off tls 1.1+ */
                ssl->options.tls1_1 = 0;
                ssl->version.minor  = TLSv1_MINOR;
            }
            else if (pv.minor == TLSv1_1_MINOR) {
                WOLFSSL_MSG("\tdowngrading to TLSv1.1");
                ssl->version.minor  = TLSv1_1_MINOR;
            }
            else if (pv.minor == TLSv1_2_MINOR) {
                WOLFSSL_MSG("    downgrading to TLSv1.2");
                ssl->version.minor  = TLSv1_2_MINOR;
            }
#ifndef NO_RSA
            haveRSA = 1;
#endif
#ifndef NO_PSK
            havePSK = ssl->options.havePSK;
#endif
#ifndef NO_CERTS
            keySz = ssl->buffers.keySz;
#endif
            ret = AllocateSuites(ssl);
            if (ret != 0)
                return ret;
            InitSuites(ssl->suites, ssl->version, keySz, haveRSA, havePSK,
                       ssl->options.haveDH, ssl->options.haveECDSAsig,
                       ssl->options.haveECC, TRUE, ssl->options.haveStaticECC,
                       ssl->options.useAnon,
                       TRUE, TRUE, TRUE, TRUE, ssl->options.side);
        }

        /* suite size */
        ato16(&input[idx], &clSuites.suiteSz);
        idx += OPAQUE16_LEN;

        if (clSuites.suiteSz > WOLFSSL_MAX_SUITE_SZ)
            return BUFFER_ERROR;
        /* Make sure the suiteSz is a multiple of 3. (Old Client Hello) */
        if (clSuites.suiteSz % 3 != 0)
            return BUFFER_ERROR;
        clSuites.hashSigAlgoSz = 0;

        /* session size */
        ato16(&input[idx], &sessionSz);
        idx += OPAQUE16_LEN;

        if (sessionSz > ID_LEN)
            return BUFFER_ERROR;

        /* random size */
        ato16(&input[idx], &randomSz);
        idx += OPAQUE16_LEN;

        if (randomSz > RAN_LEN)
            return BUFFER_ERROR;

        /* suites */
        for (i = 0, j = 0; i < clSuites.suiteSz; i += 3) {
            byte first = input[idx++];
            if (!first) { /* implicit: skip sslv2 type */
                XMEMCPY(&clSuites.suites[j], &input[idx], SUITE_LEN);
                j += SUITE_LEN;
            }
            idx += SUITE_LEN;
        }
        clSuites.suiteSz = j;

        /* session id */
        if (sessionSz) {
            XMEMCPY(ssl->arrays->sessionID, input + idx, sessionSz);
            ssl->arrays->sessionIDSz = (byte)sessionSz;
            idx += sessionSz;
            ssl->options.resuming = 1;
        }

        /* random */
        if (randomSz < RAN_LEN)
            XMEMSET(ssl->arrays->clientRandom, 0, RAN_LEN - randomSz);
        XMEMCPY(&ssl->arrays->clientRandom[RAN_LEN - randomSz], input + idx,
               randomSz);
        idx += randomSz;

        if (ssl->options.usingCompression)
            ssl->options.usingCompression = 0;  /* turn off */

        ssl->options.clientState = CLIENT_HELLO_COMPLETE;
        ssl->cbmode = WOLFSSL_CB_MODE_WRITE;
        *inOutIdx = idx;

        ssl->options.haveSessionId = 1;
        /* DoClientHello uses same resume code */
        if (ssl->options.resuming) {  /* let's try */
            WOLFSSL_SESSION* session;
        #ifdef HAVE_SESSION_TICKET
            if (ssl->options.useTicket == 1) {
                session = ssl->session;
            }
            else
        #endif
            {
                session = wolfSSL_GetSession(ssl, ssl->arrays->masterSecret, 1);
            }
            if (!session) {
                WOLFSSL_MSG("Session lookup for resume failed");
                ssl->options.resuming = 0;
            } else {
                if (MatchSuite(ssl, &clSuites) < 0) {
                    WOLFSSL_MSG("Unsupported cipher suite, OldClientHello");
                    return UNSUPPORTED_SUITE;
                }

                ret = wc_RNG_GenerateBlock(ssl->rng, ssl->arrays->serverRandom,
                                                                       RAN_LEN);
                if (ret != 0)
                    return ret;

                #ifdef NO_OLD_TLS
                    ret = DeriveTlsKeys(ssl);
                #else
                    #ifndef NO_TLS
                        if (ssl->options.tls)
                            ret = DeriveTlsKeys(ssl);
                    #endif
                        if (!ssl->options.tls)
                            ret = DeriveKeys(ssl);
                #endif
                /* SERVER: peer auth based on session secret. */
                ssl->options.peerAuthGood = (ret == 0);
                ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;

                return ret;
            }
        }

        ret = MatchSuite(ssl, &clSuites);
        if (ret != 0)return ret;
        return SanityCheckMsgReceived(ssl, client_hello);
    }

#endif /* OLD_HELLO_ALLOWED */

#ifndef WOLFSSL_NO_TLS12

    /**
     *  Handles session resumption.
     *  Session tickets are checked for validity based on the time each ticket
     *  was created, timeout value and the current time. If the tickets are
     *  judged expired, falls back to full-handshake. If you want disable this
     *  session ticket validation check in TLS1.2 and below, define
     *  WOLFSSL_NO_TICKET_EXPIRE.
     */
    int HandleTlsResumption(WOLFSSL* ssl, Suites* clSuites)
    {
        int ret = 0;
        WOLFSSL_SESSION* session;

#ifdef HAVE_SECRET_CALLBACK
        if (ssl->sessionSecretCb != NULL
#ifdef HAVE_SESSION_TICKET
                && ssl->session->ticketLen > 0
#endif
                ) {
            int secretSz = SECRET_LEN;
            WOLFSSL_MSG("Calling session secret callback");
            ret = wc_RNG_GenerateBlock(ssl->rng, ssl->arrays->serverRandom,
                                       RAN_LEN);
            if (ret == 0) {
                ret = ssl->sessionSecretCb(ssl, ssl->arrays->masterSecret,
                                              &secretSz, ssl->sessionSecretCtx);
                if (secretSz != SECRET_LEN)
                    ret = SESSION_SECRET_CB_E;
            }
            if (ret == 0)
                ret = MatchSuite(ssl, clSuites);
            if (ret == 0) {
                #ifdef NO_OLD_TLS
                    ret = DeriveTlsKeys(ssl);
                #else
                    #ifndef NO_TLS
                        if (ssl->options.tls)
                            ret = DeriveTlsKeys(ssl);
                    #endif
                        if (!ssl->options.tls)
                            ret = DeriveKeys(ssl);
                #endif
                /* SERVER: peer auth based on session secret. */
                ssl->options.peerAuthGood = (ret == 0);
                ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
            }
            if (ret != 0)
                WOLFSSL_ERROR_VERBOSE(ret);
            WOLFSSL_LEAVE("HandleTlsResumption", ret);
            return ret;
        }
#endif /* HAVE_SECRET_CALLBACK */

    #ifdef HAVE_SESSION_TICKET
        if (ssl->options.useTicket == 1) {
            session = ssl->session;
        }
        else
    #endif
        {
            session = wolfSSL_GetSession(ssl, ssl->arrays->masterSecret, 1);
        }
        if (!session) {
            WOLFSSL_MSG("Session lookup for resume failed");
            ssl->options.resuming = 0;
            return ret;
        }
#if !defined(WOLFSSL_NO_TICKET_EXPIRE) && !defined(NO_ASN_TIME)
        /* check if the ticket is valid */
        if (LowResTimer() > session->bornOn + ssl->timeout) {
            WOLFSSL_MSG("Expired session, fall back to full handshake.");
            ssl->options.resuming = 0;
        }
#endif /* !WOLFSSL_NO_TICKET_EXPIRE && !NO_ASN_TIME */

        else if (session->haveEMS != ssl->options.haveEMS) {
            /* RFC 7627, 5.3, server-side */
            /* if old sess didn't have EMS, but new does, full handshake */
            if (!session->haveEMS && ssl->options.haveEMS) {
                WOLFSSL_MSG("Attempting to resume a session that didn't "
                            "use EMS with a new session with EMS. Do full "
                            "handshake.");
                ssl->options.resuming = 0;
            }
            /* if old sess used EMS, but new doesn't, MUST abort */
            else if (session->haveEMS && !ssl->options.haveEMS) {
                WOLFSSL_MSG("Trying to resume a session with EMS without "
                            "using EMS");
            #ifdef WOLFSSL_EXTRA_ALERTS
                SendAlert(ssl, alert_fatal, handshake_failure);
            #endif
                ret = EXT_MASTER_SECRET_NEEDED_E;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
        }
        else {
        #ifndef NO_RESUME_SUITE_CHECK
            int j;

            /* Check client suites include the one in session */
            for (j = 0; j < clSuites->suiteSz; j += 2) {
                if (clSuites->suites[j] == session->cipherSuite0 &&
                                clSuites->suites[j+1] == session->cipherSuite) {
                    break;
                }
            }
            if (j == clSuites->suiteSz) {
                WOLFSSL_MSG("Prev session's cipher suite not in ClientHello");
            #ifdef WOLFSSL_EXTRA_ALERTS
                SendAlert(ssl, alert_fatal, illegal_parameter);
            #endif
                ret = UNSUPPORTED_SUITE;
                WOLFSSL_ERROR_VERBOSE(ret);
            }
        #endif

            if (ret == 0 && ssl->options.resuming) {
                /* for resumption use the cipher suite from session */
                ssl->options.cipherSuite0 = session->cipherSuite0;
                ssl->options.cipherSuite =  session->cipherSuite;
                ret = SetCipherSpecs(ssl);
                if (ret == 0) {
                    ret = PickHashSigAlgo(ssl, clSuites->hashSigAlgo,
                                          clSuites->hashSigAlgoSz, 0);
                }
            }
            else if (ret == 0) {
                if (MatchSuite(ssl, clSuites) < 0) {
                    WOLFSSL_MSG("Unsupported cipher suite, ClientHello");
                    ret = UNSUPPORTED_SUITE;
                    WOLFSSL_ERROR_VERBOSE(ret);
                }
            }
            if (ret == 0) {
                ret = wc_RNG_GenerateBlock(ssl->rng,
                                           ssl->arrays->serverRandom, RAN_LEN);
            }
            if (ret == 0) {
                #ifdef NO_OLD_TLS
                    ret = DeriveTlsKeys(ssl);
                #else
                    #ifndef NO_TLS
                        if (ssl->options.tls)
                            ret = DeriveTlsKeys(ssl);
                    #endif
                        if (!ssl->options.tls)
                            ret = DeriveKeys(ssl);
                #endif
                /* SERVER: peer auth based on session secret. */
                ssl->options.peerAuthGood = (ret == 0);
                ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
            }
        }


        return ret;
    }

    /* handle processing of client_hello (1) */
    int DoClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx,
                             word32 helloSz)
    {
        byte            b;
        ProtocolVersion pv;
        word32          i = *inOutIdx;
        word32          begin = i;
        int             ret = 0;
        byte            lesserVersion;

        WOLFSSL_START(WC_FUNC_CLIENT_HELLO_DO);
        WOLFSSL_ENTER("DoClientHello");

#ifdef WOLFSSL_CALLBACKS
        if (ssl->hsInfoOn) AddPacketName(ssl, "ClientHello");
        if (ssl->toInfoOn) AddLateName("ClientHello", &ssl->timeoutInfo);
#endif
        /* do not change state in the SSL object before the next region of code
         * to be able to statelessly compute a DTLS cookie */
#ifdef WOLFSSL_DTLS
        /* Update the ssl->options.dtlsStateful setting `if` statement in
         * wolfSSL_accept when changing this one. */
        if (IsDtlsNotSctpMode(ssl) && IsDtlsNotSrtpMode(ssl) && !IsSCR(ssl) &&
                !ssl->options.dtlsStateful) {
            DtlsSetSeqNumForReply(ssl);
            ret = DoClientHelloStateless(ssl, input + *inOutIdx, helloSz, 0,
                    NULL);
            if (ret != 0 || !ssl->options.dtlsStateful) {
                int alertType = TranslateErrorToAlert(ret);
                if (alertType != invalid_alert) {
                    int err;

                    /* propagate socket errors to avoid re-calling send alert */
                    err = SendAlert(ssl, alert_fatal, alertType);
                    if (err == WC_NO_ERR_TRACE(SOCKET_ERROR_E))
                        ret = SOCKET_ERROR_E;
                }
                *inOutIdx += helloSz;
                DtlsResetState(ssl);
                if (DtlsIgnoreError(ret))
                    ret = 0;
                return ret;
            }
            if (ssl->chGoodCb != NULL) {
                int cbret = ssl->chGoodCb(ssl, ssl->chGoodCtx);
                if (cbret < 0) {
                    ssl->error = cbret;
                    WOLFSSL_MSG("ClientHello Good Cb don't continue error");
                    return WOLFSSL_FATAL_ERROR;
                }
            }
        }
        ssl->options.dtlsStateful = 1;
#endif /* WOLFSSL_DTLS */

        /* Reset to sane value for SCR */
        ssl->options.resuming = 0;
        ssl->arrays->sessionIDSz = 0;

        /* protocol version, random and session id length check */
        if (OPAQUE16_LEN + RAN_LEN + OPAQUE8_LEN > helloSz)
            return BUFFER_ERROR;

        /* protocol version */
        XMEMCPY(&pv, input + i, OPAQUE16_LEN);
        ssl->chVersion = pv;   /* store */
        i += OPAQUE16_LEN;

        /* Legacy protocol version cannot negotiate TLS 1.3 or higher. */
        if (pv.major == SSLv3_MAJOR && pv.minor >= TLSv1_3_MINOR)
            pv.minor = TLSv1_2_MINOR;

        lesserVersion = (byte)(!ssl->options.dtls &&
                                    ssl->version.minor > pv.minor);
        lesserVersion |= ssl->options.dtls &&ssl->version.minor < pv.minor;

        if (lesserVersion) {
            byte   belowMinDowngrade;
            word16 haveRSA = 0;
            word16 havePSK = 0;
            int    keySz   = 0;

            if (!ssl->options.downgrade) {
                WOLFSSL_MSG("Client trying to connect with lesser version");
                ret = VERSION_ERROR;
                goto out;
            }

            belowMinDowngrade = pv.minor < ssl->options.minDowngrade;

            /* DTLS versions increase backwards (-1,-2,-3) ecc  */
            if (ssl->options.dtls)
                belowMinDowngrade = ssl->options.dtls
                    && pv.minor > ssl->options.minDowngrade;

            if (belowMinDowngrade) {
                WOLFSSL_MSG("\tversion below minimum allowed, fatal error");
                ret = VERSION_ERROR;
                goto out;
            }

            if (!ssl->options.dtls) {
                if (pv.minor == SSLv3_MINOR) {
                    /* turn off tls */
                    WOLFSSL_MSG("\tdowngrading to SSLv3");
                    ssl->options.tls    = 0;
                    ssl->options.tls1_1 = 0;
                    ssl->version.minor  = SSLv3_MINOR;
                }
                else if (pv.minor == TLSv1_MINOR) {
                    /* turn off tls 1.1+ */
                    WOLFSSL_MSG("\tdowngrading to TLSv1");
                    ssl->options.tls1_1 = 0;
                    ssl->version.minor  = TLSv1_MINOR;
                }
                else if (pv.minor == TLSv1_1_MINOR) {
                    WOLFSSL_MSG("\tdowngrading to TLSv1.1");
                    ssl->version.minor  = TLSv1_1_MINOR;
                }
                else if (pv.minor == TLSv1_2_MINOR) {
                    WOLFSSL_MSG("    downgrading to TLSv1.2");
                    ssl->version.minor  = TLSv1_2_MINOR;
                }
            }
            else {
                if (pv.minor == DTLSv1_2_MINOR) {
                    WOLFSSL_MSG("\tDowngrading to DTLSv1.2");
                    ssl->options.tls1_3 = 0;
                    ssl->version.minor = DTLSv1_2_MINOR;
                }
                else if (pv.minor == DTLS_MINOR) {
                    WOLFSSL_MSG("\tDowngrading to DTLSv1.0");
                    ssl->options.tls1_3 = 0;
                    ssl->version.minor = DTLS_MINOR;
                }
            }
#ifndef NO_RSA
            haveRSA = 1;
#endif
#ifndef NO_PSK
            havePSK = ssl->options.havePSK;
#endif
#ifndef NO_CERTS
            keySz = ssl->buffers.keySz;
#endif
            ret = AllocateSuites(ssl);
            if (ret != 0)
                goto out;
            InitSuites(ssl->suites, ssl->version, keySz, haveRSA, havePSK,
                       ssl->options.haveDH, ssl->options.haveECDSAsig,
                       ssl->options.haveECC, TRUE, ssl->options.haveStaticECC,
                       ssl->options.useAnon,
                       TRUE, TRUE, TRUE, TRUE, ssl->options.side);
        }

        /* check if option is set to not allow the current version
         * set from either wolfSSL_set_options or wolfSSL_CTX_set_options */
        if (!ssl->options.dtls && ssl->options.downgrade &&
            ssl->options.mask > 0) {

            int reset = 0;

            if (ssl->version.minor == TLSv1_2_MINOR &&
               (ssl->options.mask & WOLFSSL_OP_NO_TLSv1_2) ==
                WOLFSSL_OP_NO_TLSv1_2) {
                WOLFSSL_MSG("\tOption set to not allow TLSv1.2, Downgrading");
                ssl->version.minor = TLSv1_1_MINOR;
                reset = 1;
            }

            if (ssl->version.minor == TLSv1_1_MINOR &&
               (ssl->options.mask & WOLFSSL_OP_NO_TLSv1_1) ==
                WOLFSSL_OP_NO_TLSv1_1) {
                WOLFSSL_MSG("\tOption set to not allow TLSv1.1, Downgrading");
                ssl->options.tls1_1 = 0;
                ssl->version.minor = TLSv1_MINOR;
                reset = 1;
            }

            if (ssl->version.minor == TLSv1_MINOR &&
               (ssl->options.mask & WOLFSSL_OP_NO_TLSv1) ==
                WOLFSSL_OP_NO_TLSv1) {
                WOLFSSL_MSG("\tOption set to not allow TLSv1, Downgrading");
                ssl->options.tls    = 0;
                ssl->options.tls1_1 = 0;
                ssl->version.minor = SSLv3_MINOR;
                reset = 1;
            }

            if (ssl->version.minor == SSLv3_MINOR &&
               (ssl->options.mask & WOLFSSL_OP_NO_SSLv3) ==
                WOLFSSL_OP_NO_SSLv3) {
                WOLFSSL_MSG("\tError, option set to not allow SSLv3");
                ret = VERSION_ERROR;
#ifdef WOLFSSL_EXTRA_ALERTS
                SendAlert(ssl, alert_fatal, wolfssl_alert_protocol_version);
#endif
                goto out;
            }

            if (ssl->version.minor < ssl->options.minDowngrade) {
                WOLFSSL_MSG("\tversion below minimum allowed, fatal error");
                ret = VERSION_ERROR;
                goto out;
            }

            if (reset) {
                word16 haveRSA = 0;
                word16 havePSK = 0;
                int    keySz   = 0;

            #ifndef NO_RSA
                haveRSA = 1;
            #endif
            #ifndef NO_PSK
                havePSK = ssl->options.havePSK;
            #endif
            #ifndef NO_CERTS
                keySz = ssl->buffers.keySz;
            #endif

                ret = AllocateSuites(ssl);
                if (ret != 0)
                    goto out;
                /* reset cipher suites to account for TLS version change */
                InitSuites(ssl->suites, ssl->version, keySz, haveRSA, havePSK,
                           ssl->options.haveDH, ssl->options.haveECDSAsig,
                           ssl->options.haveECC, TRUE, ssl->options.haveStaticECC,
                           ssl->options.useAnon,
                           TRUE, TRUE, TRUE, TRUE, ssl->options.side);
            }
        }

        /* random */
        XMEMCPY(ssl->arrays->clientRandom, input + i, RAN_LEN);
        i += RAN_LEN;

#ifdef SHOW_SECRETS
        {
            int j;
            printf("client random: ");
            for (j = 0; j < RAN_LEN; j++)
                printf("%02x", ssl->arrays->clientRandom[j]);
            printf("\n");
        }
#endif

        /* session id */
        b = input[i++];
        if (b > ID_LEN) {
            WOLFSSL_MSG("Invalid session ID size");
            ret = BUFFER_ERROR; /* session ID greater than 32 bytes long */
            goto out;
        }
        else if (b > 0 && !IsSCR(ssl)) {
            if ((i - begin) + b > helloSz) {
                ret = BUFFER_ERROR;
                goto out;
            }

            /* Always save session ID in case we want to echo it. */
            XMEMCPY(ssl->arrays->sessionID, input + i, b);
            ssl->arrays->sessionIDSz = b;

            if (b == ID_LEN)
                ssl->options.resuming = 1; /* client wants to resume */
            WOLFSSL_MSG("Client wants to resume session");
        }
        i += b;

#ifdef WOLFSSL_DTLS
            /* cookie */
            if (ssl->options.dtls) {
                word8 peerCookieSz;
                if ((i - begin) + OPAQUE8_LEN > helloSz) {
                    ret = BUFFER_ERROR;
                    goto out;
                }

                peerCookieSz = input[i++];

                if (peerCookieSz) {
                    if (peerCookieSz > MAX_COOKIE_LEN) {
                        ret = BUFFER_ERROR;
                        goto out;
                    }

                    if ((i - begin) + peerCookieSz > helloSz) {
                        ret = BUFFER_ERROR;
                        goto out;
                    }

                    i += peerCookieSz;
                }
            }
#endif /* WOLFSSL_DTLS */

        /* suites */
        if ((i - begin) + OPAQUE16_LEN > helloSz) {
            ret = BUFFER_ERROR;
            goto out;
        }

        XFREE(ssl->clSuites, ssl->heap, DYNAMIC_TYPE_SUITES);
        ssl->clSuites = (Suites*)XMALLOC(sizeof(Suites), ssl->heap,
                                       DYNAMIC_TYPE_SUITES);
        if (ssl->clSuites == NULL) {
            ret = MEMORY_E;
            goto out;
        }
        XMEMSET(ssl->clSuites, 0, sizeof(Suites));
        ato16(&input[i], &ssl->clSuites->suiteSz);
        i += OPAQUE16_LEN;

        /* Cipher suite lists are always multiples of two in length. */
        if (ssl->clSuites->suiteSz % 2 != 0) {
            ret = BUFFER_ERROR;
            goto out;
        }

        /* suites and compression length check */
        if ((i - begin) + ssl->clSuites->suiteSz + OPAQUE8_LEN > helloSz) {
            ret = BUFFER_ERROR;
            goto out;
        }

        if (ssl->clSuites->suiteSz > WOLFSSL_MAX_SUITE_SZ) {
            ret = BUFFER_ERROR;
            goto out;
        }

        XMEMCPY(ssl->clSuites->suites, input + i, ssl->clSuites->suiteSz);

#ifdef HAVE_SERVER_RENEGOTIATION_INFO
        /* check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV suite */
        if (FindSuite(ssl->clSuites, 0, TLS_EMPTY_RENEGOTIATION_INFO_SCSV) >=
                0) {
            TLSX* extension;

            /* check for TLS_EMPTY_RENEGOTIATION_INFO_SCSV suite */
            ret = TLSX_AddEmptyRenegotiationInfo(&ssl->extensions, ssl->heap);
            if (ret != WOLFSSL_SUCCESS) {
                ret = SECURE_RENEGOTIATION_E;
                goto out;
            } else {
                ret = 0;
            }

            extension = TLSX_Find(ssl->extensions, TLSX_RENEGOTIATION_INFO);
            if (extension) {
                ssl->secure_renegotiation =
                                          (SecureRenegotiation*)extension->data;
                ssl->secure_renegotiation->enabled = 1;
            }
        }
#endif /* HAVE_SERVER_RENEGOTIATION_INFO */
#if defined(HAVE_FALLBACK_SCSV) || defined(OPENSSL_ALL)
        /* check for TLS_FALLBACK_SCSV suite */
        if (FindSuite(ssl->clSuites, TLS_FALLBACK_SCSV, 0) >= 0) {
            WOLFSSL_MSG("Found Fallback SCSV");
            if (ssl->ctx->method->version.minor > pv.minor) {
                WOLFSSL_MSG("Client trying to connect with lesser version");
                SendAlert(ssl, alert_fatal, inappropriate_fallback);
                ret = VERSION_ERROR;
                goto out;
            }
        }
#endif

        i += ssl->clSuites->suiteSz;
        ssl->clSuites->hashSigAlgoSz = 0;

        /* compression length */
        b = input[i++];

        if ((i - begin) + b > helloSz) {
            ret = BUFFER_ERROR;
            goto out;
        }

        if (b == 0) {
            WOLFSSL_MSG("No compression types in list");
#ifdef WOLFSSL_EXTRA_ALERTS
            SendAlert(ssl, alert_fatal, decode_error);
#endif
            ret = COMPRESSION_ERROR;
            goto out;
        }

        {
            /* compression match types */
            int matchNo = 0;
            int matchZlib = 0;

            while (b--) {
                byte comp = input[i++];

                if (comp == NO_COMPRESSION) {
                    matchNo = 1;
                }
                if (comp == ZLIB_COMPRESSION) {
                    matchZlib = 1;
                }
            }

            if (!matchNo) {
                WOLFSSL_MSG("Compression list missing null");
#ifdef WOLFSSL_EXTRA_ALERTS
                SendAlert(ssl, alert_fatal, illegal_parameter);
#endif
                ret = COMPRESSION_ERROR;
                goto out;
            }

            if (ssl->options.usingCompression == 0 && matchNo) {
                WOLFSSL_MSG("Matched No Compression");
            } else if (ssl->options.usingCompression && matchZlib) {
                WOLFSSL_MSG("Matched zlib Compression");
            } else if (ssl->options.usingCompression && matchNo) {
                WOLFSSL_MSG("Could only match no compression, turning off");
                ssl->options.usingCompression = 0;  /* turn off */
            } else {
                WOLFSSL_MSG("Could not match compression");
#ifdef WOLFSSL_EXTRA_ALERTS
                SendAlert(ssl, alert_fatal, illegal_parameter);
#endif
                ret = COMPRESSION_ERROR;
                goto out;
            }
        }

        *inOutIdx = i;

        /* tls extensions */
        if ((i - begin) < helloSz) {
#ifdef HAVE_TLS_EXTENSIONS
            if (TLSX_SupportExtensions(ssl))
#else
            if (IsAtLeastTLSv1_2(ssl))
#endif
            {
                /* Process the hello extension. Skip unsupported. */
                word16 totalExtSz;

#ifdef HAVE_TLS_EXTENSIONS
                /* auto populate extensions supported unless user defined */
                if ((ret = TLSX_PopulateExtensions(ssl, 1)) != 0)
                    goto out;
#else
                word32 extensions_seen = 0U;
#endif

                if ((i - begin) + OPAQUE16_LEN > helloSz) {
                    ret = BUFFER_ERROR;
                    goto out;
                }

                ato16(&input[i], &totalExtSz);
                i += OPAQUE16_LEN;

                if ((i - begin) + totalExtSz > helloSz) {
                    ret = BUFFER_ERROR;
                    goto out;
                }

#ifdef HAVE_TLS_EXTENSIONS
                /* tls extensions */
                if ((ret = TLSX_Parse(ssl, input + i, totalExtSz, client_hello,
                                                                ssl->clSuites)))
                    goto out;
    #ifdef WOLFSSL_TLS13
                if (TLSX_Find(ssl->extensions,
                                             TLSX_SUPPORTED_VERSIONS) != NULL) {
                    WOLFSSL_MSG(
                            "Client attempting to connect with higher version");
                    ret = VERSION_ERROR;
                    goto out;
                }
    #endif
    #ifdef HAVE_SNI
                if((ret=SNI_Callback(ssl)))
                    goto out;
    #endif
    #ifdef HAVE_ALPN
                if((ret=ALPN_Select(ssl)))
                    goto out;
    #endif

                i += totalExtSz;
#else
                while (totalExtSz) {
                    word16 extId, extSz;

                    if (OPAQUE16_LEN + OPAQUE16_LEN > totalExtSz) {
                        ret = BUFFER_ERROR;
                        goto out;
                    }

                    ato16(&input[i], &extId);
                    i += OPAQUE16_LEN;
                    ato16(&input[i], &extSz);
                    i += OPAQUE16_LEN;

                    if (extId < (word16)(sizeof(extensions_seen) * 8U)) {
                        word32 mask = 1U << extId;
                        if ((extensions_seen & mask) != 0U) {
                            WOLFSSL_MSG(
                                    "DoClientHello: duplicate extension found");
                            ret = DUPLICATE_TLS_EXT_E;
                            goto out;
                        }
                        extensions_seen |= mask;
                    }

                    if (OPAQUE16_LEN + OPAQUE16_LEN + extSz > totalExtSz) {
                        ret = BUFFER_ERROR;
                        goto out;
                    }

                    if (extId == HELLO_EXT_SIG_ALGO) {
                        word16 hashSigAlgoSz;

                        ato16(&input[i], &hashSigAlgoSz);
                        i += OPAQUE16_LEN;

                        if (OPAQUE16_LEN + hashSigAlgoSz > extSz) {
                            ret = BUFFER_ERROR;
                            goto out;
                        }

                        if (hashSigAlgoSz % 2 != 0) {
                            ret = BUFFER_ERROR;
                            goto out;
                        }

                        ssl->clSuites->hashSigAlgoSz = hashSigAlgoSz;
                        if (ssl->clSuites->hashSigAlgoSz >
                                WOLFSSL_MAX_SIGALGO) {
                            WOLFSSL_MSG("ClientHello SigAlgo list exceeds max, "
                                                                  "truncating");
                            ssl->clSuites->hashSigAlgoSz = WOLFSSL_MAX_SIGALGO;
                        }

                        XMEMCPY(ssl->clSuites->hashSigAlgo, &input[i],
                                                  ssl->clSuites->hashSigAlgoSz);

                        i += hashSigAlgoSz;
                    }
#ifdef HAVE_EXTENDED_MASTER
                    else if (extId == HELLO_EXT_EXTMS)
                        ssl->options.haveEMS = 1;
#endif
                    else
                        i += extSz;

                    totalExtSz -= (word16)(OPAQUE16_LEN + OPAQUE16_LEN) + extSz;
                }
#endif
                *inOutIdx = i;
            }
            else
                *inOutIdx = begin + helloSz; /* skip extensions */
        }

#ifdef WOLFSSL_DTLS_CID
        if (ssl->options.useDtlsCID)
            DtlsCIDOnExtensionsParsed(ssl);
#endif /* WOLFSSL_DTLS_CID */

        ssl->options.clientState   = CLIENT_HELLO_COMPLETE;
        ssl->options.haveSessionId = 1;

        /* ProcessOld uses same resume code */
        WOLFSSL_MSG_EX("ssl->options.resuming %d", ssl->options.resuming);
        if (ssl->options.resuming) {
            ret = HandleTlsResumption(ssl, ssl->clSuites);
            if (ret != 0)
                goto out;

#if defined(HAVE_TLS_EXTENSIONS) && defined(HAVE_ENCRYPT_THEN_MAC) && \
    !defined(WOLFSSL_AEAD_ONLY)
            if (ssl->options.encThenMac && ssl->specs.cipher_type == block) {
                ret = TLSX_EncryptThenMac_Respond(ssl);
                if (ret != 0)
                    goto out;
            }
            else
                ssl->options.encThenMac = 0;
#endif
            if (ssl->options.clientState == CLIENT_KEYEXCHANGE_COMPLETE) {
                WOLFSSL_LEAVE("DoClientHello", ret);
                WOLFSSL_END(WC_FUNC_CLIENT_HELLO_DO);

                goto out;
            }
        }


#if defined(HAVE_TLS_EXTENSIONS) && defined(HAVE_DH_DEFAULT_PARAMS)
    #if defined(HAVE_FFDHE) && defined(HAVE_SUPPORTED_CURVES)
        if (TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS) != NULL) {
            /* Set FFDHE parameters or clear DHE parameters if FFDH parameters
             * present and no matches in the server's list. */
            ret = TLSX_SupportedFFDHE_Set(ssl);
            if (ret != 0)
                goto out;
        }
    #endif
#endif

#ifdef WOLFSSL_CERT_SETUP_CB
        /* Give user last chance to provide a cert for cipher selection */
        if (ret == 0 && ssl->ctx->certSetupCb != NULL)
            ret = CertSetupCbWrapper(ssl);
#endif
        if (ret == 0)
            ret = MatchSuite(ssl, ssl->clSuites);

#if defined(HAVE_TLS_EXTENSIONS) && defined(HAVE_ENCRYPT_THEN_MAC) && \
    !defined(WOLFSSL_AEAD_ONLY)
        if (ret == 0 && ssl->options.encThenMac &&
                                              ssl->specs.cipher_type == block) {
            ret = TLSX_EncryptThenMac_Respond(ssl);
        }
        else
            ssl->options.encThenMac = 0;
#endif

#ifdef WOLFSSL_DTLS
        if (ret == 0 && ssl->options.dtls)
            DtlsMsgPoolReset(ssl);
#endif

    out:
#if !defined(OPENSSL_EXTRA)
        XFREE(ssl->clSuites, ssl->heap, DYNAMIC_TYPE_SUITES);
        ssl->clSuites = NULL;
#endif
        WOLFSSL_LEAVE("DoClientHello", ret);
        WOLFSSL_END(WC_FUNC_CLIENT_HELLO_DO);

        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(ret);
        }

        return ret;
    }


#if (!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \
                        defined(HAVE_ED448)) && !defined(WOLFSSL_NO_CLIENT_AUTH)

    typedef struct DcvArgs {
        byte*  output; /* not allocated */
        word32 sendSz;
        word16 sz;
        word32 sigSz;
        word32 idx;
        word32 begin;
    } DcvArgs;

    static void FreeDcvArgs(WOLFSSL* ssl, void* pArgs)
    {
        DcvArgs* args = (DcvArgs*)pArgs;

        (void)ssl;
        (void)args;
    }

    /* handle processing of certificate_verify (15) */
    static int DoCertificateVerify(WOLFSSL* ssl, byte* input,
                                word32* inOutIdx, word32 size)
    {
        int ret = 0;
    #ifdef WOLFSSL_ASYNC_CRYPT
        DcvArgs* args = NULL;
        WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
    #else
        DcvArgs  args[1];
    #endif

        WOLFSSL_START(WC_FUNC_CERTIFICATE_VERIFY_DO);
        WOLFSSL_ENTER("DoCertificateVerify");

    #ifdef WOLFSSL_ASYNC_CRYPT
        if (ssl->async == NULL) {
            ssl->async = (struct WOLFSSL_ASYNC*)
                    XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
                            DYNAMIC_TYPE_ASYNC);
            if (ssl->async == NULL)
                ERROR_OUT(MEMORY_E, exit_dcv);
        }
        args = (DcvArgs*)ssl->async->args;

        ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState);
        if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
            /* Check for error */
            if (ret < 0)
                goto exit_dcv;
        }
        else
    #endif
        {
            /* Reset state */
            ret = 0;
            ssl->options.asyncState = TLS_ASYNC_BEGIN;
            XMEMSET(args, 0, sizeof(DcvArgs));
            ssl->options.peerHashAlgo = sha_mac;
            ssl->options.peerSigAlgo = anonymous_sa_algo;
            args->idx = *inOutIdx;
            args->begin = *inOutIdx;
        #ifdef WOLFSSL_ASYNC_CRYPT
            ssl->async->freeArgs = FreeDcvArgs;
        #endif
        }

        switch(ssl->options.asyncState)
        {
            case TLS_ASYNC_BEGIN:
            {
            #ifdef WOLFSSL_CALLBACKS
                if (ssl->hsInfoOn)
                    AddPacketName(ssl, "CertificateVerify");
                if (ssl->toInfoOn)
                    AddLateName("CertificateVerify", &ssl->timeoutInfo);
            #endif

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_BUILD;
            } /* case TLS_ASYNC_BEGIN */
            FALL_THROUGH;

            case TLS_ASYNC_BUILD:
            {
                if (IsAtLeastTLSv1_2(ssl)) {
                    if ((args->idx - args->begin) + ENUM_LEN + ENUM_LEN >
                                                                         size) {
                        ERROR_OUT(BUFFER_ERROR, exit_dcv);
                    }

                    /* Check if hashSigAlgo in CertificateVerify is supported
                     * in our ssl->suites or ssl->ctx->suites. */
                    if (!SupportedHashSigAlgo(ssl, &input[args->idx])) {
                        WOLFSSL_MSG("Signature algorithm was not in "
                                                          "CertificateRequest");
                        ERROR_OUT(INVALID_PARAMETER, exit_dcv);
                    }

                    DecodeSigAlg(&input[args->idx], &ssl->options.peerHashAlgo,
                                 &ssl->options.peerSigAlgo);
                    args->idx += 2;

                #ifndef NO_RSA
                    if (ssl->peerRsaKeyPresent) {
                        if (ssl->options.peerSigAlgo != rsa_sa_algo
                        #ifdef WC_RSA_PSS
                            && ssl->options.peerSigAlgo != rsa_pss_sa_algo
                        #endif
                            ) {
                            WOLFSSL_MSG("Oops, peer sent RSA key but not in "
                                                                      "verify");
                            ERROR_OUT(INVALID_PARAMETER, exit_dcv);
                        }
                    }
                    else
                #endif
                #ifdef HAVE_ECC
                    if (ssl->peerEccDsaKeyPresent) {
                        if (ssl->options.peerSigAlgo != ecc_dsa_sa_algo
                        #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                            && ssl->options.peerSigAlgo != sm2_sa_algo
                        #endif
                            ) {
                            WOLFSSL_MSG("Oops, peer sent ECC key but not in "
                                                                      "verify");
                            ERROR_OUT(INVALID_PARAMETER, exit_dcv);
                        }
                    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                        if (ssl->options.peerSigAlgo == sm2_sa_algo &&
                            ssl->options.peerHashAlgo != sm3_mac) {
                            WOLFSSL_MSG("SM2 with SM3 only");
                            ERROR_OUT(INVALID_PARAMETER, exit_dcv);
                        }
                    #endif
                    }
                    else
                #endif
                #if defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)
                    if (ssl->peerEd25519KeyPresent) {
                        if (ssl->options.peerSigAlgo != ed25519_sa_algo) {
                            WOLFSSL_MSG("Oops, peer sent Ed25519 key but not "
                                                                   "in verify");
                            ERROR_OUT(INVALID_PARAMETER, exit_dcv);
                        }
                    }
                    else
                #endif /* HAVE_ED25519 && !NO_ED25519_CLIENT_AUTH */
                #if defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)
                    if (ssl->peerEd448KeyPresent) {
                        if (ssl->options.peerSigAlgo != ed448_sa_algo) {
                            WOLFSSL_MSG("Oops, peer sent Ed448 key but not in "
                                                                      "verify");
                            ERROR_OUT(INVALID_PARAMETER, exit_dcv);
                        }
                    }
                    else
                #endif /* HAVE_ED448 && !NO_ED448_CLIENT_AUTH */
                    {
                        ERROR_OUT(INVALID_PARAMETER, exit_dcv);
                    }

                    SetDigest(ssl, ssl->options.peerHashAlgo);
                }
                else {
                    /* make sure a default is defined */
                #if !defined(NO_SHA)
                    SetDigest(ssl, sha_mac);
                #elif !defined(NO_SHA256)
                    SetDigest(ssl, sha256_mac);
                #elif defined(WOLFSSL_SM3)
                    SetDigest(ssl, sm3_mac);
                #elif defined(WOLFSSL_SHA384)
                    SetDigest(ssl, sha384_mac);
                #elif defined(WOLFSSL_SHA512)
                    SetDigest(ssl, sha512_mac);
                #else
                    #error No digest enabled for ECC sig verify
                #endif

                #ifndef NO_RSA
                    if (ssl->peerRsaKeyPresent)
                        ssl->options.peerSigAlgo = rsa_sa_algo;
                    else
                #endif
                #ifdef HAVE_ECC
                    if (ssl->peerEccDsaKeyPresent) {
                    #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                        if (ssl->peerEccDsaKey->dp->id == ECC_SM2P256V1) {
                            ssl->options.peerSigAlgo = sm2_sa_algo;
                        }
                        else
                    #endif
                        {
                            ssl->options.peerSigAlgo = ecc_dsa_sa_algo;
                        }
                    }
                    else
                #endif
                #if defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)
                    if (ssl->peerEd25519KeyPresent)
                        ssl->options.peerSigAlgo = ed25519_sa_algo;
                    else
                #endif /* HAVE_ED25519 && !NO_ED25519_CLIENT_AUTH */
                #if defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)
                    if (ssl->peerEd448KeyPresent)
                        ssl->options.peerSigAlgo = ed448_sa_algo;
                    else
                #endif /* HAVE_ED448 && !NO_ED448_CLIENT_AUTH */
                    {
                        ERROR_OUT(NO_PEER_KEY, exit_dcv);
                    }
                }

                if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
                    ERROR_OUT(BUFFER_ERROR, exit_dcv);
                }

                ato16(input + args->idx, &args->sz);
                args->idx += OPAQUE16_LEN;

                if ((args->idx - args->begin) + args->sz > size ||
                                                    args->sz > ENCRYPT_LEN) {
                    ERROR_OUT(BUFFER_ERROR, exit_dcv);
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_DO;
            } /* case TLS_ASYNC_BUILD */
            FALL_THROUGH;

            case TLS_ASYNC_DO:
            {
            #ifndef NO_RSA
                if (ssl->peerRsaKey != NULL && ssl->peerRsaKeyPresent != 0) {
                    WOLFSSL_MSG("Doing RSA peer cert verify");

                    ret = RsaVerify(ssl,
                        input + args->idx,
                        args->sz,
                        &args->output,
                        ssl->options.peerSigAlgo, ssl->options.peerHashAlgo,
                        ssl->peerRsaKey,
                    #ifdef HAVE_PK_CALLBACKS
                        &ssl->buffers.peerRsaKey
                    #else
                        NULL
                    #endif
                    );
                    if (ret >= 0) {
                        if (ssl->options.peerSigAlgo == rsa_sa_algo)
                            args->sendSz = (word32)ret;
                        else {
                            args->sigSz = (word32)ret;
                            args->sendSz = ssl->buffers.digest.length;
                        }
                        ret = 0;
                    }
                }
            #endif /* !NO_RSA */
            #ifdef HAVE_ECC
                if (ssl->peerEccDsaKeyPresent) {
                    WOLFSSL_MSG("Doing ECC peer cert verify");

                #if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
                    if (ssl->options.peerSigAlgo == sm2_sa_algo) {
                        ret = Sm2wSm3Verify(ssl,
                            TLS12_SM2_SIG_ID, TLS12_SM2_SIG_ID_SZ,
                            input + args->idx, args->sz,
                            ssl->hsHashes->messages, ssl->hsHashes->prevLen,
                            ssl->peerEccDsaKey,
                        #ifdef HAVE_PK_CALLBACKS
                            &ssl->buffers.peerEccDsaKey
                        #else
                            NULL
                        #endif
                        );
                    }
                    else
                #endif
                    {
                        ret = EccVerify(ssl,
                            input + args->idx, args->sz,
                            ssl->buffers.digest.buffer,
                            ssl->buffers.digest.length,
                            ssl->peerEccDsaKey,
                        #ifdef HAVE_PK_CALLBACKS
                            &ssl->buffers.peerEccDsaKey
                        #else
                            NULL
                        #endif
                        );
                    }
                    /* SERVER: Data verified with certificate's public key. */
                    ssl->options.peerAuthGood = ssl->options.havePeerCert &&
                                                (ret == 0);
                }
            #endif /* HAVE_ECC */
            #if defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)
                if (ssl->peerEd25519KeyPresent) {
                    WOLFSSL_MSG("Doing Ed25519 peer cert verify");

                    ret = Ed25519Verify(ssl,
                        input + args->idx, args->sz,
                        ssl->hsHashes->messages, ssl->hsHashes->prevLen,
                        ssl->peerEd25519Key,
                    #ifdef HAVE_PK_CALLBACKS
                        &ssl->buffers.peerEd25519Key
                    #else
                        NULL
                    #endif
                    );
                    /* SERVER: Data verified with certificate's public key. */
                    ssl->options.peerAuthGood = ssl->options.havePeerCert &&
                                                (ret == 0);
                }
            #endif /* HAVE_ED25519 && !NO_ED25519_CLIENT_AUTH */
            #if defined(HAVE_ED448) && !defined(NO_ED448_CLIENT_AUTH)
                if (ssl->peerEd448KeyPresent) {
                    WOLFSSL_MSG("Doing Ed448 peer cert verify");

                    ret = Ed448Verify(ssl,
                        input + args->idx, args->sz,
                        ssl->hsHashes->messages, ssl->hsHashes->prevLen,
                        ssl->peerEd448Key,
                    #ifdef HAVE_PK_CALLBACKS
                        &ssl->buffers.peerEd448Key
                    #else
                        NULL
                    #endif
                    );
                    /* SERVER: Data verified with certificate's public key. */
                    ssl->options.peerAuthGood = ssl->options.havePeerCert &&
                                                (ret == 0);
                }
            #endif /* HAVE_ED448 && !NO_ED448_CLIENT_AUTH */

            #ifdef WOLFSSL_ASYNC_CRYPT
                /* handle async pending */
                if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                    goto exit_dcv;
            #endif

                /* Check for error */
                if (ret != 0) {
                    ret = SIG_VERIFY_E;
                    goto exit_dcv;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_VERIFY;
            } /* case TLS_ASYNC_DO */
            FALL_THROUGH;

            case TLS_ASYNC_VERIFY:
            {
            #ifndef NO_RSA
                if (ssl->peerRsaKey != NULL && ssl->peerRsaKeyPresent != 0) {
                    if (IsAtLeastTLSv1_2(ssl)) {
                    #ifdef WC_RSA_PSS
                        if (ssl->options.peerSigAlgo == rsa_pss_sa_algo) {
                        #ifdef HAVE_SELFTEST
                            ret = wc_RsaPSS_CheckPadding(
                                            ssl->buffers.digest.buffer,
                                            ssl->buffers.digest.length,
                                            args->output, args->sigSz,
                                            HashAlgoToType(ssl->options.peerHashAlgo));
                        #else
                            ret = wc_RsaPSS_CheckPadding_ex(
                                            ssl->buffers.digest.buffer,
                                            ssl->buffers.digest.length,
                                            args->output, args->sigSz,
                                            HashAlgoToType(ssl->options.peerHashAlgo), -1,
                                            mp_count_bits(&ssl->peerRsaKey->n));
                        #endif
                            if (ret != 0) {
                                ret = SIG_VERIFY_E;
                                goto exit_dcv;
                            }
                        }
                        else
                    #endif
                        {
                        #ifndef WOLFSSL_SMALL_STACK
                            byte  encodedSig[MAX_ENCODED_SIG_SZ];
                        #else
                            byte* encodedSig = (byte*)XMALLOC(MAX_ENCODED_SIG_SZ,
                                             ssl->heap, DYNAMIC_TYPE_SIGNATURE);
                            if (encodedSig == NULL) {
                                ERROR_OUT(MEMORY_E, exit_dcv);
                            }
                        #endif


                            args->sigSz = wc_EncodeSignature(encodedSig,
                                ssl->buffers.digest.buffer,
                                ssl->buffers.digest.length,
                                TypeHash(ssl->options.peerHashAlgo));

                            if (args->sendSz != args->sigSz || !args->output ||
                                XMEMCMP(args->output, encodedSig,
                                   min(args->sigSz, MAX_ENCODED_SIG_SZ)) != 0) {
                                ret = VERIFY_CERT_ERROR;
                            }

                            WC_FREE_VAR_EX(encodedSig, ssl->heap,
                                DYNAMIC_TYPE_SIGNATURE);
                        }
                    }
                    else {
                        if (args->sendSz != FINISHED_SZ || !args->output ||
                            XMEMCMP(args->output,
                                &ssl->hsHashes->certHashes, FINISHED_SZ) != 0) {
                            ret = VERIFY_CERT_ERROR;
                        }
                    }
                    if (ret == 0) {
                        /* SERVER: Data verified with cert's public key. */
                        ssl->options.peerAuthGood = ssl->options.havePeerCert &&
                                                    (ret == 0);
                    }
                }
            #endif /* !NO_RSA */
                if (ret != 0)
                    break;

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_FINALIZE;
            } /* case TLS_ASYNC_VERIFY */
            FALL_THROUGH;

            case TLS_ASYNC_FINALIZE:
            {
                if (IsEncryptionOn(ssl, 0))
                    args->idx += ssl->keys.padSz;

                ssl->options.havePeerVerify = 1;

                /* Set final index */
                args->idx += args->sz;
                *inOutIdx = args->idx;

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_END;
            } /* case TLS_ASYNC_FINALIZE */
            FALL_THROUGH;

            case TLS_ASYNC_END:
            {
                break;
            }
            default:
                ret = INPUT_CASE_ERROR;
        } /* switch(ssl->options.asyncState) */

    exit_dcv:

        WOLFSSL_LEAVE("DoCertificateVerify", ret);
        WOLFSSL_END(WC_FUNC_CERTIFICATE_VERIFY_DO);

    #ifdef WOLFSSL_ASYNC_CRYPT
        /* Handle async operation */
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            /* Mark message as not received so it can process again */
            ssl->msgsReceived.got_certificate_verify = 0;

            return ret;
        }
    #endif /* WOLFSSL_ASYNC_CRYPT */
    #ifdef WOLFSSL_EXTRA_ALERTS
        if (ret == WC_NO_ERR_TRACE(BUFFER_ERROR))
            SendAlert(ssl, alert_fatal, decode_error);
        else if (ret == WC_NO_ERR_TRACE(SIG_VERIFY_E))
            SendAlert(ssl, alert_fatal, decrypt_error);
        else if (ret != 0)
            SendAlert(ssl, alert_fatal, bad_certificate);
    #endif
        /* Digest is not allocated, so do this to prevent free */
        if(ssl->buffers.digest.buffer) {
            if (!ssl->options.dontFreeDigest) {
                /*This should not happen*/
                XFREE(ssl->buffers.digest.buffer,
                    ssl->heap, DYNAMIC_TYPE_DIGEST);
            }
        }
        ssl->buffers.digest.buffer = NULL;
        ssl->buffers.digest.length = 0;
        ssl->options.dontFreeDigest = 0;

    #ifdef WOLFSSL_ASYNC_CRYPT
        /* Cleanup async */
        FreeAsyncCtx(ssl, 0);
    #else
        FreeDcvArgs(ssl, args);
    #endif
        /* Final cleanup */
        FreeKeyExchange(ssl);

        if (ret != 0) {
            WOLFSSL_ERROR_VERBOSE(ret);
        }

        return ret;
    }

#endif /* (!NO_RSA || ECC || ED25519 || ED448) && !WOLFSSL_NO_CLIENT_AUTH */

    /* handle generation of server_hello_done (14) */
    int SendServerHelloDone(WOLFSSL* ssl)
    {
        byte* output;
        int   sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
        int   ret;

        WOLFSSL_START(WC_FUNC_SERVER_HELLO_DONE_SEND);
        WOLFSSL_ENTER("SendServerHelloDone");

    #ifdef WOLFSSL_DTLS
        if (ssl->options.dtls)
            sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
    #endif

        if (IsEncryptionOn(ssl, 1))
            sendSz += MAX_MSG_EXTRA;

        /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
         * is not advanced yet */
        ssl->options.buildingMsg = 1;

        /* check for available size */
        if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
            return ret;

        /* get output buffer */
        output = GetOutputBuffer(ssl);
        AddHeaders(output, 0, server_hello_done, ssl);
        ret = BuildMsgOrHashOutput(ssl, output, &sendSz,
                 HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ +
                   (ssl->options.dtls ?
                       DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA : 0),
                 server_hello_done, "ServerHelloDone");
        if (ret != 0)
            return ret;

        ssl->options.serverState = SERVER_HELLODONE_COMPLETE;

        ret = SendBuffered(ssl);

        WOLFSSL_LEAVE("SendServerHelloDone", ret);
        WOLFSSL_END(WC_FUNC_SERVER_HELLO_DONE_SEND);

        return ret;
    }

#endif /* !WOLFSSL_NO_TLS12 */

#ifdef HAVE_SESSION_TICKET

#ifdef WOLFSSL_TICKET_HAVE_ID
    static void GetRealSessionID(WOLFSSL* ssl, const byte** id, byte* idSz)
    {
        if (ssl->session->haveAltSessionID) {
            *id = ssl->session->altSessionID;
            *idSz = ID_LEN;
        }
        else if (!IsAtLeastTLSv1_3(ssl->version) && ssl->arrays != NULL) {
            *id = ssl->arrays->sessionID;
            *idSz = ssl->arrays->sessionIDSz;
        }
        else {
            *id = ssl->session->sessionID;
            *idSz = ssl->session->sessionIDSz;
        }
    }
#endif

    int SetupTicket(WOLFSSL* ssl)
    {
        int ret = 0;

        (void)ssl;

#ifdef WOLFSSL_TLS13
        {
            /* Client adds to ticket age to obfuscate. */
            byte ageAdd[AGEADD_LEN]; /* Obfuscation of age */
            ret = wc_RNG_GenerateBlock(ssl->rng, ageAdd, AGEADD_LEN);
            if (ret != 0)
                return ret;
            ato32(ageAdd, &ssl->session->ticketAdd);
        }
#endif

#ifdef WOLFSSL_TICKET_HAVE_ID
        {
            const byte* id = NULL;
            byte idSz = 0;

            GetRealSessionID(ssl, &id, &idSz);
            if (idSz == 0) {
                ret = wc_RNG_GenerateBlock(ssl->rng, ssl->session->altSessionID,
                                           ID_LEN);
                if (ret != 0)
                    return ret;
                ssl->session->haveAltSessionID = 1;
            }
        }
#endif
        return ret;
    }

    /* create a new session ticket, 0 on success
     * Do any kind of setup in SetupTicket */
    int CreateTicket(WOLFSSL* ssl)
    {
        InternalTicket* it;
        ExternalTicket* et;
        int encLen;
        int ret;
        int error;
        word32 itHash = 0;
        byte zeros[WOLFSSL_TICKET_MAC_SZ];   /* biggest cmp size */
        int internalTicketSz;
        byte* mac;
#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
    !defined(NO_CERT_IN_TICKET)
        word16 peerCertSz = 0;
#endif

        WOLFSSL_ASSERT_GE(sizeof(ssl->session->staticTicket), WOLFSSL_TICKET_ENC_SZ);
        WOLFSSL_ASSERT_SIZEOF_GE(ssl->session->staticTicket, *et);

        if (ssl->session->ticket != ssl->session->staticTicket) {
            /* Always use the static ticket buffer */
            XFREE(ssl->session->ticket, NULL, DYNAMIC_TYPE_SESSION_TICK);
            ssl->session->ticket = ssl->session->staticTicket;
            ssl->session->ticketLenAlloc = 0;
        }

        et = (ExternalTicket*)ssl->session->ticket;
        it = (InternalTicket*)et->enc_ticket;

    #ifdef WOLFSSL_ASYNC_CRYPT
        if (ssl->error != WC_NO_ERR_TRACE(WC_PENDING_E))
    #endif
        {
            XMEMSET(ssl->session->ticket, 0, SESSION_TICKET_LEN);
        }

        /* build internal */
        it->pv.major = ssl->version.major;
        it->pv.minor = ssl->version.minor;

        it->suite[0] = ssl->options.cipherSuite0;
        it->suite[1] = ssl->options.cipherSuite;

    #ifdef WOLFSSL_EARLY_DATA
        c32toa(ssl->options.maxEarlyDataSz, it->maxEarlyDataSz);
    #endif

        if (!ssl->options.tls1_3) {
            if (ssl->arrays == NULL) {
                WOLFSSL_MSG("CreateTicket called with null arrays");
                ret = BAD_FUNC_ARG;
                goto error;
            }
            XMEMCPY(it->msecret, ssl->arrays->masterSecret, SECRET_LEN);
#ifndef NO_ASN_TIME
            c32toa(LowResTimer(), it->timestamp);
#endif
            it->haveEMS = (byte) ssl->options.haveEMS;
        }
        else {
#ifdef WOLFSSL_TLS13
        #ifdef WOLFSSL_32BIT_MILLI_TIME
            word32 now = TimeNowInMilliseconds();
        #else
            sword64 now = TimeNowInMilliseconds();
        #endif
            if (now == 0) {
                ret = GETTIME_ERROR;
                goto error;
            }

            c32toa(ssl->session->ticketAdd, it->ageAdd);
            c16toa(ssl->session->namedGroup, it->namedGroup);
        #ifdef WOLFSSL_32BIT_MILLI_TIME
            c32toa(now, it->timestamp);
        #else
            c32toa((word32)(now >> 32), it->timestamp);
            c32toa((word32)now        , it->timestamp + OPAQUE32_LEN);
        #endif
            /* Resumption master secret. */
            XMEMCPY(it->msecret, ssl->session->masterSecret, SECRET_LEN);
            if (ssl->session->ticketNonce.len > MAX_TICKET_NONCE_STATIC_SZ) {
                WOLFSSL_MSG("Bad ticket nonce value");
                ret = BAD_TICKET_MSG_SZ;
                goto error;
            }
            XMEMCPY(it->ticketNonce, ssl->session->ticketNonce.data,
                ssl->session->ticketNonce.len);
            it->ticketNonceLen = ssl->session->ticketNonce.len;
#endif
        }

#ifdef OPENSSL_EXTRA
        it->sessionCtxSz = ssl->sessionCtxSz;
        XMEMCPY(it->sessionCtx, ssl->sessionCtx, ID_LEN);
#endif

#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
    !defined(NO_CERT_IN_TICKET)
        /* Store peer certificate in ticket for session resumption.
         * Try ssl->peerCert first, then ssl->session->chain as fallback.
         * Skip for DTLS to keep ticket size small for MTU constraints. */
        if (ssl->options.dtls) {
            c16toa(0, it->peerCertLen);
            peerCertSz = 0;
        }
        else {
            const byte* certDer = NULL;
            word32 certDerSz = 0;

            if (ssl->peerCert.derCert != NULL &&
                    ssl->peerCert.derCert->length > 0) {
                /* Use current peer certificate */
                certDer = ssl->peerCert.derCert->buffer;
                certDerSz = ssl->peerCert.derCert->length;
            }
#ifdef SESSION_CERTS
            else if (ssl->session->chain.count > 0) {
                /* Use peer certificate from session chain */
                certDer = ssl->session->chain.certs[0].buffer;
                certDerSz = ssl->session->chain.certs[0].length;
            }
#endif

            if (certDer != NULL && certDerSz > 0 &&
                    certDerSz <= MAX_TICKET_PEER_CERT_SZ
#ifdef HAVE_MAX_FRAGMENT
                    /* We don't support fragmentation in
                     * SendTls13NewSessionTicket yet. */
                    && (!IsAtLeastTLSv1_3(ssl->version) ||
                        ssl->max_fragment == MAX_RECORD_SIZE)
#endif
                    ) {
                c16toa((word16)certDerSz, it->peerCertLen);
                XMEMCPY(it->peerCert, certDer, certDerSz);
                peerCertSz = (word16)certDerSz;
            }
            else {
                if (certDerSz > MAX_TICKET_PEER_CERT_SZ)
                    WOLFSSL_MSG("Peer cert too large for ticket, skipping");
                c16toa(0, it->peerCertLen);
                peerCertSz = 0;
            }
        }
#endif

#ifdef WOLFSSL_TICKET_HAVE_ID
        {
            const byte* id = NULL;
            byte idSz = 0;
            GetRealSessionID(ssl, &id, &idSz);
            /* make sure idSz is not larger than ID_LEN */
            if (idSz > ID_LEN)
                idSz = ID_LEN;
            XMEMCPY(it->id, id, idSz);
        }
#endif

        internalTicketSz = (int)WOLFSSL_INTERNAL_TICKET_BASE_SZ;
#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
    !defined(NO_CERT_IN_TICKET)
        internalTicketSz += peerCertSz;
#endif
#ifdef WOLFSSL_TICKET_ENC_CBC_HMAC
        internalTicketSz = (internalTicketSz + 15) & (~0xf);
#endif
        /* MAC is placed after the encrypted data */
        mac = et->enc_ticket + WOLFSSL_TICKET_ENC_SZ;

        /* encrypt */
        encLen = WOLFSSL_TICKET_ENC_SZ;  /* max size user can use */
        if (ssl->ctx->ticketEncCb == NULL
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || defined(WOLFSSL_WPAS_SMALL)
                ||
                /* WOLFSSL_OP_NO_TICKET turns off tickets in <= 1.2. Forces
                 * "stateful" tickets for 1.3 so just use the regular
                 * stateless ones. */
                (!IsAtLeastTLSv1_3(ssl->version) &&
                        (ssl->options.mask & WOLFSSL_OP_NO_TICKET) != 0)
#endif
                        ) {
            /* Use BAD_TICKET_ENCRYPT to signal missing ticket callback */
            ret = BAD_TICKET_ENCRYPT;
        }
        else {
            itHash = HashObject((byte*)it, (word32)internalTicketSz, &error);
            if (error == 0) {
                ret = ssl->ctx->ticketEncCb(ssl, et->key_name, et->iv, mac,
                        1, et->enc_ticket, internalTicketSz, &encLen,
                        SSL_TICKET_CTX(ssl));
            }
            else {
                ret = WOLFSSL_TICKET_RET_FATAL;
            }
        }
        if (ret != WOLFSSL_TICKET_RET_OK) {
#ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                return ret;
            }
#endif
            goto error;
        }
        if (encLen < internalTicketSz ||
                encLen > (int)WOLFSSL_TICKET_ENC_SZ) {
            WOLFSSL_MSG("Bad user ticket encrypt size");
            ret = BAD_TICKET_KEY_CB_SZ;
        }

        /* sanity checks on encrypt callback */

        /* internal ticket can't be the same if encrypted */
        if (itHash == HashObject((byte*)it, (word32)internalTicketSz, &error) ||
                error != 0)
        {
            WOLFSSL_MSG("User ticket encrypt didn't encrypt or hash failed");
            ret = BAD_TICKET_ENCRYPT;
            goto error;
        }

        XMEMSET(zeros, 0, sizeof(zeros));

        /* name */
        if (XMEMCMP(et->key_name, zeros, WOLFSSL_TICKET_NAME_SZ) == 0) {
            WOLFSSL_MSG("User ticket encrypt didn't set name");
            ret = BAD_TICKET_ENCRYPT;
            goto error;
        }

        /* iv */
        if (XMEMCMP(et->iv, zeros, WOLFSSL_TICKET_IV_SZ) == 0) {
            WOLFSSL_MSG("User ticket encrypt didn't set iv");
            ret = BAD_TICKET_ENCRYPT;
            goto error;
        }

        /* mac */
        if (XMEMCMP(mac, zeros, WOLFSSL_TICKET_MAC_SZ) == 0) {
            WOLFSSL_MSG("User ticket encrypt didn't set mac");
            ret = BAD_TICKET_ENCRYPT;
            goto error;
        }

        /* set size */
        c16toa((word16)encLen, et->enc_len);
        if (encLen < (int)WOLFSSL_TICKET_ENC_SZ) {
            /* move mac up since whole enc buffer not used */
            XMEMMOVE(et->enc_ticket + encLen, mac,
                    WOLFSSL_TICKET_MAC_SZ);
        }
        ssl->session->ticketLen =
                (word16)(encLen + WOLFSSL_TICKET_FIXED_SZ);

        return ret;
    error:
#ifdef WOLFSSL_CHECK_MEM_ZERO
        /* Ticket has sensitive data in it now. */
        wc_MemZero_Add("Create Ticket internal", it,
                       WOLFSSL_INTERNAL_TICKET_MAX_SZ);
#endif
        ForceZero(it, WOLFSSL_INTERNAL_TICKET_MAX_SZ);
#ifdef WOLFSSL_CHECK_MEM_ZERO
        wc_MemZero_Check(it, WOLFSSL_INTERNAL_TICKET_MAX_SZ);
#endif
        WOLFSSL_ERROR_VERBOSE(ret);
        return ret;

    }

    int DoDecryptTicket(const WOLFSSL* ssl, const byte* input, word32 len,
        InternalTicket **it)
    {
        ExternalTicket* et;
        int             ret;
        int             outLen;
        word16          inLen;

        WOLFSSL_START(WC_FUNC_TICKET_DO);
        WOLFSSL_ENTER("DoDecryptTicket");

        if (len > SESSION_TICKET_LEN ||
            len < (word32)(WOLFSSL_INTERNAL_TICKET_LEN +
                           WOLFSSL_TICKET_FIXED_SZ)) {
            WOLFSSL_ERROR_VERBOSE(BAD_TICKET_MSG_SZ);
            return WOLFSSL_TICKET_RET_REJECT;
        }

        et = (ExternalTicket*)input;

        /* decrypt */
        ato16(et->enc_len, &inLen);
        if (inLen > WOLFSSL_TICKET_ENC_SZ) {
            WOLFSSL_ERROR_VERBOSE(BAD_TICKET_MSG_SZ);
            return WOLFSSL_TICKET_RET_REJECT;
        }
        if ((word32)inLen + WOLFSSL_TICKET_FIXED_SZ > len) {
            WOLFSSL_ERROR_VERBOSE(BAD_TICKET_MSG_SZ);
            return WOLFSSL_TICKET_RET_REJECT;
        }
        outLen = (int)inLen;   /* may be reduced by user padding */

        if (ssl->ctx->ticketEncCb == NULL
#if defined(OPENSSL_EXTRA) || defined(HAVE_WEBSERVER) || defined(WOLFSSL_WPAS_SMALL)
                ||
                /* WOLFSSL_OP_NO_TICKET turns off tickets in < 1.2. Forces
                 * "stateful" tickets for 1.3 so just use the regular
                 * stateless ones. */
                (!IsAtLeastTLSv1_3(ssl->version) &&
                        (ssl->options.mask & WOLFSSL_OP_NO_TICKET) != 0)
#endif
                        ) {
            /* Use BAD_TICKET_ENCRYPT to signal missing ticket callback */
            WOLFSSL_ERROR_VERBOSE(BAD_TICKET_ENCRYPT);
            ret = WOLFSSL_TICKET_RET_REJECT;
        }
        else {
            /* Callback uses ssl without const but for DTLS, it really shouldn't
             * modify its state. MAC is located after encrypted data. */
            ret = ssl->ctx->ticketEncCb((WOLFSSL*)ssl, et->key_name, et->iv,
                                    et->enc_ticket + inLen, 0,
                                    et->enc_ticket, inLen, &outLen,
                                    SSL_TICKET_CTX(ssl));
        }
        if (ret != WOLFSSL_TICKET_RET_OK) {
        #ifdef WOLFSSL_ASYNC_CRYPT
            if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
                return ret;
            }
        #endif /* WOLFSSL_ASYNC_CRYPT */
            if (ret != WOLFSSL_TICKET_RET_CREATE) {
                WOLFSSL_ERROR_VERBOSE(BAD_TICKET_KEY_CB_SZ);
                return WOLFSSL_TICKET_RET_REJECT;
            }
        }
        if (outLen > (int)inLen || outLen < (int)WOLFSSL_INTERNAL_TICKET_LEN) {
            WOLFSSL_MSG("Bad user ticket decrypt len");
            WOLFSSL_ERROR_VERBOSE(BAD_TICKET_KEY_CB_SZ);
            return BAD_TICKET_KEY_CB_SZ;
        }
        *it = (InternalTicket*)et->enc_ticket;
        return ret;
    }

    static int DoClientTicketCheckVersion(const WOLFSSL* ssl,
            InternalTicket* it)
    {
        if (ssl->version.minor < it->pv.minor) {
            WOLFSSL_MSG("Ticket has greater version");
            return VERSION_ERROR;
        }
        else if (ssl->version.minor > it->pv.minor) {
            if (IsAtLeastTLSv1_3(it->pv) != IsAtLeastTLSv1_3(ssl->version)) {
                WOLFSSL_MSG("Tickets cannot be shared between "
                                           "TLS 1.3 and TLS 1.2 and lower");
                return VERSION_ERROR;
            }

            if (!ssl->options.downgrade) {
                WOLFSSL_MSG("Ticket has lesser version");
                return VERSION_ERROR;
            }

            WOLFSSL_MSG("Downgrading protocol due to ticket");

            if (it->pv.minor < ssl->options.minDowngrade) {
                WOLFSSL_MSG("Ticket has lesser version than allowed");
                return VERSION_ERROR;
            }
        }
    #ifdef WOLFSSL_TLS13
        /* Check resumption master secret. */
        if (IsAtLeastTLSv1_3(it->pv) &&
                it->ticketNonceLen > MAX_TICKET_NONCE_STATIC_SZ) {
            WOLFSSL_MSG("Unsupported ticketNonce len in ticket");
            return BAD_TICKET_ENCRYPT;
        }
    #endif
        return 0;
    }

#if defined(WOLFSSL_TLS13)
    /* Return 0 when check successful. <0 on failure. */
    int DoClientTicketCheck(const WOLFSSL* ssl, const PreSharedKey* psk,
            sword64 timeout, const byte* suite)
    {
        word32 ticketAdd;
#ifdef WOLFSSL_32BIT_MILLI_TIME
        word32 now;
        sword64 diff;
        word32 ticketSeen;        /* Time ticket seen (ms) */

        ato32(psk->it->timestamp, &ticketSeen);

        now = TimeNowInMilliseconds();
        if (now == 0)
            return GETTIME_ERROR;
        /* Difference between now and time ticket constructed
         * (from decrypted ticket). */
        diff = now;
        diff -= ticketSeen;
        if (diff > timeout * 1000 ||
            diff > (sword64)TLS13_MAX_TICKET_AGE * 1000)
            return WOLFSSL_FATAL_ERROR;
#else
        sword64 diff;
        sword64 ticketSeen; /* Time ticket seen (ms) */
        word32 seenHi, seenLo;

        ato32(psk->it->timestamp               , &seenHi);
        ato32(psk->it->timestamp + OPAQUE32_LEN, &seenLo);
        ticketSeen = ((sword64)seenHi << 32) + seenLo;

        diff = TimeNowInMilliseconds();
        if (diff == 0)
            return GETTIME_ERROR;
        /* Difference between now and time ticket constructed
         * (from decrypted ticket). */
        diff -= ticketSeen;
        if (diff > timeout * 1000 ||
            diff > (sword64)TLS13_MAX_TICKET_AGE * 1000)
            return WOLFSSL_FATAL_ERROR;
#endif
        ato32(psk->it->ageAdd, &ticketAdd);
        /* Subtract client's ticket age and unobfuscate. */
        diff -= psk->ticketAge;
        diff += ticketAdd;
        /* Check session and ticket age timeout.
         * Allow +/- 1000 milliseconds on ticket age.
         */
        if (diff < -1000 || diff - MAX_TICKET_AGE_DIFF * 1000 > 1000)
            return WOLFSSL_FATAL_ERROR;

#if !defined(WOLFSSL_PSK_ONE_ID) && !defined(WOLFSSL_PRIORITIZE_PSK)
        /* Check whether resumption is possible based on suites in SSL and
         * ciphersuite in ticket.
         */
        (void)ssl;
        if (XMEMCMP(suite, psk->it->suite, SUITE_LEN) != 0)
            return WOLFSSL_FATAL_ERROR;
#else
        (void)suite;
        if (!FindSuiteSSL(ssl, psk->it->suite))
            return WOLFSSL_FATAL_ERROR;
#endif
#ifdef OPENSSL_EXTRA
        if (ssl->sessionCtxSz > 0 &&
               (psk->it->sessionCtxSz != ssl->sessionCtxSz ||
                XMEMCMP(psk->it->sessionCtx, ssl->sessionCtx,
                        ssl->sessionCtxSz) != 0))
            return WOLFSSL_FATAL_ERROR;
#endif
        return 0;
    }
#endif /* WOLFSSL_SLT13 */

#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
    !defined(NO_CERT_IN_TICKET)
    static void RestorePeerCertFromTicket(WOLFSSL* ssl, InternalTicket* it)
    {
        word16 peerCertLen = 0;
        ato16(it->peerCertLen, &peerCertLen);

        if (peerCertLen > 0 && peerCertLen <= MAX_TICKET_PEER_CERT_SZ) {
#ifdef SESSION_CERTS
            /* Clear existing chain and add the peer certificate */
            ssl->session->chain.count = 0;
            AddSessionCertToChain(&ssl->session->chain,
                                  it->peerCert, peerCertLen);
#endif
            /* Also decode into ssl->peerCert for direct access */
            {
                int ret;
                DecodedCert* dCert;

                dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), ssl->heap,
                                               DYNAMIC_TYPE_DCERT);
                if (dCert != NULL) {
                    InitDecodedCert(dCert, it->peerCert, peerCertLen, ssl->heap);
                    ret = ParseCertRelative(dCert, CERT_TYPE, 0, NULL, NULL);
                    if (ret == 0) {
                        FreeX509(&ssl->peerCert);
                        InitX509(&ssl->peerCert, 0, ssl->heap);
                        ret = CopyDecodedToX509(&ssl->peerCert, dCert);
                        if (ret != 0) {
                            /* Failed to copy - clear peerCert */
                            FreeX509(&ssl->peerCert);
                            InitX509(&ssl->peerCert, 0, ssl->heap);
                        }
                    }
                    FreeDecodedCert(dCert);
                    XFREE(dCert, ssl->heap, DYNAMIC_TYPE_DCERT);
                }
            }
        }
    }
#endif /* OPENSSL_ALL && KEEP_PEER_CERT && !NO_CERT_IN_TICKET */

    void DoClientTicketFinalize(WOLFSSL* ssl, InternalTicket* it,
            const WOLFSSL_SESSION* sess)
    {
#ifdef WOLFSSL_TICKET_HAVE_ID
        ssl->session->haveAltSessionID = 1;
        XMEMCPY(ssl->session->altSessionID, it->id, ID_LEN);
#endif
        if (sess != NULL) {
            byte bogusID[ID_LEN];
            byte bogusIDSz = ssl->session->sessionIDSz;
            XMEMCPY(bogusID, ssl->session->sessionID, ID_LEN);
            /* Failure here should not interrupt the resumption. We already have
             * all the cipher material we need in `it` */
            WOLFSSL_MSG("Copying in session from passed in arg");
            (void)wolfSSL_DupSession(sess, ssl->session, 1);
            /* Restore the fake ID */
            XMEMCPY(ssl->session->sessionID, bogusID, ID_LEN);
            ssl->session->sessionIDSz= bogusIDSz;
        }
#ifdef WOLFSSL_TICKET_HAVE_ID
        else {
            if (wolfSSL_GetSession(ssl, NULL, 1) != NULL) {
                WOLFSSL_MSG("Found session matching the session id"
                            " found in the ticket");
            }
            else {
                WOLFSSL_MSG("Can't find session matching the session id"
                            " found in the ticket");
            }
        }
#endif

        if (!IsAtLeastTLSv1_3(ssl->version)) {
            XMEMCPY(ssl->arrays->masterSecret, it->msecret, SECRET_LEN);
            /* Copy the haveExtendedMasterSecret property from the ticket to
             * the saved session, so the property may be checked later. */
            ssl->session->haveEMS = it->haveEMS;
            ato32((const byte*)&it->timestamp, &ssl->session->bornOn);
#ifndef NO_RESUME_SUITE_CHECK
            ssl->session->cipherSuite0 = it->suite[0];
            ssl->session->cipherSuite = it->suite[1];
#endif
        }
        else {
#ifdef WOLFSSL_TLS13
            /* This should have been already checked in
             * DoClientTicketCheckVersion */
            if (it->ticketNonceLen > MAX_TICKET_NONCE_STATIC_SZ) {
                WOLFSSL_MSG("Unsupported ticketNonce len in ticket");
                return;
            }
            /* Restore information to renegotiate. */
#ifdef WOLFSSL_32BIT_MILLI_TIME
            ato32(it->timestamp, &ssl->session->ticketSeen);
#else
            {
                word32 seenHi, seenLo;

                ato32(it->timestamp               , &seenHi);
                ato32(it->timestamp + OPAQUE32_LEN, &seenLo);
                ssl->session->ticketSeen = ((sword64)seenHi << 32) + seenLo;
            }
#endif
            ato32(it->ageAdd, &ssl->session->ticketAdd);
            ssl->session->cipherSuite0 = it->suite[0];
            ssl->session->cipherSuite = it->suite[1];
#ifdef WOLFSSL_EARLY_DATA
            ato32(it->maxEarlyDataSz, &ssl->session->maxEarlyDataSz);
#endif
            /* Resumption master secret. */
            XMEMCPY(ssl->session->masterSecret, it->msecret, SECRET_LEN);
#if defined(WOLFSSL_TICKET_NONCE_MALLOC) && \
     (!defined(HAVE_FIPS) || (defined(FIPS_VERSION_GE) && FIPS_VERSION_GE(5,3)))
            if (ssl->session->ticketNonce.data
                   != ssl->session->ticketNonce.dataStatic) {
                XFREE(ssl->session->ticketNonce.data, ssl->heap,
                    DYNAMIC_TYPE_SESSION_TICK);
                ssl->session->ticketNonce.data =
                    ssl->session->ticketNonce.dataStatic;
            }
#endif /* defined(WOLFSSL_TICKET_NONCE_MALLOC) && FIPS_VERSION_GE(5,3) */
            XMEMCPY(ssl->session->ticketNonce.data, it->ticketNonce,
                it->ticketNonceLen);
            ssl->session->ticketNonce.len = it->ticketNonceLen;
            ato16(it->namedGroup, &ssl->session->namedGroup);
#endif
        }

#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
    !defined(NO_CERT_IN_TICKET)
        RestorePeerCertFromTicket(ssl, it);
#endif

        ssl->version.minor = it->pv.minor;
    }

#if defined(WOLFSSL_TLS13)
    static void PopulateInternalTicketFromSession(const WOLFSSL_SESSION* sess,
            InternalTicket* it)
    {
#ifdef WOLFSSL_32BIT_MILLI_TIME
        word32 milliBornOn = sess->bornOn;
#else
        sword64 milliBornOn = (sword64)sess->bornOn;
#endif
        /* Convert to milliseconds */
        milliBornOn *= 1000;
        it->pv = sess->version;
        it->suite[0] = sess->cipherSuite0;
        it->suite[1] = sess->cipherSuite;
        XMEMCPY(it->msecret, sess->masterSecret, SECRET_LEN);
#ifdef WOLFSSL_32BIT_MILLI_TIME
        c32toa(milliBornOn, it->timestamp);
#else
        c32toa((word32)(milliBornOn >> 32), it->timestamp);
        c32toa((word32)milliBornOn        , it->timestamp + OPAQUE32_LEN);
#endif
        it->haveEMS = (byte)sess->haveEMS;
        c32toa(sess->ticketAdd, it->ageAdd);
        c16toa(sess->namedGroup, it->namedGroup);
        if (sess->ticketNonce.len <= MAX_TICKET_NONCE_STATIC_SZ) {
            it->ticketNonceLen = sess->ticketNonce.len;
            XMEMCPY(it->ticketNonce, sess->ticketNonce.data,
                    sess->ticketNonce.len);
        }
#ifdef WOLFSSL_EARLY_DATA
        c32toa(sess->maxEarlyDataSz, it->maxEarlyDataSz);
#endif
#ifdef WOLFSSL_TICKET_HAVE_ID
        if (sess->haveAltSessionID)
            XMEMCPY(it->id, sess->altSessionID, ID_LEN);
        else
            XMEMCPY(it->id, sess->sessionID, ID_LEN);
#endif
#ifdef OPENSSL_EXTRA
        it->sessionCtxSz = sess->sessionCtxSz;
        XMEMCPY(it->sessionCtx, sess->sessionCtx, sess->sessionCtxSz);
#endif
#if defined(OPENSSL_ALL) && defined(KEEP_PEER_CERT) && \
    defined(SESSION_CERTS) && !defined(NO_CERT_IN_TICKET)
        /* Store peer certificate from session chain */
        if (sess->chain.count > 0) {
            word32 certLen = sess->chain.certs[0].length;
            if (certLen <= MAX_TICKET_PEER_CERT_SZ) {
                c16toa((word16)certLen, it->peerCertLen);
                XMEMCPY(it->peerCert, sess->chain.certs[0].buffer, certLen);
            }
            else {
                c16toa(0, it->peerCertLen);
            }
        }
        else {
            c16toa(0, it->peerCertLen);
        }
#endif
    }


    static const WOLFSSL_SESSION* GetSesionFromCacheOrExt(const WOLFSSL* ssl,
            const byte* id, psk_sess_free_cb_ctx* freeCtx)
    {
        const WOLFSSL_SESSION* sess = NULL;
        int ret;
        XMEMSET(freeCtx, 0, sizeof(*freeCtx));
#ifdef HAVE_EXT_CACHE
        if (ssl->ctx->get_sess_cb != NULL) {
            int copy = 0;
            sess = ssl->ctx->get_sess_cb((WOLFSSL*)ssl,
                    id, ID_LEN, &copy);
            if (sess != NULL) {
                freeCtx->extCache = 1;
                /* If copy not set then free immediately */
                if (!copy)
                    freeCtx->freeSess = 1;
            }
        }
#endif
        if (sess == NULL) {
            ret = TlsSessionCacheGetAndRdLock(id, &sess, &freeCtx->row,
                    (byte)ssl->options.side);
            if (ret != 0)
                sess = NULL;
        }
        return sess;
    }

    static void FreeSessionFromCacheOrExt(const WOLFSSL* ssl,
            const WOLFSSL_SESSION* sess, psk_sess_free_cb_ctx* freeCtx)
    {
        (void)ssl;
        (void)sess;
#ifdef HAVE_EXT_CACHE
        if (freeCtx->extCache) {
            if (freeCtx->freeSess)
                /* In this case sess is not longer const and the external cache
                 * wants us to free it. */
                wolfSSL_FreeSession(ssl->ctx, (WOLFSSL_SESSION*)sess);
        }
        else
#endif
            TlsSessionCacheUnlockRow(freeCtx->row);
    }

    /* Parse ticket sent by client, returns callback return value. Doesn't
     * modify ssl and stores the InternalTicket inside psk */
    int DoClientTicket_ex(const WOLFSSL* ssl, PreSharedKey* psk, int retainSess)
    {
        int ret;
        int decryptRet = WOLFSSL_TICKET_RET_REJECT;

        WOLFSSL_START(WC_FUNC_TICKET_DO);
        WOLFSSL_ENTER("DoClientTicket_ex");

        if (psk->identityLen == ID_LEN && IsAtLeastTLSv1_3(ssl->version)) {
            /* This is a stateful ticket. We can be sure about this because
             * stateless tickets are much longer. */
            const WOLFSSL_SESSION* sess = NULL;
            sess = GetSesionFromCacheOrExt(ssl, psk->identity,
                    &psk->sess_free_cb_ctx);
            if (sess != NULL) {
                /* Session found in cache. Copy in relevant info to psk */
                byte* tmp;
                WOLFSSL_MSG("Found session matching the session id"
                            " found in the ticket");
                /* Allocate and populate an InternalTicket. Need max size
                 * because PopulateInternalTicketFromSession may write peer cert */
            #ifdef WOLFSSL_NO_REALLOC
                tmp = (byte*)XMALLOC(WOLFSSL_INTERNAL_TICKET_MAX_SZ, ssl->heap,
                        DYNAMIC_TYPE_TLSX);
                if (tmp != NULL && psk->identity != NULL)
                {
                   XMEMCPY(tmp, psk->identity, psk->identityLen);
                   XFREE(psk->identity, ssl->heap, DYNAMIC_TYPE_TLSX);
                   psk->identity = NULL;
                }
            #else
                tmp = (byte*)XREALLOC(psk->identity, WOLFSSL_INTERNAL_TICKET_MAX_SZ,
                        ssl->heap, DYNAMIC_TYPE_TLSX);
            #endif
                if (tmp != NULL) {
                    XMEMSET(tmp, 0, WOLFSSL_INTERNAL_TICKET_MAX_SZ);
                    psk->identity = tmp;
                    psk->identityLen = WOLFSSL_INTERNAL_TICKET_MAX_SZ;
                    psk->it = (InternalTicket*)tmp;
                    PopulateInternalTicketFromSession(sess, psk->it);
                    decryptRet = WOLFSSL_TICKET_RET_OK;
                    if (retainSess) {
                        psk->sess = sess;
                        psk->sess_free_cb = FreeSessionFromCacheOrExt;
                    }
                }
                if (psk->sess == NULL) {
                    FreeSessionFromCacheOrExt(ssl, sess,
                            &psk->sess_free_cb_ctx);
                    XMEMSET(&psk->sess_free_cb_ctx, 0,
                            sizeof(psk_sess_free_cb_ctx));
                }
            }
        }
        else {
            decryptRet = DoDecryptTicket(ssl, psk->identity, psk->identityLen,
                    &psk->it);
        }
        switch (decryptRet) {
        case WOLFSSL_TICKET_RET_OK:
            psk->decryptRet = PSK_DECRYPT_OK;
            break;
        case WOLFSSL_TICKET_RET_CREATE:
            psk->decryptRet = PSK_DECRYPT_CREATE;
            break;
        default:
            psk->decryptRet = PSK_DECRYPT_FAIL;
            WOLFSSL_LEAVE("DoClientTicket_ex", decryptRet);
            return decryptRet;
        }
#ifdef WOLFSSL_CHECK_MEM_ZERO
        /* Internal ticket successfully decrypted. */
        wc_MemZero_Add("Do Client Ticket internal", psk->identity,
            psk->identityLen);
#endif

        ret = DoClientTicketCheckVersion(ssl, psk->it);
        if (ret != 0) {
            psk->decryptRet = PSK_DECRYPT_FAIL;
            ForceZero(psk->identity, psk->identityLen);
#ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Check(psk->it, psk->identityLen);
#endif
            WOLFSSL_LEAVE("DoClientTicket_ex", ret);
            return ret;
        }
        WOLFSSL_LEAVE("DoClientTicket_ex", decryptRet);
        return decryptRet;
    }
#endif /* WOLFSL_TLS13 */

    /* Parse ticket sent by client, returns callback return value */
    int DoClientTicket(WOLFSSL* ssl, const byte* input, word32 len)
    {
        int decryptRet = WOLFSSL_TICKET_RET_REJECT;
        int ret;
        InternalTicket* it = NULL;
#ifdef WOLFSSL_TLS13
        /* Static buffer for stateful tickets - need max size for peer cert */
    #ifdef WOLFSSL_SMALL_STACK
        byte* staticItBuf = NULL;
    #else
        byte staticItBuf[WOLFSSL_INTERNAL_TICKET_MAX_SZ];
    #endif
        InternalTicket* staticIt = NULL;
        const WOLFSSL_SESSION* sess = NULL;
        psk_sess_free_cb_ctx freeCtx;

        XMEMSET(&freeCtx, 0, sizeof(psk_sess_free_cb_ctx));
#endif

        WOLFSSL_START(WC_FUNC_TICKET_DO);
        WOLFSSL_ENTER("DoClientTicket");

#ifdef HAVE_SECRET_CALLBACK
        if (ssl->ticketParseCb != NULL) {
            decryptRet = WOLFSSL_TICKET_RET_OK;
            if (!ssl->ticketParseCb(ssl, input, len, ssl->ticketParseCtx)) {
                /* Failure kills the connection */
                decryptRet = WOLFSSL_TICKET_RET_FATAL;
            }
            else {
                if (wolfSSL_set_SessionTicket(ssl, input, len) !=
                        WOLFSSL_SUCCESS)
                    decryptRet = WOLFSSL_TICKET_RET_REJECT;
            }
            goto cleanup;
        }
        else
#endif
#ifdef WOLFSSL_TLS13
        if (len == ID_LEN && IsAtLeastTLSv1_3(ssl->version)) {
            /* This is a stateful ticket. We can be sure about this because
             * stateless tickets are much longer. */
            sess = GetSesionFromCacheOrExt(ssl, input, &freeCtx);
            if (sess != NULL) {
    #ifdef WOLFSSL_SMALL_STACK
                staticItBuf = (byte*)XMALLOC(WOLFSSL_INTERNAL_TICKET_MAX_SZ,
                                             ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
                if (staticItBuf == NULL) {
                    decryptRet = WOLFSSL_TICKET_RET_FATAL;
                    goto cleanup;
                }
    #endif
                staticIt = (InternalTicket*)staticItBuf;
                it = staticIt;
                XMEMSET(it, 0, WOLFSSL_INTERNAL_TICKET_MAX_SZ);
                PopulateInternalTicketFromSession(sess, it);
                decryptRet = WOLFSSL_TICKET_RET_OK;
            }
        }
        else
#endif
        if (len >= WOLFSSL_INTERNAL_TICKET_LEN + WOLFSSL_TICKET_FIXED_SZ)
            decryptRet = DoDecryptTicket(ssl, input, len, &it);
        else
            WOLFSSL_MSG("Ticket is smaller than minimum size. Rejecting.");


        if (decryptRet != WOLFSSL_TICKET_RET_OK &&
                decryptRet != WOLFSSL_TICKET_RET_CREATE) {
            it = NULL;
            goto cleanup;
        }
    #ifdef WOLFSSL_CHECK_MEM_ZERO
        /* Internal ticket successfully decrypted. Zero at least the minimum
         * internal ticket size (contains master secret). */
        wc_MemZero_Add("Do Client Ticket internal", it,
                       WOLFSSL_INTERNAL_TICKET_LEN);
    #endif

        ret = DoClientTicketCheckVersion(ssl, it);
        if (ret != 0) {
            decryptRet = ret;
            goto cleanup;
        }

        DoClientTicketFinalize(ssl, it, NULL);

cleanup:
        if (it != NULL) {
            /* Zero the minimum internal ticket size. The it pointer points into
             * the input buffer which may be smaller than MAX_SZ. We use the
             * minimum length (WOLFSSL_INTERNAL_TICKET_LEN) which is guaranteed
             * to fit and contains the sensitive master secret. */
            ForceZero(it, WOLFSSL_INTERNAL_TICKET_LEN);
#ifdef WOLFSSL_CHECK_MEM_ZERO
            wc_MemZero_Check(it, WOLFSSL_INTERNAL_TICKET_LEN);
#endif
        }
#ifdef WOLFSSL_TLS13
    #ifdef WOLFSSL_SMALL_STACK
        XFREE(staticItBuf, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
    #endif
        if (sess != NULL)
            FreeSessionFromCacheOrExt(ssl, sess, &freeCtx);
#endif
        return decryptRet;
    }

#ifdef WOLFSSL_TLS13
    void CleanupClientTickets(PreSharedKey* psk)
    {
        for (; psk != NULL; psk = psk->next) {
            if (psk->decryptRet == PSK_DECRYPT_OK ||
                    psk->decryptRet == PSK_DECRYPT_CREATE) {
                psk->decryptRet = PSK_DECRYPT_NONE;
                ForceZero(psk->identity, psk->identityLen);
#ifdef WOLFSSL_CHECK_MEM_ZERO
                wc_MemZero_Check(psk->identity, psk->identityLen);
#endif
            }
        }
    }
#endif /* WOLFSSL_TLS13 */


    /* send Session Ticket */
    int SendTicket(WOLFSSL* ssl)
    {
        byte*              output;
        int                ret;
        int                sendSz;
        word32             length = SESSION_HINT_SZ + LENGTH_SZ;
        word32             idx    = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
        word32             headerSz = 0;

        WOLFSSL_START(WC_FUNC_TICKET_SEND);
        WOLFSSL_ENTER("SendTicket");

        if (ssl->options.createTicket &&
            /* This will be set when SendHandshakeMsg returns WANT_WRITE. Create
             * a new ticket only once. */
            !ssl->options.buildingMsg) {
            ret = SetupTicket(ssl);
            if (ret != 0)
                return ret;
            ret = CreateTicket(ssl);
            if (ret != 0)
                return ret;
        }

        length += ssl->session->ticketLen;
        sendSz = (int)length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;

        if (!ssl->options.dtls) {
            if (IsEncryptionOn(ssl, 1) && ssl->options.handShakeDone)
                sendSz += MAX_MSG_EXTRA;
        }
        else {
        #ifdef WOLFSSL_DTLS
            sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
            idx    += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
        #endif
        }
        headerSz = idx;

        output = (byte*)XMALLOC(sendSz, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
        if (output == NULL)
            return MEMORY_E;

        /* get output buffer */
        AddHeaders(output, length, session_ticket, ssl);

        /* hint */
        c32toa(ssl->ctx->ticketHint, output + idx);
        idx += SESSION_HINT_SZ;

        /* length */
        c16toa(ssl->session->ticketLen, output + idx);
        idx += LENGTH_SZ;

        /* ticket */
        XMEMCPY(output + idx, ssl->session->ticket, ssl->session->ticketLen);
        idx += ssl->session->ticketLen;

        ret = SendHandshakeMsg(ssl, output, idx - headerSz, session_ticket,
                "Session Ticket");
        XFREE(output, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);

        WOLFSSL_LEAVE("SendTicket", ret);
        WOLFSSL_END(WC_FUNC_TICKET_SEND);

        return ret;
    }

#if !defined(WOLFSSL_NO_DEF_TICKET_ENC_CB) && !defined(NO_TLS)

/* Initialize the context for session ticket encryption.
 *
 * @param [in]  ctx     SSL context.
 * @param [in]  keyCtx  Context for session ticket encryption.
 * @return  0 on success.
 * @return  BAD_MUTEX_E when initializing mutex fails.
 */
static int TicketEncCbCtx_Init(WOLFSSL_CTX* ctx, TicketEncCbCtx* keyCtx)
{
    int ret = 0;

    XMEMSET(keyCtx, 0, sizeof(*keyCtx));
    keyCtx->ctx = ctx;

#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Add("TicketEncCbCtx_Init keyCtx->name", keyCtx->name,
        sizeof(keyCtx->name));
    wc_MemZero_Add("TicketEncCbCtx_Init keyCtx->key[0]", keyCtx->key[0],
        sizeof(keyCtx->key[0]));
    wc_MemZero_Add("TicketEncCbCtx_Init keyCtx->key[1]", keyCtx->key[1],
        sizeof(keyCtx->key[1]));
#endif

#ifndef SINGLE_THREADED
    ret = wc_InitMutex(&keyCtx->mutex);
#endif

    return ret;
}

/* Setup the session ticket encryption context for this.
 *
 * Initialize RNG, generate name, generate primary key and set primary key
 * expirary.
 *
 * @param [in]  keyCtx  Context for session ticket encryption.
 * @param [in]  heap    Dynamic memory allocation hint.
 * @param [in]  devId   Device identifier.
 * @return  0 on success.
 * @return  Other value when random number generator fails.
 */
static int TicketEncCbCtx_Setup(TicketEncCbCtx* keyCtx, void* heap, int devId)
{
    int ret;

#ifndef SINGLE_THREADED
    ret = 0;

    /* Check that key wasn't set up while waiting. */
    if (keyCtx->expirary[0] == 0)
#endif
    {
        ret = wc_InitRng_ex(&keyCtx->rng, heap, devId);
        if (ret == 0) {
            ret = wc_RNG_GenerateBlock(&keyCtx->rng, keyCtx->name,
                                       sizeof(keyCtx->name));
        }
        if (ret == 0) {
            /* Mask of the bottom bit - used for index of key. */
            keyCtx->name[WOLFSSL_TICKET_NAME_SZ - 1] &= 0xfe;

            /* Generate initial primary key. */
            ret = wc_RNG_GenerateBlock(&keyCtx->rng, keyCtx->key[0],
                                       WOLFSSL_TICKET_KEY_SZ);
        }
        if (ret == 0) {
            keyCtx->expirary[0] = LowResTimer() + WOLFSSL_TICKET_KEY_LIFETIME;
        }
    }

    return ret;
}
/* Free the context for session ticket encryption.
 *
 * Zeroize keys and name.
 *
 * @param [in]  keyCtx  Context for session ticket encryption.
 */
static void TicketEncCbCtx_Free(TicketEncCbCtx* keyCtx)
{
    /* Zeroize sensitive data. */
    ForceZero(keyCtx->name, sizeof(keyCtx->name));
    ForceZero(keyCtx->key[0], sizeof(keyCtx->key[0]));
    ForceZero(keyCtx->key[1], sizeof(keyCtx->key[1]));

#ifdef WOLFSSL_CHECK_MEM_ZERO
    wc_MemZero_Check(keyCtx->name, sizeof(keyCtx->name));
    wc_MemZero_Check(keyCtx->key[0], sizeof(keyCtx->key[0]));
    wc_MemZero_Check(keyCtx->key[1], sizeof(keyCtx->key[1]));
#endif

#ifndef SINGLE_THREADED
    wc_FreeMutex(&keyCtx->mutex);
#endif
    wc_FreeRng(&keyCtx->rng);
}

#ifdef WOLFSSL_TICKET_ENC_CBC_HMAC
/* Ticket encryption/decryption implementation.
 *
 * @param [in]   key     Key for encryption/decryption and HMAC.
 * @param [in]   keyLen  Length of key in bytes.
 * @param [in]   iv      IV/Nonce for encryption/decryption.
 * @param [in]   aad     Additional authentication data.
 * @param [in]   aadSz   Length of additional authentication data.
 * @param [in]   in      Data to encrypt/decrypt.
 * @param [in]   inLen   Length of encrypted data.
 * @param [out]  out     Resulting data from encrypt/decrypt.
 * @param [out]  outLen  Size of resulting data.
 * @param [in]   tag     Authentication tag for encrypted data.
 * @param [in]   heap    Dynamic memory allocation data hint.
 * @param [in]   enc     1 when encrypting, 0 when decrypting.
 * @return  0 on success.
 * @return  Other value when encryption/decryption fails.
 */
static int TicketEncDec(byte* key, int keyLen, byte* iv, byte* aad, int aadSz,
                        byte* in, int inLen, byte* out, int* outLen, byte* tag,
                        void* heap, int enc)
{
    int ret;
#ifdef WOLFSSL_SMALL_STACK
    Aes*  aes;
    Hmac* hmac;
#else
    Aes  aes[1];
    Hmac hmac[1];
#endif

    (void)heap;

#ifdef WOLFSSL_SMALL_STACK
    aes = (Aes*)XMALLOC(sizeof(Aes), heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (aes == NULL)
        return MEMORY_E;
    hmac = (Hmac*)XMALLOC(sizeof(Hmac), heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (hmac == NULL) {
        XFREE(aes, heap, DYNAMIC_TYPE_TMP_BUFFER);
        return MEMORY_E;
    }
#endif

    XMEMSET(aes, 0, sizeof(Aes));
    XMEMSET(hmac, 0, sizeof(Hmac));

    ret = wc_HmacInit(hmac, heap, DYNAMIC_TYPE_TMP_BUFFER);
    if (ret == 0) {
        ret = wc_HmacSetKey(hmac, WOLFSSL_TICKET_ENC_HMAC, key + keyLen -
            WOLFSSL_TICKET_HMAC_KEY_SZ, WOLFSSL_TICKET_HMAC_KEY_SZ);
    }
    if (ret == 0) {
        ret = wc_HmacUpdate(hmac, aad, aadSz);
    }

    if (ret == 0) {
        if (enc) {
            ret = wc_AesInit(aes, NULL, INVALID_DEVID);
            if (ret == 0) {
                ret = wc_AesSetKey(aes, key,
                    keyLen - WOLFSSL_TICKET_HMAC_KEY_SZ, iv, AES_ENCRYPTION);
            }
            if (ret == 0) {
                ret = wc_HmacUpdate(hmac, in, inLen);
            }
            if (ret == 0) {
                ret = wc_AesCbcEncrypt(aes, in, out, inLen);
            }
            if (ret == 0) {
                XMEMSET(tag, 0, WOLFSSL_TICKET_MAC_SZ);
                ret = wc_HmacFinal(hmac, tag);
            }
            wc_AesFree(aes);
        }
        else {
            unsigned char calcTag[WOLFSSL_TICKET_MAC_SZ];

            ret = wc_AesInit(aes, NULL, INVALID_DEVID);
            if (ret == 0) {
                ret = wc_AesSetKey(aes, key,
                    keyLen - WOLFSSL_TICKET_HMAC_KEY_SZ, iv, AES_DECRYPTION);
            }
            if (ret == 0) {
                ret = wc_AesCbcDecrypt(aes, in, out, inLen);
            }
            if (ret == 0) {
                ret = wc_HmacUpdate(hmac, out, inLen);
            }
            if (ret == 0) {
                XMEMSET(calcTag, 0, WOLFSSL_TICKET_MAC_SZ);
                ret = wc_HmacFinal(hmac, calcTag);
            }
            if (ret == 0) {
                int i;
                calcTag[0] ^= tag[0];
                for (i = 1; i < WOLFSSL_TICKET_MAC_SZ; i++) {
                    calcTag[0] |= calcTag[i] ^ tag[i];
                }
                /* Return a negative value when no match. */
                ret = -calcTag[0];
            }
            wc_AesFree(aes);
        }
    }
    wc_HmacFree(hmac);

    WC_FREE_VAR_EX(hmac, heap, DYNAMIC_TYPE_TMP_BUFFER);
    WC_FREE_VAR_EX(aes, heap, DYNAMIC_TYPE_TMP_BUFFER);

    *outLen = inLen;

    return ret;
}
#elif defined(HAVE_CHACHA) && defined(HAVE_POLY1305) && \
    !defined(WOLFSSL_TICKET_ENC_AES128_GCM) && \
    !defined(WOLFSSL_TICKET_ENC_AES256_GCM)
/* Ticket encryption/decryption implementation.
 *
 * @param [in]   key     Key for encryption/decryption.
 * @param [in]   keyLen  Length of key in bytes.
 * @param [in]   iv      IV/Nonce for encryption/decryption.
 * @param [in]   aad     Additional authentication data.
 * @param [in]   aadSz   Length of additional authentication data.
 * @param [in]   in      Data to encrypt/decrypt.
 * @param [in]   inLen   Length of encrypted data.
 * @param [out]  out     Resulting data from encrypt/decrypt.
 * @param [out]  outLen  Size of resulting data.
 * @param [in]   tag     Authentication tag for encrypted data.
 * @param [in]   heap    Dynamic memory allocation data hint.
 * @param [in]   enc     1 when encrypting, 0 when decrypting.
 * @return  0 on success.
 * @return  Other value when encryption/decryption fails.
 */
static int TicketEncDec(byte* key, int keyLen, byte* iv, byte* aad, int aadSz,
                        byte* in, int inLen, byte* out, int* outLen, byte* tag,
                        void* heap, int enc)
{
    int ret;

    (void)keyLen;
    (void)heap;

    if (enc) {
        ret = wc_ChaCha20Poly1305_Encrypt(key, iv, aad, aadSz, in, inLen, out,
                                          tag);
    }
    else {
        ret = wc_ChaCha20Poly1305_Decrypt(key, iv, aad, aadSz, in, inLen, tag,
                                          out);
    }

    *outLen = inLen;

    return ret;
}
#elif defined(HAVE_AESGCM)
/* Ticket encryption/decryption implementation.
 *
 * @param [in]   key     Key for encryption/decryption.
 * @param [in]   keyLen  Length of key in bytes.
 * @param [in]   iv      IV/Nonce for encryption/decryption.
 * @param [in]   aad     Additional authentication data.
 * @param [in]   aadSz   Length of additional authentication data.
 * @param [in]   in      Data to encrypt/decrypt.
 * @param [in]   inLen   Length of encrypted data.
 * @param [out]  out     Resulting data from encrypt/decrypt.
 * @param [out]  outLen  Size of resulting data.
 * @param [in]   tag     Authentication tag for encrypted data.
 * @param [in]   heap    Dynamic memory allocation data hint.
 * @param [in]   enc     1 when encrypting, 0 when decrypting.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other value when encryption/decryption fails.
 */
static int TicketEncDec(byte* key, int keyLen, byte* iv, byte* aad, int aadSz,
                        byte* in, int inLen, byte* out, int* outLen, byte* tag,
                        void* heap, int enc)
{
    int ret;
    WC_DECLARE_VAR(aes, Aes, 1, 0);

    (void)heap;

    WC_ALLOC_VAR_EX(aes, Aes, 1, heap, DYNAMIC_TYPE_TMP_BUFFER,
        return MEMORY_E);

    if (enc) {
        ret = wc_AesInit(aes, NULL, INVALID_DEVID);
        if (ret == 0) {
            ret = wc_AesGcmSetKey(aes, key, keyLen);
        }
        if (ret == 0) {
            ret = wc_AesGcmEncrypt(aes, in, out, inLen, iv, GCM_NONCE_MID_SZ,
                                   tag, WC_AES_BLOCK_SIZE, aad, aadSz);
        }
        wc_AesFree(aes);
    }
    else {
        ret = wc_AesInit(aes, NULL, INVALID_DEVID);
        if (ret == 0) {
            ret = wc_AesGcmSetKey(aes, key, keyLen);
        }
        if (ret == 0) {
            ret = wc_AesGcmDecrypt(aes, in, out, inLen, iv, GCM_NONCE_MID_SZ,
                                   tag, WC_AES_BLOCK_SIZE, aad, aadSz);
        }
        wc_AesFree(aes);
    }

    WC_FREE_VAR_EX(aes, heap, DYNAMIC_TYPE_TMP_BUFFER);

    *outLen = inLen;

    return ret;
}
#elif defined(WOLFSSL_SM4_GCM)
/* Ticket encryption/decryption implementation.
 *
 * @param [in]   key     Key for encryption/decryption.
 * @param [in]   keyLen  Length of key in bytes.
 * @param [in]   iv      IV/Nonce for encryption/decryption.
 * @param [in]   aad     Additional authentication data.
 * @param [in]   aadSz   Length of additional authentication data.
 * @param [in]   in      Data to encrypt/decrypt.
 * @param [in]   inLen   Length of encrypted data.
 * @param [out]  out     Resulting data from encrypt/decrypt.
 * @param [out]  outLen  Size of resulting data.
 * @param [in]   tag     Authentication tag for encrypted data.
 * @param [in]   heap    Dynamic memory allocation data hint.
 * @param [in]   enc     1 when encrypting, 0 when decrypting.
 * @return  0 on success.
 * @return  MEMORY_E when dynamic memory allocation fails.
 * @return  Other value when encryption/decryption fails.
 */
static int TicketEncDec(byte* key, int keyLen, byte* iv, byte* aad, int aadSz,
                        byte* in, int inLen, byte* out, int* outLen, byte* tag,
                        void* heap, int enc)
{
    int ret;
    WC_DECLARE_VAR(sm4, wc_Sm4, 1, 0);

    (void)heap;

    WC_ALLOC_VAR_EX(sm4, wc_Sm4, 1, heap, DYNAMIC_TYPE_TMP_BUFFER,
        return MEMORY_E);

    if (enc) {
        ret = wc_Sm4Init(sm4, NULL, INVALID_DEVID);
        if (ret == 0) {
            ret = wc_Sm4GcmSetKey(sm4, key, keyLen);
        }
        if (ret == 0) {
            ret = wc_Sm4GcmEncrypt(sm4, in, out, inLen, iv, GCM_NONCE_MID_SZ,
                                   tag, SM4_BLOCK_SIZE, aad, aadSz);
        }
        wc_Sm4Free(sm4);
    }
    else {
        ret = wc_Sm4Init(sm4, NULL, INVALID_DEVID);
        if (ret == 0) {
            ret = wc_Sm4GcmSetKey(sm4, key, keyLen);
        }
        if (ret == 0) {
            ret = wc_Sm4GcmDecrypt(sm4, in, out, inLen, iv, GCM_NONCE_MID_SZ,
                                   tag, SM4_BLOCK_SIZE, aad, aadSz);
        }
        wc_Sm4Free(sm4);
    }

    WC_FREE_VAR_EX(sm4, heap, DYNAMIC_TYPE_TMP_BUFFER);

    *outLen = inLen;

    return ret;
}
#else
    #error "No encryption algorithm available for default ticket encryption."
#endif

/* Choose a key to use for encryption.
 *
 * Generate a new key if the current ones are expired.
 * If the secondary key has not been used and the primary key has expired then
 * generate a new primary key.
 *
 * @param [in]   Ticket encryption callback context.
 * @param [in]   Session ticket lifetime.
 * @param [out]  Index of key to use for encryption.
 * @return  0 on success.
 * @return  Other value when random number generation fails.
 */
static int TicketEncCbCtx_ChooseKey(TicketEncCbCtx* keyCtx, int ticketHint,
                                    int* keyIdx)
{
    int ret = 0;

    /* Get new current time as lock may have taken some time. */
    word32 now = LowResTimer();

    /* Check expirary of primary key for encrypt. */
    if (keyCtx->expirary[0] >= now + ticketHint) {
        *keyIdx = 0;
    }
    /* Check expirary of primary key for encrypt. */
    else if (keyCtx->expirary[1] >= now + ticketHint) {
        *keyIdx = 1;
    }
    /* No key available to use. */
    else {
        int genKey;

        /* Generate which ever key is expired for decrypt - primary first. */
        if (keyCtx->expirary[0] < now) {
            genKey = 0;
        }
        else if (keyCtx->expirary[1] < now) {
            genKey = 1;
        }
        /* Timeouts and expirary should not allow this to happen. */
        else {
            return BAD_STATE_E;
        }

        /* Generate the required key */
        ret = wc_RNG_GenerateBlock(&keyCtx->rng, keyCtx->key[genKey],
                                   WOLFSSL_TICKET_KEY_SZ);
        if (ret == 0) {
            keyCtx->expirary[genKey] = now + WOLFSSL_TICKET_KEY_LIFETIME;
            *keyIdx = genKey;
        }
    }

    return ret;
}

/* Default Session Ticket encryption/decryption callback.
 *
 * Use ChaCha20-Poly1305, AES-GCM or SM4-GCM to encrypt/decrypt the ticket.
 * Two keys are used:
 *  - When the first expires for encryption, then use the other.
 *  - Don't encrypt with key if the ticket lifetime will go beyond expirary.
 *  - Generate a new primary key when primary key expired for decrypt and
 *    no secondary key is activate for encryption.
 *  - Generate a new secondary key when expired and needed.
 *  - Calculate expirary starting from first encrypted ticket.
 *  - Key name has last bit set to indicate index of key.
 * Keys expire for decryption after ticket key lifetime from the first encrypted
 * ticket.
 * Keys can only be use for encryption while the ticket hint does not exceed
 * the key lifetime.
 * Lifetime of a key must be greater than the lifetime of a ticket. This means
 * that if one ticket is only valid for decryption, then the other will be
 * valid for encryption.
 * AAD = key_name | iv | ticket len (16-bits network order)
 *
 * @param [in]      ssl       SSL connection.
 * @param [in,out]  key_name  Name of key from client.
 *                            Encrypt: name of key returned.
 *                            Decrypt: name from ticket message to check.
 * @param [in]      iv        IV to use in encryption/decryption.
 * @param [in]      mac       MAC for authentication of encrypted data.
 * @param [in]      enc       1 when encrypting ticket, 0 when decrypting.
 * @param [in,out]  ticket    Encrypted/decrypted session ticket bytes.
 * @param [in]      inLen     Length of incoming ticket.
 * @param [out]     outLen    Length of outgoing ticket.
 * @param [in]      userCtx   Context for encryption/decryption of ticket.
 * @return  WOLFSSL_TICKET_RET_OK when successful.
 * @return  WOLFSSL_TICKET_RET_CREATE when successful and a new ticket is to
 *          be created for TLS 1.2 and below.
 * @return  WOLFSSL_TICKET_RET_REJECT when failed to produce valid encrypted or
 *          decrypted ticket.
 * @return  WOLFSSL_TICKET_RET_FATAL when key name does not match.
 */
static int DefTicketEncCb(WOLFSSL* ssl, byte key_name[WOLFSSL_TICKET_NAME_SZ],
                          byte iv[WOLFSSL_TICKET_IV_SZ],
                          byte mac[WOLFSSL_TICKET_MAC_SZ],
                          int enc, byte* ticket, int inLen, int* outLen,
                          void* userCtx)
{
    int ret;
    TicketEncCbCtx* keyCtx = (TicketEncCbCtx*)userCtx;
    WOLFSSL_CTX* ctx = keyCtx->ctx;
    word16 sLen = XHTONS((word16)inLen);
    byte aad[WOLFSSL_TICKET_NAME_SZ + WOLFSSL_TICKET_IV_SZ + sizeof(sLen)];
    int  aadSz = WOLFSSL_TICKET_NAME_SZ + WOLFSSL_TICKET_IV_SZ + sizeof(sLen);
    byte* p = aad;
    int keyIdx = 0;

    WOLFSSL_ENTER("DefTicketEncCb");

    /* For decryption, check minimum internal ticket size */
    if ((!enc) && (inLen < (int)WOLFSSL_INTERNAL_TICKET_LEN)) {
        return BUFFER_E;
    }

    /* Check we have setup the RNG, name and primary key. */
    if (keyCtx->expirary[0] == 0) {
#ifndef SINGLE_THREADED
        /* Lock around access to expirary and key - stop initial key being
         * generated twice at the same time. */
        if (wc_LockMutex(&keyCtx->mutex) != 0) {
            WOLFSSL_MSG("Couldn't lock key context mutex");
            return WOLFSSL_TICKET_RET_REJECT;
        }
#endif
        /* Sets expirary of primary key in setup. */
        ret = TicketEncCbCtx_Setup(keyCtx, ssl->ctx->heap, ssl->ctx->devId);
#ifndef SINGLE_THREADED
        wc_UnLockMutex(&keyCtx->mutex);
#endif
        if (ret != 0)
            return ret;
    }

    if (enc) {
        /* Return the name of the key - missing key index. */
        XMEMCPY(key_name, keyCtx->name, WOLFSSL_TICKET_NAME_SZ);

        /* Generate a new IV into buffer to be returned.
         * Don't use the RNG in keyCtx as it's for generating private data. */
        ret = wc_RNG_GenerateBlock(ssl->rng, iv, WOLFSSL_TICKET_IV_SZ);
        if (ret != 0) {
            return WOLFSSL_TICKET_RET_REJECT;
        }
    }
    else {
        /* Mask of last bit that is the key index. */
        byte lastByte = key_name[WOLFSSL_TICKET_NAME_SZ - 1] & 0xfe;

        /* For decryption, see if we know this key - check all but last byte. */
        if (XMEMCMP(key_name, keyCtx->name, WOLFSSL_TICKET_NAME_SZ - 1) != 0) {
            return WOLFSSL_TICKET_RET_FATAL;
        }
        /* Ensure last byte without index bit matches too. */
        if (lastByte != keyCtx->name[WOLFSSL_TICKET_NAME_SZ - 1]) {
            return WOLFSSL_TICKET_RET_FATAL;
        }
    }

    /* Build AAD from: key name, iv, and length of ticket. */
    XMEMCPY(p, keyCtx->name, WOLFSSL_TICKET_NAME_SZ);
    p += WOLFSSL_TICKET_NAME_SZ;
    XMEMCPY(p, iv, WOLFSSL_TICKET_IV_SZ);
    p += WOLFSSL_TICKET_IV_SZ;
    XMEMCPY(p, &sLen, sizeof(sLen));

    /* Encrypt ticket. */
    if (enc) {
        word32 now;

        now = LowResTimer();
        /* As long as encryption expirary isn't imminent - no lock. */
        if (keyCtx->expirary[0] > now + ctx->ticketHint) {
            keyIdx = 0;
        }
        else if (keyCtx->expirary[1] > now + ctx->ticketHint) {
            keyIdx = 1;
        }
        else {
#ifndef SINGLE_THREADED
            /* Lock around access to expirary and key - stop key being generated
             * twice at the same time. */
            if (wc_LockMutex(&keyCtx->mutex) != 0) {
                WOLFSSL_MSG("Couldn't lock key context mutex");
                return WOLFSSL_TICKET_RET_REJECT;
            }
#endif
            ret = TicketEncCbCtx_ChooseKey(keyCtx, ctx->ticketHint, &keyIdx);
#ifndef SINGLE_THREADED
            wc_UnLockMutex(&keyCtx->mutex);
#endif
            if (ret != 0) {
                return WOLFSSL_TICKET_RET_REJECT;
            }
        }
        /* Set the name of the key to the index chosen. */
        key_name[WOLFSSL_TICKET_NAME_SZ - 1] |= keyIdx;
        /* Update AAD too. */
        aad[WOLFSSL_TICKET_NAME_SZ - 1] |= keyIdx;

        /* Encrypt ticket data. */
        ret = TicketEncDec(keyCtx->key[keyIdx], WOLFSSL_TICKET_KEY_SZ, iv, aad,
                           aadSz, ticket, inLen, ticket, outLen, mac, ssl->heap,
                           1);
        if (ret != 0) return WOLFSSL_TICKET_RET_REJECT;
    }
    /* Decrypt ticket. */
    else {
        /* Get index of key from name. */
        keyIdx = key_name[WOLFSSL_TICKET_NAME_SZ - 1] & 0x1;
        /* Update AAD with index. */
        aad[WOLFSSL_TICKET_NAME_SZ - 1] |= keyIdx;

        /* Check expirary */
        if (keyCtx->expirary[keyIdx] <= LowResTimer()) {
            return WOLFSSL_TICKET_RET_REJECT;
        }

        /* Decrypt ticket data. */
        ret = TicketEncDec(keyCtx->key[keyIdx], WOLFSSL_TICKET_KEY_SZ, iv, aad,
                           aadSz, ticket, inLen, ticket, outLen, mac, ssl->heap,
                           0);
        if (ret != 0) {
            return WOLFSSL_TICKET_RET_REJECT;
        }
    }

#ifndef WOLFSSL_TICKET_DECRYPT_NO_CREATE
    if (!IsAtLeastTLSv1_3(ssl->version) && !enc)
        return WOLFSSL_TICKET_RET_CREATE;
#endif
    return WOLFSSL_TICKET_RET_OK;
}

#endif /* !WOLFSSL_NO_DEF_TICKET_ENC_CB */

#endif /* HAVE_SESSION_TICKET */

#ifndef WOLFSSL_NO_TLS12

#if defined(HAVE_SECURE_RENEGOTIATION) && \
    !defined(NO_WOLFSSL_SERVER)

    /* handle generation of server's hello_request (0) */
    int SendHelloRequest(WOLFSSL* ssl)
    {
        byte* output;
        int sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
        int ret;

        WOLFSSL_START(WC_FUNC_HELLO_REQUEST_SEND);
        WOLFSSL_ENTER("SendHelloRequest");

        if (IsEncryptionOn(ssl, 1))
            sendSz += MAX_MSG_EXTRA;

        if (ssl->options.dtls)
            sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;

        /* Set this in case CheckAvailableSize returns a WANT_WRITE so that state
         * is not advanced yet */
        ssl->options.buildingMsg = 1;

        /* check for available size */
        if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
            return ret;

        /* get output buffer */
        output = GetOutputBuffer(ssl);
        AddHeaders(output, 0, hello_request, ssl);

        if (IsEncryptionOn(ssl, 1)) {
            byte* input;
            int   inputSz = HANDSHAKE_HEADER_SZ; /* build msg adds rec hdr */
            int   recordHeaderSz = RECORD_HEADER_SZ;

            if (ssl->options.dtls) {
                recordHeaderSz += DTLS_RECORD_EXTRA;
                inputSz += DTLS_HANDSHAKE_EXTRA;
            }

            input = (byte*)XMALLOC(inputSz, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
            if (input == NULL)
                return MEMORY_E;

            XMEMCPY(input, output + recordHeaderSz, inputSz);
            #ifdef WOLFSSL_DTLS
            if (IsDtlsNotSctpMode(ssl) &&
                    (ret = DtlsMsgPoolSave(ssl, input, inputSz, hello_request)) != 0) {
                XFREE(input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
                return ret;
            }
            #endif
            sendSz = BuildMessage(ssl, output, sendSz, input, inputSz,
                                  handshake, 0, 0, 0, CUR_ORDER);
            XFREE(input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);

            if (sendSz < 0)
                return sendSz;
        }

        ssl->buffers.outputBuffer.length += sendSz;
        ssl->options.buildingMsg = 0;

        ret = SendBuffered(ssl);

        WOLFSSL_LEAVE("SendHelloRequest", ret);
        WOLFSSL_END(WC_FUNC_HELLO_REQUEST_SEND);

        return ret;
    }

#endif /* HAVE_SECURE_RENEGOTIATION && !NO_WOLFSSL_SERVER */

#ifdef WOLFSSL_DTLS
    /* handle generation of DTLS hello_verify_request (3) */
    int SendHelloVerifyRequest(WOLFSSL* ssl,
                               const byte* cookie, byte cookieSz)
    {
        byte* output;
        int   length = VERSION_SZ + ENUM_LEN + cookieSz;
        int   idx    = DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ;
        int   sendSz = length + idx;
        int   ret;

        /* are we in scr */
        if (IsEncryptionOn(ssl, 1)) {
            sendSz += MAX_MSG_EXTRA;
        }

        /* reset hashes  */
        ret = InitHandshakeHashes(ssl);
        if (ret != 0)
            return ret;

        /* check for available size */
        if ((ret = CheckAvailableSize(ssl, sendSz)) != 0)
            return ret;

        /* get output buffer */
        output = GetOutputBuffer(ssl);

        /* Hello Verify Request should use the same sequence number
         * as the Client Hello unless we are in renegotiation then
         * don't change numbers */
#ifdef HAVE_SECURE_RENEGOTIATION
        if (!IsSCR(ssl))
#endif
        {
            ssl->keys.dtls_sequence_number_hi = ssl->keys.curSeq_hi;
            ssl->keys.dtls_sequence_number_lo = ssl->keys.curSeq_lo;
        }
        AddHeaders(output, (word32)length, hello_verify_request, ssl);

        output[idx++] = DTLS_MAJOR;
        output[idx++] = DTLS_MINOR;

        output[idx++] = cookieSz;
        if (cookie == NULL || cookieSz == 0)
            return COOKIE_ERROR;

        XMEMCPY(output + idx, cookie, cookieSz);

#if defined(WOLFSSL_CALLBACKS) || defined(OPENSSL_EXTRA)
        if (ssl->hsInfoOn)
            AddPacketName(ssl, "HelloVerifyRequest");
        if (ssl->toInfoOn) {
            ret = AddPacketInfo(ssl, "HelloVerifyRequest", handshake, output,
                          sendSz, WRITE_PROTO, 0, ssl->heap);
            if (ret != 0)
                return ret;
        }
#endif

        /* are we in scr */
        if (IsEncryptionOn(ssl, 1)) {
            byte* input;
            int   inputSz = DTLS_HANDSHAKE_HEADER_SZ + length; /* build msg adds rec hdr */
            int   recordHeaderSz = DTLS_RECORD_HEADER_SZ;

            input = (byte*)XMALLOC(inputSz, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
            if (input == NULL)
                return MEMORY_E;

            XMEMCPY(input, output + recordHeaderSz, inputSz);
            sendSz = BuildMessage(ssl, output, sendSz, input, inputSz,
                                  handshake, 0, 0, 0, CUR_ORDER);
            XFREE(input, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);

            if (sendSz < 0)
                return sendSz;
        }

        ssl->buffers.outputBuffer.length += sendSz;

        return SendBuffered(ssl);
    }
#endif /* WOLFSSL_DTLS */

    typedef struct DckeArgs {
        byte*  output; /* not allocated */
        word32 length;
        word32 idx;
        word32 begin;
        word32 sigSz;
    #ifndef NO_RSA
        int    lastErr;
    #endif
    } DckeArgs;

    static void FreeDckeArgs(WOLFSSL* ssl, void* pArgs)
    {
        DckeArgs* args = (DckeArgs*)pArgs;

        (void)ssl;
        (void)args;
    }

    #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                     defined(HAVE_CURVE448)) && !defined(NO_PSK) \
        || !defined(NO_DH) && !defined(NO_PSK)
    static int ReadPSKHint(WOLFSSL* ssl, DckeArgs* args,
                           byte* input, word32 size)
    {
        word16 clientSz;
        /* Read in the PSK hint */
        if ((args->idx - args->begin) + OPAQUE16_LEN > size)
            return BUFFER_ERROR;
        ato16(input + args->idx, &clientSz);
        args->idx += OPAQUE16_LEN;
        if (clientSz > MAX_PSK_ID_LEN)
            return CLIENT_ID_ERROR;
        if ((args->idx - args->begin) + clientSz > size)
            return BUFFER_ERROR;
        XMEMCPY(ssl->arrays->client_identity, input + args->idx, clientSz);
        args->idx += clientSz;
        ssl->arrays->client_identity[clientSz] = '\0'; /* null term */
        return 0;
    }
    #endif
    #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                             defined(HAVE_CURVE448)
    static int ImportPeerECCKey(WOLFSSL* ssl, byte* input, DckeArgs* args,
                                word32 size, ecc_key* private_key, byte* haveCb)
    {
        int ret;
        int kea = ssl->specs.kea;

        *haveCb = 0;
        if ((args->idx - args->begin) + OPAQUE8_LEN > size)
            return BUFFER_ERROR;

        args->length = input[args->idx++];

        if ((args->idx - args->begin) + args->length > size)
            return BUFFER_ERROR;

        if (kea == ecdhe_psk_kea)
            args->sigSz = ENCRYPT_LEN - OPAQUE16_LEN;

    #ifdef HAVE_CURVE25519
        if (ssl->ecdhCurveOID == ECC_X25519_OID) {
        #ifdef HAVE_PK_CALLBACKS
            /* if callback then use it for shared secret */
            if (ssl->ctx->X25519SharedSecretCb != NULL) {
                *haveCb = 1;
                return 0;
            }
        #endif

            if (kea == ecdhe_psk_kea && ssl->eccTempKeyPresent == 0) {
                WOLFSSL_MSG("X25519 ephemeral key not made correctly");
                return ECC_MAKEKEY_ERROR;
            }

            if (ssl->peerX25519Key == NULL) {
                /* alloc/init on demand */
                ret = AllocKey(ssl, DYNAMIC_TYPE_CURVE25519,
                               (void**)&ssl->peerX25519Key);
                if (ret != 0) {
                    return ret;
                }
            } else if (ssl->peerX25519KeyPresent) {
                ret = ReuseKey(ssl, DYNAMIC_TYPE_CURVE25519,
                               ssl->peerX25519Key);
                ssl->peerX25519KeyPresent = 0;
                if (ret != 0) {
                    return ret;
                }
            }

            if ((ret = wc_curve25519_check_public(input + args->idx,
                                   args->length, EC25519_LITTLE_ENDIAN)) != 0) {
            #ifdef WOLFSSL_EXTRA_ALERTS
                if (ret == WC_NO_ERR_TRACE(BUFFER_E))
                    SendAlert(ssl, alert_fatal, decode_error);
                else if (ret == WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E))
                    SendAlert(ssl, alert_fatal, bad_record_mac);
                else {
                    SendAlert(ssl, alert_fatal, illegal_parameter);
                }
            #else
                (void)ret;
            #endif
                return ECC_PEERKEY_ERROR;
            }

            if (wc_curve25519_import_public_ex(input + args->idx, args->length,
                               ssl->peerX25519Key, EC25519_LITTLE_ENDIAN)) {
             #ifdef WOLFSSL_EXTRA_ALERTS
                SendAlert(ssl, alert_fatal, illegal_parameter);
             #endif
                return ECC_PEERKEY_ERROR;
            }
            if (kea == ecc_diffie_hellman_kea)
                ssl->arrays->preMasterSz = CURVE25519_KEYSIZE;

            ssl->peerX25519KeyPresent = 1;
            return 0;
        }
    #endif
    #ifdef HAVE_CURVE448
        if (ssl->ecdhCurveOID == ECC_X448_OID) {
        #ifdef HAVE_PK_CALLBACKS
            /* if callback then use it for shared secret */
            if (ssl->ctx->X448SharedSecretCb != NULL) {
                *haveCb = 1;
                return 0;
            }
        #endif

            if (kea == ecdhe_psk_kea && ssl->eccTempKeyPresent == 0) {
                WOLFSSL_MSG("X448 ephemeral key not made correctly");
                return ECC_MAKEKEY_ERROR;
            }

            if (ssl->peerX448Key == NULL) {
                /* alloc/init on demand */
                ret = AllocKey(ssl, DYNAMIC_TYPE_CURVE448,
                               (void**)&ssl->peerX448Key);
                if (ret != 0)
                    return ret;
            } else if (ssl->peerX448KeyPresent) {
                ret = ReuseKey(ssl, DYNAMIC_TYPE_CURVE448,
                               ssl->peerX448Key);
                ssl->peerX448KeyPresent = 0;
                if (ret != 0)
                    return ret;
            }

            if ((ret = wc_curve448_check_public(input + args->idx, args->length,
                                                EC448_LITTLE_ENDIAN)) != 0) {
            #ifdef WOLFSSL_EXTRA_ALERTS
                if (ret == WC_NO_ERR_TRACE(BUFFER_E))
                    SendAlert(ssl, alert_fatal, decode_error);
                else if (ret == WC_NO_ERR_TRACE(ECC_OUT_OF_RANGE_E))
                    SendAlert(ssl, alert_fatal, bad_record_mac);
                else {
                    SendAlert(ssl, alert_fatal, illegal_parameter);
                }
            #else
                (void)ret;
            #endif
                return ECC_PEERKEY_ERROR;
            }

            if (wc_curve448_import_public_ex(input + args->idx, args->length,
                                    ssl->peerX448Key, EC448_LITTLE_ENDIAN)) {
            #ifdef WOLFSSL_EXTRA_ALERTS
                SendAlert(ssl, alert_fatal, illegal_parameter);
            #endif

                return ECC_PEERKEY_ERROR;
            }

            if (kea == ecc_diffie_hellman_kea)
                ssl->arrays->preMasterSz = CURVE448_KEY_SIZE;

            ssl->peerX448KeyPresent = 1;

            return 0;
        }
    #endif
    #ifdef HAVE_ECC
    #ifdef HAVE_PK_CALLBACKS
        /* if callback then use it for shared secret */
        if (ssl->ctx->EccSharedSecretCb != NULL) {
            *haveCb = 1;
            return 0;
        }
    #endif

        if (ssl->eccTempKeyPresent == 0 &&
            (kea == ecdhe_psk_kea || !ssl->specs.static_ecdh)) {
            WOLFSSL_MSG("Ecc ephemeral key not made correctly");
            return ECC_MAKEKEY_ERROR;
        }

        if (ssl->peerEccKey == NULL) {
            /* alloc/init on demand */
            ret = AllocKey(ssl, DYNAMIC_TYPE_ECC, (void**)&ssl->peerEccKey);
            if (ret != 0)
                return ret;
        }
        else if (ssl->peerEccKeyPresent) {
            ret = ReuseKey(ssl, DYNAMIC_TYPE_ECC, ssl->peerEccKey);
            ssl->peerEccKeyPresent = 0;
            if (ret != 0)
                return ret;
        }
#if !defined(HAVE_SELFTEST) && !defined(HAVE_FIPS)
        if (wc_ecc_import_x963_ex2(input + args->idx, args->length,
                ssl->peerEccKey, kea == ecdhe_psk_kea ? ssl->eccTempKey->dp->id
                                                      : private_key->dp->id, 1))
#else
        /* FIPS has validation define on. */
        if (wc_ecc_import_x963_ex(input + args->idx, args->length,
                ssl->peerEccKey, kea == ecdhe_psk_kea ? ssl->eccTempKey->dp->id
                                                      : private_key->dp->id))
#endif
        {
        #ifdef WOLFSSL_EXTRA_ALERTS
            SendAlert(ssl, alert_fatal, illegal_parameter);
        #endif

            return ECC_PEERKEY_ERROR;
        }

        if (kea == ecc_diffie_hellman_kea)
            ssl->arrays->preMasterSz = (word32)private_key->dp->size;

        ssl->peerEccKeyPresent = 1;
    #else
        (void)private_key;
    #endif /* HAVE_ECC */
        return 0;
    }
    #endif /* defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                   defined(HAVE_CURVE448) */

    /* handle processing client_key_exchange (16) */
    static int DoClientKeyExchange(WOLFSSL* ssl, byte* input, word32* inOutIdx,
                                                                    word32 size)
    {
        int ret;
    #ifdef WOLFSSL_ASYNC_CRYPT
        DckeArgs* args = NULL;
        WOLFSSL_ASSERT_SIZEOF_GE(ssl->async->args, *args);
    #else
        DckeArgs  args[1];
    #endif

        (void)size;
        (void)input;

        WOLFSSL_START(WC_FUNC_CLIENT_KEY_EXCHANGE_DO);
        WOLFSSL_ENTER("DoClientKeyExchange");

    #ifdef WOLFSSL_BLIND_PRIVATE_KEY
        wolfssl_priv_der_blind_toggle(ssl->buffers.key, ssl->buffers.keyMask);
    #endif

    #ifdef WOLFSSL_ASYNC_CRYPT
        if (ssl->async == NULL) {
            ssl->async = (struct WOLFSSL_ASYNC*)
                    XMALLOC(sizeof(struct WOLFSSL_ASYNC), ssl->heap,
                            DYNAMIC_TYPE_ASYNC);
            if (ssl->async == NULL)
                ERROR_OUT(MEMORY_E, exit_dcke);
        }
        args = (DckeArgs*)ssl->async->args;

        ret = wolfSSL_AsyncPop(ssl, &ssl->options.asyncState);
        if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E)) {
            /* Check for error */
            if (ret < 0)
                goto exit_dcke;
        }
        else
    #endif /* WOLFSSL_ASYNC_CRYPT */
        {
            /* Reset state */
            ret = 0;
            ssl->options.asyncState = TLS_ASYNC_BEGIN;
            XMEMSET(args, 0, sizeof(DckeArgs));
            args->idx = *inOutIdx;
            args->begin = *inOutIdx;
        #ifdef WOLFSSL_ASYNC_CRYPT
            ssl->async->freeArgs = FreeDckeArgs;
        #endif
        }

        /* Do Client Key Exchange State Machine */
        switch(ssl->options.asyncState)
        {
            case TLS_ASYNC_BEGIN:
            {
                /* Sanity checks */
                /* server side checked in SanityCheckMsgReceived */
                if (ssl->options.clientState < CLIENT_HELLO_COMPLETE) {
                    WOLFSSL_MSG("Client sending keyexchange at wrong time");
                    SendAlert(ssl, alert_fatal, unexpected_message);
                    ERROR_OUT(OUT_OF_ORDER_E, exit_dcke);
                }

            #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH)
                if (ssl->options.verifyPeer &&
                         (ssl->options.mutualAuth || ssl->options.failNoCert)) {
                    if (!ssl->options.havePeerCert) {
                        WOLFSSL_MSG("client didn't present peer cert");
                        ERROR_OUT(NO_PEER_CERT, exit_dcke);
                    }
                }

                if (ssl->options.verifyPeer && ssl->options.failNoCertxPSK) {
                    if (!ssl->options.havePeerCert &&
                                             !ssl->options.usingPSK_cipher) {
                        WOLFSSL_MSG("client didn't present peer cert");
                        ERROR_OUT(NO_PEER_CERT, exit_dcke);
                    }
                }
            #endif /* !NO_CERTS && !WOLFSSL_NO_CLIENT_AUTH */

            #if defined(WOLFSSL_CALLBACKS)
                if (ssl->hsInfoOn) {
                    AddPacketName(ssl, "ClientKeyExchange");
                }
                if (ssl->toInfoOn) {
                    AddLateName("ClientKeyExchange", &ssl->timeoutInfo);
                }
            #endif

                if (ssl->arrays->preMasterSecret == NULL) {
                    ssl->arrays->preMasterSz = ENCRYPT_LEN;
                    ssl->arrays->preMasterSecret = (byte*)XMALLOC(ENCRYPT_LEN,
                                                ssl->heap, DYNAMIC_TYPE_SECRET);
                    if (ssl->arrays->preMasterSecret == NULL) {
                        ERROR_OUT(MEMORY_E, exit_dcke);
                    }
                    XMEMSET(ssl->arrays->preMasterSecret, 0, ENCRYPT_LEN);
                }

                switch (ssl->specs.kea) {
                #ifndef NO_RSA
                    case rsa_kea:
                    {
                        break;
                    } /* rsa_kea */
                #endif /* !NO_RSA */
                #ifndef NO_PSK
                    case psk_kea:
                    {
                        /* sanity check that PSK server callback has been set */
                        if (ssl->options.server_psk_cb == NULL) {
                           WOLFSSL_MSG("No server PSK callback set");
                           ERROR_OUT(PSK_KEY_ERROR, exit_dcke);
                        }
                        break;
                    }
                #endif /* !NO_PSK */
                #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                    case ecc_diffie_hellman_kea:
                    {
                        break;
                    }
                #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
                #ifndef NO_DH
                    case diffie_hellman_kea:
                    {
                        break;
                    }
                #endif /* !NO_DH */
                #if !defined(NO_DH) && !defined(NO_PSK)
                    case dhe_psk_kea:
                    {
                        /* sanity check that PSK server callback has been set */
                        if (ssl->options.server_psk_cb == NULL) {
                            WOLFSSL_MSG("No server PSK callback set");
                            ERROR_OUT(PSK_KEY_ERROR, exit_dcke);
                        }
                        break;
                    }
                #endif /* !NO_DH && !NO_PSK */
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                    {
                        /* sanity check that PSK server callback has been set */
                        if (ssl->options.server_psk_cb == NULL) {
                            WOLFSSL_MSG("No server PSK callback set");
                            ERROR_OUT(PSK_KEY_ERROR, exit_dcke);
                        }
                        break;
                    }
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                    default:
                        WOLFSSL_MSG("Bad kea type");
                        ret = BAD_KEA_TYPE_E;
                } /* switch (ssl->specs.kea) */

                /* Check for error */
                if (ret != 0) {
                    goto exit_dcke;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_BUILD;
            } /* TLS_ASYNC_BEGIN */
            FALL_THROUGH;

            case TLS_ASYNC_BUILD:
            {
                switch (ssl->specs.kea) {
                #ifndef NO_RSA
                    case rsa_kea:
                    {
                        word32 keySz;

                        ssl->buffers.keyType = rsa_sa_algo;
                        ret = DecodePrivateKey(ssl, &keySz);
                        if (ret != 0) {
                            goto exit_dcke;
                        }
                        args->length = (word32)keySz;
                        ssl->arrays->preMasterSz = SECRET_LEN;

                        if (ssl->options.tls) {
                            word16 check;

                            if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
                                ERROR_OUT(BUFFER_ERROR, exit_dcke);
                            }

                            ato16(input + args->idx, &check);
                            args->idx += OPAQUE16_LEN;

                            if ((word32)check != args->length) {
                                WOLFSSL_MSG("RSA explicit size doesn't match");
                        #ifdef WOLFSSL_EXTRA_ALERTS
                                SendAlert(ssl, alert_fatal, bad_record_mac);
                        #endif
                                ERROR_OUT(RSA_PRIVATE_ERROR, exit_dcke);
                            }
                        }

                        if ((args->idx - args->begin) + args->length > size) {
                            WOLFSSL_MSG("RSA message too big");
                            ERROR_OUT(BUFFER_ERROR, exit_dcke);
                        }

                        /* pre-load PreMasterSecret with RNG data */
                        ret = wc_RNG_GenerateBlock(ssl->rng,
                            &ssl->arrays->preMasterSecret[VERSION_SZ],
                            SECRET_LEN - VERSION_SZ);
                        if (ret != 0) {
                            goto exit_dcke;
                        }

                        args->output = NULL;
                        break;
                    } /* rsa_kea */
                #endif /* !NO_RSA */
                #ifndef NO_PSK
                    case psk_kea:
                    {
                        word16 ci_sz;

                        if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
                            ERROR_OUT(BUFFER_ERROR, exit_dcke);
                        }

                        ato16(input + args->idx, &ci_sz);
                        args->idx += OPAQUE16_LEN;

                        if (ci_sz > MAX_PSK_ID_LEN) {
                            ERROR_OUT(CLIENT_ID_ERROR, exit_dcke);
                        }

                        if ((args->idx - args->begin) + ci_sz > size) {
                            ERROR_OUT(BUFFER_ERROR, exit_dcke);
                        }

                        XMEMCPY(ssl->arrays->client_identity,
                                                    input + args->idx, ci_sz);
                        args->idx += ci_sz;
                        ssl->arrays->client_identity[ci_sz] = '\0'; /* null term */
                        if (AddPSKtoPreMasterSecret(ssl))
                            ERROR_OUT(PSK_KEY_ERROR, exit_dcke);
                        MakePSKPreMasterSecret(ssl->arrays, 1);
                        break;
                    }
                #endif /* !NO_PSK */
                #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                    case ecc_diffie_hellman_kea:
                    {
                        ecc_key* private_key = NULL;
                        byte haveCb;
                    #ifdef HAVE_ECC
                        private_key = ssl->eccTempKey;

                        /* handle static private key */
                        if (ssl->specs.static_ecdh &&
                                          ssl->ecdhCurveOID != ECC_X25519_OID &&
                                          ssl->ecdhCurveOID != ECC_X448_OID) {
                            word32 keySz;

                            ssl->buffers.keyType = ecc_dsa_sa_algo;
                            ret = DecodePrivateKey(ssl, &keySz);
                            if (ret != 0) {
                                goto exit_dcke;
                            }
                            private_key = (ecc_key*)ssl->hsKey;
                        }
                    #endif

                        if ((ret = ImportPeerECCKey(ssl, input, args, size,
                                                    private_key, &haveCb)))
                            ERROR_OUT(ret, exit_dcke);
                    #ifdef HAVE_PK_CALLBACKS
                        if (haveCb) {
                            WOLFSSL_MSG("Using PK callback for peer key");
                            break;
                        }
                    #else
                        (void)haveCb; /* not used */
                    #endif

                    #if defined(WOLFSSL_TLS13) || defined(HAVE_FFDHE)
                        /* client_hello may have sent FFEDH2048, which sets namedGroup,
                            but that is not being used, so clear it */
                        /* resolves issue with server side wolfSSL_get_curve_name */
                        ssl->namedGroup = 0;
                    #endif
                        break;
                    }
                #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
                #ifndef NO_DH
                    case diffie_hellman_kea:
                    {
                        word16 clientPubSz;

                        if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
                            ERROR_OUT(BUFFER_ERROR, exit_dcke);
                        }

                        ato16(input + args->idx, &clientPubSz);
                        args->idx += OPAQUE16_LEN;

                        if ((args->idx - args->begin) + clientPubSz > size) {
                            ERROR_OUT(BUFFER_ERROR, exit_dcke);
                        }

                        args->sigSz = clientPubSz;

                        ret = AllocKey(ssl, DYNAMIC_TYPE_DH,
                                            (void**)&ssl->buffers.serverDH_Key);
                        if (ret != 0) {
                            goto exit_dcke;
                        }

                        ret = wc_DhSetKey(ssl->buffers.serverDH_Key,
                            ssl->buffers.serverDH_P.buffer,
                            ssl->buffers.serverDH_P.length,
                            ssl->buffers.serverDH_G.buffer,
                            ssl->buffers.serverDH_G.length);

                        /* set the max agree result size */
                        ssl->arrays->preMasterSz = ENCRYPT_LEN;
                        break;
                    }
                #endif /* !NO_DH */
                #if !defined(NO_DH) && !defined(NO_PSK)
                    case dhe_psk_kea:
                    {
                        word16 clientSz;

                        CHECK_RET(ret, ReadPSKHint(ssl, args, input, size),
                                  exit_dcke);

                        /* Read in the DHE business */
                        if ((args->idx - args->begin) + OPAQUE16_LEN > size) {
                            ERROR_OUT(BUFFER_ERROR, exit_dcke);
                        }

                        ato16(input + args->idx, &clientSz);
                        args->idx += OPAQUE16_LEN;

                        if ((args->idx - args->begin) + clientSz > size) {
                            ERROR_OUT(BUFFER_ERROR, exit_dcke);
                        }

                        args->sigSz = clientSz;

                        ret = AllocKey(ssl, DYNAMIC_TYPE_DH,
                                            (void**)&ssl->buffers.serverDH_Key);
                        if (ret != 0) {
                            goto exit_dcke;
                        }

                        ret = wc_DhSetKey(ssl->buffers.serverDH_Key,
                            ssl->buffers.serverDH_P.buffer,
                            ssl->buffers.serverDH_P.length,
                            ssl->buffers.serverDH_G.buffer,
                            ssl->buffers.serverDH_G.length);

                        break;
                    }
                #endif /* !NO_DH && !NO_PSK */
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                    {
                        byte haveCb;

                        CHECK_RET(ret, ReadPSKHint(ssl, args, input, size),
                                  exit_dcke);
                        if ((ret = ImportPeerECCKey(ssl, input, args, size,
                                                    NULL, &haveCb)))
                            ERROR_OUT(ret, exit_dcke);
                    #ifdef HAVE_PK_CALLBACKS
                        if (haveCb) {
                            WOLFSSL_MSG("Using PK callback for peer key");
                            break;
                        }
                    #else
                        (void)haveCb; /* not used */
                    #endif
                        break;
                    }
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                    default:
                        ret = BAD_KEA_TYPE_E;
                } /* switch (ssl->specs.kea) */

                /* Check for error */
                if (ret != 0) {
                    goto exit_dcke;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_DO;
            } /* TLS_ASYNC_BUILD */
            FALL_THROUGH;

            case TLS_ASYNC_DO:
            {
                switch (ssl->specs.kea) {
                #if !defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY)
                    case rsa_kea:
                    {
                        RsaKey* key = (RsaKey*)ssl->hsKey;
                        volatile int lenErrMask;
                        int mask;

                        ret = RsaDec(ssl,
                            input + args->idx,
                            args->length,
                            &args->output,
                            &args->sigSz,
                            key,
                        #ifdef HAVE_PK_CALLBACKS
                            ssl->buffers.key
                        #else
                            NULL
                        #endif
                        );

                        /*  Errors that can occur here that should be
                         *  indistinguishable:
                         *       RSA_BUFFER_E, RSA_PAD_E and RSA_PRIVATE_ERROR
                         */
                    #ifdef WOLFSSL_ASYNC_CRYPT
                        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E))
                            goto exit_dcke;
                    #endif
                        if (ret == WC_NO_ERR_TRACE(BAD_FUNC_ARG))
                            goto exit_dcke;

                        lenErrMask = 0 - (SECRET_LEN != args->sigSz);
                        /* Snapshot volatile to avoid multiple volatile
                         * accesses per expression. */
                        mask = lenErrMask;
                        args->lastErr = (ret & (~mask)) |
                            (WC_NO_ERR_TRACE(RSA_PAD_E) & mask);
                        ret = 0;
                        break;
                    } /* rsa_kea */
                #endif /* !NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY */
                #ifndef NO_PSK
                    case psk_kea:
                    {
                        break;
                    }
                #endif /* !NO_PSK */
                #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                    case ecc_diffie_hellman_kea:
                    {
                        void* private_key = ssl->eccTempKey;
                        (void)private_key;

                    #ifdef HAVE_CURVE25519
                        if (ssl->ecdhCurveOID == ECC_X25519_OID) {
                            ret = X25519SharedSecret(ssl,
                                (curve25519_key*)private_key,
                                ssl->peerX25519Key,
                                input + args->idx, &args->length,
                                ssl->arrays->preMasterSecret,
                                &ssl->arrays->preMasterSz,
                                WOLFSSL_SERVER_END
                            );
                            break;
                        }
                    #endif
                    #ifdef HAVE_CURVE448
                        if (ssl->ecdhCurveOID == ECC_X448_OID) {
                            ret = X448SharedSecret(ssl,
                                (curve448_key*)private_key,
                                ssl->peerX448Key,
                                input + args->idx, &args->length,
                                ssl->arrays->preMasterSecret,
                                &ssl->arrays->preMasterSz,
                                WOLFSSL_SERVER_END
                            );
                            break;
                        }
                    #endif
                    #ifdef HAVE_ECC
                        if (ssl->specs.static_ecdh) {
                            private_key = ssl->hsKey;
                        }

                        /* Generate shared secret */
                        ret = EccSharedSecret(ssl,
                            (ecc_key*)private_key, ssl->peerEccKey,
                            input + args->idx, &args->length,
                            ssl->arrays->preMasterSecret,
                            &ssl->arrays->preMasterSz,
                            WOLFSSL_SERVER_END
                        );
                    #ifdef WOLFSSL_ASYNC_CRYPT
                        if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
                    #endif
                        {
                            FreeKey(ssl, DYNAMIC_TYPE_ECC,
                                                      (void**)&ssl->peerEccKey);
                            ssl->peerEccKeyPresent = 0;
                        }
                    #endif
                        break;
                    }
                #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
                #ifndef NO_DH
                    case diffie_hellman_kea:
                    {
                        ret = DhAgree(ssl, ssl->buffers.serverDH_Key,
                            ssl->buffers.serverDH_Priv.buffer,
                            ssl->buffers.serverDH_Priv.length,
                            input + args->idx,
                            (word16)args->sigSz,
                            ssl->arrays->preMasterSecret,
                            &ssl->arrays->preMasterSz,
                            ssl->buffers.serverDH_P.buffer,
                            ssl->buffers.serverDH_P.length);
                        break;
                    }
                #endif /* !NO_DH */
                #if !defined(NO_DH) && !defined(NO_PSK)
                    case dhe_psk_kea:
                    {
                        ret = DhAgree(ssl, ssl->buffers.serverDH_Key,
                            ssl->buffers.serverDH_Priv.buffer,
                            ssl->buffers.serverDH_Priv.length,
                            input + args->idx,
                            (word16)args->sigSz,
                            ssl->arrays->preMasterSecret + OPAQUE16_LEN,
                            &ssl->arrays->preMasterSz,
                            ssl->buffers.serverDH_P.buffer,
                            ssl->buffers.serverDH_P.length);
                        break;
                    }
                #endif /* !NO_DH && !NO_PSK */
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                    {
                    #ifdef HAVE_CURVE25519
                        if (ssl->ecdhCurveOID == ECC_X25519_OID) {
                            ret = X25519SharedSecret(ssl,
                                (curve25519_key*)ssl->eccTempKey,
                                ssl->peerX25519Key,
                                input + args->idx, &args->length,
                                ssl->arrays->preMasterSecret + OPAQUE16_LEN,
                                &args->sigSz,
                                WOLFSSL_SERVER_END
                            );
                        #ifdef WOLFSSL_ASYNC_CRYPT
                            if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
                        #endif
                            {
                                FreeKey(ssl, DYNAMIC_TYPE_CURVE25519,
                                                   (void**)&ssl->peerX25519Key);
                                ssl->peerX25519KeyPresent = 0;
                            }
                            break;
                        }
                    #endif
                    #ifdef HAVE_CURVE448
                        if (ssl->ecdhCurveOID == ECC_X448_OID) {
                            ret = X448SharedSecret(ssl,
                                (curve448_key*)ssl->eccTempKey,
                                ssl->peerX448Key,
                                input + args->idx, &args->length,
                                ssl->arrays->preMasterSecret + OPAQUE16_LEN,
                                &args->sigSz,
                                WOLFSSL_SERVER_END
                            );
                        #ifdef WOLFSSL_ASYNC_CRYPT
                            if (ret != WC_NO_ERR_TRACE(WC_PENDING_E))
                        #endif
                            {
                                FreeKey(ssl, DYNAMIC_TYPE_CURVE448,
                                                     (void**)&ssl->peerX448Key);
                                ssl->peerX448KeyPresent = 0;
                            }
                            break;
                        }
                    #endif
                        /* Generate shared secret */
                        ret = EccSharedSecret(ssl,
                            ssl->eccTempKey, ssl->peerEccKey,
                            input + args->idx, &args->length,
                            ssl->arrays->preMasterSecret + OPAQUE16_LEN,
                            &args->sigSz,
                            WOLFSSL_SERVER_END
                        );
                        if (!ssl->specs.static_ecdh
                    #ifdef WOLFSSL_ASYNC_CRYPT
                            && ret != WC_NO_ERR_TRACE(WC_PENDING_E)
                    #endif
                        ) {
                            FreeKey(ssl, DYNAMIC_TYPE_ECC,
                                                      (void**)&ssl->peerEccKey);
                            ssl->peerEccKeyPresent = 0;
                        }
                        break;
                    }
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                    default:
                        ret = BAD_KEA_TYPE_E;
                } /* switch (ssl->specs.kea) */

                /* Check for error */
                if (ret != 0) {
                    goto exit_dcke;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_VERIFY;
            } /* TLS_ASYNC_DO */
            FALL_THROUGH;

            case TLS_ASYNC_VERIFY:
            {
                switch (ssl->specs.kea) {
                #ifndef NO_RSA
                    case rsa_kea:
                    {
                        byte *tmpRsa;
                        volatile byte mask;

                        /* Add the signature length to idx */
                        args->idx += args->length;

                    #ifdef DEBUG_WOLFSSL
                        /* check version (debug warning message only) */
                        if (args->output != NULL) {
                            if (args->output[0] != ssl->chVersion.major ||
                                args->output[1] != ssl->chVersion.minor) {
                                WOLFSSL_MSG("preMasterSecret version mismatch");
                            }
                        }
                    #endif

                        /* RFC5246 7.4.7.1:
                         * Treat incorrectly formatted message blocks and/or
                         * mismatched version numbers in a manner
                         * indistinguishable from correctly formatted RSA blocks
                         */

                        ret = args->lastErr;
                        args->lastErr = 0; /* reset */
                        /* On error 'ret' will be negative */
                        mask = (byte)(((unsigned int)ret >> ((sizeof(ret) * 8) - 1)) - 1);

                        /* build PreMasterSecret */
                        ssl->arrays->preMasterSecret[0] = ssl->chVersion.major;
                        ssl->arrays->preMasterSecret[1] = ssl->chVersion.minor;

                        tmpRsa = input + args->idx - VERSION_SZ - SECRET_LEN;
                        ctMaskCopy(~mask, (byte*)&args->output, (byte*)&tmpRsa,
                            sizeof(args->output));
                        if (args->output != NULL) {
                            int i;
                            /* Use random secret on error */
                            for (i = VERSION_SZ; i < SECRET_LEN; i++) {
                                ssl->arrays->preMasterSecret[i] =
                                     ctMaskSel(mask, args->output[i],
                                               ssl->arrays->preMasterSecret[i]);
                            }
                        }
                        /* preMasterSecret has RNG and version set
                         * return proper length and ignore error
                         * error will be caught as decryption error
                         */
                        args->sigSz = SECRET_LEN;
                        ret = 0;
                        break;
                    } /* rsa_kea */
                #endif /* !NO_RSA */
                #ifndef NO_PSK
                    case psk_kea:
                    {
                        break;
                    }
                #endif /* !NO_PSK */
                #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                                          defined(HAVE_CURVE448)
                    case ecc_diffie_hellman_kea:
                    {
                        /* skip past the imported peer key */
                        args->idx += args->length;
                        break;
                    }
                #endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
                #ifndef NO_DH
                    case diffie_hellman_kea:
                    {
                        args->idx += (word16)args->sigSz;
                        break;
                    }
                #endif /* !NO_DH */
                #if !defined(NO_DH) && !defined(NO_PSK)
                    case dhe_psk_kea:
                    {
                        word16 clientSz = (word16)args->sigSz;

                        args->idx += clientSz;

                        /* Use the PSK hint to look up the PSK and add it to the
                         * preMasterSecret here. */
                        if (AddPSKtoPreMasterSecret(ssl))
                            ERROR_OUT(PSK_KEY_ERROR, exit_dcke);
                        MakePSKPreMasterSecret(ssl->arrays, 0);
                        break;
                    }
                #endif /* !NO_DH && !NO_PSK */
                #if (defined(HAVE_ECC) || defined(HAVE_CURVE25519) || \
                                     defined(HAVE_CURVE448)) && !defined(NO_PSK)
                    case ecdhe_psk_kea:
                    {
                        word16 clientSz = (word16)args->sigSz;

                        /* skip past the imported peer key */
                        args->idx += args->length;

                        /* Add preMasterSecret */
                        ssl->arrays->preMasterSz = clientSz;

                        /* Use the PSK hint to look up the PSK and add it to the
                         * preMasterSecret here. */
                        if (AddPSKtoPreMasterSecret(ssl))
                            ERROR_OUT(PSK_KEY_ERROR, exit_dcke);
                        MakePSKPreMasterSecret(ssl->arrays, 0);
                        break;
                    }
                #endif /* (HAVE_ECC || CURVE25519 || CURVE448) && !NO_PSK */
                    default:
                        ret = BAD_KEA_TYPE_E;
                } /* switch (ssl->specs.kea) */

                /* Check for error */
                if (ret != 0) {
                    goto exit_dcke;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_FINALIZE;
            } /* TLS_ASYNC_VERIFY */
            FALL_THROUGH;

            case TLS_ASYNC_FINALIZE:
            {
                if (IsEncryptionOn(ssl, 0))
                    args->idx += ssl->keys.padSz;

                ret = MakeMasterSecret(ssl);

                /* Check for error */
                if (ret != 0) {
                    goto exit_dcke;
                }

                /* Advance state and proceed */
                ssl->options.asyncState = TLS_ASYNC_END;
            } /* TLS_ASYNC_FINALIZE */
            FALL_THROUGH;

            case TLS_ASYNC_END:
            {
                /* Set final index */
                *inOutIdx = args->idx;

                ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
            #if !defined(NO_CERTS) && !defined(WOLFSSL_NO_CLIENT_AUTH)
                if (ssl->options.verifyPeer) {
                    ret = BuildCertHashes(ssl, &ssl->hsHashes->certHashes);
                }
            #endif
                break;
            } /* TLS_ASYNC_END */
            default:
                ret = INPUT_CASE_ERROR;
        } /* switch(ssl->options.asyncState) */

    exit_dcke:

    #ifdef WOLFSSL_BLIND_PRIVATE_KEY
        if (ret == 0) {
            ret = wolfssl_priv_der_blind(ssl->rng, ssl->buffers.key,
                &ssl->buffers.keyMask);
        }
        else {
            wolfssl_priv_der_blind_toggle(ssl->buffers.key,
                                          ssl->buffers.keyMask);
        }
    #endif

        WOLFSSL_LEAVE("DoClientKeyExchange", ret);
        WOLFSSL_END(WC_FUNC_CLIENT_KEY_EXCHANGE_DO);
    #ifdef WOLFSSL_ASYNC_CRYPT
        /* Handle async operation */
        if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            /* Mark message as not received so it can process again */
            ssl->msgsReceived.got_client_key_exchange = 0;

            return ret;
        }
        /* Cleanup async */
        FreeAsyncCtx(ssl, 0);
    #else
        FreeDckeArgs(ssl, args);
    #endif /* WOLFSSL_ASYNC_CRYPT */
    #ifdef OPENSSL_ALL
        /* add error ret value to error queue */
        if (ret != 0) {
            WOLFSSL_ERROR(ret);
        }
    #endif


        /* Cleanup PMS */
        if (ssl->arrays->preMasterSecret != NULL) {
            ForceZero(ssl->arrays->preMasterSecret, ssl->arrays->preMasterSz);
        }
        ssl->arrays->preMasterSz = 0;

        /* Final cleanup */
        FreeKeyExchange(ssl);

        return ret;
    }

#endif /* !WOLFSSL_NO_TLS12 */

#ifdef HAVE_SNI
    int SNI_Callback(WOLFSSL* ssl)
    {
        int ad = 0;
        int sniRet = 0;
        int ret = 0;

        /* OpenSSL defaults alert to SSL_AD_UNRECOGNIZED_NAME, use this if
           WOLFSSL_EXTRA_ALERTS is defined, indicating user is OK with
           potential information disclosure from alerts. */
#if defined(OPENSSL_EXTRA) && defined(WOLFSSL_EXTRA_ALERTS)
        ad = WOLFSSL_AD_UNRECOGNIZED_NAME;
#endif
        /* Stunnel supports a custom sni callback to switch an SSL's ctx
        * when SNI is received. Call it now if exists */
        if(ssl && ssl->ctx && ssl->ctx->sniRecvCb) {
            WOLFSSL_MSG("Calling custom sni callback");
            sniRet = ssl->ctx->sniRecvCb(ssl, &ad, ssl->ctx->sniRecvCbArg);
            switch (sniRet) {
                case warning_return:
                    WOLFSSL_MSG("Error in custom sni callback. Warning alert");
                    ret = SendAlert(ssl, alert_warning, ad);
                    break;
                case fatal_return:
                    WOLFSSL_MSG("Error in custom sni callback. Fatal alert");
                    SendAlert(ssl, alert_fatal, ad);
                    return FATAL_ERROR;
                case noack_return:
                    WOLFSSL_MSG("Server quietly not acking servername.");
                    break;
                default:
                    break;
            }
        }
        return ret;
    }
#endif /* HAVE_SNI */

#endif /* !NO_WOLFSSL_SERVER && !NO_TLS */


#ifdef WOLFSSL_ASYNC_CRYPT
int wolfSSL_AsyncPop(WOLFSSL* ssl, byte* state)
{
    int ret = 0;
    WC_ASYNC_DEV* asyncDev;
    WOLF_EVENT* event;

    if (ssl == NULL) {
        return BAD_FUNC_ARG;
    }

    /* check for pending async */
    asyncDev = ssl->asyncDev;
    if (asyncDev) {
        /* grab event pointer */
        event = &asyncDev->event;

        ret = wolfAsync_EventPop(event, WOLF_EVENT_TYPE_ASYNC_WOLFSSL);
        if (ret != WC_NO_ERR_TRACE(WC_NO_PENDING_E) &&
            ret != WC_NO_ERR_TRACE(WC_PENDING_E)) {
            /* advance key share state if doesn't need called again */
            if (state && (asyncDev->event.flags & WC_ASYNC_FLAG_CALL_AGAIN) == 0) {
                (*state)++;
            }
            /* clear event and async device */
            XMEMSET(&asyncDev->event, 0, sizeof(WOLF_EVENT));
            ssl->asyncDev = NULL;
        }
        /* for crypto or PK callback, if pending remove from queue */
    #if (defined(WOLF_CRYPTO_CB) || defined(HAVE_PK_CALLBACKS)) && \
        !defined(WOLFSSL_ASYNC_CRYPT_SW) && !defined(HAVE_INTEL_QA) && \
        !defined(HAVE_CAVIUM)
        else if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) {
            /* Allow the underlying crypto API to be called again to trigger the
             * crypto or PK callback. The actual callback must be called, since
             * the completion is not detected in the poll like Intel QAT or
             * Nitrox */
            ret = wolfEventQueue_Remove(&ssl->ctx->event_queue, event);
            /* Clear async device so stale pending state from
             * wolfSSL_AsyncInit does not confuse subsequent operations */
            XMEMSET(&asyncDev->event, 0, sizeof(WOLF_EVENT));
            ssl->asyncDev = NULL;
        }
    #endif
    }
    else {
        ret = WC_NO_PENDING_E;
    }

    WOLFSSL_LEAVE("wolfSSL_AsyncPop", ret);

    return ret;
}

int wolfSSL_AsyncInit(WOLFSSL* ssl, WC_ASYNC_DEV* asyncDev, word32 flags)
{
    int ret;
    WOLF_EVENT* event;

    if (ssl == NULL || asyncDev == NULL) {
        return BAD_FUNC_ARG;
    }

    /* grab event pointer */
    event = &asyncDev->event;

    /* init event */
    ret = wolfAsync_EventInit(event, WOLF_EVENT_TYPE_ASYNC_WOLFSSL, ssl, flags);

    WOLFSSL_LEAVE("wolfSSL_AsyncInit", ret);

    return ret;
}

int wolfSSL_AsyncPush(WOLFSSL* ssl, WC_ASYNC_DEV* asyncDev)
{
    int ret;
    WOLF_EVENT* event;

    if (ssl == NULL || asyncDev == NULL) {
        return BAD_FUNC_ARG;
    }

    /* grab event pointer */
    event = &asyncDev->event;

    /* store reference to active async operation */
    ssl->asyncDev = asyncDev;

    /* place event into queue */
    ret = wolfAsync_EventQueuePush(&ssl->ctx->event_queue, event);

    /* success means return WC_PENDING_E */
    if (ret == 0) {
        ret = WC_PENDING_E;
    }

    WOLFSSL_LEAVE("wolfSSL_AsyncPush", ret);

    return ret;
}

#endif /* WOLFSSL_ASYNC_CRYPT */

#if !defined(NO_TLS)
/** Return the record size for sending payloadSz of data
 * @param ssl         WOLFSSL object
 * @param payloadSz   Size of data to be sent in record
 * @param isEncrypted 1 if encryption is on, 0 if not
 * @return            Record size for sending payloadSz of data
 */
int wolfssl_local_GetRecordSize(WOLFSSL *ssl, int payloadSz, int isEncrypted)
{
    int recordSz;

    if (ssl == NULL)
        return BAD_FUNC_ARG;

    if (isEncrypted) {
        recordSz = BuildMessage(ssl, NULL, 0, NULL, payloadSz, application_data,
             0, 1, 0, CUR_ORDER);
        /* use a safe upper bound in case of error */
        if (recordSz < 0) {
            recordSz = payloadSz + RECORD_HEADER_SZ
                + cipherExtraData(ssl) + COMP_EXTRA;
            if (ssl->options.dtls) {
                recordSz += DTLS_RECORD_EXTRA;
            }
        }
    }
    else {
        recordSz = payloadSz + RECORD_HEADER_SZ;
        if (ssl->options.dtls) {
            recordSz += DTLS_RECORD_EXTRA;
        }
    }
    return recordSz;
}
#endif

/** Return the maximum plaintext size for the current Max Fragment and MTU.
 * @param ssl         WOLFSSL object containing ciphersuite information.
 * @return            Max plaintext size for current MTU
 */
int wolfssl_local_GetMaxPlaintextSize(WOLFSSL *ssl)
{
    int maxFrag;

    if (ssl == NULL)
        return BAD_FUNC_ARG;

    maxFrag = wolfSSL_GetMaxFragSize(ssl);

#if defined(WOLFSSL_DTLS)
    if (IsDtlsNotSctpMode(ssl)) {
        int recordSz;
        int mtu;

#if defined(WOLFSSL_DTLS_MTU)
        mtu = ssl->dtlsMtuSz;
#else
        mtu = MAX_MTU;
#endif

        recordSz = wolfssl_local_GetRecordSize(ssl, maxFrag,
                       IsEncryptionOn(ssl, 1));
        /* record size of maxFrag fits in MTU */
        if (recordSz <= mtu) {
            return maxFrag;
        }

        /* adjust plaintext size to fit in MTU */
        maxFrag -= (recordSz - mtu);
        if (maxFrag <= 0) {
            WOLFSSL_MSG("MTU too small for any plaintext");
            return DTLS_SIZE_ERROR;
        }

#ifndef WOLFSSL_AEAD_ONLY
        /* For block ciphers, reducing maxFrag may change padding alignment,
         * causing the record to still exceed MTU. Iterate to find exact fit.
         * Converges in at most 2 iterations due to bounded padding variance. */
        if (ssl->specs.cipher_type == block) {
            int iter;
            for (iter = 0; iter < 2; iter++) {
                recordSz = wolfssl_local_GetRecordSize(ssl, maxFrag,
                    IsEncryptionOn(ssl, 1));
                if (recordSz <= mtu)
                    break;
                maxFrag -= (recordSz - mtu);
            }
            if (recordSz > mtu) {
                /* this should never happen */
                WOLFSSL_MSG("Failed to fit record in MTU after padding adjust");
                return DTLS_SIZE_ERROR;
            }
        }
#endif
    }
#endif /* WOLFSSL_DTLS */

    return maxFrag;
}
/**
 * Return the max fragment size. This is essentially the maximum
 * fragment_length available.
 * @param ssl         WOLFSSL object containing ciphersuite information.
 * @return            Max fragment size
 */
int wolfSSL_GetMaxFragSize(WOLFSSL* ssl)
{
    int maxFragment;

    if (ssl == NULL)
        return BAD_FUNC_ARG;

    maxFragment = MAX_RECORD_SIZE;

#ifdef HAVE_MAX_FRAGMENT
    if ((ssl->max_fragment != 0) && ((word16)maxFragment > ssl->max_fragment)) {
        maxFragment = ssl->max_fragment;
    }
#endif /* HAVE_MAX_FRAGMENT */

    return maxFragment;
}

#if defined(WOLFSSL_IOTSAFE) && defined(HAVE_PK_CALLBACKS)

IOTSAFE *wolfSSL_get_iotsafe_ctx(WOLFSSL *ssl)
{
    if (ssl == NULL)
        return NULL;
    return &ssl->iotsafe;

}

int wolfSSL_set_iotsafe_ctx(WOLFSSL *ssl, IOTSAFE *iotsafe)
{
    if ((ssl == NULL) || (iotsafe == NULL))
        return BAD_FUNC_ARG;
    XMEMCPY(&ssl->iotsafe, iotsafe, sizeof(IOTSAFE));
    return 0;
}

#endif /* WOLFSSL_IOTSAFE && HAVE_PK_CALLBACKS */

#if defined(OPENSSL_ALL) && !defined(NO_FILESYSTEM) && !defined(NO_WOLFSSL_DIR)
/* create an instance of WOLFSSL_BY_DIR_HASH structure */
WOLFSSL_BY_DIR_HASH* wolfSSL_BY_DIR_HASH_new(void)
{
    WOLFSSL_BY_DIR_HASH* dir_hash;

    WOLFSSL_ENTER("wolfSSL_BY_DIR_HASH_new");

    dir_hash = (WOLFSSL_BY_DIR_HASH*)XMALLOC(sizeof(WOLFSSL_BY_DIR_HASH), NULL,
        DYNAMIC_TYPE_OPENSSL);
    if (dir_hash) {
        XMEMSET(dir_hash, 0, sizeof(WOLFSSL_BY_DIR_HASH));
    }
    return dir_hash;
}
/* release a WOLFSSL_BY_DIR_HASH resource */
void wolfSSL_BY_DIR_HASH_free(WOLFSSL_BY_DIR_HASH* dir_hash)
{
    if (dir_hash == NULL)
        return;

    XFREE(dir_hash, NULL, DYNAMIC_TYPE_OPENSSL);
}
/* create an instance of WOLFSSL_STACK for STACK_TYPE_BY_DIR_hash */
WOLFSSL_STACK* wolfSSL_sk_BY_DIR_HASH_new_null(void)
{
    WOLFSSL_STACK* sk = wolfSSL_sk_new_node(NULL);

    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_HASH_new_null");

    if (sk) {
        sk->type = STACK_TYPE_BY_DIR_hash;
    }
    return sk;
}

/* returns value less than 0 on fail to match
 * On a successful match the priority level found is returned
 */
int wolfSSL_sk_BY_DIR_HASH_find(
   WOLF_STACK_OF(WOLFSSL_BY_DIR_HASH)* sk, const WOLFSSL_BY_DIR_HASH* toFind)
{
    WOLFSSL_STACK* next;
    int i, sz;

    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_HASH_find");

    if (sk == NULL || toFind == NULL) {
        return WOLFSSL_FAILURE;
    }

    sz   = wolfSSL_sk_BY_DIR_HASH_num(sk);
    next = sk;
    for (i = 0; i < sz && next != NULL; i++) {
        if (next->data.dir_hash->hash_value == toFind->hash_value) {
            return sz - i; /* reverse because stack pushed highest on first */
        }
        next = next->next;
    }
    return WOLFSSL_FATAL_ERROR;
}
/* return a number of WOLFSSL_BY_DIR_HASH in stack */
int wolfSSL_sk_BY_DIR_HASH_num(const WOLF_STACK_OF(WOLFSSL_BY_DIR_HASH) *sk)
{
    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_HASH_num");

    if (sk == NULL)
        return WOLFSSL_FATAL_ERROR;
    return (int)sk->num;
}
/* return WOLFSSL_BY_DIR_HASH instance at i */
WOLFSSL_BY_DIR_HASH* wolfSSL_sk_BY_DIR_HASH_value(
                        const WOLF_STACK_OF(WOLFSSL_BY_DIR_HASH) *sk, int i)
{
    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_HASH_value");

    for (; sk != NULL && i > 0; i--)
        sk = sk->next;

    if (i != 0 || sk == NULL)
        return NULL;
    return sk->data.dir_hash;
}
/* pop WOLFSSL_BY_DIR_HASH instance, and remove its node from stack */
WOLFSSL_BY_DIR_HASH* wolfSSL_sk_BY_DIR_HASH_pop(
                                WOLF_STACK_OF(WOLFSSL_BY_DIR_HASH)* sk)
{
    return (WOLFSSL_BY_DIR_HASH *)wolfSSL_sk_pop(sk);
}
/* release all contents in stack, and then release stack itself. */
/* Second argument is a function pointer to release resources.   */
/* It calls the function to release resources when it is passed  */
/* instead of wolfSSL_BY_DIR_HASH_free().                        */
void wolfSSL_sk_BY_DIR_HASH_pop_free(WOLF_STACK_OF(BY_DIR_HASH)* sk,
    void (*f) (WOLFSSL_BY_DIR_HASH*))
{
    WOLFSSL_STACK* node;

    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_HASH_pop_free");

    if (sk == NULL) {
        return;
    }

    /* parse through stack freeing each node */
    node = sk->next;
    while (node && sk->num > 1) {
        WOLFSSL_STACK* tmp = node;
        node = node->next;

        if (f)
            f(tmp->data.dir_hash);
        else
            wolfSSL_BY_DIR_HASH_free(tmp->data.dir_hash);
        tmp->data.dir_hash = NULL;
        XFREE(tmp, NULL, DYNAMIC_TYPE_OPENSSL);
        sk->num -= 1;
    }

    /* free head of stack */
    if (sk->num == 1) {
        if (f)
            f(sk->data.dir_hash);
        else
            wolfSSL_BY_DIR_HASH_free(sk->data.dir_hash);
        sk->data.dir_hash = NULL;
    }
    XFREE(sk, NULL, DYNAMIC_TYPE_OPENSSL);
}
/* release all contents in stack, and then release stack itself */
void wolfSSL_sk_BY_DIR_HASH_free(WOLF_STACK_OF(WOLFSSL_BY_DIR_HASH) *sk)
{
    wolfSSL_sk_BY_DIR_HASH_pop_free(sk, NULL);
}
/* Adds the WOLFSSL_BY_DIR_HASH to the stack "sk". "sk" takes control of "in" and
 * tries to free it when the stack is free'd.
 *
 * return 1 on success 0 on fail
 */
int wolfSSL_sk_BY_DIR_HASH_push(WOLF_STACK_OF(WOLFSSL_BY_DIR_HASH)* sk,
                                               WOLFSSL_BY_DIR_HASH* in)
{
    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_HASH_push");

    if (sk == NULL || in == NULL) {
        return WOLFSSL_FAILURE;
    }

    return wolfSSL_sk_push(sk, in);
}
/* create an instance of WOLFSSL_BY_DIR_entry structure */
WOLFSSL_BY_DIR_entry* wolfSSL_BY_DIR_entry_new(void)
{
    WOLFSSL_BY_DIR_entry* entry;

    WOLFSSL_ENTER("wolfSSL_BY_DIR_entry_new");

    entry = (WOLFSSL_BY_DIR_entry*)XMALLOC(sizeof(WOLFSSL_BY_DIR_entry), NULL,
        DYNAMIC_TYPE_OPENSSL);

    if (entry) {
        XMEMSET(entry, 0, sizeof(WOLFSSL_BY_DIR_entry));
    }
    return entry;
}
/* release a WOLFSSL_BY_DIR_entry resource */
void wolfSSL_BY_DIR_entry_free(WOLFSSL_BY_DIR_entry* entry)
{
    WOLFSSL_ENTER("wolfSSL_BY_DIR_entry_free");

    if (entry == NULL)
        return;

    if (entry->hashes) {
        wolfSSL_sk_BY_DIR_HASH_free(entry->hashes);
    }

    XFREE(entry->dir_name, NULL, DYNAMIC_TYPE_OPENSSL);

    XFREE(entry, NULL, DYNAMIC_TYPE_OPENSSL);
}

WOLFSSL_STACK* wolfSSL_sk_BY_DIR_entry_new_null(void)
{
    WOLFSSL_STACK* sk = wolfSSL_sk_new_node(NULL);

    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_entry_new_null");

    if (sk) {
        sk->type = STACK_TYPE_BY_DIR_entry;
    }
    return sk;
}
/* return a number of WOLFSSL_BY_DIR_entry in stack */
int wolfSSL_sk_BY_DIR_entry_num(const WOLF_STACK_OF(WOLFSSL_BY_DIR_entry) *sk)
{
    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_entry_num");

    if (sk == NULL)
        return WOLFSSL_FATAL_ERROR;
    return (int)sk->num;
}
/* return WOLFSSL_BY_DIR_entry instance at i */
WOLFSSL_BY_DIR_entry* wolfSSL_sk_BY_DIR_entry_value(
                        const WOLF_STACK_OF(WOLFSSL_BY_DIR_entry) *sk, int i)
{
    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_entry_value");

    for (; sk != NULL && i > 0; i--)
        sk = sk->next;

    if (i != 0 || sk == NULL)
        return NULL;
    return sk->data.dir_entry;
}
/* pop WOLFSSL_BY_DIR_entry instance first, and remove its node from stack */
WOLFSSL_BY_DIR_entry* wolfSSL_sk_BY_DIR_entry_pop(
                                WOLF_STACK_OF(WOLFSSL_BY_DIR_entry)* sk)
{
    return (WOLFSSL_BY_DIR_entry *)wolfSSL_sk_pop(sk);
}
/* release all contents in stack, and then release stack itself. */
/* Second argument is a function pointer to release resources.   */
/* It calls the function to release resources when it is passed  */
/* instead of wolfSSL_BY_DIR_entry_free().                       */
void wolfSSL_sk_BY_DIR_entry_pop_free(WOLF_STACK_OF(WOLFSSL_BY_DIR_entry)* sk,
    void (*f) (WOLFSSL_BY_DIR_entry*))
{
    WOLFSSL_STACK* node;

    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_entry_pop_free");

    if (sk == NULL) {
        return;
    }

    /* parse through stack freeing each node */
    node = sk->next;
    while (node && sk->num > 1) {
        WOLFSSL_STACK* tmp = node;
        node = node->next;

        if (f)
            f(tmp->data.dir_entry);
        else
            wolfSSL_BY_DIR_entry_free(tmp->data.dir_entry);
        tmp->data.dir_entry = NULL;
        XFREE(tmp, NULL, DYNAMIC_TYPE_OPENSSL);
        sk->num -= 1;
    }

    /* free head of stack */
    if (sk->num == 1) {
        if (f)
            f(sk->data.dir_entry);
        else
            wolfSSL_BY_DIR_entry_free(sk->data.dir_entry);
        sk->data.dir_entry = NULL;
    }
    XFREE(sk, NULL, DYNAMIC_TYPE_OPENSSL);
}
/* release all contents in stack, and then release stack itself */
void wolfSSL_sk_BY_DIR_entry_free(WOLF_STACK_OF(wolfSSL_BY_DIR_entry) *sk)
{
    wolfSSL_sk_BY_DIR_entry_pop_free(sk, NULL);
}

/* Adds the wolfSSL_BY_DIR_entry to the stack "sk". "sk" takes control of "in" and
 * tries to free it when the stack is free'd.
 *
 * return 1 on success 0 on fail
 */
int wolfSSL_sk_BY_DIR_entry_push(WOLF_STACK_OF(WOLFSSL_BY_DIR_entry)* sk,
                                               WOLFSSL_BY_DIR_entry* in)
{
    WOLFSSL_ENTER("wolfSSL_sk_BY_DIR_entry_push");

    if (sk == NULL || in == NULL) {
        return WOLFSSL_FAILURE;
    }

    return wolfSSL_sk_push(sk, in);
}

#endif /* OPENSSL_ALL && !NO_FILESYSTEM && !NO_FILESYSTEM */

#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) && \
    (!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))

/*
 * Converts a DER formatted certificate to a SecCertificateRef
 *
 * @param derCert pointer to the DER formatted certificate
 * @param derLen length of the DER formatted cert, in bytes
 *
 * @return The newly created SecCertificateRef. Must be freed by caller when
 * no longer in use
 */
static SecCertificateRef ConvertToSecCertificateRef(const byte* derCert,
                                                    int derLen)
{
    CFDataRef         derData = NULL;
    SecCertificateRef secCert = NULL;

    WOLFSSL_ENTER("ConvertToSecCertificateRef");

    /* Create a CFDataRef from the DER encoded certificate */
    derData = CFDataCreate(kCFAllocatorDefault, derCert, derLen);
    if (!derData) {
        WOLFSSL_MSG("Error: can't create CFDataRef object for DER cert");
        goto cleanup;
    }

    /* Create a SecCertificateRef from the CFDataRef */
    secCert = SecCertificateCreateWithData(kCFAllocatorDefault, derData);
    if (!secCert) {
        WOLFSSL_MSG("Error: can't create SecCertificateRef from CFDataRef");
        goto cleanup;
    }

cleanup:
    if (derData) {
        CFRelease(derData);
    }

    WOLFSSL_LEAVE("ConvertToSecCertificateRef", !!secCert);

    return secCert;
}

static int DisplaySecTrustError(CFErrorRef error, SecTrustRef trust)
{
    CFStringRef        desc;
    CFStringRef        domain;
    SecTrustResultType trustResult;
    CFDictionaryRef    info;

    /* Description */
    desc = CFErrorCopyDescription(error);
    if (desc) {
        char buf[256];
        if (CFStringGetCString(desc, buf, sizeof(buf),
                               kCFStringEncodingUTF8)) {
            WOLFSSL_MSG_EX("SecTrustEvaluateWithError Error description: %s\n",
                           buf);
        }
        CFRelease(desc);
    }

    /* Domain */
    domain = CFErrorGetDomain(error);
    if (domain) {
        char domainStr[128];
        if (CFStringGetCString(domain, domainStr, sizeof(domainStr),
                               kCFStringEncodingUTF8)) {
            WOLFSSL_MSG_EX("SecTrustEvaluateWithError Domain: %s\n", domainStr);
        }
    }

    /* Get additional trust result info */
    if (SecTrustGetTrustResult(trust, &trustResult) == errSecSuccess) {
        WOLFSSL_MSG_EX("SecTrustResultType: %d\n", trustResult);
        /* Optional: decode the enum */
        switch (trustResult) {
            case kSecTrustResultInvalid:
                WOLFSSL_MSG("TrustResult: Invalid\n");
                break;
            case kSecTrustResultProceed:
                WOLFSSL_MSG("TrustResult: Proceed\n");
                break;
            case kSecTrustResultDeny:
                WOLFSSL_MSG("TrustResult: Deny\n");
                break;
            case kSecTrustResultUnspecified:
                WOLFSSL_MSG("TrustResult: Unspecified (implicitly trusted)\n");
                break;
            case kSecTrustResultRecoverableTrustFailure:
                WOLFSSL_MSG("TrustResult: Recoverable trust failure\n");
                break;
            case kSecTrustResultFatalTrustFailure:
                WOLFSSL_MSG("TrustResult: Fatal trust failure\n");
                break;
            case kSecTrustResultOtherError:
                WOLFSSL_MSG("TrustResult: Other error\n");
                break;
            default:
                WOLFSSL_MSG("TrustResult: Unknown\n");
                break;
        }
    }
    else {
        WOLFSSL_MSG("SecTrustGetTrustResult failed\n");
    }

    info = CFErrorCopyUserInfo(error);
    if (info) {
        WOLFSSL_MSG("Trust error info dump:\n");
        CFShow(info);
        CFRelease(info);
    }

    return 0;
}

#if defined(WOLFSSL_APPLE_NATIVE_CERT_VALIDATION) && \
    defined (WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION)
static int MaxValidityPeriodErrorOnly(CFErrorRef error)
{
    int multiple = 0;

    CFDictionaryRef userInfo = CFErrorCopyUserInfo(error);
    if (userInfo) {
        /* Get underlying error */
        CFTypeRef underlying  =
            CFDictionaryGetValue(userInfo, kCFErrorUnderlyingErrorKey);
        if (underlying) {
            /* Get underlying error value*/
            CFDictionaryRef underlyingDict =
                CFErrorCopyUserInfo((CFErrorRef)underlying);
            if (underlyingDict) {
                char buffer[512];
                CFStringRef values =
                    CFDictionaryGetValue(underlyingDict,
                        kCFErrorLocalizedDescriptionKey);
                if(CFStringGetCString(values, buffer, sizeof(buffer),
                    kCFStringEncodingUTF8)) {
                    if (XSTRSTR(buffer, "Certificate exceeds maximum "
                            "temporal validity period") &&
                        (!XSTRSTR(buffer, "Certificate exceeds maximum "
                            "temporal validity period,") ||
                        !XSTRSTR(buffer, ", Certificate exceeds maximum "
                            "temporal validity period"))) {
                        WOLFSSL_MSG("Maximum validity period error only");
                    } else {
                        WOLFSSL_MSG("Found other errors");
                        multiple = 1;
                    }
                }
                CFRelease(underlyingDict);
            }
        }
        CFRelease(userInfo);
    }
    return multiple;
}
#endif
/*
 * Validates a chain of certificates using the Apple system trust APIs
 *
 * @param certs pointer to the certificate chain to validate
 * @param totalCerts the number of certificates in certs
 *
 * @return 1 if chain is valid and trusted
 * @return 0 if chain is invalid or untrusted
 *
 * As of MacOS 14.0 we are still able to access system certificates and load
 * them manually into wolfSSL. For other apple devices, apple has removed the
 * ability to obtain certificates from the trust store, so we can't use
 * wolfSSL's built-in certificate validation mechanisms anymore. We instead
 * must call into the Security Framework APIs to authenticate peer certificates
 */
static int DoAppleNativeCertValidation(WOLFSSL*                   ssl,
                                       const WOLFSSL_BUFFER_INFO* certs,
                                       int                        totalCerts)
{
    int               i;
    int               ret;
    OSStatus          status;
    CFMutableArrayRef certArray = NULL;
    SecCertificateRef secCert   = NULL;
    SecTrustRef       trust     = NULL;
    SecPolicyRef      policy    = NULL;
    CFStringRef       hostname  = NULL;
    CFErrorRef        error     = NULL;

    WOLFSSL_ENTER("DoAppleNativeCertValidation");

    certArray = CFArrayCreateMutable(kCFAllocatorDefault, totalCerts,
                                     &kCFTypeArrayCallBacks);
    if (!certArray) {
        WOLFSSL_MSG("Error: can't allocate CFArray for certificates");
        ret = 0;
        goto cleanup;
    }

    for (i = 0; i < totalCerts; i++) {
        secCert =
            ConvertToSecCertificateRef(certs[i].buffer, (int)certs[i].length);
        if (!secCert) {
            WOLFSSL_MSG("Error: can't convert DER cert to SecCertificateRef");
            ret = 0;
            goto cleanup;
        }
        else {
            CFArrayAppendValue(certArray, secCert);
            /* Release, since the array now holds the reference */
            CFRelease(secCert);
        }
    }

    /* Create trust object for SecCertifiate Ref */
    if (ssl->buffers.domainName.buffer && ssl->buffers.domainName.length > 0) {
        /* Create policy with specified value to require host name match */
        hostname = CFStringCreateWithCString(
            kCFAllocatorDefault, (const char*)ssl->buffers.domainName.buffer,
            kCFStringEncodingUTF8);
    }

    /* If we're the client, we're validating the server's cert - use server
     * policy (true). If we're the server, we're validating the client's cert -
     * use client policy (false). Hostname validation only applies to server
     * certs. */
    {
        int isServerCert = (ssl->options.side == WOLFSSL_CLIENT_END);
        policy = SecPolicyCreateSSL(isServerCert,
                                    isServerCert ? hostname : NULL);
    }

    status = SecTrustCreateWithCertificates(certArray, policy, &trust);
    if (status != errSecSuccess) {
        WOLFSSL_MSG_EX("Error creating trust object, "
                       "SecTrustCreateWithCertificates returned %d",
                       status);
        ret = 0;
        goto cleanup;
    }

#if defined(WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION)
    /* TEST ONLY CODE:
     * Set accumulated list of trusted CA certificates as trust anchors */
    WOLFSSL_MSG("Setting anchor certificates");
    if (ssl->ctx->testTrustedCAs != NULL) {
        status = SecTrustSetAnchorCertificates(trust, ssl->ctx->testTrustedCAs);
        if (status != errSecSuccess) {
            WOLFSSL_MSG_EX("Error setting anchor certificates: %d", status);
            ret = 0;
            goto cleanup;
        }
    }
#endif

    /* Evaluate the certificate's authenticity */
    WOLFSSL_MSG("Performing Apple native cert validation via "
                "SecTrustEvaluateWithError");
    ret              = SecTrustEvaluateWithError(trust, &error);
    if (ret != 1) {
        if (error) {
            CFIndex code;
            code = CFErrorGetCode(error);
            WOLFSSL_MSG_EX("SecTrustEvaluateWithError failed with code: %ld\n",
                           code);
            DisplaySecTrustError(error, trust);

#ifdef WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION
            /* TEST ONLY CODE:
             * wolfSSL API tests use a cert with a validity period that is too
             * long for the Apple system trust APIs
             * (See: https://support.apple.com/en-us/103769)
             * therefore we should skip over this particular error */
            if (code == errSecCertificateValidityPeriodTooLong) {
                if (MaxValidityPeriodErrorOnly(error)) {
                    WOLFSSL_MSG("Multiple reasons for validity period error, "
                                "not skipping");
                    ret = 0;
                } else {
                    WOLFSSL_MSG("Skipping certificate validity period error");
                    ret = 1;
                }
            }
#endif
            (void)code;
            CFRelease(error);
        }
        else {
            WOLFSSL_MSG(
                "SecTrustEvaluateWithError failed with unknown error.\n");
        }
    }
    else {
        WOLFSSL_MSG("SecTrustEvaluateWithError succeeded");
    }

    /* Cleanup */
cleanup:
    if (certArray) {
        CFRelease(certArray);
    }
    if (trust) {
        CFRelease(trust);
    }
    if (policy) {
        CFRelease(policy);
    }
    if (hostname) {
        CFRelease(hostname);
    }

    WOLFSSL_LEAVE("DoAppleNativeCertValidation", ret);

    return ret;
}

#if defined(WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION)
int wolfSSL_TestAppleNativeCertValidation_AppendCA(WOLFSSL_CTX* ctx,
                                                   const byte*  derCert,
                                                   int          derLen)
{
    SecCertificateRef certRef;

    if (derCert == NULL || derLen == 0) {
        return WOLFSSL_FAILURE;
    }

    /* Create the base array for trust anchors if it doesn't exist */
    if (ctx->testTrustedCAs == NULL) {
        ctx->testTrustedCAs =
            CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
        if (!ctx->testTrustedCAs) {
            return WOLFSSL_FAILURE;
        }
    }

    certRef = ConvertToSecCertificateRef(derCert, derLen);
    if (!certRef) {
        return false;
    }

    CFArrayAppendValue(ctx->testTrustedCAs, certRef);
    CFRelease(certRef);
    return WOLFSSL_SUCCESS;
}
#endif /* WOLFSSL_TEST_APPLE_NATIVE_CERT_VALIDATION */

#endif /* __APPLE__ && WOLFSSL_SYS_CA_CERTS && (!NO_WOLFSSL_CLIENT) ||
        * !WOLFSSL_NO_CLIENT_AUTH) */

/* Do not try to process error for async, non blocking io, and app_read */
void wolfssl_local_MaybeCheckAlertOnErr(WOLFSSL* ssl, int err)
{
#if defined(WOLFSSL_CHECK_ALERT_ON_ERR)
#if defined(WOLFSSL_ASYNC_CRYPT)
    if (err == WC_NO_ERR_TRACE(WC_PENDING_E)) {
        return;
    }
#endif
#if defined(WOLFSSL_NONBLOCK_OCSP)
    if (err == WC_NO_ERR_TRACE(OCSP_WANT_READ)) {
        return;
    }
#endif
#if defined(WOLFSSL_EARLY_DATA)
    if (err == WC_NO_ERR_TRACE(APP_DATA_READY)) {
        return;
    }
#endif
    if (err == WC_NO_ERR_TRACE(WANT_WRITE) ||
            err == WC_NO_ERR_TRACE(WANT_READ)) {
        return;
    }
    /* check if an alert was sent */
    ProcessReplyEx(ssl, 1);
#else
    (void)ssl;
    (void)err;
#endif /* WOLFSSL_CHECK_ALERT_ON_ERR */
}

#undef ERROR_OUT

#endif /* !WOLFCRYPT_ONLY */
