ssh

Dependents:   OS

Files at this revision

API Documentation at this revision

Comitter:
sPymbed
Date:
Mon Nov 25 14:24:05 2019 +0000
Commit message:
first commit

Changed in this revision

src/internal.c Show annotated file Show diff for this revision Revisions of this file
src/io.c Show annotated file Show diff for this revision Revisions of this file
src/keygen.c Show annotated file Show diff for this revision Revisions of this file
src/log.c Show annotated file Show diff for this revision Revisions of this file
src/memory.c Show annotated file Show diff for this revision Revisions of this file
src/misc.c Show annotated file Show diff for this revision Revisions of this file
src/port.c Show annotated file Show diff for this revision Revisions of this file
src/ssh.c Show annotated file Show diff for this revision Revisions of this file
wolfssh/error.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/internal.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/keygen.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/log.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/memory.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/misc.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/port.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/settings.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/ssh.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/test.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/version.h Show annotated file Show diff for this revision Revisions of this file
wolfssh/version.h.in Show annotated file Show diff for this revision Revisions of this file
wolfssh/visibility.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r c4152c628df5 src/internal.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/internal.c	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,6697 @@
+/* internal.c
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The internal module contains the private data and functions. The public
+ * API calls into this module to do the work of processing the connections.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssh/ssh.h>
+#include <wolfssh/internal.h>
+#include <wolfssh/log.h>
+#include <wolfcrypt/asn.h>
+#include <wolfcrypt/dh.h>
+#include <wolfcrypt/rsa.h>
+#include <wolfcrypt/ecc.h>
+#include <wolfcrypt/hmac.h>
+#include <wolfcrypt/integer.h>
+#include <wolfcrypt/signature.h>
+
+//#include "esp_log.h"
+//#include "esp_system.h"
+
+#ifdef NO_INLINE
+    #include <wolfssh/misc.h>
+#else
+    #define WOLFSSH_MISC_INCLUDED
+    #include "src/misc.c"
+#endif
+
+
+static const char sshProtoIdStr[] = "SSH-2.0-wolfSSHv"
+                                    LIBWOLFSSH_VERSION_STRING
+                                    "\r\n";
+#ifndef WOLFSSH_DEFAULT_GEXDH_MIN
+    #define WOLFSSH_DEFAULT_GEXDH_MIN 1024
+#endif
+#ifndef WOLFSSH_DEFAULT_GEXDH_PREFERRED
+    #define WOLFSSH_DEFAULT_GEXDH_PREFERRED 3072
+#endif
+#ifndef WOLFSSH_DEFAULT_GEXDH_MAX
+    #define WOLFSSH_DEFAULT_GEXDH_MAX 8192
+#endif
+
+
+const char* GetErrorString(int err)
+{
+    (void)err;
+
+#ifdef NO_WOLFSSH_STRINGS
+    return "No wolfSSH strings available";
+#else
+    switch (err) {
+        case WS_SUCCESS:
+            return "function success";
+
+        case WS_FATAL_ERROR:
+            return "general function failure";
+
+        case WS_BAD_ARGUMENT:
+            return "bad function argument";
+
+        case WS_MEMORY_E:
+            return "memory allocation failure";
+
+        case WS_BUFFER_E:
+            return "input/output buffer size error";
+
+        case WS_PARSE_E:
+            return "general parsing error";
+
+        case WS_NOT_COMPILED:
+            return "feature not compiled in";
+
+        case WS_OVERFLOW_E:
+            return "would overflow if continued failure";
+
+        case WS_BAD_USAGE:
+            return "bad example usage";
+
+        case WS_SOCKET_ERROR_E:
+            return "socket error";
+
+        case WS_WANT_READ:
+            return "I/O callback would read block error";
+
+        case WS_WANT_WRITE:
+            return "I/O callback would write block error";
+
+        case WS_RECV_OVERFLOW_E:
+            return "receive buffer overflow";
+
+        case WS_VERSION_E:
+            return "peer version unsupported";
+
+        case WS_SEND_OOB_READ_E:
+            return "attempted to read buffer out of bounds";
+
+        case WS_INPUT_CASE_E:
+            return "bad process input state, programming error";
+
+        case WS_BAD_FILETYPE_E:
+            return "bad filetype";
+
+        case WS_UNIMPLEMENTED_E:
+            return "feature not implemented";
+
+        case WS_RSA_E:
+            return "RSA buffer error";
+
+        case WS_BAD_FILE_E:
+            return "bad file";
+
+        case WS_DECRYPT_E:
+            return "decrypt error";
+
+        case WS_ENCRYPT_E:
+            return "encrypt error";
+
+        case WS_VERIFY_MAC_E:
+            return "verify mac error";
+
+        case WS_CREATE_MAC_E:
+            return "verify mac error";
+
+        case WS_RESOURCE_E:
+            return "insufficient resources for new channel";
+
+        case WS_INVALID_CHANTYPE:
+            return "peer requested invalid channel type";
+
+        case WS_INVALID_CHANID:
+            return "peer requested invalid channel id";
+
+        case WS_INVALID_USERNAME:
+            return "invalid user name";
+
+        case WS_CRYPTO_FAILED:
+            return "crypto action failed";
+
+        case WS_INVALID_STATE_E:
+            return "invalid state";
+
+        case WS_REKEYING:
+            return "rekeying with peer";
+
+        case WS_INVALID_PRIME_CURVE:
+            return "invalid prime curve in ecc";
+
+        case WS_ECC_E:
+            return "ECDSA buffer error";
+
+        case WS_CHANOPEN_FAILED:
+            return "peer returned channel open failure";
+
+        case WS_CHANNEL_CLOSED:
+            return "channel closed";
+
+        default:
+            return "Unknown error code";
+    }
+#endif
+}
+
+
+static int wsHighwater(byte dir, void* ctx)
+{
+    int ret = WS_SUCCESS;
+
+    (void)dir;
+
+    if (ctx) {
+        WOLFSSH* ssh = (WOLFSSH*)ctx;
+
+        WLOG(WS_LOG_DEBUG, "HIGHWATER MARK: (%u) %s",
+             wolfSSH_GetHighwater(ssh),
+             (dir == WOLFSSH_HWSIDE_RECEIVE) ? "receive" : "transmit");
+
+        ret = wolfSSH_TriggerKeyExchange(ssh);
+    }
+
+    return ret;
+}
+
+
+static INLINE void HighwaterCheck(WOLFSSH* ssh, byte side)
+{
+    if (!ssh->highwaterFlag && ssh->highwaterMark &&
+        (ssh->txCount >= ssh->highwaterMark ||
+         ssh->rxCount >= ssh->highwaterMark)) {
+
+        WLOG(WS_LOG_DEBUG, "%s over high water mark",
+             (side == WOLFSSH_HWSIDE_TRANSMIT) ? "Transmit" : "Receive");
+
+        ssh->highwaterFlag = 1;
+
+        if (ssh->ctx->highwaterCb)
+            ssh->ctx->highwaterCb(side, ssh->highwaterCtx);
+    }
+}
+
+
+static HandshakeInfo* HandshakeInfoNew(void* heap)
+{
+    HandshakeInfo* newHs;
+
+    WLOG(WS_LOG_DEBUG, "Entering HandshakeInfoNew()");
+    newHs = (HandshakeInfo*)WMALLOC(sizeof(HandshakeInfo),
+                                    heap, DYNTYPE_HS);
+    if (newHs != NULL) {
+        WMEMSET(newHs, 0, sizeof(HandshakeInfo));
+        newHs->kexId = ID_NONE;
+        newHs->pubKeyId  = ID_NONE;
+        newHs->encryptId = ID_NONE;
+        newHs->macId = ID_NONE;
+        newHs->blockSz = MIN_BLOCK_SZ;
+        newHs->hashId = WC_HASH_TYPE_NONE;
+        newHs->dhGexMinSz = WOLFSSH_DEFAULT_GEXDH_MIN;
+        newHs->dhGexPreferredSz = WOLFSSH_DEFAULT_GEXDH_PREFERRED;
+        newHs->dhGexMaxSz = WOLFSSH_DEFAULT_GEXDH_MAX;
+    }
+
+    return newHs;
+}
+
+void ForceZero(const void* mem, word32 length){
+	}
+
+static void HandshakeInfoFree(HandshakeInfo* hs, void* heap)
+{
+    (void)heap;
+
+    WLOG(WS_LOG_DEBUG, "Entering HandshakeInfoFree()");
+    if (hs) {
+        WFREE(hs->kexInit, heap, DYNTYPE_STRING);
+        WFREE(hs->primeGroup, heap, DYNTYPE_MPINT);
+        WFREE(hs->generator, heap, DYNTYPE_MPINT);
+        ForceZero(hs, sizeof(HandshakeInfo));
+        WFREE(hs, heap, DYNTYPE_HS);
+    }
+}
+
+
+#ifdef DEBUG_WOLFSSH
+
+static const char cannedBanner[] =
+    "CANNED BANNER\r\n"
+    "This server is an example test server. "
+    "It should have its own banner, but\r\n"
+    "it is currently using a canned one in "
+    "the library. Be happy or not.\r\n";
+static const word32 cannedBannerSz = sizeof(cannedBanner) - 1;
+
+#endif /* DEBUG_WOLFSSH */
+
+
+WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap)
+{
+    WLOG(WS_LOG_DEBUG, "Entering CtxInit()");
+
+    if (ctx == NULL)
+        return ctx;
+
+    WMEMSET(ctx, 0, sizeof(WOLFSSH_CTX));
+
+    if (heap)
+        ctx->heap = heap;
+
+    ctx->side = side;
+#ifndef WOLFSSH_USER_IO
+    ctx->ioRecvCb = wsEmbedRecv;
+    ctx->ioSendCb = wsEmbedSend;
+#endif /* WOLFSSH_USER_IO */
+    ctx->highwaterMark = DEFAULT_HIGHWATER_MARK;
+    ctx->highwaterCb = wsHighwater;
+#ifdef DEBUG_WOLFSSH
+    ctx->banner = cannedBanner;
+    ctx->bannerSz = cannedBannerSz;
+#endif /* DEBUG_WOLFSSH */
+
+    return ctx;
+}
+
+
+void CtxResourceFree(WOLFSSH_CTX* ctx)
+{
+    WLOG(WS_LOG_DEBUG, "Entering CtxResourceFree()");
+
+    if (ctx->privateKey) {
+        ForceZero(ctx->privateKey, ctx->privateKeySz);
+        WFREE(ctx->privateKey, ctx->heap, DYNTYPE_PRIVKEY);
+    }
+}
+
+
+WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx)
+{
+    HandshakeInfo* handshake;
+    RNG*           rng;
+    void*          heap;
+
+    WLOG(WS_LOG_DEBUG, "Entering SshInit()");
+    //ESP_LOGI("WOLFSSH", "SshInit()");
+
+    if (ssh == NULL || ctx == NULL)
+        return ssh;
+    heap = ctx->heap;
+    //ESP_LOGI("WOLFSSH", "heap=ctx->heap");
+
+    handshake = HandshakeInfoNew(heap);
+    //ESP_LOGI("WOLFSSH", "HandshakeInfoNew(heap) OK");
+    rng = (RNG*)WMALLOC(sizeof(RNG), heap, DYNTYPE_RNG);
+    //ESP_LOGI("WOLFSSH", "malloc RNG OK");
+
+    //if(handshake == NULL) ESP_LOGE("WOLFSSH", "handshake is NULL");
+    //if(rng == NULL) ESP_LOGE("WOLFSSH", "rng is NULL");
+
+
+    //ESP_LOGI("WOLFSSH", "wc_InitRng(rng), free heap:%d", esp_get_free_heap_size());
+
+    int wc_InitRng_result = wc_InitRng(rng);
+
+    if (handshake == NULL || rng == NULL || wc_InitRng_result != 0) {
+    	//ESP_LOGE("WOLFSSH", "Cannot allocate memory, wc_InitRng_result ret=%d", wc_InitRng_result);
+        WLOG(WS_LOG_DEBUG, "SshInit: Cannot allocate memory.\n");
+        WFREE(handshake, heap, DYNTYPE_HS);
+        WFREE(rng, heap, DYNTYPE_RNG);
+        wolfSSH_free(ssh);
+        return NULL;
+    }
+
+    WMEMSET(ssh, 0, sizeof(WOLFSSH));  /* default init to zeros */
+    //ESP_LOGI("WOLFSSH", "memset WOLFSSH");
+
+    ssh->ctx         = ctx;
+    ssh->error       = WS_SUCCESS;
+    ssh->rfd         = -1;         /* set to invalid */
+    ssh->wfd         = -1;         /* set to invalid */
+    ssh->ioReadCtx   = &ssh->rfd;  /* prevent invalid access if not correctly */
+    ssh->ioWriteCtx  = &ssh->wfd;  /* set */
+    ssh->highwaterMark = ctx->highwaterMark;
+    ssh->highwaterCtx  = (void*)ssh;
+    ssh->acceptState = ACCEPT_BEGIN;
+    ssh->clientState = CLIENT_BEGIN;
+    ssh->isKeying    = 1;
+    ssh->nextChannel = DEFAULT_NEXT_CHANNEL;
+    ssh->blockSz     = MIN_BLOCK_SZ;
+    ssh->encryptId   = ID_NONE;
+    ssh->macId       = ID_NONE;
+    ssh->peerBlockSz = MIN_BLOCK_SZ;
+    ssh->rng         = rng;
+    ssh->kSz         = sizeof(ssh->k);
+    ssh->handshake   = handshake;
+
+    //ESP_LOGI("WOLFSSH", "BufferInit...");
+    if (BufferInit(&ssh->inputBuffer, 0, ctx->heap) != WS_SUCCESS ||
+        BufferInit(&ssh->outputBuffer, 0, ctx->heap) != WS_SUCCESS) {
+
+        wolfSSH_free(ssh);
+        ssh = NULL;
+    }
+    //ESP_LOGI("WOLFSSH", "BufferInit OK");
+
+    return ssh;
+}
+
+void SshResourceFree(WOLFSSH* ssh, void* heap)
+{
+    /* when ssh holds resources, free here */
+    (void)heap;
+
+    WLOG(WS_LOG_DEBUG, "Entering sshResourceFree()");
+
+    ShrinkBuffer(&ssh->inputBuffer, 1);
+    ShrinkBuffer(&ssh->outputBuffer, 1);
+    ForceZero(ssh->k, ssh->kSz);
+    HandshakeInfoFree(ssh->handshake, heap);
+    ForceZero(&ssh->keys, sizeof(Keys));
+    ForceZero(&ssh->peerKeys, sizeof(Keys));
+    if (ssh->rng) {
+        wc_FreeRng(ssh->rng);
+        WFREE(ssh->rng, heap, DYNTYPE_RNG);
+    }
+    if (ssh->userName) {
+        WFREE(ssh->userName, heap, DYNTYPE_STRING);
+    }
+    if (ssh->peerProtoId) {
+        WFREE(ssh->peerProtoId, heap, DYNTYPE_STRING);
+    }
+    if (ssh->channelList) {
+        WOLFSSH_CHANNEL* cur = ssh->channelList;
+        WOLFSSH_CHANNEL* next;
+        while (cur) {
+            next = cur->next;
+            ChannelDelete(cur, heap);
+            cur = next;
+        }
+    }
+}
+
+
+int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
+                          const byte* in, word32 inSz,
+                          int format, int type)
+{
+    int dynamicType;
+    void* heap;
+    byte* der;
+    word32 derSz;
+
+    if (ctx == NULL || in == NULL || inSz == 0)
+        return WS_BAD_ARGUMENT;
+
+    if (format != WOLFSSH_FORMAT_ASN1 && format != WOLFSSH_FORMAT_PEM &&
+                                         format != WOLFSSH_FORMAT_RAW)
+        return WS_BAD_FILETYPE_E;
+
+    if (type == BUFTYPE_CA)
+        dynamicType = DYNTYPE_CA;
+    else if (type == BUFTYPE_CERT)
+        dynamicType = DYNTYPE_CERT;
+    else if (type == BUFTYPE_PRIVKEY)
+        dynamicType = DYNTYPE_PRIVKEY;
+    else
+        return WS_BAD_ARGUMENT;
+
+    heap = ctx->heap;
+
+    if (format == WOLFSSH_FORMAT_PEM)
+        return WS_UNIMPLEMENTED_E;
+    else {
+        /* format is ASN1 or RAW */
+        der = (byte*)WMALLOC(inSz, heap, dynamicType);
+        if (der == NULL)
+            return WS_MEMORY_E;
+        WMEMCPY(der, in, inSz);
+        derSz = inSz;
+    }
+
+    /* Maybe decrypt */
+
+    if (type == BUFTYPE_PRIVKEY) {
+        if (ctx->privateKey)
+            WFREE(ctx->privateKey, heap, dynamicType);
+        ctx->privateKey = der;
+        ctx->privateKeySz = derSz;
+    }
+    else {
+        WFREE(der, heap, dynamicType);
+        return WS_UNIMPLEMENTED_E;
+    }
+
+    if (type == BUFTYPE_PRIVKEY && format != WOLFSSH_FORMAT_RAW) {
+        /* Check RSA key */
+        union {
+            RsaKey rsa;
+            ecc_key ecc;
+        } key;
+        word32 scratch = 0;
+        int ret;
+
+        if (wc_InitRsaKey(&key.rsa, NULL) < 0)
+            return WS_RSA_E;
+
+        ret = wc_RsaPrivateKeyDecode(der, &scratch, &key.rsa, derSz);
+        wc_FreeRsaKey(&key.rsa);
+
+        if (ret < 0) {
+            /* Couldn't decode as RSA key. Try decoding as ECC key. */
+            scratch = 0;
+            if (wc_ecc_init_ex(&key.ecc, ctx->heap, INVALID_DEVID) != 0)
+                return WS_ECC_E;
+
+            ret = wc_EccPrivateKeyDecode(ctx->privateKey, &scratch,
+                                         &key.ecc, ctx->privateKeySz);
+            if (ret == 0) {
+                int curveId = wc_ecc_get_curve_id(key.ecc.idx);
+                if (curveId == ECC_SECP256R1 ||
+                    curveId == ECC_SECP384R1 ||
+                    curveId == ECC_SECP521R1) {
+
+                    ctx->useEcc = curveId;
+                }
+                else
+                    ret = WS_BAD_FILE_E;
+            }
+            wc_ecc_free(&key.ecc);
+
+            if (ret != 0)
+                return WS_BAD_FILE_E;
+        }
+    }
+
+    return WS_SUCCESS;
+}
+
+void c32toa(word32 u32, byte* c){
+	}
+	
+int GenerateKey(byte hashId, byte keyId,
+                byte* key, word32 keySz,
+                const byte* k, word32 kSz,
+                const byte* h, word32 hSz,
+                const byte* sessionId, word32 sessionIdSz)
+{
+    word32 blocks, remainder;
+    wc_HashAlg hash;
+    byte kPad = 0;
+    byte pad = 0;
+    byte kSzFlat[LENGTH_SZ];
+    int digestSz;
+    int ret;
+
+    if (key == NULL || keySz == 0 ||
+        k == NULL || kSz == 0 ||
+        h == NULL || hSz == 0 ||
+        sessionId == NULL || sessionIdSz == 0) {
+
+        WLOG(WS_LOG_DEBUG, "GK: bad argument");
+        return WS_BAD_ARGUMENT;
+    }
+
+    digestSz = wc_HashGetDigestSize(hashId);
+    if (digestSz == 0) {
+        WLOG(WS_LOG_DEBUG, "GK: bad hash ID");
+        return WS_BAD_ARGUMENT;
+    }
+
+    if (k[0] & 0x80) kPad = 1;
+    c32toa(kSz + kPad, kSzFlat);
+
+    blocks = keySz / digestSz;
+    remainder = keySz % digestSz;
+
+    ret = wc_HashInit(&hash, hashId);
+    if (ret == WS_SUCCESS)
+        ret = wc_HashUpdate(&hash, hashId, kSzFlat, LENGTH_SZ);
+    if (ret == WS_SUCCESS && kPad)
+        ret = wc_HashUpdate(&hash, hashId, &pad, 1);
+    if (ret == WS_SUCCESS)
+        ret = wc_HashUpdate(&hash, hashId, k, kSz);
+    if (ret == WS_SUCCESS)
+        ret = wc_HashUpdate(&hash, hashId, h, hSz);
+    if (ret == WS_SUCCESS)
+        ret = wc_HashUpdate(&hash, hashId, &keyId, sizeof(keyId));
+    if (ret == WS_SUCCESS)
+        ret = wc_HashUpdate(&hash, hashId, sessionId, sessionIdSz);
+
+    if (ret == WS_SUCCESS) {
+        if (blocks == 0) {
+            if (remainder > 0) {
+                byte lastBlock[WC_MAX_DIGEST_SIZE];
+                ret = wc_HashFinal(&hash, hashId, lastBlock);
+                if (ret == WS_SUCCESS)
+                    WMEMCPY(key, lastBlock, remainder);
+            }
+        }
+        else {
+            word32 runningKeySz, curBlock;
+
+            runningKeySz = digestSz;
+            ret = wc_HashFinal(&hash, hashId, key);
+
+            for (curBlock = 1; curBlock < blocks; curBlock++) {
+                ret = wc_HashInit(&hash, hashId);
+                if (ret != WS_SUCCESS) break;
+                ret = wc_HashUpdate(&hash, hashId, kSzFlat, LENGTH_SZ);
+                if (ret != WS_SUCCESS) break;
+                if (kPad)
+                    ret = wc_HashUpdate(&hash, hashId, &pad, 1);
+                if (ret != WS_SUCCESS) break;
+                ret = wc_HashUpdate(&hash, hashId, k, kSz);
+                if (ret != WS_SUCCESS) break;
+                ret = wc_HashUpdate(&hash, hashId, h, hSz);
+                if (ret != WS_SUCCESS) break;
+                ret = wc_HashUpdate(&hash, hashId, key, runningKeySz);
+                if (ret != WS_SUCCESS) break;
+                ret = wc_HashFinal(&hash, hashId, key + runningKeySz);
+                if (ret != WS_SUCCESS) break;
+                runningKeySz += digestSz;
+            }
+
+            if (remainder > 0) {
+                byte lastBlock[WC_MAX_DIGEST_SIZE];
+                if (ret == WS_SUCCESS)
+                    ret = wc_HashInit(&hash, hashId);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HashUpdate(&hash, hashId, kSzFlat, LENGTH_SZ);
+                if (ret == WS_SUCCESS && kPad)
+                    ret = wc_HashUpdate(&hash, hashId, &pad, 1);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HashUpdate(&hash, hashId, k, kSz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HashUpdate(&hash, hashId, h, hSz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HashUpdate(&hash, hashId, key, runningKeySz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HashFinal(&hash, hashId, lastBlock);
+                if (ret == WS_SUCCESS)
+                    WMEMCPY(key + runningKeySz, lastBlock, remainder);
+            }
+        }
+    }
+
+    if (ret != WS_SUCCESS)
+        ret = WS_CRYPTO_FAILED;
+
+    return ret;
+}
+
+
+static int GenerateKeys(WOLFSSH* ssh)
+{
+    Keys* cK;
+    Keys* sK;
+    byte hashId;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+    else {
+        if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
+            cK = &ssh->handshake->peerKeys;
+            sK = &ssh->handshake->keys;
+        }
+        else {
+            cK = &ssh->handshake->keys;
+            sK = &ssh->handshake->peerKeys;
+        }
+        hashId = ssh->handshake->hashId;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GenerateKey(hashId, 'A',
+                          cK->iv, cK->ivSz,
+                          ssh->k, ssh->kSz, ssh->h, ssh->hSz,
+                          ssh->sessionId, ssh->sessionIdSz);
+    if (ret == WS_SUCCESS)
+        ret = GenerateKey(hashId, 'B',
+                          sK->iv, sK->ivSz,
+                          ssh->k, ssh->kSz, ssh->h, ssh->hSz,
+                          ssh->sessionId, ssh->sessionIdSz);
+    if (ret == WS_SUCCESS)
+        ret = GenerateKey(hashId, 'C',
+                          cK->encKey, cK->encKeySz,
+                          ssh->k, ssh->kSz, ssh->h, ssh->hSz,
+                          ssh->sessionId, ssh->sessionIdSz);
+    if (ret == WS_SUCCESS)
+        ret = GenerateKey(hashId, 'D',
+                          sK->encKey, sK->encKeySz,
+                          ssh->k, ssh->kSz, ssh->h, ssh->hSz,
+                          ssh->sessionId, ssh->sessionIdSz);
+    if (!ssh->handshake->aeadMode) {
+        if (ret == WS_SUCCESS)
+            ret = GenerateKey(hashId, 'E',
+                              cK->macKey, cK->macKeySz,
+                              ssh->k, ssh->kSz, ssh->h, ssh->hSz,
+                              ssh->sessionId, ssh->sessionIdSz);
+        if (ret == WS_SUCCESS)
+            ret = GenerateKey(hashId, 'F',
+                              sK->macKey, sK->macKeySz,
+                              ssh->k, ssh->kSz, ssh->h, ssh->hSz,
+                              ssh->sessionId, ssh->sessionIdSz);
+    }
+#ifdef SHOW_SECRETS
+    if (ret == WS_SUCCESS) {
+        printf("\n** Showing Secrets **\nK:\n");
+        DumpOctetString(ssh->k, ssh->kSz);
+        printf("H:\n");
+        DumpOctetString(ssh->h, ssh->hSz);
+        printf("Session ID:\n");
+        DumpOctetString(ssh->sessionId, ssh->sessionIdSz);
+        printf("A:\n");
+        DumpOctetString(cK->iv, cK->ivSz);
+        printf("B:\n");
+        DumpOctetString(sK->iv, sK->ivSz);
+        printf("C:\n");
+        DumpOctetString(cK->encKey, cK->encKeySz);
+        printf("D:\n");
+        DumpOctetString(sK->encKey, sK->encKeySz);
+        printf("E:\n");
+        DumpOctetString(cK->macKey, cK->macKeySz);
+        printf("F:\n");
+        DumpOctetString(sK->macKey, sK->macKeySz);
+        printf("\n");
+    }
+#endif /* SHOW_SECRETS */
+
+    return ret;
+}
+
+
+typedef struct {
+    byte id;
+    const char* name;
+} NameIdPair;
+
+
+static const NameIdPair NameIdMap[] = {
+    { ID_NONE, "none" },
+
+    /* Encryption IDs */
+    { ID_AES128_CBC, "aes128-cbc" },
+    { ID_AES128_GCM, "aes128-gcm@openssh.com" },
+
+    /* Integrity IDs */
+    { ID_HMAC_SHA1, "hmac-sha1" },
+    { ID_HMAC_SHA1_96, "hmac-sha1-96" },
+    { ID_HMAC_SHA2_256, "hmac-sha2-256" },
+
+    /* Key Exchange IDs */
+    { ID_DH_GROUP1_SHA1, "diffie-hellman-group1-sha1" },
+    { ID_DH_GROUP14_SHA1, "diffie-hellman-group14-sha1" },
+    { ID_DH_GEX_SHA256, "diffie-hellman-group-exchange-sha256" },
+    { ID_ECDH_SHA2_NISTP256, "ecdh-sha2-nistp256" },
+    { ID_ECDH_SHA2_NISTP384, "ecdh-sha2-nistp384" },
+    { ID_ECDH_SHA2_NISTP521, "ecdh-sha2-nistp521" },
+
+    /* Public Key IDs */
+    { ID_SSH_RSA, "ssh-rsa" },
+    { ID_ECDSA_SHA2_NISTP256, "ecdsa-sha2-nistp256" },
+    { ID_ECDSA_SHA2_NISTP384, "ecdsa-sha2-nistp384" },
+    { ID_ECDSA_SHA2_NISTP521, "ecdsa-sha2-nistp521" },
+
+    /* Service IDs */
+    { ID_SERVICE_USERAUTH, "ssh-userauth" },
+    { ID_SERVICE_CONNECTION, "ssh-connection" },
+
+    /* UserAuth IDs */
+    { ID_USERAUTH_PASSWORD, "password" },
+    { ID_USERAUTH_PUBLICKEY, "publickey" },
+
+    /* Channel Type IDs */
+    { ID_CHANTYPE_SESSION, "session" }
+};
+
+
+byte NameToId(const char* name, word32 nameSz)
+{
+    byte id = ID_UNKNOWN;
+    word32 i;
+
+    for (i = 0; i < (sizeof(NameIdMap)/sizeof(NameIdPair)); i++) {
+        if (nameSz == WSTRLEN(NameIdMap[i].name) &&
+            WSTRNCMP(name, NameIdMap[i].name, nameSz) == 0) {
+
+            id = NameIdMap[i].id;
+            break;
+        }
+    }
+
+    return id;
+}
+
+
+const char* IdToName(byte id)
+{
+    const char* name = "unknown";
+    word32 i;
+
+    for (i = 0; i < (sizeof(NameIdMap)/sizeof(NameIdPair)); i++) {
+        if (NameIdMap[i].id == id) {
+            name = NameIdMap[i].name;
+            break;
+        }
+    }
+
+    return name;
+}
+
+
+WOLFSSH_CHANNEL* ChannelNew(WOLFSSH* ssh, byte channelType,
+                            word32 initialWindowSz, word32 maxPacketSz)
+{
+    WOLFSSH_CHANNEL* newChannel = NULL;
+
+    WLOG(WS_LOG_DEBUG, "Entering ChannelNew()");
+    if (ssh == NULL || ssh->ctx == NULL) {
+        WLOG(WS_LOG_DEBUG, "Trying to create new channel without ssh or ctx");
+    }
+    else {
+        void* heap = ssh->ctx->heap;
+
+        newChannel = (WOLFSSH_CHANNEL*)WMALLOC(sizeof(WOLFSSH_CHANNEL),
+                                               heap, DYNTYPE_CHANNEL);
+        if (newChannel != NULL)
+        {
+            byte* buffer;
+
+            buffer = (byte*)WMALLOC(initialWindowSz, heap, DYNTYPE_BUFFER);
+            if (buffer != NULL) {
+                WMEMSET(newChannel, 0, sizeof(WOLFSSH_CHANNEL));
+                newChannel->channelType = channelType;
+                newChannel->channel = ssh->nextChannel++;
+                newChannel->windowSz = initialWindowSz;
+                newChannel->maxPacketSz = maxPacketSz;
+                /*
+                 * In the context of the channel input buffer, the buffer is
+                 * a fixed size. The property length will be the insert point
+                 * for new received data. The property idx will be the pull
+                 * point for the data.
+                 */
+                newChannel->inputBuffer.heap = heap;
+                newChannel->inputBuffer.buffer = buffer;
+                newChannel->inputBuffer.bufferSz = initialWindowSz;
+                newChannel->inputBuffer.dynamicFlag = 1;
+            }
+            else {
+                WLOG(WS_LOG_DEBUG, "Unable to allocate new channel's buffer");
+                WFREE(newChannel, heap, DYNTYPE_CHANNEL);
+                newChannel = NULL;
+            }
+        }
+        else {
+            WLOG(WS_LOG_DEBUG, "Unable to allocate new channel");
+        }
+    }
+
+    WLOG(WS_LOG_INFO, "Leaving ChannelNew(), ret = %p", newChannel);
+
+    return newChannel;
+}
+
+
+void ChannelDelete(WOLFSSH_CHANNEL* channel, void* heap)
+{
+    (void)heap;
+
+    if (channel) {
+        WFREE(channel->inputBuffer.buffer,
+              channel->inputBuffer.heap, DYNTYPE_BUFFER);
+        WFREE(channel, heap, DYNTYPE_CHANNEL);
+    }
+}
+
+
+#define FIND_SELF 0
+#define FIND_PEER 1
+
+WOLFSSH_CHANNEL* ChannelFind(WOLFSSH* ssh, word32 channel, byte peer)
+{
+    WOLFSSH_CHANNEL* findChannel = NULL;
+
+    WLOG(WS_LOG_DEBUG, "Entering ChannelFind()");
+
+    if (ssh == NULL) {
+        WLOG(WS_LOG_DEBUG, "Null ssh, not looking for channel");
+    }
+    else {
+        WOLFSSH_CHANNEL* list = ssh->channelList;
+        word32 listSz = ssh->channelListSz;
+
+        while (list && listSz) {
+            if (channel == ((peer == FIND_PEER) ?
+                            list->peerChannel : list->channel)) {
+                findChannel = list;
+                break;
+            }
+            list = list->next;
+            listSz--;
+        }
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving ChannelFind(): %p", findChannel);
+
+    return findChannel;
+}
+
+
+int ChannelUpdate(WOLFSSH_CHANNEL* channel, word32 peerChannelId,
+                  word32 peerInitialWindowSz, word32 peerMaxPacketSz)
+{
+    int ret = WS_SUCCESS;
+
+    if (channel == NULL)
+        ret = WS_BAD_ARGUMENT;
+    else {
+        channel->peerChannel = peerChannelId;
+        channel->peerWindowSz = peerInitialWindowSz;
+        channel->peerMaxPacketSz = peerMaxPacketSz;
+    }
+
+    return ret;
+}
+
+
+static int ChannelAppend(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering ChannelAppend()");
+
+    if (ssh == NULL || channel == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ssh->channelList == NULL) {
+        ssh->channelList = channel;
+        ssh->channelListSz = 1;
+    }
+    else {
+        WOLFSSH_CHANNEL* cur = ssh->channelList;
+        while (cur->next != NULL)
+            cur = cur->next;
+        cur->next = channel;
+        ssh->channelListSz++;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving ChannelAppend(), ret = %d", ret);
+    return ret;
+}
+
+
+int ChannelRemove(WOLFSSH* ssh, word32 channel, byte peer)
+{
+    int ret = WS_SUCCESS;
+    WOLFSSH_CHANNEL* list;
+
+    WLOG(WS_LOG_DEBUG, "Entering ChannelRemove()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    list = ssh->channelList;
+    if (list == NULL)
+        ret = WS_INVALID_CHANID;
+
+    if (ret == WS_SUCCESS) {
+        WOLFSSH_CHANNEL* prev = NULL;
+        word32 listSz = ssh->channelListSz;
+
+        while (list && listSz) {
+            if (channel == ((peer == FIND_PEER) ?
+                            list->peerChannel : list->channel)) {
+                if (prev == NULL)
+                    ssh->channelList = list->next;
+                else
+                    prev->next = list->next;
+                ChannelDelete(list, ssh->ctx->heap);
+                ssh->channelListSz--;
+
+                break;
+            }
+            prev = list;
+            list = list->next;
+            listSz--;
+        }
+
+        if (listSz == 0)
+            ret = WS_INVALID_CHANID;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving ChannelRemove(), ret = %d", ret);
+    return ret;
+}
+
+
+int ChannelPutData(WOLFSSH_CHANNEL* channel, byte* data, word32 dataSz)
+{
+    Buffer* inBuf;
+
+    WLOG(WS_LOG_DEBUG, "Entering ChannelPutData()");
+
+    if (channel == NULL || data == NULL)
+        return WS_BAD_ARGUMENT;
+
+    inBuf = &channel->inputBuffer;
+
+    if (inBuf->length < inBuf->bufferSz &&
+        inBuf->length + dataSz <= inBuf->bufferSz) {
+
+        WMEMCPY(inBuf->buffer + inBuf->length, data, dataSz);
+        inBuf->length += dataSz;
+
+        WLOG(WS_LOG_INFO, "  dataSz = %u", dataSz);
+        WLOG(WS_LOG_INFO, "  windowSz = %u", channel->windowSz);
+        channel->windowSz -= dataSz;
+        WLOG(WS_LOG_INFO, "  windowSz = %u", channel->windowSz);
+    }
+    else {
+        return WS_RECV_OVERFLOW_E;
+    }
+
+    return WS_SUCCESS;
+}
+
+
+int BufferInit(Buffer* buffer, word32 size, void* heap)
+{
+    if (buffer == NULL)
+        return WS_BAD_ARGUMENT;
+
+    if (size <= STATIC_BUFFER_LEN)
+        size = STATIC_BUFFER_LEN;
+
+    WMEMSET(buffer, 0, sizeof(Buffer));
+    buffer->heap = heap;
+    buffer->bufferSz = size;
+    if (size > STATIC_BUFFER_LEN) {
+        buffer->buffer = (byte*)WMALLOC(size, heap, DYNTYPE_BUFFER);
+        if (buffer->buffer == NULL)
+            return WS_MEMORY_E;
+        buffer->dynamicFlag = 1;
+    }
+    else
+        buffer->buffer = buffer->staticBuffer;
+
+    return WS_SUCCESS;
+}
+
+
+int GrowBuffer(Buffer* buf, word32 sz, word32 usedSz)
+{
+#if 0
+    WLOG(WS_LOG_DEBUG, "GB: buf = %p", buf);
+    WLOG(WS_LOG_DEBUG, "GB: sz = %d", sz);
+    WLOG(WS_LOG_DEBUG, "GB: usedSz = %d", usedSz);
+#endif
+    /* New buffer will end up being sz+usedSz long
+     * empty space at the head of the buffer will be compressed */
+    if (buf != NULL) {
+        word32 newSz = sz + usedSz;
+        /*WLOG(WS_LOG_DEBUG, "GB: newSz = %d", newSz);*/
+
+        if (newSz > buf->bufferSz) {
+            byte* newBuffer = (byte*)WMALLOC(newSz,
+                                                     buf->heap, DYNTYPE_BUFFER);
+
+            if (newBuffer == NULL)
+                return WS_MEMORY_E;
+
+            /*WLOG(WS_LOG_DEBUG, "GB: resizing buffer");*/
+            if (buf->length > 0)
+                WMEMCPY(newBuffer, buf->buffer + buf->idx, usedSz);
+
+            if (!buf->dynamicFlag)
+                buf->dynamicFlag = 1;
+            else
+                WFREE(buf->buffer, buf->heap, DYNTYPE_BUFFER);
+
+            buf->buffer = newBuffer;
+            buf->bufferSz = newSz;
+            buf->length = usedSz;
+            buf->idx = 0;
+        }
+    }
+
+    return WS_SUCCESS;
+}
+
+
+void ShrinkBuffer(Buffer* buf, int forcedFree)
+{
+    WLOG(WS_LOG_DEBUG, "Entering ShrinkBuffer()");
+
+    if (buf != NULL) {
+        word32 usedSz = buf->length - buf->idx;
+
+        WLOG(WS_LOG_DEBUG, "SB: usedSz = %u, forcedFree = %u",
+             usedSz, forcedFree);
+
+        if (!forcedFree && usedSz > STATIC_BUFFER_LEN)
+            return;
+
+        if (!forcedFree && usedSz) {
+            WLOG(WS_LOG_DEBUG, "SB: shifting down");
+            WMEMCPY(buf->staticBuffer, buf->buffer + buf->idx, usedSz);
+        }
+
+        if (buf->dynamicFlag) {
+            WLOG(WS_LOG_DEBUG, "SB: releasing dynamic buffer");
+            WFREE(buf->buffer, buf->heap, DYNTYPE_BUFFER);
+        }
+        buf->dynamicFlag = 0;
+        buf->buffer = buf->staticBuffer;
+        buf->bufferSz = STATIC_BUFFER_LEN;
+        buf->length = forcedFree ? 0 : usedSz;
+        buf->idx = 0;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving ShrinkBuffer()");
+}
+
+
+static int Receive(WOLFSSH* ssh, byte* buf, word32 sz)
+{
+    int recvd;
+
+    if (ssh->ctx->ioRecvCb == NULL) {
+        WLOG(WS_LOG_DEBUG, "Your IO Recv callback is null, please set");
+        return -1;
+    }
+
+retry:
+    recvd = ssh->ctx->ioRecvCb(ssh, buf, sz, ssh->ioReadCtx);
+    WLOG(WS_LOG_DEBUG, "Receive: recvd = %d", recvd);
+    if (recvd < 0)
+        switch (recvd) {
+            case WS_CBIO_ERR_GENERAL:        /* general/unknown error */
+                return -1;
+
+            case WS_CBIO_ERR_WANT_READ:      /* want read, would block */
+                return WS_WANT_READ;
+
+            case WS_CBIO_ERR_CONN_RST:       /* connection reset */
+                ssh->connReset = 1;
+                return -1;
+
+            case WS_CBIO_ERR_ISR:            /* interrupt */
+                goto retry;
+
+            case WS_CBIO_ERR_CONN_CLOSE:     /* peer closed connection */
+                ssh->isClosed = 1;
+                return -1;
+
+            case WS_CBIO_ERR_TIMEOUT:
+                return -1;
+
+            default:
+                return recvd;
+        }
+
+    return recvd;
+}
+
+
+static int GetInputText(WOLFSSH* ssh, byte** pEol)
+{
+    int gotLine = 0;
+    int inSz = 255;
+    int in;
+    char *eol;
+
+    if (GrowBuffer(&ssh->inputBuffer, inSz, 0) < 0)
+        return WS_MEMORY_E;
+
+    do {
+        in = Receive(ssh,
+                     ssh->inputBuffer.buffer + ssh->inputBuffer.length, inSz);
+
+        if (in == -1)
+            return WS_SOCKET_ERROR_E;
+
+        if (in == WS_WANT_READ)
+            return WS_WANT_READ;
+
+        if (in > inSz)
+            return WS_RECV_OVERFLOW_E;
+
+        ssh->inputBuffer.length += in;
+        inSz -= in;
+
+        eol = WSTRNSTR((const char*)ssh->inputBuffer.buffer, "\r\n",
+                       ssh->inputBuffer.length);
+
+        if (eol)
+            gotLine = 1;
+
+    } while (!gotLine && inSz);
+
+    if (pEol)
+        *pEol = (byte*)eol;
+
+    return (gotLine ? WS_SUCCESS : WS_VERSION_E);
+}
+
+
+static int SendBuffered(WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering SendBuffered()");
+
+    if (ssh->ctx->ioSendCb == NULL) {
+        WLOG(WS_LOG_DEBUG, "Your IO Send callback is null, please set");
+        return WS_SOCKET_ERROR_E;
+    }
+
+    while (ssh->outputBuffer.length > 0) {
+        int sent = ssh->ctx->ioSendCb(ssh,
+                               ssh->outputBuffer.buffer + ssh->outputBuffer.idx,
+                               ssh->outputBuffer.length, ssh->ioReadCtx);
+
+        if (sent < 0) {
+            switch (sent) {
+                case WS_CBIO_ERR_WANT_WRITE:     /* want write, would block */
+                    return WS_WANT_WRITE;
+
+                case WS_CBIO_ERR_CONN_RST:       /* connection reset */
+                    ssh->connReset = 1;
+                    break;
+
+                case WS_CBIO_ERR_CONN_CLOSE:     /* peer closed connection */
+                    ssh->isClosed = 1;
+                    break;
+            }
+            return WS_SOCKET_ERROR_E;
+        }
+
+        if ((word32)sent > ssh->outputBuffer.length) {
+            WLOG(WS_LOG_DEBUG, "SendBuffered() out of bounds read");
+            return WS_SEND_OOB_READ_E;
+        }
+
+        ssh->outputBuffer.idx += sent;
+        ssh->outputBuffer.length -= sent;
+    }
+
+    ssh->outputBuffer.idx = 0;
+
+    WLOG(WS_LOG_DEBUG, "SB: Shrinking output buffer");
+    ShrinkBuffer(&ssh->outputBuffer, 0);
+
+    HighwaterCheck(ssh, WOLFSSH_HWSIDE_TRANSMIT);
+
+    return WS_SUCCESS;
+}
+
+
+static int GetInputData(WOLFSSH* ssh, word32 size)
+{
+    int in;
+    int inSz;
+    int maxLength;
+    int usedLength;
+
+    /* check max input length */
+    usedLength = ssh->inputBuffer.length - ssh->inputBuffer.idx;
+    maxLength  = ssh->inputBuffer.bufferSz - usedLength;
+    inSz       = (int)(size - usedLength);      /* from last partial read */
+#if 0
+    WLOG(WS_LOG_DEBUG, "GID: size = %u", size);
+    WLOG(WS_LOG_DEBUG, "GID: usedLength = %d", usedLength);
+    WLOG(WS_LOG_DEBUG, "GID: maxLength = %d", maxLength);
+    WLOG(WS_LOG_DEBUG, "GID: inSz = %d", inSz);
+#endif
+    /*
+     * usedLength - how much untouched data is in the buffer
+     * maxLength - how much empty space is in the buffer
+     * inSz - difference between requested data and empty space in the buffer
+     *        how much more we need to allocate
+     */
+
+    if (inSz <= 0)
+        return WS_SUCCESS;
+
+    /*
+     * If we need more space than there is left in the buffer grow buffer.
+     * Growing the buffer also compresses empty space at the head of the
+     * buffer and resets idx to 0.
+     */
+    if (inSz > maxLength) {
+        if (GrowBuffer(&ssh->inputBuffer, size, usedLength) < 0)
+            return WS_MEMORY_E;
+    }
+
+    /* Put buffer data at start if not there */
+    /* Compress the buffer if needed, i.e. buffer idx is non-zero */
+    if (usedLength > 0 && ssh->inputBuffer.idx != 0) {
+        WMEMMOVE(ssh->inputBuffer.buffer,
+                ssh->inputBuffer.buffer + ssh->inputBuffer.idx,
+                usedLength);
+    }
+
+    /* remove processed data */
+    ssh->inputBuffer.idx    = 0;
+    ssh->inputBuffer.length = usedLength;
+
+    /* read data from network */
+    do {
+        in = Receive(ssh,
+                     ssh->inputBuffer.buffer + ssh->inputBuffer.length, inSz);
+        if (in == -1) {
+        	//ESP_LOGE("WOLFSSH", "GetInputData: WS_SOCKET_ERROR_E");
+            return WS_SOCKET_ERROR_E;
+        }
+
+        if (in == WS_WANT_READ) {
+        	//ESP_LOGE("WOLFSSH", "GetInputData: WS_WANT_READ");
+            return WS_WANT_READ;
+        }
+
+        if (in > inSz) {
+        	//ESP_LOGE("WOLFSSH", "GetInputData: WS_RECV_OVERFLOW_E");
+            return WS_RECV_OVERFLOW_E;
+        }
+
+        ssh->inputBuffer.length += in;
+        inSz -= in;
+
+    } while (ssh->inputBuffer.length < size);
+
+    return 0;
+}
+
+
+static int GetBoolean(byte* v, byte* buf, word32 len, word32* idx)
+{
+    int result = WS_BUFFER_E;
+
+    if (*idx < len) {
+        *v = buf[*idx];
+        *idx += BOOLEAN_SZ;
+        result = WS_SUCCESS;
+    }
+
+    return result;
+}
+
+void ato32(const byte* c, word32 * u32){}
+
+static int GetUint32(word32* v, byte* buf, word32 len, word32* idx)
+{
+    int result = WS_BUFFER_E;
+
+    if (*idx < len && *idx + UINT32_SZ <= len) {
+        ato32(buf + *idx, v);
+        *idx += UINT32_SZ;
+        result = WS_SUCCESS;
+    }
+
+    return result;
+}
+
+
+/* Gets the size of the mpint, and puts the pointer to the start of
+ * buf's number into *mpint. This function does not copy. */
+static int GetMpint(word32* mpintSz, byte** mpint,
+                    byte* buf, word32 len, word32* idx)
+{
+    int result;
+
+    result = GetUint32(mpintSz, buf, len, idx);
+
+    if (result == WS_SUCCESS) {
+        result = WS_BUFFER_E;
+
+        if (*idx < len && *idx + *mpintSz <= len) {
+            *mpint = buf + *idx;
+            *idx += *mpintSz;
+            result = WS_SUCCESS;
+        }
+    }
+
+    return result;
+}
+
+
+/* Gets the size of a string, copies it as much of it as will fit in
+ * the provided buffer, and terminates it with a NULL. */
+static int GetString(char* s, word32* sSz,
+                     byte* buf, word32 len, word32 *idx)
+{
+    int result;
+    word32 strSz;
+
+    result = GetUint32(&strSz, buf, len, idx);
+
+    if (result == WS_SUCCESS) {
+        result = WS_BUFFER_E;
+        if (*idx < len && *idx + strSz <= len) {
+            *sSz = (strSz >= *sSz) ? *sSz : strSz;
+            WMEMCPY(s, buf + *idx, *sSz);
+            *idx += strSz;
+            s[*sSz] = 0;
+            result = WS_SUCCESS;
+        }
+    }
+
+    return result;
+}
+
+
+static int GetNameList(byte* idList, word32* idListSz,
+                       byte* buf, word32 len, word32* idx)
+{
+    byte idListIdx;
+    word32 nameListSz, nameListIdx;
+    word32 begin;
+    byte* name;
+    word32 nameSz;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering GetNameList()");
+
+    if (idList == NULL || idListSz == NULL ||
+        buf == NULL || len == 0 || idx == NULL) {
+
+        ret = WS_BAD_ARGUMENT;
+    }
+
+    /*
+     * This iterates across a name list and finds names that end in either the
+     * comma delimeter or with the end of the list.
+     */
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        if (begin >= len || begin + 4 >= len)
+            ret = WS_BUFFER_E;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&nameListSz, buf, len, &begin);
+
+    /* The strings we want are now in the bounds of the message, and the
+     * length of the list. Find the commas, or end of list, and then decode
+     * the values. */
+    if (ret == WS_SUCCESS) {
+        name = buf + begin;
+        nameSz = 0;
+        nameListIdx = 0;
+        idListIdx = 0;
+
+        while (nameListIdx < nameListSz) {
+            nameListIdx++;
+
+            if (nameListIdx == nameListSz)
+                nameSz++;
+
+            if (nameListIdx == nameListSz || name[nameSz] == ',') {
+                byte id;
+
+                id = NameToId((char*)name, nameSz);
+                {
+                    const char* displayName = IdToName(id);
+                    if (displayName) {
+                        WLOG(WS_LOG_DEBUG, "DNL: name ID = %s", displayName);
+                    }
+                }
+                if (id != ID_UNKNOWN)
+                    idList[idListIdx++] = id;
+
+                name += 1 + nameSz;
+                nameSz = 0;
+            }
+            else
+                nameSz++;
+        }
+
+        begin += nameListSz;
+        *idListSz = idListIdx;
+        *idx = begin;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving GetNameList(), ret = %d", ret);
+    return ret;
+}
+
+
+static const byte  cannedEncAlgo[] = {ID_AES128_GCM, ID_AES128_CBC};
+static const byte  cannedMacAlgo[] = {ID_HMAC_SHA2_256, ID_HMAC_SHA1_96,
+                                         ID_HMAC_SHA1};
+static const byte  cannedKeyAlgoRsa[] = {ID_SSH_RSA};
+static const byte  cannedKeyAlgoEcc256[] = {ID_ECDSA_SHA2_NISTP256};
+static const byte  cannedKeyAlgoEcc384[] = {ID_ECDSA_SHA2_NISTP384};
+static const byte  cannedKeyAlgoEcc521[] = {ID_ECDSA_SHA2_NISTP521};
+static const byte  cannedKexAlgo[] = {ID_ECDH_SHA2_NISTP256,
+                                         ID_DH_GEX_SHA256,
+                                         ID_DH_GROUP14_SHA1,
+                                         ID_DH_GROUP1_SHA1};
+
+static const word32 cannedEncAlgoSz = sizeof(cannedEncAlgo);
+static const word32 cannedMacAlgoSz = sizeof(cannedMacAlgo);
+static const word32 cannedKeyAlgoRsaSz = sizeof(cannedKeyAlgoRsa);
+static const word32 cannedKeyAlgoEcc256Sz = sizeof(cannedKeyAlgoEcc256);
+static const word32 cannedKeyAlgoEcc384Sz = sizeof(cannedKeyAlgoEcc384);
+static const word32 cannedKeyAlgoEcc521Sz = sizeof(cannedKeyAlgoEcc521);
+static const word32 cannedKexAlgoSz = sizeof(cannedKexAlgo);
+
+
+static byte MatchIdLists(const byte* left, word32 leftSz,
+                         const byte* right, word32 rightSz)
+{
+    word32 i, j;
+
+    if (left != NULL && leftSz > 0 && right != NULL && rightSz > 0) {
+        for (i = 0; i < leftSz; i++) {
+            for (j = 0; j < rightSz; j++) {
+                if (left[i] == right[j]) {
+#if 0
+                    WLOG(WS_LOG_DEBUG, "MID: matched %s", IdToName(left[i]));
+#endif
+                    return left[i];
+                }
+            }
+        }
+    }
+
+    return ID_UNKNOWN;
+}
+
+
+static INLINE byte BlockSzForId(byte id)
+{
+    switch (id) {
+        case ID_AES128_CBC:
+        case ID_AES128_GCM:
+            return AES_BLOCK_SIZE;
+        default:
+            return 0;
+    }
+}
+
+
+static INLINE byte MacSzForId(byte id)
+{
+    switch (id) {
+        case ID_HMAC_SHA1:
+            return SHA_DIGEST_SIZE;
+        case ID_HMAC_SHA1_96:
+            return SHA1_96_SZ;
+        case ID_HMAC_SHA2_256:
+            return SHA256_DIGEST_SIZE;
+        default:
+            return 0;
+    }
+}
+
+
+static INLINE byte KeySzForId(byte id)
+{
+    switch (id) {
+        case ID_HMAC_SHA1:
+        case ID_HMAC_SHA1_96:
+            return SHA_DIGEST_SIZE;
+        case ID_HMAC_SHA2_256:
+            return SHA256_DIGEST_SIZE;
+        case ID_AES128_CBC:
+        case ID_AES128_GCM:
+            return AES_BLOCK_SIZE;
+        default:
+            return 0;
+    }
+}
+
+
+static INLINE byte HashForId(byte id)
+{
+    switch (id) {
+        case ID_DH_GROUP1_SHA1:
+        case ID_DH_GROUP14_SHA1:
+        case ID_SSH_RSA:
+            return WC_HASH_TYPE_SHA;
+        case ID_DH_GEX_SHA256:
+        case ID_ECDH_SHA2_NISTP256:
+        case ID_ECDSA_SHA2_NISTP256:
+            return WC_HASH_TYPE_SHA256;
+        case ID_ECDH_SHA2_NISTP384:
+        case ID_ECDSA_SHA2_NISTP384:
+            return WC_HASH_TYPE_SHA384;
+        case ID_ECDH_SHA2_NISTP521:
+        case ID_ECDSA_SHA2_NISTP521:
+            return WC_HASH_TYPE_SHA512;
+        default:
+            return WC_HASH_TYPE_NONE;
+    }
+}
+
+
+static INLINE int wcPrimeForId(byte id)
+{
+    switch (id) {
+        case ID_ECDH_SHA2_NISTP256:
+        case ID_ECDSA_SHA2_NISTP256:
+            return ECC_SECP256R1;
+        case ID_ECDH_SHA2_NISTP384:
+        case ID_ECDSA_SHA2_NISTP384:
+            return ECC_SECP384R1;
+        case ID_ECDH_SHA2_NISTP521:
+        case ID_ECDSA_SHA2_NISTP521:
+            return ECC_SECP521R1;
+        default:
+            return ECC_CURVE_INVALID;
+    }
+}
+static INLINE const char *PrimeNameForId(byte id)
+{
+    switch (id) {
+        case ID_ECDH_SHA2_NISTP256:
+        case ID_ECDSA_SHA2_NISTP256:
+            return "nistp256";
+        case ID_ECDH_SHA2_NISTP384:
+        case ID_ECDSA_SHA2_NISTP384:
+            return "nistp384";
+        case ID_ECDH_SHA2_NISTP521:
+        case ID_ECDSA_SHA2_NISTP521:
+            return "nistp521";
+        default:
+            return "unknown";
+    }
+}
+
+
+static INLINE byte AeadModeForId(byte id)
+{
+    return (id == ID_AES128_GCM);
+}
+
+
+static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    int ret = WS_SUCCESS;
+    byte algoId;
+    byte list[6];
+    word32 listSz;
+    word32 skipSz;
+    word32 begin;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoKexInit()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    /*
+     * I don't need to save what the client sends here. I should decode
+     * each list into a local array of IDs, and pick the one the peer is
+     * using that's on my known list, or verify that the one the peer can
+     * support the other direction is on my known list. All I need to do
+     * is save the actual values.
+     */
+
+    if (ssh->handshake == NULL) {
+        ssh->handshake = HandshakeInfoNew(ssh->ctx->heap);
+        if (ssh->handshake == NULL) {
+            WLOG(WS_LOG_DEBUG, "Couldn't allocate handshake info");
+            ret = WS_MEMORY_E;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+
+        /* Check that the cookie exists inside the message */
+        if (begin + COOKIE_SZ > len) {
+            /* error, out of bounds */
+            ret = WS_PARSE_E;
+        }
+        else {
+            /* Move past the cookie. */
+            begin += COOKIE_SZ;
+        }
+    }
+
+    /* KEX Algorithms */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: KEX Algorithms");
+        listSz = 2;
+        ret = GetNameList(list, &listSz, buf, len, &begin);
+        if (ret == WS_SUCCESS) {
+            algoId = MatchIdLists(list, listSz, cannedKexAlgo, cannedKexAlgoSz);
+            if (algoId == ID_UNKNOWN) {
+                WLOG(WS_LOG_DEBUG, "Unable to negotiate KEX Algo");
+                ret = WS_INVALID_ALGO_ID;
+            }
+            else {
+                ssh->handshake->kexId = algoId;
+                ssh->handshake->hashId = HashForId(algoId);
+            }
+        }
+    }
+
+    /* Server Host Key Algorithms */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: Server Host Key Algorithms");
+        listSz = 1;
+        ret = GetNameList(list, &listSz, buf, len, &begin);
+        if (ret == WS_SUCCESS) {
+            const byte *cannedKeyAlgo;
+            word32 cannedKeyAlgoSz;
+
+            switch (ssh->ctx->useEcc) {
+                case ECC_SECP256R1:
+                    cannedKeyAlgo = cannedKeyAlgoEcc256;
+                    cannedKeyAlgoSz = cannedKeyAlgoEcc256Sz;
+                    break;
+                case ECC_SECP384R1:
+                    cannedKeyAlgo = cannedKeyAlgoEcc384;
+                    cannedKeyAlgoSz = cannedKeyAlgoEcc384Sz;
+                    break;
+                case ECC_SECP521R1:
+                    cannedKeyAlgo = cannedKeyAlgoEcc521;
+                    cannedKeyAlgoSz = cannedKeyAlgoEcc521Sz;
+                    break;
+                default:
+                    cannedKeyAlgo = cannedKeyAlgoRsa;
+                    cannedKeyAlgoSz = cannedKeyAlgoRsaSz;
+            }
+            algoId = MatchIdLists(list, listSz,
+                                  cannedKeyAlgo, cannedKeyAlgoSz);
+            if (algoId == ID_UNKNOWN) {
+                WLOG(WS_LOG_DEBUG, "Unable to negotiate Server Host Key Algo");
+                return WS_INVALID_ALGO_ID;
+            }
+            else
+                ssh->handshake->pubKeyId = algoId;
+        }
+    }
+
+    /* Enc Algorithms - Client to Server */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Client to Server");
+        listSz = 3;
+        ret = GetNameList(list, &listSz, buf, len, &begin);
+        if (ret == WS_SUCCESS) {
+            algoId = MatchIdLists(list, listSz, cannedEncAlgo, cannedEncAlgoSz);
+            if (algoId == ID_UNKNOWN) {
+                WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo C2S");
+                ret = WS_INVALID_ALGO_ID;
+            }
+        }
+    }
+
+    /* Enc Algorithms - Server to Client */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Server to Client");
+        listSz = 3;
+        ret = GetNameList(list, &listSz, buf, len, &begin);
+        if (MatchIdLists(list, listSz, &algoId, 1) == ID_UNKNOWN) {
+            WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo S2C");
+            ret = WS_INVALID_ALGO_ID;
+        }
+        else {
+            ssh->handshake->encryptId = algoId;
+            ssh->handshake->aeadMode = AeadModeForId(algoId);
+            ssh->handshake->blockSz = BlockSzForId(algoId);
+            ssh->handshake->keys.encKeySz =
+                ssh->handshake->peerKeys.encKeySz =
+                KeySzForId(algoId);
+            if (!ssh->handshake->aeadMode) {
+                ssh->handshake->keys.ivSz =
+                    ssh->handshake->peerKeys.ivSz =
+                    ssh->handshake->blockSz;
+            }
+            else {
+                ssh->handshake->keys.ivSz =
+                    ssh->handshake->peerKeys.ivSz =
+                    AEAD_NONCE_SZ;
+                ssh->handshake->macSz = ssh->handshake->blockSz;
+            }
+        }
+    }
+
+    /* MAC Algorithms - Client to Server */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Client to Server");
+        listSz = 2;
+        ret = GetNameList(list, &listSz, buf, len, &begin);
+        if (ret == WS_SUCCESS && !ssh->aeadMode) {
+            algoId = MatchIdLists(list, listSz, cannedMacAlgo, cannedMacAlgoSz);
+            if (algoId == ID_UNKNOWN) {
+                WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo C2S");
+                ret = WS_INVALID_ALGO_ID;
+            }
+        }
+    }
+
+    /* MAC Algorithms - Server to Client */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Server to Client");
+        listSz = 2;
+        ret = GetNameList(list, &listSz, buf, len, &begin);
+        if (ret == WS_SUCCESS && !ssh->handshake->aeadMode) {
+            if (MatchIdLists(list, listSz, &algoId, 1) == ID_UNKNOWN) {
+                WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo S2C");
+                ret = WS_INVALID_ALGO_ID;
+            }
+            else {
+                ssh->handshake->macId = algoId;
+                ssh->handshake->macSz = MacSzForId(algoId);
+                ssh->handshake->keys.macKeySz =
+                    ssh->handshake->peerKeys.macKeySz =
+                    KeySzForId(algoId);
+            }
+        }
+    }
+
+    /* Compression Algorithms - Client to Server */
+    if (ret == WS_SUCCESS) {
+        /* The compression algorithm lists should have none as a value. */
+        algoId = ID_NONE;
+
+        WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Client to Server");
+        listSz = 1;
+        ret = GetNameList(list, &listSz, buf, len, &begin);
+        if (ret == WS_SUCCESS) {
+            if (MatchIdLists(list, listSz, &algoId, 1) == ID_UNKNOWN) {
+                WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo C2S");
+                ret = WS_INVALID_ALGO_ID;
+            }
+        }
+    }
+
+    /* Compression Algorithms - Server to Client */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Server to Client");
+        listSz = 1;
+        ret = GetNameList(list, &listSz, buf, len, &begin);
+        if (ret == WS_SUCCESS) {
+            if (MatchIdLists(list, listSz, &algoId, 1) == ID_UNKNOWN) {
+                WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo S2C");
+                ret = WS_INVALID_ALGO_ID;
+            }
+        }
+    }
+
+    /* Languages - Client to Server, skip */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: Languages - Client to Server");
+        ret = GetUint32(&skipSz, buf, len, &begin);
+        if (ret == WS_SUCCESS)
+            begin += skipSz;
+    }
+
+    /* Languages - Server to Client, skip */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: Languages - Server to Client");
+        ret = GetUint32(&skipSz, buf, len, &begin);
+        if (ret == WS_SUCCESS)
+            begin += skipSz;
+    }
+
+    /* First KEX Packet Follows */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: KEX Packet Follows");
+        ret = GetBoolean(&ssh->handshake->kexPacketFollows, buf, len, &begin);
+    }
+
+    /* Skip the "for future use" length. */
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DKI: For Future Use");
+        ret = GetUint32(&skipSz, buf, len, &begin);
+        if (ret == WS_SUCCESS)
+            begin += skipSz;
+    }
+
+    if (ret == WS_SUCCESS) {
+        byte scratchLen[LENGTH_SZ];
+        word32 strSz;
+
+        if (!ssh->isKeying) {
+            WLOG(WS_LOG_DEBUG, "Keying initiated");
+            ret = SendKexInit(ssh);
+        }
+
+        if (ret == WS_SUCCESS)
+            ret = wc_HashInit(&ssh->handshake->hash, ssh->handshake->hashId);
+
+        if (ret == WS_SUCCESS) {
+            if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    ssh->peerProtoId, ssh->peerProtoIdSz);
+        }
+
+        if (ret == WS_SUCCESS) {
+            strSz = (word32)WSTRLEN(sshProtoIdStr) - SSH_PROTO_EOL_SZ;
+            c32toa(strSz, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+        }
+
+        if (ret == WS_SUCCESS)
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                (const byte*)sshProtoIdStr, strSz);
+
+        if (ret == WS_SUCCESS) {
+            if (ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) {
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    ssh->peerProtoId, ssh->peerProtoIdSz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HashUpdate(&ssh->handshake->hash,
+                                        ssh->handshake->hashId,
+                                        ssh->handshake->kexInit,
+                                        ssh->handshake->kexInitSz);
+            }
+        }
+
+        if (ret == WS_SUCCESS) {
+            c32toa(len + 1, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+        }
+
+        if (ret == WS_SUCCESS) {
+            scratchLen[0] = MSGID_KEXINIT;
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                scratchLen, MSG_ID_SZ);
+        }
+
+        if (ret == WS_SUCCESS)
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                buf, len);
+
+        if (ret == WS_SUCCESS) {
+            if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    ssh->handshake->kexInit,
+                                    ssh->handshake->kexInitSz);
+        }
+
+        if (ret == WS_SUCCESS) {
+            *idx = begin;
+            if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER)
+                ssh->clientState = CLIENT_KEXINIT_DONE;
+            else
+                ssh->serverState = SERVER_KEXINIT_DONE;
+        }
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoKexInit(), ret = %d", ret);
+    return ret;
+}
+
+
+static const byte dhGenerator[] = { 2 };
+static const byte dhPrimeGroup1[] = {
+    /* SSH DH Group 1 (Oakley Group 2, 1024-bit MODP Group, RFC 2409) */
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+static const byte dhPrimeGroup14[] = {
+    /* SSH DH Group 14 (Oakley Group 14, 2048-bit MODP Group, RFC 3526) */
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+    0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+    0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+    0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+    0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+    0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+    0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+    0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+    0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+    0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+    0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+    0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+    0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+    0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+    0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+    0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+    0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+    0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+    0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+    0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+    0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+    0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+    0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+    0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+    0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+    0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+    0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+    0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+    0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+    0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+static const word32 dhGeneratorSz = sizeof(dhGenerator);
+static const word32 dhPrimeGroup1Sz = sizeof(dhPrimeGroup1);
+static const word32 dhPrimeGroup14Sz = sizeof(dhPrimeGroup14);
+
+
+static int DoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    /* First get the length of the MP_INT, and then add in the hash of the
+     * mp_int value of e as it appears in the packet. After that, decode e
+     * into an mp_int struct for the DH calculation by wolfCrypt.
+     *
+     * This function also works as MSGID_KEXECDH_INIT (30). That message
+     * has the same format as MSGID_KEXDH_INIT, except it is the ECDH Q value
+     * in the message isn't of the DH e value. Treat the Q as e. */
+    /* DYNTYPE_DH */
+
+    byte* e;
+    word32 eSz;
+    word32 begin;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        ret = GetUint32(&eSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        e = buf + begin;
+        begin += eSz;
+
+        if (eSz <= sizeof(ssh->handshake->e)) {
+            WMEMCPY(ssh->handshake->e, e, eSz);
+            ssh->handshake->eSz = eSz;
+        }
+
+        ssh->clientState = CLIENT_KEXDH_INIT_DONE;
+        *idx = begin;
+
+        //ESP_LOGI("WOLFSSH", "Sending KexDH Reply to client");
+        ret = SendKexDhReply(ssh);
+    }
+
+    return ret;
+}
+
+
+static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    byte* pubKey;
+    word32 pubKeySz;
+    byte* f;
+    word32 fSz;
+    byte* sig;
+    word32 sigSz;
+    word32 scratch;
+    byte  scratchLen[LENGTH_SZ];
+    word32 kPad = 0;
+    struct {
+        byte useRsa;
+        word32 keySz;
+        union {
+            struct {
+                RsaKey   key;
+            } rsa;
+            struct {
+                ecc_key key;
+            } ecc;
+        } sk;
+    } sigKeyBlock;
+    word32 begin;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoKexDhReply()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        pubKey = buf + begin;
+        ret = GetUint32(&pubKeySz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = wc_HashUpdate(&ssh->handshake->hash,
+                            ssh->handshake->hashId,
+                            pubKey, pubKeySz + LENGTH_SZ);
+
+    if (ret == WS_SUCCESS) {
+        pubKey = buf + begin;
+        begin += pubKeySz;
+    }
+
+    /* If using DH-GEX include the GEX specific values. */
+    if (ssh->handshake->kexId == ID_DH_GEX_SHA256) {
+        byte primeGroupPad = 0, generatorPad = 0;
+
+        /* Hash in the client's requested minimum key size. */
+        if (ret == 0) {
+            c32toa(ssh->handshake->dhGexMinSz, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash,
+                                ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+        }
+        /* Hash in the client's requested preferred key size. */
+        if (ret == 0) {
+            c32toa(ssh->handshake->dhGexPreferredSz, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash,
+                                ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+        }
+        /* Hash in the client's requested maximum key size. */
+        if (ret == 0) {
+            c32toa(ssh->handshake->dhGexMaxSz, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash,
+                                ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+        }
+        /* Add a pad byte if the mpint has the MSB set. */
+        if (ret == 0) {
+            if (ssh->handshake->primeGroup[0] & 0x80)
+                primeGroupPad = 1;
+
+            /* Hash in the length of the GEX prime group. */
+            c32toa(ssh->handshake->primeGroupSz + primeGroupPad,
+                   scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash,
+                                ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+        }
+        /* Hash in the pad byte for the GEX prime group. */
+        if (ret == 0) {
+            if (primeGroupPad) {
+                scratchLen[0] = 0;
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, 1);
+            }
+        }
+        /* Hash in the GEX prime group. */
+        if (ret == 0)
+            ret  = wc_HashUpdate(&ssh->handshake->hash,
+                                 ssh->handshake->hashId,
+                                 ssh->handshake->primeGroup,
+                                 ssh->handshake->primeGroupSz);
+        /* Add a pad byte if the mpint has the MSB set. */
+        if (ret == 0) {
+            if (ssh->handshake->generator[0] & 0x80)
+                generatorPad = 1;
+
+            /* Hash in the length of the GEX generator. */
+            c32toa(ssh->handshake->generatorSz + generatorPad, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash,
+                                ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+        }
+        /* Hash in the pad byte for the GEX generator. */
+        if (ret == 0) {
+            if (generatorPad) {
+                scratchLen[0] = 0;
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, 1);
+            }
+        }
+        /* Hash in the GEX generator. */
+        if (ret == 0)
+            ret = wc_HashUpdate(&ssh->handshake->hash,
+                                ssh->handshake->hashId,
+                                ssh->handshake->generator,
+                                ssh->handshake->generatorSz);
+    }
+
+    /* Hash in the size of the client's DH e-value (ECDH Q-value). */
+    if (ret == 0) {
+        c32toa(ssh->handshake->eSz, scratchLen);
+        ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                            scratchLen, LENGTH_SZ);
+    }
+    /* Hash in the client's DH e-value (ECDH Q-value). */
+    if (ret == 0)
+        ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                            ssh->handshake->e, ssh->handshake->eSz);
+
+    /* Get and hash in the server's DH f-value (ECDH Q-value) */
+    if (ret == WS_SUCCESS) {
+        f = buf + begin;
+        ret = GetUint32(&fSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = wc_HashUpdate(&ssh->handshake->hash,
+                            ssh->handshake->hashId,
+                            f, fSz + LENGTH_SZ);
+
+    if (ret == WS_SUCCESS) {
+        f = buf + begin;
+        begin += fSz;
+        ret = GetUint32(&sigSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        sig = buf + begin;
+        begin += sigSz;
+        *idx = begin;
+
+        /* Load in the server's public signing key */
+        sigKeyBlock.useRsa = ssh->handshake->pubKeyId == ID_SSH_RSA;
+
+        if (sigKeyBlock.useRsa) {
+            byte* e;
+            word32 eSz;
+            byte* n;
+            word32 nSz;
+            word32 pubKeyIdx = 0;
+
+            ret = wc_InitRsaKey(&sigKeyBlock.sk.rsa.key, ssh->ctx->heap);
+            if (ret != 0)
+                ret = WS_RSA_E;
+            if (ret == 0)
+                ret = GetUint32(&scratch, pubKey, pubKeySz, &pubKeyIdx);
+            /* This is the algo name. */
+            if (ret == WS_SUCCESS) {
+                pubKeyIdx += scratch;
+                ret = GetUint32(&eSz, pubKey, pubKeySz, &pubKeyIdx);
+            }
+            if (ret == WS_SUCCESS) {
+                e = pubKey + pubKeyIdx;
+                pubKeyIdx += eSz;
+                ret = GetUint32(&nSz, pubKey, pubKeySz, &pubKeyIdx);
+            }
+            if (ret == WS_SUCCESS) {
+                n = pubKey + pubKeyIdx;
+                ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz,
+                                               &sigKeyBlock.sk.rsa.key);
+            }
+
+            if (ret == 0)
+                sigKeyBlock.keySz = sizeof(sigKeyBlock.sk.rsa.key);
+            else
+                ret = WS_RSA_E;
+        }
+        else {
+            ret = wc_ecc_init_ex(&sigKeyBlock.sk.ecc.key, ssh->ctx->heap,
+                                 INVALID_DEVID);
+            if (ret == 0)
+                ret = wc_ecc_import_x963(pubKey, pubKeySz,
+                                         &sigKeyBlock.sk.ecc.key);
+            if (ret == 0)
+                sigKeyBlock.keySz = sizeof(sigKeyBlock.sk.ecc.key);
+            else
+                ret = WS_ECC_E;
+        }
+
+        /* Generate and hash in the shared secret */
+        if (ret == 0) {
+            if (!ssh->handshake->useEcc) {
+                ret = wc_DhAgree(&ssh->handshake->privKey.dh,
+                                 ssh->k, &ssh->kSz,
+                                 ssh->handshake->x, ssh->handshake->xSz,
+                                 f, fSz);
+                ForceZero(ssh->handshake->x, ssh->handshake->xSz);
+                wc_FreeDhKey(&ssh->handshake->privKey.dh);
+            }
+            else {
+                ecc_key key;
+                ret = wc_ecc_init(&key);
+                if (ret == 0)
+                    ret = wc_ecc_import_x963(f, fSz, &key);
+                if (ret == 0)
+                    ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc,
+                                               &key, ssh->k, &ssh->kSz);
+                wc_ecc_free(&key);
+                wc_ecc_free(&ssh->handshake->privKey.ecc);
+            }
+        }
+
+        /* Hash in the shared secret K. */
+        if (ret == 0) {
+            kPad = (ssh->k[0] & 0x80) ? 1 : 0;
+            c32toa(ssh->kSz + kPad, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+        }
+        if (ret == 0) {
+            if (kPad) {
+                scratchLen[0] = 0;
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId, scratchLen, 1);
+            }
+        }
+        if (ret == 0)
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                ssh->k, ssh->kSz);
+
+        /* Save the exchange hash value H, and session ID. */
+        if (ret == 0)
+            ret = wc_HashFinal(&ssh->handshake->hash,
+                               ssh->handshake->hashId, ssh->h);
+        if (ret == 0) {
+            ssh->hSz = wc_HashGetDigestSize(ssh->handshake->hashId);
+            if (ssh->sessionIdSz == 0) {
+                WMEMCPY(ssh->sessionId, ssh->h, ssh->hSz);
+                ssh->sessionIdSz = ssh->hSz;
+            }
+        }
+
+        if (ret != WS_SUCCESS)
+            ret = WS_CRYPTO_FAILED;
+    }
+
+    /* Verify h with the server's public key. */
+    if (ret == WS_SUCCESS) {
+        /* Skip past the sig name. Check it, though. Other SSH implementations
+         * do the verify based on the name, despite what was agreed upon. XXX*/
+        begin = 0;
+        ret = GetUint32(&scratch, sig, sigSz, &begin);
+        if (ret == WS_SUCCESS) {
+            begin += scratch;
+            ret = GetUint32(&scratch, sig, sigSz, &begin);
+        }
+        if (ret == WS_SUCCESS) {
+            sig = sig + begin;
+            sigSz = scratch;
+
+            ret = wc_SignatureVerify(HashForId(ssh->handshake->pubKeyId),
+                                     sigKeyBlock.useRsa ?
+                                      WC_SIGNATURE_TYPE_RSA_W_ENC :
+                                      WC_SIGNATURE_TYPE_ECC,
+                                     ssh->h, ssh->hSz, sig, sigSz,
+                                     &sigKeyBlock.sk, sigKeyBlock.keySz);
+            if (ret != 0) {
+                WLOG(WS_LOG_DEBUG,
+                     "DoKexDhReply: Signature Verify fail (%d)", ret);
+                ret = sigKeyBlock.useRsa ? WS_RSA_E : WS_ECC_E;
+            }
+        }
+    }
+
+    if (sigKeyBlock.useRsa)
+        wc_FreeRsaKey(&sigKeyBlock.sk.rsa.key);
+    else
+        wc_ecc_free(&sigKeyBlock.sk.ecc.key);
+
+    if (ret == WS_SUCCESS)
+        ret = GenerateKeys(ssh);
+
+    if (ret == WS_SUCCESS)
+        ret = SendNewKeys(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoKexDhReply(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoNewKeys(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    int ret = WS_SUCCESS;
+
+    (void)buf;
+    (void)len;
+    (void)idx;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        ssh->peerEncryptId = ssh->handshake->encryptId;
+        ssh->peerMacId = ssh->handshake->macId;
+        ssh->peerBlockSz = ssh->handshake->blockSz;
+        ssh->peerMacSz = ssh->handshake->macSz;
+        ssh->peerAeadMode = ssh->handshake->aeadMode;
+        WMEMCPY(&ssh->peerKeys, &ssh->handshake->peerKeys, sizeof(Keys));
+
+        switch (ssh->peerEncryptId) {
+            case ID_NONE:
+                WLOG(WS_LOG_DEBUG, "DNK: peer using cipher none");
+                //ESP_LOGI("WOLFSSH", "DNK: peer using cipher none");
+                break;
+
+            case ID_AES128_CBC:
+                WLOG(WS_LOG_DEBUG, "DNK: peer using cipher aes128-cbc");
+                ret = wc_AesSetKey(&ssh->decryptCipher.aes,
+                                   ssh->peerKeys.encKey, ssh->peerKeys.encKeySz,
+                                   ssh->peerKeys.iv, AES_DECRYPTION);
+                //ESP_LOGI("WOLFSSH", "DNK: peer using cipher aes128-cbc");
+                break;
+
+            case ID_AES128_GCM:
+                WLOG(WS_LOG_DEBUG, "DNK: peer using cipher aes128-gcm");
+                ret = wc_AesGcmSetKey(&ssh->decryptCipher.aes,
+                                      ssh->peerKeys.encKey,
+                                      ssh->peerKeys.encKeySz);
+                //ESP_LOGI("WOLFSSH", "DNK: peer using cipher aes128-gcm");
+                break;
+
+            default:
+                WLOG(WS_LOG_DEBUG, "DNK: peer using cipher invalid");
+                //ESP_LOGI("WOLFSSH", "DNK: peer using cipher invalid");
+                break;
+        }
+
+        if (ret == 0)
+            ret = WS_SUCCESS;
+        else
+            ret = WS_CRYPTO_FAILED;
+    }
+
+    if (ret == WS_SUCCESS) {
+        ssh->rxCount = 0;
+        ssh->highwaterFlag = 0;
+        ssh->isKeying = 0;
+        HandshakeInfoFree(ssh->handshake, ssh->ctx->heap);
+        ssh->handshake = NULL;
+        WLOG(WS_LOG_DEBUG, "Keying completed");
+        //ESP_LOGI("WOLFSSH", "Keying completed");
+    }
+
+    return ret;
+}
+
+
+static int DoKexDhGexRequest(WOLFSSH* ssh,
+                             byte* buf, word32 len, word32* idx)
+{
+    word32 begin;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        ret = GetUint32(&ssh->handshake->dhGexMinSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = GetUint32(&ssh->handshake->dhGexPreferredSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = GetUint32(&ssh->handshake->dhGexMaxSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_INFO, "  min = %u, preferred = %u, max = %u",
+                ssh->handshake->dhGexMinSz,
+                ssh->handshake->dhGexPreferredSz,
+                ssh->handshake->dhGexMaxSz);
+        *idx = begin;
+        ret = SendKexDhGexGroup(ssh);
+    }
+
+    return ret;
+}
+
+
+static int DoKexDhGexGroup(WOLFSSH* ssh,
+                           byte* buf, word32 len, word32* idx)
+{
+    byte* primeGroup = NULL;
+    word32 primeGroupSz;
+    byte* generator = NULL;
+    word32 generatorSz;
+    word32 begin;
+    int ret = WS_UNIMPLEMENTED_E;
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        ret = GetMpint(&primeGroupSz, &primeGroup, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetMpint(&generatorSz, &generator, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        ssh->handshake->primeGroup =
+            (byte*)WMALLOC(primeGroupSz + UINT32_SZ,
+                           ssh->ctx->heap, DYNTYPE_MPINT);
+        if (ssh->handshake->primeGroup == NULL)
+            ret = WS_MEMORY_E;
+    }
+
+    if (ret == WS_SUCCESS) {
+        ssh->handshake->generator =
+            (byte*)WMALLOC(generatorSz + UINT32_SZ,
+                           ssh->ctx->heap, DYNTYPE_MPINT);
+        if (ssh->handshake->generator == NULL) {
+            ret = WS_MEMORY_E;
+            WFREE(ssh->handshake->primeGroup, ssh->ctx->heap, DYNTYPE_MPINT);
+            ssh->handshake->primeGroup = NULL;
+        }
+    }
+
+    if (WS_SUCCESS) {
+        c32toa(primeGroupSz, ssh->handshake->primeGroup);
+        WMEMCPY(ssh->handshake->primeGroup + UINT32_SZ,
+                primeGroup, primeGroupSz);
+        ssh->handshake->primeGroupSz = primeGroupSz;
+        c32toa(generatorSz, ssh->handshake->generator);
+        WMEMCPY(ssh->handshake->generator + UINT32_SZ,
+                generator, generatorSz);
+        ssh->handshake->generatorSz = generatorSz;
+
+        *idx = begin;
+        ret = SendKexDhInit(ssh);
+    }
+
+    return ret;
+}
+
+
+static int DoIgnore(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    word32 dataSz;
+    word32 begin = *idx;
+
+    (void)ssh;
+    (void)len;
+
+    ato32(buf + begin, &dataSz);
+    begin += LENGTH_SZ + dataSz;
+
+    *idx = begin;
+
+    return WS_SUCCESS;
+}
+
+
+static int DoDebug(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    byte  alwaysDisplay;
+    char*    msg = NULL;
+    char*    lang = NULL;
+    word32 strSz;
+    word32 begin = *idx;
+
+    (void)ssh;
+    (void)len;
+
+    alwaysDisplay = buf[begin++];
+
+    ato32(buf + begin, &strSz);
+    begin += LENGTH_SZ;
+    if (strSz > 0) {
+        msg = (char*)WMALLOC(strSz + 1, ssh->ctx->heap, DYNTYPE_STRING);
+        if (msg != NULL) {
+            WMEMCPY(msg, buf + begin, strSz);
+            msg[strSz] = 0;
+        }
+        else {
+            return WS_MEMORY_E;
+        }
+        begin += strSz;
+    }
+
+    ato32(buf + begin, &strSz);
+    begin += LENGTH_SZ;
+    if (strSz > 0) {
+        lang = (char*)WMALLOC(strSz + 1, ssh->ctx->heap, DYNTYPE_STRING);
+        if (lang != NULL) {
+            WMEMCPY(lang, buf + begin, strSz);
+            lang[strSz] = 0;
+        }
+        else {
+            WFREE(msg, ssh->ctx->heap, DYNTYPE_STRING);
+            return WS_MEMORY_E;
+        }
+        begin += strSz;
+    }
+
+    if (alwaysDisplay) {
+        WLOG(WS_LOG_DEBUG, "DEBUG MSG (%s): %s",
+             (lang == NULL) ? "none" : lang,
+             (msg == NULL) ? "no message" : msg);
+    }
+
+    *idx = begin;
+
+    WFREE(msg, ssh->ctx->heap, DYNTYPE_STRING);
+    WFREE(lang, ssh->ctx->heap, DYNTYPE_STRING);
+
+    return WS_SUCCESS;
+}
+
+
+static int DoUnimplemented(WOLFSSH* ssh,
+                           byte* buf, word32 len, word32* idx)
+{
+    word32 seq;
+    word32 begin = *idx;
+
+    (void)ssh;
+    (void)len;
+
+    ato32(buf + begin, &seq);
+    begin += UINT32_SZ;
+
+    WLOG(WS_LOG_DEBUG, "UNIMPLEMENTED: seq %u", seq);
+
+    *idx = begin;
+
+    return WS_SUCCESS;
+}
+
+
+static int DoDisconnect(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    word32 reason;
+    const char* reasonStr;
+    word32 begin = *idx;
+
+    (void)ssh;
+    (void)len;
+    (void)reasonStr;
+
+    ato32(buf + begin, &reason);
+    begin += UINT32_SZ;
+
+#ifdef NO_WOLFSSH_STRINGS
+    WLOG(WS_LOG_DEBUG, "DISCONNECT: (%u)", reason);
+#elif defined(DEBUG_WOLFSSH)
+    switch (reason) {
+        case WOLFSSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT:
+            reasonStr = "host not allowed to connect"; break;
+        case WOLFSSH_DISCONNECT_PROTOCOL_ERROR:
+            reasonStr = "protocol error"; break;
+        case WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED:
+            reasonStr = "key exchange failed"; break;
+        case WOLFSSH_DISCONNECT_RESERVED:
+            reasonStr = "reserved"; break;
+        case WOLFSSH_DISCONNECT_MAC_ERROR:
+            reasonStr = "mac error"; break;
+        case WOLFSSH_DISCONNECT_COMPRESSION_ERROR:
+            reasonStr = "compression error"; break;
+        case WOLFSSH_DISCONNECT_SERVICE_NOT_AVAILABLE:
+            reasonStr = "service not available"; break;
+        case WOLFSSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED:
+            reasonStr = "protocol version not supported"; break;
+        case WOLFSSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE:
+            reasonStr = "host key not verifiable"; break;
+        case WOLFSSH_DISCONNECT_CONNECTION_LOST:
+            reasonStr = "connection lost"; break;
+        case WOLFSSH_DISCONNECT_BY_APPLICATION:
+            reasonStr = "disconnect by application"; break;
+        case WOLFSSH_DISCONNECT_TOO_MANY_CONNECTIONS:
+            reasonStr = "too many connections"; break;
+        case WOLFSSH_DISCONNECT_AUTH_CANCELLED_BY_USER:
+            reasonStr = "auth cancelled by user"; break;
+        case WOLFSSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE:
+            reasonStr = "no more auth methods available"; break;
+        case WOLFSSH_DISCONNECT_ILLEGAL_USER_NAME:
+            reasonStr = "illegal user name"; break;
+        default:
+            reasonStr = "unknown reason";
+    }
+    WLOG(WS_LOG_DEBUG, "DISCONNECT: (%u) %s", reason, reasonStr);
+#endif
+
+    *idx = begin;
+
+    return WS_SUCCESS;
+}
+
+
+static int DoServiceRequest(WOLFSSH* ssh,
+                            byte* buf, word32 len, word32* idx)
+{
+    word32 begin = *idx;
+    word32 nameSz;
+    char     serviceName[32];
+
+    (void)len;
+
+    ato32(buf + begin, &nameSz);
+    begin += LENGTH_SZ;
+
+    WMEMCPY(serviceName, buf + begin, nameSz);
+    begin += nameSz;
+    serviceName[nameSz] = 0;
+
+    *idx = begin;
+
+    WLOG(WS_LOG_DEBUG, "Requesting service: %s", serviceName);
+    ssh->clientState = CLIENT_USERAUTH_REQUEST_DONE;
+
+    return WS_SUCCESS;
+}
+
+
+static int DoServiceAccept(WOLFSSH* ssh,
+                           byte* buf, word32 len, word32* idx)
+{
+    word32 begin = *idx;
+    word32 nameSz;
+    char     serviceName[32];
+
+    (void)len;
+
+    ato32(buf + begin, &nameSz);
+    begin += LENGTH_SZ;
+
+    WMEMCPY(serviceName, buf + begin, nameSz);
+    begin += nameSz;
+    serviceName[nameSz] = 0;
+
+    *idx = begin;
+
+    WLOG(WS_LOG_DEBUG, "Accepted service: %s", serviceName);
+    ssh->serverState = SERVER_USERAUTH_REQUEST_DONE;
+
+    return WS_SUCCESS;
+}
+
+
+/* Utility for DoUserAuthRequest() */
+static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData,
+                                     byte* buf, word32 len, word32* idx)
+{
+    word32 begin;
+    WS_UserAuthData_Password* pw;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPassword()");
+
+    if (ssh == NULL || authData == NULL ||
+        buf == NULL || len == 0 || idx == NULL) {
+
+        ret = WS_BAD_ARGUMENT;
+    }
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        pw = &authData->sf.password;
+        authData->type = WOLFSSH_USERAUTH_PASSWORD;
+        ret = GetBoolean(&pw->hasNewPassword, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&pw->passwordSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        pw->password = buf + begin;
+        begin += pw->passwordSz;
+
+        if (pw->hasNewPassword) {
+            /* Skip the password change. Maybe error out since we aren't
+             * supporting password changes at this time. */
+            ret = GetUint32(&pw->newPasswordSz, buf, len, &begin);
+            if (ret == WS_SUCCESS) {
+                pw->newPassword = buf + begin;
+                begin += pw->newPasswordSz;
+            }
+        }
+        else {
+            pw->newPassword = NULL;
+            pw->newPasswordSz = 0;
+        }
+
+        if (ssh->ctx->userAuthCb != NULL) {
+            WLOG(WS_LOG_DEBUG, "DUARPW: Calling the userauth callback");
+            ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD,
+                                       authData, ssh->userAuthCtx);
+            if (ret == WOLFSSH_USERAUTH_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, "DUARPW: password check successful");
+                ssh->clientState = CLIENT_USERAUTH_DONE;
+                ret = WS_SUCCESS;
+            }
+            else {
+                WLOG(WS_LOG_DEBUG, "DUARPW: password check failed");
+                ret = SendUserAuthFailure(ssh, 0);
+            }
+        }
+        else {
+            WLOG(WS_LOG_DEBUG, "DUARPW: No user auth callback");
+            ret = SendUserAuthFailure(ssh, 0);
+        }
+    }
+
+    if (ret == WS_SUCCESS)
+        *idx = begin;
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPassword(), ret = %d", ret);
+    return ret;
+}
+
+int ConstantCompare(byte encDigest[], byte checkDigest[],
+                                  word32 encDigestSz){
+                                  	return 0;
+                                  	}
+                                  
+
+/* Utility for DoUserAuthRequestPublicKey() */
+/* returns negative for error, positive is size of digest. */
+static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
+                                byte hashId, byte* digest, word32 digestSz)
+{
+    RsaKey key;
+    byte checkDigest[MAX_ENCODED_SIG_SZ];
+    int checkDigestSz;
+    byte* publicKeyType;
+    word32 publicKeyTypeSz = 0;
+    byte* n;
+    word32 nSz = 0;
+    byte* e;
+    word32 eSz = 0;
+    word32 i = 0;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestRsa()");
+
+    if (ssh == NULL || pk == NULL || digest == NULL || digestSz == 0)
+        ret = WS_BAD_ARGUMENT;
+
+    /* First check that the public key's type matches the one we are
+     * expecting. */
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i);
+
+    if (ret == WS_SUCCESS) {
+        publicKeyType = pk->publicKey + i;
+        i += publicKeyTypeSz;
+        if (publicKeyTypeSz != pk->publicKeyTypeSz &&
+            WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
+
+            WLOG(WS_LOG_DEBUG,
+                "Public Key's type does not match public key type");
+            ret = WS_INVALID_ALGO_ID;
+        }
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&eSz, pk->publicKey, pk->publicKeySz, &i);
+
+    if (ret == WS_SUCCESS) {
+        e = pk->publicKey + i;
+        i += eSz;
+        ret = GetUint32(&nSz, pk->publicKey, pk->publicKeySz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        n = pk->publicKey + i;
+
+        ret = wc_InitRsaKey(&key, ssh->ctx->heap);
+        if (ret == 0)
+            ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, &key);
+        if (ret != 0) {
+            WLOG(WS_LOG_DEBUG, "Could not decode public key");
+            ret = WS_CRYPTO_FAILED;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        i = 0;
+        /* First check that the signature's public key type matches the one
+         * we are expecting. */
+        ret = GetUint32(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        publicKeyType = pk->publicKey + i;
+        i += publicKeyTypeSz;
+
+        if (publicKeyTypeSz != pk->publicKeyTypeSz &&
+            WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
+
+            WLOG(WS_LOG_DEBUG,
+                 "Signature's type does not match public key type");
+            ret = WS_INVALID_ALGO_ID;
+        }
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&nSz, pk->signature, pk->signatureSz, &i);
+
+    if (ret == WS_SUCCESS) {
+        n = pk->signature + i;
+        checkDigestSz = wc_RsaSSL_Verify(n, nSz, checkDigest,
+                                         sizeof(checkDigest), &key);
+        if (checkDigestSz <= 0) {
+            WLOG(WS_LOG_DEBUG, "Could not verify signature");
+            ret = WS_CRYPTO_FAILED;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        byte encDigest[MAX_ENCODED_SIG_SZ];
+        word32 encDigestSz;
+        volatile int compare;
+        volatile int sizeCompare;
+
+        encDigestSz = wc_EncodeSignature(encDigest, digest,
+                                         wc_HashGetDigestSize(hashId),
+                                         wc_HashGetOID(hashId));
+
+        compare = ConstantCompare(encDigest, checkDigest,
+                                  encDigestSz);
+        sizeCompare = encDigestSz != (word32)checkDigestSz;
+
+        if ((compare | sizeCompare) == 0)
+            ret = WS_SUCCESS;
+        else
+            ret = WS_RSA_E;
+    }
+
+    wc_FreeRsaKey(&key);
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestRsa(), ret = %d", ret);
+    return ret;
+}
+
+
+/* Utility for DoUserAuthRequestPublicKey() */
+/* returns negative for error, positive is size of digest. */
+static int DoUserAuthRequestEcc(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
+                                byte hashId, byte* digest, word32 digestSz)
+{
+    ecc_key key;
+    byte* publicKeyType;
+    word32 publicKeyTypeSz = 0;
+    byte* curveName;
+    word32 curveNameSz = 0;
+    mp_int r, s;
+    byte* q;
+    word32 sz, qSz;
+    word32 i = 0;
+    int ret = WS_SUCCESS;
+
+    (void)hashId;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestRsa()");
+
+    if (ssh == NULL || pk == NULL || digest == NULL || digestSz == 0)
+        ret = WS_BAD_ARGUMENT;
+
+    /* First check that the public key's type matches the one we are
+     * expecting. */
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&publicKeyTypeSz, pk->publicKey, pk->publicKeySz, &i);
+
+    if (ret == WS_SUCCESS) {
+        publicKeyType = pk->publicKey + i;
+        i += publicKeyTypeSz;
+        if (publicKeyTypeSz != pk->publicKeyTypeSz &&
+            WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
+
+            WLOG(WS_LOG_DEBUG,
+                "Public Key's type does not match public key type");
+            ret = WS_INVALID_ALGO_ID;
+        }
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&curveNameSz, pk->publicKey, pk->publicKeySz, &i);
+
+    if (ret == WS_SUCCESS) {
+        curveName = pk->publicKey + i;
+        (void)curveName; /* Not used at the moment, hush the compiler. */
+        i += curveNameSz;
+        ret = GetUint32(&qSz, pk->publicKey, pk->publicKeySz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        q = pk->publicKey + i;
+        i += qSz;
+        ret = wc_ecc_init_ex(&key, ssh->ctx->heap, INVALID_DEVID);
+    }
+
+    if (ret == 0)
+        ret = wc_ecc_import_x963(q, qSz, &key);
+
+    if (ret != 0) {
+        WLOG(WS_LOG_DEBUG, "Could not decode public key");
+        ret = WS_CRYPTO_FAILED;
+    }
+
+    if (ret == WS_SUCCESS) {
+        i = 0;
+        /* First check that the signature's public key type matches the one
+         * we are expecting. */
+        ret = GetUint32(&publicKeyTypeSz, pk->signature, pk->signatureSz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        publicKeyType = pk->signature + i;
+        i += publicKeyTypeSz;
+
+        if (publicKeyTypeSz != pk->publicKeyTypeSz &&
+            WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
+
+            WLOG(WS_LOG_DEBUG,
+                 "Signature's type does not match public key type");
+            ret = WS_INVALID_ALGO_ID;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        /* Get the size of the signature blob. */
+        ret = GetUint32(&sz, pk->signature, pk->signatureSz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        /* Get R and S. */
+        ret = GetUint32(&sz, pk->signature, pk->signatureSz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = mp_read_unsigned_bin(&r, pk->signature + i, sz);
+        if (ret != 0)
+            ret = WS_PARSE_E;
+        else
+            ret = WS_SUCCESS;
+    }
+
+    if (ret == WS_SUCCESS) {
+        i += sz;
+        ret = GetUint32(&sz, pk->signature, pk->signatureSz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = mp_read_unsigned_bin(&s, pk->signature + i, sz);
+        if (ret != 0)
+            ret = WS_PARSE_E;
+        else
+            ret = WS_SUCCESS;
+    }
+
+    if (ret == WS_SUCCESS) {
+        int status = 0;
+
+        ret = wc_ecc_verify_hash_ex(&r, &s, digest, digestSz, &status, &key);
+        if (ret != 0) {
+            WLOG(WS_LOG_DEBUG, "Could not verify signature");
+            ret = WS_CRYPTO_FAILED;
+        }
+        else
+            ret = status ? WS_SUCCESS : WS_ECC_E;
+    }
+
+    mp_clear(&r);
+    mp_clear(&s);
+    wc_ecc_free(&key);
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEcc(), ret = %d", ret);
+    return ret;
+}
+
+
+/* Utility for DoUserAuthRequest() */
+static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
+                                      byte* buf, word32 len, word32* idx)
+{
+    word32 begin;
+    WS_UserAuthData_PublicKey* pk;
+    int ret = WS_SUCCESS;
+    int authFailure = 0;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPublicKey()");
+
+    if (ssh == NULL || authData == NULL ||
+        buf == NULL || len == 0 || idx == NULL) {
+
+        ret = WS_BAD_ARGUMENT;
+    }
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        pk = &authData->sf.publicKey;
+        authData->type = WOLFSSH_USERAUTH_PUBLICKEY;
+        ret = GetBoolean(&pk->hasSignature, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&pk->publicKeyTypeSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        pk->publicKeyType = buf + begin;
+        begin += pk->publicKeyTypeSz;
+        ret = GetUint32(&pk->publicKeySz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        pk->publicKey = buf + begin;
+        begin += pk->publicKeySz;
+
+        if (pk->hasSignature) {
+            ret = GetUint32(&pk->signatureSz, buf, len, &begin);
+            if (ret == WS_SUCCESS) {
+                pk->signature = buf + begin;
+                begin += pk->signatureSz;
+            }
+        }
+        else {
+            pk->signature = NULL;
+            pk->signatureSz = 0;
+        }
+
+        if (ret == WS_SUCCESS) {
+            *idx = begin;
+
+            if (ssh->ctx->userAuthCb != NULL) {
+                WLOG(WS_LOG_DEBUG, "DUARPK: Calling the userauth callback");
+                ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY,
+                                           authData, ssh->userAuthCtx);
+                WLOG(WS_LOG_DEBUG, "DUARPK: callback result = %d", ret);
+                if (ret == WOLFSSH_USERAUTH_SUCCESS)
+                    ret = WS_SUCCESS;
+                else {
+                    ret = SendUserAuthFailure(ssh, 0);
+                    authFailure = 1;
+                }
+            }
+            else {
+                WLOG(WS_LOG_DEBUG, "DUARPK: no userauth callback set");
+                ret = SendUserAuthFailure(ssh, 0);
+                authFailure = 1;
+            }
+        }
+    }
+
+    if (ret == WS_SUCCESS && !authFailure) {
+        if (pk->signature == NULL) {
+            WLOG(WS_LOG_DEBUG, "DUARPK: Send the PK OK");
+            ret = SendUserAuthPkOk(ssh, pk->publicKeyType, pk->publicKeyTypeSz,
+                                   pk->publicKey, pk->publicKeySz);
+        }
+        else {
+            wc_HashAlg hash;
+            byte digest[WC_MAX_DIGEST_SIZE];
+            word32 digestSz;
+            byte hashId = WC_HASH_TYPE_SHA;
+            byte pkTypeId;
+
+            pkTypeId = NameToId((char*)pk->publicKeyType,
+                                pk->publicKeyTypeSz);
+            if (pkTypeId == ID_UNKNOWN)
+                ret = WS_INVALID_ALGO_ID;
+
+            if (ret == WS_SUCCESS) {
+                hashId = HashForId(pkTypeId);
+                WMEMSET(digest, 0, sizeof(digest));
+                digestSz = wc_HashGetDigestSize(hashId);
+                ret = wc_HashInit(&hash, hashId);
+            }
+
+            if (ret == 0) {
+                c32toa(ssh->sessionIdSz, digest);
+                ret = wc_HashUpdate(&hash, hashId, digest, UINT32_SZ);
+            }
+
+            if (ret == 0)
+                ret = wc_HashUpdate(&hash, hashId,
+                                    ssh->sessionId, ssh->sessionIdSz);
+
+            if (ret == 0) {
+                digest[0] = MSGID_USERAUTH_REQUEST;
+                ret = wc_HashUpdate(&hash, hashId, digest, MSG_ID_SZ);
+            }
+
+            /* The rest of the fields in the signature are already
+             * in the buffer. Just need to account for the sizes. */
+            if (ret == 0)
+                ret = wc_HashUpdate(&hash, hashId, pk->dataToSign,
+                                    authData->usernameSz +
+                                    authData->serviceNameSz +
+                                    authData->authNameSz + BOOLEAN_SZ +
+                                    pk->publicKeyTypeSz + pk->publicKeySz +
+                                    (UINT32_SZ * 5));
+            if (ret == 0) {
+                ret = wc_HashFinal(&hash, hashId, digest);
+
+                if (ret != 0)
+                    ret = WS_CRYPTO_FAILED;
+                else
+                    ret = WS_SUCCESS;
+            }
+
+            if (ret == WS_SUCCESS) {
+                if (pkTypeId == ID_SSH_RSA)
+                    ret = DoUserAuthRequestRsa(ssh, pk,
+                                               hashId, digest, digestSz);
+                else if (pkTypeId == ID_ECDSA_SHA2_NISTP256 ||
+                         pkTypeId == ID_ECDSA_SHA2_NISTP384 ||
+                         pkTypeId == ID_ECDSA_SHA2_NISTP521)
+                    ret = DoUserAuthRequestEcc(ssh, pk,
+                                               hashId, digest, digestSz);
+            }
+
+            if (ret != WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, "DUARPK: signature compare failure");
+                ret = SendUserAuthFailure(ssh, 0);
+            }
+            else {
+                ssh->clientState = CLIENT_USERAUTH_DONE;
+            }
+        }
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPublicKey(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoUserAuthRequest(WOLFSSH* ssh,
+                             byte* buf, word32 len, word32* idx)
+{
+    word32 begin;
+    int ret = WS_SUCCESS;
+    byte authNameId;
+    WS_UserAuthData authData;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequest()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        WMEMSET(&authData, 0, sizeof(authData));
+        ret = GetUint32(&authData.usernameSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        authData.username = buf + begin;
+        begin += authData.usernameSz;
+
+        ret = GetUint32(&authData.serviceNameSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        authData.serviceName = buf + begin;
+        begin += authData.serviceNameSz;
+
+        ret = GetUint32(&authData.authNameSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        authData.authName = buf + begin;
+        begin += authData.authNameSz;
+        authNameId = NameToId((char*)authData.authName, authData.authNameSz);
+
+        if (authNameId == ID_USERAUTH_PASSWORD)
+            ret = DoUserAuthRequestPassword(ssh, &authData, buf, len, &begin);
+        else if (authNameId == ID_USERAUTH_PUBLICKEY) {
+            authData.sf.publicKey.dataToSign = buf + *idx;
+            ret = DoUserAuthRequestPublicKey(ssh, &authData, buf, len, &begin);
+        }
+#ifdef WOLFSSH_ALLOW_USERAUTH_NONE
+        else if (authNameId == ID_NONE) {
+            ssh->clientState = CLIENT_USERAUTH_DONE;
+        }
+#endif
+        else {
+            WLOG(WS_LOG_DEBUG,
+                 "invalid userauth type: %s", IdToName(authNameId));
+            ret = SendUserAuthFailure(ssh, 0);
+        }
+
+        *idx = begin;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequest(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoUserAuthFailure(WOLFSSH* ssh,
+                             byte* buf, word32 len, word32* idx)
+{
+    byte authList[3]; /* Should only ever be password, publickey, hostname */
+    word32 authListSz;
+    byte partialSuccess;
+    byte authId = ID_USERAUTH_PASSWORD;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthFailure()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = GetNameList(authList, &authListSz, buf, len, idx);
+
+    if (ret == WS_SUCCESS)
+        ret = GetBoolean(&partialSuccess, buf, len, idx);
+
+    if (ret == WS_SUCCESS)
+        ret = SendUserAuthRequest(ssh, authId);
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthFailure(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoUserAuthSuccess(WOLFSSH* ssh,
+                             byte* buf, word32 len, word32* idx)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthSuccess()");
+
+    /* This message does not have any payload. len should be 0. */
+    if (ssh == NULL || buf == NULL || len != 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    ssh->serverState = SERVER_USERAUTH_ACCEPT_DONE;
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthSuccess(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoUserAuthBanner(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    word32 begin;
+    char banner[80];
+    word32 bannerSz = sizeof(banner);
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthBanner()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        ret = GetString(banner, &bannerSz, buf, len, idx);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&bannerSz, buf, len, idx);
+
+    if (ret == WS_SUCCESS) {
+        begin += bannerSz;
+        if (ssh->ctx->showBanner) {
+            WLOG(WS_LOG_INFO, "%s", banner);
+        }
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthBanner(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoGlobalRequest(WOLFSSH* ssh,
+                           byte* buf, word32 len, word32* idx)
+{
+    word32 begin;
+    int ret = WS_SUCCESS;
+    char name[80];
+    word32 nameSz = sizeof(name);
+    byte wantReply = 0;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoGlobalRequest()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        ret = GetString(name, &nameSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "DGR: request name = %s", name);
+        ret = GetBoolean(&wantReply, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS) {
+        *idx += len;
+
+        if (wantReply)
+            ret = SendRequestSuccess(ssh, 0);
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoGlobalRequest(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelOpen(WOLFSSH* ssh,
+                         byte* buf, word32 len, word32* idx)
+{
+    word32 begin = *idx;
+    word32 typeSz;
+    char type[32];
+    byte typeId = ID_UNKNOWN;
+    word32 peerChannelId;
+    word32 peerInitialWindowSz;
+    word32 peerMaxPacketSz;
+    int ret;
+    WOLFSSH_CHANNEL* newChannel;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelOpen()");
+    //ESP_LOGI("WOLFSSH", "Entering DoChannelOpen()");
+
+    typeSz = sizeof(type);
+    ret = GetString(type, &typeSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&peerChannelId, buf, len, &begin);
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&peerInitialWindowSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&peerMaxPacketSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        *idx = begin;
+
+        WLOG(WS_LOG_INFO, "  type = %s", type);
+        WLOG(WS_LOG_INFO, "  peerChannelId = %u", peerChannelId);
+        WLOG(WS_LOG_INFO, "  peerInitialWindowSz = %u", peerInitialWindowSz);
+        WLOG(WS_LOG_INFO, "  peerMaxPacketSz = %u", peerMaxPacketSz);
+
+        //ESP_LOGI("WOLFSSH", "DoChannelOpen type = %s", type);
+        //ESP_LOGI("WOLFSSH", "DoChannelOpen peerChId = %u", peerChannelId);
+        //ESP_LOGI("WOLFSSH", "DoChannelOpen peerInitWndSz = %u", peerInitialWindowSz);
+        //ESP_LOGI("WOLFSSH", "DoChannelOpen peerMaxPktSz = %u", peerMaxPacketSz);
+
+        typeId = NameToId(type, typeSz);
+        if (typeId != ID_CHANTYPE_SESSION)
+            ret = WS_INVALID_CHANTYPE;
+        //ESP_LOGI("WOLFSSH", "DoChannelOpen ret = %d", ret);
+    }
+
+    if (ret == WS_SUCCESS) {
+        newChannel = ChannelNew(ssh, typeId,
+                                DEFAULT_WINDOW_SZ, DEFAULT_MAX_PACKET_SZ);
+        //ESP_LOGI("WOLFSSH", "DoChannelOpen newCh = %p", newChannel);
+        if (newChannel == NULL)
+            ret = WS_RESOURCE_E;
+        else {
+            ChannelUpdate(newChannel, peerChannelId,
+                          peerInitialWindowSz, peerMaxPacketSz);
+            //ESP_LOGI("WOLFSSH", "DoChannelOpen ChUpdate");
+            if (ssh->channelListSz == 0)
+                ssh->defaultPeerChannelId = peerChannelId;
+            ChannelAppend(ssh, newChannel);
+
+            ssh->clientState = CLIENT_DONE;
+        }
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpen(), ret = %d", ret);
+    //ESP_LOGI("WOLFSSH", "Leaving DoChannelOpen(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelOpenConf(WOLFSSH* ssh,
+                             byte* buf, word32 len, word32* idx)
+{
+    WOLFSSH_CHANNEL* channel;
+    word32 begin, channelId, peerChannelId,
+           peerInitialWindowSz, peerMaxPacketSz;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelOpenConf()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        ret = GetUint32(&channelId, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&peerChannelId, buf, len, &begin);
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&peerInitialWindowSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&peerMaxPacketSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_INFO, "  channelId = %u", channelId);
+        WLOG(WS_LOG_INFO, "  peerChannelId = %u", peerChannelId);
+        WLOG(WS_LOG_INFO, "  peerInitialWindowSz = %u", peerInitialWindowSz);
+        WLOG(WS_LOG_INFO, "  peerMaxPacketSz = %u", peerMaxPacketSz);
+
+        channel = ChannelFind(ssh, channelId, FIND_SELF);
+        if (channel == NULL)
+            ret = WS_INVALID_CHANID;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = ChannelUpdate(channel, peerChannelId,
+                            peerInitialWindowSz, peerMaxPacketSz);
+
+    if (ret == WS_SUCCESS)
+        ssh->serverState = SERVER_CHANNEL_OPEN_DONE;
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpenConf(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelOpenFail(WOLFSSH* ssh,
+                             byte* buf, word32 len, word32* idx)
+{
+    char desc[80];
+    word32 begin, channelId, reasonId, descSz, langSz;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelOpenFail()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        begin = *idx;
+        ret = GetUint32(&channelId, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&reasonId, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        descSz = sizeof(desc);
+        ret = GetString(desc, &descSz, buf, len, &begin);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&langSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        *idx = begin + langSz;
+
+        WLOG(WS_LOG_INFO, "channel open failure reason code: %u", reasonId);
+        if (descSz > 0) {
+            WLOG(WS_LOG_INFO, "description: %s", desc);
+        }
+
+        ret = ChannelRemove(ssh, channelId, FIND_SELF);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = WS_CHANOPEN_FAILED;
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelOpenFail(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelClose(WOLFSSH* ssh,
+                          byte* buf, word32 len, word32* idx)
+{
+    WOLFSSH_CHANNEL* channel = NULL;
+    word32 begin = *idx;
+    word32 channelId;
+    int ret;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelClose()");
+
+    ret = GetUint32(&channelId, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        *idx = begin;
+
+        channel = ChannelFind(ssh, channelId, FIND_SELF);
+        if (channel == NULL)
+            ret = WS_INVALID_CHANID;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendChannelClose(ssh, channelId);
+
+    if (ret == WS_SUCCESS)
+        ret = ChannelRemove(ssh, channelId, FIND_SELF);
+
+	if (ret == WS_SUCCESS)
+		ret = WS_CHANNEL_CLOSED;
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelClose(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelRequest(WOLFSSH* ssh,
+                            byte* buf, word32 len, word32* idx)
+{
+    word32 begin = *idx;
+    word32 channelId;
+    word32 typeSz;
+    char type[32];
+    byte wantReply;
+    int ret;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelRequest()");
+
+    ret = GetUint32(&channelId, buf, len, &begin);
+
+    typeSz = sizeof(type);
+    if (ret == WS_SUCCESS)
+        ret = GetString(type, &typeSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS)
+        ret = GetBoolean(&wantReply, buf, len, &begin);
+
+    if (ret != WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "Leaving DoChannelRequest(), ret = %d", ret);
+        return ret;
+    }
+
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "  channelId = %u", channelId);
+        WLOG(WS_LOG_DEBUG, "  type = %s", type);
+        WLOG(WS_LOG_DEBUG, "  wantReply = %u", wantReply);
+
+        if (WSTRNCMP(type, "pty-req", typeSz) == 0) {
+            char term[32];
+            word32 termSz;
+            word32 widthChar, heightRows, widthPixels, heightPixels;
+            word32 modesSz;
+
+            termSz = sizeof(term);
+            ret = GetString(term, &termSz, buf, len, &begin);
+            if (ret == WS_SUCCESS)
+                ret = GetUint32(&widthChar, buf, len, &begin);
+            if (ret == WS_SUCCESS)
+                ret = GetUint32(&heightRows, buf, len, &begin);
+            if (ret == WS_SUCCESS)
+                ret = GetUint32(&widthPixels, buf, len, &begin);
+            if (ret == WS_SUCCESS)
+                ret = GetUint32(&heightPixels, buf, len, &begin);
+            if (ret == WS_SUCCESS)
+                ret = GetUint32(&modesSz, buf, len, &begin);
+
+            if (ret == WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, "  term = %s", term);
+                WLOG(WS_LOG_DEBUG, "  widthChar = %u", widthChar);
+                WLOG(WS_LOG_DEBUG, "  heightRows = %u", heightRows);
+                WLOG(WS_LOG_DEBUG, "  widthPixels = %u", widthPixels);
+                WLOG(WS_LOG_DEBUG, "  heightPixels = %u", heightPixels);
+                WLOG(WS_LOG_DEBUG, "  modes = %u", (modesSz - 1) / 5);
+            }
+        }
+        else if (WSTRNCMP(type, "env", typeSz) == 0) {
+            char name[32];
+            word32 nameSz;
+            char value[32];
+            word32 valueSz;
+
+            nameSz = sizeof(name);
+            valueSz = sizeof(value);
+            ret = GetString(name, &nameSz, buf, len, &begin);
+            if (ret == WS_SUCCESS)
+                ret = GetString(value, &valueSz, buf, len, &begin);
+
+            WLOG(WS_LOG_DEBUG, "  %s = %s", name, value);
+        }
+    }
+
+    if (ret == WS_SUCCESS)
+        *idx = len;
+
+    if (wantReply) {
+        int replyRet;
+
+        replyRet = SendChannelSuccess(ssh, channelId, (ret == WS_SUCCESS));
+        if (replyRet != WS_SUCCESS)
+            ret = replyRet;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelRequest(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelSuccess(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelSuccess()");
+
+    if (ssh == NULL || buf == NULL || len == 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    ssh->serverState = SERVER_DONE;
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelSuccess(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelFailure(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelFailure()");
+
+    if (ssh == NULL || buf == NULL || len != 0 || idx == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = WS_CHANOPEN_FAILED;
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelFailure(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelWindowAdjust(WOLFSSH* ssh,
+                                 byte* buf, word32 len, word32* idx)
+{
+    WOLFSSH_CHANNEL* channel = NULL;
+    word32 begin = *idx;
+    word32 channelId, bytesToAdd;
+    int ret;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelWindowAdjust()");
+
+    ret = GetUint32(&channelId, buf, len, &begin);
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&bytesToAdd, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        *idx = begin;
+
+        channel = ChannelFind(ssh, channelId, FIND_SELF);
+        if (channel == NULL)
+            ret = WS_INVALID_CHANID;
+        else {
+            WLOG(WS_LOG_INFO, "  channelId = %u", channelId);
+            WLOG(WS_LOG_INFO, "  bytesToAdd = %u", bytesToAdd);
+            WLOG(WS_LOG_INFO, "  peerWindowSz = %u",
+                 channel->peerWindowSz);
+
+            channel->peerWindowSz += bytesToAdd;
+
+            WLOG(WS_LOG_INFO, "  update peerWindowSz = %u",
+                 channel->peerWindowSz);
+
+        }
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelWindowAdjust(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoChannelData(WOLFSSH* ssh,
+                         byte* buf, word32 len, word32* idx)
+{
+    WOLFSSH_CHANNEL* channel = NULL;
+    word32 begin = *idx;
+    word32 dataSz = 0;
+    word32 channelId;
+    int ret;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoChannelData()");
+
+    ret = GetUint32(&channelId, buf, len, &begin);
+    if (ret == WS_SUCCESS)
+        ret = GetUint32(&dataSz, buf, len, &begin);
+
+    if (ret == WS_SUCCESS) {
+        *idx = begin + dataSz;
+
+        channel = ChannelFind(ssh, channelId, FIND_SELF);
+        if (channel == NULL)
+            ret = WS_INVALID_CHANID;
+        else
+            ret = ChannelPutData(channel, buf + begin, dataSz);
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoChannelData(), ret = %d", ret);
+    return ret;
+}
+
+
+static int DoPacket(WOLFSSH* ssh)
+{
+    byte* buf = (byte*)ssh->inputBuffer.buffer;
+    word32 idx = ssh->inputBuffer.idx;
+    word32 len = ssh->inputBuffer.length;
+    word32 payloadSz;
+    byte padSz;
+    byte msg;
+    word32 payloadIdx = 0;
+    int ret;
+
+    //ESP_LOGI("WOLFSSH", "DoPacket sequence number: %d", ssh->peerSeq);
+    WLOG(WS_LOG_DEBUG, "DoPacket sequence number: %d", ssh->peerSeq);
+
+    idx += LENGTH_SZ;
+    padSz = buf[idx++];
+    payloadSz = ssh->curSz - PAD_LENGTH_SZ - padSz - MSG_ID_SZ;
+
+    msg = buf[idx++];
+    /* At this point, payload starts at "buf + idx". */
+
+    switch (msg) {
+
+        case MSGID_DISCONNECT:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_DISCONNECT");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_DISCONNECT");
+            ret = DoDisconnect(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_IGNORE:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_IGNORE");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_IGNORE");
+            ret = DoIgnore(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_UNIMPLEMENTED:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_UNIMPLEMENTED");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_UNIMPLEMENTED");
+            ret = DoUnimplemented(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_DEBUG:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_DEBUG");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_DEBUG");
+            ret = DoDebug(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_KEXINIT:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXINIT");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXINIT");
+            ret = DoKexInit(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_NEWKEYS:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_NEWKEYS");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_NEWKEYS");
+            ret = DoNewKeys(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_KEXDH_INIT:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_INIT");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_INIT");
+            ret = DoKexDhInit(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_KEXDH_REPLY:
+            if (ssh->handshake->kexId == ID_DH_GEX_SHA256) {
+            	//ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_GEX_GROUP");
+                WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_GROUP");
+                ret = DoKexDhGexGroup(ssh, buf + idx, payloadSz, &payloadIdx);
+            }
+            else {
+            	//ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_REPLY");
+                WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_REPLY");
+                ret = DoKexDhReply(ssh, buf + idx, payloadSz, &payloadIdx);
+            }
+            break;
+
+        case MSGID_KEXDH_GEX_REQUEST:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_GEX_REQUEST");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_REQUEST");
+            ret = DoKexDhGexRequest(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_KEXDH_GEX_INIT:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_GEX_INIT");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_INIT");
+            ret = DoKexDhInit(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_KEXDH_GEX_REPLY:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_KEXDH_GEX_INIT");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_KEXDH_GEX_INIT");
+            ret = DoKexDhReply(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_SERVICE_REQUEST:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_SERVICE_REQUEST");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_SERVICE_REQUEST");
+            ret = DoServiceRequest(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_SERVICE_ACCEPT:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_SERVER_ACCEPT");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_SERVER_ACCEPT");
+            ret = DoServiceAccept(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_USERAUTH_REQUEST:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_USERAUTH_REQUEST");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_REQUEST");
+            ret = DoUserAuthRequest(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_USERAUTH_FAILURE:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_USERAUTH_FAILURE");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_FAILURE");
+            ret = DoUserAuthFailure(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_USERAUTH_SUCCESS:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_USERAUTH_SUCCESS");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_SUCCESS");
+            ret = DoUserAuthSuccess(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_USERAUTH_BANNER:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_USERAUTH_BANNER");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_USERAUTH_BANNER");
+            ret = DoUserAuthBanner(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_GLOBAL_REQUEST:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_GLOBAL_REQUEST");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_GLOBAL_REQUEST");
+            ret = DoGlobalRequest(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_OPEN:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_OPEN");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN");
+            ret = DoChannelOpen(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_OPEN_CONF:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_OPEN_CONF");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN_CONF");
+            ret = DoChannelOpenConf(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_OPEN_FAIL:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_OPEN_FAIL");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_OPEN_FAIL");
+            ret = DoChannelOpenFail(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_WINDOW_ADJUST:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_WINDOW_ADJUST");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_WINDOW_ADJUST");
+            ret = DoChannelWindowAdjust(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_DATA:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_DATA");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_DATA");
+            ret = DoChannelData(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_EOF:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_EOF");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_EOF");
+            ret = WS_SUCCESS;
+            break;
+
+        case MSGID_CHANNEL_CLOSE:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_CLOSE");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_CLOSE");
+            ret = DoChannelClose(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_REQUEST:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_REQUEST");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_REQUEST");
+            ret = DoChannelRequest(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_SUCCESS:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_SUCCESS");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_SUCCESS");
+            ret = DoChannelSuccess(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        case MSGID_CHANNEL_FAILURE:
+        	//ESP_LOGI("WOLFSSH", "Decoding MSGID_CHANNEL_FAILURE");
+            WLOG(WS_LOG_DEBUG, "Decoding MSGID_CHANNEL_FAILURE");
+            ret = DoChannelFailure(ssh, buf + idx, payloadSz, &payloadIdx);
+            break;
+
+        default:
+        	//ESP_LOGI("WOLFSSH", "Unimplemented message ID (%d)", msg);
+            WLOG(WS_LOG_DEBUG, "Unimplemented message ID (%d)", msg);
+#ifdef SHOW_UNIMPLEMENTED
+            DumpOctetString(buf + idx, payloadSz);
+#endif
+            ret = SendUnimplemented(ssh);
+    }
+
+    if (ret == WS_SUCCESS) {
+        idx += payloadIdx;
+
+        if (idx + padSz > len) {
+            WLOG(WS_LOG_DEBUG, "Not enough data in buffer for pad.");
+            ret = WS_BUFFER_E;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        idx += padSz;
+        ssh->inputBuffer.idx = idx;
+        ssh->peerSeq++;
+    }
+
+    return ret;
+}
+
+
+static INLINE int Encrypt(WOLFSSH* ssh, byte* cipher, const byte* input,
+                          word16 sz)
+{
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL || cipher == NULL || input == NULL || sz == 0)
+        return WS_BAD_ARGUMENT;
+
+    WLOG(WS_LOG_DEBUG, "Encrypt %s", IdToName(ssh->encryptId));
+
+    switch (ssh->encryptId) {
+        case ID_NONE:
+            break;
+
+        case ID_AES128_CBC:
+            if (wc_AesCbcEncrypt(&ssh->encryptCipher.aes,
+                                 cipher, input, sz) < 0) {
+
+                ret = WS_ENCRYPT_E;
+            }
+            break;
+
+        default:
+            ret = WS_INVALID_ALGO_ID;
+    }
+
+    ssh->txCount += sz;
+
+    return ret;
+}
+
+
+static INLINE int Decrypt(WOLFSSH* ssh, byte* plain, const byte* input,
+                          word16 sz)
+{
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL || plain == NULL || input == NULL || sz == 0)
+        return WS_BAD_ARGUMENT;
+
+    WLOG(WS_LOG_DEBUG, "Decrypt %s", IdToName(ssh->peerEncryptId));
+
+    switch (ssh->peerEncryptId) {
+        case ID_NONE:
+            break;
+
+        case ID_AES128_CBC:
+            if (wc_AesCbcDecrypt(&ssh->decryptCipher.aes,
+                                 plain, input, sz) < 0) {
+
+                ret = WS_DECRYPT_E;
+            }
+            break;
+
+        default:
+            ret = WS_INVALID_ALGO_ID;
+    }
+
+    ssh->rxCount += sz;
+    HighwaterCheck(ssh, WOLFSSH_HWSIDE_RECEIVE);
+
+    return ret;
+}
+
+
+static INLINE int CreateMac(WOLFSSH* ssh, const byte* in, word32 inSz,
+                            byte* mac)
+{
+    byte flatSeq[LENGTH_SZ];
+    int ret;
+
+    c32toa(ssh->seq, flatSeq);
+
+    WLOG(WS_LOG_DEBUG, "CreateMac %s", IdToName(ssh->macId));
+
+    /* Need to MAC the sequence number and the unencrypted packet */
+    switch (ssh->macId) {
+        case ID_NONE:
+            ret = WS_SUCCESS;
+            break;
+
+        case ID_HMAC_SHA1_96:
+            {
+                Hmac hmac;
+                byte digest[SHA_DIGEST_SIZE];
+
+                ret = wc_HmacSetKey(&hmac, SHA,
+                                    ssh->keys.macKey, ssh->keys.macKeySz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacUpdate(&hmac, in, inSz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacFinal(&hmac, digest);
+                if (ret == WS_SUCCESS)
+                    WMEMCPY(mac, digest, SHA1_96_SZ);
+            }
+            break;
+
+        case ID_HMAC_SHA1:
+            {
+                Hmac hmac;
+
+                ret = wc_HmacSetKey(&hmac, SHA,
+                                    ssh->keys.macKey, ssh->keys.macKeySz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacUpdate(&hmac, in, inSz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacFinal(&hmac, mac);
+            }
+            break;
+
+        case ID_HMAC_SHA2_256:
+            {
+                Hmac hmac;
+
+                ret = wc_HmacSetKey(&hmac, SHA256,
+                                    ssh->keys.macKey,
+                                    ssh->keys.macKeySz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacUpdate(&hmac, in, inSz);
+                if (ret == WS_SUCCESS)
+                    ret = wc_HmacFinal(&hmac, mac);
+            }
+            break;
+
+        default:
+            WLOG(WS_LOG_DEBUG, "Invalid Mac ID");
+            ret = WS_FATAL_ERROR;
+    }
+
+    return ret;
+}
+
+
+static INLINE int VerifyMac(WOLFSSH* ssh, const byte* in, word32 inSz,
+                            const byte* mac)
+{
+    int ret;
+    byte flatSeq[LENGTH_SZ];
+    byte checkMac[MAX_HMAC_SZ];
+    Hmac hmac;
+
+    c32toa(ssh->peerSeq, flatSeq);
+
+    WLOG(WS_LOG_DEBUG, "VerifyMac %s", IdToName(ssh->peerMacId));
+    WLOG(WS_LOG_DEBUG, "VM: inSz = %u", inSz);
+    WLOG(WS_LOG_DEBUG, "VM: seq = %u", ssh->peerSeq);
+    WLOG(WS_LOG_DEBUG, "VM: keyLen = %u", ssh->peerKeys.macKeySz);
+
+    WMEMSET(checkMac, 0, sizeof(checkMac));
+
+    switch (ssh->peerMacId) {
+        case ID_NONE:
+            ret = WS_SUCCESS;
+            break;
+
+        case ID_HMAC_SHA1:
+        case ID_HMAC_SHA1_96:
+            ret = wc_HmacSetKey(&hmac, SHA,
+                                ssh->peerKeys.macKey, ssh->peerKeys.macKeySz);
+            if (ret == WS_SUCCESS)
+                ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
+            if (ret == WS_SUCCESS)
+                ret = wc_HmacUpdate(&hmac, in, inSz);
+            if (ret == WS_SUCCESS)
+                ret = wc_HmacFinal(&hmac, checkMac);
+            if (ConstantCompare(checkMac, mac, ssh->peerMacSz) != 0)
+                ret = WS_VERIFY_MAC_E;
+            break;
+
+        case ID_HMAC_SHA2_256:
+            ret = wc_HmacSetKey(&hmac, SHA256,
+                                ssh->peerKeys.macKey, ssh->peerKeys.macKeySz);
+            if (ret == WS_SUCCESS)
+                ret = wc_HmacUpdate(&hmac, flatSeq, sizeof(flatSeq));
+            if (ret == WS_SUCCESS)
+                ret = wc_HmacUpdate(&hmac, in, inSz);
+            if (ret == WS_SUCCESS)
+                ret = wc_HmacFinal(&hmac, checkMac);
+            if (ConstantCompare(checkMac, mac, ssh->peerMacSz) != 0)
+                ret = WS_VERIFY_MAC_E;
+            break;
+
+        default:
+            ret = WS_INVALID_ALGO_ID;
+    }
+
+    return ret;
+}
+
+
+static INLINE void AeadIncrementExpIv(byte* iv)
+{
+    int i;
+
+    iv += AEAD_IMP_IV_SZ;
+
+    for (i = AEAD_EXP_IV_SZ-1; i >= 0; i--) {
+        if (++iv[i]) return;
+    }
+}
+
+
+static INLINE int EncryptAead(WOLFSSH* ssh, byte* cipher,
+                              const byte* input, word16 sz,
+                              byte* authTag, const byte* auth,
+                              word16 authSz)
+{
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL || cipher == NULL || input == NULL || sz == 0 ||
+        authTag == NULL || auth == NULL || authSz == 0)
+        return WS_BAD_ARGUMENT;
+
+    WLOG(WS_LOG_DEBUG, "EncryptAead %s", IdToName(ssh->encryptId));
+
+    if (ssh->encryptId == ID_AES128_GCM) {
+        ret = wc_AesGcmEncrypt(&ssh->encryptCipher.aes, cipher, input, sz,
+                               ssh->keys.iv, ssh->keys.ivSz,
+                               authTag, ssh->macSz, auth, authSz);
+    }
+    else
+        ret = WS_INVALID_ALGO_ID;
+
+    AeadIncrementExpIv(ssh->keys.iv);
+    ssh->txCount += sz;
+
+    return ret;
+}
+
+
+static INLINE int DecryptAead(WOLFSSH* ssh, byte* plain,
+                              const byte* input, word16 sz,
+                              const byte* authTag, const byte* auth,
+                              word16 authSz)
+{
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL || plain == NULL || input == NULL || sz == 0 ||
+        authTag == NULL || auth == NULL || authSz == 0)
+        return WS_BAD_ARGUMENT;
+
+    WLOG(WS_LOG_DEBUG, "DecryptAead %s", IdToName(ssh->peerEncryptId));
+
+    if (ssh->peerEncryptId == ID_AES128_GCM) {
+        ret = wc_AesGcmDecrypt(&ssh->decryptCipher.aes, plain, input, sz,
+                               ssh->peerKeys.iv, ssh->peerKeys.ivSz,
+                               authTag, ssh->peerMacSz, auth, authSz);
+    }
+    else
+        ret = WS_INVALID_ALGO_ID;
+
+    AeadIncrementExpIv(ssh->peerKeys.iv);
+    ssh->rxCount += sz;
+    HighwaterCheck(ssh, WOLFSSH_HWSIDE_RECEIVE);
+
+    return ret;
+}
+
+
+int DoReceive(WOLFSSH* ssh)
+{
+    int ret = WS_FATAL_ERROR;
+    int verifyResult;
+    word32 readSz;
+    byte peerBlockSz = ssh->peerBlockSz;
+    byte peerMacSz = ssh->peerMacSz;
+    byte aeadMode = ssh->peerAeadMode;
+
+    for (;;) {
+        switch (ssh->processReplyState) {
+            case PROCESS_INIT:
+                readSz = peerBlockSz;
+                //ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_INT");
+                WLOG(WS_LOG_DEBUG, "PR1: size = %u", readSz);
+                //ESP_LOGI("WOLFSSH", "PR1: size = %u", readSz);
+                if ((ret = GetInputData(ssh, readSz)) < 0) {
+                    return ret;
+                }
+                ssh->processReplyState = PROCESS_PACKET_LENGTH;
+
+                if (!aeadMode) {
+                    /* Decrypt first block if encrypted */
+                    ret = Decrypt(ssh,
+                                  ssh->inputBuffer.buffer +
+                                     ssh->inputBuffer.idx,
+                                  ssh->inputBuffer.buffer +
+                                     ssh->inputBuffer.idx,
+                                  readSz);
+                    if (ret != WS_SUCCESS) {
+                    	//ESP_LOGI("WOLFSSH", "PR: First decrypt fail");
+                        WLOG(WS_LOG_DEBUG, "PR: First decrypt fail");
+                        return ret;
+                    }
+                }
+                //ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_INT DONE");
+                FALL_THROUGH;
+
+            case PROCESS_PACKET_LENGTH:
+            	//ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_PACKET_LENGTH");
+                /* Peek at the packet_length field. */
+                ato32(ssh->inputBuffer.buffer + ssh->inputBuffer.idx,
+                      &ssh->curSz);
+                ssh->processReplyState = PROCESS_PACKET_FINISH;
+                FALL_THROUGH;
+
+            case PROCESS_PACKET_FINISH:
+            	//ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_PACKET_FINISH");
+                readSz = ssh->curSz + LENGTH_SZ + peerMacSz;
+                WLOG(WS_LOG_DEBUG, "PR2: size = %u", readSz);
+                if (readSz > 0) {
+                    if ((ret = GetInputData(ssh, readSz)) < 0) {
+                        return ret;
+                    }
+
+                    if (!aeadMode) {
+                        if (ssh->curSz + LENGTH_SZ - peerBlockSz > 0) {
+                            ret = Decrypt(ssh,
+                                          ssh->inputBuffer.buffer +
+                                             ssh->inputBuffer.idx + peerBlockSz,
+                                          ssh->inputBuffer.buffer +
+                                             ssh->inputBuffer.idx + peerBlockSz,
+                                          ssh->curSz + LENGTH_SZ - peerBlockSz);
+                        }
+                        else {
+                            WLOG(WS_LOG_INFO,
+                                 "Not trying to decrypt short message.");
+                        }
+
+                        /* Verify the buffer is big enough for the data and mac.
+                         * Even if the decrypt step fails, verify the MAC anyway.
+                         * This keeps consistent timing. */
+                        verifyResult = VerifyMac(ssh,
+                                                 ssh->inputBuffer.buffer +
+                                                     ssh->inputBuffer.idx,
+                                                 ssh->curSz + LENGTH_SZ,
+                                                 ssh->inputBuffer.buffer +
+                                                     ssh->inputBuffer.idx +
+                                                     LENGTH_SZ + ssh->curSz);
+                        if (ret != WS_SUCCESS) {
+                            WLOG(WS_LOG_DEBUG, "PR: Decrypt fail");
+                            return ret;
+                        }
+                        if (verifyResult != WS_SUCCESS) {
+                            WLOG(WS_LOG_DEBUG, "PR: VerifyMac fail");
+                            return ret;
+                        }
+                    }
+                    else {
+                        ret = DecryptAead(ssh,
+                                          ssh->inputBuffer.buffer +
+                                             ssh->inputBuffer.idx +
+                                             LENGTH_SZ,
+                                          ssh->inputBuffer.buffer +
+                                             ssh->inputBuffer.idx +
+                                             LENGTH_SZ,
+                                          ssh->curSz,
+                                          ssh->inputBuffer.buffer +
+                                              ssh->inputBuffer.idx +
+                                              ssh->curSz + LENGTH_SZ,
+                                          ssh->inputBuffer.buffer +
+                                              ssh->inputBuffer.idx,
+                                          LENGTH_SZ);
+
+                        if (ret != WS_SUCCESS) {
+                            WLOG(WS_LOG_DEBUG, "PR: DecryptAead fail");
+                            return ret;
+                        }
+                    }
+                }
+                ssh->processReplyState = PROCESS_PACKET;
+                FALL_THROUGH;
+
+            case PROCESS_PACKET:
+            	//ESP_LOGI("WOLFSSH", "DoReceive: PROCESS_PACKET");
+                if ( (ret = DoPacket(ssh)) < 0) {
+                    return ret;
+                }
+                WLOG(WS_LOG_DEBUG, "PR3: peerMacSz = %u", peerMacSz);
+                ssh->inputBuffer.idx += peerMacSz;
+                break;
+
+            default:
+            	//ESP_LOGI("WOLFSSH", "DoReceive: Bad input state!!");
+                WLOG(WS_LOG_DEBUG, "Bad process input state, program error");
+                return WS_INPUT_CASE_E;
+        }
+        WLOG(WS_LOG_DEBUG, "PR4: Shrinking input buffer");
+    	//ESP_LOGI("WOLFSSH", "PR4: Shrinking input buffer");
+        ShrinkBuffer(&ssh->inputBuffer, 1);
+        ssh->processReplyState = PROCESS_INIT;
+
+        WLOG(WS_LOG_DEBUG, "PR5: txCount = %u, rxCount = %u",
+             ssh->txCount, ssh->rxCount);
+    	//ESP_LOGI("WOLFSSH", "PR5: txCount = %u, rxCount = %u", ssh->txCount, ssh->rxCount);
+
+        return WS_SUCCESS;
+    }
+}
+
+
+int DoProtoId(WOLFSSH* ssh)
+{
+    int ret;
+    word32 idSz;
+    byte* eol;
+
+    if ( (ret = GetInputText(ssh, &eol)) < 0) {
+        WLOG(WS_LOG_DEBUG, "get input text failed");
+        return ret;
+    }
+
+    if (eol == NULL) {
+        WLOG(WS_LOG_DEBUG, "invalid EOL");
+        return WS_VERSION_E;
+    }
+
+    if (WSTRNCASECMP((char*)ssh->inputBuffer.buffer,
+                     sshProtoIdStr, SSH_PROTO_SZ) == 0) {
+
+        if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER)
+            ssh->clientState = CLIENT_VERSION_DONE;
+        else
+            ssh->serverState = SERVER_VERSION_DONE;
+    }
+    else {
+        WLOG(WS_LOG_DEBUG, "SSH version mismatch");
+        return WS_VERSION_E;
+    }
+
+    *eol = 0;
+
+    idSz = (word32)WSTRLEN((char*)ssh->inputBuffer.buffer);
+
+    /* Store the proto ID for later use. It is used in keying and rekeying. */
+    ssh->peerProtoId = (byte*)WMALLOC(idSz + LENGTH_SZ,
+                                         ssh->ctx->heap, DYNTYPE_STRING);
+    if (ssh->peerProtoId == NULL)
+        ret = WS_MEMORY_E;
+    else {
+        c32toa(idSz, ssh->peerProtoId);
+        WMEMCPY(ssh->peerProtoId + LENGTH_SZ, ssh->inputBuffer.buffer, idSz);
+        ssh->peerProtoIdSz = idSz + LENGTH_SZ;
+    }
+
+    ssh->inputBuffer.idx += idSz + SSH_PROTO_EOL_SZ;
+    ShrinkBuffer(&ssh->inputBuffer, 0);
+
+    return ret;
+}
+
+
+int SendProtoId(WOLFSSH* ssh)
+{
+    int ret = WS_SUCCESS;
+    word32 sshProtoIdStrSz;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "%s", sshProtoIdStr);
+        sshProtoIdStrSz = (word32)WSTRLEN(sshProtoIdStr);
+        ret = GrowBuffer(&ssh->outputBuffer, sshProtoIdStrSz, 0);
+    }
+
+    if (ret == WS_SUCCESS) {
+        WMEMCPY(ssh->outputBuffer.buffer, sshProtoIdStr, sshProtoIdStrSz);
+        ssh->outputBuffer.length = sshProtoIdStrSz;
+        ret = SendBuffered(ssh);
+    }
+
+    return ret;
+}
+
+
+static int PreparePacket(WOLFSSH* ssh, word32 payloadSz)
+{
+    int ret = WS_SUCCESS;
+    byte* output;
+    word32 outputSz;
+    word32 packetSz;
+    word32 usedSz;
+    byte paddingSz;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        /* Minimum value for paddingSz is 4. */
+        paddingSz = ssh->blockSz -
+                    ((ssh->aeadMode ? 0 : LENGTH_SZ) +
+                     PAD_LENGTH_SZ + payloadSz) % ssh->blockSz;
+        if (paddingSz < MIN_PAD_LENGTH)
+            paddingSz += ssh->blockSz;
+        ssh->paddingSz = paddingSz;
+        packetSz = PAD_LENGTH_SZ + payloadSz + paddingSz;
+        outputSz = LENGTH_SZ + packetSz + ssh->macSz;
+        usedSz = ssh->outputBuffer.length - ssh->outputBuffer.idx;
+
+        ret = GrowBuffer(&ssh->outputBuffer, outputSz, usedSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ssh->packetStartIdx = ssh->outputBuffer.length;
+        output = ssh->outputBuffer.buffer + ssh->outputBuffer.length;
+
+        /* fill in the packetSz, paddingSz */
+        c32toa(packetSz, output);
+        output[LENGTH_SZ] = paddingSz;
+
+        ssh->outputBuffer.length += LENGTH_SZ + PAD_LENGTH_SZ;
+    }
+
+    return ret;
+}
+
+
+static int BundlePacket(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx;
+    byte paddingSz;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+        paddingSz = ssh->paddingSz;
+
+        /* Add the padding */
+        WLOG(WS_LOG_DEBUG, "BP: paddingSz = %u", paddingSz);
+        if (ssh->encryptId == ID_NONE)
+            WMEMSET(output + idx, 0, paddingSz);
+        else if (wc_RNG_GenerateBlock(ssh->rng, output + idx, paddingSz) < 0)
+            ret = WS_CRYPTO_FAILED;
+    }
+
+    if (!ssh->aeadMode) {
+        if (ret == WS_SUCCESS) {
+            idx += paddingSz;
+            ret = CreateMac(ssh, ssh->outputBuffer.buffer + ssh->packetStartIdx,
+                            ssh->outputBuffer.length -
+                                ssh->packetStartIdx + paddingSz,
+                            output + idx);
+        }
+        else {
+            WLOG(WS_LOG_DEBUG, "BP: failed to add padding");
+        }
+
+        if (ret == WS_SUCCESS) {
+            idx += ssh->macSz;
+            ret = Encrypt(ssh,
+                          ssh->outputBuffer.buffer + ssh->packetStartIdx,
+                          ssh->outputBuffer.buffer + ssh->packetStartIdx,
+                          ssh->outputBuffer.length -
+                              ssh->packetStartIdx + paddingSz);
+        }
+        else {
+            WLOG(WS_LOG_DEBUG, "BP: failed to generate mac");
+        }
+    }
+    else {
+        if (ret == WS_SUCCESS) {
+            idx += paddingSz;
+            ret = EncryptAead(ssh,
+                              ssh->outputBuffer.buffer +
+                                  ssh->packetStartIdx + LENGTH_SZ,
+                              ssh->outputBuffer.buffer +
+                                  ssh->packetStartIdx + LENGTH_SZ,
+                              ssh->outputBuffer.length -
+                                  ssh->packetStartIdx + paddingSz -
+                                  LENGTH_SZ,
+                              output + idx,
+                              ssh->outputBuffer.buffer +
+                                  ssh->packetStartIdx,
+                              LENGTH_SZ);
+            idx += ssh->macSz;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        ssh->seq++;
+        ssh->outputBuffer.length = idx;
+    }
+    else {
+        WLOG(WS_LOG_DEBUG, "BP: failed to encrypt buffer");
+    }
+
+    return ret;
+}
+
+
+static INLINE void CopyNameList(byte* buf, word32* idx,
+                                                const char* src, word32 srcSz)
+{
+    word32 begin = *idx;
+
+    c32toa(srcSz, buf + begin);
+    begin += LENGTH_SZ;
+    WMEMCPY(buf + begin, src, srcSz);
+    begin += srcSz;
+
+    *idx = begin;
+}
+
+
+static const char cannedEncAlgoNames[] = "aes128-gcm@openssh.com,aes128-cbc";
+static const char cannedMacAlgoNames[] = "hmac-sha2-256,hmac-sha1-96,"
+                                         "hmac-sha1";
+static const char cannedKeyAlgoRsaNames[] = "ssh-rsa";
+static const char cannedKeyAlgoEcc256Names[] = "ecdsa-sha2-nistp256";
+static const char cannedKeyAlgoEcc384Names[] = "ecdsa-sha2-nistp384";
+static const char cannedKeyAlgoEcc521Names[] = "ecdsa-sha2-nistp521";
+static const char cannedKexAlgoNames[] = "ecdh-sha2-nistp256,"
+                                         "diffie-hellman-group-exchange-sha256,"
+                                         "diffie-hellman-group14-sha1,"
+                                         "diffie-hellman-group1-sha1";
+static const char cannedNoneNames[] = "none";
+
+static const word32 cannedEncAlgoNamesSz = sizeof(cannedEncAlgoNames) - 1;
+static const word32 cannedMacAlgoNamesSz = sizeof(cannedMacAlgoNames) - 1;
+static const word32 cannedKeyAlgoRsaNamesSz = sizeof(cannedKeyAlgoRsaNames) - 1;
+static const word32 cannedKeyAlgoEcc256NamesSz =
+                                           sizeof(cannedKeyAlgoEcc256Names) - 1;
+static const word32 cannedKeyAlgoEcc384NamesSz =
+                                           sizeof(cannedKeyAlgoEcc384Names) - 1;
+static const word32 cannedKeyAlgoEcc521NamesSz =
+                                           sizeof(cannedKeyAlgoEcc521Names) - 1;
+static const word32 cannedKexAlgoNamesSz = sizeof(cannedKexAlgoNames) - 1;
+static const word32 cannedNoneNamesSz = sizeof(cannedNoneNames) - 1;
+
+
+int SendKexInit(WOLFSSH* ssh)
+{
+    byte* output;
+    byte* payload;
+    word32 idx = 0;
+    word32 payloadSz;
+    int ret = WS_SUCCESS;
+    const char* cannedKeyAlgoNames;
+    word32 cannedKeyAlgoNamesSz;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendKexInit()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    ssh->isKeying = 1;
+    if (ssh->handshake == NULL) {
+        ssh->handshake = HandshakeInfoNew(ssh->ctx->heap);
+        if (ssh->handshake == NULL) {
+            WLOG(WS_LOG_DEBUG, "Couldn't allocate handshake info");
+            ret = WS_MEMORY_E;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        switch (ssh->ctx->useEcc) {
+            case ECC_SECP256R1:
+                cannedKeyAlgoNames = cannedKeyAlgoEcc256Names;
+                cannedKeyAlgoNamesSz = cannedKeyAlgoEcc256NamesSz;
+                break;
+            case ECC_SECP384R1:
+                cannedKeyAlgoNames = cannedKeyAlgoEcc384Names;
+                cannedKeyAlgoNamesSz = cannedKeyAlgoEcc384NamesSz;
+                break;
+            case ECC_SECP521R1:
+                cannedKeyAlgoNames = cannedKeyAlgoEcc521Names;
+                cannedKeyAlgoNamesSz = cannedKeyAlgoEcc521NamesSz;
+                break;
+            default:
+                cannedKeyAlgoNames = cannedKeyAlgoRsaNames;
+                cannedKeyAlgoNamesSz = cannedKeyAlgoRsaNamesSz;
+        }
+        payloadSz = MSG_ID_SZ + COOKIE_SZ + (LENGTH_SZ * 11) + BOOLEAN_SZ +
+                   cannedKexAlgoNamesSz + cannedKeyAlgoNamesSz +
+                   (cannedEncAlgoNamesSz * 2) +
+                   (cannedMacAlgoNamesSz * 2) +
+                   (cannedNoneNamesSz * 2);
+        ret = PreparePacket(ssh, payloadSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+        payload = output + idx;
+
+        output[idx++] = MSGID_KEXINIT;
+
+        ret = wc_RNG_GenerateBlock(ssh->rng, output + idx, COOKIE_SZ);
+    }
+
+    if (ret == WS_SUCCESS) {
+        byte* buf;
+        word32 bufSz = payloadSz + LENGTH_SZ;
+
+        idx += COOKIE_SZ;
+
+        CopyNameList(output, &idx, cannedKexAlgoNames, cannedKexAlgoNamesSz);
+        CopyNameList(output, &idx, cannedKeyAlgoNames, cannedKeyAlgoNamesSz);
+        CopyNameList(output, &idx, cannedEncAlgoNames, cannedEncAlgoNamesSz);
+        CopyNameList(output, &idx, cannedEncAlgoNames, cannedEncAlgoNamesSz);
+        CopyNameList(output, &idx, cannedMacAlgoNames, cannedMacAlgoNamesSz);
+        CopyNameList(output, &idx, cannedMacAlgoNames, cannedMacAlgoNamesSz);
+        CopyNameList(output, &idx, cannedNoneNames, cannedNoneNamesSz);
+        CopyNameList(output, &idx, cannedNoneNames, cannedNoneNamesSz);
+        c32toa(0, output + idx); /* Languages - Client To Server (0) */
+        idx += LENGTH_SZ;
+        c32toa(0, output + idx); /* Languages - Server To Client (0) */
+        idx += LENGTH_SZ;
+        output[idx++] = 0;       /* First KEX packet follows (false) */
+        c32toa(0, output + idx); /* Reserved (0) */
+        idx += LENGTH_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        buf = (byte*)WMALLOC(bufSz, ssh->ctx->heap, DYNTYPE_STRING);
+        if (buf == NULL) {
+            WLOG(WS_LOG_DEBUG, "Cannot allocate storage for KEX Init msg");
+            ret = WS_MEMORY_E;
+        }
+        else {
+            c32toa(payloadSz, buf);
+            WMEMCPY(buf + LENGTH_SZ, payload, payloadSz);
+            ssh->handshake->kexInit = buf;
+            ssh->handshake->kexInitSz = bufSz;
+        }
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = BundlePacket(ssh);
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendKexInit(), ret = %d", ret);
+    return ret;
+}
+
+
+/* SendKexDhReply()
+ * It is also the funciton used for MSGID_KEXECDH_REPLY. The parameters
+ * are analogous between the two messages. Where MSGID_KEXDH_REPLY has
+ * server's public host key (K_S), f, and the signature of H;
+ * MSGID_KEXECDH_REPLY has K_S, the server'e ephemeral public key (Q_S),
+ * and the signature of H. This also applies to the GEX version of this.
+ * H is calculated the same for KEXDH and KEXECDH, and has some exceptions
+ * for GEXDH. */
+int SendKexDhReply(WOLFSSH* ssh)
+{
+/* This function and DoKexDhReply() are unwieldy and in need of refactoring. */
+    const byte* primeGroup = dhPrimeGroup14;
+    word32 primeGroupSz = dhPrimeGroup14Sz;
+    const byte* generator = dhGenerator;
+    word32 generatorSz = dhGeneratorSz;
+
+    byte useEcc = 0;
+    byte f[257];
+    word32 fSz = sizeof(f);
+    byte fPad = 0;
+    byte kPad = 0;
+
+    struct {
+        byte useRsa;
+        word32 sz;
+        const char *name;
+        word32 nameSz;
+        union {
+            struct {
+                RsaKey key;
+                byte e[257];
+                word32 eSz;
+                byte ePad;
+                byte n[257];
+                word32 nSz;
+                byte nPad;
+            } rsa;
+            struct {
+                ecc_key key;
+                word32 keyBlobSz;
+                const char *keyBlobName;
+                word32 keyBlobNameSz;
+                byte q[257];
+                word32 qSz;
+                byte qPad;
+                const char *primeName;
+                word32 primeNameSz;
+            } ecc;
+        } sk;
+    } sigKeyBlock;
+
+    byte sig[512];
+    word32 sigSz = sizeof(sig);
+    word32 sigBlockSz;
+
+    word32 payloadSz;
+    byte scratchLen[LENGTH_SZ];
+    word32 scratch = 0;
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    byte msgId = MSGID_KEXDH_REPLY;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendKexDhReply()");
+    //ESP_LOGI("WOLFSSH", "Entering SendKexDhReply()");
+
+    sigKeyBlock.useRsa = ssh->handshake->pubKeyId == ID_SSH_RSA;
+    sigKeyBlock.name = IdToName(ssh->handshake->pubKeyId);
+    sigKeyBlock.nameSz = (word32)strlen(sigKeyBlock.name);
+
+    switch (ssh->handshake->kexId) {
+        case ID_DH_GROUP1_SHA1:
+            //ESP_LOGI("WOLFSSH", "KexID: ID_DH_GROUP1_SHA1");
+            primeGroup = dhPrimeGroup1;
+            primeGroupSz = dhPrimeGroup1Sz;
+            break;
+
+        case ID_DH_GROUP14_SHA1:
+            //ESP_LOGI("WOLFSSH", "KexID: ID_DH_GROUP14_SHA1 (default)");
+            /* This is the default case. */
+            break;
+
+        case ID_DH_GEX_SHA256:
+            //ESP_LOGI("WOLFSSH", "KexID: ID_DH_GEX_SHA256");
+            msgId = MSGID_KEXDH_GEX_REPLY;
+            break;
+
+        case ID_ECDH_SHA2_NISTP256:
+        case ID_ECDH_SHA2_NISTP384:
+        case ID_ECDH_SHA2_NISTP521:
+            //ESP_LOGI("WOLFSSH", "KexID: ID_ECDH_SHA2_NISTP256/384/521");
+            useEcc = 1;
+            msgId = MSGID_KEXDH_REPLY;
+            break;
+
+        default:
+            //ESP_LOGI("WOLFSSH", "KexID: INVALID");
+            ret = WS_INVALID_ALGO_ID;
+    }
+
+    /* At this point, the exchange hash, H, includes items V_C, V_S, I_C,
+     * and I_S. Next add K_S, the server's public host key. K_S will
+     * either be RSA or ECDSA public key blob. */
+    if (ret == WS_SUCCESS) {
+        if (sigKeyBlock.useRsa) {
+            //ESP_LOGI("WOLFSSH", "KeyBlock: RSA");
+            /* Decode the user-configured RSA private key. */
+            sigKeyBlock.sk.rsa.eSz = sizeof(sigKeyBlock.sk.rsa.e);
+            sigKeyBlock.sk.rsa.nSz = sizeof(sigKeyBlock.sk.rsa.n);
+            //ESP_LOGI("WOLFSSH", "wc_InitRsaKey");
+            ret = wc_InitRsaKey(&sigKeyBlock.sk.rsa.key, ssh->ctx->heap);
+            //ESP_LOGI("WOLFSSH", "wc_InitRsaKey ret=%d", ret);
+            if (ret == 0)
+                ret = wc_RsaPrivateKeyDecode(ssh->ctx->privateKey, &scratch,
+                                             &sigKeyBlock.sk.rsa.key,
+                                             (int)ssh->ctx->privateKeySz);
+            //ESP_LOGI("WOLFSSH", "RSAPrivKey: %p (%d)", ssh->ctx->privateKey, (int)ssh->ctx->privateKeySz);
+            //ESP_LOGI("WOLFSSH", "wc_RsaPrivateKeyDecode ret=%d", ret);
+            /* Flatten the public key into mpint values for the hash. */
+            if (ret == 0)
+                ret = wc_RsaFlattenPublicKey(&sigKeyBlock.sk.rsa.key,
+                                             sigKeyBlock.sk.rsa.e,
+                                             &sigKeyBlock.sk.rsa.eSz,
+                                             sigKeyBlock.sk.rsa.n,
+                                             &sigKeyBlock.sk.rsa.nSz);
+            //ESP_LOGI("WOLFSSH", "wc_RsaFlattenPublicKey ret=%d", ret);
+            if (ret == 0) {
+                /* Add a pad byte if the mpint has the MSB set. */
+                sigKeyBlock.sk.rsa.ePad = (sigKeyBlock.sk.rsa.e[0] & 0x80) ?
+                                           1 : 0;
+                sigKeyBlock.sk.rsa.nPad = (sigKeyBlock.sk.rsa.n[0] & 0x80) ?
+                                           1 : 0;
+                sigKeyBlock.sz = (LENGTH_SZ * 3) + sigKeyBlock.nameSz +
+                                  sigKeyBlock.sk.rsa.eSz +
+                                  sigKeyBlock.sk.rsa.ePad +
+                                  sigKeyBlock.sk.rsa.nSz +
+                                  sigKeyBlock.sk.rsa.nPad;
+                c32toa(sigKeyBlock.sz, scratchLen);
+                /* Hash in the length of the public key block. */
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+                //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+            }
+            /* Hash in the length of the key type string. */
+            if (ret == 0) {
+                c32toa(sigKeyBlock.nameSz, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+                //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+            }
+            /* Hash in the key type string. */
+            if (ret == 0)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    (byte*)sigKeyBlock.name,
+                                    sigKeyBlock.nameSz);
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+            /* Hash in the length of the RSA public key E value. */
+            if (ret == 0) {
+                c32toa(sigKeyBlock.sk.rsa.eSz + sigKeyBlock.sk.rsa.ePad,
+                       scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+                //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+            }
+            /* Hash in the pad byte for the RSA public key E value. */
+            if (ret == 0) {
+                if (sigKeyBlock.sk.rsa.ePad) {
+                    scratchLen[0] = 0;
+                    ret = wc_HashUpdate(&ssh->handshake->hash,
+                                        ssh->handshake->hashId, scratchLen, 1);
+                    //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+                }
+            }
+            /* Hash in the RSA public key E value. */
+            if (ret == 0)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    sigKeyBlock.sk.rsa.e,
+                                    sigKeyBlock.sk.rsa.eSz);
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+            /* Hash in the length of the RSA public key N value. */
+            if (ret == 0) {
+                c32toa(sigKeyBlock.sk.rsa.nSz + sigKeyBlock.sk.rsa.nPad,
+                       scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+                //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+            }
+            /* Hash in the pad byte for the RSA public key N value. */
+            if (ret == 0) {
+                if (sigKeyBlock.sk.rsa.nPad) {
+                    scratchLen[0] = 0;
+                    ret = wc_HashUpdate(&ssh->handshake->hash,
+                                        ssh->handshake->hashId, scratchLen, 1);
+                    //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+                }
+            }
+            /* Hash in the RSA public key N value. */
+            if (ret == 0)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    sigKeyBlock.sk.rsa.n,
+                                    sigKeyBlock.sk.rsa.nSz);
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+        }
+        else {
+        	//ESP_LOGI("WOLFSSH", "KeyBlock: Non-RSA");
+            sigKeyBlock.sk.ecc.primeName =
+                                       PrimeNameForId(ssh->handshake->pubKeyId);
+            sigKeyBlock.sk.ecc.primeNameSz =
+                                 (word32)strlen(sigKeyBlock.sk.ecc.primeName);
+
+            /* Decode the user-configured ECDSA private key. */
+            sigKeyBlock.sk.ecc.qSz = sizeof(sigKeyBlock.sk.ecc.q);
+            ret = wc_ecc_init_ex(&sigKeyBlock.sk.ecc.key, ssh->ctx->heap,
+                                 INVALID_DEVID);
+            scratch = 0;
+            if (ret == 0)
+                ret = wc_EccPrivateKeyDecode(ssh->ctx->privateKey, &scratch,
+                                             &sigKeyBlock.sk.ecc.key,
+                                             ssh->ctx->privateKeySz);
+            /* Flatten the public key into x963 value for the exchange hash. */
+            if (ret == 0)
+                ret = wc_ecc_export_x963(&sigKeyBlock.sk.ecc.key,
+                                         sigKeyBlock.sk.ecc.q,
+                                         &sigKeyBlock.sk.ecc.qSz);
+            /* Hash in the length of the public key block. */
+            if (ret == 0) {
+                sigKeyBlock.sz = (LENGTH_SZ * 3) +
+                                 sigKeyBlock.nameSz +
+                                 sigKeyBlock.sk.ecc.primeNameSz +
+                                 sigKeyBlock.sk.ecc.qSz;
+                c32toa(sigKeyBlock.sz, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            /* Hash in the length of the key type string. */
+            if (ret == 0) {
+                c32toa(sigKeyBlock.nameSz, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            /* Hash in the key type string. */
+            if (ret == 0)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    (byte*)sigKeyBlock.name,
+                                    sigKeyBlock.nameSz);
+            /* Hash in the length of the name of the prime. */
+            if (ret == 0) {
+                c32toa(sigKeyBlock.sk.ecc.primeNameSz, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            /* Hash in the name of the prime. */
+            if (ret == 0)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                   ssh->handshake->hashId,
+                                   (const byte*)sigKeyBlock.sk.ecc.primeName,
+                                   sigKeyBlock.sk.ecc.primeNameSz);
+            /* Hash in the length of the public key. */
+            if (ret == 0) {
+                c32toa(sigKeyBlock.sk.ecc.qSz, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            /* Hash in the public key. */
+            if (ret == 0)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    sigKeyBlock.sk.ecc.q,
+                                    sigKeyBlock.sk.ecc.qSz);
+        }
+
+        /* If using DH-GEX include the GEX specific values. */
+        if (ssh->handshake->kexId == ID_DH_GEX_SHA256) {
+            byte primeGroupPad = 0, generatorPad = 0;
+
+            /* Hash in the client's requested minimum key size. */
+            if (ret == 0) {
+                c32toa(ssh->handshake->dhGexMinSz, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate(dhGexMinSz) ret=%d", ret);
+            /* Hash in the client's requested preferred key size. */
+            if (ret == 0) {
+                c32toa(ssh->handshake->dhGexPreferredSz, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate(dhGexPreferredSz) ret=%d", ret);
+            /* Hash in the client's requested maximum key size. */
+            if (ret == 0) {
+                c32toa(ssh->handshake->dhGexMaxSz, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate(dhGexMaxSz) ret=%d", ret);
+            /* Add a pad byte if the mpint has the MSB set. */
+            if (ret == 0) {
+                if (primeGroup[0] & 0x80)
+                    primeGroupPad = 1;
+
+                /* Hash in the length of the GEX prime group. */
+                c32toa(primeGroupSz + primeGroupPad, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+                //ESP_LOGI("WOLFSSH", "wc_HashUpdate(primeGroupSz + primeGroupPad) ret=%d", ret);
+            }
+            /* Hash in the pad byte for the GEX prime group. */
+            if (ret == 0) {
+                if (primeGroupPad) {
+                    scratchLen[0] = 0;
+                    ret = wc_HashUpdate(&ssh->handshake->hash,
+                                        ssh->handshake->hashId,
+                                        scratchLen, 1);
+                    //ESP_LOGI("WOLFSSH", "wc_HashUpdate(primeGroupPad) ret=%d", ret);
+                }
+            }
+            /* Hash in the GEX prime group. */
+            if (ret == 0)
+                ret  = wc_HashUpdate(&ssh->handshake->hash,
+                                     ssh->handshake->hashId,
+                                     primeGroup, primeGroupSz);
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate(hasn in gex prime grp) ret=%d", ret);
+            /* Add a pad byte if the mpint has the MSB set. */
+            if (ret == 0) {
+                if (generator[0] & 0x80)
+                    generatorPad = 1;
+
+                /* Hash in the length of the GEX generator. */
+                c32toa(generatorSz + generatorPad, scratchLen);
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    scratchLen, LENGTH_SZ);
+                //ESP_LOGI("WOLFSSH", "wc_HashUpdate(add pad byte) ret=%d", ret);
+            }
+            /* Hash in the pad byte for the GEX generator. */
+            if (ret == 0) {
+                if (generatorPad) {
+                    scratchLen[0] = 0;
+                    ret = wc_HashUpdate(&ssh->handshake->hash,
+                                        ssh->handshake->hashId,
+                                        scratchLen, 1);
+                    //ESP_LOGI("WOLFSSH", "wc_HashUpdate(hash in the pad byte for gex gen) ret=%d", ret);
+                }
+            }
+            /* Hash in the GEX generator. */
+            if (ret == 0)
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId,
+                                    generator, generatorSz);
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate(hash in the gex gen) ret=%d", ret);
+        }
+
+        /* Hash in the size of the client's DH e-value (ECDH Q-value). */
+        if (ret == 0) {
+            c32toa(ssh->handshake->eSz, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate(client DH e-val size) ret=%d", ret);
+        }
+        /* Hash in the client's DH e-value (ECDH Q-value). */
+        if (ret == 0)
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                ssh->handshake->e, ssh->handshake->eSz);
+        //ESP_LOGI("WOLFSSH", "wc_HashUpdate(client DH e-val) ret=%d", ret);
+
+        /* Make the server's DH f-value and the shared secret K. */
+        /* Or make the server's ECDH private value, and the shared secret K. */
+        if (ret == 0) {
+            if (!useEcc) {
+                DhKey privKey;
+                byte y[256];
+                word32 ySz = sizeof(y);
+
+                ret = wc_InitDhKey(&privKey);
+                //ESP_LOGI("WOLFSSH", "wc_InitDhKey ret=%d", ret);
+                if (ret == 0)
+                    ret = wc_DhSetKey(&privKey, primeGroup, primeGroupSz,
+                                      generator, generatorSz);
+                //ESP_LOGI("WOLFSSH", "wc_DhSetKey ret=%d", ret);
+                if (ret == 0)
+                    ret = wc_DhGenerateKeyPair(&privKey, ssh->rng,
+                                               y, &ySz, f, &fSz);
+                //ESP_LOGI("WOLFSSH", "wc_DhGenerateKeyPair ret=%d", ret);
+                if (ret == 0)
+                    ret = wc_DhAgree(&privKey, ssh->k, &ssh->kSz, y, ySz,
+                                     ssh->handshake->e, ssh->handshake->eSz);
+                //ESP_LOGI("WOLFSSH", "wc_DhAgree ret=%d", ret);
+                ForceZero(y, ySz);
+                wc_FreeDhKey(&privKey);
+            }
+            else {
+                ecc_key pubKey;
+                ecc_key privKey;
+                int primeId = wcPrimeForId(ssh->handshake->kexId);
+
+                if (primeId == ECC_CURVE_INVALID)
+                    ret = WS_INVALID_PRIME_CURVE;
+
+                if (ret == 0)
+                    ret = wc_ecc_init_ex(&pubKey, ssh->ctx->heap,
+                                         INVALID_DEVID);
+                if (ret == 0)
+                    ret = wc_ecc_init_ex(&privKey, ssh->ctx->heap,
+                                         INVALID_DEVID);
+
+                if (ret == 0)
+                    ret = wc_ecc_import_x963_ex(ssh->handshake->e,
+                                                ssh->handshake->eSz,
+                                                &pubKey, primeId);
+
+                if (ret == 0)
+                    ret = wc_ecc_make_key_ex(ssh->rng,
+                                         wc_ecc_get_curve_size_from_id(primeId),
+                                         &privKey, primeId);
+                if (ret == 0)
+                    ret = wc_ecc_export_x963(&privKey, f, &fSz);
+                if (ret == 0)
+                    ret = wc_ecc_shared_secret(&privKey, &pubKey,
+                                               ssh->k, &ssh->kSz);
+                wc_ecc_free(&privKey);
+                wc_ecc_free(&pubKey);
+            }
+        }
+
+        /* Hash in the server's DH f-value. */
+        if (ret == 0) {
+            fPad = (f[0] & 0x80) ? 1 : 0;
+            c32toa(fSz + fPad, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                               scratchLen, LENGTH_SZ);
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+        }
+        if (ret == 0) {
+            if (fPad) {
+                scratchLen[0] = 0;
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId, scratchLen, 1);
+                //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+            }
+        }
+        if (ret == 0)
+            ret = wc_HashUpdate(&ssh->handshake->hash,
+                                ssh->handshake->hashId, f, fSz);
+        //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+
+        /* Hash in the shared secret K. */
+        if (ret == 0) {
+            kPad = (ssh->k[0] & 0x80) ? 1 : 0;
+            c32toa(ssh->kSz + kPad, scratchLen);
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                scratchLen, LENGTH_SZ);
+            //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+        }
+        if (ret == 0) {
+            if (kPad) {
+                scratchLen[0] = 0;
+                ret = wc_HashUpdate(&ssh->handshake->hash,
+                                    ssh->handshake->hashId, scratchLen, 1);
+                //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+            }
+        }
+        if (ret == 0)
+            ret = wc_HashUpdate(&ssh->handshake->hash, ssh->handshake->hashId,
+                                ssh->k, ssh->kSz);
+        //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+
+        /* Save the exchange hash value H, and session ID. */
+        if (ret == 0)
+            ret = wc_HashFinal(&ssh->handshake->hash,
+                               ssh->handshake->hashId, ssh->h);
+        //ESP_LOGI("WOLFSSH", "wc_HashUpdate ret=%d", ret);
+        if (ret == 0) {
+            ssh->hSz = wc_HashGetDigestSize(ssh->handshake->hashId);
+            if (ssh->sessionIdSz == 0) {
+                WMEMCPY(ssh->sessionId, ssh->h, ssh->hSz);
+                ssh->sessionIdSz = ssh->hSz;
+            }
+        }
+
+        if (ret != WS_SUCCESS)
+            ret = WS_CRYPTO_FAILED;
+        //ESP_LOGI("WOLFSSH", "overall ret ret=%d", ret);
+    }
+
+    /* Sign h with the server's private key. */
+    if (ret == WS_SUCCESS) {
+        wc_HashAlg digestHash;
+        byte digest[WC_MAX_DIGEST_SIZE];
+        byte sigHashId;
+
+        sigHashId = HashForId(ssh->handshake->pubKeyId);
+
+        ret = wc_HashInit(&digestHash, sigHashId);
+        if (ret == 0)
+            ret = wc_HashUpdate(&digestHash, sigHashId, ssh->h, ssh->hSz);
+        if (ret == 0)
+            ret = wc_HashFinal(&digestHash, sigHashId, digest);
+        if (ret != 0)
+            ret = WS_CRYPTO_FAILED;
+
+        if (ret == WS_SUCCESS) {
+            if (sigKeyBlock.useRsa) {
+                byte encSig[MAX_ENCODED_SIG_SZ];
+                word32 encSigSz;
+
+                encSigSz = wc_EncodeSignature(encSig, digest,
+                                              wc_HashGetDigestSize(sigHashId),
+                                              wc_HashGetOID(sigHashId));
+                if (encSigSz <= 0) {
+                    WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad Encode Sig");
+                    ret = WS_CRYPTO_FAILED;
+                }
+                else {
+                    WLOG(WS_LOG_INFO, "Signing hash with RSA.");
+                    sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig, sizeof(sig),
+                                           &sigKeyBlock.sk.rsa.key, ssh->rng);
+                    if (sigSz <= 0) {
+                        WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign");
+                        ret = WS_RSA_E;
+                    }
+                }
+            }
+            else {
+                WLOG(WS_LOG_INFO, "Signing hash with ECDSA.");
+                sigSz = sizeof(sig);
+                ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId),
+                                       sig, &sigSz,
+                                       ssh->rng, &sigKeyBlock.sk.ecc.key);
+                if (ret != MP_OKAY) {
+                    WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ECDSA Sign");
+                    ret = WS_ECC_E;
+                }
+                else {
+                    byte r[257];
+                    word32 rSz = sizeof(r);
+                    byte rPad;
+                    byte s[257];
+                    word32 sSz = sizeof(s);
+                    byte sPad;
+
+                    ret = wc_ecc_sig_to_rs(sig, sigSz, r, &rSz, s, &sSz);
+                    if (ret == 0) {
+                        idx = 0;
+                        rPad = (r[0] & 0x80) ? 1 : 0;
+                        sPad = (s[0] & 0x80) ? 1 : 0;
+                        sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad;
+
+                        c32toa(rSz + rPad, sig + idx);
+                        idx += LENGTH_SZ;
+                        if (rPad)
+                            sig[idx++] = 0;
+                        WMEMCPY(sig + idx, r, rSz);
+                        idx += rSz;
+                        c32toa(sSz + sPad, sig + idx);
+                        idx += LENGTH_SZ;
+                        if (sPad)
+                            sig[idx++] = 0;
+                        WMEMCPY(sig + idx, s, sSz);
+                    }
+                }
+            }
+        }
+    }
+
+    //ESP_LOGI("WOLFSSH", "Here??? %d", ret);
+
+    if (sigKeyBlock.useRsa)
+        wc_FreeRsaKey(&sigKeyBlock.sk.rsa.key);
+    else
+        wc_ecc_free(&sigKeyBlock.sk.ecc.key);
+
+    sigBlockSz = (LENGTH_SZ * 2) + sigKeyBlock.nameSz + sigSz;
+
+    if (ret == WS_SUCCESS)
+        ret = GenerateKeys(ssh);
+
+    /* Get the buffer, copy the packet data, once f is laid into the buffer,
+     * add it to the hash and then add K. */
+    if (ret == WS_SUCCESS) {
+        payloadSz = MSG_ID_SZ + (LENGTH_SZ * 3) +
+                    sigKeyBlock.sz + fSz + fPad + sigBlockSz;
+        ret = PreparePacket(ssh, payloadSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = msgId;
+
+        /* Copy the rsaKeyBlock into the buffer. */
+        c32toa(sigKeyBlock.sz, output + idx);
+        idx += LENGTH_SZ;
+        c32toa(sigKeyBlock.nameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, sigKeyBlock.name, sigKeyBlock.nameSz);
+        idx += sigKeyBlock.nameSz;
+        if (sigKeyBlock.useRsa) {
+            c32toa(sigKeyBlock.sk.rsa.eSz + sigKeyBlock.sk.rsa.ePad,
+                   output + idx);
+            idx += LENGTH_SZ;
+            if (sigKeyBlock.sk.rsa.ePad) output[idx++] = 0;
+            WMEMCPY(output + idx, sigKeyBlock.sk.rsa.e, sigKeyBlock.sk.rsa.eSz);
+            idx += sigKeyBlock.sk.rsa.eSz;
+            c32toa(sigKeyBlock.sk.rsa.nSz + sigKeyBlock.sk.rsa.nPad,
+                   output + idx);
+            idx += LENGTH_SZ;
+            if (sigKeyBlock.sk.rsa.nPad) output[idx++] = 0;
+            WMEMCPY(output + idx, sigKeyBlock.sk.rsa.n, sigKeyBlock.sk.rsa.nSz);
+            idx += sigKeyBlock.sk.rsa.nSz;
+        }
+        else {
+            c32toa(sigKeyBlock.sk.ecc.primeNameSz, output + idx);
+            idx += LENGTH_SZ;
+            WMEMCPY(output + idx, sigKeyBlock.sk.ecc.primeName,
+                    sigKeyBlock.sk.ecc.primeNameSz);
+            idx += sigKeyBlock.sk.ecc.primeNameSz;
+            c32toa(sigKeyBlock.sk.ecc.qSz, output + idx);
+            idx += LENGTH_SZ;
+            WMEMCPY(output + idx, sigKeyBlock.sk.ecc.q,
+                    sigKeyBlock.sk.ecc.qSz);
+            idx += sigKeyBlock.sk.ecc.qSz;
+        }
+
+        /* Copy the server's public key. F for DE, or Q_S for ECDH. */
+        c32toa(fSz + fPad, output + idx);
+        idx += LENGTH_SZ;
+        if (fPad) output[idx++] = 0;
+        WMEMCPY(output + idx, f, fSz);
+        idx += fSz;
+
+        /* Copy the signature of the exchange hash. */
+        c32toa(sigBlockSz, output + idx);
+        idx += LENGTH_SZ;
+        c32toa(sigKeyBlock.nameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, sigKeyBlock.name, sigKeyBlock.nameSz);
+        idx += sigKeyBlock.nameSz;
+        c32toa(sigSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, sig, sigSz);
+        idx += sigSz;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendNewKeys(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendKexDhReply(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendNewKeys(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx = 0;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendNewKeys()");
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_NEWKEYS;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    if (ret == WS_SUCCESS) {
+        ssh->blockSz = ssh->handshake->blockSz;
+        ssh->encryptId = ssh->handshake->encryptId;
+        ssh->macSz = ssh->handshake->macSz;
+        ssh->macId = ssh->handshake->macId;
+        ssh->aeadMode = ssh->handshake->aeadMode;
+        WMEMCPY(&ssh->keys, &ssh->handshake->keys, sizeof(Keys));
+
+        switch (ssh->encryptId) {
+            case ID_NONE:
+                WLOG(WS_LOG_DEBUG, "SNK: using cipher none");
+                break;
+
+            case ID_AES128_CBC:
+                WLOG(WS_LOG_DEBUG, "SNK: using cipher aes128-cbc");
+                ret = wc_AesSetKey(&ssh->encryptCipher.aes,
+                                  ssh->keys.encKey, ssh->keys.encKeySz,
+                                  ssh->keys.iv, AES_ENCRYPTION);
+                break;
+
+            case ID_AES128_GCM:
+                WLOG(WS_LOG_DEBUG, "SNK: using cipher aes128-gcm");
+                ret = wc_AesGcmSetKey(&ssh->encryptCipher.aes,
+                                     ssh->keys.encKey, ssh->keys.encKeySz);
+                break;
+
+            default:
+                WLOG(WS_LOG_DEBUG, "SNK: using cipher invalid");
+                ret = WS_INVALID_ALGO_ID;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        ssh->txCount = 0;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendNewKeys(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendKexDhGexRequest(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx = 0;
+    word32 payloadSz;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendKexDhGexRequest()");
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        payloadSz = MSG_ID_SZ + (UINT32_SZ * 3);
+        ret = PreparePacket(ssh, payloadSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_KEXDH_GEX_REQUEST;
+
+        c32toa(ssh->handshake->dhGexMinSz, output + idx);
+        idx += UINT32_SZ;
+        c32toa(ssh->handshake->dhGexPreferredSz, output + idx);
+        idx += UINT32_SZ;
+        c32toa(ssh->handshake->dhGexMaxSz, output + idx);
+        idx += UINT32_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendKexDhGexRequest(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendKexDhGexGroup(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx = 0;
+    word32 payloadSz;
+    const byte* primeGroup = dhPrimeGroup14;
+    word32 primeGroupSz = dhPrimeGroup14Sz;
+    const byte* generator = dhGenerator;
+    word32 generatorSz = dhGeneratorSz;
+    byte primePad = 0;
+    byte generatorPad = 0;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendKexDhGexGroup()");
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (primeGroup[0] & 0x80)
+        primePad = 1;
+
+    if (generator[0] & 0x80)
+        generatorPad = 1;
+
+    if (ret == WS_SUCCESS) {
+        payloadSz = MSG_ID_SZ + (LENGTH_SZ * 2) +
+                    primeGroupSz + primePad +
+                    generatorSz + generatorPad;
+        ret = PreparePacket(ssh, payloadSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_KEXDH_GEX_GROUP;
+
+        c32toa(primeGroupSz + primePad, output + idx);
+        idx += LENGTH_SZ;
+
+        if (primePad) {
+            output[idx] = 0;
+            idx += 1;
+        }
+
+        WMEMCPY(output + idx, primeGroup, primeGroupSz);
+        idx += primeGroupSz;
+
+        c32toa(generatorSz + generatorPad, output + idx);
+        idx += LENGTH_SZ;
+
+        if (generatorPad) {
+            output[idx] = 0;
+            idx += 1;
+        }
+
+        WMEMCPY(output + idx, generator, generatorSz);
+        idx += generatorSz;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendKexDhGexGroup(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendKexDhInit(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx = 0;
+    word32 payloadSz;
+    const byte* primeGroup = dhPrimeGroup14;
+    word32 primeGroupSz = dhPrimeGroup14Sz;
+    const byte* generator = dhGenerator;
+    word32 generatorSz = dhGeneratorSz;
+    int ret = WS_SUCCESS;
+    byte msgId = MSGID_KEXDH_INIT;
+    byte e[256];
+    word32 eSz = sizeof(e);
+    byte  ePad = 0;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendKexDhInit()");
+
+    switch (ssh->handshake->kexId) {
+        case ID_DH_GROUP1_SHA1:
+            primeGroup = dhPrimeGroup1;
+            primeGroupSz = dhPrimeGroup1Sz;
+            break;
+
+        case ID_DH_GROUP14_SHA1:
+            /* This is the default case. */
+            break;
+
+        case ID_DH_GEX_SHA256:
+            primeGroup = ssh->handshake->primeGroup;
+            primeGroupSz = ssh->handshake->primeGroupSz;
+            generator = ssh->handshake->generator;
+            generatorSz = ssh->handshake->generatorSz;
+            msgId = MSGID_KEXDH_GEX_INIT;
+            break;
+
+        case ID_ECDH_SHA2_NISTP256:
+        case ID_ECDH_SHA2_NISTP384:
+        case ID_ECDH_SHA2_NISTP521:
+            ssh->handshake->useEcc = 1;
+            msgId = MSGID_KEXECDH_INIT;
+            break;
+
+        default:
+            WLOG(WS_LOG_DEBUG, "Invalid algo: %u", ssh->handshake->kexId);
+            ret = WS_INVALID_ALGO_ID;
+    }
+
+
+    if (ret == WS_SUCCESS) {
+        if (!ssh->handshake->useEcc) {
+            DhKey* privKey = &ssh->handshake->privKey.dh;
+
+            ret = wc_InitDhKey(privKey);
+            if (ret == 0)
+                ret = wc_DhSetKey(privKey, primeGroup, primeGroupSz,
+                                  generator, generatorSz);
+            if (ret == 0)
+                ret = wc_DhGenerateKeyPair(privKey, ssh->rng,
+                                           ssh->handshake->x,
+                                           &ssh->handshake->xSz,
+                                           e, &eSz);
+        }
+        else {
+            ecc_key* privKey = &ssh->handshake->privKey.ecc;
+            int primeId = wcPrimeForId(ssh->handshake->kexId);
+
+            if (primeId == ECC_CURVE_INVALID)
+                ret = WS_INVALID_PRIME_CURVE;
+
+            if (ret == 0)
+                ret = wc_ecc_init_ex(privKey, ssh->ctx->heap,
+                                     INVALID_DEVID);
+
+            if (ret == 0)
+                ret = wc_ecc_make_key_ex(ssh->rng,
+                                     wc_ecc_get_curve_size_from_id(primeId),
+                                     privKey, primeId);
+            if (ret == 0)
+                ret = wc_ecc_export_x963(privKey, e, &eSz);
+        }
+
+        if (ret == 0)
+            ret = WS_SUCCESS;
+    }
+
+    if (ret == WS_SUCCESS) {
+        if (e[0] & 0x80)  {
+            ePad = 1;
+            ssh->handshake->e[0] = 0;
+        }
+        WMEMCPY(ssh->handshake->e + ePad, e, eSz);
+        ssh->handshake->eSz = eSz + ePad;
+
+        payloadSz = MSG_ID_SZ + LENGTH_SZ + eSz + ePad;
+        ret = PreparePacket(ssh, payloadSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = msgId;
+
+        c32toa(eSz + ePad, output + idx);
+        idx += LENGTH_SZ;
+
+        if (ePad) {
+            output[idx] = 0;
+            idx += 1;
+        }
+
+        WMEMCPY(output + idx, e, eSz);
+        idx += eSz;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendKexDhInit(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendUnimplemented(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx = 0;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG,
+         "Entering SendUnimplemented(), peerSeq = %u", ssh->peerSeq);
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_UNIMPLEMENTED;
+        c32toa(ssh->peerSeq, output + idx);
+        idx += UINT32_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendUnimplemented(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendDisconnect(WOLFSSH* ssh, word32 reason)
+{
+    byte* output;
+    word32 idx = 0;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + (LENGTH_SZ * 2));
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_DISCONNECT;
+        c32toa(reason, output + idx);
+        idx += UINT32_SZ;
+        c32toa(0, output + idx);
+        idx += LENGTH_SZ;
+        c32toa(0, output + idx);
+        idx += LENGTH_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    return ret;
+}
+
+
+int SendIgnore(WOLFSSH* ssh, const unsigned char* data, word32 dataSz)
+{
+    byte* output;
+    word32 idx = 0;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL || (data == NULL && dataSz > 0))
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + dataSz);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_IGNORE;
+        c32toa(dataSz, output + idx);
+        idx += LENGTH_SZ;
+        if (dataSz > 0) {
+            WMEMCPY(output + idx, data, dataSz);
+            idx += dataSz;
+        }
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    return ret;
+}
+
+
+static const char cannedLangTag[] = "en-us";
+static const word32 cannedLangTagSz = sizeof(cannedLangTag) - 1;
+
+
+int SendDebug(WOLFSSH* ssh, byte alwaysDisplay, const char* msg)
+{
+    word32 msgSz;
+    byte* output;
+    word32 idx = 0;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        msgSz = (msg != NULL) ? (word32)WSTRLEN(msg) : 0;
+
+        ret = PreparePacket(ssh,
+                            MSG_ID_SZ + BOOLEAN_SZ + (LENGTH_SZ * 2) +
+                            msgSz + cannedLangTagSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_DEBUG;
+        output[idx++] = (alwaysDisplay != 0);
+        c32toa(msgSz, output + idx);
+        idx += LENGTH_SZ;
+        if (msgSz > 0) {
+            WMEMCPY(output + idx, msg, msgSz);
+            idx += msgSz;
+        }
+        c32toa(cannedLangTagSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, cannedLangTag, cannedLangTagSz);
+        idx += cannedLangTagSz;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    return ret;
+}
+
+
+int SendServiceRequest(WOLFSSH* ssh, byte serviceId)
+{
+    const char* serviceName;
+    word32 serviceNameSz;
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendServiceRequest()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        serviceName = IdToName(serviceId);
+        serviceNameSz = (word32)WSTRLEN(serviceName);
+
+        ret = PreparePacket(ssh,
+                            MSG_ID_SZ + LENGTH_SZ + serviceNameSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_SERVICE_REQUEST;
+        c32toa(serviceNameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, serviceName, serviceNameSz);
+        idx += serviceNameSz;
+
+        ssh->outputBuffer.length = idx;
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendServiceRequest(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendServiceAccept(WOLFSSH* ssh, byte serviceId)
+{
+    const char* serviceName;
+    word32 serviceNameSz;
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        serviceName = IdToName(serviceId);
+        serviceNameSz = (word32)WSTRLEN(serviceName);
+        ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + serviceNameSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_SERVICE_ACCEPT;
+        c32toa(serviceNameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, serviceName, serviceNameSz);
+        idx += serviceNameSz;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendUserAuthBanner(ssh);
+
+    return ret;
+}
+
+
+static const char cannedAuths[] = "publickey,password";
+static const word32 cannedAuthsSz = sizeof(cannedAuths) - 1;
+
+
+int SendUserAuthRequest(WOLFSSH* ssh, byte authId)
+{
+    byte* output;
+    word32 idx;
+    const char* authName;
+    word32 authNameSz;
+    const char* serviceName;
+    word32 serviceNameSz;
+    word32 payloadSz;
+    int ret = WS_SUCCESS;
+    WS_UserAuthData authData;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendUserAuthRequest()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        if (authId == ID_USERAUTH_PASSWORD && ssh->ctx->userAuthCb != NULL) {
+            WLOG(WS_LOG_DEBUG, "SUARPW: Calling the userauth callback");
+            ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD,
+                                       &authData, ssh->userAuthCtx);
+            if (ret != WOLFSSH_USERAUTH_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, "SUARPW: Couldn't get password");
+                ret = WS_FATAL_ERROR;
+            }
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        serviceName = IdToName(ID_SERVICE_CONNECTION);
+        serviceNameSz = (word32)WSTRLEN(serviceName);
+        authName = IdToName(authId);
+        authNameSz = (word32)WSTRLEN(authName);
+
+        payloadSz = MSG_ID_SZ + (LENGTH_SZ * 3) +
+                    ssh->userNameSz + serviceNameSz + authNameSz;
+
+        if (authId == ID_USERAUTH_PASSWORD) {
+            payloadSz += BOOLEAN_SZ + LENGTH_SZ +
+                         authData.sf.password.passwordSz;
+        }
+        else if (authId != ID_NONE)
+            ret = WS_INVALID_ALGO_ID;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, payloadSz);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_USERAUTH_REQUEST;
+        c32toa(ssh->userNameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, ssh->userName, ssh->userNameSz);
+        idx += ssh->userNameSz;
+
+        c32toa(serviceNameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, serviceName, serviceNameSz);
+        idx += serviceNameSz;
+
+        c32toa(authNameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, authName, authNameSz);
+        idx += authNameSz;
+
+        if (authId == ID_USERAUTH_PASSWORD) {
+            output[idx++] = 0; /* Boolean "FALSE" for password change */
+            c32toa(authData.sf.password.passwordSz, output + idx);
+            idx += LENGTH_SZ;
+            WMEMCPY(output + idx, authData.sf.password.password,
+                    authData.sf.password.passwordSz);
+            idx += authData.sf.password.passwordSz;
+        }
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendUserAuthRequest(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendUserAuthFailure(WOLFSSH* ssh, byte partialSuccess)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendUserAuthFailure()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh,
+                            MSG_ID_SZ + LENGTH_SZ +
+                            cannedAuthsSz + BOOLEAN_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_USERAUTH_FAILURE;
+        c32toa(cannedAuthsSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, cannedAuths, cannedAuthsSz);
+        idx += cannedAuthsSz;
+        output[idx++] = (partialSuccess != 0);
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    return ret;
+}
+
+
+int SendUserAuthSuccess(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_USERAUTH_SUCCESS;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    return ret;
+}
+
+
+int SendUserAuthPkOk(WOLFSSH* ssh,
+                     const byte* algoName, word32 algoNameSz,
+                     const byte* publicKey, word32 publicKeySz)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL ||
+        algoName == NULL || algoNameSz == 0 ||
+        publicKey == NULL || publicKeySz == 0) {
+
+        ret = WS_BAD_ARGUMENT;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + (LENGTH_SZ * 2) +
+                                 algoNameSz + publicKeySz);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_USERAUTH_PK_OK;
+        c32toa(algoNameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, algoName, algoNameSz);
+        idx += algoNameSz;
+        c32toa(publicKeySz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, publicKey, publicKeySz);
+        idx += publicKeySz;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    return ret;
+}
+
+
+int SendUserAuthBanner(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    const char* banner;
+    word32 bannerSz = 0;
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ssh->ctx->banner != NULL && ssh->ctx->bannerSz > 0) {
+        banner = ssh->ctx->banner;
+        bannerSz = ssh->ctx->bannerSz;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + (LENGTH_SZ * 2) +
+                            bannerSz + cannedLangTagSz);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_USERAUTH_BANNER;
+        c32toa(bannerSz, output + idx);
+        idx += LENGTH_SZ;
+        if (bannerSz > 0)
+            WMEMCPY(output + idx, banner, bannerSz);
+        idx += bannerSz;
+        c32toa(cannedLangTagSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, cannedLangTag, cannedLangTagSz);
+        idx += cannedLangTagSz;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    return ret;
+}
+
+
+int SendRequestSuccess(WOLFSSH* ssh, int success)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendRequestSuccess(), %s",
+         success ? "Success" : "Failure");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = success ?
+                        MSGID_REQUEST_SUCCESS : MSGID_REQUEST_FAILURE;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendRequestSuccess(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendChannelOpenSession(WOLFSSH* ssh,
+                           word32 initialWindowSz, word32 maxPacketSz)
+{
+    WOLFSSH_CHANNEL* newChannel;
+    byte* output;
+    const char* channelType;
+    word32 channelTypeSz, channelId, idx;
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendChannelOpenSession()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        channelId = ssh->nextChannel;
+        newChannel = ChannelNew(ssh, ID_CHANTYPE_SESSION,
+                                initialWindowSz, maxPacketSz);
+        if (newChannel == NULL)
+            ret = WS_MEMORY_E;
+
+        if (ret == WS_SUCCESS)
+            ret = ChannelAppend(ssh, newChannel);
+    }
+
+    if (ret == WS_SUCCESS) {
+        channelType = IdToName(ID_CHANTYPE_SESSION);
+        channelTypeSz = (word32)WSTRLEN(channelType);
+
+        ret = PreparePacket(ssh, MSG_ID_SZ + LENGTH_SZ + channelTypeSz +
+                                 (UINT32_SZ * 3));
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_CHANNEL_OPEN;
+        c32toa(channelTypeSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, channelType, channelTypeSz);
+        idx += channelTypeSz;
+        c32toa(channelId, output + idx);
+        idx += UINT32_SZ;
+        c32toa(initialWindowSz, output + idx);
+        idx += UINT32_SZ;
+        c32toa(maxPacketSz, output + idx);
+        idx += UINT32_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpenSession(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendChannelOpenConf(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    WOLFSSH_CHANNEL* channel;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendChannelOpenConf()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        channel = ChannelFind(ssh, ssh->defaultPeerChannelId, FIND_PEER);
+        if (channel == NULL)
+            ret = WS_INVALID_CHANID;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + (UINT32_SZ * 4));
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_CHANNEL_OPEN_CONF;
+        c32toa(channel->peerChannel, output + idx);
+        idx += UINT32_SZ;
+        c32toa(channel->channel, output + idx);
+        idx += UINT32_SZ;
+        c32toa(channel->windowSz, output + idx);
+        idx += UINT32_SZ;
+        c32toa(channel->maxPacketSz, output + idx);
+        idx += UINT32_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpenConf(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendChannelEof(WOLFSSH* ssh, word32 peerChannelId)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    WOLFSSH_CHANNEL* channel;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendChannelEof()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        channel = ChannelFind(ssh, peerChannelId, FIND_PEER);
+        if (channel == NULL)
+            ret = WS_INVALID_CHANID;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_CHANNEL_EOF;
+        c32toa(channel->peerChannel, output + idx);
+        idx += UINT32_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendChannelEof(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendChannelClose(WOLFSSH* ssh, word32 peerChannelId)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    WOLFSSH_CHANNEL* channel;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendChannelClose()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        channel = ChannelFind(ssh, peerChannelId, FIND_PEER);
+        if (channel == NULL)
+            ret = WS_INVALID_CHANID;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_CHANNEL_CLOSE;
+        c32toa(channel->peerChannel, output + idx);
+        idx += UINT32_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendChannelClose(), ret = %d", ret);
+    return ret;
+}
+
+word32 min(word32 a, word32 b){
+	return 0;
+	}
+
+int SendChannelData(WOLFSSH* ssh, word32 peerChannel,
+                    byte* data, word32 dataSz)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    WOLFSSH_CHANNEL* channel;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendChannelData()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        if (ssh->isKeying)
+            ret = WS_REKEYING;
+    }
+
+    if (ret == WS_SUCCESS) {
+        channel = ChannelFind(ssh, peerChannel, FIND_PEER);
+        if (channel == NULL) {
+            WLOG(WS_LOG_DEBUG, "Invalid peer channel");
+            ret = WS_INVALID_CHANID;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        word32 bound = min(channel->peerWindowSz, channel->peerMaxPacketSz);
+
+        if (dataSz > bound) {
+            WLOG(WS_LOG_DEBUG,
+                 "Trying to send %u, client will only accept %u, limiting",
+                 dataSz, bound);
+            dataSz = bound;
+        }
+
+        ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ + dataSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_CHANNEL_DATA;
+        c32toa(channel->peerChannel, output + idx);
+        idx += UINT32_SZ;
+        c32toa(dataSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, data, dataSz);
+        idx += dataSz;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_INFO, "  dataSz = %u", dataSz);
+        WLOG(WS_LOG_INFO, "  peerWindowSz = %u", channel->peerWindowSz);
+        channel->peerWindowSz -= dataSz;
+        WLOG(WS_LOG_INFO, "  update peerWindowSz = %u", channel->peerWindowSz);
+        ret = dataSz;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendChannelData(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendChannelWindowAdjust(WOLFSSH* ssh, word32 peerChannel,
+                            word32 bytesToAdd)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    WOLFSSH_CHANNEL* channel;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendChannelWindowAdjust()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    channel = ChannelFind(ssh, peerChannel, FIND_PEER);
+    if (channel == NULL) {
+        WLOG(WS_LOG_DEBUG, "Invalid peer channel");
+        ret = WS_INVALID_CHANID;
+    }
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + (UINT32_SZ * 2));
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_CHANNEL_WINDOW_ADJUST;
+        c32toa(channel->peerChannel, output + idx);
+        idx += UINT32_SZ;
+        c32toa(bytesToAdd, output + idx);
+        idx += UINT32_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendChannelWindowAdjust(), ret = %d", ret);
+    return ret;
+}
+
+
+static const char cannedShellName[] = "shell";
+static const word32 cannedShellNameSz = sizeof(cannedShellName) - 1;
+
+
+int SendChannelRequestShell(WOLFSSH* ssh)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    WOLFSSH_CHANNEL* channel;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendChannelRequestShell()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        channel = ChannelFind(ssh, ssh->defaultPeerChannelId, FIND_PEER);
+        if (channel == NULL)
+            ret = WS_INVALID_CHANID;
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ +
+                                 cannedShellNameSz + BOOLEAN_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = MSGID_CHANNEL_REQUEST;
+        c32toa(channel->peerChannel, output + idx);
+        idx += UINT32_SZ;
+        c32toa(cannedShellNameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, cannedShellName, cannedShellNameSz);
+        idx += cannedShellNameSz;
+        output[idx++] = 1;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendChannelRequestShell(), ret = %d", ret);
+    return ret;
+}
+
+
+int SendChannelSuccess(WOLFSSH* ssh, word32 channelId, int success)
+{
+    byte* output;
+    word32 idx;
+    int ret = WS_SUCCESS;
+    WOLFSSH_CHANNEL* channel;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendChannelSuccess(), %s",
+         success ? "Success" : "Failure");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS) {
+        channel = ChannelFind(ssh, channelId, FIND_SELF);
+        if (channel == NULL) {
+            WLOG(WS_LOG_DEBUG, "Invalid channel");
+            ret = WS_INVALID_CHANID;
+        }
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ);
+
+    if (ret == WS_SUCCESS) {
+        output = ssh->outputBuffer.buffer;
+        idx = ssh->outputBuffer.length;
+
+        output[idx++] = success ?
+                        MSGID_CHANNEL_SUCCESS : MSGID_CHANNEL_FAILURE;
+        c32toa(channel->peerChannel, output + idx);
+        idx += UINT32_SZ;
+
+        ssh->outputBuffer.length = idx;
+
+        ret = BundlePacket(ssh);
+    }
+
+    if (ret == WS_SUCCESS)
+        ret = SendBuffered(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving SendChannelSuccess(), ret = %d", ret);
+    return ret;
+}
+
+
+#ifdef DEBUG_WOLFSSH
+
+#define LINE_WIDTH 16
+void DumpOctetString(const byte* input, word32 inputSz)
+{
+    int rows = inputSz / LINE_WIDTH;
+    int remainder = inputSz % LINE_WIDTH;
+    int i,j;
+
+    for (i = 0; i < rows; i++) {
+        printf("%04X: ", i * LINE_WIDTH);
+        for (j = 0; j < LINE_WIDTH; j++) {
+            printf("%02X ", input[i * LINE_WIDTH + j]);
+        }
+        printf("\n");
+    }
+    if (remainder) {
+        printf("%04X: ", i * LINE_WIDTH);
+        for (j = 0; j < remainder; j++) {
+            printf("%02X ", input[i * LINE_WIDTH + j]);
+        }
+        printf("\n");
+    }
+}
+
+#endif
diff -r 000000000000 -r c4152c628df5 src/io.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/io.c	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,374 @@
+/* io.c 
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The io module provides the default send and receive callbacks used
+ * by the library to handle network I/O. By default they handle UNIX
+ * style I/O.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssh/ssh.h>
+#include <wolfssh/internal.h>
+#include <wolfssh/log.h>
+
+#ifndef NULL
+    #include <stddef.h>
+#endif
+
+
+/* allow I/O callback handlers whether user I/O or not */
+
+/* install I/O recv callback */
+void wolfSSH_SetIORecv(WOLFSSH_CTX* ctx, WS_CallbackIORecv cb)
+{
+    if (ctx)
+        ctx->ioRecvCb = cb;
+}
+
+
+/* install I/O send callback */
+void wolfSSH_SetIOSend(WOLFSSH_CTX* ctx, WS_CallbackIOSend cb)
+{
+    if (ctx)
+        ctx->ioSendCb = cb;
+}
+
+
+/* install I/O read context */
+void wolfSSH_SetIOReadCtx(WOLFSSH* ssh, void *ctx)
+{
+    if (ssh)
+        ssh->ioReadCtx = ctx;
+}
+
+
+/* install I/O read context */
+void wolfSSH_SetIOWriteCtx(WOLFSSH* ssh, void *ctx)
+{
+    if (ssh)
+        ssh->ioWriteCtx = ctx;
+}
+
+
+/* get I/O read context */
+void* wolfSSH_GetIOReadCtx(WOLFSSH* ssh)
+{
+    if (ssh)
+        return ssh->ioReadCtx;
+
+    return NULL;
+}
+
+
+/* get I/O write context */
+void* wolfSSH_GetIOWriteCtx(WOLFSSH* ssh)
+{
+    if (ssh)
+        return ssh->ioWriteCtx;
+
+    return NULL;
+}
+
+
+#ifndef WOLFSSH_USER_IO
+
+/* default I/O callbacks, use BSD style sockets */
+
+
+#ifndef USE_WINDOWS_API
+    #ifdef WOLFSSH_LWIP
+        /* lwIP needs to be configured to use sockets API in this mode */
+        /* LWIP_SOCKET 1 in lwip/opt.h or in build */
+        #include "lwip/sockets.h"
+        #include <errno.h>
+        #ifndef LWIP_PROVIDE_ERRNO
+            #define LWIP_PROVIDE_ERRNO 1
+        #endif
+    #elif defined(FREESCALE_MQX)
+        #include <posix.h>
+        #include <rtcs.h>
+    #elif defined(WOLFSSH_MDK_ARM)
+        #if defined(WOLFSSH_MDK5)
+            #include "cmsis_os.h"
+            #include "rl_fs.h" 
+            #include "rl_net.h" 
+        #else
+            #include <rtl.h>
+        #endif
+        #undef RNG
+        #include "WOLFSSH_MDK_ARM.h"
+        #undef RNG
+        #define RNG CyaSSL_RNG 
+        /* for avoiding name conflict in "stm32f2xx.h" */
+        static int errno;
+    #else
+        //#include <sys/types.h>
+        #include <errno.h>
+        #ifndef EBSNET
+            //#include <unistd.h>
+        #endif
+        //#include <fcntl.h>
+        #if !(defined(DEVKITPRO) || defined(HAVE_RTP_SYS) || defined(EBSNET))
+            #include <sys/socket.h>
+            #include <arpa/inet.h>
+            //#include <netinet/in.h>
+            #include <netdb.h>
+            #ifdef __PPU
+                #include <netex/errno.h>
+            #else
+                //#include <sys/ioctl.h>
+            #endif
+        #endif
+        #ifdef HAVE_RTP_SYS
+            #include <socket.h>
+        #endif
+        #ifdef EBSNET
+            #include "rtipapi.h"  /* errno */
+            #include "socket.h"
+        #endif
+    #endif
+#endif /* USE_WINDOWS_API */
+
+#ifdef __sun
+    #include <sys/filio.h>
+#endif
+
+#ifdef USE_WINDOWS_API 
+    /* no epipe yet */
+    #ifndef WSAEPIPE
+        #define WSAEPIPE       -12345
+    #endif
+    #define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK
+    #define SOCKET_EAGAIN      WSAETIMEDOUT
+    #define SOCKET_ECONNRESET  WSAECONNRESET
+    #define SOCKET_EINTR       WSAEINTR
+    #define SOCKET_EPIPE       WSAEPIPE
+    #define SOCKET_ECONNREFUSED WSAENOTCONN
+    #define SOCKET_ECONNABORTED WSAECONNABORTED
+    #define close(s) closesocket(s)
+#elif defined(__PPU)
+    #define SOCKET_EWOULDBLOCK SYS_NET_EWOULDBLOCK
+    #define SOCKET_EAGAIN      SYS_NET_EAGAIN
+    #define SOCKET_ECONNRESET  SYS_NET_ECONNRESET
+    #define SOCKET_EINTR       SYS_NET_EINTR
+    #define SOCKET_EPIPE       SYS_NET_EPIPE
+    #define SOCKET_ECONNREFUSED SYS_NET_ECONNREFUSED
+    #define SOCKET_ECONNABORTED SYS_NET_ECONNABORTED
+#elif defined(FREESCALE_MQX)
+    /* RTCS doesn't have an EWOULDBLOCK error */
+    #define SOCKET_EWOULDBLOCK EAGAIN
+    #define SOCKET_EAGAIN      EAGAIN
+    #define SOCKET_ECONNRESET  RTCSERR_TCP_CONN_RESET
+    #define SOCKET_EINTR       EINTR
+    #define SOCKET_EPIPE       EPIPE
+    #define SOCKET_ECONNREFUSED RTCSERR_TCP_CONN_REFUSED
+    #define SOCKET_ECONNABORTED RTCSERR_TCP_CONN_ABORTED
+#elif defined(WOLFSSH_MDK_ARM)
+    #if defined(WOLFSSH_MDK5)
+        #define SOCKET_EWOULDBLOCK BSD_ERROR_WOULDBLOCK
+        #define SOCKET_EAGAIN      BSD_ERROR_LOCKED
+        #define SOCKET_ECONNRESET  BSD_ERROR_CLOSED
+        #define SOCKET_EINTR       BSD_ERROR
+        #define SOCKET_EPIPE       BSD_ERROR
+        #define SOCKET_ECONNREFUSED BSD_ERROR
+        #define SOCKET_ECONNABORTED BSD_ERROR
+    #else
+        #define SOCKET_EWOULDBLOCK SCK_EWOULDBLOCK
+        #define SOCKET_EAGAIN      SCK_ELOCKED
+        #define SOCKET_ECONNRESET  SCK_ECLOSED
+        #define SOCKET_EINTR       SCK_ERROR
+        #define SOCKET_EPIPE       SCK_ERROR
+        #define SOCKET_ECONNREFUSED SCK_ERROR
+        #define SOCKET_ECONNABORTED SCK_ERROR
+    #endif
+#else
+    #define SOCKET_EWOULDBLOCK EWOULDBLOCK
+    #define SOCKET_EAGAIN      EAGAIN
+    #define SOCKET_ECONNRESET  ECONNRESET
+    #define SOCKET_EINTR       EINTR
+    #define SOCKET_EPIPE       EPIPE
+    #define SOCKET_ECONNREFUSED ECONNREFUSED
+    #define SOCKET_ECONNABORTED ECONNABORTED
+#endif /* USE_WINDOWS_API */
+
+int recv(int a, void* b, int c, unsigned int d){
+    return 0;
+    }
+
+int send(int a, const void* b, int c, unsigned int d){
+    return 0;
+    }
+        
+#ifdef DEVKITPRO
+    /* from network.h */
+    int net_send(int, const void*, int, unsigned int);
+    int net_recv(int, void*, int, unsigned int);
+    #define SEND_FUNCTION net_send
+    #define RECV_FUNCTION net_recv
+#elif defined(WOLFSSH_LWIP)
+    #define SEND_FUNCTION lwip_send
+    #define RECV_FUNCTION lwip_recv
+#else
+    #define SEND_FUNCTION send
+    #define RECV_FUNCTION recv
+#endif
+
+
+/* Translates return codes returned from 
+ * send() and recv() if need be. 
+ */
+static INLINE int TranslateReturnCode(int old, int sd)
+{
+    (void)sd;
+
+#ifdef FREESCALE_MQX
+    if (old == 0) {
+        errno = SOCKET_EWOULDBLOCK;
+        return -1;  /* convert to BSD style wouldblock as error */
+    }
+
+    if (old < 0) {
+        errno = RTCS_geterror(sd);
+        if (errno == RTCSERR_TCP_CONN_CLOSING)
+            return 0;   /* convert to BSD style closing */
+    }
+#endif
+
+    return old;
+}
+
+static INLINE int LastError(void)
+{
+#ifdef USE_WINDOWS_API 
+    return WSAGetLastError();
+#elif defined(EBSNET)
+    return xn_getlasterror();
+#else
+    return errno;
+#endif
+}
+
+
+/* The receive embedded callback
+ *  return : nb bytes read, or error
+ */
+int wsEmbedRecv(WOLFSSH* ssh, void* data, word32 sz, void* ctx)
+{
+    int recvd;
+    int err;
+    int sd = *(int*)ctx;
+    char* buf = (char*)data;
+
+    recvd = (int)RECV_FUNCTION(sd, buf, sz, ssh->rflags);
+
+    recvd = TranslateReturnCode(recvd, sd);
+
+    if (recvd < 0) {
+        err = LastError();
+        WLOG(WS_LOG_DEBUG,"Embed Receive error");
+
+        /*if (err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN) {
+            WLOG(WS_LOG_DEBUG,"    Would block");
+            return WS_CBIO_ERR_WANT_READ;
+        }
+        else if (err == SOCKET_ECONNRESET) {
+            WLOG(WS_LOG_DEBUG,"    Connection reset");
+            return WS_CBIO_ERR_CONN_RST;
+        }
+        else if (err == SOCKET_EINTR) {
+            WLOG(WS_LOG_DEBUG,"    Socket interrupted");
+            return WS_CBIO_ERR_ISR;
+        }
+        else if (err == SOCKET_ECONNREFUSED) {
+            WLOG(WS_LOG_DEBUG,"    Connection refused");
+            return WS_CBIO_ERR_WANT_READ;
+        }
+        else if (err == SOCKET_ECONNABORTED) {
+            WLOG(WS_LOG_DEBUG,"    Connection aborted");
+            return WS_CBIO_ERR_CONN_CLOSE;
+        }
+        else*/ {
+            WLOG(WS_LOG_DEBUG,"    General error");
+            return WS_CBIO_ERR_GENERAL;
+        }
+    }
+    else if (recvd == 0) {
+        WLOG(WS_LOG_DEBUG,"Embed receive connection closed");
+        return WS_CBIO_ERR_CONN_CLOSE;
+    }
+
+    return recvd;
+}
+
+
+/* The send embedded callback
+ *  return : nb bytes sent, or error
+ */
+int wsEmbedSend(WOLFSSH* ssh, void* data, word32 sz, void* ctx)
+{
+    int sd = *(int*)ctx;
+    int sent;
+    int err;
+    char* buf = (char*)data;
+
+    WLOG(WS_LOG_DEBUG,"Embed Send trying to send %d", sz);
+
+    sent = (int)SEND_FUNCTION(sd, buf, sz, ssh->wflags);
+
+    WLOG(WS_LOG_DEBUG,"Embed Send sent %d", sent);
+
+    if (sent < 0) {
+        err = LastError();
+        WLOG(WS_LOG_DEBUG,"Embed Send error");
+
+        /*if (err == SOCKET_EWOULDBLOCK || err == SOCKET_EAGAIN) {
+            WLOG(WS_LOG_DEBUG,"    Would Block");
+            return WS_CBIO_ERR_WANT_WRITE;
+        }
+        else if (err == SOCKET_ECONNRESET) {
+            WLOG(WS_LOG_DEBUG,"    Connection reset");
+            return WS_CBIO_ERR_CONN_RST;
+        }
+        else if (err == SOCKET_EINTR) {
+            WLOG(WS_LOG_DEBUG,"    Socket interrupted");
+            return WS_CBIO_ERR_ISR;
+        }
+        else if (err == SOCKET_EPIPE) {
+            WLOG(WS_LOG_DEBUG,"    Socket EPIPE");
+            return WS_CBIO_ERR_CONN_CLOSE;
+        }
+        else */{
+            WLOG(WS_LOG_DEBUG,"    General error");
+            return WS_CBIO_ERR_GENERAL;
+        }
+    }
+ 
+    return sent;
+}
+
+
+#endif /* WOLFSSH_USER_IO */
+
diff -r 000000000000 -r c4152c628df5 src/keygen.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/keygen.c	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,102 @@
+/* keygen.c
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The keygen module contains utility functions wrapping the wolfCrypt
+ * key generation functions to product SSH friendly keys.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssl/options.h>
+#include <wolfcrypt/random.h>
+#include <wolfcrypt/rsa.h>
+#include <wolfssh/error.h>
+#include <wolfssh/keygen.h>
+#include <wolfssh/log.h>
+
+#ifdef WOLFSSH_KEYGEN
+
+#ifdef NO_INLINE
+    #include <wolfssh/misc.h>
+#else
+    #define WOLFSSH_MISC_INCLUDED
+    #include "src/misc.c"
+#endif
+
+
+int wolfSSH_MakeRsaKey(byte* out, word32 outSz,
+                       word32 size, word32 e)
+{
+    int ret = WS_SUCCESS;
+    WC_RNG rng;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_MakeRsaKey()");
+
+    if (wc_InitRng(&rng) != 0) {
+        WLOG(WS_LOG_DEBUG, "Couldn't create RNG");
+        ret = WS_CRYPTO_FAILED;
+    }
+
+    if (ret == WS_SUCCESS) {
+        RsaKey key;
+
+        if (wc_InitRsaKey(&key, NULL) != 0)
+            ret = WS_CRYPTO_FAILED;
+
+        if (ret == WS_SUCCESS) {
+            if (wc_MakeRsaKey(&key, size, e, &rng) != 0) {
+                WLOG(WS_LOG_DEBUG, "RSA key generation failed");
+                ret = WS_CRYPTO_FAILED;
+            }
+        }
+
+        if (ret == WS_SUCCESS) {
+            int keySz;
+
+            keySz = wc_RsaKeyToDer(&key, out, outSz);
+            if (keySz < 0) {
+                WLOG(WS_LOG_DEBUG, "RSA key to DER failed");
+                ret = WS_CRYPTO_FAILED;
+            }
+            else
+                ret = keySz;
+        }
+
+        if (wc_FreeRsaKey(&key) != 0) {
+            WLOG(WS_LOG_DEBUG, "RSA key free failed");
+            ret = WS_CRYPTO_FAILED;
+        }
+
+        if (wc_FreeRng(&rng) != 0) {
+            WLOG(WS_LOG_DEBUG, "Couldn't free RNG");
+            ret = WS_CRYPTO_FAILED;
+        }
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_MakeRsaKey(), ret = %d", ret);
+    return ret;
+}
+
+#endif
diff -r 000000000000 -r c4152c628df5 src/log.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/log.c	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,162 @@
+/* log.c
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The log module contains the interface to the logging function. When
+ * debugging is enabled and turned on, the logger will output to STDOUT.
+ * A custom logging callback may be installed.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssh/ssh.h>
+#include <wolfssh/log.h>
+#include <wolfssh/error.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#ifndef NO_TIMESTAMP
+    //#include <time.h>
+#endif
+
+//#include "esp_log.h"
+
+
+#ifndef WOLFSSH_DEFAULT_LOG_WIDTH
+    #define WOLFSSH_DEFAULT_LOG_WIDTH 120
+#endif
+
+
+static const char *TAG = "WOLFSSH";
+ 
+
+static void DefaultLoggingCb(enum wolfSSH_LogLevel, const char *const);
+
+static wolfSSH_LoggingCb logFunction = DefaultLoggingCb;
+static enum wolfSSH_LogLevel logLevel = WS_LOG_USER; // esp32: max
+#ifdef DEBUG_WOLFSSH
+    static int logEnable = 0;
+#endif
+
+
+/* turn debugging on if supported */
+void wolfSSH_Debugging_ON(void)
+{
+#ifdef DEBUG_WOLFSSH
+    logEnable = 1;
+#endif
+}
+
+
+/* turn debugging off */
+void wolfSSH_Debugging_OFF(void)
+{
+#ifdef DEBUG_WOLFSSH
+    logEnable = 0;
+#endif
+}
+
+
+/* set logging callback function */
+void wolfSSH_SetLoggingCb(wolfSSH_LoggingCb logF)
+{
+    if (logF)
+        logFunction = logF;
+}
+
+
+int wolfSSH_LogEnabled(void)
+{
+#ifdef DEBUG_WOLFSSH
+    return logEnable;
+#else
+    return 0;
+#endif
+}
+
+
+/* log level string */
+static const char* GetLogStr(enum wolfSSH_LogLevel level)
+{
+    switch (level) {
+        case WS_LOG_INFO:
+            return "INFO";
+
+        case WS_LOG_WARN:
+            return "WARNING";
+
+        case WS_LOG_ERROR:
+            return "ERROR";
+
+        case WS_LOG_DEBUG:
+            return "DEBUG";
+
+        case WS_LOG_USER:
+            return "USER";
+
+        default:
+            return "UNKNOWN";
+    }
+}
+
+
+void DefaultLoggingCb(enum wolfSSH_LogLevel level, const char *const msgStr)
+{
+    char    timeStr[80];
+    timeStr[0] = '\0';
+#ifndef NO_TIMESTAMP
+    {
+        time_t  current;
+        struct  tm local;
+
+        current = time(NULL);
+        if (WLOCALTIME(&current, &local)) {
+            /* make pretty */
+            strftime(timeStr, sizeof(timeStr), "%b %d %T %Y: ", &local);
+        }
+    }
+#endif /* NO_TIMESTAMP */
+    //fprintf(stdout, "%s[%s] %s\n", timeStr, GetLogStr(level), msgStr);
+	//ESP_LOGI(TAG, "%s[%s] %s\n", timeStr, GetLogStr(level), msgStr);
+}
+
+
+/* our default logger */
+void wolfSSH_Log(enum wolfSSH_LogLevel level, const char *const fmt, ...)
+{
+    va_list vlist;
+    char    msgStr[WOLFSSH_DEFAULT_LOG_WIDTH];
+
+    if (level < logLevel)
+        return;   /* don't need to output */
+
+    /* format msg */
+    va_start(vlist, fmt);
+    WVSNPRINTF(msgStr, sizeof(msgStr), fmt, vlist);
+    va_end(vlist);
+
+    if (logFunction)
+        logFunction(level, msgStr);
+}
diff -r 000000000000 -r c4152c628df5 src/memory.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/memory.c	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,109 @@
+/* memory.c 
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The memory module contains the interface to the custom memory handling hooks.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssh/settings.h>
+
+
+#ifdef USE_WOLFSSH_MEMORY
+
+#include <wolfssh/memory.h>
+#include <wolfssh/error.h>
+
+/* Set these to default values initially. */
+static wolfSSH_Malloc_cb  malloc_function  = NULL;
+static wolfSSH_Free_cb    free_function    = NULL;
+static wolfSSH_Realloc_cb realloc_function = NULL;
+
+
+/* set wolfSSH allocator callbacks, 0 on success */
+int wolfSSH_SetAllocators(wolfSSH_Malloc_cb  mf,
+                           wolfSSH_Free_cb    ff,
+                           wolfSSH_Realloc_cb rf)
+{
+    int res = 0;
+
+    if (mf)
+        malloc_function = mf;
+    else
+        res = WS_BAD_ARGUMENT;
+
+    if (ff)
+        free_function = ff;
+    else
+        res = WS_BAD_ARGUMENT;
+
+    if (rf)
+        realloc_function = rf;
+    else
+        res = WS_BAD_ARGUMENT;
+
+    return res;
+}
+
+
+/* our malloc handler */
+void* wolfSSH_Malloc(size_t size)
+{
+    void* res = 0;
+
+    if (malloc_function)
+        res = malloc_function(size);
+    else
+        res = malloc(size);
+
+    return res;
+}
+
+
+/* our free handler */
+void wolfSSH_Free(void *ptr)
+{
+    if (free_function)
+        free_function(ptr);
+    else
+        free(ptr);
+}
+
+
+/* our realloc handler */
+void* wolfSSH_Realloc(void *ptr, size_t size)
+{
+    void* res = 0;
+
+    if (realloc_function)
+        res = realloc_function(ptr, size);
+    else
+        res = realloc(ptr, size);
+
+    return res;
+}
+
+#endif /* USE_WOLFSSH_MEMORY */
+
diff -r 000000000000 -r c4152c628df5 src/misc.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/misc.c	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,118 @@
+/* misc.c
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * The misc module contains inline functions. This file is either included
+ * into source files or built separately depending on the inline configure
+ * option.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+
+#include <wolfssh/settings.h>
+
+
+#ifndef WOLFSSH_MISC_C
+#define WOLFSSH_MISC_C
+
+
+#include <wolfssh/misc.h>
+
+
+#ifdef NO_INLINE
+    #define STATIC
+#else
+    #define STATIC static
+#endif
+
+
+/* Check for if compiling misc.c when not needed. */
+#if !defined(WOLFSSH_MISC_INCLUDED) && !defined(NO_INLINE)
+    #define MISC_WARNING "misc.c does not need to be compiled when using inline (NO_INLINE not defined))"
+
+    #ifndef _MSC_VER
+        #warning MISC_WARNING
+    #else
+        #pragma message("warning: " MISC_WARNING)
+    #endif
+
+#else /* !WOLFSSL_MISC_INCLUDED && !NO_INLINE */
+
+
+#ifndef min
+STATIC INLINE word32 min(word32 a, word32 b)
+{
+    return a > b ? b : a;
+}
+#endif /* min */
+
+
+/* convert opaque to 32 bit integer */
+STATIC INLINE void ato32(const byte* c, word32* u32)
+{
+    *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
+}
+
+
+/* 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;
+}
+
+
+/* Make sure compiler doesn't skip */
+STATIC INLINE void ForceZero(const void* mem, word32 length)
+{
+    volatile byte* z = (volatile byte*)mem;
+
+    while (length--) *z++ = 0;
+}
+
+
+/* check all length bytes for equality, return 0 on success */
+STATIC INLINE int ConstantCompare(const byte* a, const byte* b,
+                                  word32 length)
+{
+    word32 i;
+    word32 compareSum = 0;
+
+    for (i = 0; i < length; i++) {
+        compareSum |= a[i] ^ b[i];
+    }
+
+    return compareSum;
+}
+
+
+#undef STATIC
+
+
+#endif /* !WOLFSSL_MISC_INCLUDED && !NO_INLINE */
+
+
+#endif /* WOLFSSH_MISC_C */
diff -r 000000000000 -r c4152c628df5 src/port.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/port.c	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,72 @@
+/* port.c 
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The port module wraps standard C library functions with macros to
+ * cover portablility issues when building in environments that rename
+ * those functions. This module also provides local versions of some
+ * standard C library functions that are missing on some platforms.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <stdio.h>
+#include <wolfssh/port.h>
+
+
+int wfopen(WFILE** f, const char* filename, const char* mode)
+{
+#ifdef USE_WINDOWS_API
+    return fopen_s(f, filename, mode) != 0;
+#else
+    if (f != NULL) {
+        *f = fopen(filename, mode);
+        return *f == NULL;
+    }
+    return 1;
+#endif
+}
+
+
+#ifndef WSTRING_USER
+
+char* wstrnstr(const char* s1, const char* s2, unsigned int n)
+{
+    unsigned int s2_len = (unsigned int)WSTRLEN(s2);
+
+    if (s2_len == 0)
+        return (char*)s1;
+
+    while (n >= s2_len && s1[0]) {
+        if (s1[0] == s2[0])
+            if (WMEMCMP(s1, s2, s2_len) == 0)
+                return (char*)s1;
+        s1++;
+        n--;
+    }
+
+    return NULL;
+}
+
+#endif /* WSTRING_USER */
diff -r 000000000000 -r c4152c628df5 src/ssh.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ssh.c	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,859 @@
+/* ssh.c
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The ssh module contains the public API for wolfSSH.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+    #include <config.h>
+#endif
+
+#include <wolfssh/ssh.h>
+#include <wolfssh/internal.h>
+#include <wolfssh/log.h>
+#include <wolfcrypt/wc_port.h>
+//#include "esp_log.h"
+
+#ifdef NO_INLINE
+    #include <wolfssh/misc.h>
+#else
+    #define WOLFSSH_MISC_INCLUDED
+    #include "src/misc.c"
+#endif
+
+
+int wolfSSH_Init(void)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_Init()");
+    if (wolfCrypt_Init() != 0)
+        ret = WS_CRYPTO_FAILED;
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_Init(), returning %d", ret);
+    return ret;
+}
+
+
+int wolfSSH_Cleanup(void)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_Cleanup()");
+
+    if (wolfCrypt_Cleanup() != 0)
+        ret = WS_CRYPTO_FAILED;
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_Cleanup(), returning %d", ret);
+    return ret;
+}
+
+
+WOLFSSH_CTX* wolfSSH_CTX_new(byte side, void* heap)
+{
+    WOLFSSH_CTX* ctx;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_new()");
+
+    if (side != WOLFSSH_ENDPOINT_SERVER && side != WOLFSSH_ENDPOINT_CLIENT) {
+        WLOG(WS_LOG_DEBUG, "Invalid endpoint type");
+        return NULL;
+    }
+
+    ctx = (WOLFSSH_CTX*)WMALLOC(sizeof(WOLFSSH_CTX), heap, DYNTYPE_CTX);
+    ctx = CtxInit(ctx, side, heap);
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_new(), ctx = %p", ctx);
+
+    return ctx;
+}
+
+
+void wolfSSH_CTX_free(WOLFSSH_CTX* ctx)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_free()");
+
+    if (ctx) {
+        CtxResourceFree(ctx);
+        WFREE(ctx, ctx->heap, DYNTYPE_CTX);
+    }
+}
+
+
+WOLFSSH* wolfSSH_new(WOLFSSH_CTX* ctx)
+{
+    WOLFSSH* ssh;
+    void*    heap = NULL;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_new()");
+    //ESP_LOGI("WOLFSSH", "Entering wolfSSH_new()");
+
+    if (ctx)
+        heap = ctx->heap;
+    else {
+        WLOG(WS_LOG_ERROR, "Trying to init a wolfSSH w/o wolfSSH_CTX");
+        return NULL;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_new()");
+    //ESP_LOGI("WOLFSSH", "ctx check ok, now malloc WOLFSSH");
+
+    ssh = (WOLFSSH*)WMALLOC(sizeof(WOLFSSH), heap, DYNTYPE_SSH);
+    //ESP_LOGI("WOLFSSH", "malloc done, SSH Initialization...");
+    //if(ssh == NULL) ESP_LOGE("WOLFSSH", "ssh is NULL, passing to SshInit() anyway...");
+    ssh = SshInit(ssh, ctx);
+    //ESP_LOGI("WOLFSSH", "SshInit OK");
+    //if(ssh == NULL) ESP_LOGE("WOLFSSH", "ssh is NULL after SshInit()");
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_new(), ssh = %p", ssh);
+    //ESP_LOGI("WOLFSSH", "Leaving wolfSSH_new()");
+
+    return ssh;
+}
+
+
+void wolfSSH_free(WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_free()");
+
+    if (ssh) {
+        void* heap = ssh->ctx ? ssh->ctx->heap : NULL;
+        SshResourceFree(ssh, heap);
+        WFREE(ssh, heap, DYNTYPE_SSH);
+    }
+}
+
+
+int wolfSSH_set_fd(WOLFSSH* ssh, int fd)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_set_fd()");
+
+    if (ssh) {
+        ssh->rfd = fd;
+        ssh->wfd = fd;
+
+        ssh->ioReadCtx  = &ssh->rfd;
+        ssh->ioWriteCtx = &ssh->wfd;
+
+        return WS_SUCCESS;
+    }
+    return WS_BAD_ARGUMENT;
+}
+
+
+int wolfSSH_get_fd(const WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_fd()");
+
+    if (ssh)
+        return ssh->rfd;
+
+    return WS_BAD_ARGUMENT;
+}
+
+
+int wolfSSH_SetHighwater(WOLFSSH* ssh, word32 highwater)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwater()");
+
+    if (ssh) {
+        ssh->highwaterMark = highwater;
+
+        return WS_SUCCESS;
+    }
+
+    return WS_BAD_ARGUMENT;
+}
+
+
+word32 wolfSSH_GetHighwater(WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetHighwater()");
+
+    if (ssh)
+        return ssh->highwaterMark;
+
+    return 0;
+}
+
+
+void wolfSSH_SetHighwaterCb(WOLFSSH_CTX* ctx, word32 highwater,
+                            WS_CallbackHighwater cb)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwaterCb()");
+
+    if (ctx) {
+        ctx->highwaterMark = highwater;
+        ctx->highwaterCb = cb;
+    }
+}
+
+
+void wolfSSH_SetHighwaterCtx(WOLFSSH* ssh, void* ctx)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetHighwaterCtx()");
+
+    if (ssh)
+        ssh->highwaterCtx = ctx;
+}
+
+
+void* wolfSSH_GetHighwaterCtx(WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_GetHighwaterCtx()");
+
+    if (ssh)
+        return ssh->highwaterCtx;
+
+    return NULL;
+}
+
+
+int wolfSSH_get_error(const WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_error()");
+
+    if (ssh)
+        return ssh->error;
+
+    return WS_BAD_ARGUMENT;
+}
+
+
+const char* wolfSSH_get_error_name(const WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_get_error_name()");
+
+    if (ssh)
+        return GetErrorString(ssh->error);
+
+    return NULL;
+}
+
+
+const char acceptError[] = "accept error: %s, %d";
+const char acceptState[] = "accept state: %s";
+
+
+int wolfSSH_accept(WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_accept()");
+    //ESP_LOGI("WOLFSSH", "Entering wolfSSH_accept()");
+
+    if (ssh == NULL)
+        return WS_BAD_ARGUMENT;
+
+    //ESP_LOGI("WOLFSSH", "ssh is not NULL");
+
+    switch (ssh->acceptState) {
+
+        case ACCEPT_BEGIN:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_BEGIN");
+            if ( (ssh->error = SendProtoId(ssh)) < WS_SUCCESS) {
+            	while(ssh->error == WS_WANT_READ) {
+            		// non-blocking tcp
+            		//ESP_LOGW("WOLFSSH", "WANT_READ: looping");
+            		ssh->error = DoReceive(ssh);
+            	}
+            	if(ssh->error == WS_SUCCESS) break;
+                WLOG(WS_LOG_DEBUG, acceptError, "BEGIN", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->acceptState = ACCEPT_SERVER_VERSION_SENT;
+            WLOG(WS_LOG_DEBUG, acceptState, "SERVER_VERSION_SENT");
+            FALL_THROUGH;
+
+        case ACCEPT_SERVER_VERSION_SENT:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_SERVER_VERSION_SENT");
+            while (ssh->clientState < CLIENT_VERSION_DONE) {
+                if ( (ssh->error = DoProtoId(ssh)) < WS_SUCCESS) {
+                	while(ssh->error == WS_WANT_READ) {
+                		// non-blocking tcp
+                		//ESP_LOGW("WOLFSSH", "WANT_READ: looping");
+                		ssh->error = DoReceive(ssh);
+                	}
+                	if(ssh->error == WS_SUCCESS) break;
+                    WLOG(WS_LOG_DEBUG, acceptError,
+                         "SERVER_VERSION_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->acceptState = ACCEPT_CLIENT_VERSION_DONE;
+            WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_VERSION_DONE");
+            FALL_THROUGH;
+
+        case ACCEPT_CLIENT_VERSION_DONE:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_CLIENT_VERSION_DONE");
+            if ( (ssh->error = SendKexInit(ssh)) < WS_SUCCESS) {
+            	while(ssh->error == WS_WANT_READ) {
+            		// non-blocking tcp
+            		//ESP_LOGW("WOLFSSH", "WANT_READ: looping");
+            		ssh->error = DoReceive(ssh);
+            	}
+            	if(ssh->error == WS_SUCCESS) break;
+                WLOG(WS_LOG_DEBUG, acceptError,
+                     "CLIENT_VERSION_DONE", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->acceptState = ACCEPT_SERVER_KEXINIT_SENT;
+            WLOG(WS_LOG_DEBUG, acceptState, "SERVER_KEXINIT_SENT");
+            FALL_THROUGH;
+
+        case ACCEPT_SERVER_KEXINIT_SENT:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_SERVER_KEXINIT_SENT");
+            while (ssh->isKeying) {
+                if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) {
+                	while(ssh->error == WS_WANT_READ) {
+                		// non-blocking tcp
+                		//ESP_LOGW("WOLFSSH", "WANT_READ: looping");
+                		ssh->error = DoReceive(ssh);
+                	}
+                	if(ssh->error == WS_SUCCESS) break;
+                    WLOG(WS_LOG_DEBUG, acceptError,
+                         "SERVER_KEXINIT_SENT", ssh->error);
+                	//ESP_LOGE("WOLFSSH", "Accept error: SERVER_KEXINIT_SENT, %d", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->acceptState = ACCEPT_KEYED;
+            WLOG(WS_LOG_DEBUG, acceptState, "KEYED");
+            FALL_THROUGH;
+
+        case ACCEPT_KEYED:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_KEYED");
+            while (ssh->clientState < CLIENT_USERAUTH_REQUEST_DONE) {
+                if ( (ssh->error = DoReceive(ssh)) < 0) {
+                	while(ssh->error == WS_WANT_READ) {
+                		// non-blocking tcp
+                		//ESP_LOGW("WOLFSSH", "WANT_READ: looping");
+                		ssh->error = DoReceive(ssh);
+                	}
+                	if(ssh->error == WS_SUCCESS) break;
+                    WLOG(WS_LOG_DEBUG, acceptError,
+                         "KEYED", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->acceptState = ACCEPT_CLIENT_USERAUTH_REQUEST_DONE;
+            WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_USERAUTH_REQUEST_DONE");
+            FALL_THROUGH;
+
+        case ACCEPT_CLIENT_USERAUTH_REQUEST_DONE:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_CLIENT_USERAUTH_REQUEST_DONE");
+            if ( (ssh->error = SendServiceAccept(ssh, ID_SERVICE_USERAUTH)) <
+                                                                  WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, acceptError,
+                     "CLIENT_USERAUTH_REQUEST_DONE", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->acceptState = ACCEPT_SERVER_USERAUTH_ACCEPT_SENT;
+            WLOG(WS_LOG_DEBUG, acceptState,
+                 "ACCEPT_SERVER_USERAUTH_ACCEPT_SENT");
+            FALL_THROUGH;
+
+        case ACCEPT_SERVER_USERAUTH_ACCEPT_SENT:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_SERVER_USERAUTH_ACCEPT_SENT");
+            while (ssh->clientState < CLIENT_USERAUTH_DONE) {
+                if ( (ssh->error = DoReceive(ssh)) < 0) {
+                	while(ssh->error == WS_WANT_READ) {
+                		// non-blocking tcp
+                		//ESP_LOGW("WOLFSSH", "WANT_READ: looping");
+                		ssh->error = DoReceive(ssh);
+                	}
+                	if(ssh->error == WS_SUCCESS) break;
+                    WLOG(WS_LOG_DEBUG, acceptError,
+                         "SERVER_USERAUTH_ACCEPT_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->acceptState = ACCEPT_CLIENT_USERAUTH_DONE;
+            WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_USERAUTH_DONE");
+            FALL_THROUGH;
+
+        case ACCEPT_CLIENT_USERAUTH_DONE:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_CLIENT_USERAUTH_DONE");
+            if ( (ssh->error = SendUserAuthSuccess(ssh)) < WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, acceptError,
+                     "CLIENT_USERAUTH_DONE", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->acceptState = ACCEPT_SERVER_USERAUTH_SENT;
+            WLOG(WS_LOG_DEBUG, acceptState, "SERVER_USERAUTH_SENT");
+            FALL_THROUGH;
+
+        case ACCEPT_SERVER_USERAUTH_SENT:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_SERVER_USERAUTH_SENT");
+            while (ssh->clientState < CLIENT_DONE) {
+                if ( (ssh->error = DoReceive(ssh)) < 0) {
+                	while(ssh->error == WS_WANT_READ) {
+                		// non-blocking tcp
+                		//ESP_LOGW("WOLFSSH", "WANT_READ: looping");
+                		ssh->error = DoReceive(ssh);
+                	}
+                	if(ssh->error == WS_SUCCESS) break;
+                    WLOG(WS_LOG_DEBUG, acceptError,
+                         "SERVER_USERAUTH_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->acceptState = ACCEPT_CLIENT_CHANNEL_REQUEST_DONE;
+            WLOG(WS_LOG_DEBUG, acceptState, "CLIENT_CHANNEL_REQUEST_DONE");
+            FALL_THROUGH;
+
+        case ACCEPT_CLIENT_CHANNEL_REQUEST_DONE:
+        	//ESP_LOGI("WOLFSSH", "acceptState = ACCEPT_CLIENT_CHANNEL_REQUEST_DONE");
+            if ( (ssh->error = SendChannelOpenConf(ssh)) < WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, acceptError,
+                     "CLIENT_CHANNEL_REQUEST_DONE", ssh->error);
+                //ESP_LOGE("WOLFSSH", "SendChannelOpenConf failed: %d", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->acceptState = ACCEPT_SERVER_CHANNEL_ACCEPT_SENT;
+            WLOG(WS_LOG_DEBUG, acceptState, "SERVER_CHANNEL_ACCEPT_SENT");
+    }
+
+    return WS_SUCCESS;
+}
+
+
+const char connectError[] = "connect error: %s, %d";
+const char connectState[] = "connect state: %s";
+
+
+int wolfSSH_connect(WOLFSSH* ssh)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_connect()");
+
+    if (ssh == NULL)
+        return WS_BAD_ARGUMENT;
+
+    switch (ssh->connectState) {
+
+        case CONNECT_BEGIN:
+            if ( (ssh->error = SendProtoId(ssh)) < WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, connectError, "BEGIN", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->connectState = CONNECT_CLIENT_VERSION_SENT;
+            WLOG(WS_LOG_DEBUG, connectState, "CLIENT_VERSION_SENT");
+            FALL_THROUGH;
+
+        case CONNECT_CLIENT_VERSION_SENT:
+            while (ssh->serverState < SERVER_VERSION_DONE) {
+                if ( (ssh->error = DoProtoId(ssh)) < WS_SUCCESS) {
+                    WLOG(WS_LOG_DEBUG, connectError,
+                         "CLIENT_VERSION_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->connectState = CONNECT_SERVER_VERSION_DONE;
+            WLOG(WS_LOG_DEBUG, connectState, "SERVER_VERSION_DONE");
+            FALL_THROUGH;
+
+        case CONNECT_SERVER_VERSION_DONE:
+            if ( (ssh->error = SendKexInit(ssh)) < WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, acceptError,
+                     "SERVER_VERSION_DONE", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->connectState = CONNECT_CLIENT_KEXINIT_SENT;
+            WLOG(WS_LOG_DEBUG, connectState, "CLIENT_KEXINIT_SENT");
+            FALL_THROUGH;
+
+        case CONNECT_CLIENT_KEXINIT_SENT:
+            while (ssh->serverState < SERVER_KEXINIT_DONE) {
+                if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) {
+                    WLOG(WS_LOG_DEBUG, connectError,
+                         "CLIENT_KEXINIT_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->connectState = CONNECT_SERVER_KEXINIT_DONE;
+            WLOG(WS_LOG_DEBUG, connectState, "SERVER_KEXINIT_DONE");
+            FALL_THROUGH;
+
+        case CONNECT_SERVER_KEXINIT_DONE:
+            if (ssh->handshake->kexId == ID_DH_GEX_SHA256)
+                ssh->error = SendKexDhGexRequest(ssh);
+            else
+                ssh->error = SendKexDhInit(ssh);
+            if (ssh->error < WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, connectError,
+                     "SERVER_KEXINIT_DONE", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->connectState = CONNECT_CLIENT_KEXDH_INIT_SENT;
+            WLOG(WS_LOG_DEBUG, connectState, "CLIENT_KEXDH_INIT_SENT");
+            FALL_THROUGH;
+
+        case CONNECT_CLIENT_KEXDH_INIT_SENT:
+            while (ssh->isKeying) {
+                if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) {
+                    WLOG(WS_LOG_DEBUG, connectError,
+                         "CLIENT_KEXDH_INIT_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->connectState = CONNECT_KEYED;
+            WLOG(WS_LOG_DEBUG, connectState, "KEYED");
+            FALL_THROUGH;
+
+        case CONNECT_KEYED:
+            if ( (ssh->error = SendServiceRequest(ssh, ID_SERVICE_USERAUTH)) <
+                                                                  WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, connectError, "KEYED", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->connectState = CONNECT_CLIENT_USERAUTH_REQUEST_SENT;
+            WLOG(WS_LOG_DEBUG, connectState, "CLIENT_USERAUTH_REQUEST_SENT");
+            FALL_THROUGH;
+
+        case CONNECT_CLIENT_USERAUTH_REQUEST_SENT:
+            while (ssh->serverState < SERVER_USERAUTH_REQUEST_DONE) {
+                if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) {
+                    WLOG(WS_LOG_DEBUG, connectError,
+                         "CLIENT_USERAUTH_REQUEST_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->connectState = CONNECT_SERVER_USERAUTH_REQUEST_DONE;
+            WLOG(WS_LOG_DEBUG, connectState, "SERVER_USERAUTH_REQUEST_DONE");
+            FALL_THROUGH;
+
+        case CONNECT_SERVER_USERAUTH_REQUEST_DONE:
+            if ( (ssh->error = SendUserAuthRequest(ssh, ID_NONE)) <
+                                                                  WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, connectError,
+                     "SERVER_USERAUTH_REQUEST_DONE", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->connectState = CONNECT_CLIENT_USERAUTH_SENT;
+            WLOG(WS_LOG_DEBUG, connectState, "CLIENT_USERAUTH_SENT");
+            FALL_THROUGH;
+
+        case CONNECT_CLIENT_USERAUTH_SENT:
+            while (ssh->serverState < SERVER_USERAUTH_ACCEPT_DONE) {
+                if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) {
+                    WLOG(WS_LOG_DEBUG, connectError,
+                         "CLIENT_USERAUTH_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->connectState = CONNECT_SERVER_USERAUTH_ACCEPT_DONE;
+            WLOG(WS_LOG_DEBUG, connectState, "SERVER_USERAUTH_ACCEPT_DONE");
+            FALL_THROUGH;
+
+        case CONNECT_SERVER_USERAUTH_ACCEPT_DONE:
+            if ( (ssh->error = SendChannelOpenSession(ssh, DEFAULT_WINDOW_SZ,
+                                        DEFAULT_MAX_PACKET_SZ)) < WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, connectError,
+                     "SERVER_USERAUTH_ACCEPT_DONE", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->connectState = CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT;
+            WLOG(WS_LOG_DEBUG, connectState,
+                 "CLIENT_CHANNEL_OPEN_SESSION_SENT");
+            FALL_THROUGH;
+
+        case CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT:
+            while (ssh->serverState < SERVER_CHANNEL_OPEN_DONE) {
+                if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) {
+                    WLOG(WS_LOG_DEBUG, connectError,
+                         "CLIENT_CHANNEL_OPEN_SESSION_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->connectState = CONNECT_SERVER_CHANNEL_OPEN_SESSION_DONE;
+            WLOG(WS_LOG_DEBUG, connectState,
+                 "SERVER_CHANNEL_OPEN_SESSION_DONE");
+            FALL_THROUGH;
+
+        case CONNECT_SERVER_CHANNEL_OPEN_SESSION_DONE:
+            if ( (ssh->error = SendChannelRequestShell(ssh)) < WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, connectError,
+                     "SERVER_CHANNEL_OPEN_SESSION_DONE", ssh->error);
+                return WS_FATAL_ERROR;
+            }
+            ssh->connectState = CONNECT_CLIENT_CHANNEL_REQUEST_SHELL_SENT;
+            WLOG(WS_LOG_DEBUG, connectState,
+                 "CLIENT_CHANNEL_REQUEST_SHELL_SENT");
+            FALL_THROUGH;
+
+        case CONNECT_CLIENT_CHANNEL_REQUEST_SHELL_SENT:
+            while (ssh->serverState < SERVER_DONE) {
+                if ( (ssh->error = DoReceive(ssh)) < WS_SUCCESS) {
+                    WLOG(WS_LOG_DEBUG, connectError,
+                         "CLIENT_CHANNEL_REQUEST_SHELL_SENT", ssh->error);
+                    return WS_FATAL_ERROR;
+                }
+            }
+            ssh->connectState = CONNECT_SERVER_CHANNEL_REQUEST_SHELL_DONE;
+            WLOG(WS_LOG_DEBUG, connectState,
+                 "SERVER_CHANNEL_REQUEST_SHELL_DONE");
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_connect()");
+    return WS_SUCCESS;
+}
+
+
+int wolfSSH_shutdown(WOLFSSH* ssh)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_shutdown()");
+
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = SendChannelEof(ssh, 0);
+
+    if (ret == WS_SUCCESS)
+        ret = SendChannelClose(ssh, 0);
+
+    if (ret == WS_SUCCESS)
+        ret = SendDisconnect(ssh, WOLFSSH_DISCONNECT_BY_APPLICATION);
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_shutdown(), ret = %d", ret);
+    return ret;
+}
+
+
+int wolfSSH_TriggerKeyExchange(WOLFSSH* ssh)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_TriggerKeyExchange()");
+    if (ssh == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = SendKexInit(ssh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_TriggerKeyExchange(), ret = %d", ret);
+    return ret;
+}
+
+
+int wolfSSH_stream_read(WOLFSSH* ssh, byte* buf, word32 bufSz)
+{
+    Buffer* inputBuffer;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_read()");
+    //ESP_LOGI("WOLFSSH", "wolfSSH_stream_read()");
+
+    if (ssh == NULL || buf == NULL || bufSz == 0 || ssh->channelList == NULL)
+        return WS_BAD_ARGUMENT;
+
+    inputBuffer = &ssh->channelList->inputBuffer;
+
+    while (inputBuffer->length - inputBuffer->idx == 0) {
+        int ret = DoReceive(ssh);
+        if (ret < 0) {
+            WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_read(), ret = %d", ret);
+            return ret;
+        }
+    }
+    //ESP_LOGI("WOLFSSH", "wolfSSH_stream_read: while loop done");
+
+    bufSz = min(bufSz, inputBuffer->length - inputBuffer->idx);
+    WMEMCPY(buf, inputBuffer->buffer + inputBuffer->idx, bufSz);
+    inputBuffer->idx += bufSz;
+
+    if (!ssh->isKeying && (inputBuffer->length > inputBuffer->bufferSz / 2)) {
+
+        word32 usedSz = inputBuffer->length - inputBuffer->idx;
+        word32 bytesToAdd = inputBuffer->idx;
+        int sendResult;
+
+        WLOG(WS_LOG_DEBUG, "Making more room: %u", usedSz);
+        if (usedSz) {
+            WLOG(WS_LOG_DEBUG, "  ...moving data down");
+            WMEMMOVE(inputBuffer->buffer,
+                     inputBuffer->buffer + bytesToAdd, usedSz);
+        }
+
+        sendResult = SendChannelWindowAdjust(ssh,
+                                             ssh->channelList->peerChannel,
+                                             bytesToAdd);
+        if (sendResult != WS_SUCCESS)
+            bufSz = sendResult;
+
+        WLOG(WS_LOG_INFO, "  bytesToAdd = %u", bytesToAdd);
+        WLOG(WS_LOG_INFO, "  windowSz = %u", ssh->channelList->windowSz);
+        ssh->channelList->windowSz += bytesToAdd;
+        WLOG(WS_LOG_INFO, "  update windowSz = %u", ssh->channelList->windowSz);
+
+        inputBuffer->length = usedSz;
+        inputBuffer->idx = 0;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_read(), rxd = %d", bufSz);
+    return bufSz;
+}
+
+
+int wolfSSH_stream_send(WOLFSSH* ssh, byte* buf, word32 bufSz)
+{
+    int bytesTxd = 0;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_stream_send()");
+
+    if (ssh == NULL || buf == NULL || ssh->channelList == NULL)
+        return WS_BAD_ARGUMENT;
+
+    bytesTxd = SendChannelData(ssh, ssh->channelList->peerChannel, buf, bufSz);
+
+    WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_stream_send(), txd = %d", bytesTxd);
+    return bytesTxd;
+}
+
+
+void wolfSSH_SetUserAuth(WOLFSSH_CTX* ctx, WS_CallbackUserAuth cb)
+{
+    if (ctx != NULL) {
+        ctx->userAuthCb = cb;
+    }
+}
+
+
+void wolfSSH_SetUserAuthCtx(WOLFSSH* ssh, void* userAuthCtx)
+{
+    if (ssh != NULL) {
+        ssh->userAuthCtx = userAuthCtx;
+    }
+}
+
+
+void* wolfSSH_GetUserAuthCtx(WOLFSSH* ssh)
+{
+    if (ssh != NULL) {
+        return ssh->userAuthCtx;
+    }
+    return NULL;
+}
+
+
+int wolfSSH_SetUsername(WOLFSSH* ssh, const char* username)
+{
+    char* value = NULL;
+    word32 valueSz;
+    int ret = WS_SUCCESS;
+
+    if (ssh == NULL || ssh->handshake == NULL ||
+        ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER ||
+        username == NULL) {
+
+        ret = WS_BAD_ARGUMENT;
+    }
+
+    if (ret == WS_SUCCESS) {
+        valueSz = (word32)WSTRLEN(username);
+        if (valueSz > 0)
+            value = (char*)WMALLOC(valueSz + 1, ssh->ctx->heap, DYNTYPE_STRING);
+        if (value == NULL)
+            ret = WS_MEMORY_E;
+    }
+
+    if (ret == WS_SUCCESS) {
+        WSTRNCPY(value, username, valueSz + 1);
+        if (ssh->userName != NULL) {
+            WFREE(ssh->userName, heap, DYNTYPE_STRING);
+            ssh->userName = NULL;
+        }
+        ssh->userName = value;
+        ssh->userNameSz = valueSz;
+    }
+
+    return ret;
+}
+
+
+int wolfSSH_CTX_SetBanner(WOLFSSH_CTX* ctx,
+                          const char* newBanner)
+{
+    word32 newBannerSz = 0;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_SetBanner()");
+
+    if (ctx == NULL)
+        return WS_BAD_ARGUMENT;
+
+    if (newBanner != NULL) {
+        WLOG(WS_LOG_INFO, "  setting banner to: \"%s\"", newBanner);
+        newBannerSz = (word32)WSTRLEN(newBanner);
+    }
+
+    ctx->banner = newBanner;
+    ctx->bannerSz = newBannerSz;
+
+    return WS_SUCCESS;
+}
+
+
+int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX* ctx,
+                                   const byte* in, word32 inSz, int format)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_UsePrivateKey_buffer()");
+    return wolfSSH_ProcessBuffer(ctx, in, inSz, format, BUFTYPE_PRIVKEY); 
+}
+
+
+void wolfSSH_GetStats(WOLFSSH* ssh, word32* txCount, word32* rxCount,
+                      word32* seq, word32* peerSeq)
+{
+    word32 rTxCount = 0;
+    word32 rRxCount = 0;
+    word32 rSeq = 0;
+    word32 rPeerSeq = 0;
+
+    if (ssh != NULL) {
+        rTxCount = ssh->txCount;
+        rRxCount = ssh->rxCount;
+        rSeq = ssh->seq;
+        rPeerSeq = ssh->peerSeq;
+    }
+
+    if (txCount != NULL)
+        *txCount = rTxCount;
+    if (rxCount != NULL)
+        *rxCount = rRxCount;
+    if (seq != NULL)
+        *seq = rSeq;
+    if (peerSeq != NULL)
+        *peerSeq = rPeerSeq;
+}
+
+
+int wolfSSH_KDF(byte hashId, byte keyId,
+                byte* key, word32 keySz,
+                const byte* k, word32 kSz,
+                const byte* h, word32 hSz,
+                const byte* sessionId, word32 sessionIdSz)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_KDF()");
+    return GenerateKey(hashId, keyId, key, keySz, k, kSz, h, hSz,
+                       sessionId, sessionIdSz);
+}
+
diff -r 000000000000 -r c4152c628df5 wolfssh/error.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/error.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,94 @@
+/* error.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The error header file provides the error codes used in the library.
+ */
+
+
+#pragma once
+
+#include <wolfssh/settings.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* add new error strings to error.c */
+
+
+/* main public return values */
+enum WS_ErrorCodes {
+    WS_SUCCESS         =  0,    /* function success */
+    WS_FATAL_ERROR     = -1,    /* general function failure */
+    WS_BAD_ARGUMENT    = -2,    /* bad function argument */
+    WS_MEMORY_E        = -3,    /* memory allocation failure */
+    WS_BUFFER_E        = -4,    /* input/output buffer size error */
+    WS_PARSE_E         = -5,    /* general parsing error */
+    WS_NOT_COMPILED    = -6,    /* feature not compiled in */
+    WS_OVERFLOW_E      = -7,    /* would overflow if continued */
+    WS_BAD_USAGE       = -8,    /* bad example usage */
+    WS_SOCKET_ERROR_E  = -9,
+    WS_WANT_READ       = -10,
+    WS_WANT_WRITE      = -11,
+    WS_RECV_OVERFLOW_E = -12,
+    WS_VERSION_E       = -13,   /* Peer using wrong version of SSH */
+    WS_SEND_OOB_READ_E = -14,
+    WS_INPUT_CASE_E    = -15,
+    WS_BAD_FILETYPE_E  = -16,
+    WS_UNIMPLEMENTED_E = -17,
+    WS_RSA_E           = -18,
+    WS_BAD_FILE_E      = -19,
+    WS_INVALID_ALGO_ID = -20,
+    WS_DECRYPT_E       = -21,
+    WS_ENCRYPT_E       = -22,
+    WS_VERIFY_MAC_E    = -23,
+    WS_CREATE_MAC_E    = -24,
+    WS_RESOURCE_E      = -25,   /* insufficient resources for new channel */
+    WS_INVALID_CHANTYPE = -26,  /* invalid channel type */
+    WS_INVALID_CHANID   = -27,
+    WS_INVALID_USERNAME = -28,
+    WS_CRYPTO_FAILED   = -29,   /* crypto action failed */
+    WS_INVALID_STATE_E = -30,
+    WS_INVALID_PRIME_CURVE = -32,
+    WS_ECC_E           = -33,
+    WS_CHANOPEN_FAILED = -34,
+    WS_REKEYING        = -90,   /* Status: rekey in progress */
+    WS_CHANNEL_CLOSED  = -91    /* Status: channel closed */
+};
+
+
+/* I/O Callback default errors */
+enum WS_IOerrors {
+    WS_CBIO_ERR_GENERAL    = -1,     /* general unexpected err */
+    WS_CBIO_ERR_WANT_READ  = -2,     /* need to call read  again */
+    WS_CBIO_ERR_WANT_WRITE = -2,     /* need to call write again */
+    WS_CBIO_ERR_CONN_RST   = -3,     /* connection reset */
+    WS_CBIO_ERR_ISR        = -4,     /* interrupt */
+    WS_CBIO_ERR_CONN_CLOSE = -5,     /* connection closed or epipe */
+    WS_CBIO_ERR_TIMEOUT    = -6      /* socket timeout */
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/internal.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/internal.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,525 @@
+/* internal.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The internal module contains the private data and functions. The public
+ * API calls into this module to do the work of processing the connections.
+ */
+
+
+#pragma once
+
+#include <wolfssh/ssh.h>
+#include <wolfcrypt/hash.h>
+#include <wolfcrypt/random.h>
+#include <wolfcrypt/aes.h>
+#include <wolfcrypt/dh.h>
+#include <wolfcrypt/ecc.h>
+
+
+#if !defined (ALIGN16)
+    #if defined (__GNUC__)
+        #define ALIGN16 __attribute__ ( (aligned (16)))
+    #elif defined(_MSC_VER)
+        /* disable align warning, we want alignment ! */
+        #pragma warning(disable: 4324)
+        #define ALIGN16 __declspec (align (16))
+    #else
+        #define ALIGN16
+    #endif
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+WOLFSSH_LOCAL const char* GetErrorString(int);
+
+
+enum {
+    /* Any of the items can be none. */
+    ID_NONE,
+
+    /* Encryption IDs */
+    ID_AES128_CBC,
+    ID_AES128_GCM,
+
+    /* Integrity IDs */
+    ID_HMAC_SHA1,
+    ID_HMAC_SHA1_96,
+    ID_HMAC_SHA2_256,
+
+    /* Key Exchange IDs */
+    ID_DH_GROUP1_SHA1,
+    ID_DH_GROUP14_SHA1,
+    ID_DH_GEX_SHA256,
+    ID_ECDH_SHA2_NISTP256,
+    ID_ECDH_SHA2_NISTP384,
+    ID_ECDH_SHA2_NISTP521,
+
+    /* Public Key IDs */
+    ID_SSH_RSA,
+    ID_ECDSA_SHA2_NISTP256,
+    ID_ECDSA_SHA2_NISTP384,
+    ID_ECDSA_SHA2_NISTP521,
+
+    /* Service IDs */
+    ID_SERVICE_USERAUTH,
+    ID_SERVICE_CONNECTION,
+
+    /* UserAuth IDs */
+    ID_USERAUTH_PASSWORD,
+    ID_USERAUTH_PUBLICKEY,
+
+    /* Channel Type IDs */
+    ID_CHANTYPE_SESSION,
+
+    ID_UNKNOWN
+};
+
+
+#define MAX_ENCRYPTION 3
+#define MAX_INTEGRITY 2
+#define MAX_KEY_EXCHANGE 2
+#define MAX_PUBLIC_KEY 1
+#define MAX_HMAC_SZ SHA256_DIGEST_SIZE
+#define MIN_BLOCK_SZ 8
+#define COOKIE_SZ 16
+#define LENGTH_SZ 4
+#define PAD_LENGTH_SZ 1
+#define MIN_PAD_LENGTH 4
+#define BOOLEAN_SZ 1
+#define MSG_ID_SZ 1
+#define SHA1_96_SZ 12
+#define UINT32_SZ 4
+#define SSH_PROTO_SZ 7 /* "SSH-2.0" */
+#define SSH_PROTO_EOL_SZ 2 /* Just the CRLF */
+#define AEAD_IMP_IV_SZ 4
+#define AEAD_EXP_IV_SZ 8
+#define AEAD_NONCE_SZ (AEAD_IMP_IV_SZ+AEAD_EXP_IV_SZ)
+#ifndef DEFAULT_HIGHWATER_MARK
+    #define DEFAULT_HIGHWATER_MARK ((1024 * 1024 * 1024) - (32 * 1024))
+#endif
+#ifndef DEFAULT_WINDOW_SZ
+    //#define DEFAULT_WINDOW_SZ (1024 * 1024)
+	#define DEFAULT_WINDOW_SZ (8 * 1024) // ESP32 Doesn't have that much RAM
+#endif
+#ifndef DEFAULT_MAX_PACKET_SZ
+    //#define DEFAULT_MAX_PACKET_SZ (16 * 1024)
+	#define DEFAULT_MAX_PACKET_SZ (2 * 1024) // ESP32 Doesn't have that much RAM
+#endif
+#ifndef DEFAULT_NEXT_CHANNEL
+    #define DEFAULT_NEXT_CHANNEL 0
+#endif
+
+
+WOLFSSH_LOCAL byte NameToId(const char*, word32);
+WOLFSSH_LOCAL const char* IdToName(byte);
+
+
+#define STATIC_BUFFER_LEN AES_BLOCK_SIZE
+/* This is one AES block size. We always grab one
+ * block size first to decrypt to find the size of
+ * the rest of the data. */
+
+
+typedef struct Buffer {
+    void* heap;       /* Heap for allocations */
+    word32 length;    /* total buffer length used */
+    word32 idx;       /* idx to part of length already consumed */
+    byte* buffer;     /* place holder for actual buffer */
+    word32 bufferSz;  /* current buffer size */
+    ALIGN16 byte staticBuffer[STATIC_BUFFER_LEN];
+    byte dynamicFlag; /* dynamic memory currently in use */
+} Buffer;
+
+
+WOLFSSH_LOCAL int BufferInit(Buffer*, word32, void*);
+WOLFSSH_LOCAL int GrowBuffer(Buffer*, word32, word32);
+WOLFSSH_LOCAL void ShrinkBuffer(Buffer* buf, int);
+
+
+/* our wolfSSH Context */
+struct WOLFSSH_CTX {
+    void* heap;                       /* heap hint */
+    WS_CallbackIORecv ioRecvCb;       /* I/O Receive Callback */
+    WS_CallbackIOSend ioSendCb;       /* I/O Send Callback */
+    WS_CallbackUserAuth userAuthCb;   /* User Authentication Callback */
+    WS_CallbackHighwater highwaterCb; /* Data Highwater Mark Callback */
+
+    byte* privateKey;                 /* Owned by CTX */
+    word32 privateKeySz;
+    byte useEcc;                      /* Depends on the private key */
+    word32 highwaterMark;
+    const char* banner;
+    word32 bannerSz;
+    byte side;                        /* client or server */
+    byte showBanner;
+};
+
+
+typedef struct Ciphers {
+    Aes aes;
+} Ciphers;
+
+
+typedef struct Keys {
+    byte iv[AES_BLOCK_SIZE];
+    byte ivSz;
+    byte encKey[AES_BLOCK_SIZE];
+    byte encKeySz;
+    byte macKey[MAX_HMAC_SZ];
+    byte macKeySz;
+} Keys;
+
+
+typedef struct HandshakeInfo {
+    byte kexId;
+    byte pubKeyId;
+    byte encryptId;
+    byte macId;
+    byte hashId;
+    byte kexPacketFollows;
+    byte aeadMode;
+
+    byte blockSz;
+    byte macSz;
+
+    Keys keys;
+    Keys peerKeys;
+    wc_HashAlg hash;
+    byte e[257]; /* May have a leading zero for unsigned or is a Q_S value. */
+    word32 eSz;
+    byte x[257]; /* May have a leading zero, for unsigned. */
+    word32 xSz;
+    byte* kexInit;
+    word32 kexInitSz;
+
+    word32 dhGexMinSz;
+    word32 dhGexPreferredSz;
+    word32 dhGexMaxSz;
+    byte* primeGroup;
+    word32 primeGroupSz;
+    byte* generator;
+    word32 generatorSz;
+
+    byte useEcc;
+    union {
+        DhKey dh;
+        ecc_key ecc;
+    } privKey;
+} HandshakeInfo;
+
+
+/* our wolfSSH session */
+struct WOLFSSH {
+    WOLFSSH_CTX* ctx;      /* owner context */
+    int error;
+    int rfd;
+    int wfd;
+    void* ioReadCtx;       /* I/O Read  Context handle */
+    void* ioWriteCtx;      /* I/O Write Context handle */
+    int rflags;            /* optional read  flags */
+    int wflags;            /* optional write flags */
+    word32 txCount;
+    word32 rxCount;
+    word32 highwaterMark;
+    byte highwaterFlag;    /* Set when highwater CB called */
+    void* highwaterCtx;
+    word32 curSz;
+    word32 seq;
+    word32 peerSeq;
+    word32 packetStartIdx; /* Current send packet start index */
+    byte paddingSz;        /* Current send packet padding size */
+    byte acceptState;
+    byte connectState;
+    byte clientState;
+    byte serverState;
+    byte processReplyState;
+    byte isKeying;
+
+    byte connReset;
+    byte isClosed;
+
+    byte blockSz;
+    byte encryptId;
+    byte macId;
+    byte macSz;
+    byte aeadMode;
+    byte peerBlockSz;
+    byte peerEncryptId;
+    byte peerMacId;
+    byte peerMacSz;
+    byte peerAeadMode;
+
+    Ciphers encryptCipher;
+    Ciphers decryptCipher;
+
+    word32 nextChannel;
+    WOLFSSH_CHANNEL* channelList;
+    word32 channelListSz;
+    word32 defaultPeerChannelId;
+
+    Buffer inputBuffer;
+    Buffer outputBuffer;
+    WC_RNG* rng;
+
+    byte h[WC_MAX_DIGEST_SIZE];
+    word32 hSz;
+    byte k[257];           /* May have a leading zero, for unsigned. */
+    word32 kSz;
+    byte sessionId[WC_MAX_DIGEST_SIZE];
+    word32 sessionIdSz;
+
+    Keys keys;
+    Keys peerKeys;
+    HandshakeInfo* handshake;
+
+    void* userAuthCtx;
+    char* userName;
+    word32 userNameSz;
+    char* password;
+    word32 passwordSz;
+    byte* pkBlob;
+    word32 pkBlobSz;
+    byte* peerProtoId;     /* Save for rekey */
+    word32 peerProtoIdSz;
+};
+
+
+struct WOLFSSH_CHANNEL {
+    byte channelType;
+    word32 channel;
+    word32 windowSz;
+    word32 maxPacketSz;
+    word32 peerChannel;
+    word32 peerWindowSz;
+    word32 peerMaxPacketSz;
+    Buffer inputBuffer;
+    struct WOLFSSH* ssh;
+    struct WOLFSSH_CHANNEL* next;
+};
+
+
+WOLFSSH_LOCAL WOLFSSH_CTX* CtxInit(WOLFSSH_CTX*, byte, void*);
+WOLFSSH_LOCAL void CtxResourceFree(WOLFSSH_CTX*);
+WOLFSSH_LOCAL WOLFSSH* SshInit(WOLFSSH*, WOLFSSH_CTX*);
+WOLFSSH_LOCAL void SshResourceFree(WOLFSSH*, void*);
+
+WOLFSSH_LOCAL WOLFSSH_CHANNEL* ChannelNew(WOLFSSH*, byte, word32, word32);
+WOLFSSH_LOCAL int ChannelUpdate(WOLFSSH_CHANNEL*, word32, word32, word32);
+WOLFSSH_LOCAL void ChannelDelete(WOLFSSH_CHANNEL*, void*);
+WOLFSSH_LOCAL WOLFSSH_CHANNEL* ChannelFind(WOLFSSH*, word32, byte);
+WOLFSSH_LOCAL int ChannelRemove(WOLFSSH*, word32, byte);
+WOLFSSH_LOCAL int ChannelPutData(WOLFSSH_CHANNEL*, byte*, word32);
+WOLFSSH_LOCAL int wolfSSH_ProcessBuffer(WOLFSSH_CTX*,
+                                        const byte*, word32,
+                                        int, int);
+
+
+#ifndef WOLFSSH_USER_IO
+
+/* default I/O handlers */
+WOLFSSH_LOCAL int wsEmbedRecv(WOLFSSH*, void*, word32, void*);
+WOLFSSH_LOCAL int wsEmbedSend(WOLFSSH*, void*, word32, void*);
+
+#endif /* WOLFSSH_USER_IO */
+
+
+WOLFSSH_LOCAL int DoReceive(WOLFSSH*);
+WOLFSSH_LOCAL int DoProtoId(WOLFSSH*);
+WOLFSSH_LOCAL int SendProtoId(WOLFSSH*);
+WOLFSSH_LOCAL int SendKexInit(WOLFSSH*);
+WOLFSSH_LOCAL int SendKexDhInit(WOLFSSH*);
+WOLFSSH_LOCAL int SendKexDhReply(WOLFSSH*);
+WOLFSSH_LOCAL int SendKexDhGexRequest(WOLFSSH*);
+WOLFSSH_LOCAL int SendKexDhGexGroup(WOLFSSH*);
+WOLFSSH_LOCAL int SendNewKeys(WOLFSSH*);
+WOLFSSH_LOCAL int SendUnimplemented(WOLFSSH*);
+WOLFSSH_LOCAL int SendDisconnect(WOLFSSH*, word32);
+WOLFSSH_LOCAL int SendIgnore(WOLFSSH*, const unsigned char*, word32);
+WOLFSSH_LOCAL int SendDebug(WOLFSSH*, byte, const char*);
+WOLFSSH_LOCAL int SendServiceRequest(WOLFSSH*, byte);
+WOLFSSH_LOCAL int SendServiceAccept(WOLFSSH*, byte);
+WOLFSSH_LOCAL int SendUserAuthRequest(WOLFSSH*, byte);
+WOLFSSH_LOCAL int SendUserAuthSuccess(WOLFSSH*);
+WOLFSSH_LOCAL int SendUserAuthFailure(WOLFSSH*, byte);
+WOLFSSH_LOCAL int SendUserAuthBanner(WOLFSSH*);
+WOLFSSH_LOCAL int SendUserAuthPkOk(WOLFSSH*, const byte*, word32,
+                                   const byte*, word32);
+WOLFSSH_LOCAL int SendRequestSuccess(WOLFSSH*, int);
+WOLFSSH_LOCAL int SendChannelOpenSession(WOLFSSH*, word32, word32);
+WOLFSSH_LOCAL int SendChannelOpenConf(WOLFSSH*);
+WOLFSSH_LOCAL int SendChannelEof(WOLFSSH*, word32);
+WOLFSSH_LOCAL int SendChannelClose(WOLFSSH*, word32);
+WOLFSSH_LOCAL int SendChannelData(WOLFSSH*, word32, byte*, word32);
+WOLFSSH_LOCAL int SendChannelWindowAdjust(WOLFSSH*, word32, word32);
+WOLFSSH_LOCAL int SendChannelRequestShell(WOLFSSH*);
+WOLFSSH_LOCAL int SendChannelSuccess(WOLFSSH*, word32, int);
+WOLFSSH_LOCAL int GenerateKey(byte, byte, byte*, word32, const byte*, word32,
+                              const byte*, word32, const byte*, word32);
+
+
+enum AcceptStates {
+    ACCEPT_BEGIN = 0,
+    ACCEPT_SERVER_VERSION_SENT,
+    ACCEPT_CLIENT_VERSION_DONE,
+    ACCEPT_SERVER_KEXINIT_SENT,
+    ACCEPT_KEYED,
+    ACCEPT_CLIENT_USERAUTH_REQUEST_DONE,
+    ACCEPT_SERVER_USERAUTH_ACCEPT_SENT,
+    ACCEPT_CLIENT_USERAUTH_DONE,
+    ACCEPT_SERVER_USERAUTH_SENT,
+    ACCEPT_CLIENT_CHANNEL_REQUEST_DONE,
+    ACCEPT_SERVER_CHANNEL_ACCEPT_SENT
+};
+
+
+enum ConnectStates {
+    CONNECT_BEGIN = 0,
+    CONNECT_CLIENT_VERSION_SENT,
+    CONNECT_SERVER_VERSION_DONE,
+    CONNECT_CLIENT_KEXINIT_SENT,
+    CONNECT_SERVER_KEXINIT_DONE,
+    CONNECT_CLIENT_KEXDH_INIT_SENT,
+    CONNECT_KEYED,
+    CONNECT_CLIENT_USERAUTH_REQUEST_SENT,
+    CONNECT_SERVER_USERAUTH_REQUEST_DONE,
+    CONNECT_CLIENT_USERAUTH_SENT,
+    CONNECT_SERVER_USERAUTH_ACCEPT_DONE,
+    CONNECT_CLIENT_CHANNEL_OPEN_SESSION_SENT,
+    CONNECT_SERVER_CHANNEL_OPEN_SESSION_DONE,
+    CONNECT_CLIENT_CHANNEL_REQUEST_SHELL_SENT,
+    CONNECT_SERVER_CHANNEL_REQUEST_SHELL_DONE
+};
+
+
+enum ClientStates {
+    CLIENT_BEGIN = 0,
+    CLIENT_VERSION_DONE,
+    CLIENT_KEXINIT_DONE,
+    CLIENT_KEXDH_INIT_DONE,
+    CLIENT_USERAUTH_REQUEST_DONE,
+    CLIENT_USERAUTH_DONE,
+    CLIENT_DONE
+};
+
+
+enum ServerStates {
+    SERVER_BEGIN = 0,
+    SERVER_VERSION_DONE,
+    SERVER_KEXINIT_DONE,
+    SERVER_USERAUTH_REQUEST_DONE,
+    SERVER_USERAUTH_ACCEPT_DONE,
+    SERVER_CHANNEL_OPEN_DONE,
+    SERVER_DONE
+};
+
+
+enum ProcessReplyStates {
+    PROCESS_INIT,
+    PROCESS_PACKET_LENGTH,
+    PROCESS_PACKET_FINISH,
+    PROCESS_PACKET
+};
+
+
+enum WS_MessageIds {
+    MSGID_DISCONNECT = 1,
+    MSGID_IGNORE = 2,
+    MSGID_UNIMPLEMENTED = 3,
+    MSGID_DEBUG = 4,
+    MSGID_SERVICE_REQUEST = 5,
+    MSGID_SERVICE_ACCEPT = 6,
+
+    MSGID_KEXINIT = 20,
+    MSGID_NEWKEYS = 21,
+
+    MSGID_KEXDH_INIT = 30,
+    MSGID_KEXECDH_INIT = 30,
+
+    MSGID_KEXDH_REPLY = 31,
+    MSGID_KEXECDH_REPLY = 31,
+    MSGID_KEXDH_GEX_GROUP = 31,
+    MSGID_KEXDH_GEX_INIT = 32,
+    MSGID_KEXDH_GEX_REPLY = 33,
+    MSGID_KEXDH_GEX_REQUEST = 34,
+
+    MSGID_USERAUTH_REQUEST = 50,
+    MSGID_USERAUTH_FAILURE = 51,
+    MSGID_USERAUTH_SUCCESS = 52,
+    MSGID_USERAUTH_BANNER = 53,
+    MSGID_USERAUTH_PK_OK = 60, /* Public Key OK */
+    MSGID_USERAUTH_PW_CHRQ = 60, /* Password Change Request */
+
+    MSGID_GLOBAL_REQUEST = 80,
+    MSGID_REQUEST_SUCCESS = 81,
+    MSGID_REQUEST_FAILURE = 82,
+
+    MSGID_CHANNEL_OPEN = 90,
+    MSGID_CHANNEL_OPEN_CONF = 91,
+    MSGID_CHANNEL_OPEN_FAIL = 92,
+    MSGID_CHANNEL_WINDOW_ADJUST = 93,
+    MSGID_CHANNEL_DATA = 94,
+    MSGID_CHANNEL_EOF = 96,
+    MSGID_CHANNEL_CLOSE = 97,
+    MSGID_CHANNEL_REQUEST = 98,
+    MSGID_CHANNEL_SUCCESS = 99,
+    MSGID_CHANNEL_FAILURE = 100
+};
+
+
+/* dynamic memory types */
+enum WS_DynamicTypes {
+    DYNTYPE_CTX,
+    DYNTYPE_SSH,
+    DYNTYPE_CHANNEL,
+    DYNTYPE_BUFFER,
+    DYNTYPE_ID,
+    DYNTYPE_HS,
+    DYNTYPE_CA,
+    DYNTYPE_CERT,
+    DYNTYPE_PRIVKEY,
+    DYNTYPE_PUBKEY,
+    DYNTYPE_DH,
+    DYNTYPE_RNG,
+    DYNTYPE_STRING,
+    DYNTYPE_MPINT
+};
+
+
+enum WS_BufferTypes {
+    BUFTYPE_CA,
+    BUFTYPE_CERT,
+    BUFTYPE_PRIVKEY,
+    BUFTYPE_PUBKEY
+};
+
+
+WOLFSSH_LOCAL void DumpOctetString(const byte*, word32);
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/keygen.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/keygen.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,52 @@
+/* keygen.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The keygen module contains utility functions wrapping the wolfCrypt
+ * key generation functions to product SSH friendly keys.
+ */
+
+
+#pragma once
+
+#ifndef WOLFSSH_KEYGEN_H
+#define WOLFSSH_KEYGEN_H
+
+#include <wolfssh/settings.h>
+#include <wolfssh/port.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define WOLFSSH_RSAKEY_DEFAULT_SZ 2048
+#define WOLFSSH_RSAKEY_DEFAULT_E  65537
+
+
+WOLFSSH_API int wolfSSH_MakeRsaKey(byte*, word32, word32, word32);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff -r 000000000000 -r c4152c628df5 wolfssh/log.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/log.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,73 @@
+/* log.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The log module contains the interface to the logging function. When
+ * debugging is enabled and turned on, the logger will output to STDOUT.
+ * A custom logging callback may be installed.
+ */
+
+
+#pragma once
+
+#include <wolfssh/settings.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+enum wolfSSH_LogLevel {
+    WS_LOG_USER  = 5,
+    WS_LOG_ERROR = 4,
+    WS_LOG_WARN  = 3,
+    WS_LOG_INFO  = 2,
+    WS_LOG_DEBUG = 1,
+    WS_LOG_DEFAULT = WS_LOG_DEBUG
+};
+
+
+typedef void (*wolfSSH_LoggingCb)(enum wolfSSH_LogLevel,
+                                  const char *const logMsg);
+WOLFSSH_API void wolfSSH_SetLoggingCb(wolfSSH_LoggingCb logF);
+WOLFSSH_API int wolfSSH_LogEnabled(void);
+
+
+#ifdef __GNUC__
+    #define FMTCHECK __attribute__((format(printf,2,3)))
+#else
+    #define FMTCHECK
+#endif /* __GNUC__ */
+
+
+WOLFSSH_API void wolfSSH_Log(enum wolfSSH_LogLevel,
+                             const char *const, ...) FMTCHECK;
+
+#define WLOG(...) do { \
+                      if (wolfSSH_LogEnabled()) \
+                          wolfSSH_Log(__VA_ARGS__); \
+                  } while (0)
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/memory.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/memory.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,56 @@
+/* memory.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The memory module contains the interface to the custom memory handling hooks.
+ */
+
+
+#pragma once
+
+#include <wolfssh/settings.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef void *(*wolfSSH_Malloc_cb)(size_t size);
+typedef void (*wolfSSH_Free_cb)(void *ptr);
+typedef void *(*wolfSSH_Realloc_cb)(void *ptr, size_t size);
+
+
+/* Public set function */
+WOLFSSH_API int wolfSSH_SetAllocators(wolfSSH_Malloc_cb  malloc_function,
+                                      wolfSSH_Free_cb    free_function,
+                                      wolfSSH_Realloc_cb realloc_function);
+
+/* Public in case user app wants to use WMALLOC/WFREE */
+WOLFSSH_API void* wolfSSH_Malloc(size_t size);
+WOLFSSH_API void  wolfSSH_Free(void *ptr);
+WOLFSSH_API void* wolfSSH_Realloc(void *ptr, size_t size);
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/misc.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/misc.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,57 @@
+/* misc.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#ifndef WOLFSSH_MISC_H
+#define WOLFSSH_MISC_H
+
+
+#ifdef __cplusplus
+    extern "C" {
+#endif
+
+
+#include <wolfssh/port.h>
+
+
+#ifdef NO_INLINE
+
+
+#ifndef min
+WOLFSSH_LOCAL word32 min(word32, word32);
+#endif /* min */
+
+WOLFSSH_LOCAL void ato32(const byte*, word32*);
+WOLFSSH_LOCAL void c32toa(word32, byte*);
+WOLFSSH_LOCAL void ForceZero(const void*, word32);
+WOLFSSH_LOCAL int ConstantCompare(const byte*, const byte*, word32);
+
+
+#endif /* NO_INLINE */
+
+
+#ifdef __cplusplus
+    }   /* extern "C" */
+#endif
+
+
+#endif /* WOLFSSH_MISC_H */
+
diff -r 000000000000 -r c4152c628df5 wolfssh/port.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/port.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,125 @@
+/* port.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The port module wraps standard C library functions with macros to
+ * cover portablility issues when building in environments that rename
+ * those functions. This module also provides local versions of some
+ * standard C library functions that are missing on some platforms.
+ */
+
+
+#pragma once
+
+#include <wolfssh/settings.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* setup memory handling */
+#ifndef WMALLOC_USER
+    #include <wolfssh/memory.h>
+
+    #define WMALLOC(s, h, t)    ((void)h, (void)t, wolfSSH_Malloc((s)))
+    #define WFREE(p, h, t)      {void* xp = (p); if ((xp)) wolfSSH_Free((xp));}
+    #define WREALLOC(p, n, h, t) wolfSSH_Realloc((p), (n))
+#endif /* WMALLOC_USER */
+
+
+/* setup string handling */
+#ifndef WSTRING_USER
+    #include <string.h>
+
+    #define WFILE FILE
+
+    WOLFSSH_API char* wstrnstr(const char*, const char*, unsigned int);
+    WOLFSSH_API int wfopen(WFILE**, const char*, const char*);
+
+    #define WMEMCPY(d,s,l)    memcpy((d),(s),(l))
+    #define WMEMSET(b,c,l)    memset((b),(c),(l))
+    #define WMEMCMP(s1,s2,n)  memcmp((s1),(s2),(n))
+    #define WMEMMOVE(d,s,l)   memmove((d),(s),(l))
+
+    #define WSTRLEN(s1)       strlen((s1))
+    #define WSTRSTR(s1,s2)    strstr((s1),(s2))
+    #define WSTRNSTR(s1,s2,n) wstrnstr((s1),(s2),(n))
+    #define WSTRNCMP(s1,s2,n) strncmp((s1),(s2),(n))
+    #define WSTRNCAT(s1,s2,n) strncat((s1),(s2),(n))
+    #define WSTRSPN(s1,s2)    strspn((s1),(s2))
+    #define WSTRCSPN(s1,s2)   strcspn((s1),(s2))
+    #define WFOPEN(f,fn,m)    wfopen((f),(fn),(m))
+    #define WFCLOSE(f)        fclose(f)
+
+    #ifndef USE_WINDOWS_API
+        #include <stdio.h>
+        #define WSTRNCPY(s1,s2,n) strncpy((s1),(s2),(n))
+        #define WSTRNCASECMP(s1,s2,n) strncasecmp((s1),(s2),(n))
+        #define WSNPRINTF(s,n,f,...) snprintf((s),(n),(f),##__VA_ARGS__)
+        #define WVSNPRINTF(s,n,f,...) vsnprintf((s),(n),(f),##__VA_ARGS__)
+        #define WLOCALTIME(c,r) (localtime_r((c),(r))!=NULL)
+    #else
+        #define WSTRNCPY(s1,s2,n) strncpy_s((s1),(n),(s2),(n))
+        #define WSTRNCASECMP(s1,s2,n) _strnicmp((s1),(s2),(n))
+        #define WSNPRINTF(s,n,f,...) _snprintf_s((s),(n),(n),(f),##__VA_ARGS__)
+        #define WVSNPRINTF(s,n,f,...) vsnprintf_s((s),(n),(n),(f),##__VA_ARGS__)
+        #define WLOCALTIME(c,r) (localtime_s((r),(c))==0)
+    #endif
+#endif /* WSTRING_USER */
+
+
+/* setup compiler inlining */
+#ifndef INLINE
+#ifndef NO_INLINE
+    #ifdef _MSC_VER
+        #define INLINE __inline
+    #elif defined(__GNUC__)
+        #define INLINE inline
+    #elif defined(__IAR_SYSTEMS_ICC__)
+        #define INLINE inline
+    #elif defined(THREADX)
+        #define INLINE _Inline
+    #else
+        #define INLINE
+    #endif
+#else
+    #define INLINE
+#endif
+#endif /* INLINE */
+
+
+/* GCC 7 has new switch() fall-through detection */
+#if defined(__GNUC__)
+    #if ((__GNUC__ > 7) || ((__GNUC__ == 7) && (__GNUC_MINOR__ >= 1)))
+        #define FALL_THROUGH __attribute__ ((fallthrough));
+    #endif
+#endif
+#ifndef FALL_THROUGH
+    #define FALL_THROUGH
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/settings.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/settings.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,52 @@
+/* settings.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The settings header provides a method for developers to provide the
+ * internal preprocessor defines that the configure script provides to
+ * environments that do not use the configure script.
+ */
+
+
+#pragma once
+
+#include <wolfssh/visibility.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* user settings here */
+#ifndef WMALLOC_USER
+    #define USE_WOLFSSH_MEMORY  /* default memory handlers */
+#endif /* WMALLOC_USER */
+
+
+#if defined (_WIN32)
+    #define USE_WINDOWS_API
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/ssh.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/ssh.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,205 @@
+/* ssh.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The ssh module contains the public API for wolfSSH.
+ */
+
+
+#pragma once
+
+#include <wolfssl/options.h>
+#include <wolfcrypt/types.h>
+#include <wolfssh/settings.h>
+#include <wolfssh/version.h>
+#include <wolfssh/port.h>
+#include <wolfssh/error.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct WOLFSSH_CTX WOLFSSH_CTX;
+typedef struct WOLFSSH WOLFSSH;
+typedef struct WOLFSSH_CHANNEL WOLFSSH_CHANNEL;
+
+
+WOLFSSH_API int wolfSSH_Init(void);
+WOLFSSH_API int wolfSSH_Cleanup(void);
+
+/* debugging output functions */
+WOLFSSH_API void wolfSSH_Debugging_ON(void);
+WOLFSSH_API void wolfSSH_Debugging_OFF(void);
+
+/* context functions */
+WOLFSSH_API WOLFSSH_CTX* wolfSSH_CTX_new(byte, void*);
+WOLFSSH_API void wolfSSH_CTX_free(WOLFSSH_CTX*);
+
+/* ssh session functions */
+WOLFSSH_API WOLFSSH* wolfSSH_new(WOLFSSH_CTX*);
+WOLFSSH_API void wolfSSH_free(WOLFSSH*);
+
+WOLFSSH_API int wolfSSH_set_fd(WOLFSSH*, int);
+WOLFSSH_API int wolfSSH_get_fd(const WOLFSSH*);
+
+/* data high water mark functions */
+WOLFSSH_API int wolfSSH_SetHighwater(WOLFSSH*, word32);
+WOLFSSH_API word32 wolfSSH_GetHighwater(WOLFSSH*);
+
+typedef int (*WS_CallbackHighwater)(byte, void*);
+WOLFSSH_API void wolfSSH_SetHighwaterCb(WOLFSSH_CTX*, word32,
+                                        WS_CallbackHighwater);
+WOLFSSH_API void wolfSSH_SetHighwaterCtx(WOLFSSH*, void*);
+WOLFSSH_API void* wolfSSH_GetHighwaterCtx(WOLFSSH*);
+
+
+WOLFSSH_API int wolfSSH_get_error(const WOLFSSH*);
+WOLFSSH_API const char* wolfSSH_get_error_name(const WOLFSSH*);
+
+/* I/O callbacks */
+typedef int (*WS_CallbackIORecv)(WOLFSSH*, void*, word32, void*);
+typedef int (*WS_CallbackIOSend)(WOLFSSH*, void*, word32, void*);
+WOLFSSH_API void wolfSSH_SetIORecv(WOLFSSH_CTX*, WS_CallbackIORecv);
+WOLFSSH_API void wolfSSH_SetIOSend(WOLFSSH_CTX*, WS_CallbackIOSend);
+WOLFSSH_API void wolfSSH_SetIOReadCtx(WOLFSSH*, void*);
+WOLFSSH_API void wolfSSH_SetIOWriteCtx(WOLFSSH*, void*);
+WOLFSSH_API void* wolfSSH_GetIOReadCtx(WOLFSSH*);
+WOLFSSH_API void* wolfSSH_GetIOWriteCtx(WOLFSSH*);
+
+/* User Authentication callback */
+typedef struct WS_UserAuthData_Password {
+    byte* password;
+    word32 passwordSz;
+    /* The following are present for future use. */
+    byte hasNewPassword;
+    byte* newPassword;
+    word32 newPasswordSz;
+} WS_UserAuthData_Password;
+
+typedef struct WS_UserAuthData_PublicKey {
+    byte* dataToSign;
+    byte* publicKeyType;
+    word32 publicKeyTypeSz;
+    byte* publicKey;
+    word32 publicKeySz;
+    byte hasSignature;
+    byte* signature;
+    word32 signatureSz;
+} WS_UserAuthData_PublicKey;
+
+typedef struct WS_UserAuthData {
+    byte type;
+    byte* username;
+    word32 usernameSz;
+    byte* serviceName;
+    word32 serviceNameSz;
+    byte* authName;
+    word32 authNameSz;
+    union {
+        WS_UserAuthData_Password password;
+        WS_UserAuthData_PublicKey publicKey;
+    } sf;
+} WS_UserAuthData;
+
+typedef int (*WS_CallbackUserAuth)(byte, WS_UserAuthData*, void*);
+WOLFSSH_API void wolfSSH_SetUserAuth(WOLFSSH_CTX*, WS_CallbackUserAuth);
+WOLFSSH_API void wolfSSH_SetUserAuthCtx(WOLFSSH*, void*);
+WOLFSSH_API void* wolfSSH_GetUserAuthCtx(WOLFSSH*);
+
+WOLFSSH_API int wolfSSH_SetUsername(WOLFSSH*, const char*);
+
+WOLFSSH_API int wolfSSH_CTX_SetBanner(WOLFSSH_CTX*, const char*);
+WOLFSSH_API int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX*,
+                                                 const byte*, word32, int);
+
+WOLFSSH_API int wolfSSH_accept(WOLFSSH*);
+WOLFSSH_API int wolfSSH_connect(WOLFSSH*);
+WOLFSSH_API int wolfSSH_shutdown(WOLFSSH*);
+WOLFSSH_API int wolfSSH_stream_read(WOLFSSH*, byte*, word32);
+WOLFSSH_API int wolfSSH_stream_send(WOLFSSH*, byte*, word32);
+WOLFSSH_API int wolfSSH_TriggerKeyExchange(WOLFSSH*);
+
+WOLFSSH_API void wolfSSH_GetStats(WOLFSSH*,
+                                  word32*, word32*, word32*, word32*);
+
+WOLFSSH_API int wolfSSH_KDF(byte, byte, byte*, word32, const byte*, word32,
+                            const byte*, word32, const byte*, word32);
+
+
+enum WS_HighwaterSide {
+    WOLFSSH_HWSIDE_TRANSMIT,
+    WOLFSSH_HWSIDE_RECEIVE
+};
+
+
+enum WS_EndpointTypes {
+    WOLFSSH_ENDPOINT_SERVER,
+    WOLFSSH_ENDPOINT_CLIENT
+};
+
+
+enum WS_FormatTypes {
+    WOLFSSH_FORMAT_ASN1,
+    WOLFSSH_FORMAT_PEM,
+    WOLFSSH_FORMAT_RAW
+};
+
+
+enum WS_UserAuthTypes {
+    WOLFSSH_USERAUTH_PASSWORD,
+    WOLFSSH_USERAUTH_PUBLICKEY
+};
+
+
+enum WS_UserAuthResults {
+    WOLFSSH_USERAUTH_SUCCESS,
+    WOLFSSH_USERAUTH_FAILURE,
+    WOLFSSH_USERAUTH_INVALID_AUTHTYPE,
+    WOLFSSH_USERAUTH_INVALID_USER,
+    WOLFSSH_USERAUTH_INVALID_PASSWORD,
+    WOLFSSH_USERAUTH_INVALID_PUBLICKEY
+};
+
+
+enum WS_DisconnectReasonCodes {
+    WOLFSSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT    = 1,
+    WOLFSSH_DISCONNECT_PROTOCOL_ERROR                 = 2,
+    WOLFSSH_DISCONNECT_KEY_EXCHANGE_FAILED            = 3,
+    WOLFSSH_DISCONNECT_RESERVED                       = 4,
+    WOLFSSH_DISCONNECT_MAC_ERROR                      = 5,
+    WOLFSSH_DISCONNECT_COMPRESSION_ERROR              = 6,
+    WOLFSSH_DISCONNECT_SERVICE_NOT_AVAILABLE          = 7,
+    WOLFSSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED = 8,
+    WOLFSSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE        = 9,
+    WOLFSSH_DISCONNECT_CONNECTION_LOST                = 10,
+    WOLFSSH_DISCONNECT_BY_APPLICATION                 = 11,
+    WOLFSSH_DISCONNECT_TOO_MANY_CONNECTIONS           = 12,
+    WOLFSSH_DISCONNECT_AUTH_CANCELLED_BY_USER         = 13,
+    WOLFSSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 14,
+    WOLFSSH_DISCONNECT_ILLEGAL_USER_NAME              = 15
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/test.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/test.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,471 @@
+/* test.h */
+
+#pragma once
+
+#ifndef _WOLFSSH_TEST_H_
+#define _WOLFSSH_TEST_H_
+
+
+#include <stdio.h>
+/*#include <stdlib.h>*/
+#include <ctype.h>
+/*#include <wolfssh/error.h>*/
+
+#ifdef USE_WINDOWS_API
+    #include <winsock2.h>
+    #include <process.h>
+    #include <assert.h>
+    #ifdef TEST_IPV6            /* don't require newer SDK for IPV4 */
+        #include <ws2tcpip.h>
+        #include <wspiapi.h>
+    #endif
+    #define SOCKET_T SOCKET
+#else /* USE_WINDOWS_API */
+    #include <unistd.h>
+    #include <netdb.h>
+    #include <netinet/in.h>
+    //#include <netinet/tcp.h>
+    #include <arpa/inet.h>
+    #include <sys/ioctl.h>
+    #include <sys/socket.h>
+    #include <pthread.h>
+    #include <fcntl.h>
+    #ifndef SO_NOSIGPIPE // FIX: ESP32
+        #include <signal.h>  /* ignore SIGPIPE */
+    #endif
+    #define SOCKET_T int
+#endif /* USE_WINDOWS_API */
+
+
+/* Socket Handling */
+#ifndef WOLFSSH_SOCKET_INVALID
+#ifdef USE_WINDOWS_API
+    #define WOLFSSH_SOCKET_INVALID  ((SOCKET_T)INVALID_SOCKET)
+#elif defined(WOLFSSH_TIRTOS)
+    #define WOLFSSH_SOCKET_INVALID  ((SOCKET_T)-1)
+#else
+    #define WOLFSSH_SOCKET_INVALID  (SOCKET_T)(0)
+#endif
+#endif /* WOLFSSH_SOCKET_INVALID */
+
+#ifndef WOLFSSL_SOCKET_IS_INVALID
+#if defined(USE_WINDOWS_API) || defined(WOLFSSL_TIRTOS)
+    #define WOLFSSL_SOCKET_IS_INVALID(s)  ((SOCKET_T)(s) == WOLFSSL_SOCKET_INVALID)
+#else
+    #define WOLFSSL_SOCKET_IS_INVALID(s)  ((SOCKET_T)(s) < WOLFSSL_SOCKET_INVALID)
+#endif
+#endif /* WOLFSSL_SOCKET_IS_INVALID */
+
+
+#if defined(__MACH__) || defined(USE_WINDOWS_API)
+    #ifndef _SOCKLEN_T
+        typedef int socklen_t;
+    #endif
+#endif
+
+
+#ifdef USE_WINDOWS_API
+    #define WCLOSESOCKET(s) closesocket(s)
+    #define WSTARTTCP() do { WSADATA wsd; WSAStartup(0x0002, &wsd); } while(0)
+#else
+    #define WCLOSESOCKET(s) close(s)
+    #define WSTARTTCP()
+#endif
+
+
+#ifdef SINGLE_THREADED
+    typedef unsigned int  THREAD_RETURN;
+    typedef void*         THREAD_TYPE;
+    #define WOLFSSH_THREAD
+#else
+    #if defined(_POSIX_THREADS) && !defined(__MINGW32__)
+        typedef void*         THREAD_RETURN;
+        typedef pthread_t     THREAD_TYPE;
+        #define WOLFSSH_THREAD
+        #define INFINITE -1
+        #define WAIT_OBJECT_0 0L
+    #else
+        typedef unsigned int  THREAD_RETURN;
+        typedef intptr_t      THREAD_TYPE;
+        #define WOLFSSH_THREAD __stdcall
+    #endif
+#endif
+
+#ifdef TEST_IPV6
+    typedef struct sockaddr_in6 SOCKADDR_IN_T;
+    #define AF_INET_V AF_INET6
+#else
+    typedef struct sockaddr_in  SOCKADDR_IN_T;
+    #define AF_INET_V AF_INET
+#endif
+
+
+#define serverKeyRsaPemFile "./keys/server-key-rsa.pem"
+
+
+typedef struct tcp_ready {
+    word16 ready;     /* predicate */
+    word16 port;
+    char* srfName;     /* server ready file name */
+#if defined(_POSIX_THREADS) && !defined(__MINGW32__)
+    pthread_mutex_t mutex;
+    pthread_cond_t  cond;
+#endif
+} tcp_ready;
+
+
+static INLINE void InitTcpReady(tcp_ready* ready)
+{
+    ready->ready = 0;
+    ready->port = 0;
+    ready->srfName = NULL;
+#ifdef SINGLE_THREADED
+#elif defined(_POSIX_THREADS) && !defined(__MINGW32__)
+    pthread_mutex_init(&ready->mutex, 0);
+    pthread_cond_init(&ready->cond, 0);
+#endif
+}
+
+
+static INLINE void FreeTcpReady(tcp_ready* ready)
+{
+#ifdef SINGLE_THREADED
+    (void)ready;
+#elif defined(_POSIX_THREADS) && !defined(__MINGW32__)
+    pthread_mutex_destroy(&ready->mutex);
+    pthread_cond_destroy(&ready->cond);
+#else
+    (void)ready;
+#endif
+}
+
+
+typedef struct func_args {
+    int    argc;
+    char** argv;
+    int    return_code;
+    tcp_ready* signal;
+    WS_CallbackUserAuth user_auth;
+} func_args;
+
+
+typedef THREAD_RETURN WOLFSSH_THREAD THREAD_FUNC(void*);
+void ThreadStart(THREAD_FUNC, void*, THREAD_TYPE*);
+void ThreadJoin(THREAD_TYPE);
+void ThreadDetach(THREAD_TYPE);
+void WaitTcpReady(func_args*);
+
+
+#ifndef TEST_IPV6
+    static const char* const wolfSshIp = "127.0.0.1";
+#else /* TEST_IPV6 */
+    static const char* const wolfSshIp = "::1";
+#endif /* TEST_IPV6 */
+static const word16 wolfSshPort = 22222;
+
+
+#ifdef __GNUC__
+    #define WS_NORETURN __attribute__((noreturn))
+#else
+    #define WS_NORETURN
+#endif
+
+static INLINE WS_NORETURN void err_sys(const char* msg)
+{
+    printf("wolfSSH error: %s\n", msg);
+
+#ifndef __GNUC__
+    /* scan-build (which pretends to be gnuc) can get confused and think the
+     * msg pointer can be null even when hardcoded and then it won't exit,
+     * making null pointer checks above the err_sys() call useless.
+     * We could just always exit() but some compilers will complain about no
+     * possible return, with gcc we know the attribute to handle that with
+     * WS_NORETURN. */
+    if (msg)
+#endif
+    {
+        exit(EXIT_FAILURE);
+    }
+}
+
+
+#define MY_EX_USAGE 2
+
+extern int   myoptind;
+extern char* myoptarg;
+
+static INLINE int mygetopt(int argc, char** argv, const char* optstring)
+{
+    static char* next = NULL;
+
+    char  c;
+    char* cp;
+
+    if (myoptind == 0)
+        next = NULL;   /* we're starting new/over */
+
+    if (next == NULL || *next == '\0') {
+        if (myoptind == 0)
+            myoptind++;
+
+        if (myoptind >= argc || argv[myoptind][0] != '-' ||
+                                argv[myoptind][1] == '\0') {
+            myoptarg = NULL;
+            if (myoptind < argc)
+                myoptarg = argv[myoptind];
+
+            return -1;
+        }
+
+        if (strcmp(argv[myoptind], "--") == 0) {
+            myoptind++;
+            myoptarg = NULL;
+
+            if (myoptind < argc)
+                myoptarg = argv[myoptind];
+
+            return -1;
+        }
+
+        next = argv[myoptind];
+        next++;                  /* skip - */
+        myoptind++;
+    }
+
+    c  = *next++;
+    /* The C++ strchr can return a different value */
+    cp = (char*)strchr(optstring, c);
+
+    if (cp == NULL || c == ':')
+        return '?';
+
+    cp++;
+
+    if (*cp == ':') {
+        if (*next != '\0') {
+            myoptarg = next;
+            next     = NULL;
+        }
+        else if (myoptind < argc) {
+            myoptarg = argv[myoptind];
+            myoptind++;
+        }
+        else
+            return '?';
+    }
+
+    return c;
+}
+
+
+#ifdef USE_WINDOWS_API
+    #pragma warning(push)
+    #pragma warning(disable:4996)
+    /* For Windows builds, disable compiler warnings for:
+    * - 4996: deprecated function */
+#endif
+
+static INLINE void build_addr(SOCKADDR_IN_T* addr, const char* peer,
+                              word16 port)
+{
+    int useLookup = 0;
+    (void)useLookup;
+
+    memset(addr, 0, sizeof(SOCKADDR_IN_T));
+
+#ifndef TEST_IPV6
+    /* peer could be in human readable form */
+    if ( ((size_t)peer != INADDR_ANY) && isalpha((int)peer[0])) {
+        #ifdef CYASSL_MDK_ARM
+            int err;
+            struct hostent* entry = gethostbyname(peer, &err);
+        #else
+            struct hostent* entry = gethostbyname(peer);
+        #endif
+
+        if (entry) {
+            memcpy(&addr->sin_addr.s_addr, entry->h_addr_list[0],
+                   entry->h_length);
+            useLookup = 1;
+        }
+        else
+            err_sys("no entry for host");
+    }
+#endif
+
+#ifndef TEST_IPV6
+    #if defined(CYASSL_MDK_ARM)
+        addr->sin_family = PF_INET;
+    #else
+        addr->sin_family = AF_INET_V;
+    #endif
+    addr->sin_port = htons(port);
+    if ((size_t)peer == INADDR_ANY)
+        addr->sin_addr.s_addr = INADDR_ANY;
+    else {
+        if (!useLookup)
+            addr->sin_addr.s_addr = inet_addr(peer);
+    }
+#else
+    addr->sin6_family = AF_INET_V;
+    addr->sin6_port = htons(port);
+    if ((size_t)peer == INADDR_ANY)
+        addr->sin6_addr = in6addr_any;
+    else {
+        #ifdef HAVE_GETADDRINFO
+            struct addrinfo  hints;
+            struct addrinfo* answer = NULL;
+            int    ret;
+            char   strPort[80];
+
+            memset(&hints, 0, sizeof(hints));
+
+            hints.ai_family   = AF_INET_V;
+            hints.ai_socktype = udp ? SOCK_DGRAM : SOCK_STREAM;
+            hints.ai_protocol = udp ? IPPROTO_UDP : IPPROTO_TCP;
+
+            WSNPRINTF(strPort, sizeof(strPort), "%d", port);
+            strPort[79] = '\0';
+
+            ret = getaddrinfo(peer, strPort, &hints, &answer);
+            if (ret < 0 || answer == NULL)
+                err_sys("getaddrinfo failed");
+
+            memcpy(addr, answer->ai_addr, answer->ai_addrlen);
+            freeaddrinfo(answer);
+        #else
+            printf("no ipv6 getaddrinfo, loopback only tests/examples\n");
+            addr->sin6_addr = in6addr_loopback;
+        #endif
+    }
+#endif
+}
+
+#ifdef USE_WINDOWS_API
+    #pragma warning(pop)
+#endif
+
+
+static INLINE void tcp_socket(SOCKET_T* sockFd)
+{
+    *sockFd = socket(AF_INET_V, SOCK_STREAM, 0);
+
+#ifdef USE_WINDOWS_API
+    if (*sockFd == INVALID_SOCKET)
+        err_sys("socket failed\n");
+#else
+    if (*sockFd < 0)
+        err_sys("socket failed\n");
+#endif
+
+#ifndef USE_WINDOWS_API
+#ifdef SO_NOSIGPIPE
+    {
+        int       on = 1;
+        socklen_t len = sizeof(on);
+        int       res = setsockopt(*sockFd, SOL_SOCKET, SO_NOSIGPIPE, &on, len);
+        if (res < 0)
+            err_sys("setsockopt SO_NOSIGPIPE failed\n");
+    }
+#elif defined(CYASSL_MDK_ARM)
+    /* nothing to define */
+#else  /* no S_NOSIGPIPE */
+    // signal(SIGPIPE, SIG_IGN); // FIX: ESP32
+#endif /* S_NOSIGPIPE */
+
+#if defined(TCP_NODELAY)
+    {
+        int       on = 1;
+        socklen_t len = sizeof(on);
+        int       res = setsockopt(*sockFd, IPPROTO_TCP, TCP_NODELAY, &on, len);
+        if (res < 0)
+            err_sys("setsockopt TCP_NODELAY failed\n");
+    }
+#endif
+#endif  /* USE_WINDOWS_API */
+}
+
+
+#ifndef XNTOHS
+    #define XNTOHS(a) ntohs((a))
+#endif
+
+
+static INLINE void tcp_listen(SOCKET_T* sockfd, word16* port, int useAnyAddr)
+{
+    SOCKADDR_IN_T addr;
+
+    /* don't use INADDR_ANY by default, firewall may block, make user switch
+       on */
+    build_addr(&addr, (useAnyAddr ? INADDR_ANY : wolfSshIp), *port);
+    tcp_socket(sockfd);
+
+#if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_MDK_ARM)\
+                              && !defined(WOLFSSL_KEIL_TCP_NET)
+    {
+        int       res, on  = 1;
+        socklen_t len = sizeof(on);
+        res = setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &on, len);
+        if (res < 0)
+            err_sys("setsockopt SO_REUSEADDR failed\n");
+    }
+#endif
+
+
+    if (bind(*sockfd, (const struct sockaddr*)&addr, sizeof(addr)) != 0)
+        err_sys("tcp bind failed");
+    if (listen(*sockfd, 5) != 0)
+        err_sys("tcp listen failed");
+    #if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_TIRTOS)
+        if (*port == 0) {
+            socklen_t len = sizeof(addr);
+            if (getsockname(*sockfd, (struct sockaddr*)&addr, &len) == 0) {
+                #ifndef TEST_IPV6
+                    *port = XNTOHS(addr.sin_port);
+                #else
+                    *port = XNTOHS(addr.sin6_port);
+                #endif
+            }
+        }
+    #endif
+}
+
+
+/* Wolf Root Directory Helper */
+/* KEIL-RL File System does not support relative directory */
+#if !defined(WOLFSSL_MDK_ARM) && !defined(WOLFSSL_KEIL_FS) && !defined(WOLFSSL_TIRTOS)
+    /* Maximum depth to search for WolfSSL root */
+    #define MAX_WOLF_ROOT_DEPTH 5
+
+    static INLINE int ChangeToWolfSshRoot(void)
+    {
+        #if !defined(NO_FILESYSTEM)
+            int depth, res;
+            WFILE* file;
+            for(depth = 0; depth <= MAX_WOLF_ROOT_DEPTH; depth++) {
+                if (WFOPEN(&file, serverKeyRsaPemFile, "rb") == 0) {
+                    WFCLOSE(file);
+                    return depth;
+                }
+            #ifdef USE_WINDOWS_API
+                res = SetCurrentDirectoryA("..\\");
+            #else
+                res = chdir("../");
+            #endif
+                if (res < 0) {
+                    printf("chdir to ../ failed!\n");
+                    break;
+                }
+            }
+
+            err_sys("wolfSSH root not found");
+            return -1;
+        #else
+            return 0;
+        #endif
+    }
+#endif /* !defined(WOLFSSL_MDK_ARM) && !defined(WOLFSSL_KEIL_FS) && !defined(WOL
+FSSL_TIRTOS) */
+
+
+#endif /* _WOLFSSH_TEST_H_ */
diff -r 000000000000 -r c4152c628df5 wolfssh/version.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/version.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,42 @@
+/* version.h.in
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The version header provides the current version number to the code
+ * based and is updated automatically by the configure script. A copy
+ * of the last generated copy of version.h is included with the
+ * distribution for environments that do not use configure.
+ */
+
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBWOLFSSH_VERSION_STRING "1.2.0"
+#define LIBWOLFSSH_VERSION_HEX 0x01002000
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/version.h.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/version.h.in	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,42 @@
+/* version.h.in
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The version header provides the current version number to the code
+ * based and is updated automatically by the configure script. A copy
+ * of the last generated copy of version.h is included with the
+ * distribution for environments that do not use configure.
+ */
+
+
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LIBWOLFSSH_VERSION_STRING "@VERSION@"
+#define LIBWOLFSSH_VERSION_HEX @HEX_VERSION@
+
+#ifdef __cplusplus
+}
+#endif
+
diff -r 000000000000 -r c4152c628df5 wolfssh/visibility.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/wolfssh/visibility.h	Mon Nov 25 14:24:05 2019 +0000
@@ -0,0 +1,79 @@
+/* visibility.h
+ *
+ * Copyright (C) 2014-2016 wolfSSL Inc.
+ *
+ * This file is part of wolfSSH.
+ *
+ * wolfSSH is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfSSH 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 wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+/*
+ * The visibility header handles the visibility of function prototypes
+ * between the local (used between modules in the library) and public
+ * (exported for the library user) APIs.
+ */
+
+
+#pragma once
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* WOLFSSH_API is used for the public API symbols.
+        It either imports or exports (or does nothing for static builds)
+
+   WOLFSSH_LOCAL is used for non-API symbols (private).
+*/
+
+#if defined(BUILDING_WOLFSSH)
+    #if defined(HAVE_VISIBILITY) && HAVE_VISIBILITY
+        #define WOLFSSH_API   __attribute__ ((visibility("default")))
+        #define WOLFSSH_LOCAL __attribute__ ((visibility("hidden")))
+    #elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
+        #define WOLFSSH_API   __global  
+        #define WOLFSSH_LOCAL __hidden
+    #elif defined(_MSC_VER)
+        #ifdef WOLFSSH_DLL
+            #define WOLFSSH_API extern __declspec(dllexport)
+        #else
+            #define WOLFSSH_API
+        #endif
+        #define WOLFSSH_LOCAL
+    #else
+        #define WOLFSSH_API
+        #define WOLFSSH_LOCAL
+    #endif /* HAVE_VISIBILITY */
+#else /* BUILDING_WOLFSSH */
+    #if defined(_MSC_VER)
+        #ifdef WOLFSSH_DLL
+            #define WOLFSSH_API extern __declspec(dllimport)
+        #else
+            #define WOLFSSH_API
+        #endif
+        #define WOLFSSH_LOCAL
+    #else
+        #define WOLFSSH_API
+        #define WOLFSSH_LOCAL
+    #endif
+#endif /* BUILDING_WOLFSSH */
+
+
+
+#ifdef __cplusplus
+}
+#endif
+