wolfSSL SSL/TLS library, support up to TLS1.3

Dependents:   CyaSSL-Twitter-OAuth4Tw Example-client-tls-cert TwitterReader TweetTest ... more

Revision:
7:481bce714567
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/tls.c	Tue May 02 08:44:47 2017 +0000
@@ -0,0 +1,5141 @@
+/* tls.c
+ *
+ * Copyright (C) 2006-2016 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 2 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
+ */
+
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssl/wolfcrypt/settings.h>
+
+#ifndef WOLFCRYPT_ONLY
+
+#include <wolfssl/ssl.h>
+#include <wolfssl/internal.h>
+#include <wolfssl/error-ssl.h>
+#include <wolfssl/wolfcrypt/hmac.h>
+#ifdef NO_INLINE
+    #include <wolfssl/wolfcrypt/misc.h>
+#else
+    #define WOLFSSL_MISC_INCLUDED
+    #include <wolfcrypt/src/misc.c>
+#endif
+
+#ifdef HAVE_NTRU
+    #include "libntruencrypt/ntru_crypto.h"
+    #include <wolfssl/wolfcrypt/random.h>
+#endif
+#ifdef HAVE_QSH
+    static int TLSX_AddQSHKey(QSHKey** list, QSHKey* key);
+    static byte* TLSX_QSHKeyFind_Pub(QSHKey* qsh, word16* pubLen, word16 name);
+    static int TLSX_CreateNtruKey(WOLFSSL* ssl, int type);
+#endif
+
+
+#ifndef NO_TLS
+
+/* Digest enable checks */
+#ifdef NO_OLD_TLS /* TLS 1.2 only */
+    #if defined(NO_SHA256) && !defined(WOLFSSL_SHA384) && \
+            !defined(WOLFSSL_SHA512)
+        #error Must have SHA256, SHA384 or SHA512 enabled for TLS 1.2
+    #endif
+#else  /* TLS 1.1 or older */
+    #if defined(NO_MD5) && defined(NO_SHA)
+        #error Must have SHA1 and MD5 enabled for old TLS
+    #endif
+#endif
+
+
+#ifdef WOLFSSL_SHA384
+    #define P_HASH_MAX_SIZE SHA384_DIGEST_SIZE
+#else
+    #define P_HASH_MAX_SIZE SHA256_DIGEST_SIZE
+#endif
+
+/* compute p_hash for MD5, SHA-1, SHA-256, or SHA-384 for TLSv1 PRF */
+static int p_hash(byte* result, word32 resLen, const byte* secret,
+                   word32 secLen, const byte* seed, word32 seedLen, int hash)
+{
+    word32 len = P_HASH_MAX_SIZE;
+    word32 times;
+    word32 lastLen;
+    word32 lastTime;
+    word32 i;
+    word32 idx = 0;
+    int    ret = 0;
+#ifdef WOLFSSL_SMALL_STACK
+    byte*  previous;
+    byte*  current;
+    Hmac*  hmac;
+#else
+    byte   previous[P_HASH_MAX_SIZE];  /* max size */
+    byte   current[P_HASH_MAX_SIZE];   /* max size */
+    Hmac   hmac[1];
+#endif
+
+#ifdef WOLFSSL_SMALL_STACK
+    previous = (byte*)XMALLOC(P_HASH_MAX_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    current  = (byte*)XMALLOC(P_HASH_MAX_SIZE, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    hmac     = (Hmac*)XMALLOC(sizeof(Hmac),    NULL, DYNAMIC_TYPE_TMP_BUFFER);
+
+    if (previous == NULL || current == NULL || hmac == NULL) {
+        if (previous) XFREE(previous, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (current)  XFREE(current,  NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (hmac)     XFREE(hmac,     NULL, DYNAMIC_TYPE_TMP_BUFFER);
+
+        return MEMORY_E;
+    }
+#endif
+
+    switch (hash) {
+        #ifndef NO_MD5
+            case md5_mac:
+                hash = MD5;
+                len  = MD5_DIGEST_SIZE;
+            break;
+        #endif
+
+        #ifndef NO_SHA256
+            case sha256_mac:
+                hash = SHA256;
+                len  = SHA256_DIGEST_SIZE;
+            break;
+        #endif
+
+        #ifdef WOLFSSL_SHA384
+            case sha384_mac:
+                hash = SHA384;
+                len  = SHA384_DIGEST_SIZE;
+            break;
+        #endif
+
+        #ifndef NO_SHA
+            case sha_mac:
+            default:
+                hash = SHA;
+                len  = SHA_DIGEST_SIZE;
+            break;
+        #endif
+    }
+
+    times   = resLen / len;
+    lastLen = resLen % len;
+
+    if (lastLen)
+        times += 1;
+
+    lastTime = times - 1;
+
+    if ((ret = wc_HmacSetKey(hmac, hash, secret, secLen)) == 0) {
+        if ((ret = wc_HmacUpdate(hmac, seed, seedLen)) == 0) { /* A0 = seed */
+            if ((ret = wc_HmacFinal(hmac, previous)) == 0) {   /* A1 */
+                for (i = 0; i < times; i++) {
+                    ret = wc_HmacUpdate(hmac, previous, len);
+                    if (ret != 0)
+                        break;
+                    ret = wc_HmacUpdate(hmac, seed, seedLen);
+                    if (ret != 0)
+                        break;
+                    ret = wc_HmacFinal(hmac, current);
+                    if (ret != 0)
+                        break;
+
+                    if ((i == lastTime) && lastLen)
+                        XMEMCPY(&result[idx], current,
+                                                 min(lastLen, P_HASH_MAX_SIZE));
+                    else {
+                        XMEMCPY(&result[idx], current, len);
+                        idx += len;
+                        ret = wc_HmacUpdate(hmac, previous, len);
+                        if (ret != 0)
+                            break;
+                        ret = wc_HmacFinal(hmac, previous);
+                        if (ret != 0)
+                            break;
+                    }
+                }
+            }
+        }
+    }
+
+    ForceZero(previous,  P_HASH_MAX_SIZE);
+    ForceZero(current,   P_HASH_MAX_SIZE);
+    ForceZero(hmac,      sizeof(Hmac));
+
+#ifdef WOLFSSL_SMALL_STACK
+    XFREE(previous, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    XFREE(current,  NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    XFREE(hmac,     NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+
+    return ret;
+}
+
+#undef P_HASH_MAX_SIZE
+
+
+#ifndef NO_OLD_TLS
+
+/* calculate XOR for TLSv1 PRF */
+static INLINE void get_xor(byte *digest, word32 digLen, byte* md5, byte* sha)
+{
+    word32 i;
+
+    for (i = 0; i < digLen; i++)
+        digest[i] = md5[i] ^ sha[i];
+}
+
+
+/* compute TLSv1 PRF (pseudo random function using HMAC) */
+static int doPRF(byte* digest, word32 digLen, const byte* secret,word32 secLen,
+                 const byte* label, word32 labLen, const byte* seed,
+                 word32 seedLen)
+{
+    int    ret  = 0;
+    word32 half = (secLen + 1) / 2;
+
+#ifdef WOLFSSL_SMALL_STACK
+    byte* md5_half;
+    byte* sha_half;
+    byte* labelSeed;
+    byte* md5_result;
+    byte* sha_result;
+#else
+    byte  md5_half[MAX_PRF_HALF];     /* half is real size */
+    byte  sha_half[MAX_PRF_HALF];     /* half is real size */
+    byte  labelSeed[MAX_PRF_LABSEED]; /* labLen + seedLen is real size */
+    byte  md5_result[MAX_PRF_DIG];    /* digLen is real size */
+    byte  sha_result[MAX_PRF_DIG];    /* digLen is real size */
+#endif
+
+    if (half > MAX_PRF_HALF)
+        return BUFFER_E;
+    if (labLen + seedLen > MAX_PRF_LABSEED)
+        return BUFFER_E;
+    if (digLen > MAX_PRF_DIG)
+        return BUFFER_E;
+
+#ifdef WOLFSSL_SMALL_STACK
+    md5_half   = (byte*)XMALLOC(MAX_PRF_HALF,    NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    sha_half   = (byte*)XMALLOC(MAX_PRF_HALF,    NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    labelSeed  = (byte*)XMALLOC(MAX_PRF_LABSEED, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    md5_result = (byte*)XMALLOC(MAX_PRF_DIG,     NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    sha_result = (byte*)XMALLOC(MAX_PRF_DIG,     NULL, DYNAMIC_TYPE_TMP_BUFFER);
+
+    if (md5_half == NULL || sha_half == NULL || labelSeed == NULL ||
+                                     md5_result == NULL || sha_result == NULL) {
+        if (md5_half)   XFREE(md5_half,   NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (sha_half)   XFREE(sha_half,   NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (labelSeed)  XFREE(labelSeed,  NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (md5_result) XFREE(md5_result, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+        if (sha_result) XFREE(sha_result, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+
+        return MEMORY_E;
+    }
+#endif
+
+    XMEMSET(md5_result, 0, digLen);
+    XMEMSET(sha_result, 0, digLen);
+
+    XMEMCPY(md5_half, secret, half);
+    XMEMCPY(sha_half, secret + half - secLen % 2, half);
+
+    XMEMCPY(labelSeed, label, labLen);
+    XMEMCPY(labelSeed + labLen, seed, seedLen);
+
+    if ((ret = p_hash(md5_result, digLen, md5_half, half, labelSeed,
+                                             labLen + seedLen, md5_mac)) == 0) {
+        if ((ret = p_hash(sha_result, digLen, sha_half, half, labelSeed,
+                                             labLen + seedLen, sha_mac)) == 0) {
+            get_xor(digest, digLen, md5_result, sha_result);
+        }
+    }
+
+#ifdef WOLFSSL_SMALL_STACK
+    XFREE(md5_half,   NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    XFREE(sha_half,   NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    XFREE(labelSeed,  NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    XFREE(md5_result, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    XFREE(sha_result, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+
+    return ret;
+}
+
+#endif
+
+
+/* Wrapper to call straight thru to p_hash in TSL 1.2 cases to remove stack
+   use */
+static int PRF(byte* digest, word32 digLen, const byte* secret, word32 secLen,
+            const byte* label, word32 labLen, const byte* seed, word32 seedLen,
+            int useAtLeastSha256, int hash_type)
+{
+    int ret = 0;
+
+    if (useAtLeastSha256) {
+#ifdef WOLFSSL_SMALL_STACK
+        byte* labelSeed;
+#else
+        byte labelSeed[MAX_PRF_LABSEED]; /* labLen + seedLen is real size */
+#endif
+
+        if (labLen + seedLen > MAX_PRF_LABSEED)
+            return BUFFER_E;
+
+#ifdef WOLFSSL_SMALL_STACK
+        labelSeed = (byte*)XMALLOC(MAX_PRF_LABSEED, NULL,
+                                                       DYNAMIC_TYPE_TMP_BUFFER);
+        if (labelSeed == NULL)
+           return MEMORY_E;
+#endif
+
+        XMEMCPY(labelSeed, label, labLen);
+        XMEMCPY(labelSeed + labLen, seed, seedLen);
+
+        /* If a cipher suite wants an algorithm better than sha256, it
+         * should use better. */
+        if (hash_type < sha256_mac || hash_type == blake2b_mac)
+            hash_type = sha256_mac;
+        ret = p_hash(digest, digLen, secret, secLen, labelSeed,
+                     labLen + seedLen, hash_type);
+
+#ifdef WOLFSSL_SMALL_STACK
+        XFREE(labelSeed, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+    }
+#ifndef NO_OLD_TLS
+    else {
+        ret = doPRF(digest, digLen, secret, secLen, label, labLen, seed,
+                    seedLen);
+    }
+#endif
+
+    return ret;
+}
+
+
+#ifdef WOLFSSL_SHA384
+    #define HSHASH_SZ SHA384_DIGEST_SIZE
+#else
+    #define HSHASH_SZ FINISHED_SZ
+#endif
+
+
+int BuildTlsHandshakeHash(WOLFSSL* ssl, byte* hash, word32* hashLen)
+{
+    word32 hashSz = FINISHED_SZ;
+
+    if (ssl == NULL || hash == NULL || hashLen == NULL || *hashLen < HSHASH_SZ)
+        return BAD_FUNC_ARG;
+
+#ifndef NO_OLD_TLS
+    wc_Md5GetHash(&ssl->hsHashes->hashMd5, hash);
+    wc_ShaGetHash(&ssl->hsHashes->hashSha, &hash[MD5_DIGEST_SIZE]);
+#endif
+
+    if (IsAtLeastTLSv1_2(ssl)) {
+#ifndef NO_SHA256
+        if (ssl->specs.mac_algorithm <= sha256_mac ||
+            ssl->specs.mac_algorithm == blake2b_mac) {
+            int ret = wc_Sha256GetHash(&ssl->hsHashes->hashSha256, hash);
+
+            if (ret != 0)
+                return ret;
+
+            hashSz = SHA256_DIGEST_SIZE;
+        }
+#endif
+#ifdef WOLFSSL_SHA384
+        if (ssl->specs.mac_algorithm == sha384_mac) {
+            int ret = wc_Sha384GetHash(&ssl->hsHashes->hashSha384, hash);
+
+            if (ret != 0)
+                return ret;
+
+            hashSz = SHA384_DIGEST_SIZE;
+        }
+#endif
+    }
+
+    *hashLen = hashSz;
+
+    return 0;
+}
+
+
+int BuildTlsFinished(WOLFSSL* ssl, Hashes* hashes, const byte* sender)
+{
+    int         ret;
+    const byte* side;
+    byte        handshake_hash[HSHASH_SZ];
+    word32      hashSz = HSHASH_SZ;
+
+    ret = BuildTlsHandshakeHash(ssl, handshake_hash, &hashSz);
+    if (ret < 0)
+        return ret;
+
+    if ( XSTRNCMP((const char*)sender, (const char*)client, SIZEOF_SENDER) == 0)
+        side = tls_client;
+    else
+        side = tls_server;
+
+    return PRF((byte*)hashes, TLS_FINISHED_SZ, ssl->arrays->masterSecret,
+               SECRET_LEN, side, FINISHED_LABEL_SZ, handshake_hash, hashSz,
+               IsAtLeastTLSv1_2(ssl), ssl->specs.mac_algorithm);
+}
+
+
+#ifndef NO_OLD_TLS
+
+ProtocolVersion MakeTLSv1(void)
+{
+    ProtocolVersion pv;
+    pv.major = SSLv3_MAJOR;
+    pv.minor = TLSv1_MINOR;
+
+    return pv;
+}
+
+
+ProtocolVersion MakeTLSv1_1(void)
+{
+    ProtocolVersion pv;
+    pv.major = SSLv3_MAJOR;
+    pv.minor = TLSv1_1_MINOR;
+
+    return pv;
+}
+
+#endif
+
+
+ProtocolVersion MakeTLSv1_2(void)
+{
+    ProtocolVersion pv;
+    pv.major = SSLv3_MAJOR;
+    pv.minor = TLSv1_2_MINOR;
+
+    return pv;
+}
+
+
+#ifdef HAVE_EXTENDED_MASTER
+static const byte ext_master_label[EXT_MASTER_LABEL_SZ + 1] =
+                                                      "extended master secret";
+#endif
+static const byte master_label[MASTER_LABEL_SZ + 1] = "master secret";
+static const byte key_label   [KEY_LABEL_SZ + 1]    = "key expansion";
+
+
+/* External facing wrapper so user can call as well, 0 on success */
+int wolfSSL_DeriveTlsKeys(byte* key_data, word32 keyLen,
+                         const byte* ms, word32 msLen,
+                         const byte* sr, const byte* cr,
+                         int tls1_2, int hash_type)
+{
+    byte  seed[SEED_LEN];
+
+    XMEMCPY(seed,           sr, RAN_LEN);
+    XMEMCPY(seed + RAN_LEN, cr, RAN_LEN);
+
+    return PRF(key_data, keyLen, ms, msLen, key_label, KEY_LABEL_SZ,
+               seed, SEED_LEN, tls1_2, hash_type);
+}
+
+
+int DeriveTlsKeys(WOLFSSL* ssl)
+{
+    int   ret;
+    int   length = 2 * ssl->specs.hash_size +
+                   2 * ssl->specs.key_size  +
+                   2 * ssl->specs.iv_size;
+#ifdef WOLFSSL_SMALL_STACK
+    byte* key_data;
+#else
+    byte  key_data[MAX_PRF_DIG];
+#endif
+
+#ifdef WOLFSSL_SMALL_STACK
+    key_data = (byte*)XMALLOC(MAX_PRF_DIG, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    if (key_data == NULL) {
+        return MEMORY_E;
+    }
+#endif
+
+    ret = wolfSSL_DeriveTlsKeys(key_data, length,
+                           ssl->arrays->masterSecret, SECRET_LEN,
+                           ssl->arrays->serverRandom, ssl->arrays->clientRandom,
+                           IsAtLeastTLSv1_2(ssl), ssl->specs.mac_algorithm);
+    if (ret == 0)
+        ret = StoreKeys(ssl, key_data);
+
+#ifdef WOLFSSL_SMALL_STACK
+    XFREE(key_data, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+
+    return ret;
+}
+
+
+/* External facing wrapper so user can call as well, 0 on success */
+int wolfSSL_MakeTlsMasterSecret(byte* ms, word32 msLen,
+                               const byte* pms, word32 pmsLen,
+                               const byte* cr, const byte* sr,
+                               int tls1_2, int hash_type)
+{
+    byte  seed[SEED_LEN];
+
+    XMEMCPY(seed,           cr, RAN_LEN);
+    XMEMCPY(seed + RAN_LEN, sr, RAN_LEN);
+
+    return PRF(ms, msLen, pms, pmsLen, master_label, MASTER_LABEL_SZ,
+               seed, SEED_LEN, tls1_2, hash_type);
+}
+
+
+#ifdef HAVE_EXTENDED_MASTER
+
+/* External facing wrapper so user can call as well, 0 on success */
+int wolfSSL_MakeTlsExtendedMasterSecret(byte* ms, word32 msLen,
+                                        const byte* pms, word32 pmsLen,
+                                        const byte* sHash, word32 sHashLen,
+                                        int tls1_2, int hash_type)
+{
+    return PRF(ms, msLen, pms, pmsLen, ext_master_label, EXT_MASTER_LABEL_SZ,
+               sHash, sHashLen, tls1_2, hash_type);
+}
+
+#endif /* HAVE_EXTENDED_MASTER */
+
+
+int MakeTlsMasterSecret(WOLFSSL* ssl)
+{
+    int    ret;
+#ifdef HAVE_EXTENDED_MASTER
+    byte   handshake_hash[HSHASH_SZ];
+    word32 hashSz = HSHASH_SZ;
+
+    if (ssl->options.haveEMS) {
+
+        ret = BuildTlsHandshakeHash(ssl, handshake_hash, &hashSz);
+        if (ret < 0)
+            return ret;
+
+        ret = wolfSSL_MakeTlsExtendedMasterSecret(
+                ssl->arrays->masterSecret, SECRET_LEN,
+                ssl->arrays->preMasterSecret, ssl->arrays->preMasterSz,
+                handshake_hash, hashSz,
+                IsAtLeastTLSv1_2(ssl), ssl->specs.mac_algorithm);
+    } else
+#endif
+    ret = wolfSSL_MakeTlsMasterSecret(ssl->arrays->masterSecret, SECRET_LEN,
+              ssl->arrays->preMasterSecret, ssl->arrays->preMasterSz,
+              ssl->arrays->clientRandom, ssl->arrays->serverRandom,
+              IsAtLeastTLSv1_2(ssl), ssl->specs.mac_algorithm);
+
+    if (ret == 0) {
+    #ifdef SHOW_SECRETS
+        int i;
+
+        printf("master secret: ");
+        for (i = 0; i < SECRET_LEN; i++)
+            printf("%02x", ssl->arrays->masterSecret[i]);
+        printf("\n");
+    #endif
+
+        ret = DeriveTlsKeys(ssl);
+    }
+
+    return ret;
+}
+
+
+/* Used by EAP-TLS and EAP-TTLS to derive keying material from
+ * the master_secret. */
+int wolfSSL_make_eap_keys(WOLFSSL* ssl, void* msk, unsigned int len,
+                                                              const char* label)
+{
+    int   ret;
+#ifdef WOLFSSL_SMALL_STACK
+    byte* seed;
+#else
+    byte  seed[SEED_LEN];
+#endif
+
+#ifdef WOLFSSL_SMALL_STACK
+    seed = (byte*)XMALLOC(SEED_LEN, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    if (seed == NULL)
+        return MEMORY_E;
+#endif
+
+    /*
+     * As per RFC-5281, the order of the client and server randoms is reversed
+     * from that used by the TLS protocol to derive keys.
+     */
+    XMEMCPY(seed,           ssl->arrays->clientRandom, RAN_LEN);
+    XMEMCPY(seed + RAN_LEN, ssl->arrays->serverRandom, RAN_LEN);
+
+    ret = PRF((byte*)msk, len, ssl->arrays->masterSecret, SECRET_LEN,
+              (const byte *)label, (word32)XSTRLEN(label), seed, SEED_LEN,
+              IsAtLeastTLSv1_2(ssl), ssl->specs.mac_algorithm);
+
+#ifdef WOLFSSL_SMALL_STACK
+    XFREE(seed, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+#endif
+
+    return ret;
+}
+
+
+/*** next for static INLINE s copied internal.c ***/
+
+/* convert 16 bit integer to opaque */
+static INLINE void c16toa(word16 u16, byte* c)
+{
+    c[0] = (u16 >> 8) & 0xff;
+    c[1] =  u16 & 0xff;
+}
+
+#ifdef HAVE_TLS_EXTENSIONS
+/* convert opaque to 16 bit integer */
+static INLINE void ato16(const byte* c, word16* u16)
+{
+    *u16 = (c[0] << 8) | (c[1]);
+}
+
+#if defined(HAVE_SNI) && !defined(NO_WOLFSSL_SERVER)
+/* convert a 24 bit integer into a 32 bit one */
+static INLINE void c24to32(const word24 u24, word32* u32)
+{
+    *u32 = (u24[0] << 16) | (u24[1] << 8) | u24[2];
+}
+#endif
+#endif
+
+/* convert 32 bit integer to opaque */
+static INLINE void c32toa(word32 u32, byte* c)
+{
+    c[0] = (u32 >> 24) & 0xff;
+    c[1] = (u32 >> 16) & 0xff;
+    c[2] = (u32 >>  8) & 0xff;
+    c[3] =  u32 & 0xff;
+}
+
+
+static 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++;
+        }
+    }
+}
+
+
+#ifdef WOLFSSL_DTLS
+static INLINE void DtlsGetSEQ(WOLFSSL* ssl, int order, word32 seq[2])
+{
+    if (order == PREV_ORDER) {
+        /* Previous epoch case */
+        seq[0] = ((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) {
+        seq[0] = (ssl->keys.curEpoch << 16) |
+                 (ssl->keys.curSeq_hi & 0xFFFF);
+        seq[1] = ssl->keys.curSeq_lo; /* explicit from peer */
+    }
+    else {
+        seq[0] = (ssl->keys.dtls_epoch << 16) |
+                 (ssl->keys.dtls_sequence_number_hi & 0xFFFF);
+        seq[1] = ssl->keys.dtls_sequence_number_lo;
+    }
+}
+#endif /* WOLFSSL_DTLS */
+
+
+static INLINE 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);
+}
+
+
+/*** end copy ***/
+
+
+/* return HMAC digest type in wolfSSL format */
+int wolfSSL_GetHmacType(WOLFSSL* ssl)
+{
+    if (ssl == NULL)
+        return BAD_FUNC_ARG;
+
+    switch (ssl->specs.mac_algorithm) {
+        #ifndef NO_MD5
+        case md5_mac:
+        {
+            return MD5;
+        }
+        #endif
+        #ifndef NO_SHA256
+        case sha256_mac:
+        {
+            return SHA256;
+        }
+        #endif
+        #ifdef WOLFSSL_SHA384
+        case sha384_mac:
+        {
+            return SHA384;
+        }
+
+        #endif
+        #ifndef NO_SHA
+        case sha_mac:
+        {
+            return SHA;
+        }
+        #endif
+        #ifdef HAVE_BLAKE2
+        case blake2b_mac:
+        {
+            return BLAKE2B_ID;
+        }
+        #endif
+        default:
+        {
+            return SSL_FATAL_ERROR;
+        }
+    }
+}
+
+
+int wolfSSL_SetTlsHmacInner(WOLFSSL* ssl, byte* inner, word32 sz, int content,
+                           int verify)
+{
+    if (ssl == NULL || inner == NULL)
+        return BAD_FUNC_ARG;
+
+    XMEMSET(inner, 0, WOLFSSL_TLS_HMAC_INNER_SZ);
+
+    WriteSEQ(ssl, verify, inner);
+    inner[SEQ_SZ] = (byte)content;
+    inner[SEQ_SZ + ENUM_LEN]            = ssl->version.major;
+    inner[SEQ_SZ + ENUM_LEN + ENUM_LEN] = ssl->version.minor;
+    c16toa((word16)sz, inner + SEQ_SZ + ENUM_LEN + VERSION_SZ);
+
+    return 0;
+}
+
+
+/* TLS type HMAC */
+int TLS_hmac(WOLFSSL* ssl, byte* digest, const byte* in, word32 sz,
+              int content, int verify)
+{
+    Hmac hmac;
+    int  ret;
+    byte myInner[WOLFSSL_TLS_HMAC_INNER_SZ];
+
+    if (ssl == NULL)
+        return BAD_FUNC_ARG;
+
+#ifdef HAVE_FUZZER
+    if (ssl->fuzzerCb)
+        ssl->fuzzerCb(ssl, in, sz, FUZZ_HMAC, ssl->fuzzerCtx);
+#endif
+
+    wolfSSL_SetTlsHmacInner(ssl, myInner, sz, content, verify);
+
+    ret = wc_HmacSetKey(&hmac, wolfSSL_GetHmacType(ssl),
+                     wolfSSL_GetMacSecret(ssl, verify), ssl->specs.hash_size);
+    if (ret != 0)
+        return ret;
+    ret = wc_HmacUpdate(&hmac, myInner, sizeof(myInner));
+    if (ret != 0)
+        return ret;
+    ret = wc_HmacUpdate(&hmac, in, sz);                                /* content */
+    if (ret != 0)
+        return ret;
+    ret = wc_HmacFinal(&hmac, digest);
+    if (ret != 0)
+        return ret;
+
+    return 0;
+}
+
+#ifdef HAVE_TLS_EXTENSIONS
+
+/**
+ * The TLSX semaphore is used to calculate the size of the extensions to be sent
+ * from one peer to another.
+ */
+
+/** Supports up to 64 flags. Increase as needed. */
+#define SEMAPHORE_SIZE 8
+
+/**
+ * Converts the extension type (id) to an index in the semaphore.
+ *
+ * Oficial reference for TLS extension types:
+ *   http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xml
+ *
+ * Motivation:
+ *   Previously, we used the extension type itself as the index of that
+ *   extension in the semaphore as the extension types were declared
+ *   sequentially, but maintain a semaphore as big as the number of available
+ *   extensions is no longer an option since the release of renegotiation_info.
+ *
+ * How to update:
+ *   Assign extension types that extrapolate the number of available semaphores
+ *   to the first available index going backwards in the semaphore array.
+ *   When adding a new extension type that don't extrapolate the number of
+ *   available semaphores, check for a possible collision with with a
+ *   'remapped' extension type.
+ */
+static INLINE word16 TLSX_ToSemaphore(word16 type)
+{
+    switch (type) {
+
+        case TLSX_RENEGOTIATION_INFO: /* 0xFF01 */
+            return 63;
+
+        default:
+            if (type > 62) {
+                /* This message SHOULD only happens during the adding of
+                   new TLS extensions in which its IANA number overflows
+                   the current semaphore's range, or if its number already
+                   is assigned to be used by another extension.
+                   Use this check value for the new extension and decrement
+                   the check value by one. */
+                WOLFSSL_MSG("### TLSX semaphore colision or overflow detected!");
+            }
+    }
+
+    return type;
+}
+
+/** Checks if a specific light (tls extension) is not set in the semaphore. */
+#define IS_OFF(semaphore, light) \
+    ((semaphore)[(light) / 8] ^  (byte) (0x01 << ((light) % 8)))
+
+/** Turn on a specific light (tls extension) in the semaphore. */
+#define TURN_ON(semaphore, light) \
+    ((semaphore)[(light) / 8] |= (byte) (0x01 << ((light) % 8)))
+
+/** Creates a new extension. */
+static TLSX* TLSX_New(TLSX_Type type, void* data, void* heap)
+{
+    TLSX* extension = (TLSX*)XMALLOC(sizeof(TLSX), heap, DYNAMIC_TYPE_TLSX);
+
+    if (extension) {
+        extension->type = type;
+        extension->data = data;
+        extension->resp = 0;
+        extension->next = NULL;
+    }
+
+    return extension;
+}
+
+/**
+ * Creates a new extension and pushes it to the provided list.
+ * Checks for duplicate extensions, keeps the newest.
+ */
+static int TLSX_Push(TLSX** list, TLSX_Type type, void* data, void* heap)
+{
+    TLSX* extension = TLSX_New(type, data, heap);
+
+    if (extension == NULL)
+        return MEMORY_E;
+
+    /* pushes the new extension on the list. */
+    extension->next = *list;
+    *list = extension;
+
+    /* remove duplicate extensions, there should be only one of each type. */
+    do {
+        if (extension->next && extension->next->type == type) {
+            TLSX *next = extension->next;
+
+            extension->next = next->next;
+            next->next = NULL;
+
+            TLSX_FreeAll(next, heap);
+
+            /* there is no way to occur more than */
+            /* two extensions of the same type.   */
+            break;
+        }
+    } while ((extension = extension->next));
+
+    return 0;
+}
+
+#ifndef NO_WOLFSSL_SERVER
+
+/** Mark an extension to be sent back to the client. */
+void TLSX_SetResponse(WOLFSSL* ssl, TLSX_Type type);
+
+void TLSX_SetResponse(WOLFSSL* ssl, TLSX_Type type)
+{
+    TLSX *ext = TLSX_Find(ssl->extensions, type);
+
+    if (ext)
+        ext->resp = 1;
+}
+
+#endif
+
+/******************************************************************************/
+/* Application-Layer Protocol Negotiation                                     */
+/******************************************************************************/
+
+#ifdef HAVE_ALPN
+/** Creates a new ALPN object, providing protocol name to use. */
+static ALPN* TLSX_ALPN_New(char *protocol_name, word16 protocol_nameSz,
+                                                                     void* heap)
+{
+    ALPN *alpn;
+
+    WOLFSSL_ENTER("TLSX_ALPN_New");
+
+    if (protocol_name == NULL ||
+        protocol_nameSz > WOLFSSL_MAX_ALPN_PROTO_NAME_LEN) {
+        WOLFSSL_MSG("Invalid arguments");
+        return NULL;
+    }
+
+    alpn = (ALPN*)XMALLOC(sizeof(ALPN), heap, DYNAMIC_TYPE_TLSX);
+    if (alpn == NULL) {
+        WOLFSSL_MSG("Memory failure");
+        return NULL;
+    }
+
+    alpn->next = NULL;
+    alpn->negotiated = 0;
+    alpn->options = 0;
+
+    alpn->protocol_name = (char*)XMALLOC(protocol_nameSz + 1,
+                                         heap, DYNAMIC_TYPE_TLSX);
+    if (alpn->protocol_name == NULL) {
+        WOLFSSL_MSG("Memory failure");
+        XFREE(alpn, heap, DYNAMIC_TYPE_TLSX);
+        return NULL;
+    }
+
+    XMEMCPY(alpn->protocol_name, protocol_name, protocol_nameSz);
+    alpn->protocol_name[protocol_nameSz] = 0;
+
+    return alpn;
+}
+
+/** Releases an ALPN object. */
+static void TLSX_ALPN_Free(ALPN *alpn, void* heap)
+{
+    (void)heap;
+
+    if (alpn == NULL)
+        return;
+
+    XFREE(alpn->protocol_name, heap, DYNAMIC_TYPE_TLSX);
+    XFREE(alpn, heap, DYNAMIC_TYPE_TLSX);
+}
+
+/** Releases all ALPN objects in the provided list. */
+static void TLSX_ALPN_FreeAll(ALPN *list, void* heap)
+{
+    ALPN* alpn;
+
+    while ((alpn = list)) {
+        list = alpn->next;
+        TLSX_ALPN_Free(alpn, heap);
+    }
+}
+
+/** Tells the buffered size of the ALPN objects in a list. */
+static word16 TLSX_ALPN_GetSize(ALPN *list)
+{
+    ALPN* alpn;
+    word16 length = OPAQUE16_LEN; /* list length */
+
+    while ((alpn = list)) {
+        list = alpn->next;
+
+        length++; /* protocol name length is on one byte */
+        length += (word16)XSTRLEN(alpn->protocol_name);
+    }
+
+    return length;
+}
+
+/** Writes the ALPN objects of a list in a buffer. */
+static word16 TLSX_ALPN_Write(ALPN *list, byte *output)
+{
+    ALPN* alpn;
+    word16 length = 0;
+    word16 offset = OPAQUE16_LEN; /* list length offset */
+
+    while ((alpn = list)) {
+        list = alpn->next;
+
+        length = (word16)XSTRLEN(alpn->protocol_name);
+
+        /* protocol name length */
+        output[offset++] = (byte)length;
+
+        /* protocol name value */
+        XMEMCPY(output + offset, alpn->protocol_name, length);
+
+        offset += length;
+    }
+
+    /* writing list length */
+    c16toa(offset - OPAQUE16_LEN, output);
+
+    return offset;
+}
+
+/** Finds a protocol name in the provided ALPN list */
+static ALPN* TLSX_ALPN_Find(ALPN *list, char *protocol_name, word16 size)
+{
+    ALPN *alpn;
+
+    if (list == NULL || protocol_name == NULL)
+        return NULL;
+
+    alpn = list;
+    while (alpn != NULL && (
+           (word16)XSTRLEN(alpn->protocol_name) != size ||
+           XSTRNCMP(alpn->protocol_name, protocol_name, size)))
+        alpn = alpn->next;
+
+    return alpn;
+}
+
+/** Set the ALPN matching client and server requirements */
+static int TLSX_SetALPN(TLSX** extensions, const void* data, word16 size,
+                                                                     void* heap)
+{
+    ALPN *alpn;
+    int  ret;
+
+    if (extensions == NULL || data == NULL)
+        return BAD_FUNC_ARG;
+
+    alpn = TLSX_ALPN_New((char *)data, size, heap);
+    if (alpn == NULL) {
+        WOLFSSL_MSG("Memory failure");
+        return MEMORY_E;
+    }
+
+    alpn->negotiated = 1;
+
+    ret = TLSX_Push(extensions, TLSX_APPLICATION_LAYER_PROTOCOL, (void*)alpn,
+                                                                          heap);
+    if (ret != 0) {
+        TLSX_ALPN_Free(alpn, heap);
+        return ret;
+    }
+
+    return SSL_SUCCESS;
+}
+
+/** Parses a buffer of ALPN extensions and set the first one matching
+ * client and server requirements */
+static int TLSX_ALPN_ParseAndSet(WOLFSSL *ssl, byte *input, word16 length,
+                                 byte isRequest)
+{
+    word16  size = 0, offset = 0, idx = 0;
+    int     r = BUFFER_ERROR;
+    byte    match = 0;
+    TLSX    *extension;
+    ALPN    *alpn = NULL, *list;
+
+    extension = TLSX_Find(ssl->extensions, TLSX_APPLICATION_LAYER_PROTOCOL);
+    if (extension == NULL)
+        extension = TLSX_Find(ssl->ctx->extensions,
+                                               TLSX_APPLICATION_LAYER_PROTOCOL);
+
+    if (extension == NULL || extension->data == NULL) {
+        WOLFSSL_MSG("No ALPN extensions not used or bad");
+        return isRequest ? 0             /* not using ALPN */
+                         : BUFFER_ERROR; /* unexpected ALPN response */
+    }
+
+    if (OPAQUE16_LEN > length)
+        return BUFFER_ERROR;
+
+    ato16(input, &size);
+    offset += OPAQUE16_LEN;
+
+    /* validating alpn list length */
+    if (length != OPAQUE16_LEN + size)
+        return BUFFER_ERROR;
+
+    list = (ALPN*)extension->data;
+
+    /* keep the list sent by client */
+    if (isRequest) {
+        if (ssl->alpn_client_list != NULL)
+            XFREE(ssl->alpn_client_list, ssl->heap, DYNAMIC_TYPE_TMP_BUFFER);
+
+        ssl->alpn_client_list = (char *)XMALLOC(size, ssl->heap,
+                                                DYNAMIC_TYPE_TMP_BUFFER);
+        if (ssl->alpn_client_list == NULL)
+            return MEMORY_ERROR;
+    }
+
+    for (size = 0; offset < length; offset += size) {
+
+        size = input[offset++];
+        if (offset + size > length)
+            return BUFFER_ERROR;
+
+        if (isRequest) {
+            XMEMCPY(ssl->alpn_client_list+idx, (char*)input + offset, size);
+            idx += size;
+            ssl->alpn_client_list[idx++] = ',';
+        }
+
+        if (!match) {
+            alpn = TLSX_ALPN_Find(list, (char*)input + offset, size);
+            if (alpn != NULL) {
+                WOLFSSL_MSG("ALPN protocol match");
+                match = 1;
+
+                /* skip reading other values if not required */
+                if (!isRequest)
+                    break;
+            }
+        }
+    }
+
+    if (isRequest)
+        ssl->alpn_client_list[idx-1] = 0;
+
+    if (!match) {
+        WOLFSSL_MSG("No ALPN protocol match");
+
+        /* do nothing if no protocol match between client and server and option
+         is set to continue (like OpenSSL) */
+        if (list->options & WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) {
+            WOLFSSL_MSG("Continue on mismatch");
+            return 0;
+        }
+
+        SendAlert(ssl, alert_fatal, no_application_protocol);
+        return UNKNOWN_ALPN_PROTOCOL_NAME_E;
+    }
+
+    /* set the matching negotiated protocol */
+    r = TLSX_SetALPN(&ssl->extensions,
+                     alpn->protocol_name,
+                     (word16)XSTRLEN(alpn->protocol_name),
+                     ssl->heap);
+    if (r != SSL_SUCCESS) {
+        WOLFSSL_MSG("TLSX_UseALPN failed");
+        return BUFFER_ERROR;
+    }
+
+    /* reply to ALPN extension sent from client */
+    if (isRequest) {
+#ifndef NO_WOLFSSL_SERVER
+        TLSX_SetResponse(ssl, TLSX_APPLICATION_LAYER_PROTOCOL);
+#endif
+    }
+
+    return 0;
+}
+
+/** Add a protocol name to the list of accepted usable ones */
+int TLSX_UseALPN(TLSX** extensions, const void* data, word16 size, byte options,
+                                                                     void* heap)
+{
+    ALPN *alpn;
+    TLSX *extension;
+    int  ret;
+
+    if (extensions == NULL || data == NULL)
+        return BAD_FUNC_ARG;
+
+    alpn = TLSX_ALPN_New((char *)data, size, heap);
+    if (alpn == NULL) {
+        WOLFSSL_MSG("Memory failure");
+        return MEMORY_E;
+    }
+
+    /* Set Options of ALPN */
+    alpn->options = options;
+
+    extension = TLSX_Find(*extensions, TLSX_APPLICATION_LAYER_PROTOCOL);
+    if (extension == NULL) {
+        ret = TLSX_Push(extensions, TLSX_APPLICATION_LAYER_PROTOCOL,
+                                                             (void*)alpn, heap);
+        if (ret != 0) {
+            TLSX_ALPN_Free(alpn, heap);
+            return ret;
+        }
+    }
+    else {
+        /* push new ALPN object to extension data. */
+        alpn->next = (ALPN*)extension->data;
+        extension->data = (void*)alpn;
+    }
+
+    return SSL_SUCCESS;
+}
+
+/** Get the protocol name set by the server */
+int TLSX_ALPN_GetRequest(TLSX* extensions, void** data, word16 *dataSz)
+{
+    TLSX *extension;
+    ALPN *alpn;
+
+    if (extensions == NULL || data == NULL || dataSz == NULL)
+        return BAD_FUNC_ARG;
+
+    extension = TLSX_Find(extensions, TLSX_APPLICATION_LAYER_PROTOCOL);
+    if (extension == NULL) {
+        WOLFSSL_MSG("TLS extension not found");
+        return SSL_ALPN_NOT_FOUND;
+    }
+
+    alpn = (ALPN *)extension->data;
+    if (alpn == NULL) {
+        WOLFSSL_MSG("ALPN extension not found");
+        *data = NULL;
+        *dataSz = 0;
+        return SSL_FATAL_ERROR;
+    }
+
+    if (alpn->negotiated != 1) {
+
+        /* consider as an error */
+        if (alpn->options & WOLFSSL_ALPN_FAILED_ON_MISMATCH) {
+            WOLFSSL_MSG("No protocol match with peer -> Failed");
+            return SSL_FATAL_ERROR;
+        }
+
+        /* continue without negotiated protocol */
+        WOLFSSL_MSG("No protocol match with peer -> Continue");
+        return SSL_ALPN_NOT_FOUND;
+    }
+
+    if (alpn->next != NULL) {
+        WOLFSSL_MSG("Only one protocol name must be accepted");
+        return SSL_FATAL_ERROR;
+    }
+
+    *data = alpn->protocol_name;
+    *dataSz = (word16)XSTRLEN((char*)*data);
+
+    return SSL_SUCCESS;
+}
+
+#define ALPN_FREE_ALL     TLSX_ALPN_FreeAll
+#define ALPN_GET_SIZE     TLSX_ALPN_GetSize
+#define ALPN_WRITE        TLSX_ALPN_Write
+#define ALPN_PARSE        TLSX_ALPN_ParseAndSet
+
+#else /* HAVE_ALPN */
+
+#define ALPN_FREE_ALL(list, heap)
+#define ALPN_GET_SIZE(list)     0
+#define ALPN_WRITE(a, b)        0
+#define ALPN_PARSE(a, b, c, d)  0
+
+#endif /* HAVE_ALPN */
+
+/******************************************************************************/
+/* Server Name Indication                                                     */
+/******************************************************************************/
+
+#ifdef HAVE_SNI
+
+/** Creates a new SNI object. */
+static SNI* TLSX_SNI_New(byte type, const void* data, word16 size, void* heap)
+{
+    SNI* sni = (SNI*)XMALLOC(sizeof(SNI), heap, DYNAMIC_TYPE_TLSX);
+
+    if (sni) {
+        sni->type = type;
+        sni->next = NULL;
+
+    #ifndef NO_WOLFSSL_SERVER
+        sni->options = 0;
+        sni->status  = WOLFSSL_SNI_NO_MATCH;
+    #endif
+
+        switch (sni->type) {
+            case WOLFSSL_SNI_HOST_NAME:
+                sni->data.host_name = (char*)XMALLOC(size + 1, heap,
+                                                     DYNAMIC_TYPE_TLSX);
+                if (sni->data.host_name) {
+                    XSTRNCPY(sni->data.host_name, (const char*)data, size);
+                    sni->data.host_name[size] = 0;
+                } else {
+                    XFREE(sni, heap, DYNAMIC_TYPE_TLSX);
+                    sni = NULL;
+                }
+            break;
+
+            default: /* invalid type */
+                XFREE(sni, heap, DYNAMIC_TYPE_TLSX);
+                sni = NULL;
+        }
+    }
+
+    return sni;
+}
+
+/** Releases a SNI object. */
+static void TLSX_SNI_Free(SNI* sni, void* heap)
+{
+    if (sni) {
+        switch (sni->type) {
+            case WOLFSSL_SNI_HOST_NAME:
+                XFREE(sni->data.host_name, heap, DYNAMIC_TYPE_TLSX);
+            break;
+        }
+
+        XFREE(sni, heap, DYNAMIC_TYPE_TLSX);
+    }
+    (void)heap;
+}
+
+/** Releases all SNI objects in the provided list. */
+static void TLSX_SNI_FreeAll(SNI* list, void* heap)
+{
+    SNI* sni;
+
+    while ((sni = list)) {
+        list = sni->next;
+        TLSX_SNI_Free(sni, heap);
+    }
+}
+
+/** Tells the buffered size of the SNI objects in a list. */
+static word16 TLSX_SNI_GetSize(SNI* list)
+{
+    SNI* sni;
+    word16 length = OPAQUE16_LEN; /* list length */
+
+    while ((sni = list)) {
+        list = sni->next;
+
+        length += ENUM_LEN + OPAQUE16_LEN; /* sni type + sni length */
+
+        switch (sni->type) {
+            case WOLFSSL_SNI_HOST_NAME:
+                length += XSTRLEN((char*)sni->data.host_name);
+            break;
+        }
+    }
+
+    return length;
+}
+
+/** Writes the SNI objects of a list in a buffer. */
+static word16 TLSX_SNI_Write(SNI* list, byte* output)
+{
+    SNI* sni;
+    word16 length = 0;
+    word16 offset = OPAQUE16_LEN; /* list length offset */
+
+    while ((sni = list)) {
+        list = sni->next;
+
+        output[offset++] = sni->type; /* sni type */
+
+        switch (sni->type) {
+            case WOLFSSL_SNI_HOST_NAME:
+                length = XSTRLEN((char*)sni->data.host_name);
+
+                c16toa(length, output + offset); /* sni length */
+                offset += OPAQUE16_LEN;
+
+                XMEMCPY(output + offset, sni->data.host_name, length);
+
+                offset += length;
+            break;
+        }
+    }
+
+    c16toa(offset - OPAQUE16_LEN, output); /* writing list length */
+
+    return offset;
+}
+
+#ifndef NO_WOLFSSL_SERVER
+
+/** Finds a SNI object in the provided list. */
+static SNI* TLSX_SNI_Find(SNI *list, byte type)
+{
+    SNI *sni = list;
+
+    while (sni && sni->type != type)
+        sni = sni->next;
+
+    return sni;
+}
+
+
+/** Sets the status of a SNI object. */
+static void TLSX_SNI_SetStatus(TLSX* extensions, byte type, byte status)
+{
+    TLSX* extension = TLSX_Find(extensions, TLSX_SERVER_NAME);
+    SNI* sni = TLSX_SNI_Find(extension ? (SNI*)extension->data : NULL, type);
+
+    if (sni)
+        sni->status = status;
+}
+
+/** Gets the status of a SNI object. */
+byte TLSX_SNI_Status(TLSX* extensions, byte type)
+{
+    TLSX* extension = TLSX_Find(extensions, TLSX_SERVER_NAME);
+    SNI* sni = TLSX_SNI_Find(extension ? (SNI*)extension->data : NULL, type);
+
+    if (sni)
+        return sni->status;
+
+    return 0;
+}
+
+#endif /* NO_WOLFSSL_SERVER */
+
+/** Parses a buffer of SNI extensions. */
+static int TLSX_SNI_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                                                                 byte isRequest)
+{
+#ifndef NO_WOLFSSL_SERVER
+    word16 size = 0;
+    word16 offset = 0;
+    int cacheOnly = 0;
+#endif
+
+    TLSX *extension = TLSX_Find(ssl->extensions, TLSX_SERVER_NAME);
+
+    if (!extension)
+        extension = TLSX_Find(ssl->ctx->extensions, TLSX_SERVER_NAME);
+
+    (void)isRequest;
+    (void)input;
+
+    if (!extension || !extension->data) {
+#if defined(WOLFSSL_ALWAYS_KEEP_SNI) && !defined(NO_WOLFSSL_SERVER)
+        /* This will keep SNI even though TLSX_UseSNI has not been called.
+         * Enable it so that the received sni is available to functions
+         * that use a custom callback when SNI is received */
+        cacheOnly = 1;
+        WOLFSSL_MSG("Forcing SSL object to store SNI parameter");
+#else
+        return isRequest ? 0             /* not using SNI.           */
+                         : BUFFER_ERROR; /* unexpected SNI response. */
+#endif
+    }
+
+    if (!isRequest)
+        return length ? BUFFER_ERROR /* SNI response MUST be empty. */
+                      : 0;           /* nothing else to do.         */
+
+#ifndef NO_WOLFSSL_SERVER
+
+    if (OPAQUE16_LEN > length)
+        return BUFFER_ERROR;
+
+    ato16(input, &size);
+    offset += OPAQUE16_LEN;
+
+    /* validating sni list length */
+    if (length != OPAQUE16_LEN + size)
+        return BUFFER_ERROR;
+
+    for (size = 0; offset < length; offset += size) {
+        SNI *sni = NULL;
+        byte type = input[offset++];
+
+        if (offset + OPAQUE16_LEN > length)
+            return BUFFER_ERROR;
+
+        ato16(input + offset, &size);
+        offset += OPAQUE16_LEN;
+
+        if (offset + size > length)
+            return BUFFER_ERROR;
+
+        if (!cacheOnly && !(sni = TLSX_SNI_Find((SNI*)extension->data, type)))
+            continue; /* not using this type of SNI. */
+
+        switch(type) {
+            case WOLFSSL_SNI_HOST_NAME: {
+                int matchStat;
+                byte matched = cacheOnly ||
+                            ((XSTRLEN(sni->data.host_name) == size)
+                            && (XSTRNCMP(sni->data.host_name,
+                                       (const char*)input + offset, size) == 0));
+
+                if (matched || sni->options & WOLFSSL_SNI_ANSWER_ON_MISMATCH) {
+                    int r = TLSX_UseSNI(&ssl->extensions,
+                                         type, input + offset, size, ssl->heap);
+
+                    if (r != SSL_SUCCESS)
+                        return r; /* throws error. */
+
+                    if(cacheOnly) {
+                        WOLFSSL_MSG("Forcing storage of SNI, Fake match");
+                        matchStat = WOLFSSL_SNI_FORCE_KEEP;
+                    } else if(matched) {
+                        WOLFSSL_MSG("SNI did match!");
+                        matchStat = WOLFSSL_SNI_REAL_MATCH;
+                    } else {
+                        WOLFSSL_MSG("fake SNI match from ANSWER_ON_MISMATCH");
+                        matchStat = WOLFSSL_SNI_FAKE_MATCH;
+                    }
+
+                    TLSX_SNI_SetStatus(ssl->extensions, type, matchStat);
+
+                    if(!cacheOnly)
+                        TLSX_SetResponse(ssl, TLSX_SERVER_NAME);
+
+                } else if (!(sni->options & WOLFSSL_SNI_CONTINUE_ON_MISMATCH)) {
+                    SendAlert(ssl, alert_fatal, unrecognized_name);
+
+                    return UNKNOWN_SNI_HOST_NAME_E;
+                }
+                break;
+            }
+        }
+    }
+
+#endif
+
+    return 0;
+}
+
+static int TLSX_SNI_VerifyParse(WOLFSSL* ssl,  byte isRequest)
+{
+    (void)ssl;
+
+    if (isRequest) {
+    #ifndef NO_WOLFSSL_SERVER
+        TLSX* ctx_ext = TLSX_Find(ssl->ctx->extensions, TLSX_SERVER_NAME);
+        TLSX* ssl_ext = TLSX_Find(ssl->extensions,      TLSX_SERVER_NAME);
+        SNI* ctx_sni = ctx_ext ? (SNI*)ctx_ext->data : NULL;
+        SNI* ssl_sni = ssl_ext ? (SNI*)ssl_ext->data : NULL;
+        SNI* sni = NULL;
+
+        for (; ctx_sni; ctx_sni = ctx_sni->next) {
+            if (ctx_sni->options & WOLFSSL_SNI_ABORT_ON_ABSENCE) {
+                sni = TLSX_SNI_Find(ssl_sni, ctx_sni->type);
+
+                if (sni) {
+                    if (sni->status != WOLFSSL_SNI_NO_MATCH)
+                        continue;
+
+                    /* if ssl level overrides ctx level, it is ok. */
+                    if ((sni->options & WOLFSSL_SNI_ABORT_ON_ABSENCE) == 0)
+                        continue;
+                }
+
+                SendAlert(ssl, alert_fatal, handshake_failure);
+                return SNI_ABSENT_ERROR;
+            }
+        }
+
+        for (; ssl_sni; ssl_sni = ssl_sni->next) {
+            if (ssl_sni->options & WOLFSSL_SNI_ABORT_ON_ABSENCE) {
+                if (ssl_sni->status != WOLFSSL_SNI_NO_MATCH)
+                    continue;
+
+                SendAlert(ssl, alert_fatal, handshake_failure);
+                return SNI_ABSENT_ERROR;
+            }
+        }
+    #endif /* NO_WOLFSSL_SERVER */
+    }
+
+    return 0;
+}
+
+int TLSX_UseSNI(TLSX** extensions, byte type, const void* data, word16 size,
+                                                                     void* heap)
+{
+    TLSX* extension = TLSX_Find(*extensions, TLSX_SERVER_NAME);
+    SNI*  sni       = NULL;
+
+    if (extensions == NULL || data == NULL)
+        return BAD_FUNC_ARG;
+
+    if ((sni = TLSX_SNI_New(type, data, size, heap)) == NULL)
+        return MEMORY_E;
+
+    if (!extension) {
+        int ret = TLSX_Push(extensions, TLSX_SERVER_NAME, (void*)sni, heap);
+        if (ret != 0) {
+            TLSX_SNI_Free(sni, heap);
+            return ret;
+        }
+    }
+    else {
+        /* push new SNI object to extension data. */
+        sni->next = (SNI*)extension->data;
+        extension->data = (void*)sni;
+
+        /* remove duplicate SNI, there should be only one of each type. */
+        do {
+            if (sni->next && sni->next->type == type) {
+                SNI *next = sni->next;
+
+                sni->next = next->next;
+                TLSX_SNI_Free(next, heap);
+
+                /* there is no way to occur more than */
+                /* two SNIs of the same type.         */
+                break;
+            }
+        } while ((sni = sni->next));
+    }
+
+    return SSL_SUCCESS;
+}
+
+#ifndef NO_WOLFSSL_SERVER
+
+/** Tells the SNI requested by the client. */
+word16 TLSX_SNI_GetRequest(TLSX* extensions, byte type, void** data)
+{
+    TLSX* extension = TLSX_Find(extensions, TLSX_SERVER_NAME);
+    SNI* sni = TLSX_SNI_Find(extension ? (SNI*)extension->data : NULL, type);
+
+    if (sni && sni->status != WOLFSSL_SNI_NO_MATCH) {
+        switch (sni->type) {
+            case WOLFSSL_SNI_HOST_NAME:
+                *data = sni->data.host_name;
+                return XSTRLEN((char*)*data);
+        }
+    }
+
+    return 0;
+}
+
+/** Sets the options for a SNI object. */
+void TLSX_SNI_SetOptions(TLSX* extensions, byte type, byte options)
+{
+    TLSX* extension = TLSX_Find(extensions, TLSX_SERVER_NAME);
+    SNI* sni = TLSX_SNI_Find(extension ? (SNI*)extension->data : NULL, type);
+
+    if (sni)
+        sni->options = options;
+}
+
+/** Retrieves a SNI request from a client hello buffer. */
+int TLSX_SNI_GetFromBuffer(const byte* clientHello, word32 helloSz,
+                           byte type, byte* sni, word32* inOutSz)
+{
+    word32 offset = 0;
+    word32 len32  = 0;
+    word16 len16  = 0;
+
+    if (helloSz < RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + CLIENT_HELLO_FIRST)
+        return INCOMPLETE_DATA;
+
+    /* TLS record header */
+    if ((enum ContentType) clientHello[offset++] != handshake) {
+
+        /* checking for SSLv2.0 client hello according to: */
+        /* http://tools.ietf.org/html/rfc4346#appendix-E.1 */
+        if ((enum HandShakeType) clientHello[++offset] == client_hello) {
+            offset += ENUM_LEN + VERSION_SZ; /* skip version */
+
+            ato16(clientHello + offset, &len16);
+            offset += OPAQUE16_LEN;
+
+            if (len16 % 3) /* cipher_spec_length must be multiple of 3 */
+                return BUFFER_ERROR;
+
+            ato16(clientHello + offset, &len16);
+            /* Returning SNI_UNSUPPORTED do not increment offset here */
+
+            if (len16 != 0) /* session_id_length must be 0 */
+                return BUFFER_ERROR;
+
+            return SNI_UNSUPPORTED;
+        }
+
+        return BUFFER_ERROR;
+    }
+
+    if (clientHello[offset++] != SSLv3_MAJOR)
+        return BUFFER_ERROR;
+
+    if (clientHello[offset++] < TLSv1_MINOR)
+        return SNI_UNSUPPORTED;
+
+    ato16(clientHello + offset, &len16);
+    offset += OPAQUE16_LEN;
+
+    if (offset + len16 > helloSz)
+        return INCOMPLETE_DATA;
+
+    /* Handshake header */
+    if ((enum HandShakeType) clientHello[offset] != client_hello)
+        return BUFFER_ERROR;
+
+    c24to32(clientHello + offset + 1, &len32);
+    offset += HANDSHAKE_HEADER_SZ;
+
+    if (offset + len32 > helloSz)
+        return BUFFER_ERROR;
+
+    /* client hello */
+    offset += VERSION_SZ + RAN_LEN; /* version, random */
+
+    if (helloSz < offset + clientHello[offset])
+        return BUFFER_ERROR;
+
+    offset += ENUM_LEN + clientHello[offset]; /* skip session id */
+
+    /* cypher suites */
+    if (helloSz < offset + OPAQUE16_LEN)
+        return BUFFER_ERROR;
+
+    ato16(clientHello + offset, &len16);
+    offset += OPAQUE16_LEN;
+
+    if (helloSz < offset + len16)
+        return BUFFER_ERROR;
+
+    offset += len16; /* skip cypher suites */
+
+    /* compression methods */
+    if (helloSz < offset + 1)
+        return BUFFER_ERROR;
+
+    if (helloSz < offset + clientHello[offset])
+        return BUFFER_ERROR;
+
+    offset += ENUM_LEN + clientHello[offset]; /* skip compression methods */
+
+    /* extensions */
+    if (helloSz < offset + OPAQUE16_LEN)
+        return 0; /* no extensions in client hello. */
+
+    ato16(clientHello + offset, &len16);
+    offset += OPAQUE16_LEN;
+
+    if (helloSz < offset + len16)
+        return BUFFER_ERROR;
+
+    while (len16 >= OPAQUE16_LEN + OPAQUE16_LEN) {
+        word16 extType;
+        word16 extLen;
+
+        ato16(clientHello + offset, &extType);
+        offset += OPAQUE16_LEN;
+
+        ato16(clientHello + offset, &extLen);
+        offset += OPAQUE16_LEN;
+
+        if (helloSz < offset + extLen)
+            return BUFFER_ERROR;
+
+        if (extType != TLSX_SERVER_NAME) {
+            offset += extLen; /* skip extension */
+        } else {
+            word16 listLen;
+
+            ato16(clientHello + offset, &listLen);
+            offset += OPAQUE16_LEN;
+
+            if (helloSz < offset + listLen)
+                return BUFFER_ERROR;
+
+            while (listLen > ENUM_LEN + OPAQUE16_LEN) {
+                byte   sniType = clientHello[offset++];
+                word16 sniLen;
+
+                ato16(clientHello + offset, &sniLen);
+                offset += OPAQUE16_LEN;
+
+                if (helloSz < offset + sniLen)
+                    return BUFFER_ERROR;
+
+                if (sniType != type) {
+                    offset  += sniLen;
+                    listLen -= min(ENUM_LEN + OPAQUE16_LEN + sniLen, listLen);
+                    continue;
+                }
+
+                *inOutSz = min(sniLen, *inOutSz);
+                XMEMCPY(sni, clientHello + offset, *inOutSz);
+
+                return SSL_SUCCESS;
+            }
+        }
+
+        len16 -= min(2 * OPAQUE16_LEN + extLen, len16);
+    }
+
+    return len16 ? BUFFER_ERROR : 0;
+}
+
+#endif
+
+#define SNI_FREE_ALL     TLSX_SNI_FreeAll
+#define SNI_GET_SIZE     TLSX_SNI_GetSize
+#define SNI_WRITE        TLSX_SNI_Write
+#define SNI_PARSE        TLSX_SNI_Parse
+#define SNI_VERIFY_PARSE TLSX_SNI_VerifyParse
+
+#else
+
+#define SNI_FREE_ALL(list, heap)
+#define SNI_GET_SIZE(list)     0
+#define SNI_WRITE(a, b)        0
+#define SNI_PARSE(a, b, c, d)  0
+#define SNI_VERIFY_PARSE(a, b) 0
+
+#endif /* HAVE_SNI */
+
+/******************************************************************************/
+/* Max Fragment Length Negotiation                                            */
+/******************************************************************************/
+
+#ifdef HAVE_MAX_FRAGMENT
+
+static word16 TLSX_MFL_Write(byte* data, byte* output)
+{
+    output[0] = data[0];
+
+    return ENUM_LEN;
+}
+
+static int TLSX_MFL_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                                                                 byte isRequest)
+{
+    (void)isRequest;
+
+    if (length != ENUM_LEN)
+        return BUFFER_ERROR;
+
+    switch (*input) {
+        case WOLFSSL_MFL_2_9 : ssl->max_fragment =  512; break;
+        case WOLFSSL_MFL_2_10: ssl->max_fragment = 1024; break;
+        case WOLFSSL_MFL_2_11: ssl->max_fragment = 2048; break;
+        case WOLFSSL_MFL_2_12: ssl->max_fragment = 4096; break;
+        case WOLFSSL_MFL_2_13: ssl->max_fragment = 8192; break;
+
+        default:
+            SendAlert(ssl, alert_fatal, illegal_parameter);
+
+            return UNKNOWN_MAX_FRAG_LEN_E;
+    }
+
+#ifndef NO_WOLFSSL_SERVER
+    if (isRequest) {
+        int r = TLSX_UseMaxFragment(&ssl->extensions, *input, ssl->heap);
+
+        if (r != SSL_SUCCESS) return r; /* throw error */
+
+        TLSX_SetResponse(ssl, TLSX_MAX_FRAGMENT_LENGTH);
+    }
+#endif
+
+    return 0;
+}
+
+int TLSX_UseMaxFragment(TLSX** extensions, byte mfl, void* heap)
+{
+    byte* data = NULL;
+    int   ret  = 0;
+
+    if (extensions == NULL)
+        return BAD_FUNC_ARG;
+
+    if (mfl < WOLFSSL_MFL_2_9 || WOLFSSL_MFL_2_13 < mfl)
+        return BAD_FUNC_ARG;
+
+    if ((data = (byte*)XMALLOC(ENUM_LEN, heap, DYNAMIC_TYPE_TLSX)) == NULL)
+        return MEMORY_E;
+
+    data[0] = mfl;
+
+    /* push new MFL extension. */
+    if ((ret = TLSX_Push(extensions, TLSX_MAX_FRAGMENT_LENGTH, data, heap))
+                                                                         != 0) {
+        XFREE(data, heap, DYNAMIC_TYPE_TLSX);
+        return ret;
+    }
+
+    return SSL_SUCCESS;
+}
+
+
+#define MFL_FREE_ALL(data, heap) XFREE(data, (heap), DYNAMIC_TYPE_TLSX)
+#define MFL_GET_SIZE(data) ENUM_LEN
+#define MFL_WRITE          TLSX_MFL_Write
+#define MFL_PARSE          TLSX_MFL_Parse
+
+#else
+
+#define MFL_FREE_ALL(a, b)
+#define MFL_GET_SIZE(a)       0
+#define MFL_WRITE(a, b)       0
+#define MFL_PARSE(a, b, c, d) 0
+
+#endif /* HAVE_MAX_FRAGMENT */
+
+/******************************************************************************/
+/* Truncated HMAC                                                             */
+/******************************************************************************/
+
+#ifdef HAVE_TRUNCATED_HMAC
+
+static int TLSX_THM_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                                                                 byte isRequest)
+{
+    (void)isRequest;
+
+    if (length != 0 || input == NULL)
+        return BUFFER_ERROR;
+
+#ifndef NO_WOLFSSL_SERVER
+    if (isRequest) {
+        int r = TLSX_UseTruncatedHMAC(&ssl->extensions, ssl->heap);
+
+        if (r != SSL_SUCCESS)
+            return r; /* throw error */
+
+        TLSX_SetResponse(ssl, TLSX_TRUNCATED_HMAC);
+    }
+#endif
+
+    ssl->truncated_hmac = 1;
+
+    return 0;
+}
+
+int TLSX_UseTruncatedHMAC(TLSX** extensions, void* heap)
+{
+    int ret = 0;
+
+    if (extensions == NULL)
+        return BAD_FUNC_ARG;
+
+    if ((ret = TLSX_Push(extensions, TLSX_TRUNCATED_HMAC, NULL, heap)) != 0)
+        return ret;
+
+    return SSL_SUCCESS;
+}
+
+#define THM_PARSE TLSX_THM_Parse
+
+#else
+
+#define THM_PARSE(a, b, c, d) 0
+
+#endif /* HAVE_TRUNCATED_HMAC */
+
+/******************************************************************************/
+/* Certificate Status Request                                                 */
+/******************************************************************************/
+
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
+
+static void TLSX_CSR_Free(CertificateStatusRequest* csr, void* heap)
+{
+    switch (csr->status_type) {
+        case WOLFSSL_CSR_OCSP:
+            FreeOcspRequest(&csr->request.ocsp);
+        break;
+    }
+
+    XFREE(csr, heap, DYNAMIC_TYPE_TLSX);
+    (void)heap;
+}
+
+static word16 TLSX_CSR_GetSize(CertificateStatusRequest* csr, byte isRequest)
+{
+    word16 size = 0;
+
+    /* shut up compiler warnings */
+    (void) csr; (void) isRequest;
+
+#ifndef NO_WOLFSSL_CLIENT
+    if (isRequest) {
+        switch (csr->status_type) {
+            case WOLFSSL_CSR_OCSP:
+                size += ENUM_LEN + 2 * OPAQUE16_LEN;
+
+                if (csr->request.ocsp.nonceSz)
+                    size += OCSP_NONCE_EXT_SZ;
+            break;
+        }
+    }
+#endif
+
+    return size;
+}
+
+static word16 TLSX_CSR_Write(CertificateStatusRequest* csr, byte* output,
+                                                                 byte isRequest)
+{
+    /* shut up compiler warnings */
+    (void) csr; (void) output; (void) isRequest;
+
+#ifndef NO_WOLFSSL_CLIENT
+    if (isRequest) {
+        word16 offset = 0;
+        word16 length = 0;
+
+        /* type */
+        output[offset++] = csr->status_type;
+
+        switch (csr->status_type) {
+            case WOLFSSL_CSR_OCSP:
+                /* responder id list */
+                c16toa(0, output + offset);
+                offset += OPAQUE16_LEN;
+
+                /* request extensions */
+                if (csr->request.ocsp.nonceSz)
+                    length = (word16)EncodeOcspRequestExtensions(
+                                                 &csr->request.ocsp,
+                                                 output + offset + OPAQUE16_LEN,
+                                                 OCSP_NONCE_EXT_SZ);
+
+                c16toa(length, output + offset);
+                offset += OPAQUE16_LEN + length;
+
+            break;
+        }
+
+        return offset;
+    }
+#endif
+
+    return 0;
+}
+
+static int TLSX_CSR_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                                                                 byte isRequest)
+{
+    int ret;
+
+    /* shut up compiler warnings */
+    (void) ssl; (void) input;
+
+    if (!isRequest) {
+#ifndef NO_WOLFSSL_CLIENT
+        TLSX* extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST);
+        CertificateStatusRequest* csr = extension ?
+                              (CertificateStatusRequest*)extension->data : NULL;
+
+        if (!csr) {
+            /* look at context level */
+            extension = TLSX_Find(ssl->ctx->extensions, TLSX_STATUS_REQUEST);
+            csr = extension ? (CertificateStatusRequest*)extension->data : NULL;
+
+            if (!csr)
+                return BUFFER_ERROR; /* unexpected extension */
+
+            /* enable extension at ssl level */
+            ret = TLSX_UseCertificateStatusRequest(&ssl->extensions,
+                                     csr->status_type, csr->options, ssl->heap);
+            if (ret != SSL_SUCCESS)
+                return ret;
+
+            switch (csr->status_type) {
+                case WOLFSSL_CSR_OCSP:
+                    /* propagate nonce */
+                    if (csr->request.ocsp.nonceSz) {
+                        OcspRequest* request =
+                             (OcspRequest*)TLSX_CSR_GetRequest(ssl->extensions);
+
+                        if (request) {
+                            XMEMCPY(request->nonce, csr->request.ocsp.nonce,
+                                                    csr->request.ocsp.nonceSz);
+                            request->nonceSz = csr->request.ocsp.nonceSz;
+                        }
+                    }
+                break;
+            }
+        }
+
+        ssl->status_request = 1;
+
+        return length ? BUFFER_ERROR : 0; /* extension_data MUST be empty. */
+#endif
+    }
+    else {
+#ifndef NO_WOLFSSL_SERVER
+        byte   status_type;
+        word16 offset = 0;
+        word16 size = 0;
+
+        if (length < ENUM_LEN)
+            return BUFFER_ERROR;
+
+        status_type = input[offset++];
+
+        switch (status_type) {
+            case WOLFSSL_CSR_OCSP: {
+
+                /* skip responder_id_list */
+                if (length - offset < OPAQUE16_LEN)
+                    return BUFFER_ERROR;
+
+                ato16(input + offset, &size);
+                offset += OPAQUE16_LEN + size;
+
+                /* skip request_extensions */
+                if (length - offset < OPAQUE16_LEN)
+                    return BUFFER_ERROR;
+
+                ato16(input + offset, &size);
+                offset += OPAQUE16_LEN + size;
+
+                if (offset > length)
+                    return BUFFER_ERROR;
+
+                /* is able to send OCSP response? */
+                if (ssl->ctx->cm == NULL || !ssl->ctx->cm->ocspStaplingEnabled)
+                    return 0;
+            }
+            break;
+
+            /* unknown status type */
+            default:
+                return 0;
+        }
+
+        /* if using status_request and already sending it, skip this one */
+        #ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
+        if (ssl->status_request_v2)
+            return 0;
+        #endif
+
+        /* accept the first good status_type and return */
+        ret = TLSX_UseCertificateStatusRequest(&ssl->extensions, status_type,
+                                                                  0, ssl->heap);
+        if (ret != SSL_SUCCESS)
+            return ret; /* throw error */
+
+        TLSX_SetResponse(ssl, TLSX_STATUS_REQUEST);
+        ssl->status_request = status_type;
+
+#endif
+    }
+
+    return 0;
+}
+
+int TLSX_CSR_InitRequest(TLSX* extensions, DecodedCert* cert, void* heap)
+{
+    TLSX* extension = TLSX_Find(extensions, TLSX_STATUS_REQUEST);
+    CertificateStatusRequest* csr = extension ?
+        (CertificateStatusRequest*)extension->data : NULL;
+    int ret = 0;
+
+    if (csr) {
+        switch (csr->status_type) {
+            case WOLFSSL_CSR_OCSP: {
+                byte nonce[MAX_OCSP_NONCE_SZ];
+                int  nonceSz = csr->request.ocsp.nonceSz;
+
+                /* preserve nonce */
+                XMEMCPY(nonce, csr->request.ocsp.nonce, nonceSz);
+
+                if ((ret = InitOcspRequest(&csr->request.ocsp, cert, 0, heap))
+                                                                           != 0)
+                    return ret;
+
+                /* restore nonce */
+                XMEMCPY(csr->request.ocsp.nonce, nonce, nonceSz);
+                csr->request.ocsp.nonceSz = nonceSz;
+            }
+            break;
+        }
+    }
+
+    return ret;
+}
+
+void* TLSX_CSR_GetRequest(TLSX* extensions)
+{
+    TLSX* extension = TLSX_Find(extensions, TLSX_STATUS_REQUEST);
+    CertificateStatusRequest* csr = extension ?
+                              (CertificateStatusRequest*)extension->data : NULL;
+
+    if (csr) {
+        switch (csr->status_type) {
+            case WOLFSSL_CSR_OCSP:
+                return &csr->request.ocsp;
+            break;
+        }
+    }
+
+    return NULL;
+}
+
+int TLSX_CSR_ForceRequest(WOLFSSL* ssl)
+{
+    TLSX* extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST);
+    CertificateStatusRequest* csr = extension ?
+                              (CertificateStatusRequest*)extension->data : NULL;
+
+    if (csr) {
+        switch (csr->status_type) {
+            case WOLFSSL_CSR_OCSP:
+                if (ssl->ctx->cm->ocspEnabled)
+                    return CheckOcspRequest(ssl->ctx->cm->ocsp,
+                                                      &csr->request.ocsp, NULL);
+                else
+                    return OCSP_LOOKUP_FAIL;
+        }
+    }
+
+    return 0;
+}
+
+int TLSX_UseCertificateStatusRequest(TLSX** extensions, byte status_type,
+                                                       byte options, void* heap)
+{
+    CertificateStatusRequest* csr = NULL;
+    int ret = 0;
+
+    if (!extensions || status_type != WOLFSSL_CSR_OCSP)
+        return BAD_FUNC_ARG;
+
+    csr = (CertificateStatusRequest*)
+             XMALLOC(sizeof(CertificateStatusRequest), heap, DYNAMIC_TYPE_TLSX);
+    if (!csr)
+        return MEMORY_E;
+
+    ForceZero(csr, sizeof(CertificateStatusRequest));
+
+    csr->status_type = status_type;
+    csr->options     = options;
+
+    switch (csr->status_type) {
+        case WOLFSSL_CSR_OCSP:
+            if (options & WOLFSSL_CSR_OCSP_USE_NONCE) {
+                WC_RNG rng;
+
+#ifdef WOLFSSL_STATIC_MEMORY
+                if (wc_InitRng_ex(&rng, heap) == 0) {
+#else
+                if (wc_InitRng(&rng) == 0) {
+#endif
+                    if (wc_RNG_GenerateBlock(&rng, csr->request.ocsp.nonce,
+                                                        MAX_OCSP_NONCE_SZ) == 0)
+                        csr->request.ocsp.nonceSz = MAX_OCSP_NONCE_SZ;
+
+                    wc_FreeRng(&rng);
+                }
+            }
+        break;
+    }
+
+    if ((ret = TLSX_Push(extensions, TLSX_STATUS_REQUEST, csr, heap)) != 0) {
+        XFREE(csr, heap, DYNAMIC_TYPE_TLSX);
+        return ret;
+    }
+
+    return SSL_SUCCESS;
+}
+
+#define CSR_FREE_ALL TLSX_CSR_Free
+#define CSR_GET_SIZE TLSX_CSR_GetSize
+#define CSR_WRITE    TLSX_CSR_Write
+#define CSR_PARSE    TLSX_CSR_Parse
+
+#else
+
+#define CSR_FREE_ALL(data, heap)
+#define CSR_GET_SIZE(a, b)    0
+#define CSR_WRITE(a, b, c)    0
+#define CSR_PARSE(a, b, c, d) 0
+
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST */
+
+/******************************************************************************/
+/* Certificate Status Request v2                                              */
+/******************************************************************************/
+
+#ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2
+
+static void TLSX_CSR2_FreeAll(CertificateStatusRequestItemV2* csr2, void* heap)
+{
+    CertificateStatusRequestItemV2* next;
+
+    for (; csr2; csr2 = next) {
+        next = csr2->next;
+
+        switch (csr2->status_type) {
+            case WOLFSSL_CSR2_OCSP:
+            case WOLFSSL_CSR2_OCSP_MULTI:
+                while(csr2->requests--)
+                    FreeOcspRequest(&csr2->request.ocsp[csr2->requests]);
+            break;
+        }
+
+        XFREE(csr2, heap, DYNAMIC_TYPE_TLSX);
+    }
+    (void)heap;
+}
+
+static word16 TLSX_CSR2_GetSize(CertificateStatusRequestItemV2* csr2,
+                                                                 byte isRequest)
+{
+    word16 size = 0;
+
+    /* shut up compiler warnings */
+    (void) csr2; (void) isRequest;
+
+#ifndef NO_WOLFSSL_CLIENT
+    if (isRequest) {
+        CertificateStatusRequestItemV2* next;
+
+        for (size = OPAQUE16_LEN; csr2; csr2 = next) {
+            next = csr2->next;
+
+            switch (csr2->status_type) {
+                case WOLFSSL_CSR2_OCSP:
+                case WOLFSSL_CSR2_OCSP_MULTI:
+                    size += ENUM_LEN + 3 * OPAQUE16_LEN;
+
+                    if (csr2->request.ocsp[0].nonceSz)
+                        size += OCSP_NONCE_EXT_SZ;
+                break;
+            }
+        }
+    }
+#endif
+
+    return size;
+}
+
+static word16 TLSX_CSR2_Write(CertificateStatusRequestItemV2* csr2,
+                                                   byte* output, byte isRequest)
+{
+    /* shut up compiler warnings */
+    (void) csr2; (void) output; (void) isRequest;
+
+#ifndef NO_WOLFSSL_CLIENT
+    if (isRequest) {
+        word16 offset;
+        word16 length;
+
+        for (offset = OPAQUE16_LEN; csr2 != NULL; csr2 = csr2->next) {
+            /* status_type */
+            output[offset++] = csr2->status_type;
+
+            /* request */
+            switch (csr2->status_type) {
+                case WOLFSSL_CSR2_OCSP:
+                case WOLFSSL_CSR2_OCSP_MULTI:
+                    /* request_length */
+                    length = 2 * OPAQUE16_LEN;
+
+                    if (csr2->request.ocsp[0].nonceSz)
+                        length += OCSP_NONCE_EXT_SZ;
+
+                    c16toa(length, output + offset);
+                    offset += OPAQUE16_LEN;
+
+                    /* responder id list */
+                    c16toa(0, output + offset);
+                    offset += OPAQUE16_LEN;
+
+                    /* request extensions */
+                    length = 0;
+
+                    if (csr2->request.ocsp[0].nonceSz)
+                        length = (word16)EncodeOcspRequestExtensions(
+                                                 &csr2->request.ocsp[0],
+                                                 output + offset + OPAQUE16_LEN,
+                                                 OCSP_NONCE_EXT_SZ);
+
+                    c16toa(length, output + offset);
+                    offset += OPAQUE16_LEN + length;
+                break;
+            }
+        }
+
+        /* list size */
+        c16toa(offset - OPAQUE16_LEN, output);
+
+        return offset;
+    }
+#endif
+
+    return 0;
+}
+
+static int TLSX_CSR2_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                                                                 byte isRequest)
+{
+    int ret;
+
+    /* shut up compiler warnings */
+    (void) ssl; (void) input;
+
+    if (!isRequest) {
+#ifndef NO_WOLFSSL_CLIENT
+        TLSX* extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST_V2);
+        CertificateStatusRequestItemV2* csr2 = extension ?
+                        (CertificateStatusRequestItemV2*)extension->data : NULL;
+
+        if (!csr2) {
+            /* look at context level */
+            extension = TLSX_Find(ssl->ctx->extensions, TLSX_STATUS_REQUEST_V2);
+            csr2 = extension ?
+                        (CertificateStatusRequestItemV2*)extension->data : NULL;
+
+            if (!csr2)
+                return BUFFER_ERROR; /* unexpected extension */
+
+            /* enable extension at ssl level */
+            for (; csr2; csr2 = csr2->next) {
+                ret = TLSX_UseCertificateStatusRequestV2(&ssl->extensions,
+                                   csr2->status_type, csr2->options, ssl->heap);
+                if (ret != SSL_SUCCESS)
+                    return ret;
+
+                switch (csr2->status_type) {
+                    case WOLFSSL_CSR2_OCSP:
+                        /* followed by */
+                    case WOLFSSL_CSR2_OCSP_MULTI:
+                        /* propagate nonce */
+                        if (csr2->request.ocsp[0].nonceSz) {
+                            OcspRequest* request =
+                             (OcspRequest*)TLSX_CSR2_GetRequest(ssl->extensions,
+                                                          csr2->status_type, 0);
+
+                            if (request) {
+                                XMEMCPY(request->nonce,
+                                        csr2->request.ocsp[0].nonce,
+                                        csr2->request.ocsp[0].nonceSz);
+
+                                request->nonceSz =
+                                                  csr2->request.ocsp[0].nonceSz;
+                            }
+                        }
+                    break;
+                }
+            }
+        }
+
+        ssl->status_request_v2 = 1;
+
+        return length ? BUFFER_ERROR : 0; /* extension_data MUST be empty. */
+#endif
+    }
+    else {
+#ifndef NO_WOLFSSL_SERVER
+        byte   status_type;
+        word16 request_length;
+        word16 offset = 0;
+        word16 size = 0;
+
+        /* list size */
+        ato16(input + offset, &request_length);
+        offset += OPAQUE16_LEN;
+
+        if (length - OPAQUE16_LEN != request_length)
+            return BUFFER_ERROR;
+
+        while (length > offset) {
+            if (length - offset < ENUM_LEN + OPAQUE16_LEN)
+                return BUFFER_ERROR;
+
+            status_type = input[offset++];
+
+            ato16(input + offset, &request_length);
+            offset += OPAQUE16_LEN;
+
+            if (length - offset < request_length)
+                return BUFFER_ERROR;
+
+            switch (status_type) {
+                case WOLFSSL_CSR2_OCSP:
+                case WOLFSSL_CSR2_OCSP_MULTI:
+                    /* skip responder_id_list */
+                    if (length - offset < OPAQUE16_LEN)
+                        return BUFFER_ERROR;
+
+                    ato16(input + offset, &size);
+                    offset += OPAQUE16_LEN + size;
+
+                    /* skip request_extensions */
+                    if (length - offset < OPAQUE16_LEN)
+                        return BUFFER_ERROR;
+
+                    ato16(input + offset, &size);
+                    offset += OPAQUE16_LEN + size;
+
+                    if (offset > length)
+                        return BUFFER_ERROR;
+
+                    /* is able to send OCSP response? */
+                    if (ssl->ctx->cm == NULL
+                    || !ssl->ctx->cm->ocspStaplingEnabled)
+                        continue;
+                break;
+
+                default:
+                    /* unknown status type, skipping! */
+                    offset += request_length;
+                    continue;
+            }
+
+            /* if using status_request and already sending it, skip this one */
+            #ifdef HAVE_CERTIFICATE_STATUS_REQUEST
+            if (ssl->status_request)
+                return 0;
+            #endif
+
+            /* accept the first good status_type and return */
+            ret = TLSX_UseCertificateStatusRequestV2(&ssl->extensions,
+                                                     status_type, 0, ssl->heap);
+            if (ret != SSL_SUCCESS)
+                return ret; /* throw error */
+
+            TLSX_SetResponse(ssl, TLSX_STATUS_REQUEST_V2);
+            ssl->status_request_v2 = status_type;
+
+            return 0;
+        }
+#endif
+    }
+
+    return 0;
+}
+
+int TLSX_CSR2_InitRequests(TLSX* extensions, DecodedCert* cert, byte isPeer,
+                                                                     void* heap)
+{
+    TLSX* extension = TLSX_Find(extensions, TLSX_STATUS_REQUEST_V2);
+    CertificateStatusRequestItemV2* csr2 = extension ?
+        (CertificateStatusRequestItemV2*)extension->data : NULL;
+    int ret = 0;
+
+    for (; csr2; csr2 = csr2->next) {
+        switch (csr2->status_type) {
+            case WOLFSSL_CSR2_OCSP:
+                if (!isPeer || csr2->requests != 0)
+                    break;
+
+                /* followed by */
+
+            case WOLFSSL_CSR2_OCSP_MULTI: {
+                if (csr2->requests < 1 + MAX_CHAIN_DEPTH) {
+                    byte nonce[MAX_OCSP_NONCE_SZ];
+                    int  nonceSz = csr2->request.ocsp[0].nonceSz;
+
+                    /* preserve nonce, replicating nonce of ocsp[0] */
+                    XMEMCPY(nonce, csr2->request.ocsp[0].nonce, nonceSz);
+
+                    if ((ret = InitOcspRequest(
+                                      &csr2->request.ocsp[csr2->requests], cert,
+                                                                 0, heap)) != 0)
+                        return ret;
+
+                    /* restore nonce */
+                    XMEMCPY(csr2->request.ocsp[csr2->requests].nonce,
+                                                                nonce, nonceSz);
+                    csr2->request.ocsp[csr2->requests].nonceSz = nonceSz;
+                    csr2->requests++;
+                }
+            }
+            break;
+        }
+    }
+
+    (void)cert;
+    return ret;
+}
+
+void* TLSX_CSR2_GetRequest(TLSX* extensions, byte status_type, byte index)
+{
+    TLSX* extension = TLSX_Find(extensions, TLSX_STATUS_REQUEST_V2);
+    CertificateStatusRequestItemV2* csr2 = extension ?
+                        (CertificateStatusRequestItemV2*)extension->data : NULL;
+
+    for (; csr2; csr2 = csr2->next) {
+        if (csr2->status_type == status_type) {
+            switch (csr2->status_type) {
+                case WOLFSSL_CSR2_OCSP:
+                    /* followed by */
+
+                case WOLFSSL_CSR2_OCSP_MULTI:
+                    /* requests are initialized in the reverse order */
+                    return index < csr2->requests
+                         ? &csr2->request.ocsp[csr2->requests - index - 1]
+                         : NULL;
+                break;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+int TLSX_CSR2_ForceRequest(WOLFSSL* ssl)
+{
+    TLSX* extension = TLSX_Find(ssl->extensions, TLSX_STATUS_REQUEST_V2);
+    CertificateStatusRequestItemV2* csr2 = extension ?
+                        (CertificateStatusRequestItemV2*)extension->data : NULL;
+
+    /* forces only the first one */
+    if (csr2) {
+        switch (csr2->status_type) {
+            case WOLFSSL_CSR2_OCSP:
+                /* followed by */
+
+            case WOLFSSL_CSR2_OCSP_MULTI:
+                if (ssl->ctx->cm->ocspEnabled)
+                    return CheckOcspRequest(ssl->ctx->cm->ocsp,
+                                                  &csr2->request.ocsp[0], NULL);
+                else
+                    return OCSP_LOOKUP_FAIL;
+        }
+    }
+
+    return 0;
+}
+
+int TLSX_UseCertificateStatusRequestV2(TLSX** extensions, byte status_type,
+                                                       byte options, void* heap)
+{
+    TLSX* extension = NULL;
+    CertificateStatusRequestItemV2* csr2 = NULL;
+    int ret = 0;
+
+    if (!extensions)
+        return BAD_FUNC_ARG;
+
+    if (status_type != WOLFSSL_CSR2_OCSP
+    &&  status_type != WOLFSSL_CSR2_OCSP_MULTI)
+        return BAD_FUNC_ARG;
+
+    csr2 = (CertificateStatusRequestItemV2*)
+       XMALLOC(sizeof(CertificateStatusRequestItemV2), heap, DYNAMIC_TYPE_TLSX);
+    if (!csr2)
+        return MEMORY_E;
+
+    ForceZero(csr2, sizeof(CertificateStatusRequestItemV2));
+
+    csr2->status_type = status_type;
+    csr2->options     = options;
+    csr2->next        = NULL;
+
+    switch (csr2->status_type) {
+        case WOLFSSL_CSR2_OCSP:
+        case WOLFSSL_CSR2_OCSP_MULTI:
+            if (options & WOLFSSL_CSR2_OCSP_USE_NONCE) {
+                WC_RNG rng;
+
+#ifdef WOLFSSL_STATIC_MEMORY
+                if (wc_InitRng_ex(&rng, heap) == 0) {
+#else
+                if (wc_InitRng(&rng) == 0) {
+#endif
+                    if (wc_RNG_GenerateBlock(&rng, csr2->request.ocsp[0].nonce,
+                                                        MAX_OCSP_NONCE_SZ) == 0)
+                        csr2->request.ocsp[0].nonceSz = MAX_OCSP_NONCE_SZ;
+
+                    wc_FreeRng(&rng);
+                }
+            }
+        break;
+    }
+
+    /* append new item */
+    if ((extension = TLSX_Find(*extensions, TLSX_STATUS_REQUEST_V2))) {
+        CertificateStatusRequestItemV2* last =
+                               (CertificateStatusRequestItemV2*)extension->data;
+
+        for (; last->next; last = last->next);
+
+        last->next = csr2;
+    }
+    else if ((ret = TLSX_Push(extensions, TLSX_STATUS_REQUEST_V2, csr2,heap))) {
+        XFREE(csr2, heap, DYNAMIC_TYPE_TLSX);
+        return ret;
+    }
+
+    return SSL_SUCCESS;
+}
+
+#define CSR2_FREE_ALL TLSX_CSR2_FreeAll
+#define CSR2_GET_SIZE TLSX_CSR2_GetSize
+#define CSR2_WRITE    TLSX_CSR2_Write
+#define CSR2_PARSE    TLSX_CSR2_Parse
+
+#else
+
+#define CSR2_FREE_ALL(data, heap)
+#define CSR2_GET_SIZE(a, b)    0
+#define CSR2_WRITE(a, b, c)    0
+#define CSR2_PARSE(a, b, c, d) 0
+
+#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
+
+/******************************************************************************/
+/* Supported Elliptic Curves                                                  */
+/******************************************************************************/
+
+#ifdef HAVE_SUPPORTED_CURVES
+
+#ifndef HAVE_ECC
+#error Elliptic Curves Extension requires Elliptic Curve Cryptography. \
+       Use --enable-ecc in the configure script or define HAVE_ECC.
+#endif
+
+static void TLSX_EllipticCurve_FreeAll(EllipticCurve* list, void* heap)
+{
+    EllipticCurve* curve;
+
+    while ((curve = list)) {
+        list = curve->next;
+        XFREE(curve, heap, DYNAMIC_TYPE_TLSX);
+    }
+    (void)heap;
+}
+
+static int TLSX_EllipticCurve_Append(EllipticCurve** list, word16 name,
+                                                                     void* heap)
+{
+    EllipticCurve* curve = NULL;
+
+    if (list == NULL)
+        return BAD_FUNC_ARG;
+
+    curve = (EllipticCurve*)XMALLOC(sizeof(EllipticCurve), heap,
+                                                             DYNAMIC_TYPE_TLSX);
+    if (curve == NULL)
+        return MEMORY_E;
+
+    curve->name = name;
+    curve->next = *list;
+
+    *list = curve;
+
+    return 0;
+}
+
+#ifndef NO_WOLFSSL_CLIENT
+
+static void TLSX_EllipticCurve_ValidateRequest(WOLFSSL* ssl, byte* semaphore)
+{
+    int i;
+
+    for (i = 0; i < ssl->suites->suiteSz; i+= 2)
+        if (ssl->suites->suites[i] == ECC_BYTE ||
+                                          ssl->suites->suites[i] == CHACHA_BYTE)
+            return;
+
+    /* turns semaphore on to avoid sending this extension. */
+    TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_SUPPORTED_GROUPS));
+}
+
+static word16 TLSX_EllipticCurve_GetSize(EllipticCurve* list)
+{
+    EllipticCurve* curve;
+    word16 length = OPAQUE16_LEN; /* list length */
+
+    while ((curve = list)) {
+        list = curve->next;
+        length += OPAQUE16_LEN; /* curve length */
+    }
+
+    return length;
+}
+
+static word16 TLSX_EllipticCurve_WriteR(EllipticCurve* curve, byte* output);
+static word16 TLSX_EllipticCurve_WriteR(EllipticCurve* curve, byte* output)
+{
+    word16 offset = 0;
+
+    if (!curve)
+        return offset;
+
+    offset = TLSX_EllipticCurve_WriteR(curve->next, output);
+    c16toa(curve->name, output + offset);
+
+    return OPAQUE16_LEN + offset;
+}
+
+static word16 TLSX_EllipticCurve_Write(EllipticCurve* list, byte* output)
+{
+    word16 length = TLSX_EllipticCurve_WriteR(list, output + OPAQUE16_LEN);
+
+    c16toa(length, output); /* writing list length */
+
+    return OPAQUE16_LEN + length;
+}
+
+#endif /* NO_WOLFSSL_CLIENT */
+#ifndef NO_WOLFSSL_SERVER
+
+static int TLSX_EllipticCurve_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                                                                 byte isRequest)
+{
+    word16 offset;
+    word16 name;
+    int r;
+
+    (void) isRequest; /* shut up compiler! */
+
+    if (OPAQUE16_LEN > length || length % OPAQUE16_LEN)
+        return BUFFER_ERROR;
+
+    ato16(input, &offset);
+
+    /* validating curve list length */
+    if (length != OPAQUE16_LEN + offset)
+        return BUFFER_ERROR;
+
+    while (offset) {
+        ato16(input + offset, &name);
+        offset -= OPAQUE16_LEN;
+
+        r = TLSX_UseSupportedCurve(&ssl->extensions, name, ssl->heap);
+
+        if (r != SSL_SUCCESS) return r; /* throw error */
+    }
+
+    return 0;
+}
+
+int TLSX_ValidateEllipticCurves(WOLFSSL* ssl, byte first, byte second) {
+    TLSX*          extension = (first == ECC_BYTE || first == CHACHA_BYTE)
+                             ? TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS)
+                             : NULL;
+    EllipticCurve* curve     = NULL;
+    word32         oid       = 0;
+    word16         octets    = 0; /* according to 'ecc_set_type ecc_sets[];' */
+    int            sig       = 0; /* validate signature */
+    int            key       = 0; /* validate key       */
+
+    (void)oid;
+    (void)octets;
+
+    if (!extension)
+        return 1; /* no suite restriction */
+
+    for (curve = (EllipticCurve*)extension->data;
+         curve && !(sig && key);
+         curve = curve->next) {
+
+        /* find supported curve */
+        switch (curve->name) {
+    #if defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)
+        #ifndef NO_ECC_SECP
+            case WOLFSSL_ECC_SECP160R1: oid = ECC_SECP160R1_OID; octets = 20; break;
+        #endif /* !NO_ECC_SECP */
+        #ifdef HAVE_ECC_SECPR2
+            case WOLFSSL_ECC_SECP160R2: oid = ECC_SECP160R2_OID; octets = 20; break;
+        #endif /* HAVE_ECC_SECPR2 */
+        #ifdef HAVE_ECC_KOBLITZ
+            case WOLFSSL_ECC_SECP160K1: oid = ECC_SECP160K1_OID; octets = 20; break;
+        #endif /* HAVE_ECC_KOBLITZ */
+    #endif
+    #if defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)
+        #ifndef NO_ECC_SECP
+            case WOLFSSL_ECC_SECP192R1: oid = ECC_SECP192R1_OID; octets = 24; break;
+        #endif /* !NO_ECC_SECP */
+        #ifdef HAVE_ECC_KOBLITZ
+            case WOLFSSL_ECC_SECP192K1: oid = ECC_SECP192K1_OID; octets = 24; break;
+        #endif /* HAVE_ECC_KOBLITZ */
+    #endif
+    #if defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)
+        #ifndef NO_ECC_SECP
+            case WOLFSSL_ECC_SECP224R1: oid = ECC_SECP224R1_OID; octets = 28; break;
+        #endif /* !NO_ECC_SECP */
+        #ifdef HAVE_ECC_KOBLITZ
+            case WOLFSSL_ECC_SECP224K1: oid = ECC_SECP224K1_OID; octets = 28; break;
+        #endif /* HAVE_ECC_KOBLITZ */
+    #endif
+    #if !defined(NO_ECC256)  || defined(HAVE_ALL_CURVES)
+        #ifndef NO_ECC_SECP
+            case WOLFSSL_ECC_SECP256R1: oid = ECC_SECP256R1_OID; octets = 32; break;
+        #endif /* !NO_ECC_SECP */
+        #ifdef HAVE_ECC_KOBLITZ
+            case WOLFSSL_ECC_SECP256K1: oid = ECC_SECP256K1_OID; octets = 32; break;
+        #endif /* HAVE_ECC_KOBLITZ */
+        #ifdef HAVE_ECC_BRAINPOOL
+            case WOLFSSL_ECC_BRAINPOOLP256R1: oid = ECC_BRAINPOOLP256R1_OID; octets = 32; break;
+        #endif /* HAVE_ECC_BRAINPOOL */
+    #endif
+    #if defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)
+        #ifndef NO_ECC_SECP
+            case WOLFSSL_ECC_SECP384R1: oid = ECC_SECP384R1_OID; octets = 48; break;
+        #endif /* !NO_ECC_SECP */
+        #ifdef HAVE_ECC_BRAINPOOL
+            case WOLFSSL_ECC_BRAINPOOLP384R1: oid = ECC_BRAINPOOLP384R1_OID; octets = 48; break;
+        #endif /* HAVE_ECC_BRAINPOOL */
+    #endif
+    #if defined(HAVE_ECC512) || defined(HAVE_ALL_CURVES)
+        #ifdef HAVE_ECC_BRAINPOOL
+            case WOLFSSL_ECC_BRAINPOOLP512R1: oid = ECC_BRAINPOOLP512R1_OID; octets = 64; break;
+        #endif /* HAVE_ECC_BRAINPOOL */
+    #endif
+    #if defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)
+        #ifndef NO_ECC_SECP
+            case WOLFSSL_ECC_SECP521R1: oid = ECC_SECP521R1_OID; octets = 66; break;
+        #endif /* !NO_ECC_SECP */
+    #endif
+            default: continue; /* unsupported curve */
+        }
+
+        if (first == ECC_BYTE) {
+        switch (second) {
+            /* ECDHE_ECDSA */
+            case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+            case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+            case TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+            case TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+            case TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+            case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+            case TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+            case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+            case TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
+                sig |= ssl->pkCurveOID == oid;
+                key |= ssl->eccTempKeySz == octets;
+            break;
+
+#ifdef WOLFSSL_STATIC_DH
+            /* ECDH_ECDSA */
+            case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+            case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+            case TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+            case TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+            case TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+            case TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+            case TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+                sig |= ssl->pkCurveOID == oid;
+                key |= ssl->pkCurveOID == oid;
+            break;
+#endif /* WOLFSSL_STATIC_DH */
+#ifndef NO_RSA
+            /* ECDHE_RSA */
+            case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+            case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+            case TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+            case TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+            case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+            case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+            case TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+                sig = 1;
+                key |= ssl->eccTempKeySz == octets;
+            break;
+
+#ifdef WOLFSSL_STATIC_DH
+            /* ECDH_RSA */
+            case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+            case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+            case TLS_ECDH_RSA_WITH_RC4_128_SHA:
+            case TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+            case TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+            case TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+            case TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+                sig = 1;
+                key |= ssl->pkCurveOID == oid;
+            break;
+#endif /* WOLFSSL_STATIC_DH */
+#endif
+            default:
+                sig = 1;
+                key = 1;
+            break;
+        }
+        }
+
+        /* ChaCha20-Poly1305 ECC cipher suites */
+        if (first == CHACHA_BYTE) {
+        switch (second) {
+            /* ECDHE_ECDSA */
+            case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 :
+            case TLS_ECDHE_ECDSA_WITH_CHACHA20_OLD_POLY1305_SHA256 :
+                sig |= ssl->pkCurveOID == oid;
+                key |= ssl->eccTempKeySz == octets;
+            break;
+#ifndef NO_RSA
+            /* ECDHE_RSA */
+            case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 :
+            case TLS_ECDHE_RSA_WITH_CHACHA20_OLD_POLY1305_SHA256 :
+                sig = 1;
+                key |= ssl->eccTempKeySz == octets;
+            break;
+#endif
+            default:
+                sig = 1;
+                key = 1;
+            break;
+        }
+        }
+    }
+
+    return sig && key;
+}
+
+#endif /* NO_WOLFSSL_SERVER */
+
+int TLSX_UseSupportedCurve(TLSX** extensions, word16 name, void* heap)
+{
+    TLSX*          extension = TLSX_Find(*extensions, TLSX_SUPPORTED_GROUPS);
+    EllipticCurve* curve     = NULL;
+    int            ret       = 0;
+
+    if (extensions == NULL)
+        return BAD_FUNC_ARG;
+
+    if ((ret = TLSX_EllipticCurve_Append(&curve, name, heap)) != 0)
+        return ret;
+
+    if (!extension) {
+        if ((ret = TLSX_Push(extensions, TLSX_SUPPORTED_GROUPS, curve, heap))
+                                                                         != 0) {
+            XFREE(curve, heap, DYNAMIC_TYPE_TLSX);
+            return ret;
+        }
+    }
+    else {
+        /* push new EllipticCurve object to extension data. */
+        curve->next = (EllipticCurve*)extension->data;
+        extension->data = (void*)curve;
+
+        /* look for another curve of the same name to remove (replacement) */
+        do {
+            if (curve->next && curve->next->name == name) {
+                EllipticCurve *next = curve->next;
+
+                curve->next = next->next;
+                XFREE(next, heap, DYNAMIC_TYPE_TLSX);
+
+                break;
+            }
+        } while ((curve = curve->next));
+    }
+
+    return SSL_SUCCESS;
+}
+
+#define EC_FREE_ALL         TLSX_EllipticCurve_FreeAll
+#define EC_VALIDATE_REQUEST TLSX_EllipticCurve_ValidateRequest
+
+#ifndef NO_WOLFSSL_CLIENT
+#define EC_GET_SIZE TLSX_EllipticCurve_GetSize
+#define EC_WRITE    TLSX_EllipticCurve_Write
+#else
+#define EC_GET_SIZE(list)         0
+#define EC_WRITE(a, b)            0
+#endif
+
+#ifndef NO_WOLFSSL_SERVER
+#define EC_PARSE TLSX_EllipticCurve_Parse
+#else
+#define EC_PARSE(a, b, c, d)      0
+#endif
+
+#else
+
+#define EC_FREE_ALL(list, heap)
+#define EC_GET_SIZE(list)         0
+#define EC_WRITE(a, b)            0
+#define EC_PARSE(a, b, c, d)      0
+#define EC_VALIDATE_REQUEST(a, b)
+
+#endif /* HAVE_SUPPORTED_CURVES */
+
+/******************************************************************************/
+/* Renegotiation Indication                                                   */
+/******************************************************************************/
+
+#if defined(HAVE_SECURE_RENEGOTIATION) \
+ || defined(HAVE_SERVER_RENEGOTIATION_INFO)
+
+static byte TLSX_SecureRenegotiation_GetSize(SecureRenegotiation* data,
+                                                                  int isRequest)
+{
+    byte length = OPAQUE8_LEN; /* empty info length */
+
+    /* data will be NULL for HAVE_SERVER_RENEGOTIATION_INFO only */
+    if (data && data->enabled) {
+        /* client sends client_verify_data only */
+        length += TLS_FINISHED_SZ;
+
+        /* server also sends server_verify_data */
+        if (!isRequest)
+            length += TLS_FINISHED_SZ;
+    }
+
+    return length;
+}
+
+static word16 TLSX_SecureRenegotiation_Write(SecureRenegotiation* data,
+                                                    byte* output, int isRequest)
+{
+    word16 offset = OPAQUE8_LEN; /* RenegotiationInfo length */
+
+    if (data && data->enabled) {
+        /* client sends client_verify_data only */
+        XMEMCPY(output + offset, data->client_verify_data, TLS_FINISHED_SZ);
+        offset += TLS_FINISHED_SZ;
+
+        /* server also sends server_verify_data */
+        if (!isRequest) {
+            XMEMCPY(output + offset, data->server_verify_data, TLS_FINISHED_SZ);
+            offset += TLS_FINISHED_SZ;
+        }
+    }
+
+    output[0] = (byte)(offset - 1);  /* info length - self */
+
+    return offset;
+}
+
+static int TLSX_SecureRenegotiation_Parse(WOLFSSL* ssl, byte* input,
+                                                  word16 length, byte isRequest)
+{
+    int ret = SECURE_RENEGOTIATION_E;
+
+    if (length >= OPAQUE8_LEN) {
+        if (ssl->secure_renegotiation == NULL) {
+        #ifndef NO_WOLFSSL_SERVER
+            if (isRequest && *input == 0) {
+            #ifdef HAVE_SERVER_RENEGOTIATION_INFO
+                if (length == OPAQUE8_LEN) {
+                    if (TLSX_Find(ssl->extensions,
+                                  TLSX_RENEGOTIATION_INFO) == NULL) {
+                        ret = TLSX_AddEmptyRenegotiationInfo(&ssl->extensions,
+                                                             ssl->heap);
+                        if (ret == SSL_SUCCESS)
+                            ret = 0;
+
+                    } else {
+                        ret = 0;
+                    }
+                }
+            #else
+                ret = 0;  /* don't reply, user didn't enable */
+            #endif /* HAVE_SERVER_RENEGOTIATION_INFO */
+            }
+            #ifdef HAVE_SERVER_RENEGOTIATION_INFO
+            else if (!isRequest) {
+                /* don't do anything on client side */
+                ret = 0;
+            }
+            #endif
+        #endif
+        }
+        else if (isRequest) {
+        #ifndef NO_WOLFSSL_SERVER
+            if (*input == TLS_FINISHED_SZ) {
+                /* TODO compare client_verify_data */
+                ret = 0;
+            }
+        #endif
+        }
+        else {
+        #ifndef NO_WOLFSSL_CLIENT
+            if (!ssl->secure_renegotiation->enabled) {
+                if (*input == 0) {
+                    ssl->secure_renegotiation->enabled = 1;
+                    ret = 0;
+                }
+            }
+            else if (*input == 2 * TLS_FINISHED_SZ &&
+                     length == 2 * TLS_FINISHED_SZ + OPAQUE8_LEN) {
+                input++;  /* get past size */
+
+                /* validate client and server verify data */
+                if (XMEMCMP(input,
+                            ssl->secure_renegotiation->client_verify_data,
+                            TLS_FINISHED_SZ) == 0 &&
+                    XMEMCMP(input + TLS_FINISHED_SZ,
+                            ssl->secure_renegotiation->server_verify_data,
+                            TLS_FINISHED_SZ) == 0) {
+                    WOLFSSL_MSG("SCR client and server verify data match");
+                    ret = 0;  /* verified */
+                } else {
+                    /* already in error state */
+                    WOLFSSL_MSG("SCR client and server verify data Failure");
+                }
+            }
+        #endif
+        }
+    }
+
+    if (ret != 0) {
+        SendAlert(ssl, alert_fatal, handshake_failure);
+    }
+
+    return ret;
+}
+
+int TLSX_UseSecureRenegotiation(TLSX** extensions, void* heap)
+{
+    int ret = 0;
+    SecureRenegotiation* data = NULL;
+
+    data = (SecureRenegotiation*)XMALLOC(sizeof(SecureRenegotiation), heap,
+                                                             DYNAMIC_TYPE_TLSX);
+    if (data == NULL)
+        return MEMORY_E;
+
+    XMEMSET(data, 0, sizeof(SecureRenegotiation));
+
+    ret = TLSX_Push(extensions, TLSX_RENEGOTIATION_INFO, data, heap);
+    if (ret != 0) {
+        XFREE(data, heap, DYNAMIC_TYPE_TLSX);
+        return ret;
+    }
+
+    return SSL_SUCCESS;
+}
+
+#ifdef HAVE_SERVER_RENEGOTIATION_INFO
+
+int TLSX_AddEmptyRenegotiationInfo(TLSX** extensions, void* heap)
+{
+    int ret;
+
+    ret = TLSX_Push(extensions, TLSX_RENEGOTIATION_INFO, NULL, heap);
+    if (ret != 0)
+        return ret;
+
+    /* send empty renegotiation_info extension */
+    TLSX* ext = TLSX_Find(*extensions, TLSX_RENEGOTIATION_INFO);
+    if (ext)
+        ext->resp = 1;
+
+    return SSL_SUCCESS;
+}
+
+#endif /* HAVE_SERVER_RENEGOTIATION_INFO */
+
+
+#define SCR_FREE_ALL(data, heap) XFREE(data, (heap), DYNAMIC_TYPE_TLSX)
+#define SCR_GET_SIZE       TLSX_SecureRenegotiation_GetSize
+#define SCR_WRITE          TLSX_SecureRenegotiation_Write
+#define SCR_PARSE          TLSX_SecureRenegotiation_Parse
+
+#else
+
+#define SCR_FREE_ALL(a, heap)
+#define SCR_GET_SIZE(a, b)    0
+#define SCR_WRITE(a, b, c)    0
+#define SCR_PARSE(a, b, c, d) 0
+
+#endif /* HAVE_SECURE_RENEGOTIATION */
+
+/******************************************************************************/
+/* Session Tickets                                                            */
+/******************************************************************************/
+
+#ifdef HAVE_SESSION_TICKET
+
+static void TLSX_SessionTicket_ValidateRequest(WOLFSSL* ssl)
+{
+    TLSX*          extension = TLSX_Find(ssl->extensions, TLSX_SESSION_TICKET);
+    SessionTicket* ticket    = extension ?
+                                         (SessionTicket*)extension->data : NULL;
+
+    if (ticket) {
+        /* TODO validate ticket timeout here! */
+        if (ticket->lifetime == 0xfffffff) {
+            /* send empty ticket on timeout */
+            TLSX_UseSessionTicket(&ssl->extensions, NULL, ssl->heap);
+        }
+    }
+}
+
+
+static word16 TLSX_SessionTicket_GetSize(SessionTicket* ticket, int isRequest)
+{
+    (void)isRequest;
+    return ticket ? ticket->size : 0;
+}
+
+static word16 TLSX_SessionTicket_Write(SessionTicket* ticket, byte* output,
+                                                                  int isRequest)
+{
+    word16 offset = 0; /* empty ticket */
+
+    if (isRequest && ticket) {
+        XMEMCPY(output + offset, ticket->data, ticket->size);
+        offset += ticket->size;
+    }
+
+    return offset;
+}
+
+
+static int TLSX_SessionTicket_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                                                                 byte isRequest)
+{
+    int ret = 0;
+
+    (void) input; /* avoid unused parameter if NO_WOLFSSL_SERVER defined */
+
+    if (!isRequest) {
+        /* client side */
+        if (length != 0)
+            return BUFFER_ERROR;
+
+        ssl->expect_session_ticket = 1;
+    }
+#ifndef NO_WOLFSSL_SERVER
+    else {
+        /* server side */
+        if (ssl->ctx->ticketEncCb == NULL) {
+            WOLFSSL_MSG("Client sent session ticket, server has no callback");
+            return 0;
+        }
+
+        if (length == 0) {
+            /* blank ticket */
+            ret = TLSX_UseSessionTicket(&ssl->extensions, NULL, ssl->heap);
+            if (ret == SSL_SUCCESS) {
+                ret = 0;
+                TLSX_SetResponse(ssl, TLSX_SESSION_TICKET);  /* send blank ticket */
+                ssl->options.createTicket = 1;  /* will send ticket msg */
+                ssl->options.useTicket    = 1;
+                ssl->options.resuming     = 0;  /* no standard resumption */
+                ssl->arrays->sessionIDSz  = 0;  /* no echo on blank ticket */
+            }
+        } else {
+            /* got actual ticket from client */
+            ret = DoClientTicket(ssl, input, length);
+            if (ret == WOLFSSL_TICKET_RET_OK) {    /* use ticket to resume */
+                WOLFSSL_MSG("Using exisitng client ticket");
+                ssl->options.useTicket = 1;
+                ssl->options.resuming  = 1;
+            } else if (ret == WOLFSSL_TICKET_RET_CREATE) {
+                WOLFSSL_MSG("Using existing client ticket, creating new one");
+                ret = TLSX_UseSessionTicket(&ssl->extensions, NULL, ssl->heap);
+                if (ret == SSL_SUCCESS) {
+                    ret = 0;
+                    TLSX_SetResponse(ssl, TLSX_SESSION_TICKET);
+                                                    /* send blank ticket */
+                    ssl->options.createTicket = 1;  /* will send ticket msg */
+                    ssl->options.useTicket    = 1;
+                    ssl->options.resuming     = 1;
+                }
+            } else if (ret == WOLFSSL_TICKET_RET_REJECT) {
+                WOLFSSL_MSG("Process client ticket rejected, not using");
+                ssl->options.rejectTicket = 1;
+                ret = 0;  /* not fatal */
+            } else if (ret == WOLFSSL_TICKET_RET_FATAL || ret < 0) {
+                WOLFSSL_MSG("Process client ticket fatal error, not using");
+            }
+        }
+    }
+#endif /* NO_WOLFSSL_SERVER */
+
+    return ret;
+}
+
+WOLFSSL_LOCAL SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
+                                            byte* data, word16 size, void* heap)
+{
+    SessionTicket* ticket = (SessionTicket*)XMALLOC(sizeof(SessionTicket),
+                                                       heap, DYNAMIC_TYPE_TLSX);
+    if (ticket) {
+        ticket->data = (byte*)XMALLOC(size, heap, DYNAMIC_TYPE_TLSX);
+        if (ticket->data == NULL) {
+            XFREE(ticket, heap, DYNAMIC_TYPE_TLSX);
+            return NULL;
+        }
+
+        XMEMCPY(ticket->data, data, size);
+        ticket->size     = size;
+        ticket->lifetime = lifetime;
+    }
+
+    return ticket;
+}
+WOLFSSL_LOCAL void TLSX_SessionTicket_Free(SessionTicket* ticket, void* heap)
+{
+    if (ticket) {
+        XFREE(ticket->data, heap, DYNAMIC_TYPE_TLSX);
+        XFREE(ticket,       heap, DYNAMIC_TYPE_TLSX);
+    }
+
+    (void)heap;
+}
+
+int TLSX_UseSessionTicket(TLSX** extensions, SessionTicket* ticket, void* heap)
+{
+    int ret = 0;
+
+    if (extensions == NULL)
+        return BAD_FUNC_ARG;
+
+    /* If the ticket is NULL, the client will request a new ticket from the
+       server. Otherwise, the client will use it in the next client hello. */
+    if ((ret = TLSX_Push(extensions, TLSX_SESSION_TICKET, (void*)ticket, heap))
+                                                                           != 0)
+        return ret;
+
+    return SSL_SUCCESS;
+}
+
+#define STK_VALIDATE_REQUEST TLSX_SessionTicket_ValidateRequest
+#define STK_GET_SIZE         TLSX_SessionTicket_GetSize
+#define STK_WRITE            TLSX_SessionTicket_Write
+#define STK_PARSE            TLSX_SessionTicket_Parse
+#define STK_FREE(stk, heap)  TLSX_SessionTicket_Free((SessionTicket*)stk,(heap))
+
+#else
+
+#define STK_FREE(a, b)
+#define STK_VALIDATE_REQUEST(a)
+#define STK_GET_SIZE(a, b)      0
+#define STK_WRITE(a, b, c)      0
+#define STK_PARSE(a, b, c, d)   0
+
+#endif /* HAVE_SESSION_TICKET */
+
+/******************************************************************************/
+/* Quantum-Safe-Hybrid                                                        */
+/******************************************************************************/
+
+#ifdef HAVE_QSH
+static WC_RNG* rng;
+static wolfSSL_Mutex* rngMutex;
+
+static void TLSX_QSH_FreeAll(QSHScheme* list, void* heap)
+{
+    QSHScheme* current;
+
+    while ((current = list)) {
+        list = current->next;
+        XFREE(current, heap, DYNAMIC_TYPE_TLSX);
+    }
+
+    (void)heap;
+}
+
+static int TLSX_QSH_Append(QSHScheme** list, word16 name, byte* pub,
+                                                                  word16 pubLen)
+{
+    QSHScheme* temp;
+
+    if (list == NULL)
+        return BAD_FUNC_ARG;
+
+    if ((temp = (QSHScheme*)XMALLOC(sizeof(QSHScheme), NULL,
+                                                    DYNAMIC_TYPE_TLSX)) == NULL)
+        return MEMORY_E;
+
+    temp->name  = name;
+    temp->PK    = pub;
+    temp->PKLen = pubLen;
+    temp->next  = *list;
+
+    *list = temp;
+
+    return 0;
+}
+
+
+/* request for server's public key : 02 indicates 0-2 requested */
+static byte TLSX_QSH_SerPKReq(byte* output, byte isRequest)
+{
+    if (isRequest) {
+        /* only request one public key from the server */
+        output[0] = 0x01;
+
+        return OPAQUE8_LEN;
+    }
+    else {
+        return 0;
+    }
+}
+
+#ifndef NO_WOLFSSL_CLIENT
+
+/* check for TLS_QSH suite */
+static void TLSX_QSH_ValidateRequest(WOLFSSL* ssl, byte* semaphore)
+{
+    int i;
+
+    for (i = 0; i < ssl->suites->suiteSz; i+= 2)
+        if (ssl->suites->suites[i] == QSH_BYTE)
+            return;
+
+    /* No QSH suite found */
+    TURN_ON(semaphore, TLSX_ToSemaphore(TLSX_QUANTUM_SAFE_HYBRID));
+}
+
+
+/* return the size of the QSH hello extension
+   list      the list of QSHScheme structs containing id and key
+   isRequest if 1 then is being sent to the server
+ */
+word16 TLSX_QSH_GetSize(QSHScheme* list, byte isRequest)
+{
+    QSHScheme* temp = list;
+    word16 length = 0;
+
+    /* account for size of scheme list and public key list */
+    if (isRequest)
+        length = OPAQUE16_LEN;
+    length += OPAQUE24_LEN;
+
+    /* for each non null element in list add size */
+    while ((temp)) {
+        /* add public key info Scheme | Key Length | Key */
+        length += OPAQUE16_LEN;
+        length += OPAQUE16_LEN;
+        length += temp->PKLen;
+
+        /* if client add name size for scheme list
+           advance to next QSHScheme struct in list */
+        if (isRequest)
+            length += OPAQUE16_LEN;
+        temp = temp->next;
+    }
+
+    /* add length for request server public keys */
+    if (isRequest)
+        length += OPAQUE8_LEN;
+
+    return length;
+}
+
+
+/* write out a list of QSHScheme IDs */
+static word16 TLSX_QSH_Write(QSHScheme* list, byte* output)
+{
+    QSHScheme* current = list;
+    word16 length      = 0;
+
+    length += OPAQUE16_LEN;
+
+    while (current) {
+        c16toa(current->name, output + length);
+        length += OPAQUE16_LEN;
+        current = (QSHScheme*)current->next;
+    }
+
+    c16toa(length - OPAQUE16_LEN, output); /* writing list length */
+
+    return length;
+}
+
+
+/* write public key list in extension */
+static word16 TLSX_QSHPK_WriteR(QSHScheme* format, byte* output);
+static word16 TLSX_QSHPK_WriteR(QSHScheme* format, byte* output)
+{
+    word32 offset = 0;
+    word16 public_len = 0;
+
+    if (!format)
+        return offset;
+
+    /* write scheme ID */
+    c16toa(format->name, output + offset);
+    offset += OPAQUE16_LEN;
+
+    /* write public key matching scheme */
+    public_len = format->PKLen;
+    c16toa(public_len, output + offset);
+    offset += OPAQUE16_LEN;
+    if (format->PK) {
+        XMEMCPY(output+offset, format->PK, public_len);
+    }
+
+    return public_len + offset;
+}
+
+word16 TLSX_QSHPK_Write(QSHScheme* list, byte* output)
+{
+    QSHScheme* current = list;
+    word32 length = 0;
+    word24 toWire;
+
+    length += OPAQUE24_LEN;
+
+    while (current) {
+        length += TLSX_QSHPK_WriteR(current, output + length);
+        current = (QSHScheme*)current->next;
+    }
+    /* length of public keys sent */
+    c32to24(length - OPAQUE24_LEN, toWire);
+    output[0] = toWire[0];
+    output[1] = toWire[1];
+    output[2] = toWire[2];
+
+    return length;
+}
+
+#endif /* NO_WOLFSSL_CLIENT */
+#ifndef NO_WOLFSSL_SERVER
+
+static void TLSX_QSHAgreement(TLSX** extensions, void* heap)
+{
+    TLSX* extension = TLSX_Find(*extensions, TLSX_QUANTUM_SAFE_HYBRID);
+    QSHScheme* format = NULL;
+    QSHScheme* del    = NULL;
+    QSHScheme* prev   = NULL;
+
+    if (extension == NULL)
+        return;
+
+    format = (QSHScheme*)extension->data;
+    while (format) {
+        if (format->PKLen == 0) {
+            /* case of head */
+            if (format == extension->data) {
+                extension->data = format->next;
+            }
+            if (prev)
+                prev->next = format->next;
+            del = format;
+            format = format->next;
+            XFREE(del, heap, DYNAMIC_TYPE_TMP_BUFFER);
+            del = NULL;
+        } else {
+            prev   = format;
+            format = format->next;
+        }
+    }
+
+    (void)heap;
+}
+
+
+/* Parse in hello extension
+   input     the byte stream to process
+   length    length of total extension found
+   isRequest set to 1 if being sent to the server
+ */
+static int TLSX_QSH_Parse(WOLFSSL* ssl, byte* input, word16 length,
+                                                                 byte isRequest)
+{
+    byte   numKeys    = 0;
+    word16 offset     = 0;
+    word16 schemSz    = 0;
+    word16 offset_len = 0;
+    word32 offset_pk  = 0;
+    word16 name  = 0;
+    word16 PKLen = 0;
+    byte*  PK = NULL;
+    int r;
+
+
+    if (OPAQUE16_LEN > length)
+        return BUFFER_ERROR;
+
+    if (isRequest) {
+        ato16(input, &schemSz);
+
+        /* list of public keys available for QSH schemes */
+        offset_len = schemSz + OPAQUE16_LEN;
+    }
+
+    offset_pk = ((input[offset_len] << 16)   & 0xFF00000) |
+                (((input[offset_len + 1]) << 8) & 0xFF00) |
+                (input[offset_len + 2] & 0xFF);
+    offset_len += OPAQUE24_LEN;
+
+    /* check buffer size */
+    if (offset_pk > length)
+        return BUFFER_ERROR;
+
+    /* set maximum number of keys the client will accept */
+    if (!isRequest)
+        numKeys = (ssl->maxRequest < 1)? 1 : ssl->maxRequest;
+
+    /* hello extension read list of scheme ids */
+    if (isRequest) {
+
+        /* read in request for public keys */
+        ssl->minRequest = (input[length -1] >> 4) & 0xFF;
+        ssl->maxRequest = input[length -1] & 0x0F;
+
+        /* choose the min between min requested by client and 1 */
+        numKeys = (ssl->minRequest > 1) ? ssl->minRequest : 1;
+
+        if (ssl->minRequest > ssl->maxRequest)
+            return BAD_FUNC_ARG;
+
+        offset  += OPAQUE16_LEN;
+        schemSz += offset;
+
+        /* check buffer size */
+        if (schemSz > length)
+            return BUFFER_ERROR;
+
+        while ((offset < schemSz) && numKeys) {
+            /* Scheme ID list */
+            ato16(input + offset, &name);
+            offset += OPAQUE16_LEN;
+
+            /* validate we have scheme id */
+            if (ssl->user_set_QSHSchemes &&
+                    !TLSX_ValidateQSHScheme(&ssl->extensions, name)) {
+                continue;
+            }
+
+            /* server create keys on demand */
+            if ((r = TLSX_CreateNtruKey(ssl, name)) != 0) {
+                WOLFSSL_MSG("Error creating ntru keys");
+                return r;
+            }
+
+            /* peer sent an agreed upon scheme */
+            r = TLSX_UseQSHScheme(&ssl->extensions, name, NULL, 0, ssl->heap);
+
+            if (r != SSL_SUCCESS) return r; /* throw error */
+
+            numKeys--;
+        }
+
+        /* choose the min between min requested by client and 1 */
+        numKeys = (ssl->minRequest > 1) ? ssl->minRequest : 1;
+    }
+
+    /* QSHPK struct */
+    offset_pk += offset_len;
+    while ((offset_len < offset_pk) && numKeys) {
+        QSHKey * temp;
+
+        if ((temp = (QSHKey*)XMALLOC(sizeof(QSHKey), ssl->heap,
+                                                    DYNAMIC_TYPE_TLSX)) == NULL)
+            return MEMORY_E;
+
+        /* initialize */
+        temp->next = NULL;
+        temp->pub.buffer = NULL;
+        temp->pub.length = 0;
+        temp->pri.buffer = NULL;
+        temp->pri.length = 0;
+
+        /* scheme id */
+        ato16(input + offset_len, &(temp->name));
+        offset_len += OPAQUE16_LEN;
+
+        /* public key length */
+        ato16(input + offset_len, &PKLen);
+        temp->pub.length = PKLen;
+        offset_len += OPAQUE16_LEN;
+
+
+        if (isRequest) {
+            /* validate we have scheme id */
+            if (ssl->user_set_QSHSchemes &&
+                    (!TLSX_ValidateQSHScheme(&ssl->extensions, temp->name))) {
+                offset_len += PKLen;
+                XFREE(temp, ssl->heap, DYNAMIC_TYPE_TLSX);
+                continue;
+            }
+        }
+
+        /* read in public key */
+        if (PKLen > 0) {
+            temp->pub.buffer = (byte*)XMALLOC(temp->pub.length,
+                                            ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
+            XMEMCPY(temp->pub.buffer, input + offset_len, temp->pub.length);
+            offset_len += PKLen;
+        }
+        else {
+            PK = NULL;
+        }
+
+        /* use own key when adding to extensions list for sending reply */
+        PKLen = 0;
+        PK = TLSX_QSHKeyFind_Pub(ssl->QSH_Key, &PKLen, temp->name);
+        r  = TLSX_UseQSHScheme(&ssl->extensions, temp->name, PK, PKLen,
+                                                                     ssl->heap);
+
+        /* store peers key */
+        ssl->peerQSHKeyPresent = 1;
+        if (TLSX_AddQSHKey(&ssl->peerQSHKey, temp) != 0)
+            return MEMORY_E;
+
+        if (temp->pub.length == 0) {
+            XFREE(temp, ssl->heap, DYNAMIC_TYPE_TLSX);
+        }
+
+        if (r != SSL_SUCCESS) {return r;} /* throw error */
+
+        numKeys--;
+    }
+
+    /* reply to a QSH extension sent from client */
+    if (isRequest) {
+        TLSX_SetResponse(ssl, TLSX_QUANTUM_SAFE_HYBRID);
+        /* only use schemes we have key generated for -- free the rest */
+        TLSX_QSHAgreement(&ssl->extensions, ssl->heap);
+    }
+
+    return 0;
+}
+
+
+/* Used for parsing in QSHCipher structs on Key Exchange */
+int TLSX_QSHCipher_Parse(WOLFSSL* ssl, const byte* input, word16 length,
+                                                                  byte isServer)
+{
+    QSHKey* key;
+    word16 Max_Secret_Len = 48;
+    word16 offset     = 0;
+    word16 offset_len = 0;
+    word32 offset_pk  = 0;
+    word16 name       = 0;
+    word16 secretLen  = 0;
+    byte*  secret     = NULL;
+    word16 buffLen    = 0;
+    byte buff[145]; /* size enough for 3 secrets */
+    buffer* buf;
+
+    /* pointer to location where secret should be stored */
+    if (isServer) {
+        buf = ssl->QSH_secret->CliSi;
+    }
+    else {
+        buf = ssl->QSH_secret->SerSi;
+    }
+
+    offset_pk = ((input[offset_len] << 16)    & 0xFF0000) |
+                (((input[offset_len + 1]) << 8) & 0xFF00) |
+                (input[offset_len + 2] & 0xFF);
+    offset_len += OPAQUE24_LEN;
+
+    /* validating extension list length -- check if trying to read over edge
+       of buffer */
+    if (length < (offset_pk + OPAQUE24_LEN)) {
+        return BUFFER_ERROR;
+    }
+
+    /* QSHCipherList struct */
+    offset_pk += offset_len;
+    while (offset_len < offset_pk) {
+
+        /* scheme id */
+        ato16(input + offset_len, &name);
+        offset_len += OPAQUE16_LEN;
+
+        /* public key length */
+        ato16(input + offset_len, &secretLen);
+        offset_len += OPAQUE16_LEN;
+
+        /* read in public key */
+        if (secretLen > 0) {
+            secret = (byte*)(input + offset_len);
+            offset_len += secretLen;
+        }
+        else {
+            secret = NULL;
+        }
+
+        /* no secret sent */
+        if (secret == NULL)
+            continue;
+
+        /* find corresponding key */
+        key = ssl->QSH_Key;
+        while (key) {
+            if (key->name == name)
+                break;
+            else
+                key = (QSHKey*)key->next;
+        }
+
+        /* if we do not have the key than there was a big issue negotiation */
+        if (key == NULL) {
+            WOLFSSL_MSG("key was null for decryption!!!\n");
+            return MEMORY_E;
+        }
+
+        /* Decrypt sent secret */
+        buffLen = Max_Secret_Len;
+        QSH_Decrypt(key, secret, secretLen, buff + offset, &buffLen);
+        offset += buffLen;
+    }
+
+    /* allocate memory for buffer */
+    buf->length = offset;
+    buf->buffer = (byte*)XMALLOC(offset, NULL, DYNAMIC_TYPE_TMP_BUFFER);
+    if (buf->buffer == NULL)
+        return MEMORY_E;
+
+    /* store secrets */
+    XMEMCPY(buf->buffer, buff, offset);
+    ForceZero(buff, offset);
+
+    return offset_len;
+}
+
+
+/* return 1 on success */
+int TLSX_ValidateQSHScheme(TLSX** extensions, word16 theirs) {
+    TLSX* extension = TLSX_Find(*extensions, TLSX_QUANTUM_SAFE_HYBRID);
+    QSHScheme* format    = NULL;
+
+    /* if no extension is sent then do not use QSH */
+    if (!extension) {
+        WOLFSSL_MSG("No QSH Extension");
+        return 0;
+    }
+
+    for (format = (QSHScheme*)extension->data; format; format = format->next) {
+        if (format->name == theirs) {
+	        WOLFSSL_MSG("Found Matching QSH Scheme");
+            return 1; /* have QSH */
+        }
+    }
+
+    return 0;
+}
+#endif /* NO_WOLFSSL_SERVER */
+
+/* test if the QSH Scheme is implemented
+   return 1 if yes 0 if no */
+static int TLSX_HaveQSHScheme(word16 name)
+{
+    switch(name) {
+        #ifdef HAVE_NTRU
+            case WOLFSSL_NTRU_EESS439:
+            case WOLFSSL_NTRU_EESS593:
+            case WOLFSSL_NTRU_EESS743:
+                    return 1;
+        #endif
+            case WOLFSSL_LWE_XXX:
+            case WOLFSSL_HFE_XXX:
+                    return 0; /* not supported yet */
+
+        default:
+            return 0;
+    }
+}
+
+
+/* Add a QSHScheme struct to list of usable ones */
+int TLSX_UseQSHScheme(TLSX** extensions, word16 name, byte* pKey, word16 pkeySz,
+                                                                     void* heap)
+{
+    TLSX*      extension = TLSX_Find(*extensions, TLSX_QUANTUM_SAFE_HYBRID);
+    QSHScheme* format    = NULL;
+    int        ret       = 0;
+
+    /* sanity check */
+    if (extensions == NULL || (pKey == NULL && pkeySz != 0))
+        return BAD_FUNC_ARG;
+
+    /* if scheme is implemented than add */
+    if (TLSX_HaveQSHScheme(name)) {
+	    if ((ret = TLSX_QSH_Append(&format, name, pKey, pkeySz)) != 0)
+	        return ret;
+
+	    if (!extension) {
+	        if ((ret = TLSX_Push(extensions, TLSX_QUANTUM_SAFE_HYBRID, format,
+                                                                  heap)) != 0) {
+	            XFREE(format, 0, DYNAMIC_TYPE_TLSX);
+	            return ret;
+	        }
+	    }
+	    else {
+	        /* push new QSH object to extension data. */
+	        format->next = (QSHScheme*)extension->data;
+	        extension->data = (void*)format;
+
+	        /* look for another format of the same name to remove (replacement) */
+	        do {
+	            if (format->next && (format->next->name == name)) {
+	                QSHScheme* next = format->next;
+
+	                format->next = next->next;
+	                XFREE(next, 0, DYNAMIC_TYPE_TLSX);
+
+	                break;
+	            }
+	        } while ((format = format->next));
+	    }
+    }
+    return SSL_SUCCESS;
+}
+
+#define QSH_FREE_ALL         TLSX_QSH_FreeAll
+#define QSH_VALIDATE_REQUEST TLSX_QSH_ValidateRequest
+
+#ifndef NO_WOLFSSL_CLIENT
+#define QSH_GET_SIZE TLSX_QSH_GetSize
+#define QSH_WRITE    TLSX_QSH_Write
+#else
+#define QSH_GET_SIZE(list)         0
+#define QSH_WRITE(a, b)            0
+#endif
+
+#ifndef NO_WOLFSSL_SERVER
+#define QSH_PARSE TLSX_QSH_Parse
+#else
+#define QSH_PARSE(a, b, c, d)      0
+#endif
+
+#define QSHPK_WRITE TLSX_QSHPK_Write
+#define QSH_SERREQ TLSX_QSH_SerPKReq
+#else
+
+#define QSH_FREE_ALL(list, heap)
+#define QSH_GET_SIZE(list, a)      0
+#define QSH_WRITE(a, b)            0
+#define QSH_PARSE(a, b, c, d)      0
+#define QSHPK_WRITE(a, b)          0
+#define QSH_SERREQ(a, b)           0
+#define QSH_VALIDATE_REQUEST(a, b)
+
+#endif /* HAVE_QSH */
+
+/******************************************************************************/
+/* TLS Extensions Framework                                                   */
+/******************************************************************************/
+
+/** Finds an extension in the provided list. */
+TLSX* TLSX_Find(TLSX* list, TLSX_Type type)
+{
+    TLSX* extension = list;
+
+    while (extension && extension->type != type)
+        extension = extension->next;
+
+    return extension;
+}
+
+/** Releases all extensions in the provided list. */
+void TLSX_FreeAll(TLSX* list, void* heap)
+{
+    TLSX* extension;
+
+    while ((extension = list)) {
+        list = extension->next;
+
+        switch (extension->type) {
+
+            case TLSX_SERVER_NAME:
+                SNI_FREE_ALL((SNI*)extension->data, heap);
+                break;
+
+            case TLSX_MAX_FRAGMENT_LENGTH:
+                MFL_FREE_ALL(extension->data, heap);
+                break;
+
+            case TLSX_TRUNCATED_HMAC:
+                /* Nothing to do. */
+                break;
+
+            case TLSX_SUPPORTED_GROUPS:
+                EC_FREE_ALL((EllipticCurve*)extension->data, heap);
+                break;
+
+            case TLSX_STATUS_REQUEST:
+                CSR_FREE_ALL((CertificateStatusRequest*)extension->data, heap);
+                break;
+
+            case TLSX_STATUS_REQUEST_V2:
+                CSR2_FREE_ALL((CertificateStatusRequestItemV2*)extension->data,
+                        heap);
+                break;
+
+            case TLSX_RENEGOTIATION_INFO:
+                SCR_FREE_ALL(extension->data, heap);
+                break;
+
+            case TLSX_SESSION_TICKET:
+                STK_FREE(extension->data, heap);
+                break;
+
+            case TLSX_QUANTUM_SAFE_HYBRID:
+                QSH_FREE_ALL((QSHScheme*)extension->data, heap);
+                break;
+
+            case TLSX_APPLICATION_LAYER_PROTOCOL:
+                ALPN_FREE_ALL((ALPN*)extension->data, heap);
+                break;
+        }
+
+        XFREE(extension, heap, DYNAMIC_TYPE_TLSX);
+    }
+
+    (void)heap;
+}
+
+/** Checks if the tls extensions are supported based on the protocol version. */
+int TLSX_SupportExtensions(WOLFSSL* ssl) {
+    return ssl && (IsTLS(ssl) || ssl->version.major == DTLS_MAJOR);
+}
+
+/** Tells the buffered size of the extensions in a list. */
+static word16 TLSX_GetSize(TLSX* list, byte* semaphore, byte isRequest)
+{
+    TLSX* extension;
+    word16 length = 0;
+
+    while ((extension = list)) {
+        list = extension->next;
+
+        /* only extensions marked as response are sent back to the client. */
+        if (!isRequest && !extension->resp)
+            continue; /* skip! */
+
+        /* ssl level extensions are expected to override ctx level ones. */
+        if (!IS_OFF(semaphore, TLSX_ToSemaphore(extension->type)))
+            continue; /* skip! */
+
+        /* extension type + extension data length. */
+        length += HELLO_EXT_TYPE_SZ + OPAQUE16_LEN;
+
+
+        switch (extension->type) {
+
+            case TLSX_SERVER_NAME:
+                /* SNI only sends the name on the request. */
+                if (isRequest)
+                    length += SNI_GET_SIZE((SNI*)extension->data);
+                break;
+
+            case TLSX_MAX_FRAGMENT_LENGTH:
+                length += MFL_GET_SIZE(extension->data);
+                break;
+
+            case TLSX_TRUNCATED_HMAC:
+                /* always empty. */
+                break;
+
+            case TLSX_SUPPORTED_GROUPS:
+                length += EC_GET_SIZE((EllipticCurve*)extension->data);
+                break;
+
+            case TLSX_STATUS_REQUEST:
+                length += CSR_GET_SIZE(
+                         (CertificateStatusRequest*)extension->data, isRequest);
+                break;
+
+            case TLSX_STATUS_REQUEST_V2:
+                length += CSR2_GET_SIZE(
+                        (CertificateStatusRequestItemV2*)extension->data,
+                        isRequest);
+                break;
+
+            case TLSX_RENEGOTIATION_INFO:
+                length += SCR_GET_SIZE((SecureRenegotiation*)extension->data,
+                        isRequest);
+                break;
+
+            case TLSX_SESSION_TICKET:
+                length += STK_GET_SIZE((SessionTicket*)extension->data,
+                        isRequest);
+                break;
+
+            case TLSX_QUANTUM_SAFE_HYBRID:
+                length += QSH_GET_SIZE((QSHScheme*)extension->data, isRequest);
+                break;
+
+            case TLSX_APPLICATION_LAYER_PROTOCOL:
+                length += ALPN_GET_SIZE((ALPN*)extension->data);
+                break;
+
+        }
+
+        /* marks the extension as processed so ctx level */
+        /* extensions don't overlap with ssl level ones. */
+        TURN_ON(semaphore, TLSX_ToSemaphore(extension->type));
+    }
+
+    return length;
+}
+
+/** Writes the extensions of a list in a buffer. */
+static word16 TLSX_Write(TLSX* list, byte* output, byte* semaphore,
+                                                                 byte isRequest)
+{
+    TLSX* extension;
+    word16 offset = 0;
+    word16 length_offset = 0;
+
+    while ((extension = list)) {
+        list = extension->next;
+
+        /* only extensions marked as response are written in a response. */
+        if (!isRequest && !extension->resp)
+            continue; /* skip! */
+
+        /* ssl level extensions are expected to override ctx level ones. */
+        if (!IS_OFF(semaphore, TLSX_ToSemaphore(extension->type)))
+            continue; /* skip! */
+
+        /* writes extension type. */
+        c16toa(extension->type, output + offset);
+        offset += HELLO_EXT_TYPE_SZ + OPAQUE16_LEN;
+        length_offset = offset;
+
+        /* extension data should be written internally. */
+        switch (extension->type) {
+            case TLSX_SERVER_NAME:
+                if (isRequest)
+                    offset += SNI_WRITE((SNI*)extension->data, output + offset);
+                break;
+
+            case TLSX_MAX_FRAGMENT_LENGTH:
+                offset += MFL_WRITE((byte*)extension->data, output + offset);
+                break;
+
+            case TLSX_TRUNCATED_HMAC:
+                /* always empty. */
+                break;
+
+            case TLSX_SUPPORTED_GROUPS:
+                offset += EC_WRITE((EllipticCurve*)extension->data,
+                                    output + offset);
+                break;
+
+            case TLSX_STATUS_REQUEST:
+                offset += CSR_WRITE((CertificateStatusRequest*)extension->data,
+                        output + offset, isRequest);
+                break;
+
+            case TLSX_STATUS_REQUEST_V2:
+                offset += CSR2_WRITE(
+                        (CertificateStatusRequestItemV2*)extension->data,
+                        output + offset, isRequest);
+                break;
+
+            case TLSX_RENEGOTIATION_INFO:
+                offset += SCR_WRITE((SecureRenegotiation*)extension->data,
+                        output + offset, isRequest);
+                break;
+
+            case TLSX_SESSION_TICKET:
+                offset += STK_WRITE((SessionTicket*)extension->data,
+                        output + offset, isRequest);
+                break;
+
+            case TLSX_QUANTUM_SAFE_HYBRID:
+                if (isRequest) {
+                    offset += QSH_WRITE((QSHScheme*)extension->data, output + offset);
+                }
+                offset += QSHPK_WRITE((QSHScheme*)extension->data, output + offset);
+                offset += QSH_SERREQ(output + offset, isRequest);
+                break;
+
+            case TLSX_APPLICATION_LAYER_PROTOCOL:
+                offset += ALPN_WRITE((ALPN*)extension->data, output + offset);
+                break;
+        }
+
+        /* writes extension data length. */
+        c16toa(offset - length_offset, output + length_offset - OPAQUE16_LEN);
+
+        /* marks the extension as processed so ctx level */
+        /* extensions don't overlap with ssl level ones. */
+        TURN_ON(semaphore, TLSX_ToSemaphore(extension->type));
+    }
+
+    return offset;
+}
+
+
+#ifdef HAVE_NTRU
+
+static word32 GetEntropy(unsigned char* out, word32 num_bytes)
+{
+    int ret = 0;
+
+    if (rng == NULL) {
+        if ((rng = (WC_RNG*)XMALLOC(sizeof(WC_RNG), NULL,
+                                                    DYNAMIC_TYPE_TLSX)) == NULL)
+            return DRBG_OUT_OF_MEMORY;
+        wc_InitRng(rng);
+    }
+
+    if (rngMutex == NULL) {
+        if ((rngMutex = (wolfSSL_Mutex*)XMALLOC(sizeof(wolfSSL_Mutex), NULL,
+                                                    DYNAMIC_TYPE_TLSX)) == NULL)
+            return DRBG_OUT_OF_MEMORY;
+        wc_InitMutex(rngMutex);
+    }
+
+    ret |= wc_LockMutex(rngMutex);
+    ret |= wc_RNG_GenerateBlock(rng, out, num_bytes);
+    ret |= wc_UnLockMutex(rngMutex);
+
+    if (ret != 0)
+        return DRBG_ENTROPY_FAIL;
+
+    return DRBG_OK;
+}
+#endif
+
+
+#ifdef HAVE_QSH
+static int TLSX_CreateQSHKey(WOLFSSL* ssl, int type)
+{
+    int ret;
+
+    switch (type) {
+#ifdef HAVE_NTRU
+        case WOLFSSL_NTRU_EESS439:
+        case WOLFSSL_NTRU_EESS593:
+        case WOLFSSL_NTRU_EESS743:
+            ret = TLSX_CreateNtruKey(ssl, type);
+            break;
+#endif
+        default:
+            WOLFSSL_MSG("Unknown type for creating NTRU key");
+            return -1;
+    }
+
+    return ret;
+}
+
+
+static int TLSX_AddQSHKey(QSHKey** list, QSHKey* key)
+{
+    QSHKey* current;
+
+    if (key == NULL)
+        return BAD_FUNC_ARG;
+
+    /* if no public key stored in key then do not add */
+    if (key->pub.length == 0 || key->pub.buffer == NULL)
+        return 0;
+
+    /* first element to be added to the list */
+    current = *list;
+    if (current == NULL) {
+        *list = key;
+        return 0;
+    }
+
+    while (current->next) {
+        /* can only have one of the key in the list */
+        if (current->name == key->name)
+            return -1;
+        current = (QSHKey*)current->next;
+    }
+
+    current->next = (struct QSHKey*)key;
+
+    return 0;
+}
+
+
+#ifdef HAVE_NTRU
+int TLSX_CreateNtruKey(WOLFSSL* ssl, int type)
+{
+    int ret;
+    int ntruType;
+
+    /* variable declarations for NTRU*/
+    QSHKey* temp = NULL;
+    byte   public_key[1027];
+    word16 public_key_len = sizeof(public_key);
+    byte   private_key[1120];
+    word16 private_key_len = sizeof(private_key);
+    DRBG_HANDLE drbg;
+
+    if (ssl == NULL)
+        return BAD_FUNC_ARG;
+
+    switch (type) {
+        case WOLFSSL_NTRU_EESS439:
+            ntruType = NTRU_EES439EP1;
+            break;
+        case WOLFSSL_NTRU_EESS593:
+            ntruType = NTRU_EES593EP1;
+            break;
+        case WOLFSSL_NTRU_EESS743:
+            ntruType = NTRU_EES743EP1;
+            break;
+        default:
+            WOLFSSL_MSG("Unknown type for creating NTRU key");
+            return -1;
+    }
+    ret = ntru_crypto_drbg_external_instantiate(GetEntropy, &drbg);
+    if (ret != DRBG_OK) {
+        WOLFSSL_MSG("NTRU drbg instantiate failed\n");
+        return ret;
+    }
+
+    if ((ret = ntru_crypto_ntru_encrypt_keygen(drbg, ntruType,
+                     &public_key_len, NULL, &private_key_len, NULL)) != NTRU_OK)
+        return ret;
+
+    if ((ret = ntru_crypto_ntru_encrypt_keygen(drbg, ntruType,
+        &public_key_len, public_key, &private_key_len, private_key)) != NTRU_OK)
+        return ret;
+
+    ret = ntru_crypto_drbg_uninstantiate(drbg);
+    if (ret != NTRU_OK) {
+        WOLFSSL_MSG("NTRU drbg uninstantiate failed\n");
+        return ret;
+    }
+
+    if ((temp = (QSHKey*)XMALLOC(sizeof(QSHKey), ssl->heap,
+                                                    DYNAMIC_TYPE_TLSX)) == NULL)
+        return MEMORY_E;
+    temp->name = type;
+    temp->pub.length = public_key_len;
+    temp->pub.buffer = (byte*)XMALLOC(public_key_len, ssl->heap,
+                                DYNAMIC_TYPE_PUBLIC_KEY);
+    XMEMCPY(temp->pub.buffer, public_key, public_key_len);
+    temp->pri.length = private_key_len;
+    temp->pri.buffer = (byte*)XMALLOC(private_key_len, ssl->heap,
+                                DYNAMIC_TYPE_ARRAYS);
+    XMEMCPY(temp->pri.buffer, private_key, private_key_len);
+    temp->next = NULL;
+
+    TLSX_AddQSHKey(&ssl->QSH_Key, temp);
+
+    return ret;
+}
+#endif
+
+
+/*
+    Used to find a public key from the list of keys
+    pubLen length of array
+    name   input the name of the scheme looking for ie WOLFSSL_NTRU_ESSXXX
+
+    returns a pointer to public key byte* or NULL if not found
+ */
+static byte* TLSX_QSHKeyFind_Pub(QSHKey* qsh, word16* pubLen, word16 name)
+{
+    QSHKey* current = qsh;
+
+    if (qsh == NULL || pubLen == NULL)
+        return NULL;
+
+    *pubLen = 0;
+
+    while(current) {
+        if (current->name == name) {
+            *pubLen = current->pub.length;
+            return current->pub.buffer;
+        }
+        current = (QSHKey*)current->next;
+    }
+
+    return NULL;
+}
+#endif /* HAVE_QSH */
+
+
+int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isServer)
+{
+    int ret = 0;
+    byte* public_key      = NULL;
+    word16 public_key_len = 0;
+#ifdef HAVE_QSH
+    TLSX* extension;
+    QSHScheme* qsh;
+    QSHScheme* next;
+
+    /* add supported QSHSchemes */
+    WOLFSSL_MSG("Adding supported QSH Schemes");
+#endif
+
+    /* server will add extension depending on whats parsed from client */
+    if (!isServer) {
+#ifdef HAVE_QSH
+        /* test if user has set a specific scheme already */
+        if (!ssl->user_set_QSHSchemes) {
+            if (ssl->sendQSHKeys && ssl->QSH_Key == NULL) {
+                if ((ret = TLSX_CreateQSHKey(ssl, WOLFSSL_NTRU_EESS743)) != 0) {
+                    WOLFSSL_MSG("Error creating ntru keys");
+                    return ret;
+                }
+                if ((ret = TLSX_CreateQSHKey(ssl, WOLFSSL_NTRU_EESS593)) != 0) {
+                    WOLFSSL_MSG("Error creating ntru keys");
+                    return ret;
+                }
+                if ((ret = TLSX_CreateQSHKey(ssl, WOLFSSL_NTRU_EESS439)) != 0) {
+                    WOLFSSL_MSG("Error creating ntru keys");
+                    return ret;
+                }
+
+            /* add NTRU 256 */
+            public_key = TLSX_QSHKeyFind_Pub(ssl->QSH_Key,
+                    &public_key_len, WOLFSSL_NTRU_EESS743);
+            }
+            if (TLSX_UseQSHScheme(&ssl->extensions, WOLFSSL_NTRU_EESS743,
+                                  public_key, public_key_len, ssl->heap)
+                                  != SSL_SUCCESS)
+                ret = -1;
+
+            /* add NTRU 196 */
+            if (ssl->sendQSHKeys) {
+                public_key = TLSX_QSHKeyFind_Pub(ssl->QSH_Key,
+                    &public_key_len, WOLFSSL_NTRU_EESS593);
+            }
+            if (TLSX_UseQSHScheme(&ssl->extensions, WOLFSSL_NTRU_EESS593,
+                                  public_key, public_key_len, ssl->heap)
+                                  != SSL_SUCCESS)
+                ret = -1;
+
+            /* add NTRU 128 */
+            if (ssl->sendQSHKeys) {
+                public_key = TLSX_QSHKeyFind_Pub(ssl->QSH_Key,
+                    &public_key_len, WOLFSSL_NTRU_EESS439);
+            }
+            if (TLSX_UseQSHScheme(&ssl->extensions, WOLFSSL_NTRU_EESS439,
+                                  public_key, public_key_len, ssl->heap)
+                                  != SSL_SUCCESS)
+                ret = -1;
+        }
+        else if (ssl->sendQSHKeys && ssl->QSH_Key == NULL) {
+            /* for each scheme make a client key */
+            extension = TLSX_Find(ssl->extensions, TLSX_QUANTUM_SAFE_HYBRID);
+            if (extension) {
+                qsh = (QSHScheme*)extension->data;
+
+                while (qsh) {
+                    if ((ret = TLSX_CreateQSHKey(ssl, qsh->name)) != 0)
+                        return ret;
+
+                    /* get next now because qsh could be freed */
+                    next = qsh->next;
+
+                    /* find the public key created and add to extension*/
+                    public_key = TLSX_QSHKeyFind_Pub(ssl->QSH_Key,
+                             &public_key_len, qsh->name);
+                    if (TLSX_UseQSHScheme(&ssl->extensions, qsh->name,
+                                          public_key, public_key_len,
+                                          ssl->heap) != SSL_SUCCESS)
+                        ret = -1;
+                    qsh = next;
+                }
+            }
+        }
+#endif
+
+#if defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES)
+        if (!ssl->options.userCurves && !ssl->ctx->userCurves) {
+        #if defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)
+            #ifndef NO_ECC_SECP
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP160R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+            #ifdef HAVE_ECC_SECPR2
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP160R2, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+            #ifdef HAVE_ECC_KOBLITZ
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP160K1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+        #endif
+        #if defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)
+            #ifndef NO_ECC_SECP
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP192R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+            #ifdef HAVE_ECC_KOBLITZ
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP192K1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+        #endif
+        #if defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)
+            #ifndef NO_ECC_SECP
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP224R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+            #ifdef HAVE_ECC_KOBLITZ
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP224K1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+        #endif
+        #if !defined(NO_ECC256)  || defined(HAVE_ALL_CURVES)
+            #ifndef NO_ECC_SECP
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP256R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+            #ifdef HAVE_ECC_KOBLITZ
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP256K1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+            #ifdef HAVE_ECC_BRAINPOOL
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_BRAINPOOLP256R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+        #endif
+        #if defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)
+            #ifndef NO_ECC_SECP
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP384R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+            #ifdef HAVE_ECC_BRAINPOOL
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_BRAINPOOLP384R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+        #endif
+        #if defined(HAVE_ECC512) || defined(HAVE_ALL_CURVES)
+            #ifdef HAVE_ECC_BRAINPOOL
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_BRAINPOOLP512R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+        #endif
+        #if defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)
+            #ifndef NO_ECC_SECP
+                ret = TLSX_UseSupportedCurve(&ssl->extensions, WOLFSSL_ECC_SECP521R1, ssl->heap);
+                if (ret != SSL_SUCCESS) return ret;
+            #endif
+        #endif
+        }
+#endif /* HAVE_ECC && HAVE_SUPPORTED_CURVES */
+    } /* is not server */
+
+    (void)public_key;
+    (void)public_key_len;
+    (void)ssl;
+
+    if (ret == SSL_SUCCESS)
+        ret = 0;
+
+    return ret;
+}
+
+
+#ifndef NO_WOLFSSL_CLIENT
+
+/** Tells the buffered size of extensions to be sent into the client hello. */
+word16 TLSX_GetRequestSize(WOLFSSL* ssl)
+{
+    word16 length = 0;
+
+    if (TLSX_SupportExtensions(ssl)) {
+        byte semaphore[SEMAPHORE_SIZE] = {0};
+
+        EC_VALIDATE_REQUEST(ssl, semaphore);
+        QSH_VALIDATE_REQUEST(ssl, semaphore);
+        STK_VALIDATE_REQUEST(ssl);
+
+        if (ssl->extensions)
+            length += TLSX_GetSize(ssl->extensions, semaphore, 1);
+
+        if (ssl->ctx && ssl->ctx->extensions)
+            length += TLSX_GetSize(ssl->ctx->extensions, semaphore, 1);
+
+        if (IsAtLeastTLSv1_2(ssl) && ssl->suites->hashSigAlgoSz)
+            length += HELLO_EXT_SZ + HELLO_EXT_SIGALGO_SZ
+                   + ssl->suites->hashSigAlgoSz;
+
+#ifdef HAVE_EXTENDED_MASTER
+        if (ssl->options.haveEMS)
+            length += HELLO_EXT_SZ;
+#endif
+    }
+
+    if (length)
+        length += OPAQUE16_LEN; /* for total length storage. */
+
+    return length;
+}
+
+/** Writes the extensions to be sent into the client hello. */
+word16 TLSX_WriteRequest(WOLFSSL* ssl, byte* output)
+{
+    word16 offset = 0;
+
+    if (TLSX_SupportExtensions(ssl) && output) {
+        byte semaphore[SEMAPHORE_SIZE] = {0};
+
+        offset += OPAQUE16_LEN; /* extensions length */
+
+        EC_VALIDATE_REQUEST(ssl, semaphore);
+        STK_VALIDATE_REQUEST(ssl);
+        QSH_VALIDATE_REQUEST(ssl, semaphore);
+
+        if (ssl->extensions)
+            offset += TLSX_Write(ssl->extensions, output + offset,
+                                                                  semaphore, 1);
+
+        if (ssl->ctx && ssl->ctx->extensions)
+            offset += TLSX_Write(ssl->ctx->extensions, output + offset,
+                                                                  semaphore, 1);
+
+        if (IsAtLeastTLSv1_2(ssl) && ssl->suites->hashSigAlgoSz) {
+            int i;
+            /* extension type */
+            c16toa(HELLO_EXT_SIG_ALGO, output + offset);
+            offset += HELLO_EXT_TYPE_SZ;
+
+            /* extension data length */
+            c16toa(OPAQUE16_LEN + ssl->suites->hashSigAlgoSz,
+                   output + offset);
+            offset += OPAQUE16_LEN;
+
+            /* sig algos length */
+            c16toa(ssl->suites->hashSigAlgoSz, output + offset);
+            offset += OPAQUE16_LEN;
+
+            /* sig algos */
+            for (i = 0; i < ssl->suites->hashSigAlgoSz; i++, offset++)
+                output[offset] = ssl->suites->hashSigAlgo[i];
+        }
+
+#ifdef HAVE_EXTENDED_MASTER
+        if (ssl->options.haveEMS) {
+            c16toa(HELLO_EXT_EXTMS, output + offset);
+            offset += HELLO_EXT_TYPE_SZ;
+            c16toa(0, output + offset);
+            offset += HELLO_EXT_SZ_SZ;
+        }
+#endif
+
+        if (offset > OPAQUE16_LEN)
+            c16toa(offset - OPAQUE16_LEN, output); /* extensions length */
+    }
+
+    return offset;
+}
+
+#endif /* NO_WOLFSSL_CLIENT */
+
+#ifndef NO_WOLFSSL_SERVER
+
+/** Tells the buffered size of extensions to be sent into the server hello. */
+word16 TLSX_GetResponseSize(WOLFSSL* ssl)
+{
+    word16 length = 0;
+    byte semaphore[SEMAPHORE_SIZE] = {0};
+
+    #ifdef HAVE_QSH
+        /* change response if not using TLS_QSH */
+        if (!ssl->options.haveQSH) {
+            TLSX* ext = TLSX_Find(ssl->extensions, TLSX_QUANTUM_SAFE_HYBRID);
+            if (ext)
+                ext->resp = 0;
+        }
+    #endif
+
+#ifdef HAVE_EXTENDED_MASTER
+    if (ssl->options.haveEMS)
+        length += HELLO_EXT_SZ;
+#endif
+
+    if (TLSX_SupportExtensions(ssl))
+        length += TLSX_GetSize(ssl->extensions, semaphore, 0);
+
+    /* All the response data is set at the ssl object only, so no ctx here. */
+
+    if (length)
+        length += OPAQUE16_LEN; /* for total length storage. */
+
+    return length;
+}
+
+/** Writes the server hello extensions into a buffer. */
+word16 TLSX_WriteResponse(WOLFSSL *ssl, byte* output)
+{
+    word16 offset = 0;
+
+    if (TLSX_SupportExtensions(ssl) && output) {
+        byte semaphore[SEMAPHORE_SIZE] = {0};
+
+        offset += OPAQUE16_LEN; /* extensions length */
+
+        offset += TLSX_Write(ssl->extensions, output + offset, semaphore, 0);
+
+#ifdef HAVE_EXTENDED_MASTER
+        if (ssl->options.haveEMS) {
+            c16toa(HELLO_EXT_EXTMS, output + offset);
+            offset += HELLO_EXT_TYPE_SZ;
+            c16toa(0, output + offset);
+            offset += HELLO_EXT_SZ_SZ;
+        }
+#endif
+
+        if (offset > OPAQUE16_LEN)
+            c16toa(offset - OPAQUE16_LEN, output); /* extensions length */
+    }
+
+    return offset;
+}
+
+#endif /* NO_WOLFSSL_SERVER */
+
+/** Parses a buffer of TLS extensions. */
+int TLSX_Parse(WOLFSSL* ssl, byte* input, word16 length, byte isRequest,
+                                                                 Suites *suites)
+{
+    int ret = 0;
+    word16 offset = 0;
+#ifdef HAVE_EXTENDED_MASTER
+    byte pendingEMS = 0;
+#endif
+
+    if (!ssl || !input || (isRequest && !suites))
+        return BAD_FUNC_ARG;
+
+    while (ret == 0 && offset < length) {
+        word16 type;
+        word16 size;
+
+        if (length - offset < HELLO_EXT_TYPE_SZ + OPAQUE16_LEN)
+            return BUFFER_ERROR;
+
+        ato16(input + offset, &type);
+        offset += HELLO_EXT_TYPE_SZ;
+
+        ato16(input + offset, &size);
+        offset += OPAQUE16_LEN;
+
+        if (offset + size > length)
+            return BUFFER_ERROR;
+
+        switch (type) {
+            case TLSX_SERVER_NAME:
+                WOLFSSL_MSG("SNI extension received");
+
+                ret = SNI_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case TLSX_MAX_FRAGMENT_LENGTH:
+                WOLFSSL_MSG("Max Fragment Length extension received");
+
+                ret = MFL_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case TLSX_TRUNCATED_HMAC:
+                WOLFSSL_MSG("Truncated HMAC extension received");
+
+                ret = THM_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case TLSX_SUPPORTED_GROUPS:
+                WOLFSSL_MSG("Elliptic Curves extension received");
+
+                ret = EC_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case TLSX_STATUS_REQUEST:
+                WOLFSSL_MSG("Certificate Status Request extension received");
+
+                ret = CSR_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case TLSX_STATUS_REQUEST_V2:
+                WOLFSSL_MSG("Certificate Status Request v2 extension received");
+
+                ret = CSR2_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+#ifdef HAVE_EXTENDED_MASTER
+            case HELLO_EXT_EXTMS:
+                WOLFSSL_MSG("Extended Master Secret extension received");
+
+#ifndef NO_WOLFSSL_SERVER
+                if (isRequest)
+                    ssl->options.haveEMS = 1;
+#endif
+                pendingEMS = 1;
+                break;
+#endif
+
+            case TLSX_RENEGOTIATION_INFO:
+                WOLFSSL_MSG("Secure Renegotiation extension received");
+
+                ret = SCR_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case TLSX_SESSION_TICKET:
+                WOLFSSL_MSG("Session Ticket extension received");
+
+                ret = STK_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case TLSX_QUANTUM_SAFE_HYBRID:
+                WOLFSSL_MSG("Quantum-Safe-Hybrid extension received");
+
+                ret = QSH_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case TLSX_APPLICATION_LAYER_PROTOCOL:
+                WOLFSSL_MSG("ALPN extension received");
+
+                ret = ALPN_PARSE(ssl, input + offset, size, isRequest);
+                break;
+
+            case HELLO_EXT_SIG_ALGO:
+                if (isRequest) {
+                    /* do not mess with offset inside the switch! */
+                    if (IsAtLeastTLSv1_2(ssl)) {
+                        ato16(input + offset, &suites->hashSigAlgoSz);
+
+                        if (suites->hashSigAlgoSz > size - OPAQUE16_LEN)
+                            return BUFFER_ERROR;
+
+                        XMEMCPY(suites->hashSigAlgo,
+                                input + offset + OPAQUE16_LEN,
+                                min(suites->hashSigAlgoSz,
+                                                        HELLO_EXT_SIGALGO_MAX));
+                    }
+                } else {
+                    WOLFSSL_MSG("Servers MUST NOT send SIG ALGO extension.");
+                }
+
+                break;
+        }
+
+        /* offset should be updated here! */
+        offset += size;
+    }
+
+#ifdef HAVE_EXTENDED_MASTER
+    if (!isRequest && ssl->options.haveEMS && !pendingEMS)
+        ssl->options.haveEMS = 0;
+#endif
+
+    if (ret == 0)
+        ret = SNI_VERIFY_PARSE(ssl, isRequest);
+
+    return ret;
+}
+
+/* undefining semaphore macros */
+#undef IS_OFF
+#undef TURN_ON
+#undef SEMAPHORE_SIZE
+
+#endif /* HAVE_TLS_EXTENSIONS */
+
+#ifndef NO_WOLFSSL_CLIENT
+
+#ifndef NO_OLD_TLS
+
+    WOLFSSL_METHOD* wolfTLSv1_client_method(void)
+    {
+        return wolfTLSv1_client_method_ex(NULL);
+    }
+
+
+    WOLFSSL_METHOD* wolfTLSv1_1_client_method(void)
+    {
+        return wolfTLSv1_1_client_method_ex(NULL);
+    }
+
+    WOLFSSL_METHOD* wolfTLSv1_client_method_ex(void* heap)
+    {
+        WOLFSSL_METHOD* method =
+                             (WOLFSSL_METHOD*) XMALLOC(sizeof(WOLFSSL_METHOD),
+                                                     heap, DYNAMIC_TYPE_METHOD);
+        if (method)
+            InitSSL_Method(method, MakeTLSv1());
+        return method;
+    }
+
+
+    WOLFSSL_METHOD* wolfTLSv1_1_client_method_ex(void* heap)
+    {
+        WOLFSSL_METHOD* method =
+                              (WOLFSSL_METHOD*) XMALLOC(sizeof(WOLFSSL_METHOD),
+                                                     heap, DYNAMIC_TYPE_METHOD);
+        if (method)
+            InitSSL_Method(method, MakeTLSv1_1());
+        return method;
+    }
+
+#endif /* !NO_OLD_TLS */
+
+
+    WOLFSSL_METHOD* wolfTLSv1_2_client_method(void)
+    {
+        return wolfTLSv1_2_client_method_ex(NULL);
+    }
+
+    WOLFSSL_METHOD* wolfTLSv1_2_client_method_ex(void* heap)
+    {
+        WOLFSSL_METHOD* method =
+                              (WOLFSSL_METHOD*) XMALLOC(sizeof(WOLFSSL_METHOD),
+                                                     heap, DYNAMIC_TYPE_METHOD);
+        (void)heap;
+        if (method)
+            InitSSL_Method(method, MakeTLSv1_2());
+        return method;
+    }
+
+
+    WOLFSSL_METHOD* wolfSSLv23_client_method(void)
+    {
+        return wolfSSLv23_client_method_ex(NULL);
+    }
+
+
+    WOLFSSL_METHOD* wolfSSLv23_client_method_ex(void* heap)
+    {
+        WOLFSSL_METHOD* method =
+                              (WOLFSSL_METHOD*) XMALLOC(sizeof(WOLFSSL_METHOD),
+                                                     heap, DYNAMIC_TYPE_METHOD);
+        (void)heap;
+        if (method) {
+#if !defined(NO_SHA256) || defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512)
+            InitSSL_Method(method, MakeTLSv1_2());
+#else
+    #ifndef NO_OLD_TLS
+            InitSSL_Method(method, MakeTLSv1_1());
+    #endif
+#endif
+#ifndef NO_OLD_TLS
+            method->downgrade = 1;
+#endif
+        }
+        return method;
+    }
+
+#endif /* NO_WOLFSSL_CLIENT */
+
+
+
+#ifndef NO_WOLFSSL_SERVER
+
+#ifndef NO_OLD_TLS
+
+    WOLFSSL_METHOD* wolfTLSv1_server_method(void)
+    {
+        return wolfTLSv1_server_method_ex(NULL);
+    }
+
+
+    WOLFSSL_METHOD* wolfTLSv1_1_server_method(void)
+    {
+        return wolfTLSv1_1_server_method_ex(NULL);
+    }
+
+    WOLFSSL_METHOD* wolfTLSv1_server_method_ex(void* heap)
+    {
+        WOLFSSL_METHOD* method =
+                              (WOLFSSL_METHOD*) XMALLOC(sizeof(WOLFSSL_METHOD),
+                                                     heap, DYNAMIC_TYPE_METHOD);
+        if (method) {
+            InitSSL_Method(method, MakeTLSv1());
+            method->side = WOLFSSL_SERVER_END;
+        }
+        return method;
+    }
+
+
+    WOLFSSL_METHOD* wolfTLSv1_1_server_method_ex(void* heap)
+    {
+        WOLFSSL_METHOD* method =
+                              (WOLFSSL_METHOD*) XMALLOC(sizeof(WOLFSSL_METHOD),
+                                                     heap, DYNAMIC_TYPE_METHOD);
+        if (method) {
+            InitSSL_Method(method, MakeTLSv1_1());
+            method->side = WOLFSSL_SERVER_END;
+        }
+        return method;
+    }
+#endif /* !NO_OLD_TLS */
+
+
+    WOLFSSL_METHOD* wolfTLSv1_2_server_method(void)
+    {
+        return wolfTLSv1_2_server_method_ex(NULL);
+    }
+
+    WOLFSSL_METHOD* wolfTLSv1_2_server_method_ex(void* heap)
+    {
+        WOLFSSL_METHOD* method =
+                              (WOLFSSL_METHOD*) XMALLOC(sizeof(WOLFSSL_METHOD),
+                                                     heap, DYNAMIC_TYPE_METHOD);
+        (void)heap;
+        if (method) {
+            InitSSL_Method(method, MakeTLSv1_2());
+            method->side = WOLFSSL_SERVER_END;
+        }
+        return method;
+    }
+
+
+    WOLFSSL_METHOD* wolfSSLv23_server_method(void)
+    {
+        return wolfSSLv23_server_method_ex(NULL);
+    }
+
+    WOLFSSL_METHOD* wolfSSLv23_server_method_ex(void* heap)
+    {
+        WOLFSSL_METHOD* method =
+                              (WOLFSSL_METHOD*) XMALLOC(sizeof(WOLFSSL_METHOD),
+                                                     heap, DYNAMIC_TYPE_METHOD);
+        (void)heap;
+        if (method) {
+#if !defined(NO_SHA256) || defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512)
+            InitSSL_Method(method, MakeTLSv1_2());
+#else
+    #ifndef NO_OLD_TLS
+            InitSSL_Method(method, MakeTLSv1_1());
+    #else
+            #error Must have SHA256, SHA384 or SHA512 enabled for TLS 1.2
+    #endif
+#endif
+#ifndef NO_OLD_TLS
+            method->downgrade = 1;
+#endif
+            method->side      = WOLFSSL_SERVER_END;
+        }
+        return method;
+    }
+
+
+#endif /* NO_WOLFSSL_SERVER */
+#endif /* NO_TLS */
+#endif /* WOLFCRYPT_ONLY */
+