CyaSSL is an SSL library for devices like mbed.

Dependents:   cyassl-client Sync

Revision:
0:5045d2638c29
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cyassl_int.c	Sat Feb 05 01:09:17 2011 +0000
@@ -0,0 +1,4782 @@
+/* cyassl_int.c
+ *
+ * Copyright (C) 2006-2009 Sawtooth Consulting Ltd.
+ *
+ * This file is part of CyaSSL.
+ *
+ * CyaSSL 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.
+ *
+ * CyaSSL 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+
+
+#include "cyassl_int.h"
+#include "cyassl_error.h"
+#include "asn.h"
+
+#ifdef HAVE_LIBZ
+    #include "zlib.h"
+#endif
+
+#ifdef HAVE_NTRU
+    #include "crypto_ntru.h"
+#endif
+
+#if defined(DEBUG_CYASSL) || defined(SHOW_SECRETS)
+    #include <stdio.h>
+#endif
+
+#ifdef __sun
+    #include <sys/filio.h>
+#endif
+
+#define TRUE  1
+#define FALSE 0
+
+
+int CyaSSL_negotiate(SSL*);
+
+
+#ifndef NO_CYASSL_CLIENT
+    static int DoHelloVerifyRequest(SSL* ssl, const byte* input, word32*);
+    static int DoServerHello(SSL* ssl, const byte* input, word32*);
+    static int DoCertificateRequest(SSL* ssl, const byte* input, word32*);
+    static int DoServerKeyExchange(SSL* ssl, const byte* input, word32*);
+#endif
+
+
+#ifndef NO_CYASSL_SERVER
+    static int DoClientHello(SSL* ssl, const byte* input, word32*, word32,
+                             word32);
+    static int DoCertificateVerify(SSL* ssl, byte*, word32*, word32);
+    static int DoClientKeyExchange(SSL* ssl, byte* input, word32*);
+#endif
+
+typedef enum {
+    doProcessInit = 0,
+#ifndef NO_CYASSL_SERVER
+    runProcessOldClientHello,
+#endif
+    getRecordLayerHeader,
+    getData,
+    runProcessingOneMessage
+} processReply;
+
+static void Hmac(SSL* ssl, byte* digest, const byte* buffer, word32 sz,
+                 int content, int verify);
+
+static void BuildCertHashes(SSL* ssl, Hashes* hashes);
+
+
+void BuildTlsFinished(SSL* ssl, Hashes* hashes, const byte* sender);
+
+
+#ifndef min
+
+    static INLINE word32 min(word32 a, word32 b)
+    {
+        return a > b ? b : a;
+    }
+
+#endif /* min */
+
+
+int IsTLS(const SSL* ssl)
+{
+    if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_MINOR)
+        return 1;
+
+    return 0;
+}
+
+
+int IsAtLeastTLSv1_2(const SSL* ssl)
+{
+    if (ssl->version.major == SSLv3_MAJOR && ssl->version.minor >=TLSv1_2_MINOR)
+        return 1;
+
+    return 0;
+}
+
+
+#ifdef HAVE_NTRU
+
+static byte GetEntropy(ENTROPY_CMD cmd, byte* out)
+{
+    /* TODO: add locking? */
+    static RNG rng;
+
+    if (cmd == INIT) {
+        int ret = InitRng(&rng);
+        if (ret == 0)
+            return 1;
+        else
+            return 0;
+    }
+
+    if (out == NULL)
+        return 0;
+
+    if (cmd == GET_BYTE_OF_ENTROPY) {
+        RNG_GenerateBlock(&rng, out, 1);
+        return 1;
+    }
+
+    if (cmd == GET_NUM_BYTES_PER_BYTE_OF_ENTROPY) {
+        *out = 1;
+        return 1;
+    }
+
+    return 0;
+}
+
+#endif /* HAVE_NTRU */
+
+static INLINE void c32to24(word32 in, word24 out)
+{
+    out[0] = (in >> 16) & 0xff;
+    out[1] = (in >>  8) & 0xff;
+    out[2] =  in & 0xff;
+}
+
+
+static INLINE void c32to48(word32 in, byte out[6])
+{
+    out[0] = 0;
+    out[1] = 0;
+    out[2] = (in >> 24) & 0xff;
+    out[3] = (in >> 16) & 0xff;
+    out[4] = (in >>  8) & 0xff;
+    out[5] =  in & 0xff;
+}
+
+
+/* convert 16 bit integer to opaque */
+static void INLINE c16toa(word16 u16, byte* c)
+{
+    c[0] = (u16 >> 8) & 0xff;
+    c[1] =  u16 & 0xff;
+}
+
+
+/* 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;
+}
+
+
+/* convert a 24 bit integer into a 32 bit one */
+static INLINE void c24to32(const word24 u24, word32* u32)
+{
+    *u32 = 0;
+    *u32 = (u24[0] << 16) | (u24[1] << 8) | u24[2];
+}
+
+
+/* convert opaque to 16 bit integer */
+static INLINE void ato16(const byte* c, word16* u16)
+{
+    *u16 = 0;
+    *u16 = (c[0] << 8) | (c[1]);
+}
+
+
+/* convert opaque to 32 bit integer */
+static INLINE void ato32(const byte* c, word32* u32)
+{
+    *u32 = 0;
+    *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
+}
+
+
+#ifdef HAVE_LIBZ
+
+    /* alloc user allocs to work with zlib */
+    void* myAlloc(void* opaque, unsigned int item, unsigned int size)
+    {
+        return XMALLOC(item * size, opaque, DYNAMIC_TYPE_LIBZ);
+    }
+
+
+    void myFree(void* opaque, void* memory)
+    {
+        XFREE(memory, opaque, DYNAMIC_TYPE_LIBZ);
+    }
+
+
+    /* init zlib comp/decomp streams, 0 on success */
+    static int InitStreams(SSL* ssl)
+    {
+        ssl->c_stream.zalloc = (alloc_func)myAlloc;
+        ssl->c_stream.zfree  = (free_func)myFree;
+        ssl->c_stream.opaque = (voidpf)ssl->heap;
+
+        if (deflateInit(&ssl->c_stream, 8) != Z_OK) return ZLIB_INIT_ERROR;
+
+        ssl->didStreamInit = 1;
+
+        ssl->d_stream.zalloc = (alloc_func)myAlloc;
+        ssl->d_stream.zfree  = (free_func)myFree;
+        ssl->d_stream.opaque = (voidpf)ssl->heap;
+
+        if (inflateInit(&ssl->d_stream) != Z_OK) return ZLIB_INIT_ERROR;
+
+        return 0;
+    }
+
+
+    static void FreeStreams(SSL* ssl)
+    {
+        if (ssl->didStreamInit) {
+            deflateEnd(&ssl->c_stream);
+            inflateEnd(&ssl->d_stream);
+        }
+    }
+
+
+    /* compress in to out, return out size or error */
+    static int Compress(SSL* ssl, byte* in, int inSz, byte* out, int outSz)
+    {
+        int    err;
+        int    currTotal = ssl->c_stream.total_out;
+
+        /* put size in front of compression */
+        c16toa((word16)inSz, out);
+        out   += 2;
+        outSz -= 2;
+
+        ssl->c_stream.next_in   = in;
+        ssl->c_stream.avail_in  = inSz;
+        ssl->c_stream.next_out  = out;
+        ssl->c_stream.avail_out = outSz;
+
+        err = deflate(&ssl->c_stream, Z_SYNC_FLUSH);
+        if (err != Z_OK && err != Z_STREAM_END) return ZLIB_COMPRESS_ERROR;
+
+        return ssl->c_stream.total_out - currTotal + sizeof(word16);
+    }
+        
+
+    /* decompress in to out, returnn out size or error */
+    static int DeCompress(SSL* ssl, byte* in, int inSz, byte* out, int outSz)
+    {
+        int    err;
+        int    currTotal = ssl->d_stream.total_out;
+        word16 len;
+
+        /* find size in front of compression */
+        ato16(in, &len);
+        in   += 2;
+        inSz -= 2;
+
+        ssl->d_stream.next_in   = in;
+        ssl->d_stream.avail_in  = inSz;
+        ssl->d_stream.next_out  = out;
+        ssl->d_stream.avail_out = outSz;
+
+        err = inflate(&ssl->d_stream, Z_SYNC_FLUSH);
+        if (err != Z_OK && err != Z_STREAM_END) return ZLIB_DECOMPRESS_ERROR;
+
+        return ssl->d_stream.total_out - currTotal;
+    }
+        
+#endif /* HAVE_LIBZ */
+
+
+void InitSSL_Method(SSL_METHOD* method, ProtocolVersion pv)
+{
+    method->version    = pv;
+    method->side       = CLIENT_END;
+    method->verifyPeer = 0;
+    method->verifyNone = 0;
+    method->failNoCert = 0;
+    method->downgrade  = 0;
+}
+
+
+void InitSSL_Ctx(SSL_CTX* ctx, SSL_METHOD* method)
+{
+    ctx->method = method;
+    ctx->certificate.buffer = 0;
+    ctx->privateKey.buffer  = 0;
+    ctx->haveDH             = 0;
+    ctx->haveNTRU           = 0;    /* start off */
+    ctx->heap               = ctx;  /* defaults to self */
+#ifndef NO_PSK
+    ctx->havePSK            = 0;
+    ctx->server_hint[0]     = 0;
+    ctx->client_psk_cb      = 0;
+    ctx->server_psk_cb      = 0;
+#endif /* NO_PSK */
+
+#ifdef OPENSSL_EXTRA
+    ctx->passwd_cb   = 0;
+    ctx->userdata    = 0;
+#endif /* OPENSSL_EXTRA */
+
+#ifndef CYASSL_USER_IO
+    ctx->CBIORecv = EmbedReceive;
+    ctx->CBIOSend = EmbedSend;
+#else
+    /* user will set */
+    ctx->CBIORecv = NULL;
+    ctx->CBIOSend = NULL;
+#endif
+    ctx->partialWrite   = 0;
+    ctx->verifyCallback = 0;
+
+    ctx->caList = 0;
+#ifdef HAVE_NTRU
+    if (method->side == CLIENT_END)
+        ctx->haveNTRU = 1;           /* always on cliet side */
+                                     /* server can turn on by loading key */
+#endif
+    /* remove DH later if server didn't set, add psk later */
+    InitSuites(&ctx->suites, method->version, TRUE, FALSE, ctx->haveNTRU);  
+    ctx->verifyPeer = 0;
+    ctx->verifyNone = 0;
+    ctx->failNoCert = 0;
+    ctx->sessionCacheOff      = 0;  /* initially on */
+    ctx->sessionCacheFlushOff = 0;  /* initially on */
+    ctx->sendVerify = 0;
+    ctx->quietShutdown = 0;
+
+}
+
+
+/* In case contexts are held in array and don't want to free actual ctx */
+void SSL_CtxResourceFree(SSL_CTX* ctx)
+{
+    XFREE(ctx->privateKey.buffer, ctx->heap, DYNAMIC_TYPE_KEY);
+    XFREE(ctx->certificate.buffer, ctx->heap, DYNAMIC_TYPE_CERT);
+    XFREE(ctx->method, ctx->heap, DYNAMIC_TYPE_METHOD);
+
+    FreeSigners(ctx->caList, ctx->heap);
+}
+
+
+void FreeSSL_Ctx(SSL_CTX* ctx)
+{
+    SSL_CtxResourceFree(ctx);
+    XFREE(ctx, ctx->heap, DYNAMIC_TYPE_CTX);
+}
+
+    
+
+void InitSuites(Suites* suites, ProtocolVersion pv, byte haveDH, byte havePSK,
+                byte haveNTRU)
+{
+    word32 idx = 0;
+    int    tls = pv.major == 3 && pv.minor >= 1;
+
+    (void)tls;  /* shut up compiler */
+
+#ifdef CYASSL_DTLS
+    if (pv.major == DTLS_MAJOR && pv.minor == DTLS_MINOR)
+        tls = 1;
+#endif
+
+    suites->setSuites = 0;  /* user hasn't set yet */
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA
+    if (tls && haveNTRU) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_NTRU_RSA_WITH_AES_256_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA
+    if (tls && haveNTRU) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_NTRU_RSA_WITH_AES_128_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA
+    if (tls && haveNTRU) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_NTRU_RSA_WITH_RC4_128_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA
+    if (tls && haveNTRU) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
+    if (tls && haveDH) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+    if (tls && haveDH) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA
+    if (tls) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA
+    if (tls) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA
+    if (tls && havePSK) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_PSK_WITH_AES_256_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA
+    if (tls && havePSK) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_PSK_WITH_AES_128_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA
+    suites->suites[idx++] = 0; 
+    suites->suites[idx++] = SSL_RSA_WITH_RC4_128_SHA;
+#endif
+
+#ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5
+    suites->suites[idx++] = 0; 
+    suites->suites[idx++] = SSL_RSA_WITH_RC4_128_MD5;
+#endif
+
+#ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA
+    suites->suites[idx++] = 0; 
+    suites->suites[idx++] = SSL_RSA_WITH_3DES_EDE_CBC_SHA;
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5
+    if (tls) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_RSA_WITH_HC_128_CBC_MD5;
+    }
+#endif
+    
+#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA
+    if (tls) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_RSA_WITH_HC_128_CBC_SHA;
+    }
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA
+    if (tls) {
+        suites->suites[idx++] = 0; 
+        suites->suites[idx++] = TLS_RSA_WITH_RABBIT_CBC_SHA;
+    }
+#endif
+
+    suites->suiteSz = idx;
+}
+
+
+int InitSSL(SSL* ssl, SSL_CTX* ctx)
+{
+    int  ret;
+    byte havePSK = 0;
+
+    ssl->ctx     = ctx; /* only for passing to calls, options could change */
+    ssl->version = ctx->method->version;
+    ssl->suites  = ctx->suites;
+
+#ifdef HAVE_LIBZ
+    ssl->didStreamInit = 0;
+#endif
+   
+    ssl->buffers.certificate.buffer   = 0;
+    ssl->buffers.key.buffer           = 0;
+    ssl->buffers.inputBuffer.length   = 0;
+    ssl->buffers.inputBuffer.idx      = 0;
+    ssl->buffers.inputBuffer.buffer = ssl->buffers.inputBuffer.staticBuffer;
+    ssl->buffers.inputBuffer.bufferSize  = STATIC_BUFFER_LEN;
+    ssl->buffers.inputBuffer.dynamicFlag = 0;
+    ssl->buffers.outputBuffer.length  = 0;
+    ssl->buffers.outputBuffer.idx     = 0;
+    ssl->buffers.outputBuffer.buffer = ssl->buffers.outputBuffer.staticBuffer;
+    ssl->buffers.outputBuffer.bufferSize  = STATIC_BUFFER_LEN;
+    ssl->buffers.outputBuffer.dynamicFlag = 0;
+    ssl->buffers.domainName.buffer    = 0;
+    ssl->buffers.serverDH_P.buffer    = 0;
+    ssl->buffers.serverDH_G.buffer    = 0;
+    ssl->buffers.serverDH_Pub.buffer  = 0;
+    ssl->buffers.serverDH_Priv.buffer = 0;
+    ssl->buffers.clearOutputBuffer.buffer  = 0;
+    ssl->buffers.clearOutputBuffer.length  = 0;
+    ssl->buffers.prevSent                  = 0;
+    ssl->buffers.plainSz                   = 0;
+
+    if ( (ret = InitRng(&ssl->rng)) )
+        return ret;
+
+    InitMd5(&ssl->hashMd5);
+    InitSha(&ssl->hashSha);
+    InitRsaKey(&ssl->peerRsaKey, ctx->heap);
+
+    ssl->peerRsaKeyPresent = 0;
+    ssl->options.side      = ctx->method->side;
+    ssl->options.downgrade = ctx->method->downgrade;
+    ssl->error = 0;
+    ssl->options.connReset = 0;
+    ssl->options.isClosed  = 0;
+    ssl->options.closeNotify  = 0;
+    ssl->options.sentNotify   = 0;
+    ssl->options.usingCompression = 0;
+    ssl->options.haveDH    = ctx->haveDH;
+    ssl->options.haveNTRU  = ctx->haveNTRU;
+    ssl->options.havePeerCert = 0; 
+    ssl->options.usingPSK_cipher = 0;
+    ssl->options.sendAlertState = 0;
+#ifndef NO_PSK
+    havePSK = ctx->havePSK;
+    ssl->options.havePSK   = ctx->havePSK;
+    ssl->options.client_psk_cb = ctx->client_psk_cb;
+    ssl->options.server_psk_cb = ctx->server_psk_cb;
+#endif /* NO_PSK */
+
+    ssl->options.serverState = NULL_STATE;
+    ssl->options.clientState = NULL_STATE;
+    ssl->options.connectState = CONNECT_BEGIN;
+    ssl->options.acceptState  = ACCEPT_BEGIN; 
+    ssl->options.handShakeState  = NULL_STATE; 
+    ssl->options.processReply = doProcessInit;
+
+#ifdef CYASSL_DTLS
+    ssl->keys.dtls_sequence_number       = 0;
+    ssl->keys.dtls_peer_sequence_number  = 0;
+    ssl->keys.dtls_handshake_number      = 0;
+    ssl->keys.dtls_epoch      = 0;
+    ssl->keys.dtls_peer_epoch = 0;
+#endif
+    ssl->keys.encryptionOn = 0;     /* initially off */
+    ssl->options.sessionCacheOff      = ctx->sessionCacheOff;
+    ssl->options.sessionCacheFlushOff = ctx->sessionCacheFlushOff;
+
+    ssl->options.verifyPeer = ctx->verifyPeer;
+    ssl->options.verifyNone = ctx->verifyNone;
+    ssl->options.failNoCert = ctx->failNoCert;
+    ssl->options.sendVerify = ctx->sendVerify;
+    
+    ssl->options.resuming = 0;
+    ssl->hmac = Hmac;         /* default to SSLv3 */
+    ssl->heap = ctx->heap;    /* defaults to self */
+    ssl->options.tls    = 0;
+    ssl->options.tls1_1 = 0;
+    ssl->options.dtls   = 0;
+    ssl->options.partialWrite  = ctx->partialWrite;
+    ssl->options.quietShutdown = ctx->quietShutdown;
+
+    /* SSL_CTX still owns certificate, key, and caList buffers */
+    ssl->buffers.certificate = ctx->certificate;
+    ssl->buffers.key = ctx->privateKey;
+    ssl->caList = ctx->caList;
+
+#ifdef OPENSSL_EXTRA
+    ssl->peerCert.issuer.sz    = 0;
+    ssl->peerCert.subject.sz   = 0;
+#endif
+    
+    /* make sure server has cert and key unless using PSK */
+    if (ssl->options.side == SERVER_END && !havePSK)
+        if (!ssl->buffers.certificate.buffer || !ssl->buffers.key.buffer)
+            return NO_PRIVATE_KEY;
+
+#ifndef NO_PSK
+    ssl->arrays.client_identity[0] = 0;
+    if (ctx->server_hint[0])   /* set in CTX */
+        XSTRNCPY(ssl->arrays.server_hint, ctx->server_hint, MAX_PSK_ID_LEN);
+    else
+        ssl->arrays.server_hint[0] = 0;
+#endif /* NO_PSK */
+
+#ifdef CYASSL_CALLBACKS
+    ssl->hsInfoOn = 0;
+    ssl->toInfoOn = 0;
+#endif
+
+    /* make sure server has DH parms, and add PSK if there, add NTRU too */
+    if (!ssl->ctx->suites.setSuites) {    /* trust user override */
+        if (ssl->options.side == SERVER_END) 
+            InitSuites(&ssl->suites, ssl->version,ssl->options.haveDH, havePSK,
+                       ssl->options.haveNTRU);
+        else 
+            InitSuites(&ssl->suites, ssl->version, TRUE, havePSK,
+                       ssl->options.haveNTRU);
+    }
+
+    ssl->rfd = -1;   /* set to invalid descriptor */
+    ssl->wfd = -1;
+    ssl->biord = 0;
+    ssl->biowr = 0;
+
+    ssl->IOCB_ReadCtx  = &ssl->rfd;   /* prevent invalid pointer acess if not */
+    ssl->IOCB_WriteCtx = &ssl->wfd;   /* correctly set */
+
+#ifdef SESSION_CERTS
+    ssl->session.chain.count = 0;
+#endif
+
+    ssl->cipher.ssl = ssl;
+
+    return 0;
+}
+
+
+int BIO_free(BIO*);  /* cyassl_int doesn't have */
+
+
+/* In case holding SSL object in array and don't want to free actual ssl */
+void SSL_ResourceFree(SSL* ssl)
+{
+    XFREE(ssl->buffers.serverDH_Priv.buffer, ssl->heap, DYNAMIC_TYPE_DH);
+    XFREE(ssl->buffers.serverDH_Pub.buffer, ssl->heap, DYNAMIC_TYPE_DH);
+    XFREE(ssl->buffers.serverDH_G.buffer, ssl->heap, DYNAMIC_TYPE_DH);
+    XFREE(ssl->buffers.serverDH_P.buffer, ssl->heap, DYNAMIC_TYPE_DH);
+    XFREE(ssl->buffers.domainName.buffer, ssl->heap, DYNAMIC_TYPE_DOMAIN);
+    FreeRsaKey(&ssl->peerRsaKey);
+    if (ssl->buffers.inputBuffer.dynamicFlag)
+        ShrinkInputBuffer(ssl, FORCED_FREE);
+    if (ssl->buffers.outputBuffer.dynamicFlag)
+        ShrinkOutputBuffer(ssl);
+#if defined(OPENSSL_EXTRA) || defined(GOAHEAD_WS)
+    BIO_free(ssl->biord);
+    if (ssl->biord != ssl->biowr)        /* in case same as write */
+        BIO_free(ssl->biowr);
+#endif
+#ifdef HAVE_LIBZ
+    FreeStreams(ssl);
+#endif
+}
+
+
+void FreeSSL(SSL* ssl)
+{
+    SSL_ResourceFree(ssl);
+    XFREE(ssl, ssl->heap, DYNAMIC_TYPE_SSL);
+}
+
+
+ProtocolVersion MakeSSLv3(void)
+{
+    ProtocolVersion pv;
+    pv.major = SSLv3_MAJOR;
+    pv.minor = SSLv3_MINOR;
+
+    return pv;
+}
+
+
+#ifdef CYASSL_DTLS
+
+ProtocolVersion MakeDTLSv1(void)
+{
+    ProtocolVersion pv;
+    pv.major = DTLS_MAJOR;
+    pv.minor = DTLS_MINOR;
+
+    return pv;
+}
+
+#endif /* CYASSL_DTLS */
+
+
+
+
+#ifdef USE_WINDOWS_API 
+
+    timer_d Timer(void)
+    {
+        static int           init = 0;
+        static LARGE_INTEGER freq;
+        LARGE_INTEGER        count;
+    
+        if (!init) {
+            QueryPerformanceFrequency(&freq);
+            init = 1;
+        }
+
+        QueryPerformanceCounter(&count);
+
+        return (double)count.QuadPart / freq.QuadPart;
+    }
+
+
+    word32 LowResTimer(void)
+    {
+        return (word32)Timer();
+    }
+
+
+#elif defined(THREADX)
+
+    #include "rtptime.h"
+
+    word32 LowResTimer(void)
+    {
+        return (word32)rtp_get_system_sec();
+    }
+
+
+#elif defined(MICRIUM)
+
+    word32 LowResTimer(void)
+    {
+        NET_SECURE_OS_TICK  clk;
+
+        #if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
+            clk = NetSecure_OS_TimeGet();
+        #endif
+        return (word32)clk;
+    }
+
+#elif defined(USER_TICKS)
+
+    word32 LowResTimer(void)
+    {
+        /*
+        write your own clock tick function if don't want time(0)
+        needs second accuracy but doesn't have to correlated to EPOCH
+        */
+    }
+
+#else /* !USE_WINDOWS_API && !THREADX && !MICRIUM && !USER_TICKS */
+
+    #include <time.h>
+
+    word32 LowResTimer(void)
+    {
+        return time(0); 
+    }
+
+
+#endif /* USE_WINDOWS_API */
+
+
+/* add output to md5 and sha handshake hashes, exclude record header */
+static void HashOutput(SSL* ssl, const byte* output, int sz, int ivSz)
+{
+    const byte* buffer = output + RECORD_HEADER_SZ + ivSz;
+    sz -= RECORD_HEADER_SZ;
+    
+#ifdef CYASSL_DTLS
+    if (ssl->options.dtls) {
+        buffer += DTLS_RECORD_EXTRA;
+        sz     -= DTLS_RECORD_EXTRA;
+    }
+#endif
+
+    Md5Update(&ssl->hashMd5, buffer, sz);
+    ShaUpdate(&ssl->hashSha, buffer, sz);
+}
+
+
+/* add input to md5 and sha handshake hashes, include handshake header */
+static void HashInput(SSL* ssl, const byte* input, int sz)
+{
+    const byte* buffer = input - HANDSHAKE_HEADER_SZ;
+    sz += HANDSHAKE_HEADER_SZ;
+    
+#ifdef CYASSL_DTLS
+    if (ssl->options.dtls) {
+        buffer -= DTLS_HANDSHAKE_EXTRA;
+        sz     += DTLS_HANDSHAKE_EXTRA;
+    }
+#endif
+
+    Md5Update(&ssl->hashMd5, buffer, sz);
+    ShaUpdate(&ssl->hashSha, buffer, sz);
+}
+
+
+/* add record layer header for message */
+static void AddRecordHeader(byte* output, word32 length, byte type, SSL* ssl)
+{
+    RecordLayerHeader* rl;
+  
+    /* record layer header */
+    rl = (RecordLayerHeader*)output;
+    rl->type    = type;
+    rl->version = ssl->version;           /* type and version same in each */
+
+    if (!ssl->options.dtls)
+        c16toa((word16)length, rl->length);
+    else {
+#ifdef CYASSL_DTLS
+        DtlsRecordLayerHeader* dtls;
+    
+        /* dtls record layer header extensions */
+        dtls = (DtlsRecordLayerHeader*)output;
+        c16toa(ssl->keys.dtls_epoch, dtls->epoch);
+        c32to48(ssl->keys.dtls_sequence_number++, dtls->sequence_number);
+        c16toa((word16)length, dtls->length);
+#endif
+    }
+}
+
+
+/* add handshake header for message */
+static void AddHandShakeHeader(byte* output, word32 length, byte type, SSL* ssl)
+{
+    HandShakeHeader* hs;
+ 
+    /* handshake header */
+    hs = (HandShakeHeader*)output;
+    hs->type = type;
+    c32to24(length, hs->length);         /* type and length same for each */
+#ifdef CYASSL_DTLS
+    if (ssl->options.dtls) {
+        DtlsHandShakeHeader* dtls;
+    
+        /* dtls handshake header extensions */
+        dtls = (DtlsHandShakeHeader*)output;
+        c16toa(ssl->keys.dtls_handshake_number++, dtls->message_seq);
+        c32to24(0, dtls->fragment_offset);
+        c32to24(length, dtls->fragment_length);
+    }
+#endif
+}
+
+
+/* add both headers for handshake message */
+static void AddHeaders(byte* output, word32 length, byte type, SSL* ssl)
+{
+    if (!ssl->options.dtls) {
+        AddRecordHeader(output, length + HANDSHAKE_HEADER_SZ, handshake, ssl);
+        AddHandShakeHeader(output + RECORD_HEADER_SZ, length, type, ssl);
+    }
+    else  {
+        AddRecordHeader(output, length+DTLS_HANDSHAKE_HEADER_SZ, handshake,ssl);
+        AddHandShakeHeader(output + DTLS_RECORD_HEADER_SZ, length, type, ssl);
+    }
+}
+
+
+static int Receive(SSL* ssl, byte* buf, word32 sz, int flags)
+{
+    int recvd;
+
+retry:
+    recvd = ssl->ctx->CBIORecv((char *)buf, (int)sz, ssl->IOCB_ReadCtx);
+    if (recvd < 0)
+        switch (recvd) {
+            case IO_ERR_GENERAL:        /* general/unknown error */
+                return -1;
+
+            case IO_ERR_WANT_READ:      /* want read, would block */
+                return WANT_READ;
+
+            case IO_ERR_CONN_RST:       /* connection reset */
+                ssl->options.connReset = 1;
+                return -1;
+
+            case IO_ERR_ISR:            /* interrupt */
+                /* see if we got our timeout */
+                #ifdef CYASSL_CALLBACKS
+                    if (ssl->toInfoOn) {
+                        struct itimerval timeout;
+                        getitimer(ITIMER_REAL, &timeout);
+                        if (timeout.it_value.tv_sec == 0 && 
+                                                timeout.it_value.tv_usec == 0) {
+                            XSTRNCPY(ssl->timeoutInfo.timeoutName,
+                                    "recv() timeout", MAX_TIMEOUT_NAME_SZ);
+                            return 0;
+                        }
+                    }
+                #endif
+                goto retry;
+
+            case IO_ERR_CONN_CLOSE:     /* peer closed connection */
+                ssl->options.isClosed = 1;
+                return -1;
+        }
+
+    return recvd;
+}
+
+
+/* Switch dynamic output buffer back to static, buffer is assumed clear */
+void ShrinkOutputBuffer(SSL* ssl)
+{
+    CYASSL_MSG("Shrinking output buffer\n");
+    XFREE(ssl->buffers.outputBuffer.buffer, ssl->heap, DYNAMIC_TYPE_OUT_BUFFER);
+    ssl->buffers.outputBuffer.buffer = ssl->buffers.outputBuffer.staticBuffer;
+    ssl->buffers.outputBuffer.bufferSize  = STATIC_BUFFER_LEN;
+    ssl->buffers.outputBuffer.dynamicFlag = 0;
+}
+
+
+/* Switch dynamic input buffer back to static, keep any remaining input */
+/* forced free means cleaning up */
+void ShrinkInputBuffer(SSL* ssl, int forcedFree)
+{
+    int usedLength = ssl->buffers.inputBuffer.length -
+                     ssl->buffers.inputBuffer.idx;
+    if (!forcedFree && usedLength > STATIC_BUFFER_LEN)
+        return;
+
+    CYASSL_MSG("Shrinking input buffer\n");
+
+    if (!forcedFree && usedLength)
+        XMEMCPY(ssl->buffers.inputBuffer.staticBuffer,
+               ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx,
+               usedLength);
+
+    XFREE(ssl->buffers.inputBuffer.buffer, ssl->heap, DYNAMIC_TYPE_IN_BUFFER);
+    ssl->buffers.inputBuffer.buffer = ssl->buffers.inputBuffer.staticBuffer;
+    ssl->buffers.inputBuffer.bufferSize  = STATIC_BUFFER_LEN;
+    ssl->buffers.inputBuffer.dynamicFlag = 0;
+    ssl->buffers.inputBuffer.idx = 0;
+    ssl->buffers.inputBuffer.length = usedLength;
+}
+
+
+int SendBuffered(SSL* ssl)
+{
+    while (ssl->buffers.outputBuffer.length > 0) {
+        int sent = ssl->ctx->CBIOSend((char*)ssl->buffers.outputBuffer.buffer +
+                                      ssl->buffers.outputBuffer.idx,
+                                      (int)ssl->buffers.outputBuffer.length,
+                                      ssl->IOCB_WriteCtx);
+        if (sent < 0) {
+            switch (sent) {
+
+                case IO_ERR_WANT_WRITE:        /* would block */
+                    return WANT_WRITE;
+
+                case IO_ERR_CONN_RST:          /* connection reset */
+                    ssl->options.connReset = 1;
+                    break;
+
+                case IO_ERR_ISR:               /* interrupt */
+                    /* see if we got our timeout */
+                    #ifdef CYASSL_CALLBACKS
+                        if (ssl->toInfoOn) {
+                            struct itimerval timeout;
+                            getitimer(ITIMER_REAL, &timeout);
+                            if (timeout.it_value.tv_sec == 0 && 
+                                                timeout.it_value.tv_usec == 0) {
+                                XSTRNCPY(ssl->timeoutInfo.timeoutName,
+                                        "send() timeout", MAX_TIMEOUT_NAME_SZ);
+                                return WANT_WRITE;
+                            }
+                        }
+                    #endif
+                    continue;
+
+                case IO_ERR_CONN_CLOSE: /* epipe / conn closed, same as reset */
+                    ssl->options.connReset = 1;
+                    break;
+            }
+
+            return SOCKET_ERROR_E;
+        }
+
+        ssl->buffers.outputBuffer.idx += sent;
+        ssl->buffers.outputBuffer.length -= sent;
+    }
+      
+    ssl->buffers.outputBuffer.idx = 0;
+
+    if (ssl->buffers.outputBuffer.dynamicFlag)
+        ShrinkOutputBuffer(ssl);
+
+    return 0;
+}
+
+
+/* Grow the output buffer, should only be to send cert, should be blank */
+static INLINE int GrowOutputBuffer(SSL* ssl, int size)
+{
+    byte* tmp = (byte*) XMALLOC(size + ssl->buffers.outputBuffer.length,
+                                ssl->heap, DYNAMIC_TYPE_OUT_BUFFER);
+    CYASSL_MSG("growing output buffer\n");
+   
+    if (!tmp) return -1;
+
+    if (ssl->buffers.outputBuffer.length)
+        XMEMCPY(tmp, ssl->buffers.outputBuffer.buffer,
+               ssl->buffers.outputBuffer.length);
+
+    if (ssl->buffers.outputBuffer.dynamicFlag)
+        XFREE(ssl->buffers.outputBuffer.buffer, ssl->heap,
+              DYNAMIC_TYPE_OUT_BUFFER);
+    ssl->buffers.outputBuffer.dynamicFlag = 1;
+    ssl->buffers.outputBuffer.buffer = tmp;
+    ssl->buffers.outputBuffer.bufferSize = size +
+                                           ssl->buffers.outputBuffer.length; 
+    return 0;
+}
+
+
+/* Grow the input buffer, should only be to read cert or big app data */
+static INLINE int GrowInputBuffer(SSL* ssl, int size, int usedLength)
+{
+    byte* tmp = (byte*) XMALLOC(size + usedLength, ssl->heap,
+                                DYNAMIC_TYPE_IN_BUFFER);
+    CYASSL_MSG("growing input buffer\n");
+   
+    if (!tmp) return -1;
+
+    if (usedLength)
+        XMEMCPY(tmp, ssl->buffers.inputBuffer.buffer +
+                    ssl->buffers.inputBuffer.idx, usedLength);
+
+    if (ssl->buffers.inputBuffer.dynamicFlag)
+        XFREE(ssl->buffers.inputBuffer.buffer,ssl->heap,DYNAMIC_TYPE_IN_BUFFER);
+
+    ssl->buffers.inputBuffer.dynamicFlag = 1;
+    ssl->buffers.inputBuffer.buffer = tmp;
+    ssl->buffers.inputBuffer.bufferSize = size + usedLength;
+    ssl->buffers.inputBuffer.idx    = 0;
+    ssl->buffers.inputBuffer.length = usedLength;
+
+    return 0;
+}
+
+
+/* check avalaible size into outbut buffer */
+static INLINE int CheckAvalaibleSize(SSL *ssl, int size)
+{
+    if ((word32)size > ssl->buffers.outputBuffer.bufferSize)
+        if (GrowOutputBuffer(ssl, size) < 0)
+            return MEMORY_E;
+
+    if (ssl->buffers.outputBuffer.bufferSize - ssl->buffers.outputBuffer.length
+                                             < (word32)size) {
+        if (SendBuffered(ssl) == SOCKET_ERROR_E)
+            return SOCKET_ERROR_E;
+        if (ssl->buffers.outputBuffer.bufferSize -
+                                ssl->buffers.outputBuffer.length < (word32)size)
+            return WANT_WRITE;
+    }
+    return 0;
+}
+
+/* do all verify and sanity checks on record header */
+static int GetRecordHeader(SSL* ssl, const byte* input, word32* inOutIdx,
+                           RecordLayerHeader* rh, word16 *size)
+{
+    if (!ssl->options.dtls) {
+        XMEMCPY(rh, input + *inOutIdx, RECORD_HEADER_SZ);
+        *inOutIdx += RECORD_HEADER_SZ;
+        ato16(rh->length, size);
+    }
+    else {
+#ifdef CYASSL_DTLS
+        /* type and version in same sport */
+        XMEMCPY(rh, input + *inOutIdx, ENUM_LEN + VERSION_SZ);
+        *inOutIdx += ENUM_LEN + VERSION_SZ;
+        *inOutIdx += 4;  /* skip epoch and first 2 seq bytes for now */
+        ato32(input + *inOutIdx, &ssl->keys.dtls_peer_sequence_number);
+        *inOutIdx += 4;  /* advance past rest of seq */
+        ato16(input + *inOutIdx, size);
+        *inOutIdx += LENGTH_SZ;
+#endif
+    }
+
+    /* catch version mismatch */
+    if (rh->version.major != ssl->version.major || 
+        rh->version.minor != ssl->version.minor) {
+        
+        if (ssl->options.side == SERVER_END && ssl->options.downgrade == 1 &&
+            ssl->options.acceptState == ACCEPT_BEGIN)
+            ;                                  /* haven't negotiated yet */
+        else
+            return VERSION_ERROR;              /* only use requested version */
+    }
+
+    /* record layer length check */
+    if (*size > (MAX_RECORD_SIZE + MAX_COMP_EXTRA + MAX_MSG_EXTRA))
+        return LENGTH_ERROR;
+
+    /* verify record type here as well */
+    switch ((enum ContentType)rh->type) {
+        case handshake:
+        case change_cipher_spec:
+        case application_data:
+        case alert:
+            break;
+        default:
+            return UNKNOWN_RECORD_TYPE;
+    }
+
+    return 0;
+}
+
+
+static int GetHandShakeHeader(SSL* ssl, const byte* input, word32* inOutIdx,
+                              byte *type, word32 *size)
+{
+    const byte *ptr = input + *inOutIdx;
+    *inOutIdx += HANDSHAKE_HEADER_SZ;
+    
+#ifdef CYASSL_DTLS
+    if (ssl->options.dtls)
+        *inOutIdx += DTLS_HANDSHAKE_EXTRA;
+#endif
+
+    *type = ptr[0];
+    c24to32(&ptr[1], size);
+
+    return 0;
+}
+
+
+/* fill with MD5 pad size since biggest required */
+static const byte PAD1[PAD_MD5] = 
+                              { 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+                                0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36
+                              };
+static const byte PAD2[PAD_MD5] =
+                              { 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
+                                0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c
+                              };
+
+/* calculate MD5 hash for finished */
+static void BuildMD5(SSL* ssl, Hashes* hashes, const byte* sender)
+{
+    byte md5_result[MD5_DIGEST_SIZE];
+
+    /* make md5 inner */    
+    Md5Update(&ssl->hashMd5, sender, SIZEOF_SENDER);
+    Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN);
+    Md5Update(&ssl->hashMd5, PAD1, PAD_MD5);
+    Md5Final(&ssl->hashMd5, md5_result);
+
+    /* make md5 outer */
+    Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN);
+    Md5Update(&ssl->hashMd5, PAD2, PAD_MD5);
+    Md5Update(&ssl->hashMd5, md5_result, MD5_DIGEST_SIZE);
+
+    Md5Final(&ssl->hashMd5, hashes->md5);
+}
+
+
+/* calculate SHA hash for finished */
+static void BuildSHA(SSL* ssl, Hashes* hashes, const byte* sender)
+{
+    byte sha_result[SHA_DIGEST_SIZE];
+
+    /* make sha inner */
+    ShaUpdate(&ssl->hashSha, sender, SIZEOF_SENDER);
+    ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN);
+    ShaUpdate(&ssl->hashSha, PAD1, PAD_SHA);
+    ShaFinal(&ssl->hashSha, sha_result);
+
+    /* make sha outer */
+    ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN);
+    ShaUpdate(&ssl->hashSha, PAD2, PAD_SHA);
+    ShaUpdate(&ssl->hashSha, sha_result, SHA_DIGEST_SIZE);
+
+    ShaFinal(&ssl->hashSha, hashes->sha);
+}
+
+
+static void BuildFinished(SSL* ssl, Hashes* hashes, const byte* sender)
+{
+    /* store current states, building requires get_digest which resets state */
+    Md5 md5 = ssl->hashMd5;
+    Sha sha = ssl->hashSha;
+
+    if (ssl->options.tls)
+        BuildTlsFinished(ssl, hashes, sender);
+    else {
+        BuildMD5(ssl, hashes, sender);
+        BuildSHA(ssl, hashes, sender);
+    }
+    
+    /* restore */
+    ssl->hashMd5 = md5;
+    ssl->hashSha = sha;
+}
+
+
+static int DoCertificate(SSL* ssl, byte* input, word32* inOutIdx)
+{
+    word32 listSz, i = *inOutIdx;
+    int    ret = 0;
+    int    firstTime = 1;  /* peer's is at front */
+    char   domain[ASN_NAME_MAX];
+
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("Certificate", &ssl->handShakeInfo);
+        if (ssl->toInfoOn) AddLateName("Certificate", &ssl->timeoutInfo);
+    #endif
+    c24to32(&input[i], &listSz);
+    i += CERT_HEADER_SZ;
+    
+    while (listSz && ret == 0) {
+        /* cert size */
+        buffer      myCert;
+        word32      certSz;
+        DecodedCert dCert;
+        word32      idx = 0;
+
+        c24to32(&input[i], &certSz);
+        i += CERT_HEADER_SZ;
+        
+        myCert.length = certSz;
+        myCert.buffer = input + i;
+        i += certSz;
+
+        listSz -= certSz + CERT_HEADER_SZ;
+
+#ifdef SESSION_CERTS
+        if (ssl->session.chain.count < MAX_CHAIN_DEPTH &&
+                                       myCert.length < MAX_X509_SIZE) {
+            ssl->session.chain.certs[ssl->session.chain.count].length =
+                 myCert.length;
+            XMEMCPY(ssl->session.chain.certs[ssl->session.chain.count].buffer,
+                   myCert.buffer, myCert.length);
+            ssl->session.chain.count++;
+        } else {
+            CYASSL_MSG("Couldn't store chain cert for session");
+        }
+#endif
+
+        InitDecodedCert(&dCert, myCert.buffer, ssl->heap);
+        ret = ParseCertRelative(&dCert, myCert.length, CERT_TYPE,
+                                !ssl->options.verifyNone, ssl->caList);
+
+        if (!firstTime) {
+            FreeDecodedCert(&dCert);
+            continue;
+        }
+
+        /* get rest of peer info in case user wants to continue */
+        if (ret != 0) {
+            if (!(ret == ASN_BEFORE_DATE_E || ret == ASN_AFTER_DATE_E ||
+                                              ret == ASN_SIG_CONFIRM_E)) {
+                FreeDecodedCert(&dCert);
+                continue;
+            }
+        }
+        
+        /* first one has peer's key */
+        firstTime = 0;
+
+        ssl->options.havePeerCert = 1;
+        /* set X509 format */
+#ifdef OPENSSL_EXTRA
+        ssl->peerCert.issuer.sz    = (int)XSTRLEN(dCert.issuer) + 1;
+        XSTRNCPY(ssl->peerCert.issuer.name, dCert.issuer, ASN_NAME_MAX);
+        ssl->peerCert.subject.sz   = (int)XSTRLEN(dCert.subject) + 1;
+        XSTRNCPY(ssl->peerCert.subject.name, dCert.subject, ASN_NAME_MAX);
+#endif    
+
+        XMEMCPY(domain, dCert.subjectCN, dCert.subjectCNLen);
+        domain[dCert.subjectCNLen] = '\0';
+
+        if (!ssl->options.verifyNone && ssl->buffers.domainName.buffer)
+            if (XSTRNCMP((char*)ssl->buffers.domainName.buffer,
+                        dCert.subjectCN,
+                        ssl->buffers.domainName.length - 1)) {
+                ret = DOMAIN_NAME_MISMATCH;   /* try to get peer key still */
+            }
+
+        /* decode peer key */
+        if (dCert.keyOID == RSAk) {
+            if (RsaPublicKeyDecode(dCert.publicKey, &idx,
+                               &ssl->peerRsaKey, dCert.pubKeySize) != 0) {
+                ret = PEER_KEY_ERROR;
+                FreeDecodedCert(&dCert);
+                continue;
+            }
+            ssl->peerRsaKeyPresent = 1;
+        }
+#ifdef HAVE_NTRU
+        else if (dCert.keyOID == NTRUk) {
+            if (dCert.pubKeySize > sizeof(ssl->peerNtruKey)) {
+                ret = PEER_KEY_ERROR;
+                FreeDecodedCert(&dCert);
+                continue;
+            }
+            XMEMCPY(ssl->peerNtruKey, dCert.publicKey, dCert.pubKeySize);
+            ssl->peerNtruKeyLen = (word16)dCert.pubKeySize;
+            ssl->peerNtruKeyPresent = 1;
+        }
+#endif
+
+        FreeDecodedCert(&dCert);
+    }
+
+    if (ret == 0 && ssl->options.side == CLIENT_END)
+        ssl->options.serverState = SERVER_CERT_COMPLETE;
+
+    if (ret != 0) {
+        if (!ssl->options.verifyNone) {
+            int why = bad_certificate;
+            if (ret == ASN_AFTER_DATE_E || ret == ASN_BEFORE_DATE_E)
+                why = certificate_expired;
+            if (ssl->ctx->verifyCallback) {
+                int            ok;
+                X509_STORE_CTX store;
+
+                store.error = ret;
+                store.error_depth = 1;
+                store.domain = domain;
+#ifdef OPENSSL_EXTRA
+                store.current_cert = &ssl->peerCert;
+#else
+                store.current_cert = NULL;
+#endif
+                ok = ssl->ctx->verifyCallback(0, &store);
+                if (ok)
+                    ret = 0;
+            }
+            if (ret != 0) {
+                SendAlert(ssl, alert_fatal, why);   /* try to send */
+                ssl->options.isClosed = 1;
+            }
+        }
+        ssl->error = ret;
+    }
+
+    *inOutIdx = i;
+    return ret;
+}
+
+
+int DoFinished(SSL* ssl, const byte* input, word32* inOutIdx, int sniff)
+{
+    byte   verifyMAC[SHA_DIGEST_SIZE];
+    int    finishedSz = ssl->options.tls ? TLS_FINISHED_SZ : FINISHED_SZ;
+    int    headerSz = HANDSHAKE_HEADER_SZ;
+    word32 macSz = finishedSz + HANDSHAKE_HEADER_SZ,
+           idx = *inOutIdx,
+           padSz = ssl->keys.encryptSz - HANDSHAKE_HEADER_SZ - finishedSz -
+                   ssl->specs.hash_size;
+    const byte* mac;
+
+    #ifdef CYASSL_DTLS
+        if (ssl->options.dtls) {
+            headerSz += DTLS_HANDSHAKE_EXTRA;
+            macSz    += DTLS_HANDSHAKE_EXTRA;
+            padSz    -= DTLS_HANDSHAKE_EXTRA;
+        }
+    #endif
+
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("Finished", &ssl->handShakeInfo);
+        if (ssl->toInfoOn) AddLateName("Finished", &ssl->timeoutInfo);
+    #endif
+    if (sniff == NO_SNIFF) {
+        if (XMEMCMP(input + idx, &ssl->verifyHashes, finishedSz))
+            return VERIFY_FINISHED_ERROR;
+    }
+
+    ssl->hmac(ssl, verifyMAC, input + idx - headerSz, macSz,
+         handshake, 1);
+    idx += finishedSz;
+
+    /* read mac and fill */
+    mac = input + idx;
+    idx += ssl->specs.hash_size;
+
+    if (ssl->options.tls1_1 && ssl->specs.cipher_type == block)
+        padSz -= ssl->specs.block_size;
+
+    idx += padSz;
+
+    /* verify mac */
+    if (XMEMCMP(mac, verifyMAC, ssl->specs.hash_size))
+        return VERIFY_MAC_ERROR;
+
+    if (ssl->options.side == CLIENT_END) {
+        ssl->options.serverState = SERVER_FINISHED_COMPLETE;
+        if (!ssl->options.resuming)
+            ssl->options.handShakeState = HANDSHAKE_DONE;
+    }
+    else {
+        ssl->options.clientState = CLIENT_FINISHED_COMPLETE;
+        if (ssl->options.resuming)
+            ssl->options.handShakeState = HANDSHAKE_DONE;
+    }
+
+    *inOutIdx = idx;
+    return 0;
+}
+
+
+static int DoHandShakeMsg(SSL* ssl, byte* input, word32* inOutIdx,
+                          word32 totalSz)
+{
+    byte type;
+    word32 size;
+    int ret = 0;
+
+    CYASSL_ENTER("DoHandShakeMsg()");
+
+    if (GetHandShakeHeader(ssl, input, inOutIdx, &type, &size) != 0)
+        return PARSE_ERROR;
+
+    if (*inOutIdx + size > totalSz)
+        return INCOMPLETE_DATA;
+    
+    HashInput(ssl, input + *inOutIdx, size);
+#ifdef CYASSL_CALLBACKS
+    /* add name later, add on record and handshake header  part back on */
+    if (ssl->toInfoOn) {
+        int add = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+        AddPacketInfo(0, &ssl->timeoutInfo, input + *inOutIdx - add,
+                      size + add, ssl->heap);
+        AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo);
+    }
+#endif
+
+    switch (type) {
+
+#ifndef NO_CYASSL_CLIENT
+    case hello_verify_request:
+        CYASSL_MSG("processing hello verify request");
+        ret = DoHelloVerifyRequest(ssl, input,inOutIdx);
+        break;
+            
+    case server_hello:
+        CYASSL_MSG("processing server hello");
+        ret = DoServerHello(ssl, input, inOutIdx);
+        break;
+
+    case certificate_request:
+        CYASSL_MSG("processing certificate request");
+        ret = DoCertificateRequest(ssl, input, inOutIdx);
+        break;
+
+    case server_key_exchange:
+        CYASSL_MSG("processing server key exchange");
+        ret = DoServerKeyExchange(ssl, input, inOutIdx);
+        break;
+#endif
+
+    case certificate:
+        CYASSL_MSG("processing certificate");
+        ret =  DoCertificate(ssl, input, inOutIdx);
+        break;
+
+    case server_hello_done:
+        CYASSL_MSG("processing server hello done");
+        #ifdef CYASSL_CALLBACKS
+            if (ssl->hsInfoOn) 
+                AddPacketName("ServerHelloDone", &ssl->handShakeInfo);
+            if (ssl->toInfoOn)
+                AddLateName("ServerHelloDone", &ssl->timeoutInfo);
+        #endif
+        ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
+        break;
+
+    case finished:
+        CYASSL_MSG("processing finished");
+        ret = DoFinished(ssl, input, inOutIdx, NO_SNIFF);
+        break;
+
+#ifndef NO_CYASSL_SERVER
+    case client_hello:
+        CYASSL_MSG("processing client hello");
+        ret = DoClientHello(ssl, input, inOutIdx, totalSz, size);
+        break;
+
+    case client_key_exchange:
+        CYASSL_MSG("processing client key exchange");
+        ret = DoClientKeyExchange(ssl, input, inOutIdx);
+        break;
+
+    case certificate_verify:
+        CYASSL_MSG("processing certificate verify");
+        ret = DoCertificateVerify(ssl, input, inOutIdx, totalSz);
+        break;
+
+#endif
+
+    default:
+        ret = UNKNOWN_HANDSHAKE_TYPE;
+    }
+
+    CYASSL_LEAVE("DoHandShakeMsg()", ret);
+    return ret;
+}
+
+
+static INLINE void Encrypt(SSL* ssl, byte* out, const byte* input, word32 sz)
+{
+    switch (ssl->specs.bulk_cipher_algorithm) {
+        #ifdef BUILD_ARC4
+            case rc4:
+                Arc4Process(&ssl->encrypt.arc4, out, input, sz);
+                break;
+        #endif
+
+        #ifdef BUILD_DES3
+            case triple_des:
+                Des3_CbcEncrypt(&ssl->encrypt.des3, out, input, sz);
+                break;
+        #endif
+
+        #ifdef BUILD_AES
+            case aes:
+#ifdef CYASSL_AESNI
+                if ((word)input % 16) {
+                    byte buffer[MAX_RECORD_SIZE + MAX_COMP_EXTRA+MAX_MSG_EXTRA];
+                    XMEMCPY(buffer, input, sz);
+                    AesCbcEncrypt(&ssl->encrypt.aes, buffer, buffer, sz);
+                    XMEMCPY(out, buffer, sz);
+                    break;
+                }
+#endif
+                AesCbcEncrypt(&ssl->encrypt.aes, out, input, sz);
+                break;
+        #endif
+
+        #ifdef BUILD_HC128
+            case hc128:
+                Hc128_Process(&ssl->encrypt.hc128, out, input, sz);
+                break;
+        #endif
+
+        #ifdef BUILD_RABBIT
+            case rabbit:
+                RabbitProcess(&ssl->encrypt.rabbit, out, input, sz);
+                break;
+        #endif
+    }
+}
+
+
+static INLINE void Decrypt(SSL* ssl, byte* plain, const byte* input, word32 sz)
+{
+    switch (ssl->specs.bulk_cipher_algorithm) {
+        #ifdef BUILD_ARC4
+            case rc4:
+                Arc4Process(&ssl->decrypt.arc4, plain, input, sz);
+                break;
+        #endif
+
+        #ifdef BUILD_DES3
+            case triple_des:
+                Des3_CbcDecrypt(&ssl->decrypt.des3, plain, input, sz);
+                break;
+        #endif
+
+        #ifdef BUILD_AES
+            case aes:
+                AesCbcDecrypt(&ssl->decrypt.aes, plain, input, sz);
+                break;
+        #endif
+
+        #ifdef BUILD_HC128
+            case hc128:
+                Hc128_Process(&ssl->decrypt.hc128, plain, input, sz);
+                break;
+        #endif
+
+        #ifdef BUILD_RABBIT
+            case rabbit:
+                RabbitProcess(&ssl->decrypt.rabbit, plain, input, sz);
+                break;
+        #endif
+    }
+}
+
+
+/* decrypt input message in place */
+static int DecryptMessage(SSL* ssl, byte* input, word32 sz, word32* idx)
+{
+    Decrypt(ssl, input, input, sz);
+    ssl->keys.encryptSz = sz;
+    if (ssl->options.tls1_1 && ssl->specs.cipher_type == block)
+        *idx += ssl->specs.block_size;  /* go past TLSv1.1 IV */
+
+    return 0;
+}
+
+
+static INLINE word32 GetSEQIncrement(SSL* ssl, int verify)
+{
+    if (verify)
+        return ssl->keys.peer_sequence_number++; 
+    else
+        return ssl->keys.sequence_number++; 
+}
+
+
+int DoApplicationData(SSL* ssl, byte* input, word32* inOutIdx)
+{
+    word32 msgSz   = ssl->keys.encryptSz;
+    word32 pad     = 0, 
+           padByte = 0,
+           idx     = *inOutIdx,
+           digestSz = ssl->specs.hash_size;
+    int    dataSz;
+    int    ivExtra = 0;
+    byte*  rawData = input + idx;  /* keep current  for hmac */
+#ifdef HAVE_LIBZ
+    byte   decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
+#endif
+
+    byte        verify[SHA_DIGEST_SIZE];
+    const byte* mac;
+
+    if (ssl->specs.cipher_type == block) {
+        if (ssl->options.tls1_1)
+            ivExtra = ssl->specs.block_size;
+        pad = *(input + idx + msgSz - ivExtra - 1);
+        padByte = 1;
+    }
+
+    dataSz = msgSz - ivExtra - digestSz - pad - padByte;
+    if (dataSz < 0)
+        return BUFFER_ERROR;
+
+    /* read data */
+    if (dataSz) {
+        int    rawSz   = dataSz;       /* keep raw size for hmac */
+
+        ssl->hmac(ssl, verify, rawData, rawSz, application_data, 1);
+
+#ifdef HAVE_LIBZ
+        byte  decomp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
+        
+        if (ssl->options.usingCompression) {
+            dataSz = DeCompress(ssl, rawData, dataSz, decomp, sizeof(decomp));
+            if (dataSz < 0) return dataSz;
+        }
+#endif
+
+        if (ssl->options.usingCompression)
+            idx += rawSz;
+        else
+            idx += dataSz;
+
+        ssl->buffers.clearOutputBuffer.buffer = rawData;
+        ssl->buffers.clearOutputBuffer.length = dataSz;
+    }
+
+    /* read mac and fill */
+    mac = input + idx;
+    idx += digestSz;
+   
+    idx += pad;
+    if (padByte)
+        idx++;
+
+#ifdef HAVE_LIBZ
+    if (ssl->options.usingCompression)
+        XMEMMOVE(rawData, decomp, dataSz);
+#endif
+
+    /* verify */
+    if (dataSz) {
+        if (XMEMCMP(mac, verify, digestSz))
+            return VERIFY_MAC_ERROR;
+    }
+    else 
+        GetSEQIncrement(ssl, 1);  /* even though no data, increment verify */
+
+    *inOutIdx = idx;
+    return 0;
+}
+
+
+/* process alert, return level */
+static int DoAlert(SSL* ssl, byte* input, word32* inOutIdx, int* type)
+{
+    byte level;
+
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn)
+            AddPacketName("Alert", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            /* add record header back on to info + 2 byte level, data */
+            AddPacketInfo("Alert", &ssl->timeoutInfo, input + *inOutIdx -
+                          RECORD_HEADER_SZ, 2 + RECORD_HEADER_SZ, ssl->heap);
+    #endif
+    level = input[(*inOutIdx)++];
+    *type  = (int)input[(*inOutIdx)++];
+
+    if (*type == close_notify)
+        ssl->options.closeNotify = 1;
+
+    if (ssl->keys.encryptionOn) {
+        int         aSz = ALERT_SIZE;
+        const byte* mac;
+        byte        verify[SHA_DIGEST_SIZE];
+        int         padSz = ssl->keys.encryptSz - aSz - ssl->specs.hash_size;
+        
+        ssl->hmac(ssl, verify, input + *inOutIdx - aSz, aSz, alert, 1);
+
+        /* read mac and fill */
+        mac = input + *inOutIdx;
+        *inOutIdx += (ssl->specs.hash_size + padSz);
+
+        /* verify */
+        if (XMEMCMP(mac, verify, ssl->specs.hash_size))
+            return VERIFY_MAC_ERROR;
+    }
+
+    return level;
+}
+
+static int GetInputData(SSL *ssl, size_t size)
+{
+    int in;
+    int inSz;
+    int maxLength;
+    int usedLength;
+
+    
+    /* check max input length */
+    usedLength = ssl->buffers.inputBuffer.length - ssl->buffers.inputBuffer.idx;
+    maxLength  = ssl->buffers.inputBuffer.bufferSize - usedLength;
+    inSz       = (int)(size - usedLength);      /* from last partial read */
+
+#ifdef CYASSL_DTLS
+    if (ssl->options.dtls)
+        inSz = 1500;       /* read ahead up to MTU */
+#endif
+    
+    if (inSz > maxLength) {
+        if (GrowInputBuffer(ssl, size, usedLength) < 0)
+            return MEMORY_E;
+    }
+           
+    if (inSz <= 0)
+        return BUFFER_ERROR;
+    
+    /* Put buffer data at start if not there */
+    if (usedLength > 0 && ssl->buffers.inputBuffer.idx != 0)
+        XMEMMOVE(ssl->buffers.inputBuffer.buffer,
+                ssl->buffers.inputBuffer.buffer + ssl->buffers.inputBuffer.idx,
+                usedLength);
+    
+    /* remove processed data */
+    ssl->buffers.inputBuffer.idx    = 0;
+    ssl->buffers.inputBuffer.length = usedLength;
+  
+    /* read data from network */
+    do {
+        in = Receive(ssl, 
+                     ssl->buffers.inputBuffer.buffer +
+                     ssl->buffers.inputBuffer.length, 
+                     inSz, 0);
+        if (in == -1)
+            return SOCKET_ERROR_E;
+   
+        if (in == WANT_READ)
+            return WANT_READ;
+        
+        ssl->buffers.inputBuffer.length += in;
+        inSz -= in;
+
+    } while (ssl->buffers.inputBuffer.length < size);
+
+    return 0;
+}
+
+/* process input requests, return 0 is done, 1 is call again to complete, and
+   negative number is error */
+int ProcessReply(SSL* ssl)
+{
+    int    ret, type, readSz;
+    word32 startIdx = 0;
+    byte   b0, b1;
+#ifdef CYASSL_DTLS
+    int    used;
+#endif
+
+    for (;;) {
+        switch ((processReply)ssl->options.processReply) {
+
+        /* in the CYASSL_SERVER case, get the first byte for detecting 
+         * old client hello */
+        case doProcessInit:
+            
+            readSz = RECORD_HEADER_SZ;
+            
+            #ifdef CYASSL_DTLS
+                if (ssl->options.dtls)
+                    readSz = DTLS_RECORD_HEADER_SZ;
+            #endif
+
+            /* get header or return error */
+            if (!ssl->options.dtls) {
+                if ((ret = GetInputData(ssl, readSz)) < 0)
+                    return ret;
+            } else {
+            #ifdef CYASSL_DTLS
+                /* read ahead may already have header */
+                used = ssl->buffers.inputBuffer.length -
+                       ssl->buffers.inputBuffer.idx;
+                if (used < readSz)
+                    if ((ret = GetInputData(ssl, readSz)) < 0)
+                        return ret;
+            #endif
+            }
+
+#ifndef NO_CYASSL_SERVER
+
+            /* see if sending SSLv2 client hello */
+            if ( ssl->options.side == SERVER_END &&
+                 ssl->options.clientState == NULL_STATE &&
+                 ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx]
+                         != handshake) {
+                ssl->options.processReply = runProcessOldClientHello;
+
+                /* how many bytes need ProcessOldClientHello */
+                b0 =
+                ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx++];
+                b1 =
+                ssl->buffers.inputBuffer.buffer[ssl->buffers.inputBuffer.idx++];
+                ssl->curSize = ((b0 & 0x7f) << 8) | b1;
+            }
+            else {
+                ssl->options.processReply = getRecordLayerHeader;
+                continue;
+            }
+
+        /* in the CYASSL_SERVER case, run the old client hello */
+        case runProcessOldClientHello:     
+
+            /* get sz bytes or return error */
+            if (!ssl->options.dtls) {
+                if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
+                    return ret;
+            } else {
+            #ifdef CYASSL_DTLS
+                /* read ahead may already have */
+                used = ssl->buffers.inputBuffer.length -
+                       ssl->buffers.inputBuffer.idx;
+                if (used < ssl->curSize)
+                    if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
+                        return ret;
+            #endif  /* CYASSL_DTLS */
+            }
+
+            ret = ProcessOldClientHello(ssl, ssl->buffers.inputBuffer.buffer,
+                                        &ssl->buffers.inputBuffer.idx,
+                                        ssl->buffers.inputBuffer.length -
+                                        ssl->buffers.inputBuffer.idx,
+                                        ssl->curSize);
+            if (ret < 0)
+                return ret;
+
+            else if (ssl->buffers.inputBuffer.idx ==
+                     ssl->buffers.inputBuffer.length) {
+                ssl->options.processReply = doProcessInit;
+                return 0;
+            }
+
+#endif  /* NO_CYASSL_SERVER */
+
+        /* get the record layer header */
+        case getRecordLayerHeader:
+
+            ret = GetRecordHeader(ssl, ssl->buffers.inputBuffer.buffer,
+                                       &ssl->buffers.inputBuffer.idx,
+                                       &ssl->curRL, &ssl->curSize);
+            if (ret != 0)
+                return ret;
+
+            ssl->options.processReply = getData;
+
+        /* retrieve record layer data */
+        case getData:
+
+            /* get sz bytes or return error */
+            if (!ssl->options.dtls) {
+                if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
+                    return ret;
+            } else {
+#ifdef CYASSL_DTLS
+                /* read ahead may already have */
+                used = ssl->buffers.inputBuffer.length -
+                       ssl->buffers.inputBuffer.idx;
+                if (used < ssl->curSize)
+                    if ((ret = GetInputData(ssl, ssl->curSize)) < 0)
+                        return ret;
+#endif
+            }
+            
+            ssl->options.processReply = runProcessingOneMessage;
+            startIdx = ssl->buffers.inputBuffer.idx;  /* in case > 1 msg per */
+
+        /* the record layer is here */
+        case runProcessingOneMessage:
+
+            if (ssl->keys.encryptionOn)
+                if (DecryptMessage(ssl, ssl->buffers.inputBuffer.buffer + 
+                                        ssl->buffers.inputBuffer.idx,
+                                        ssl->curSize,
+                                        &ssl->buffers.inputBuffer.idx) < 0)
+                    return DECRYPT_ERROR;
+
+            CYASSL_MSG("received record layer msg");
+
+            switch (ssl->curRL.type) {
+                case handshake :
+                    /* debugging in DoHandShakeMsg */
+                    if ((ret = DoHandShakeMsg(ssl, 
+                                              ssl->buffers.inputBuffer.buffer,
+                                              &ssl->buffers.inputBuffer.idx,
+                                              ssl->buffers.inputBuffer.length))
+                                                                           != 0)
+                        return ret;
+                    break;
+
+                case change_cipher_spec:
+                    CYASSL_MSG("got CHANGE CIPHER SPEC");
+                    #ifdef CYASSL_CALLBACKS
+                        if (ssl->hsInfoOn)
+                            AddPacketName("ChangeCipher", &ssl->handShakeInfo);
+                        /* add record header back on info */
+                        if (ssl->toInfoOn) {
+                            AddPacketInfo("ChangeCipher", &ssl->timeoutInfo,
+                                ssl->buffers.inputBuffer.buffer +
+                                ssl->buffers.inputBuffer.idx - RECORD_HEADER_SZ,
+                                1 + RECORD_HEADER_SZ, ssl->heap);
+                            AddLateRecordHeader(&ssl->curRL, &ssl->timeoutInfo);
+                        }
+                    #endif
+                    ssl->buffers.inputBuffer.idx++;
+                    ssl->keys.encryptionOn = 1;
+
+                    #ifdef CYASSL_DTLS
+                        if (ssl->options.dtls)
+                            ssl->keys.dtls_peer_epoch++;
+                    #endif
+
+                    #ifdef HAVE_LIBZ
+                        if (ssl->options.usingCompression)
+                            if ( (ret = InitStreams(ssl)) != 0)
+                                return ret;
+                    #endif
+                    if (ssl->options.resuming && ssl->options.side ==
+                                                                    CLIENT_END)
+                        BuildFinished(ssl, &ssl->verifyHashes, server);
+                    else if (!ssl->options.resuming && ssl->options.side ==
+                                                                    SERVER_END)
+                        BuildFinished(ssl, &ssl->verifyHashes, client);
+                    break;
+
+                case application_data:
+                    CYASSL_MSG("got app DATA");
+                    if ((ret = DoApplicationData(ssl,
+                                                ssl->buffers.inputBuffer.buffer,
+                                               &ssl->buffers.inputBuffer.idx))
+                                                                         != 0) {
+                        CYASSL_ERROR(ret);
+                        return ret;
+                    }
+                    break;
+
+                case alert:
+                    CYASSL_MSG("got ALERT!");
+                    if (DoAlert(ssl, ssl->buffers.inputBuffer.buffer,
+                           &ssl->buffers.inputBuffer.idx, &type) == alert_fatal)
+                        return FATAL_ERROR;
+
+                    /* catch warnings that are handled as errors */
+                    if (type == close_notify)
+                        return ssl->error = ZERO_RETURN;
+                           
+                    if (type == decrypt_error)
+                        return FATAL_ERROR;
+                    break;
+            
+                default:
+                    CYASSL_ERROR(UNKNOWN_RECORD_TYPE);
+                    return UNKNOWN_RECORD_TYPE;
+            }
+
+            ssl->options.processReply = doProcessInit;
+
+            /* input exhausted? */
+            if (ssl->buffers.inputBuffer.idx == ssl->buffers.inputBuffer.length)
+                return 0;
+            /* more messages per record */
+            else if ((ssl->buffers.inputBuffer.idx - startIdx) < ssl->curSize) {
+                #ifdef CYASSL_DTLS
+                    /* read-ahead but dtls doesn't bundle messages per record */
+                    if (ssl->options.dtls) {
+                        ssl->options.processReply = doProcessInit;
+                        continue;
+                    }
+                #endif
+                ssl->options.processReply = runProcessingOneMessage;
+                continue;
+            }
+            /* more records */
+            else {
+                ssl->options.processReply = doProcessInit;
+                continue;
+            }
+        }
+    }
+}
+
+
+int SendChangeCipher(SSL* ssl)
+{
+    byte              *output;
+    int                sendSz = RECORD_HEADER_SZ + ENUM_LEN;
+    int                idx    = RECORD_HEADER_SZ;
+    int                ret;
+
+    #ifdef CYASSL_DTLS
+        if (ssl->options.dtls) {
+            sendSz += DTLS_RECORD_EXTRA;
+            idx    += DTLS_RECORD_EXTRA;
+        }
+    #endif
+
+    /* check for avalaible size */
+    if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
+        return ret;
+
+    /* get ouput buffer */
+    output = ssl->buffers.outputBuffer.buffer + 
+             ssl->buffers.outputBuffer.idx;
+
+    AddRecordHeader(output, 1, change_cipher_spec, ssl);
+
+    output[idx] = 1;             /* turn it on */
+
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("ChangeCipher", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddPacketInfo("ChangeCipher", &ssl->timeoutInfo, output, sendSz,
+                           ssl->heap);
+    #endif
+    ssl->buffers.outputBuffer.length += sendSz;
+    return SendBuffered(ssl);
+}
+
+
+static INLINE const byte* GetMacSecret(SSL* ssl, int verify)
+{
+    if ( (ssl->options.side == CLIENT_END && !verify) ||
+         (ssl->options.side == SERVER_END &&  verify) )
+        return ssl->keys.client_write_MAC_secret;
+    else
+        return ssl->keys.server_write_MAC_secret;
+}
+
+
+static void Hmac(SSL* ssl, byte* digest, const byte* buffer, word32 sz,
+                 int content, int verify)
+{
+    byte   result[SHA_DIGEST_SIZE];                    /* max possible sizes */
+    word32 digestSz = ssl->specs.hash_size;            /* actual sizes */
+    word32 padSz    = ssl->specs.pad_size;
+
+    Md5 md5;
+    Sha sha;
+
+    /* data */
+    byte seq[SEQ_SZ] = { 0x00, 0x00, 0x00, 0x00 };
+    byte conLen[ENUM_LEN + LENGTH_SZ];     /* content & length */
+    const byte* macSecret = GetMacSecret(ssl, verify);
+    
+    conLen[0] = content;
+    c16toa((word16)sz, &conLen[ENUM_LEN]);
+    c32toa(GetSEQIncrement(ssl, verify), &seq[sizeof(word32)]);
+
+    if (ssl->specs.mac_algorithm == md5_mac) {
+        InitMd5(&md5);
+        /* inner */
+        Md5Update(&md5, macSecret, digestSz);
+        Md5Update(&md5, PAD1, padSz);
+        Md5Update(&md5, seq, SEQ_SZ);
+        Md5Update(&md5, conLen, sizeof(conLen));
+        /* buffer */
+        Md5Update(&md5, buffer, sz);
+        Md5Final(&md5, result);
+        /* outer */
+        Md5Update(&md5, macSecret, digestSz);
+        Md5Update(&md5, PAD2, padSz);
+        Md5Update(&md5, result, digestSz);
+        Md5Final(&md5, digest);        
+    }
+    else {
+        InitSha(&sha);
+        /* inner */
+        ShaUpdate(&sha, macSecret, digestSz);
+        ShaUpdate(&sha, PAD1, padSz);
+        ShaUpdate(&sha, seq, SEQ_SZ);
+        ShaUpdate(&sha, conLen, sizeof(conLen));
+        /* buffer */
+        ShaUpdate(&sha, buffer, sz);
+        ShaFinal(&sha, result);
+        /* outer */
+        ShaUpdate(&sha, macSecret, digestSz);
+        ShaUpdate(&sha, PAD2, padSz);
+        ShaUpdate(&sha, result, digestSz);
+        ShaFinal(&sha, digest);        
+    }
+}
+
+
+static void BuildMD5_CertVerify(SSL* ssl, byte* digest)
+{
+    byte md5_result[MD5_DIGEST_SIZE];
+
+    /* make md5 inner */
+    Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN);
+    Md5Update(&ssl->hashMd5, PAD1, PAD_MD5);
+    Md5Final(&ssl->hashMd5, md5_result);
+
+    /* make md5 outer */
+    Md5Update(&ssl->hashMd5, ssl->arrays.masterSecret, SECRET_LEN);
+    Md5Update(&ssl->hashMd5, PAD2, PAD_MD5);
+    Md5Update(&ssl->hashMd5, md5_result, MD5_DIGEST_SIZE);
+
+    Md5Final(&ssl->hashMd5, digest);
+}
+
+
+static void BuildSHA_CertVerify(SSL* ssl, byte* digest)
+{
+    byte sha_result[SHA_DIGEST_SIZE];
+    
+    /* make sha inner */
+    ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN);
+    ShaUpdate(&ssl->hashSha, PAD1, PAD_SHA);
+    ShaFinal(&ssl->hashSha, sha_result);
+
+    /* make sha outer */
+    ShaUpdate(&ssl->hashSha, ssl->arrays.masterSecret, SECRET_LEN);
+    ShaUpdate(&ssl->hashSha, PAD2, PAD_SHA);
+    ShaUpdate(&ssl->hashSha, sha_result, SHA_DIGEST_SIZE);
+
+    ShaFinal(&ssl->hashSha, digest);
+}
+
+
+static void BuildCertHashes(SSL* ssl, Hashes* hashes)
+{
+    /* store current states, building requires get_digest which resets state */
+    Md5 md5 = ssl->hashMd5;
+    Sha sha = ssl->hashSha;
+
+    if (ssl->options.tls) {
+        Md5Final(&ssl->hashMd5, hashes->md5);
+        ShaFinal(&ssl->hashSha, hashes->sha);
+    }
+    else {
+        BuildMD5_CertVerify(ssl, hashes->md5);
+        BuildSHA_CertVerify(ssl, hashes->sha);
+    }
+    
+    /* restore */
+    ssl->hashMd5 = md5;
+    ssl->hashSha = sha;
+}
+
+
+/* Build SSL Message, encrypted */
+static int BuildMessage(SSL* ssl, byte* output, const byte* input, int inSz,
+                        int type)
+{
+    word32 digestSz = ssl->specs.hash_size;
+    word32 sz = RECORD_HEADER_SZ + inSz + digestSz;                
+    word32 pad  = 0, i;
+    word32 idx  = RECORD_HEADER_SZ;
+    word32 ivSz = 0;      /* TLSv1.1  IV */
+    word32 headerSz = RECORD_HEADER_SZ;
+    word16 size;
+    byte               iv[AES_BLOCK_SIZE];                  /* max size */
+
+#ifdef CYASSL_DTLS
+    if (ssl->options.dtls) {
+        sz       += DTLS_RECORD_EXTRA;
+        idx      += DTLS_RECORD_EXTRA; 
+        headerSz += DTLS_RECORD_EXTRA;
+    }
+#endif
+
+    if (ssl->specs.cipher_type == block) {
+        word32 blockSz = ssl->specs.block_size;
+        if (ssl->options.tls1_1) {
+            ivSz = blockSz;
+            sz  += ivSz;
+            RNG_GenerateBlock(&ssl->rng, iv, ivSz);
+        }
+        sz += 1;       /* pad byte */
+        pad = (sz - headerSz) % blockSz;
+        pad = blockSz - pad;
+        sz += pad;
+    }
+
+    size = sz - headerSz;    /* include mac and digest */
+    AddRecordHeader(output, size, type, ssl);    
+
+    /* write to output */
+    if (ivSz) {
+        XMEMCPY(output + idx, iv, ivSz);
+        idx += ivSz;
+    }
+    XMEMCPY(output + idx, input, inSz);
+    idx += inSz;
+
+    if (type == handshake)
+        HashOutput(ssl, output, headerSz + inSz, ivSz);
+    ssl->hmac(ssl, output+idx, output + headerSz + ivSz, inSz, type, 0);
+    idx += digestSz;
+
+    if (ssl->specs.cipher_type == block)
+        for (i = 0; i <= pad; i++) output[idx++] = pad; /* pad byte gets */
+                                                        /* pad value too */
+    Encrypt(ssl, output + headerSz, output + headerSz, size);
+
+    return sz;
+}
+
+
+int SendFinished(SSL* ssl)
+{
+    int              sendSz,
+                     finishedSz = ssl->options.tls ? TLS_FINISHED_SZ :
+                                                     FINISHED_SZ;
+    byte             input[FINISHED_SZ + DTLS_HANDSHAKE_HEADER_SZ];  /* max */
+    byte            *output;
+    Hashes*          hashes;
+    int              ret;
+    int              headerSz = HANDSHAKE_HEADER_SZ;
+
+
+    #ifdef CYASSL_DTLS
+        if (ssl->options.dtls) {
+            headerSz += DTLS_HANDSHAKE_EXTRA;
+            ssl->keys.dtls_epoch++;
+            ssl->keys.dtls_sequence_number = 0;  /* reset after epoch change */
+        }
+    #endif
+    
+    /* check for avalaible size */
+    if ((ret = CheckAvalaibleSize(ssl, sizeof(input) + MAX_MSG_EXTRA)) != 0)
+        return ret;
+
+    /* get ouput buffer */
+    output = ssl->buffers.outputBuffer.buffer + 
+             ssl->buffers.outputBuffer.idx;
+
+    AddHandShakeHeader(input, finishedSz, finished, ssl);
+
+    /* make finished hashes */
+    hashes = (Hashes*)&input[headerSz];
+    BuildFinished(ssl, hashes, ssl->options.side == CLIENT_END ? client :
+                  server);
+
+    if ( (sendSz = BuildMessage(ssl, output, input, headerSz +
+                                finishedSz, handshake)) == -1)
+        return BUILD_MSG_ERROR;
+
+    if (!ssl->options.resuming) {
+        AddSession(ssl);    /* just try */
+        if (ssl->options.side == CLIENT_END)
+            BuildFinished(ssl, &ssl->verifyHashes, server);
+        else
+            ssl->options.handShakeState = HANDSHAKE_DONE;
+    }
+    else {
+        if (ssl->options.side == CLIENT_END)
+            ssl->options.handShakeState = HANDSHAKE_DONE;
+        else
+            BuildFinished(ssl, &ssl->verifyHashes, client);
+    }
+
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("Finished", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddPacketInfo("Finished", &ssl->timeoutInfo, output, sendSz,
+                          ssl->heap);
+    #endif
+
+    ssl->buffers.outputBuffer.length += sendSz;
+
+    return SendBuffered(ssl);
+}
+
+
+int SendCertificate(SSL* ssl)
+{
+    int    sendSz, length, ret = 0;
+    word32 i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+    word32 certSz, listSz;
+    byte*  output = 0;
+
+    if (ssl->options.usingPSK_cipher) return 0;  /* not needed */
+
+    if (ssl->options.sendVerify == SEND_BLANK_CERT) {
+        certSz = 0;
+        length = CERT_HEADER_SZ;
+        listSz = 0;
+    }
+    else {
+        certSz = ssl->buffers.certificate.length;
+        /* list + cert size */
+        length = certSz + 2 * CERT_HEADER_SZ;
+        listSz = certSz + CERT_HEADER_SZ;
+    }
+    sendSz = length + RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+
+    #ifdef CYASSL_DTLS
+        if (ssl->options.dtls) {
+            sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+            i      += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+        }
+    #endif
+
+    /* check for avalaible size */
+    if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
+        return ret;
+
+    /* get ouput buffer */
+    output = ssl->buffers.outputBuffer.buffer +
+             ssl->buffers.outputBuffer.idx;
+
+    AddHeaders(output, length, certificate, ssl);
+
+    /* list total */
+    c32to24(listSz, output + i);
+    i += CERT_HEADER_SZ;
+
+    /* member */
+    if (certSz) {
+        c32to24(certSz, output + i);
+        i += CERT_HEADER_SZ;
+        XMEMCPY(output + i, ssl->buffers.certificate.buffer, certSz);
+        i += certSz;
+    }
+    HashOutput(ssl, output, sendSz, 0);
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("Certificate", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddPacketInfo("Certificate", &ssl->timeoutInfo, output, sendSz,
+                           ssl->heap);
+    #endif
+
+    if (ssl->options.side == SERVER_END)
+        ssl->options.serverState = SERVER_CERT_COMPLETE;
+
+    ssl->buffers.outputBuffer.length += sendSz;
+    return SendBuffered(ssl);
+}
+
+
+int SendCertificateRequest(SSL* ssl)
+{
+    byte   *output;
+    int    ret;
+    int    sendSz;
+    word32 i = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+    
+    int  typeTotal = 1;  /* only rsa for now */
+    int  reqSz = ENUM_LEN + typeTotal + REQ_HEADER_SZ;  /* add auth later */
+
+    if (ssl->options.usingPSK_cipher) return 0;  /* not needed */
+
+    sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + reqSz;
+
+    #ifdef CYASSL_DTLS
+        if (ssl->options.dtls) {
+            sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+            i      += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+        }
+    #endif
+    /* check for avalaible size */
+    if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
+        return ret;
+
+    /* get ouput buffer */
+    output = ssl->buffers.outputBuffer.buffer + ssl->buffers.outputBuffer.idx;
+
+    AddHeaders(output, reqSz, certificate_request, ssl);
+
+    /* write to output */
+    output[i++] = typeTotal;  /* # of types */
+    output[i++] = rsa_sign;
+
+    c16toa(0, &output[i]);  /* auth's */
+    i += REQ_HEADER_SZ;
+
+    HashOutput(ssl, output, sendSz, 0);
+
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn)
+            AddPacketName("CertificateRequest", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddPacketInfo("CertificateRequest", &ssl->timeoutInfo, output,
+                          sendSz, ssl->heap);
+    #endif
+    ssl->buffers.outputBuffer.length += sendSz;
+    return SendBuffered(ssl);
+}
+
+
+int SendData(SSL* ssl, const void* buffer, int sz)
+{
+    int sent = 0,  /* plainText size */
+        sendSz,
+        ret;
+
+    if (ssl->error == WANT_WRITE)
+        ssl->error = 0;
+
+    if (ssl->options.handShakeState != HANDSHAKE_DONE) {
+        int err;
+        if ( (err = CyaSSL_negotiate(ssl)) != 0) 
+            return  err;
+    }
+
+    /* last time system socket output buffer was full, try again to send */
+    if (ssl->buffers.outputBuffer.length > 0) {
+        if ( (ssl->error = SendBuffered(ssl)) < 0) {
+            CYASSL_ERROR(ssl->error);
+            if (ssl->error == SOCKET_ERROR_E && ssl->options.connReset)
+                return 0;     /* peer reset */
+            return ssl->error;
+        }
+        else {
+            /* advance sent to previous sent + plain size just sent */
+            sent = ssl->buffers.prevSent + ssl->buffers.plainSz;
+            CYASSL_MSG("sent write buffered data");
+        }
+    }
+
+    for (;;) {
+        int   len = min(sz - sent, OUTPUT_RECORD_SIZE);
+        byte* out;
+        byte* sendBuffer = (byte*)buffer + sent;  /* may switch on comp */
+        int   buffSz = len;                       /* may switch on comp */
+#ifdef HAVE_LIBZ
+        byte  comp[MAX_RECORD_SIZE + MAX_COMP_EXTRA];
+#endif
+
+        if (sent == sz) break;
+
+#ifdef CYASSL_DTLS
+        if (ssl->options.dtls) {
+            len    = min(len, MAX_UDP_SIZE);
+            buffSz = len;
+        }
+#endif
+
+        /* check for avalaible size */
+        if ((ret = CheckAvalaibleSize(ssl, len + COMP_EXTRA +
+                                      MAX_MSG_EXTRA)) != 0)
+            return ret;
+
+        /* get ouput buffer */
+        out = ssl->buffers.outputBuffer.buffer +
+              ssl->buffers.outputBuffer.idx;
+
+#ifdef HAVE_LIBZ
+        if (ssl->options.usingCompression) {
+            buffSz = Compress(ssl, sendBuffer, buffSz, comp, sizeof(comp));
+            if (buffSz < 0) {
+                return buffSz;
+            }
+            sendBuffer = comp;
+        }
+#endif
+        sendSz = BuildMessage(ssl, out, sendBuffer, buffSz,
+                              application_data);
+
+        ssl->buffers.outputBuffer.length += sendSz;
+
+        if ( (ret = SendBuffered(ssl)) < 0) {
+            CYASSL_ERROR(ret);
+            /* store for next call if WANT_WRITE or user embedSend() that
+               doesn't present like WANT_WRITE */
+            ssl->buffers.plainSz  = len;
+            ssl->buffers.prevSent = sent;
+            if (ret == SOCKET_ERROR_E && ssl->options.connReset)
+                return 0;  /* peer reset */
+            return ssl->error = ret;
+        }
+
+        sent += len;
+
+        /* only one message per attempt */
+        if (ssl->options.partialWrite == 1)
+            break;
+    }
+ 
+    return sent;
+}
+
+/* process input data */
+int ReceiveData(SSL* ssl, byte* output, int sz)
+{
+    int size;
+
+    CYASSL_ENTER("ReceiveData()");
+
+    if (ssl->error == WANT_READ)
+        ssl->error = 0;
+
+    if (ssl->options.handShakeState != HANDSHAKE_DONE) {
+        int err;
+        if ( (err = CyaSSL_negotiate(ssl)) != 0)
+            return  err;
+    }
+
+    while (ssl->buffers.clearOutputBuffer.length == 0)
+        if ( (ssl->error = ProcessReply(ssl)) < 0) {
+            CYASSL_ERROR(ssl->error);
+            if (ssl->error == ZERO_RETURN) {
+                ssl->options.isClosed = 1;
+                return 0;         /* no more data coming */
+            }
+            if (ssl->error == SOCKET_ERROR_E)
+                if (ssl->options.connReset || ssl->options.isClosed)
+                    return 0;     /* peer reset or closed */
+            return ssl->error;
+        }
+
+    if (sz < (int)ssl->buffers.clearOutputBuffer.length)
+        size = sz;
+    else
+        size = ssl->buffers.clearOutputBuffer.length;
+
+    XMEMCPY(output, ssl->buffers.clearOutputBuffer.buffer, size);
+    ssl->buffers.clearOutputBuffer.length -= size;
+    ssl->buffers.clearOutputBuffer.buffer += size;
+   
+    if (ssl->buffers.clearOutputBuffer.length == 0 && 
+                                           ssl->buffers.inputBuffer.dynamicFlag)
+       ShrinkInputBuffer(ssl, NO_FORCED_FREE);
+
+    CYASSL_LEAVE("ReceiveData()", size);
+    return size;
+}
+
+
+/* send alert message */
+int SendAlert(SSL* ssl, int severity, int type)
+{
+    byte input[ALERT_SIZE];
+    byte *output;
+    int  sendSz;
+    int  ret;
+
+    /* if sendalert is called again for nonbloking */
+    if (ssl->options.sendAlertState != 0) {
+        ret = SendBuffered(ssl);
+        if (ret == 0)
+            ssl->options.sendAlertState = 0;
+        return ret;
+    }
+
+    /* check for avalaible size */
+    if ((ret = CheckAvalaibleSize(ssl, ALERT_SIZE + MAX_MSG_EXTRA)) != 0)
+        return ret;
+
+    /* get ouput buffer */
+    output = ssl->buffers.outputBuffer.buffer +
+             ssl->buffers.outputBuffer.idx;
+
+    input[0] = severity;
+    input[1] = type;
+
+    if (ssl->keys.encryptionOn)
+        sendSz = BuildMessage(ssl, output, input, sizeof(input), alert);
+    else {
+        RecordLayerHeader *const rl = (RecordLayerHeader*)output;
+        rl->type    = alert;
+        rl->version = ssl->version;
+        c16toa(ALERT_SIZE, rl->length);      
+
+        XMEMCPY(output + RECORD_HEADER_SZ, input, sizeof(input));
+        sendSz = RECORD_HEADER_SZ + sizeof(input);
+    }
+
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn)
+            AddPacketName("Alert", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddPacketInfo("Alert", &ssl->timeoutInfo, output, sendSz,ssl->heap);
+    #endif
+
+    ssl->buffers.outputBuffer.length += sendSz;
+    ssl->options.sendAlertState = 1;
+
+    return SendBuffered(ssl);
+}
+
+
+
+void SetErrorString(int error, char* buffer)
+{
+    const int max = MAX_ERROR_SZ;  /* shorthand */
+
+#ifdef NO_ERROR_STRINGS
+
+    XSTRNCPY(buffer, "no support for error strings built in", max);
+
+#else
+
+    /* pass to CTaoCrypt */
+    if (error < MAX_CODE_E && error > MIN_CODE_E) {
+        CTaoCryptErrorString(error, buffer);
+        return;
+    }
+
+    switch (error) {
+
+    case UNSUPPORTED_SUITE :
+        XSTRNCPY(buffer, "unsupported cipher suite", max);
+        break;
+
+    case PREFIX_ERROR :
+        XSTRNCPY(buffer, "bad index to key rounds", max);
+        break;
+
+    case MEMORY_ERROR :
+        XSTRNCPY(buffer, "out of memory", max);
+        break;
+
+    case VERIFY_FINISHED_ERROR :
+        XSTRNCPY(buffer, "verify problem on finished", max);
+        break;
+
+    case VERIFY_MAC_ERROR :
+        XSTRNCPY(buffer, "verify mac problem", max);
+        break;
+
+    case PARSE_ERROR :
+        XSTRNCPY(buffer, "parse error on header", max);
+        break;
+
+    case SIDE_ERROR :
+        XSTRNCPY(buffer, "wrong client/server type", max);
+        break;
+
+    case NO_PEER_CERT :
+        XSTRNCPY(buffer, "peer didn't send cert", max);
+        break;
+
+    case UNKNOWN_HANDSHAKE_TYPE :
+        XSTRNCPY(buffer, "weird handshake type", max);
+        break;
+
+    case SOCKET_ERROR_E :
+        XSTRNCPY(buffer, "error state on socket", max);
+        break;
+
+    case SOCKET_NODATA :
+        XSTRNCPY(buffer, "expected data, not there", max);
+        break;
+
+    case INCOMPLETE_DATA :
+        XSTRNCPY(buffer, "don't have enough data to complete task", max);
+        break;
+
+    case UNKNOWN_RECORD_TYPE :
+        XSTRNCPY(buffer, "unknown type in record hdr", max);
+        break;
+
+    case DECRYPT_ERROR :
+        XSTRNCPY(buffer, "error during decryption", max);
+        break;
+
+    case FATAL_ERROR :
+        XSTRNCPY(buffer, "revcd alert fatal error", max);
+        break;
+
+    case ENCRYPT_ERROR :
+        XSTRNCPY(buffer, "error during encryption", max);
+        break;
+
+    case FREAD_ERROR :
+        XSTRNCPY(buffer, "fread problem", max);
+        break;
+
+    case NO_PEER_KEY :
+        XSTRNCPY(buffer, "need peer's key", max);
+        break;
+
+    case NO_PRIVATE_KEY :
+        XSTRNCPY(buffer, "need the private key", max);
+        break;
+
+    case RSA_PRIVATE_ERROR :
+        XSTRNCPY(buffer, "error during rsa priv op", max);
+        break;
+
+    case MATCH_SUITE_ERROR :
+        XSTRNCPY(buffer, "can't match cipher suite", max);
+        break;
+
+    case BUILD_MSG_ERROR :
+        XSTRNCPY(buffer, "build message failure", max);
+        break;
+
+    case BAD_HELLO :
+        XSTRNCPY(buffer, "client hello malformed", max);
+        break;
+
+    case DOMAIN_NAME_MISMATCH :
+        XSTRNCPY(buffer, "peer subject name mismatch", max);
+        break;
+
+    case WANT_READ :
+        XSTRNCPY(buffer, "non-blocking socket wants data to be read", max);
+        break;
+
+    case NOT_READY_ERROR :
+        XSTRNCPY(buffer, "handshake layer not ready yet, complete first", max);
+        break;
+
+    case PMS_VERSION_ERROR :
+        XSTRNCPY(buffer, "premaster secret version mismatch error", max);
+        break;
+
+    case VERSION_ERROR :
+        XSTRNCPY(buffer, "record layer version error", max);
+        break;
+
+    case WANT_WRITE :
+        XSTRNCPY(buffer, "non-blocking socket write buffer full", max);
+        break;
+
+    case BUFFER_ERROR :
+        XSTRNCPY(buffer, "malformed buffer input error", max);
+        break;
+
+    case VERIFY_CERT_ERROR :
+        XSTRNCPY(buffer, "verify problem on certificate", max);
+        break;
+
+    case VERIFY_SIGN_ERROR :
+        XSTRNCPY(buffer, "verify problem based on signature", max);
+        break;
+
+    case CLIENT_ID_ERROR :
+        XSTRNCPY(buffer, "psk client identity error", max);
+        break;
+
+    case SERVER_HINT_ERROR:
+        XSTRNCPY(buffer, "psk server hint error", max);
+        break;
+
+    case PSK_KEY_ERROR:
+        XSTRNCPY(buffer, "psk key callback error", max);
+        break;
+
+    case NTRU_KEY_ERROR:
+        XSTRNCPY(buffer, "NTRU key error", max);
+        break;
+
+    case NTRU_DRBG_ERROR:
+        XSTRNCPY(buffer, "NTRU drbg error", max);
+        break;
+
+    case NTRU_ENCRYPT_ERROR:
+        XSTRNCPY(buffer, "NTRU encrypt error", max);
+        break;
+
+    case NTRU_DECRYPT_ERROR:
+        XSTRNCPY(buffer, "NTRU decrypt error", max);
+        break;
+
+    case ZLIB_INIT_ERROR:
+        XSTRNCPY(buffer, "zlib init error", max);
+        break;
+
+    case ZLIB_COMPRESS_ERROR:
+        XSTRNCPY(buffer, "zlib compress error", max);
+        break;
+
+    case ZLIB_DECOMPRESS_ERROR:
+        XSTRNCPY(buffer, "zlib decompress error", max);
+        break;
+
+    case GETTIME_ERROR:
+        XSTRNCPY(buffer, "gettimeofday() error", max);
+        break;
+
+    case GETITIMER_ERROR:
+        XSTRNCPY(buffer, "getitimer() error", max);
+        break;
+
+    case SIGACT_ERROR:
+        XSTRNCPY(buffer, "sigaction() error", max);
+        break;
+
+    case SETITIMER_ERROR:
+        XSTRNCPY(buffer, "setitimer() error", max);
+        break;
+
+    case LENGTH_ERROR:
+        XSTRNCPY(buffer, "record layer length error", max);
+        break;
+
+    case PEER_KEY_ERROR:
+        XSTRNCPY(buffer, "cant decode peer key", max);
+        break;
+
+    case ZERO_RETURN:
+        XSTRNCPY(buffer, "peer sent close notify alert", max);
+        break;
+
+    default :
+        XSTRNCPY(buffer, "unknown error number", max);
+    }
+
+#endif /* NO_ERROR_STRINGS */
+}
+
+
+
+/* be sure to add to cipher_name_idx too !!!! */
+const char* const cipher_names[] = 
+{
+#ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA
+    "RC4-SHA",
+#endif
+
+#ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5
+    "RC4-MD5",
+#endif
+
+#ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA
+    "DES-CBC3-SHA",
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA
+    "AES128-SHA",
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA
+    "AES256-SHA",
+#endif
+
+#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+    "DHE-RSA-AES128-SHA",
+#endif
+
+#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
+    "DHE-RSA-AES256-SHA",
+#endif
+
+#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA
+    "PSK-AES128-CBC-SHA",
+#endif
+
+#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA
+    "PSK-AES256-CBC-SHA",
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5
+    "HC128-MD5",
+#endif
+    
+#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA
+    "HC128-SHA",
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA
+    "RABBIT-SHA",
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA
+    "NTRU-RC4-SHA",
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA
+    "NTRU-DES-CBC3-SHA",
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA
+    "NTRU-AES128-SHA",
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA
+    "NTRU-AES256-SHA",
+#endif
+};
+
+
+
+/* cipher suite number that matches above name table */
+int cipher_name_idx[] =
+{
+
+#ifdef BUILD_SSL_RSA_WITH_RC4_128_SHA
+    SSL_RSA_WITH_RC4_128_SHA,
+#endif
+
+#ifdef BUILD_SSL_RSA_WITH_RC4_128_MD5
+    SSL_RSA_WITH_RC4_128_MD5,
+#endif
+
+#ifdef BUILD_SSL_RSA_WITH_3DES_EDE_CBC_SHA
+    SSL_RSA_WITH_3DES_EDE_CBC_SHA,
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_AES_128_CBC_SHA
+    TLS_RSA_WITH_AES_128_CBC_SHA,    
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_AES_256_CBC_SHA
+    TLS_RSA_WITH_AES_256_CBC_SHA,
+#endif
+
+#ifdef BUILD_TLS_DHE_RSA_WITH_AES_128_CBC_SHA
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA,    
+#endif
+
+#ifdef BUILD_TLS_DHE_RSA_WITH_AES_256_CBC_SHA
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+#endif
+
+#ifdef BUILD_TLS_PSK_WITH_AES_128_CBC_SHA
+    TLS_PSK_WITH_AES_128_CBC_SHA,    
+#endif
+
+#ifdef BUILD_TLS_PSK_WITH_AES_256_CBC_SHA
+    TLS_PSK_WITH_AES_256_CBC_SHA,
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_MD5
+    TLS_RSA_WITH_HC_128_CBC_MD5,    
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_HC_128_CBC_SHA
+    TLS_RSA_WITH_HC_128_CBC_SHA,    
+#endif
+
+#ifdef BUILD_TLS_RSA_WITH_RABBIT_CBC_SHA
+    TLS_RSA_WITH_RABBIT_CBC_SHA,    
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_RC4_128_SHA
+    TLS_NTRU_RSA_WITH_RC4_128_SHA,
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA
+    TLS_NTRU_RSA_WITH_3DES_EDE_CBC_SHA,
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_128_CBC_SHA
+    TLS_NTRU_RSA_WITH_AES_128_CBC_SHA,    
+#endif
+
+#ifdef BUILD_TLS_NTRU_RSA_WITH_AES_256_CBC_SHA
+    TLS_NTRU_RSA_WITH_AES_256_CBC_SHA,    
+#endif
+};
+
+
+/* return true if set, else false */
+/* only supports full name from cipher_name[] delimited by : */
+int SetCipherList(SSL_CTX* ctx, const char* list)
+{
+    int  ret = 0, i;
+    char name[MAX_SUITE_NAME];
+
+    char  needle[] = ":";
+    char* haystack = (char*)list;
+    char* prev;
+
+    const int suiteSz = sizeof(cipher_names) / sizeof(cipher_names[0]);
+    int idx = 0;
+
+    if (!list)
+        return 0;
+    
+    if (*list == 0) return 1;   /* CyaSSL default */
+
+    if (XSTRNCMP(haystack, "ALL", 3) == 0) return 1;  /* CyaSSL defualt */
+
+    for(;;) {
+        size_t len;
+        prev = haystack;
+        haystack = XSTRSTR(haystack, needle);
+
+        if (!haystack)    /* last cipher */
+            len = min(sizeof(name), XSTRLEN(prev));
+        else
+            len = min(sizeof(name), (size_t)(haystack - prev));
+
+        XSTRNCPY(name, prev, len);
+        name[(len == sizeof(name)) ? len - 1 : len] = 0;
+
+        for (i = 0; i < suiteSz; i++)
+            if (XSTRNCMP(name, cipher_names[i], sizeof(name)) == 0) {
+
+                ctx->suites.suites[idx++] = 0x00;  /* first byte always zero */
+                ctx->suites.suites[idx++] = cipher_name_idx[i];
+
+                if (!ret) ret = 1;   /* found at least one */
+                break;
+            }
+        if (!haystack) break;
+        haystack++;
+    }
+
+    if (ret) {
+        ctx->suites.setSuites = 1;
+        ctx->suites.suiteSz   = idx;
+    }
+
+    return ret;
+}
+
+
+#ifdef CYASSL_CALLBACKS
+
+    /* Initialisze HandShakeInfo */
+    void InitHandShakeInfo(HandShakeInfo* info)
+    {
+        int i;
+
+        info->cipherName[0] = 0;
+        for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++)
+            info->packetNames[i][0] = 0;
+        info->numberPackets = 0;
+        info->negotiationError = 0;
+    }
+
+    /* Set Final HandShakeInfo parameters */
+    void FinishHandShakeInfo(HandShakeInfo* info, const SSL* ssl)
+    {
+        int i;
+        int sz = sizeof(cipher_name_idx)/sizeof(int); 
+
+        for (i = 0; i < sz; i++)
+            if (ssl->options.cipherSuite == (byte)cipher_name_idx[i]) {
+                XSTRNCPY(info->cipherName, cipher_names[i], MAX_CIPHERNAME_SZ);
+                break;
+            }
+
+        /* error max and min are negative numbers */
+        if (ssl->error <= MIN_PARAM_ERR && ssl->error >= MAX_PARAM_ERR)
+            info->negotiationError = ssl->error;
+    }
+
+   
+    /* Add name to info packet names, increase packet name count */
+    void AddPacketName(const char* name, HandShakeInfo* info)
+    {
+        if (info->numberPackets < MAX_PACKETS_HANDSHAKE) {
+            XSTRNCPY(info->packetNames[info->numberPackets++], name,
+                    MAX_PACKETNAME_SZ);
+        }
+    } 
+
+
+    /* Initialisze TimeoutInfo */
+    void InitTimeoutInfo(TimeoutInfo* info)
+    {
+        int i;
+
+        info->timeoutName[0] = 0;
+        info->flags          = 0;
+
+        for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++) {
+            info->packets[i].packetName[0]     = 0;
+            info->packets[i].timestamp.tv_sec  = 0;
+            info->packets[i].timestamp.tv_usec = 0;
+            info->packets[i].bufferValue       = 0;
+            info->packets[i].valueSz           = 0;
+        }
+        info->numberPackets        = 0;
+        info->timeoutValue.tv_sec  = 0;
+        info->timeoutValue.tv_usec = 0;
+    }
+
+
+    /* Free TimeoutInfo */
+    void FreeTimeoutInfo(TimeoutInfo* info, void* heap)
+    {
+        int i;
+        for (i = 0; i < MAX_PACKETS_HANDSHAKE; i++)
+            if (info->packets[i].bufferValue) {
+                XFREE(info->packets[i].bufferValue, heap, DYNAMIC_TYPE_INFO);
+                info->packets[i].bufferValue = 0;
+            }
+
+    }
+
+
+    /* Add PacketInfo to TimeoutInfo */
+    void AddPacketInfo(const char* name, TimeoutInfo* info, const byte* data,
+                       int sz, void* heap)
+    {
+        if (info->numberPackets < (MAX_PACKETS_HANDSHAKE - 1)) {
+            Timeval currTime;
+
+            /* may add name after */
+            if (name)
+                XSTRNCPY(info->packets[info->numberPackets].packetName, name,
+                        MAX_PACKETNAME_SZ);
+
+            /* add data, put in buffer if bigger than static buffer */
+            info->packets[info->numberPackets].valueSz = sz;
+            if (sz < MAX_VALUE_SZ)
+                XMEMCPY(info->packets[info->numberPackets].value, data, sz);
+            else {
+                info->packets[info->numberPackets].bufferValue =
+                           XMALLOC(sz, heap, DYNAMIC_TYPE_INFO);
+                if (!info->packets[info->numberPackets].bufferValue)
+                    /* let next alloc catch, just don't fill, not fatal here  */
+                    info->packets[info->numberPackets].valueSz = 0;
+                else
+                    XMEMCPY(info->packets[info->numberPackets].bufferValue,
+                           data, sz);
+            }
+            gettimeofday(&currTime, 0);
+            info->packets[info->numberPackets].timestamp.tv_sec  =
+                                                             currTime.tv_sec;
+            info->packets[info->numberPackets].timestamp.tv_usec =
+                                                             currTime.tv_usec;
+            info->numberPackets++;
+        }
+    }
+
+
+    /* Add packet name to previsouly added packet info */
+    void AddLateName(const char* name, TimeoutInfo* info)
+    {
+        /* make sure we have a valid previous one */
+        if (info->numberPackets > 0 && info->numberPackets <
+                                                        MAX_PACKETS_HANDSHAKE) {
+            XSTRNCPY(info->packets[info->numberPackets - 1].packetName, name,
+                    MAX_PACKETNAME_SZ);
+        }
+    }
+
+    /* Add record header to previsouly added packet info */
+    void AddLateRecordHeader(const RecordLayerHeader* rl, TimeoutInfo* info)
+    {
+        /* make sure we have a valid previous one */
+        if (info->numberPackets > 0 && info->numberPackets <
+                                                        MAX_PACKETS_HANDSHAKE) {
+            if (info->packets[info->numberPackets - 1].bufferValue)
+                XMEMCPY(info->packets[info->numberPackets - 1].bufferValue, rl,
+                       RECORD_HEADER_SZ);
+            else
+                XMEMCPY(info->packets[info->numberPackets - 1].value, rl,
+                       RECORD_HEADER_SZ);
+        }
+    }
+
+#endif /* CYASSL_CALLBACKS */
+
+
+
+/* client only parts */
+#ifndef NO_CYASSL_CLIENT
+
+    int SendClientHello(SSL* ssl)
+    {
+        byte              *output;
+        word32             length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+        int                sendSz;
+        int                idSz = ssl->options.resuming ? ID_LEN : 0;
+        int                ret;
+
+        length = sizeof(ProtocolVersion) + RAN_LEN
+               + idSz + ENUM_LEN                      
+               + ssl->suites.suiteSz + SUITE_LEN
+               + COMP_LEN  + ENUM_LEN;
+
+        sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
+
+#ifdef CYASSL_DTLS
+        if (ssl->options.dtls) {
+            length += ENUM_LEN;   /* cookie */
+            sendSz  = length + DTLS_HANDSHAKE_HEADER_SZ + DTLS_RECORD_HEADER_SZ;
+            idx    += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
+        }
+#endif
+
+        /* check for avalaible size */
+        if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
+            return ret;
+
+        /* get ouput buffer */
+        output = ssl->buffers.outputBuffer.buffer +
+                 ssl->buffers.outputBuffer.idx;
+
+        AddHeaders(output, length, client_hello, ssl);
+
+            /* client hello, first version */
+        XMEMCPY(output + idx, &ssl->version, sizeof(ProtocolVersion));
+        idx += sizeof(ProtocolVersion);
+        ssl->chVersion = ssl->version;  /* store in case changed */
+
+            /* then random */
+        if (ssl->options.connectState == CONNECT_BEGIN) {
+            RNG_GenerateBlock(&ssl->rng, output + idx, RAN_LEN);
+            
+                /* store random */
+            XMEMCPY(ssl->arrays.clientRandom, output + idx, RAN_LEN);
+        } else {
+#ifdef CYASSL_DTLS
+                /* send same random on hello again */
+            XMEMCPY(output + idx, ssl->arrays.clientRandom, RAN_LEN);
+#endif
+        }
+        idx += RAN_LEN;
+
+            /* then session id */
+        output[idx++] = idSz;
+        if (idSz) {
+            XMEMCPY(output + idx, ssl->session.sessionID, ID_LEN);
+            idx += ID_LEN;
+        }
+        
+            /* then DTLS cookie */
+#ifdef CYASSL_DTLS
+        if (ssl->options.dtls) {
+            output[idx++] = 0;
+        }
+#endif
+            /* then cipher suites */
+        c16toa(ssl->suites.suiteSz, output + idx);
+        idx += 2;
+        XMEMCPY(output + idx, &ssl->suites.suites, ssl->suites.suiteSz);
+        idx += ssl->suites.suiteSz;
+
+            /* last, compression */
+        output[idx++] = COMP_LEN;
+        if (ssl->options.usingCompression)
+            output[idx++] = ZLIB_COMPRESSION;
+        else
+            output[idx++] = NO_COMPRESSION;
+            
+        HashOutput(ssl, output, sendSz, 0);
+
+        ssl->options.clientState = CLIENT_HELLO_COMPLETE;
+
+#ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("ClientHello", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddPacketInfo("ClientHello", &ssl->timeoutInfo, output, sendSz,
+                          ssl->heap);
+#endif
+
+        ssl->buffers.outputBuffer.length += sendSz;
+
+        return SendBuffered(ssl);
+    }
+
+
+    static int DoHelloVerifyRequest(SSL* ssl, const byte* input,
+                                    word32* inOutIdx)
+    {
+        ProtocolVersion pv;
+        byte            cookieSz;
+        
+#ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("HelloVerifyRequest",
+                                         &ssl->handShakeInfo);
+        if (ssl->toInfoOn) AddLateName("HelloVerifyRequest", &ssl->timeoutInfo);
+#endif
+        XMEMCPY(&pv, input + *inOutIdx, sizeof(pv));
+        *inOutIdx += sizeof(pv);
+        
+        cookieSz = input[(*inOutIdx)++];
+        
+        if (cookieSz)
+            *inOutIdx += cookieSz;   /* skip for now */
+        
+        ssl->options.serverState = SERVER_HELLOVERIFYREQUEST_COMPLETE;
+        return 0;
+    }
+
+
+    static int DoServerHello(SSL* ssl, const byte* input, word32* inOutIdx)
+    {
+        byte b;
+        byte compression;
+        ProtocolVersion pv;
+        word32 i = *inOutIdx;
+
+#ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("ServerHello", &ssl->handShakeInfo);
+        if (ssl->toInfoOn) AddLateName("ServerHello", &ssl->timeoutInfo);
+#endif
+        XMEMCPY(&pv, input + i, sizeof(pv));
+        i += sizeof(pv);
+        XMEMCPY(ssl->arrays.serverRandom, input + i, RAN_LEN);
+        i += RAN_LEN;
+        b = input[i++];
+        if (b) {
+            XMEMCPY(ssl->arrays.sessionID, input + i, b);
+            i += b;
+        }
+        ssl->options.cipherSuite = input[++i];  
+        compression = input[++i];
+        ++i;   /* 2nd byte */
+
+        if (compression != ZLIB_COMPRESSION && ssl->options.usingCompression)
+            ssl->options.usingCompression = 0;  /* turn off if server refused */
+        
+        ssl->options.serverState = SERVER_HELLO_COMPLETE;
+
+        *inOutIdx = i;
+
+        if (ssl->options.resuming) {
+            if (XMEMCMP(ssl->arrays.sessionID, ssl->session.sessionID, ID_LEN)
+                                                                        == 0) {
+                if (SetCipherSpecs(ssl) == 0) {
+                    XMEMCPY(ssl->arrays.masterSecret, ssl->session.masterSecret,
+                           SECRET_LEN);
+                    if (ssl->options.tls)
+                        DeriveTlsKeys(ssl);
+                    else
+                        DeriveKeys(ssl);
+                    ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
+                    return 0;
+                }
+                else
+                    return UNSUPPORTED_SUITE;
+            }
+            else
+                ssl->options.resuming = 0; /* server denied resumption try */
+        }
+
+        return SetCipherSpecs(ssl);
+    }
+
+
+    /* just read in and ignore for now TODO: */
+    static int DoCertificateRequest(SSL* ssl, const byte* input, word32*
+                                    inOutIdx)
+    {
+        word16 len;
+       
+        #ifdef CYASSL_CALLBACKS
+            if (ssl->hsInfoOn)
+                AddPacketName("CertificateRequest", &ssl->handShakeInfo);
+            if (ssl->toInfoOn)
+                AddLateName("CertificateRequest", &ssl->timeoutInfo);
+        #endif
+        len = input[(*inOutIdx)++];
+
+        /* types, read in here */
+        *inOutIdx += len;
+        ato16(&input[*inOutIdx], &len);
+        *inOutIdx += LENGTH_SZ;
+
+        /* authorities */
+        while (len) {
+            word16 dnSz;
+       
+            ato16(&input[*inOutIdx], &dnSz);
+            *inOutIdx += (REQUEST_HEADER + dnSz);
+            len -= dnSz + REQUEST_HEADER;
+        }
+
+        /* don't send client cert or cert verify if user hasn't provided
+           cert and private key */
+        if (ssl->buffers.certificate.buffer && ssl->buffers.key.buffer)
+            ssl->options.sendVerify = SEND_CERT;
+        else if (IsAtLeastTLSv1_2(ssl))
+            ssl->options.sendVerify = SEND_BLANK_CERT;
+
+        return 0;
+    }
+
+
+    static int DoServerKeyExchange(SSL* ssl, const byte* input, word32*
+                                   inOutIdx)
+    {
+    #ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn)
+            AddPacketName("ServerKeyExchange", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddLateName("ServerKeyExchange", &ssl->timeoutInfo);
+    #endif
+
+    #ifndef NO_PSK
+        if (ssl->specs.kea == psk_kea) {
+            word16 length;
+           
+            ato16(&input[*inOutIdx], &length);
+            *inOutIdx += LENGTH_SZ;
+            XMEMCPY(ssl->arrays.server_hint, &input[*inOutIdx],
+                   min(length, MAX_PSK_ID_LEN));
+            if (length < MAX_PSK_ID_LEN)
+                ssl->arrays.server_hint[length] = 0;
+            else
+                ssl->arrays.server_hint[MAX_PSK_ID_LEN - 1] = 0;
+            *inOutIdx += length;
+
+            return 0;
+        }
+    #endif
+    #ifdef OPENSSL_EXTRA
+        if (ssl->specs.kea == diffie_hellman_kea)
+        {
+        word16 length, verifySz, messageTotal = 6;  /* pSz + gSz + pubSz */
+        byte   messageVerify[MAX_DH_SZ];
+        byte*  signature;
+        byte   hash[FINISHED_SZ];
+        Md5    md5;
+        Sha    sha;
+
+        /* p */
+        ato16(&input[*inOutIdx], &length);
+        *inOutIdx += LENGTH_SZ;
+        messageTotal += length;
+
+        ssl->buffers.serverDH_P.buffer = (byte*) XMALLOC(length, ssl->heap,
+                                                         DYNAMIC_TYPE_DH);
+        if (ssl->buffers.serverDH_P.buffer)
+            ssl->buffers.serverDH_P.length = length;
+        else
+            return MEMORY_ERROR;
+        XMEMCPY(ssl->buffers.serverDH_P.buffer, &input[*inOutIdx], length);
+        *inOutIdx += length;
+
+        /* g */
+        ato16(&input[*inOutIdx], &length);
+        *inOutIdx += LENGTH_SZ;
+        messageTotal += length;
+
+        ssl->buffers.serverDH_G.buffer = (byte*) XMALLOC(length, ssl->heap,
+                                                         DYNAMIC_TYPE_DH);
+        if (ssl->buffers.serverDH_G.buffer)
+            ssl->buffers.serverDH_G.length = length;
+        else
+            return MEMORY_ERROR;
+        XMEMCPY(ssl->buffers.serverDH_G.buffer, &input[*inOutIdx], length);
+        *inOutIdx += length;
+
+        /* pub */
+        ato16(&input[*inOutIdx], &length);
+        *inOutIdx += LENGTH_SZ;
+        messageTotal += length;
+
+        ssl->buffers.serverDH_Pub.buffer = (byte*) XMALLOC(length, ssl->heap,
+                                                           DYNAMIC_TYPE_DH);
+        if (ssl->buffers.serverDH_Pub.buffer)
+            ssl->buffers.serverDH_Pub.length = length;
+        else
+            return MEMORY_ERROR;
+        XMEMCPY(ssl->buffers.serverDH_Pub.buffer, &input[*inOutIdx], length);
+        *inOutIdx += length;
+
+        /* save message for hash verify */
+        if (messageTotal > sizeof(messageVerify))
+            return BUFFER_ERROR;
+        XMEMCPY(messageVerify, &input[*inOutIdx - messageTotal], messageTotal);
+        verifySz = messageTotal;
+
+        /* signature */
+        ato16(&input[*inOutIdx], &length);
+        *inOutIdx += LENGTH_SZ;
+
+        signature = (byte*)&input[*inOutIdx];
+        *inOutIdx += length;
+
+        /* verify signature */
+
+        /* md5 */
+        InitMd5(&md5);
+        Md5Update(&md5, ssl->arrays.clientRandom, RAN_LEN);
+        Md5Update(&md5, ssl->arrays.serverRandom, RAN_LEN);
+        Md5Update(&md5, messageVerify, verifySz);
+        Md5Final(&md5, hash);
+
+        /* sha */
+        InitSha(&sha);
+        ShaUpdate(&sha, ssl->arrays.clientRandom, RAN_LEN);
+        ShaUpdate(&sha, ssl->arrays.serverRandom, RAN_LEN);
+        ShaUpdate(&sha, messageVerify, verifySz);
+        ShaFinal(&sha, &hash[MD5_DIGEST_SIZE]);
+
+        /* rsa for now */
+        {
+            int   ret;
+            byte* out;
+
+            if (!ssl->peerRsaKeyPresent)
+                return NO_PEER_KEY;
+
+            ret = RsaSSL_VerifyInline(signature, length,&out, &ssl->peerRsaKey);
+
+            if (IsAtLeastTLSv1_2(ssl)) {
+                byte   encodedSig[MAX_ENCODED_SIG_SZ];
+                word32 sigSz;
+                byte*  digest;
+                int    hashType;
+                int    digestSz;
+
+                /* sha1 for now */
+                digest   = &hash[MD5_DIGEST_SIZE];
+                hashType = SHAh;
+                digestSz = SHA_DIGEST_SIZE;
+
+                sigSz = EncodeSignature(encodedSig, digest, digestSz, hashType);
+
+                if (sigSz != ret || XMEMCMP(out, encodedSig, sigSz) != 0)
+                    return VERIFY_SIGN_ERROR;
+            }
+            else { 
+                if (ret != sizeof(hash) || XMEMCMP(out, hash, sizeof(hash)))
+                    return VERIFY_SIGN_ERROR;
+            }
+        }
+
+        ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE;
+
+        return 0;
+        }  /* dh_kea */
+    #endif /* OPENSSL_EXTRA */
+        return -1;  /* not supported by build */
+    }
+
+
+    int SendClientKeyExchange(SSL* ssl)
+    {
+        byte   encSecret[MAX_NTRU_ENCRYPT_SZ];
+        word32 encSz = 0;
+        word32 idx = 0;
+        int    ret = 0;
+
+        if (ssl->specs.kea == rsa_kea) {
+            RNG_GenerateBlock(&ssl->rng, ssl->arrays.preMasterSecret,
+                              SECRET_LEN);
+            ssl->arrays.preMasterSecret[0] = ssl->chVersion.major;
+            ssl->arrays.preMasterSecret[1] = ssl->chVersion.minor;
+            ssl->arrays.preMasterSz = SECRET_LEN;
+
+            if (ssl->peerRsaKeyPresent == 0)
+                return NO_PEER_KEY;
+
+            ret = RsaPublicEncrypt(ssl->arrays.preMasterSecret, SECRET_LEN,
+                             encSecret, sizeof(encSecret), &ssl->peerRsaKey,
+                             &ssl->rng);
+            if (ret > 0) {
+                encSz = ret;
+                ret = 0;   /* set success to 0 */
+            }
+        #ifdef OPENSSL_EXTRA
+        } else if (ssl->specs.kea == diffie_hellman_kea) {
+            buffer  serverP   = ssl->buffers.serverDH_P;
+            buffer  serverG   = ssl->buffers.serverDH_G;
+            buffer  serverPub = ssl->buffers.serverDH_Pub;
+            byte    priv[ENCRYPT_LEN];
+            word32  privSz;
+            DhKey   key;
+
+            if (serverP.buffer == 0 || serverG.buffer == 0 ||
+                                       serverPub.buffer == 0)
+                return NO_PEER_KEY;
+
+            InitDhKey(&key);
+            ret = DhSetKey(&key, serverP.buffer, serverP.length,
+                           serverG.buffer, serverG.length);
+            if (ret == 0)
+                /* for DH, encSecret is Yc, agree is pre-master */
+                ret = DhGenerateKeyPair(&key, &ssl->rng, priv, &privSz,
+                                        encSecret, &encSz);
+            if (ret == 0)
+                ret = DhAgree(&key, ssl->arrays.preMasterSecret,
+                              &ssl->arrays.preMasterSz, priv, privSz,
+                              serverPub.buffer, serverPub.length);
+            FreeDhKey(&key);
+        #endif /* OPENSSL_EXTRA */
+        #ifndef NO_PSK
+        } else if (ssl->specs.kea == psk_kea) {
+            byte* pms = ssl->arrays.preMasterSecret;
+
+            ssl->arrays.psk_keySz = ssl->options.client_psk_cb(ssl,
+                ssl->arrays.server_hint, ssl->arrays.client_identity,
+                MAX_PSK_ID_LEN, ssl->arrays.psk_key, MAX_PSK_KEY_LEN);
+            if (ssl->arrays.psk_keySz == 0 || 
+                ssl->arrays.psk_keySz > MAX_PSK_KEY_LEN)
+                return PSK_KEY_ERROR;
+            encSz = (word32)XSTRLEN(ssl->arrays.client_identity);
+            if (encSz > MAX_PSK_ID_LEN) return CLIENT_ID_ERROR;
+            XMEMCPY(encSecret, ssl->arrays.client_identity, encSz);
+
+            /* make psk pre master secret */
+            /* length of key + length 0s + length of key + key */
+            c16toa((word16)ssl->arrays.psk_keySz, pms);
+            pms += 2;
+            XMEMSET(pms, 0, ssl->arrays.psk_keySz);
+            pms += ssl->arrays.psk_keySz;
+            c16toa((word16)ssl->arrays.psk_keySz, pms);
+            pms += 2;
+            XMEMCPY(pms, ssl->arrays.psk_key, ssl->arrays.psk_keySz);
+            ssl->arrays.preMasterSz = ssl->arrays.psk_keySz * 2 + 4;
+        #endif /* NO_PSK */
+        #ifdef HAVE_NTRU
+        } else if (ssl->specs.kea == ntru_kea) {
+            word32 rc;
+            word16 cipherLen = sizeof(encSecret);
+            DRBG_HANDLE drbg;
+            static uint8_t const cyasslStr[] = {
+                'C', 'y', 'a', 'S', 'S', 'L', ' ', 'N', 'T', 'R', 'U'
+            };
+
+            RNG_GenerateBlock(&ssl->rng, ssl->arrays.preMasterSecret,
+                              SECRET_LEN);
+            ssl->arrays.preMasterSz = SECRET_LEN;
+
+            if (ssl->peerNtruKeyPresent == 0)
+                return NO_PEER_KEY;
+
+            rc = crypto_drbg_instantiate(MAX_NTRU_BITS, cyasslStr,
+                                          sizeof(cyasslStr), GetEntropy, &drbg);
+            if (rc != DRBG_OK)
+                return NTRU_DRBG_ERROR; 
+
+            rc = crypto_ntru_encrypt(drbg, ssl->peerNtruKeyLen,ssl->peerNtruKey,
+                                     ssl->arrays.preMasterSz,
+                                     ssl->arrays.preMasterSecret,
+                                     &cipherLen, encSecret);
+            crypto_drbg_uninstantiate(drbg);
+            if (rc != NTRU_OK)
+                return NTRU_ENCRYPT_ERROR;
+
+            encSz = cipherLen;
+            ret = 0;
+        #endif /* HAVE_NTRU */
+        } else
+            return -1; /* unsupported kea */
+
+        if (ret == 0) {
+            byte              *output;
+            int                sendSz;
+            word32             tlsSz = 0;
+            
+            if (ssl->options.tls || ssl->specs.kea == diffie_hellman_kea)
+                tlsSz = 2;
+
+            sendSz = encSz + tlsSz + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
+            idx    = HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
+
+            #ifdef CYASSL_DTLS
+                if (ssl->options.dtls) {
+                    sendSz += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
+                    idx    += DTLS_HANDSHAKE_EXTRA + DTLS_RECORD_EXTRA;
+                }
+            #endif
+
+            /* check for avalaible size */
+            if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
+                return ret;
+
+            /* get ouput buffer */
+            output = ssl->buffers.outputBuffer.buffer + 
+                     ssl->buffers.outputBuffer.idx;
+
+            AddHeaders(output, encSz + tlsSz, client_key_exchange, ssl);
+
+            if (tlsSz) {
+                c16toa((word16)encSz, &output[idx]);
+                idx += 2;
+            }
+            XMEMCPY(output + idx, encSecret, encSz);
+            idx += encSz;
+
+            HashOutput(ssl, output, sendSz, 0);
+
+            #ifdef CYASSL_CALLBACKS
+                if (ssl->hsInfoOn)
+                    AddPacketName("ClientKeyExchange", &ssl->handShakeInfo);
+                if (ssl->toInfoOn)
+                    AddPacketInfo("ClientKeyExchange", &ssl->timeoutInfo,
+                                  output, sendSz, ssl->heap);
+            #endif
+
+            ssl->buffers.outputBuffer.length += sendSz;
+
+            ret = SendBuffered(ssl);
+        }
+    
+        if (ret == 0 || ret == WANT_WRITE) {
+            int tmpRet = MakeMasterSecret(ssl);
+            if (tmpRet != 0)
+                ret = tmpRet;   /* save WANT_WRITE unless more serious */
+            ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
+        }
+
+        return ret;
+    }
+
+    int SendCertificateVerify(SSL* ssl)
+    {
+        byte              *output;
+        int                sendSz = 0, length, ret;
+        word32             idx = 0;
+        RsaKey             key;
+
+        if (ssl->options.sendVerify == SEND_BLANK_CERT)
+            return 0;  /* sent blank cert, can't verify */
+
+        /* check for avalaible size */
+        if ((ret = CheckAvalaibleSize(ssl, MAX_CERT_VERIFY_SZ)) != 0)
+            return ret;
+
+        /* get ouput buffer */
+        output = ssl->buffers.outputBuffer.buffer +
+                 ssl->buffers.outputBuffer.idx;
+
+        BuildCertHashes(ssl, &ssl->certHashes);
+
+        /* TODO: when add DSS support check here  */
+        InitRsaKey(&key, ssl->heap);
+        ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &idx, &key,
+                                  ssl->buffers.key.length); 
+        if (ret == 0) {
+            byte*  verify = (byte*)&output[RECORD_HEADER_SZ +
+                                           HANDSHAKE_HEADER_SZ];
+            byte*  signBuffer = ssl->certHashes.md5;
+            word32 signSz = sizeof(Hashes);
+
+            #ifdef CYASSL_DTLS
+                if (ssl->options.dtls)
+                    verify += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+            #endif
+            length = RsaEncryptSize(&key);
+            c16toa((word16)length, verify);   /* prepend verify header */
+
+            if (IsAtLeastTLSv1_2(ssl)) {
+                byte  encodedSig[MAX_ENCODED_SIG_SZ];
+                byte* digest;
+                int   hashType;
+                int   digestSz;
+
+                /* sha1 for now */
+                digest   = ssl->certHashes.sha;
+                hashType = SHAh;
+                digestSz = SHA_DIGEST_SIZE;
+
+                signSz = EncodeSignature(encodedSig, digest, digestSz,hashType);
+                signBuffer = encodedSig;
+            }
+
+            ret = RsaSSL_Sign(signBuffer, signSz, verify +
+                  VERIFY_HEADER, ENCRYPT_LEN, &key, &ssl->rng);
+
+            if (ret > 0) {
+                ret = 0;  /* reset */
+
+                AddHeaders(output, length + VERIFY_HEADER, certificate_verify,
+                           ssl);
+
+                sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ + length +
+                                            VERIFY_HEADER;
+                #ifdef CYASSL_DTLS
+                    if (ssl->options.dtls)
+                        sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+                #endif
+                HashOutput(ssl, output, sendSz, 0);
+            }
+        }
+
+        FreeRsaKey(&key);
+
+        if (ret == 0) {
+            #ifdef CYASSL_CALLBACKS
+                if (ssl->hsInfoOn)
+                    AddPacketName("CertificateVerify", &ssl->handShakeInfo);
+                if (ssl->toInfoOn)
+                    AddPacketInfo("CertificateVerify", &ssl->timeoutInfo,
+                                  output, sendSz, ssl->heap);
+            #endif
+            ssl->buffers.outputBuffer.length += sendSz;
+            return SendBuffered(ssl);
+        }
+        else
+            return ret;
+    }
+
+
+
+#endif /* NO_CYASSL_CLIENT */
+
+
+#ifndef NO_CYASSL_SERVER
+
+    int SendServerHello(SSL* ssl)
+    {
+        byte              *output;
+        word32             length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+        int                sendSz;
+        int                ret;
+
+        length = sizeof(ProtocolVersion) + RAN_LEN
+               + ID_LEN + ENUM_LEN                 
+               + SUITE_LEN 
+               + ENUM_LEN;
+
+        /* check for avalaible size */
+        if ((ret = CheckAvalaibleSize(ssl, MAX_HELLO_SZ)) != 0)
+            return ret;
+
+        /* get ouput buffer */
+        output = ssl->buffers.outputBuffer.buffer + 
+                 ssl->buffers.outputBuffer.idx;
+
+        sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
+        AddHeaders(output, length, server_hello, ssl);
+
+        #ifdef CYASSL_DTLS
+            if (ssl->options.dtls) {
+                idx    += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+                sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+            }
+        #endif
+        /* now write to output */
+            /* first version */
+        XMEMCPY(output + idx, &ssl->version, sizeof(ProtocolVersion));
+        idx += sizeof(ProtocolVersion);
+
+            /* then random */
+        if (!ssl->options.resuming)         
+            RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN);
+        XMEMCPY(output + idx, ssl->arrays.serverRandom, RAN_LEN);
+        idx += RAN_LEN;
+
+#ifdef SHOW_SECRETS
+        {
+            int j;
+            printf("server random: ");
+            for (j = 0; j < RAN_LEN; j++)
+                printf("%02x", ssl->arrays.serverRandom[j]);
+            printf("\n");
+        }
+#endif
+            /* then session id */
+        output[idx++] = ID_LEN;
+        if (!ssl->options.resuming)
+            RNG_GenerateBlock(&ssl->rng, ssl->arrays.sessionID, ID_LEN);
+        XMEMCPY(output + idx, ssl->arrays.sessionID, ID_LEN);
+        idx += ID_LEN;
+
+            /* then cipher suite */
+        output[idx++] = 0x00; 
+        output[idx++] = ssl->options.cipherSuite;
+
+            /* last, compression */
+        if (ssl->options.usingCompression)
+            output[idx++] = ZLIB_COMPRESSION;
+        else
+            output[idx++] = NO_COMPRESSION;
+            
+        ssl->buffers.outputBuffer.length += sendSz;
+        HashOutput(ssl, output, sendSz, 0);
+
+        #ifdef CYASSL_CALLBACKS
+            if (ssl->hsInfoOn)
+                AddPacketName("ServerHello", &ssl->handShakeInfo);
+            if (ssl->toInfoOn)
+                AddPacketInfo("ServerHello", &ssl->timeoutInfo, output, sendSz,
+                              ssl->heap);
+        #endif
+
+        ssl->options.serverState = SERVER_HELLO_COMPLETE;
+
+        return SendBuffered(ssl);
+    }
+
+    int SendServerKeyExchange(SSL* ssl)
+    {
+        int ret = 0;
+
+        if (ssl->specs.kea != psk_kea) return 0;
+
+        #ifndef NO_PSK
+        {
+            byte    *output;
+            word32   length, idx = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+            int      sendSz;
+            if (ssl->arrays.server_hint[0] == 0) return 0; /* don't send */
+
+            /* include size part */
+            length = (word32)XSTRLEN(ssl->arrays.server_hint);
+            if (length > MAX_PSK_ID_LEN) return SERVER_HINT_ERROR;
+            length += + HINT_LEN_SZ;
+            sendSz = length + HANDSHAKE_HEADER_SZ + RECORD_HEADER_SZ;
+
+            #ifdef CYASSL_DTLS 
+                if (ssl->options.dtls) {
+                    sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+                    idx    += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+                }
+            #endif
+            /* check for avalaible size */
+            if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
+               return ret;
+
+            /* get ouput buffer */
+            output = ssl->buffers.outputBuffer.buffer + 
+                     ssl->buffers.outputBuffer.idx;
+
+            AddHeaders(output, length, server_key_exchange, ssl);
+
+            /* key data */
+            c16toa((word16)(length - HINT_LEN_SZ), output + idx);
+            idx += HINT_LEN_SZ;
+            XMEMCPY(output + idx, ssl->arrays.server_hint, length - HINT_LEN_SZ);
+
+            HashOutput(ssl, output, sendSz, 0);
+
+            #ifdef CYASSL_CALLBACKS
+                if (ssl->hsInfoOn)
+                    AddPacketName("ServerKeyExchange", &ssl->handShakeInfo);
+                if (ssl->toInfoOn)
+                    AddPacketInfo("ServerKeyExchange", &ssl->timeoutInfo,
+                                  output, sendSz, ssl->heap);
+            #endif
+
+            ssl->buffers.outputBuffer.length += sendSz;
+            ret = SendBuffered(ssl);
+            ssl->options.serverState = SERVER_KEYEXCHANGE_COMPLETE;
+        }
+        #endif /*NO_PSK */
+
+        return ret;
+    }
+
+
+    static int MatchSuite(SSL* ssl, Suites* peerSuites)
+    {
+        word16 i, j;
+
+        /* & 0x1 equivalent % 2 */
+        if (peerSuites->suiteSz == 0 || peerSuites->suiteSz & 0x1)
+            return MATCH_SUITE_ERROR;
+
+        /* start with best, if a match we are good, Ciphers are at odd index
+           since all SSL and TLS ciphers have 0x00 first byte               */
+        for (i = 1; i < ssl->suites.suiteSz; i += 2)
+            for (j = 1; j < peerSuites->suiteSz; j += 2)
+                if (ssl->suites.suites[i] == peerSuites->suites[j]) {
+                    ssl->options.cipherSuite = ssl->suites.suites[i];
+                    return SetCipherSpecs(ssl);
+                }
+
+        return MATCH_SUITE_ERROR;
+    }
+
+
+    /* process alert, return level */
+    int ProcessOldClientHello(SSL* ssl, const byte* input, word32* inOutIdx,
+                              word32 inSz, word16 sz)
+    {
+        word32          idx = *inOutIdx;
+        word16          sessionSz;
+        word16          randomSz;
+        word16          i, j;
+        ProtocolVersion pv;
+        Suites          clSuites;
+
+#ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn)
+            AddPacketName("ClientHello", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddLateName("ClientHello", &ssl->timeoutInfo);
+#endif
+
+        /* manually hash input since different format */
+        Md5Update(&ssl->hashMd5, input + idx, sz);
+        ShaUpdate(&ssl->hashSha, input + idx, sz);
+
+        /* does this value mean client_hello? */
+        idx++;
+
+        /* version */
+        pv.major = input[idx++];
+        pv.minor = input[idx++];
+        ssl->chVersion = pv;  /* store */
+
+        if (ssl->version.minor > 0 && pv.minor == 0) {
+            if (!ssl->options.downgrade)
+                return VERSION_ERROR;
+            /* turn off tls */
+            ssl->options.tls    = 0;
+            ssl->options.tls1_1 = 0;
+            ssl->version.minor  = 0;
+            InitSuites(&ssl->suites, ssl->version, ssl->options.haveDH, FALSE,
+                       ssl->options.haveNTRU);
+        }
+
+        /* suite size */
+        ato16(&input[idx], &clSuites.suiteSz);
+        idx += 2;
+
+        if (clSuites.suiteSz > MAX_SUITE_SZ)
+            return BUFFER_ERROR;
+
+        /* session size */
+        ato16(&input[idx], &sessionSz);
+        idx += 2;
+
+        if (sessionSz > ID_LEN)
+            return BUFFER_ERROR;
+    
+        /* random size */
+        ato16(&input[idx], &randomSz);
+        idx += 2;
+
+        if (randomSz > RAN_LEN)
+            return BUFFER_ERROR;
+
+        /* suites */
+        for (i = 0, j = 0; i < clSuites.suiteSz; i += 3) {    
+            byte first = input[idx++];
+            if (!first) { /* implicit: skip sslv2 type */
+                XMEMCPY(&clSuites.suites[j], &input[idx], 2);
+                j += 2;
+            }
+            idx += 2;
+        }
+        clSuites.suiteSz = j;
+
+        /* session id */
+        if (sessionSz) {
+            XMEMCPY(ssl->arrays.sessionID, input + idx, sessionSz);
+            idx += sessionSz;
+            ssl->options.resuming = 1;
+        }
+
+        /* random */
+        if (randomSz < RAN_LEN)
+            XMEMSET(ssl->arrays.clientRandom, 0, RAN_LEN - randomSz);
+        XMEMCPY(&ssl->arrays.clientRandom[RAN_LEN - randomSz], input + idx,
+               randomSz);
+        idx += randomSz;
+
+        if (ssl->options.usingCompression)
+            ssl->options.usingCompression = 0;  /* turn off */
+
+        ssl->options.clientState = CLIENT_HELLO_COMPLETE;
+        *inOutIdx = idx;
+
+        /* DoClientHello uses same resume code */
+        while (ssl->options.resuming) {  /* let's try */
+            SSL_SESSION* session = GetSession(ssl, ssl->arrays.masterSecret);
+            if (!session) {
+                ssl->options.resuming = 0;
+                break;   /* session lookup failed */
+            }
+            if (MatchSuite(ssl, &clSuites) < 0)
+                return UNSUPPORTED_SUITE;
+
+            RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN);
+            if (ssl->options.tls)
+                DeriveTlsKeys(ssl);
+            else
+                DeriveKeys(ssl);
+            ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
+
+            return 0;
+        }
+
+        return MatchSuite(ssl, &clSuites);
+    }
+
+
+    static int DoClientHello(SSL* ssl, const byte* input, word32* inOutIdx,
+                             word32 totalSz, word32 helloSz)
+    {
+        byte b;
+        ProtocolVersion pv;
+        Suites          clSuites;
+        word32 i = *inOutIdx;
+        word32 begin = i;
+
+#ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn) AddPacketName("ClientHello", &ssl->handShakeInfo);
+        if (ssl->toInfoOn) AddLateName("ClientHello", &ssl->timeoutInfo);
+#endif
+        /* make sure can read up to session */
+        if (i + sizeof(pv) + RAN_LEN + ENUM_LEN > totalSz)
+            return INCOMPLETE_DATA;
+
+        XMEMCPY(&pv, input + i, sizeof(pv));
+        ssl->chVersion = pv;   /* store */
+        i += sizeof(pv);
+        if (ssl->version.minor > 0 && pv.minor == 0) {
+            if (!ssl->options.downgrade)
+                return VERSION_ERROR;
+            /* turn off tls */
+            ssl->options.tls    = 0;
+            ssl->options.tls1_1 = 0;
+            ssl->version.minor  = 0;
+            InitSuites(&ssl->suites, ssl->version, ssl->options.haveDH, FALSE,
+                       ssl->options.haveNTRU);
+        }
+        /* random */
+        XMEMCPY(ssl->arrays.clientRandom, input + i, RAN_LEN);
+        i += RAN_LEN;
+
+#ifdef SHOW_SECRETS
+        {
+            int j;
+            printf("client random: ");
+            for (j = 0; j < RAN_LEN; j++)
+                printf("%02x", ssl->arrays.clientRandom[j]);
+            printf("\n");
+        }
+#endif
+        /* session id */
+        b = input[i++];
+        if (b) {
+            if (i + ID_LEN > totalSz)
+                return INCOMPLETE_DATA;
+            XMEMCPY(ssl->arrays.sessionID, input + i, ID_LEN);
+            i += b;
+            ssl->options.resuming= 1; /* client wants to resume */
+        }
+        
+        #ifdef CYASSL_DTLS
+            /* cookie */
+            if (ssl->options.dtls) {
+                b = input[i++];
+                if (b) {
+                    if (b > MAX_COOKIE_LEN)
+                        return BUFFER_ERROR;
+                    if (i + b > totalSz)
+                        return INCOMPLETE_DATA;
+                    XMEMCPY(ssl->arrays.cookie, input + i, b);
+                    i += b;
+                }
+            }
+        #endif
+
+        if (i + LENGTH_SZ > totalSz)
+            return INCOMPLETE_DATA;
+        /* suites */
+        ato16(&input[i], &clSuites.suiteSz);
+        i += 2;
+
+        /* suites and comp len */
+        if (i + clSuites.suiteSz + ENUM_LEN > totalSz)
+            return INCOMPLETE_DATA;
+        if (clSuites.suiteSz > MAX_SUITE_SZ)
+            return BUFFER_ERROR;
+        XMEMCPY(clSuites.suites, input + i, clSuites.suiteSz);
+        i += clSuites.suiteSz;
+
+        b = input[i++];  /* comp len */
+        if (i + b > totalSz)
+            return INCOMPLETE_DATA;
+
+        if (ssl->options.usingCompression) {
+            int match = 0;
+            while (b--) {
+                byte comp = input[i++];
+                if (comp == ZLIB_COMPRESSION)
+                    match = 1;
+            }
+            if (!match)
+                ssl->options.usingCompression = 0;  /* turn off */
+        }
+        else
+            i += b;  /* ignore, since we're not on */
+
+        ssl->options.clientState = CLIENT_HELLO_COMPLETE;
+
+        *inOutIdx = i;
+        if ( (i - begin) < helloSz)
+            *inOutIdx = begin + helloSz;  /* skip extensions */
+        
+        /* ProcessOld uses same resume code */
+        while (ssl->options.resuming) {  /* let's try */
+            SSL_SESSION* session = GetSession(ssl, ssl->arrays.masterSecret);
+            if (!session) {
+                ssl->options.resuming = 0;
+                break;   /* session lookup failed */
+            }
+            if (MatchSuite(ssl, &clSuites) < 0)
+                return UNSUPPORTED_SUITE;
+
+            RNG_GenerateBlock(&ssl->rng, ssl->arrays.serverRandom, RAN_LEN);
+            if (ssl->options.tls)
+                DeriveTlsKeys(ssl);
+            else
+                DeriveKeys(ssl);
+            ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
+
+            return 0;
+        }
+        return MatchSuite(ssl, &clSuites);
+    }
+
+
+    static int DoCertificateVerify(SSL* ssl, byte* input, word32* inOutsz,
+                                   word32 totalSz)
+    {
+        word16      sz = 0;
+        word32      i = *inOutsz;
+        int         ret = VERIFY_CERT_ERROR;   /* start in error state */
+        byte*       sig;
+        byte*       out;
+        int         outLen;
+
+        #ifdef CYASSL_CALLBACKS
+            if (ssl->hsInfoOn)
+                AddPacketName("CertificateVerify", &ssl->handShakeInfo);
+            if (ssl->toInfoOn)
+                AddLateName("CertificateVerify", &ssl->timeoutInfo);
+        #endif
+        if ( (i + VERIFY_HEADER) > totalSz)
+            return INCOMPLETE_DATA;
+
+        ato16(&input[i], &sz);
+        i += VERIFY_HEADER;
+
+        if ( (i + sz) > totalSz)
+            return INCOMPLETE_DATA;
+
+        if (sz > ENCRYPT_LEN)
+            return BUFFER_ERROR;
+
+        sig = &input[i];
+        *inOutsz = i + sz;
+        /* TODO: when add DSS support check here  */
+        if (ssl->peerRsaKeyPresent != 0) {
+            outLen = RsaSSL_VerifyInline(sig, sz, &out, &ssl->peerRsaKey);
+
+            if (IsAtLeastTLSv1_2(ssl)) {
+                byte   encodedSig[MAX_ENCODED_SIG_SZ];
+                word32 sigSz;
+                byte*  digest;
+                int    hashType;
+                int    digestSz;
+
+                /* sha1 for now */
+                digest   = ssl->certHashes.sha;
+                hashType = SHAh;
+                digestSz = SHA_DIGEST_SIZE;
+
+                sigSz = EncodeSignature(encodedSig, digest, digestSz, hashType);
+
+                if (outLen == sigSz && XMEMCMP(out, encodedSig, sigSz) == 0)
+                    ret = 0;
+            }
+            else {
+                if (outLen == sizeof(ssl->certHashes) && XMEMCMP(out,
+                             ssl->certHashes.md5, sizeof(ssl->certHashes)) == 0)
+                    ret = 0;
+            }
+        }
+        return ret;
+    }
+
+
+    int SendServerHelloDone(SSL* ssl)
+    {
+        byte              *output;
+        int                sendSz = RECORD_HEADER_SZ + HANDSHAKE_HEADER_SZ;
+        int                ret;
+
+        #ifdef CYASSL_DTLS
+            if (ssl->options.dtls)
+                sendSz += DTLS_RECORD_EXTRA + DTLS_HANDSHAKE_EXTRA;
+        #endif
+        /* check for avalaible size */
+        if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
+            return ret;
+
+        /* get ouput buffer */
+        output = ssl->buffers.outputBuffer.buffer +
+                 ssl->buffers.outputBuffer.idx;
+
+        AddHeaders(output, 0, server_hello_done, ssl);
+
+        HashOutput(ssl, output, sendSz, 0);
+#ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn)
+            AddPacketName("ServerHelloDone", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddPacketInfo("ServerHelloDone", &ssl->timeoutInfo, output, sendSz,
+                          ssl->heap);
+#endif
+        ssl->options.serverState = SERVER_HELLODONE_COMPLETE;
+
+        ssl->buffers.outputBuffer.length += sendSz;
+
+        return SendBuffered(ssl);
+    }
+
+
+    int SendHelloVerifyRequest(SSL* ssl)
+    {
+        byte* output;
+        int   length = VERSION_SZ + ENUM_LEN;
+        int   idx    = DTLS_RECORD_HEADER_SZ + DTLS_HANDSHAKE_HEADER_SZ;
+        int   sendSz = length + idx;
+        int   ret;
+
+        /* check for avalaible size */
+        if ((ret = CheckAvalaibleSize(ssl, sendSz)) != 0)
+            return ret;
+
+        /* get ouput buffer */
+        output = ssl->buffers.outputBuffer.buffer +
+                 ssl->buffers.outputBuffer.idx;
+
+        AddHeaders(output, length, hello_verify_request, ssl);
+
+        XMEMCPY(output + idx, &ssl->chVersion, VERSION_SZ);
+        idx += VERSION_SZ;
+        output[idx++] = 0;     /* no cookie for now */
+
+        HashOutput(ssl, output, sendSz, 0);
+#ifdef CYASSL_CALLBACKS
+        if (ssl->hsInfoOn)
+            AddPacketName("HelloVerifyRequest", &ssl->handShakeInfo);
+        if (ssl->toInfoOn)
+            AddPacketInfo("HelloVerifyRequest", &ssl->timeoutInfo, output,
+                          sendSz, ssl->heap);
+#endif
+        ssl->options.serverState = SERVER_HELLOVERIFYREQUEST_COMPLETE;
+
+        ssl->buffers.outputBuffer.length += sendSz;
+
+        return SendBuffered(ssl);
+    }
+
+
+    static int DoClientKeyExchange(SSL* ssl, byte* input,
+                                   word32* inOutIdx)
+    {
+        int    ret = 0;
+        word32 length = 0;
+        byte*  out;
+
+        if (ssl->options.verifyPeer && ssl->options.failNoCert)
+            if (!ssl->options.havePeerCert) {
+                CYASSL_MSG("client didn't present peer cert");
+                return NO_PEER_CERT;
+            }
+
+        #ifdef CYASSL_CALLBACKS
+            if (ssl->hsInfoOn)
+                AddPacketName("ClientKeyExchange", &ssl->handShakeInfo);
+            if (ssl->toInfoOn)
+                AddLateName("ClientKeyExchange", &ssl->timeoutInfo);
+        #endif
+        if (ssl->specs.kea == rsa_kea) {
+            word32 idx = 0;
+            RsaKey key;
+            byte*  tmp = 0;
+
+            InitRsaKey(&key, ssl->heap);
+
+            if (ssl->buffers.key.buffer)
+                ret = RsaPrivateKeyDecode(ssl->buffers.key.buffer, &idx, &key,
+                                          ssl->buffers.key.length);
+            else
+                return NO_PRIVATE_KEY;
+
+            if (ret == 0) {
+                length = RsaEncryptSize(&key);
+                ssl->arrays.preMasterSz = SECRET_LEN;
+
+                if (ssl->options.tls)
+                    (*inOutIdx) += 2;
+                tmp = input + *inOutIdx;
+                *inOutIdx += length;
+
+                if (RsaPrivateDecryptInline(tmp, length, &out, &key) ==
+                                                             SECRET_LEN) {
+                    XMEMCPY(ssl->arrays.preMasterSecret, out, SECRET_LEN);
+                    if (ssl->arrays.preMasterSecret[0] != ssl->chVersion.major
+                     ||
+                        ssl->arrays.preMasterSecret[1] != ssl->chVersion.minor)
+
+                        ret = PMS_VERSION_ERROR;
+                    else
+                        ret = MakeMasterSecret(ssl);
+                }
+                else
+                    ret = RSA_PRIVATE_ERROR;
+            }
+
+            FreeRsaKey(&key);
+#ifndef NO_PSK
+        } else if (ssl->specs.kea == psk_kea) {
+            byte* pms = ssl->arrays.preMasterSecret;
+            word16 ci_sz;
+
+            ato16(&input[*inOutIdx], &ci_sz);
+            *inOutIdx += LENGTH_SZ;
+            if (ci_sz > MAX_PSK_ID_LEN) return CLIENT_ID_ERROR;
+
+            XMEMCPY(ssl->arrays.client_identity, &input[*inOutIdx], ci_sz);
+            *inOutIdx += ci_sz;
+            ssl->arrays.client_identity[ci_sz] = 0;
+
+            ssl->arrays.psk_keySz = ssl->options.server_psk_cb(ssl,
+                ssl->arrays.client_identity, ssl->arrays.psk_key,
+                MAX_PSK_KEY_LEN);
+            if (ssl->arrays.psk_keySz == 0 || 
+                ssl->arrays.psk_keySz > MAX_PSK_KEY_LEN) return PSK_KEY_ERROR;
+            
+            /* make psk pre master secret */
+            /* length of key + length 0s + length of key + key */
+            c16toa((word16)ssl->arrays.psk_keySz, pms);
+            pms += 2;
+            XMEMSET(pms, 0, ssl->arrays.psk_keySz);
+            pms += ssl->arrays.psk_keySz;
+            c16toa((word16)ssl->arrays.psk_keySz, pms);
+            pms += 2;
+            XMEMCPY(pms, ssl->arrays.psk_key, ssl->arrays.psk_keySz);
+            ssl->arrays.preMasterSz = ssl->arrays.psk_keySz * 2 + 4;
+
+            ret = MakeMasterSecret(ssl);
+#endif /* NO_PSK */
+#ifdef HAVE_NTRU
+        } else if (ssl->specs.kea == ntru_kea) {
+            word32 rc;
+            word16 cipherLen;
+            word16 plainLen = sizeof(ssl->arrays.preMasterSecret);
+            byte*  tmp;
+
+            if (!ssl->buffers.key.buffer)
+                return NO_PRIVATE_KEY;
+
+            ato16(&input[*inOutIdx], &cipherLen);
+            *inOutIdx += LENGTH_SZ;
+            if (cipherLen > MAX_NTRU_ENCRYPT_SZ)
+                return NTRU_KEY_ERROR;
+
+            tmp = input + *inOutIdx;
+            rc = crypto_ntru_decrypt((word16)ssl->buffers.key.length,
+                        ssl->buffers.key.buffer, cipherLen, tmp, &plainLen,
+                        ssl->arrays.preMasterSecret);
+
+            if (rc != NTRU_OK || plainLen != SECRET_LEN)
+                return NTRU_DECRYPT_ERROR;
+            *inOutIdx += cipherLen;
+
+            ssl->arrays.preMasterSz = plainLen;
+            ret = MakeMasterSecret(ssl);
+#endif /* HAVE_NTRU */
+        }
+
+        if (ret == 0) {
+            ssl->options.clientState = CLIENT_KEYEXCHANGE_COMPLETE;
+            if (ssl->options.verifyPeer)
+                BuildCertHashes(ssl, &ssl->certHashes);
+        }
+
+        return ret;
+    }
+
+#endif /* NO_CYASSL_SERVER */
+
+
+#ifdef SINGLE_THREADED
+
+int InitMutex(CyaSSL_Mutex* m)
+{
+    return 0;
+}
+
+
+int FreeMutex(CyaSSL_Mutex* m)
+{
+    return 0;
+}
+
+
+int LockMutex(CyaSSL_Mutex* m)
+{
+    return 0;
+}
+
+
+int UnLockMutex(CyaSSL_Mutex* m)
+{
+    return 0;
+}
+
+#else /* MULTI_THREAD */
+
+    #ifdef USE_WINDOWS_API
+
+        int InitMutex(CyaSSL_Mutex* m)
+        {
+            InitializeCriticalSection(m);
+            return 0;
+        }
+
+
+        int FreeMutex(CyaSSL_Mutex* m)
+        {
+            DeleteCriticalSection(m);
+            return 0;
+        }
+
+
+        int LockMutex(CyaSSL_Mutex* m)
+        {
+            EnterCriticalSection(m);
+            return 0;
+        }
+
+
+        int UnLockMutex(CyaSSL_Mutex* m)
+        {
+            LeaveCriticalSection(m);
+            return 0;
+        }
+
+    #elif defined(CYASSL_PTHREADS)
+
+        int InitMutex(CyaSSL_Mutex* m)
+        {
+            if (pthread_mutex_init(m, 0) == 0)
+                return 0;
+            else
+                return -1;
+        }
+
+
+        int FreeMutex(CyaSSL_Mutex* m)
+        {
+            if (pthread_mutex_destroy(m) == 0)
+                return 0;
+            else
+                return -1;
+        }
+
+
+        int LockMutex(CyaSSL_Mutex* m)
+        {
+            if (pthread_mutex_lock(m) == 0)
+                return 0;
+            else
+                return -1;
+        }
+
+
+        int UnLockMutex(CyaSSL_Mutex* m)
+        {
+            if (pthread_mutex_unlock(m) == 0)
+                return 0;
+            else
+                return -1;
+        }
+
+    #elif defined(THREADX)
+
+        int InitMutex(CyaSSL_Mutex* m)
+        {
+            if (tx_mutex_create(m, "CyaSSL Mutex", TX_NO_INHERIT) == 0)
+                return 0;
+            else
+                return -1;
+        }
+
+
+        int FreeMutex(CyaSSL_Mutex* m)
+        {
+            if (tx_mutex_delete(m) == 0)
+                return 0;
+            else
+                return -1;
+        }
+
+
+        int LockMutex(CyaSSL_Mutex* m)
+        {
+            if (tx_mutex_get(m, TX_WAIT_FOREVER) == 0)
+                return 0;
+            else
+                return -1;
+        }
+
+
+        int UnLockMutex(CyaSSL_Mutex* m)
+        {
+            if (tx_mutex_put(m) == 0)
+                return 0;
+            else
+                return -1;
+        }
+
+    #elif defined(MICRIUM)
+
+        int InitMutex(CyaSSL_Mutex* m)
+        {
+            #if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
+                if (NetSecure_OS_MutexCreate(m) == 0)
+                    return 0;
+                else
+                    return -1;
+            #else
+                return 0;
+            #endif
+        }
+
+
+        int FreeMutex(CyaSSL_Mutex* m)
+        {
+            #if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
+                if (NetSecure_OS_FreeMutex(m) == 0)
+                    return 0;
+                else
+                    return -1;
+            #else
+                return 0;
+            #endif
+        }
+
+
+        int LockMutex(CyaSSL_Mutex* m)
+        {
+            #if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
+                if (NetSecure_OS_LockMutex(m) == 0)
+                    return 0;
+                else
+                    return -1;
+            #else
+                return 0;
+            #endif
+        }
+
+
+        int UnLockMutex(CyaSSL_Mutex* m)
+        {
+            #if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
+                if (NetSecure_OS_UnLockMutex(m) == 0)
+                    return 0;
+                else
+                    return -1;
+            #else
+                return 0;
+            #endif
+
+        }
+
+    #endif /* USE_WINDOWS_API */
+#endif /* SINGLE_THREADED */
+
+
+#ifdef DEBUG_CYASSL
+
+    static int logging = 0;
+
+
+    int CyaSSL_Debugging_ON(void)
+    {
+        logging = 1;
+        return 0;
+    }
+
+
+    void CyaSSL_Debugging_OFF(void)
+    {
+        logging = 0;
+    }
+
+
+#ifdef THREADX
+    int dc_log_printf(char*, ...);
+#endif
+
+    void CYASSL_MSG(const char* msg)
+    {
+        if (logging) {
+#ifdef THREADX
+            dc_log_printf("%s\n", msg);
+#elif defined(MICRIUM)
+        #if (NET_SECURE_MGR_CFG_EN == DEF_ENABLED)
+            NetSecure_TraceOut((CPU_CHAR *)msg);
+        #endif
+#else
+            fprintf(stderr, "%s\n", msg);
+#endif
+        }
+    }
+
+
+    void CYASSL_ENTER(const char* msg)
+    {
+        if (logging) {
+            char buffer[80];
+            sprintf(buffer, "CyaSSL Entering %s", msg);
+            CYASSL_MSG(buffer);
+        }
+    }
+
+
+    void CYASSL_LEAVE(const char* msg, int ret)
+    {
+        if (logging) {
+            char buffer[80];
+            sprintf(buffer, "CyaSSL Leaving %s, return %d", msg, ret);
+            CYASSL_MSG(buffer);
+        }
+    }
+
+
+    void CYASSL_ERROR(int error)
+    {
+        if (logging) {
+            char buffer[80];
+            sprintf(buffer, "CyaSSL error occured, error = %d", error);
+            CYASSL_MSG(buffer);
+        }
+    }
+
+
+#else   /* DEBUG_CYASSL */
+
+    int CyaSSL_Debugging_ON(void)
+    {
+        return -1;    /* not compiled in */
+    }
+
+
+    void CyaSSL_Debugging_OFF(void)
+    {
+        /* already off */
+    }
+
+#endif  /* DEBUG_CYASSL */