ssh

Dependents:   OS

Revision:
0:c4152c628df5
--- /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);
+}
+