diff --git a/.github/workflows/kyber.yml b/.github/workflows/kyber.yml
new file mode 100644
index 000000000..5308b7a0a
--- /dev/null
+++ b/.github/workflows/kyber.yml
@@ -0,0 +1,96 @@
+name: Kyber Tests
+
+on:
+  push:
+    branches: [ '*' ]
+  pull_request:
+    branches: [ '*' ]
+
+env:
+  LIBOQS_REF: 0.10.0
+  WOLFSSL_REF: v5.7.0-stable
+  OS_REF: ubuntu-latest
+
+jobs:
+  build_liboqs:
+    name: Build liboqs
+    runs-on: ubuntu-latest
+    timeout-minutes: 4
+    steps:
+      - name: Checking cache for liboqs
+        uses: actions/cache@v4
+        id: cache-liboqs
+        with:
+          path: build-dir/
+          key: wolfssh-kyber-liboqs-${{ env.LIBOQS_REF }}-${{ env.OS_REF }}
+          lookup-only: true
+
+      - name: Checkout liboqs
+        if: steps.cache-liboqs.outputs.cache-hit != 'true'
+        uses: actions/checkout@v4
+        with:
+          repository: open-quantum-safe/liboqs
+          ref: ${{ env.LIBOQS_REF }}
+          path: liboqs
+
+      - name: Build and install liboqs
+        if: steps.cache-liboqs.outputs.cache-hit != 'true'
+        working-directory: liboqs
+        run: |
+          mkdir build
+          cd build
+          cmake -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/build-dir -DOQS_MINIMAL_BUILD=KEM_kyber_512 -DOQS_USE_OPENSSL=0 ..
+          make
+          make install
+
+  build_wolfssl:
+    name: Build wolfssl
+    runs-on: ubuntu-latest
+    timeout-minutes: 4
+    steps:
+      - name: Checking cache for wolfssl
+        uses: actions/cache@v4
+        id: cache-wolfssl
+        with:
+          path: build-dir/
+          key: wolfssh-kyber-wolfssl-${{ env.WOLFSSL_REF }}-${{ env.OS_REF }}
+          lookup-only: true
+
+      - name: Checkout, build, and install wolfssl
+        if: steps.cache-wolfssl.outputs.cache-hit != 'true'
+        uses: wolfSSL/actions-build-autotools-project@v1
+        with:
+          repository: wolfssl/wolfssl
+          ref: ${{ env.WOLFSSL_REF }}
+          path: wolfssl
+          configure: --enable-wolfssh --enable-cryptonly --disable-examples --disable-crypttests
+          check: false
+          install: true
+
+  build_wolfssh:
+    name: Build wolfssh
+    runs-on: ubuntu-latest
+    timeout-minutes: 4
+    needs: [build_wolfssl, build_liboqs]
+    steps:
+      - name: Checking cache for liboqs
+        uses: actions/cache@v4
+        with:
+          path: build-dir/
+          key: wolfssh-kyber-liboqs-${{ env.LIBOQS_REF }}-${{ env.OS_REF }}
+          fail-on-cache-miss: true
+
+      - name: Checking cache for wolfssl
+        uses: actions/cache@v4
+        with:
+          path: build-dir/
+          key: wolfssh-kyber-wolfssl-${{ env.WOLFSSL_REF }}-${{ env.OS_REF }}
+          fail-on-cache-miss: true
+
+      - name: Checkout, build, and test wolfssh
+        uses: wolfSSL/actions-build-autotools-project@v1
+        with:
+          repository: wolfssl/wolfssh
+          path: wolfssh
+          configure: --with-liboqs=${{ github.workspace }}/build-dir --with-wolfssl=${{ github.workspace }}/build-dir
+          check: true
diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml
deleted file mode 100644
index 16145791c..000000000
--- a/.github/workflows/macos-check.yml
+++ /dev/null
@@ -1,34 +0,0 @@
-name: macOS Build Test
-
-on:
-  push:
-    branches: [ '*' ]
-  pull_request:
-    branches: [ '*' ]
-
-jobs:
-  build:
-
-    runs-on: macos-latest
-
-    steps:
-    - uses: actions/checkout@v2
-      with:
-          repository: wolfSSL/wolfssl.git
-          ref: master
-    - name: brew
-      run: brew install automake
-    - name: build wolfSSL
-      run: ./autogen.sh && ./configure --enable-ssh --enable-cryptonly && make check && sudo make install
-    - uses: actions/checkout@v2
-    - name: autogen
-      run: ./autogen.sh
-    - name: configure
-      run: ./configure
-    - name: make
-      run: make
-    - name: make check
-      run: make check
-    - name: make distcheck
-      run: make distcheck
-
diff --git a/.github/workflows/os-check.yml b/.github/workflows/os-check.yml
new file mode 100644
index 000000000..5db5697d4
--- /dev/null
+++ b/.github/workflows/os-check.yml
@@ -0,0 +1,75 @@
+name: OS Check Test
+
+on:
+  push:
+    branches: [ '*' ]
+  pull_request:
+    branches: [ '*' ]
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+env:
+  WOLFSSL_REF: v5.7.0-stable
+
+jobs:
+  build_wolfssl:
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ ubuntu-latest, macos-latest ]
+    name: Build wolfssl
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 4
+    steps:
+      - name: Checking cache for wolfssl
+        uses: actions/cache@v4
+        id: cache-wolfssl
+        with:
+          path: build-dir/
+          key: wolfssh-os-check-wolfssl-${{ env.WOLFSSL_REF }}-${{ matrix.os }}
+          lookup-only: true
+
+      - name: Checkout, build, and install wolfssl
+        if: steps.cache-wolfssl.outputs.cache-hit != 'true'
+        uses: wolfSSL/actions-build-autotools-project@v1
+        with:
+          repository: wolfssl/wolfssl
+          ref: ${{ env.WOLFSSL_REF }}
+          path: wolfssl
+          configure: --enable-all
+          check: false
+          install: true
+
+  build_wolfssh:
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ ubuntu-latest, macos-latest ]
+        config: [
+          '',
+          '--enable-all',
+          '--enable-sftp',
+          '--enable-scp',
+          '--enable-shell',
+        ]
+    name: Build wolfssh
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 4
+    needs: build_wolfssl
+    steps:
+      - name: Checking cache for wolfssl
+        uses: actions/cache@v4
+        with:
+          path: build-dir/
+          key: wolfssh-os-check-wolfssl-${{ env.WOLFSSL_REF }}-${{ matrix.os }}
+          fail-on-cache-miss: true
+
+      - name: Checkout, build, and test wolfssh
+        uses: wolfSSL/actions-build-autotools-project@v1
+        with:
+          repository: wolfssl/wolfssh
+          path: wolfssh
+          configure: ${{ matrix.config }} LDFLAGS="-L${{ github.workspace }}/build-dir/lib" CPPFLAGS="-I${{ github.workspace }}/build-dir/include"
+          check: true
diff --git a/.github/workflows/singlethread-check.yml b/.github/workflows/singlethread-check.yml
new file mode 100644
index 000000000..af56fe7e9
--- /dev/null
+++ b/.github/workflows/singlethread-check.yml
@@ -0,0 +1,75 @@
+name: Single-thread Check Test
+
+on:
+  push:
+    branches: [ '*' ]
+  pull_request:
+    branches: [ '*' ]
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+env:
+  WOLFSSL_REF: v5.7.0-stable
+
+jobs:
+  build_wolfssl:
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ ubuntu-latest, macos-latest ]
+    name: Build wolfssl
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 4
+    steps:
+      - name: Checking cache for wolfssl
+        uses: actions/cache@v4
+        id: cache-wolfssl
+        with:
+          path: build-dir/
+          key: wolfssh-singlethread-check-wolfssl-${{ env.WOLFSSL_REF }}-${{ matrix.os }}
+          lookup-only: true
+
+      - name: Checkout, build, and install wolfssl
+        if: steps.cache-wolfssl.outputs.cache-hit != 'true'
+        uses: wolfSSL/actions-build-autotools-project@v1
+        with:
+          repository: wolfssl/wolfssl
+          ref: ${{ env.WOLFSSL_REF }}
+          path: wolfssl
+          configure: --enable-wolfssh  --enable-singlethreaded --enable-keygen
+          check: false
+          install: true
+
+  build_wolfssh:
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [ ubuntu-latest, macos-latest ]
+        config: [
+          '',
+          '--enable-all',
+          '--enable-sftp',
+          '--enable-scp',
+          '--enable-shell',
+        ]
+    name: Build wolfssh
+    runs-on: ${{ matrix.os }}
+    timeout-minutes: 4
+    needs: build_wolfssl
+    steps:
+      - name: Checking cache for wolfssl
+        uses: actions/cache@v4
+        with:
+          path: build-dir/
+          key: wolfssh-singlethread-check-wolfssl-${{ env.WOLFSSL_REF }}-${{ matrix.os }}
+          fail-on-cache-miss: true
+
+      - name: Checkout, build, and test wolfssh
+        uses: wolfSSL/actions-build-autotools-project@v1
+        with:
+          repository: wolfssl/wolfssh
+          path: wolfssh
+          configure: ${{ matrix.config }} LDFLAGS="-L${{ github.workspace }}/build-dir/lib" CPPFLAGS="-I${{ github.workspace }}/build-dir/include"
+          check: true
diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml
deleted file mode 100644
index 8f10e6564..000000000
--- a/.github/workflows/ubuntu-check.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: Ubuntu Build Test
-
-on:
-  push:
-    branches: [ '*' ]
-  pull_request:
-    branches: [ '*' ]
-
-jobs:
-  build:
-
-    runs-on: ubuntu-latest
-
-    steps:
-    - uses: actions/checkout@v2
-      with:
-          repository: wolfSSL/wolfssl.git
-          ref: master
-    - name: build wolfSSL
-      run: ./autogen.sh && ./configure --enable-ssh --enable-cryptonly --prefix=/usr && make check && sudo make install
-    - uses: actions/checkout@v2
-    - name: autogen
-      run: ./autogen.sh
-    - name: configure
-      run: ./configure
-    - name: make
-      run: make
-    - name: make check
-      run: make check
-    - name: make distcheck
-      run: make distcheck
-
diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml
index 1221e120d..26750fcc6 100644
--- a/.github/workflows/zephyr.yml
+++ b/.github/workflows/zephyr.yml
@@ -16,7 +16,7 @@ jobs:
             zephyr-sdk: 0.16.1
     runs-on: ubuntu-latest
     # This should be a safe limit for the tests to run.
-    timeout-minutes: 15
+    timeout-minutes: 20
     steps:
       - name: Install dependencies
         run: |
@@ -77,6 +77,8 @@ jobs:
         run: |
           ./zephyr/scripts/twister --testsuite-root modules/lib/wolfssh --test zephyr/samples/tests/sample.lib.wolfssh_tests -vvv
           rm -rf zephyr/twister-out
+          ./zephyr/scripts/twister --testsuite-root modules/lib/wolfssh --test zephyr/samples/tests/sample.lib.wolfssh_nofs_tests -vvv
+          rm -rf zephyr/twister-out
 
       - name: Zip failure logs
         if: ${{ failure() && steps.wolfssh-test.outcome == 'failure' }}
diff --git a/ChangeLog.md b/ChangeLog.md
index 651c0f357..32500bc67 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,117 @@
+# wolfSSH v1.4.18 (July 22, 2024)
+
+## New Features
+
+- Add wolfSSL style static memory pool allocation support.
+- Add Ed25519 public key support.
+- Add Banner option to wolfSSHd configuration.
+- Add non-blocking socket support to the example SCP client.
+
+## Improvements
+
+- Documentation updates.
+- Update the Zephyr test action.
+- Add a no-filesystem build to the Zephyr port.
+- Update the macOS test action.
+- Refactor certificate processing. Only verify certificates when a signature
+  is present.
+- Update the Kyber test action.
+- Refactor the Curve25519 Key Agreement support.
+- Update the STM32Cube Pack.
+- Increase the memory that Zephyr uses for a heap for testing.
+- Add a macro wrapper to replace the ReadDir function.
+- Add callback hook for keying completion.
+- Add function to return strings for the names of algorithms.
+- Add asynchronous server side user authentication.
+- Add ssh-rsa (SHA-1) to the default user auth algorithm list when
+  sha1-soft-disable is disabled.
+- Update Espressif examples using Managed Components.
+- Add SCP test case.
+- Refactor RSA sign and verify.
+- Refresh the example echoserver with updates from wolfSSHd.
+- Add callback hooks for most channel messages including open, close, success,
+  fail, and requests.
+- Reduce the number of memory allocations SCP makes.
+- Improve wolfSSHd’s behavior on closing a connection. It closes channels and
+  waits for the peer to close the channels.
+
+## Fixes
+
+- Refactor wolfSSHd service support for Windows to fix PowerShell
+  Write-Progress.
+- Fix partial success case with public key user authentication.
+- Fix the build guards with respect to cannedKeyAlgoNames.
+- Error if unable to open the local file when doing a SCP send.
+- Fix some IPv6 related build issues.
+- Add better checks for SCP error returns for closed channels.
+- In the example SCP client, move the public key check context after the
+  WOLFSSH object is created.
+- Fix error reporting for wolfSSH_SFTP_STAT.
+- In the example SCP client, fix error code checking on shutdown.
+- Change return from wolfSSH_shutdown() to WS_CHANNEL_CLOSED.
+- Fix SFTP symlink handling.
+- Fix variable initialization warnings for Zephyr builds.
+- Fix wolfSSHd case of non-console output handles.
+- Fix testsuite for single threaded builds. Add single threaded test action.
+- Fix wolfSSHd shutting down on fcntl() failure.
+- Fix wolfSSHd on Windows handling virtual terminal sequences using exec
+  commands.
+- Fix possible null dereference when matching MAC algos during key exchange.
+
+---
+
+# wolfSSH v1.4.17 (March 25, 2024)
+
+## Vulnerabilities
+
+* Fixes a vulnerability where a properly crafted SSH client can bypass user
+  authentication in the wolfSSH server code. The added fix filters the
+  messages that are allowed during different operational states.
+
+## Notes
+
+* When building wolfSSL/wolfCrypt versions before v5.6.6 with CMake,
+  wolfSSH may have a problem with RSA keys. This is due to wolfSSH not
+  checking on the size of `___uint128_t`. wolfSSH sees the RSA structure
+  as the wrong size. You will have to define `HAVE___UINT128_T` if you
+  know you have it and are using it in wolfSSL. wolfSSL v5.6.6 exports that
+  define in options.h when using CMake.
+* The example server in directory examples/server/server.c has been removed.
+  It was never kept up to date, the echoserver did its job as an example and
+  test server.
+
+## New Features
+
+* Added functions to set algorithms lists for KEX at run-time, and some
+  functions to inspect which algorithms are set or are available to use.
+* In v1.4.15, we had disabled SHA-1 in the build by default. SHA-1 has been
+  re-enabled in the build and is now "soft" disabled, where algorithms using
+  it can be configured for KEX.
+* Add Curve25519 KEX support for server/client key agreement.
+
+## Improvements
+
+* Clean up some issues when building for Nucleus.
+* Clean up some issues when building for Windows.
+* Clean up some issues when building for QNX.
+* Added more wolfSSHd testing.
+* Added more appropriate build option guard checking.
+* General improvements for the ESP32 builds.
+* Better terminal support in Windows.
+* Better I/O pipes and return codes when running commands or scripts over an
+  SSH connection.
+
+## Fixes
+
+* Fix shell terminal window resizing and it sets up the environment better.
+* Fix some corner cases with the SFTP testing.
+* Fix some corner cases with SFTP in general.
+* Fix verifying RSA signatures.
+* Add masking of file mode bits for Zephyr.
+* Fix leak of terminal modes cache.
+
+---
+
 # wolfSSH v1.4.15 (December 22, 2023)
 
 ## Vulnerabilities
diff --git a/README.md b/README.md
index 95ff39222..29376d0bf 100644
--- a/README.md
+++ b/README.md
@@ -396,11 +396,56 @@ or define `WOLFSSH_SHELL`:
     $ ./configure --enable-shell
     $ make
 
+To try out this functionality, you can use the example echoserver and client.
+In a terminal do the following to launch the server:
+
+    $ ./examples/echoserver/echoserver -P <user>:junk
+
+And in another terminal do the following to launch the example client:
+
+    $ ./examples/client/client -t -u <user> -P junk
+
+Note that `<user>` must be the user name of the current user that is logged in.
+
 By default, the echoserver will try to start a shell. To use the echo testing
 behavior, give the echoserver the command line option `-f`.
 
     $ ./examples/echoserver/echoserver -f
 
+To use the shell feature with wolfsshd add `--enable-sshd` to your configure
+command and use the following command:
+
+    $ sudo ./apps/wolfsshd/wolfsshd -D -h keys/gretel-key-ecc.pem -p 11111
+
+If it complains about a bad `sshd_config` file, simply copy it to another file
+and remove the offending line that it complains about and use the `-f` command
+line parameter to point to the new file.
+
+You can then connect to the `wolfsshd` server with ssh:
+
+    $ ssh <user>@localhost -p 11111
+
+Note that `<user>` must be the user name of the current user that is logged in.
+
+CURVE25519
+==========
+
+wolfSSH now supports Curve25519 for key exchange. To enable this support simply
+compile wolfSSL with support for wolfssh and Curve25519.
+
+    $ cd wolfssl
+    $ ./configure --enable-wolfssh --enable-curve25519
+
+After building and installing wolfSSL, you can simply configure with no options.
+
+    $ cd wolfssh
+    $ ./configure
+
+The wolfSSH client and server will automatically negotiate using Curve25519.
+
+    $ ./examples/echoserver/echoserver -f
+
+    $ ./examples/client/client -u jill -P upthehill
 
 POST-QUANTUM
 ============
@@ -473,23 +518,27 @@ authenticating a user.
 To compile wolfSSH with X.509 support, use the `--enable-certs` build option
 or define `WOLFSSH_CERTS`:
 
-    $ ./configure --enable-certs
+    $ ./configure --enable-certs CPPFLAGS=-DWOLFSSH_NO_FPKI
     $ make
 
+For this example, we are disabling the FPKI checking as the included
+certificate for "fred" does not have the required FPKI extensions. If the
+flag WOLFSSH_NO_FPKI is removed, you can see the certificate get rejected.
+
 To provide a CA root certificate to validate a user's certificate, give the
 echoserver the command line option `-a`.
 
     $ ./examples/echoserver/echoserver -a ./keys/ca-cert-ecc.pem
 
-The echoserver and client have a fake user named "john" whose certificate
+The echoserver and client have a fake user named "fred" whose certificate
 will be used for authentication.
 
 An example echoserver / client connection using the example certificate
-john-cert.der would be:
+fred-cert.der would be:
 
-    $ ./examples/echoserver/echoserver -a ./keys/ca-cert-ecc.pem -K john:./keys/john-cert.der
+    $ ./examples/echoserver/echoserver -a ./keys/ca-cert-ecc.pem -K fred:./keys/fred-cert.der
 
-    $ ./examples/client/client -u john -J ./keys/john-cert.der -i ./keys/john-key.der
+    $ ./examples/client/client -u fred -J ./keys/fred-cert.der -i ./keys/fred-key.der
 
 
 WOLFSSH APPLICATIONS
diff --git a/apps/wolfssh/common.c b/apps/wolfssh/common.c
index f83d135f7..5d5a90d33 100644
--- a/apps/wolfssh/common.c
+++ b/apps/wolfssh/common.c
@@ -1,6 +1,6 @@
 /* common.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -451,7 +451,8 @@ int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx)
                                 current->ipString);
                             WLOG(WS_LOG_DEBUG,
                                 "\texpecting host IP : %s", (char*)ctx);
-                            if (XSTRCMP(ctx, current->ipString) == 0) {
+                            if (XSTRCMP((const char*)ctx,
+                                        current->ipString) == 0) {
                                 WLOG(WS_LOG_DEBUG, "\tmatched!");
                                 ipMatch = 1;
                             }
diff --git a/apps/wolfssh/common.h b/apps/wolfssh/common.h
index 14d45dcba..0f7b84141 100644
--- a/apps/wolfssh/common.h
+++ b/apps/wolfssh/common.h
@@ -1,6 +1,6 @@
 /* common.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/apps/wolfssh/wolfssh.c b/apps/wolfssh/wolfssh.c
index 5e216cbe7..8a749c6c2 100644
--- a/apps/wolfssh/wolfssh.c
+++ b/apps/wolfssh/wolfssh.c
@@ -1,6 +1,6 @@
 /* wolfssh.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -106,14 +106,6 @@ static const char* caCert   = NULL;
 #endif
 
 
-#if defined(WOLFSSH_AGENT)
-static inline void ato32(const byte* c, word32* u32)
-{
-    *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
-}
-#endif
-
-
 static int NonBlockSSH_connect(WOLFSSH* ssh)
 {
     int ret;
@@ -215,6 +207,13 @@ static void modes_reset(void)
 
 #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS)
 
+#if defined(WOLFSSH_AGENT)
+static inline void ato32(const byte* c, word32* u32)
+{
+    *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
+}
+#endif
+
 typedef struct thread_args {
     WOLFSSH* ssh;
     wolfSSL_Mutex lock;
@@ -404,6 +403,26 @@ static THREAD_RET readPeer(void* in)
     FD_SET(fd, &errSet);
 
 #ifdef USE_WINDOWS_API
+    if (args->rawMode == 0) {
+        DWORD wrd;
+
+        /* get console mode will fail on handles that are not a console,
+         * i.e. if the stdout is being redirected to a file */
+        if (GetConsoleMode(stdoutHandle, &wrd) != FALSE) {
+            /* depend on the terminal to process VT characters */
+        #ifndef _WIN32_WINNT_WIN10
+            /* support for virtual terminal processing was introduced in windows 10 */
+            #define _WIN32_WINNT_WIN10 0x0A00
+        #endif
+        #if defined(WINVER) && (WINVER >= _WIN32_WINNT_WIN10)
+            wrd |= (ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT);
+        #endif
+            if (SetConsoleMode(stdoutHandle, wrd) == FALSE) {
+                err_sys("Unable to set console mode");
+            }
+        }
+    }
+
     /* set handle to use for window resize */
     wc_LockMutex(&args->lock);
     wolfSSH_SetTerminalResizeCtx(args->ssh, stdoutHandle);
@@ -482,22 +501,14 @@ static THREAD_RET readPeer(void* in)
                 }
             }
             else {
+            #ifdef USE_WINDOWS_API
+                DWORD writtn = 0;
+            #endif
                 buf[bufSz - 1] = '\0';
 
             #ifdef USE_WINDOWS_API
-                if (args->rawMode == 0) {
-                    ret = wolfSSH_ConvertConsole(args->ssh, stdoutHandle, buf,
-                            ret);
-                    if (ret != WS_SUCCESS && ret != WS_WANT_READ) {
-                        err_sys("issue with print out");
-                    }
-                    if (ret == WS_WANT_READ) {
-                        ret = 0;
-                    }
-                }
-                else {
-                    printf("%s", buf);
-                    fflush(stdout);
+                if (WriteFile(stdoutHandle, buf, bufSz, &writtn, NULL) == FALSE) {
+                    err_sys("Failed to write to stdout handle");
                 }
             #else
                 if (write(STDOUT_FILENO, buf, ret) < 0) {
@@ -782,7 +793,7 @@ static int config_parse_command_line(struct config* config,
                 free(config->user);
             }
             sz = WSTRLEN(cursor);
-            config->user = WMALLOC(sz + 1, NULL, 0);
+            config->user = (char*)WMALLOC(sz + 1, NULL, 0);
             strcpy(config->user, cursor);
             cursor = found + 1;
         }
@@ -979,7 +990,8 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
         err_sys("Couldn't set the username.");
 
     build_addr(&clientAddr, config.hostname, config.port);
-    tcp_socket(&sockFd);
+    tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family);
+
     ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz);
     if (ret != 0)
         err_sys("Couldn't connect to server.");
@@ -1010,14 +1022,14 @@ static THREAD_RETURN WOLFSSH_THREAD wolfSSH_Client(void* args)
     if (ret != WS_SUCCESS)
         err_sys("Couldn't connect SSH stream.");
 
+    MODES_CLEAR();
+
 #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS)
 #if 0
     if (keepOpen) /* set up for psuedo-terminal */
         ClientSetEcho(2);
 #endif
 
-    MODES_CLEAR();
-
     if (config.command != NULL || keepOpen == 1) {
     #if defined(_POSIX_THREADS)
         thread_args arg;
diff --git a/apps/wolfsshd/auth.c b/apps/wolfsshd/auth.c
index 4be651626..f60a299ab 100644
--- a/apps/wolfsshd/auth.c
+++ b/apps/wolfsshd/auth.c
@@ -1,6 +1,6 @@
 /* auth.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/apps/wolfsshd/auth.h b/apps/wolfsshd/auth.h
index ddc6e90aa..53868da81 100644
--- a/apps/wolfsshd/auth.h
+++ b/apps/wolfsshd/auth.h
@@ -1,6 +1,6 @@
 /* auth.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/apps/wolfsshd/configuration.c b/apps/wolfsshd/configuration.c
index 76f6bef07..473b0e280 100644
--- a/apps/wolfsshd/configuration.c
+++ b/apps/wolfsshd/configuration.c
@@ -1,6 +1,6 @@
 /* configuration.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -349,9 +349,10 @@ enum {
     OPT_HOST_CERT               = 20,
     OPT_TRUSTED_USER_CA_KEYS    = 21,
     OPT_PIDFILE                 = 22,
+    OPT_BANNER                  = 23,
 };
 enum {
-    NUM_OPTIONS = 23
+    NUM_OPTIONS = 24
 };
 
 static const CONFIG_OPTION options[NUM_OPTIONS] = {
@@ -378,6 +379,7 @@ static const CONFIG_OPTION options[NUM_OPTIONS] = {
     {OPT_HOST_CERT,               "HostCertificate"},
     {OPT_TRUSTED_USER_CA_KEYS,    "TrustedUserCAKeys"},
     {OPT_PIDFILE,                 "PidFile"},
+    {OPT_BANNER,                  "Banner"},
 };
 
 /* returns WS_SUCCESS on success */
@@ -1022,6 +1024,9 @@ static int HandleConfigOption(WOLFSSHD_CONFIG** conf, int opt,
         case OPT_PIDFILE:
             ret = SetFileString(&(*conf)->pidFile, value, (*conf)->heap);
             break;
+        case OPT_BANNER:
+            ret = SetFileString(&(*conf)->banner, value, (*conf)->heap);
+            break;
         default:
             break;
     }
diff --git a/apps/wolfsshd/configuration.h b/apps/wolfsshd/configuration.h
index 68807975d..e39d9fa20 100644
--- a/apps/wolfsshd/configuration.h
+++ b/apps/wolfsshd/configuration.h
@@ -1,6 +1,6 @@
 /* configuration.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c
index a7ef2f0a5..ce0567595 100644
--- a/apps/wolfsshd/wolfsshd.c
+++ b/apps/wolfsshd/wolfsshd.c
@@ -1,6 +1,6 @@
 /* wolfsshd.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -103,7 +103,6 @@ static WFILE* logFile = NULL;
 
 /* catch interrupts and close down gracefully */
 static volatile byte quit = 0;
-static const char defaultBanner[] = "wolfSSHD\n";
 
 /* Initial connection information to pass on to threads/forks */
 typedef struct WOLFSSHD_CONNECTION {
@@ -111,7 +110,7 @@ typedef struct WOLFSSHD_CONNECTION {
     WOLFSSHD_AUTH* auth;
     int            fd;
     int            listenFd;
-    char           ip[INET_ADDRSTRLEN];
+    char           ip[INET6_ADDRSTRLEN];
     byte           isThreaded;
 } WOLFSSHD_CONNECTION;
 
@@ -152,6 +151,7 @@ static void SyslogCb(enum wolfSSH_LogLevel level, const char *const msgStr)
 
 #ifdef _WIN32
 static void ServiceDebugCb(enum wolfSSH_LogLevel level, const char* const msgStr)
+#ifdef UNICODE
 {
     WCHAR* wc;
     size_t szWord = WSTRLEN(msgStr) + 3;  /* + 3 for null terminator and new
@@ -171,7 +171,13 @@ static void ServiceDebugCb(enum wolfSSH_LogLevel level, const char* const msgStr
     }
     WOLFSSH_UNUSED(level);
 }
+#else
+{
+    OutputDebugString(msgStr);
+    WOLFSSH_UNUSED(level);
+}
 #endif
+#endif /* _WIN32 */
 
 static void ShowUsage(void)
 {
@@ -216,16 +222,6 @@ static void wolfSSHDLoggingCb(enum wolfSSH_LogLevel lvl, const char *const str)
 }
 
 
-/* Frees up the WOLFSSH_CTX struct */
-static void CleanupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx)
-{
-    if (ctx != NULL && *ctx != NULL) {
-        wolfSSH_CTX_free(*ctx);
-        *ctx = NULL;
-    }
-    (void)conf;
-}
-
 #ifndef NO_FILESYSTEM
 static void freeBufferFromFile(byte* buf, void* heap)
 {
@@ -247,7 +243,7 @@ static byte* getBufferFromFile(const char* fileName, word32* bufSz, void* heap)
 
     if (WFOPEN(NULL, &file, fileName, "rb") != 0)
         return NULL;
-    WFSEEK(NULL, file, 0, XSEEK_END);
+    WFSEEK(NULL, file, 0, WSEEK_END);
     fileSz = (word32)WFTELL(NULL, file);
     WREWIND(NULL, file);
 
@@ -259,7 +255,8 @@ static byte* getBufferFromFile(const char* fileName, word32* bufSz, void* heap)
             WFREE(buf, heap, DYNTYPE_SSHD);
             return NULL;
         }
-        *bufSz = readSz;
+        if (bufSz)
+            *bufSz = readSz;
         WFCLOSE(NULL, file);
     }
 
@@ -273,13 +270,30 @@ static int UserAuthResult(byte result,
         WS_UserAuthData* authData, void* userAuthResultCtx);
 
 
+/* Frees up the WOLFSSH_CTX struct */
+static void CleanupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
+        byte** banner)
+{
+    if (banner != NULL && *banner != NULL) {
+#ifndef NO_FILESYSTEM
+        freeBufferFromFile(*banner, NULL);
+#endif
+        *banner = NULL;
+    }
+    if (ctx != NULL && *ctx != NULL) {
+        wolfSSH_CTX_free(*ctx);
+        *ctx = NULL;
+    }
+    (void)conf;
+}
+
 /* Initializes and sets up the WOLFSSH_CTX struct based on the configure options
  * return WS_SUCCESS on success
  */
-static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx)
+static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx,
+        byte** banner)
 {
     int ret = WS_SUCCESS;
-    const char* banner;
     DerBuffer* der = NULL;
     byte* privBuf;
     word32 privBufSz;
@@ -304,11 +318,13 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx)
 
     /* set banner to display on connection */
     if (ret == WS_SUCCESS) {
-        banner = wolfSSHD_ConfigGetBanner(conf);
-        if (banner == NULL) {
-            banner = defaultBanner;
+#ifndef NO_FILESYSTEM
+        *banner = getBufferFromFile(wolfSSHD_ConfigGetBanner(conf),
+                NULL, heap);
+#endif
+        if (*banner) {
+            wolfSSH_CTX_SetBanner(*ctx, (char*)*banner);
         }
-        wolfSSH_CTX_SetBanner(*ctx, banner);
     }
 
     /* Load in host private key */
@@ -665,7 +681,6 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
     }
 
     if (ret == WS_SUCCESS) {
-        r[rSz] = '\0';
         wolfSSH_Log(WS_LOG_INFO,
             "[SSHD] Using directory %s for SFTP connection", r);
         if (wolfSSH_SFTP_SetDefaultPath(ssh, r) != WS_SUCCESS) {
@@ -806,8 +821,6 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
     int cnt_r, cnt_w;
     HANDLE ptyIn = NULL, ptyOut = NULL;
     HANDLE cnslIn = NULL, cnslOut = NULL;
-    HPCON pCon = 0;
-    COORD cord;
     STARTUPINFOEX ext;
     PCWSTR sysCmd = L"c:\\windows\\system32\\cmd.exe";
 #if 0
@@ -825,7 +838,6 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
 
     /* @TODO check for conpty support LoadLibrary()and GetProcAddress(). */
 
-
     if (forcedCmd != NULL && WSTRCMP(forcedCmd, "internal-sftp") == 0) {
         wolfSSH_Log(WS_LOG_ERROR,
             "[SSHD] Only SFTP connections allowed for user "
@@ -865,7 +877,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
                 }
 
                 if (ret == WS_SUCCESS) {
-                    swprintf(cmd, cmdSz, L"%s /C \"%s\"", sysCmd, tmp);
+                    swprintf(cmd, cmdSz, L"%s /C %s", sysCmd, tmp);
                 }
 
                 if (tmp != NULL) {
@@ -898,259 +910,241 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
         }
     }
 
-    if (ret == WS_SUCCESS) {
-        HRESULT err;
-
-        CreatePipe(&cnslIn, &ptyIn, NULL, 0);
-        CreatePipe(&ptyOut, &cnslOut, NULL, 0);
+    if (ImpersonateLoggedOnUser(wolfSSHD_GetAuthToken(conn->auth)) == FALSE) {
+        ret = WS_FATAL_ERROR;
+    }
 
-        cord.X = ssh->widthChar;
-        cord.Y = ssh->heightRows;
+    if (ret == WS_SUCCESS) {
+        SECURITY_ATTRIBUTES saAttr;
 
-        /* Sanity check on cord values, if 0 than assume was not set.
-         * (can happen with exec and not req-pty message)
-         * If not set yet then use sane default values. */
-        if (cord.X == 0) {
-            cord.X = 80;
-        }
+        ZeroMemory(&saAttr, sizeof(saAttr));
+        saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+        saAttr.bInheritHandle = TRUE;
+        saAttr.lpSecurityDescriptor = NULL;
 
-        if (cord.Y == 0) {
-            cord.Y = 24;
+        if (CreatePipe(&cnslIn, &ptyIn, &saAttr, 0) != TRUE) {
+            ret = WS_FATAL_ERROR;
         }
 
-        err = CreatePseudoConsole(cord, cnslIn, cnslOut, 0, &pCon);
-        if (err != S_OK) {
-            wolfSSH_Log(WS_LOG_ERROR,
-                "[SSHD] Issue creating pseudo console");
+        if (CreatePipe(&ptyOut, &cnslOut, &saAttr, 0) != TRUE) {
             ret = WS_FATAL_ERROR;
         }
-        else {
-            CloseHandle(cnslIn);
-            CloseHandle(cnslOut);
-            wolfSSH_SetTerminalResizeCtx(ssh, (void*)&pCon);
-        }
     }
 
-    /* setup startup extended info for pseudo terminal */
     if (ret == WS_SUCCESS) {
-        ext.StartupInfo.cb = sizeof(STARTUPINFOEX);
-        (void)InitializeProcThreadAttributeList(NULL, 1, 0, &sz);
-        if (sz == 0) {
-            ret = WS_FATAL_ERROR;
-        }
+        STARTUPINFOW si;
+        PCWSTR conCmd = L"wolfsshd.exe -r ";
+        PWSTR conCmdPtr;
+        size_t conCmdSz;
 
-        if (ret == WS_SUCCESS) {
-            /* Using HeapAlloc for better support when possibly passing
-               memory between Windows Modules */
-            ext.lpAttributeList =
-                (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sz);
-            if (ext.lpAttributeList == NULL) {
-                wolfSSH_Log(WS_LOG_ERROR,
-                    "[SSHD] Issue getting memory for attribute list");
-                ret = WS_FATAL_ERROR;
-            }
-        }
+        SetHandleInformation(ptyIn, HANDLE_FLAG_INHERIT, 0);
+        SetHandleInformation(ptyOut, HANDLE_FLAG_INHERIT, 0);
 
-        if (ret == WS_SUCCESS) {
-            if (InitializeProcThreadAttributeList(ext.lpAttributeList, 1, 0,
-                    &sz) != TRUE) {
-                wolfSSH_Log(WS_LOG_ERROR,
-                    "[SSHD] Issue initializing proc thread attribute");
-                ret = WS_FATAL_ERROR;
-            }
-        }
+        wolfSSH_SetTerminalResizeCtx(ssh, (void*)&ptyIn);
 
-        if (ret == WS_SUCCESS) {
-            if (UpdateProcThreadAttribute(ext.lpAttributeList, 0,
-                    PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
-                    pCon, sizeof(HPCON), NULL, NULL) != TRUE) {
-                wolfSSH_Log(WS_LOG_ERROR,
-                    "[SSHD] Issue updating proc thread attribute");
-                ret = WS_FATAL_ERROR;
-            }
+        conCmdSz = wcslen(conCmd) + cmdSz + 3;
+            /* +1 for terminator, +2 for quotes */
+        conCmdPtr = (PWSTR)WMALLOC(sizeof(wchar_t) * conCmdSz,
+                NULL, DYNTYPE_SSHD);
+        if (conCmdPtr == NULL) {
+            ret = WS_MEMORY_E;
+        }
+        else {
+            _snwprintf_s(conCmdPtr, conCmdSz, conCmdSz,
+                L"wolfsshd.exe -r \"%s\"", cmd);
         }
-    }
 
+        ZeroMemory(&si, sizeof(si));
+        si.cb = sizeof(si);
 
-    if (ret == WS_SUCCESS) {
-#if 1
-        if (CreateProcessAsUserW(wolfSSHD_GetAuthToken(conn->auth), NULL, cmd,
-            NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, h,
-            &ext.StartupInfo, &processInfo) != TRUE) {
+        si.hStdInput  = cnslIn;
+        si.hStdOutput = cnslOut;
+        si.hStdError  = cnslOut;
+        si.dwFlags    = STARTF_USESTDHANDLES;
+        si.lpDesktop  = NULL;
+
+        if (CreateProcessAsUserW(wolfSSHD_GetAuthToken(conn->auth), NULL, conCmdPtr,
+            NULL, NULL, TRUE, DETACHED_PROCESS, NULL, h,
+            &si, &processInfo) != TRUE) {
             wolfSSH_Log(WS_LOG_ERROR,
                 "[SSHD] Issue creating process, Windows error %d", GetLastError());
             return WS_FATAL_ERROR;
         }
-#else
-        /* Needs enabled when running as non-service, compiled out for now to
-         * make sure it can not accidentally be used since the permissions of
-         * the created process match the current process. */
-        if (CreateProcessW(NULL, cmd, NULL, NULL, FALSE,
-            EXTENDED_STARTUPINFO_PRESENT, NULL, h, &ext.StartupInfo, &processInfo)
-            != TRUE) {
-            wolfSSH_Log(WS_LOG_ERROR,
-                "[SSHD] Issue creating process, windows error %d", WSAGetLastError());
-            if (cmd != NULL) {
-                WFREE(cmd, NULL, DYNTYPE_SSHD);
-            }
-            return WS_FATAL_ERROR;
-        }
-#endif
-        else {
-            SOCKET sshFd;
-            byte tmp[2];
-            fd_set readFds;
-            WS_SOCKET_T maxFd;
-            int pending = 0;
-            int readPending = 0;
-            int rc = 0;
-            DWORD processState;
-            DWORD ava;
-            struct timeval t;
 
-            t.tv_sec  = 0;
-            t.tv_usec = 800;
+        CloseHandle(cnslIn);
+        CloseHandle(cnslOut);
 
-            sshFd = wolfSSH_get_fd(ssh);
-            maxFd = sshFd;
+        WFREE(conCmdPtr, NULL, DYNTYPE_SSHD);
+    }
 
-            FD_ZERO(&readFds);
-            FD_SET(sshFd, &readFds);
+    if (ret == WS_SUCCESS) {
+        char cmdWSize[20];
+        int cmdWSizeSz = 20;
+        DWORD wrtn = 0;
 
-            wolfSSH_Log(WS_LOG_INFO, "[SSHD] Successfully created process for "
-                "console, waiting for it to start");
+        wolfSSH_Log(WS_LOG_INFO, "[SSHD] Successfully created process for "
+            "console, waiting for it to start");
 
-            WaitForInputIdle(processInfo.hProcess, 1000);
+        WaitForInputIdle(processInfo.hProcess, 1000);
 
-            do {
-                /* @TODO currently not blocking till data comes in */
-                if (PeekNamedPipe(ptyOut, NULL, 0, NULL, &ava, NULL) == TRUE) {
-                    if (ava > 0) {
-                        readPending = 1;
-                    }
+        /* Send initial terminal size to pseudo console with VT control sequence */
+        cmdWSizeSz = snprintf(cmdWSize, cmdWSizeSz, "\x1b[8;%d;%dt", ssh->heightRows, ssh->widthChar);
+        if (WriteFile(ptyIn, cmdWSize, cmdWSizeSz, &wrtn, 0) != TRUE) {
+            WLOG(WS_LOG_ERROR, "Issue with pseudo console resize");
+            ret = WS_FATAL_ERROR;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        SOCKET sshFd;
+        byte tmp[2];
+        fd_set readFds;
+        WS_SOCKET_T maxFd;
+        int pending = 0;
+        int readPending = 0;
+        int rc = 0;
+        DWORD processState;
+        DWORD ava;
+        struct timeval t;
+
+        t.tv_sec = 0;
+        t.tv_usec = 800;
+
+        sshFd = wolfSSH_get_fd(ssh);
+        maxFd = sshFd;
+
+        FD_ZERO(&readFds);
+        FD_SET(sshFd, &readFds);
+
+        do {
+            /* @TODO currently not blocking till data comes in */
+            if (PeekNamedPipe(ptyOut, NULL, 0, NULL, &ava, NULL) == TRUE) {
+                if (ava > 0) {
+                    readPending = 1;
                 }
+            }
 
-                if (readPending == 0) {
-                    /* check if process is still running before waiting to read */
-                    if (GetExitCodeProcess(processInfo.hProcess, &processState) 
+            if (readPending == 0) {
+                /* check if process is still running before waiting to read */
+                if (GetExitCodeProcess(processInfo.hProcess, &processState)
+                    == TRUE) {
+                    if (processState != STILL_ACTIVE) {
+                        wolfSSH_Log(WS_LOG_INFO,
+                            "[SSHD] Process has exited, exit state = %d, "
+                            "close down SSH connection", processState);
+                        Sleep(100); /* give the stdout/stderr of process a
+                                     * little time to write to pipe */
+                        if (PeekNamedPipe(ptyOut, NULL, 0, NULL, &ava, NULL)
                             == TRUE) {
-                        if (processState != STILL_ACTIVE) {
-                            wolfSSH_Log(WS_LOG_INFO,
-                                "[SSHD] Process has exited, exit state = %d, "
-                                "close down SSH connection", processState);
-                            Sleep(100); /* give the stdout/stderr of process a
-                                         * little time to write to pipe */
-                            if (PeekNamedPipe(ptyOut, NULL, 0, NULL, &ava, NULL)
-                                == TRUE) {
-                                if (ava > 0) {
-                                    /* if data still pending then continue
-                                     * sending it over SSH */
-                                    readPending = 1;
-                                    continue;
-                                }
+                            if (ava > 0) {
+                                /* if data still pending then continue
+                                 * sending it over SSH */
+                                readPending = 1;
+                                continue;
                             }
-                            break;
                         }
+                        break;
                     }
-                    if (wolfSSH_stream_peek(ssh, tmp, 1) <= 0) {
-                        rc = select((int)maxFd + 1, &readFds, NULL, NULL, &t);
-                        if (rc == -1) {
-                            wolfSSH_Log(WS_LOG_INFO,
-                                "[SSHD] select call waiting on socket failed");
-                            break;
-                        }
-                        /* when select times out and no socket is set as ready
-                           Windows overwrites readFds with 0. Reset the fd here
-                           for next select call */
-                        if (rc == 0) {
-                            FD_SET(sshFd, &readFds);
-                        }
+                }
+                if (wolfSSH_stream_peek(ssh, tmp, 1) <= 0) {
+                    rc = select((int)maxFd + 1, &readFds, NULL, NULL, &t);
+                    if (rc == -1) {
+                        wolfSSH_Log(WS_LOG_INFO,
+                            "[SSHD] select call waiting on socket failed");
+                        break;
                     }
-                    else {
-                        pending = 1;
+                    /* when select times out and no socket is set as ready
+                       Windows overwrites readFds with 0. Reset the fd here
+                       for next select call */
+                    if (rc == 0) {
+                        FD_SET(sshFd, &readFds);
                     }
                 }
+                else {
+                    pending = 1;
+                }
+            }
 
-                if (rc != 0 && (pending || FD_ISSET(sshFd, &readFds))) {
-                    word32 lastChannel = 0;
-
-                    /* The following tries to read from the first channel inside
-                       the stream. If the pending data in the socket is for
-                       another channel, this will return an error with id
-                       WS_CHAN_RXD. That means the agent has pending data in its
-                       channel. The additional channel is only used with the
-                       agent. */
-                    cnt_r = wolfSSH_worker(ssh, &lastChannel);
-                    if (cnt_r < 0) {
-                        rc = wolfSSH_get_error(ssh);
-                        if (rc == WS_CHAN_RXD) {
-                            if (lastChannel == shellChannelId) {
-                                cnt_r = wolfSSH_ChannelIdRead(ssh,
-                                    shellChannelId, shellBuffer,
-                                    sizeof shellBuffer);
-                                if (cnt_r <= 0)
-                                    break;
-                                pending = 0;
-                                if (WriteFile(ptyIn, shellBuffer, cnt_r, &cnt_r,
-                                    NULL) != TRUE) {
-                                    wolfSSH_Log(WS_LOG_INFO,
-                                        "[SSHD] Error writing to pipe for "
-                                        "console");
-                                    break;
-                                }
+            if (rc != 0 && (pending || FD_ISSET(sshFd, &readFds))) {
+                word32 lastChannel = 0;
+
+                /* The following tries to read from the first channel inside
+                   the stream. If the pending data in the socket is for
+                   another channel, this will return an error with id
+                   WS_CHAN_RXD. That means the agent has pending data in its
+                   channel. The additional channel is only used with the
+                   agent. */
+                cnt_r = wolfSSH_worker(ssh, &lastChannel);
+                if (cnt_r < 0) {
+                    rc = wolfSSH_get_error(ssh);
+                    if (rc == WS_CHAN_RXD) {
+                        if (lastChannel == shellChannelId) {
+                            cnt_r = wolfSSH_ChannelIdRead(ssh,
+                                shellChannelId, shellBuffer,
+                                sizeof shellBuffer);
+                            if (cnt_r <= 0)
+                                break;
+                            pending = 0;
+                            if (WriteFile(ptyIn, shellBuffer, cnt_r, &cnt_r,
+                                NULL) != TRUE) {
+                                wolfSSH_Log(WS_LOG_INFO,
+                                    "[SSHD] Error writing to pipe for "
+                                    "console");
+                                break;
                             }
                         }
-                        else if (rc == WS_CHANNEL_CLOSED) {
-                            continue;
-                        }
-                        else if (rc != WS_WANT_READ) {
-                            break;
-                        }
+                    }
+                    else if (rc == WS_CHANNEL_CLOSED) {
+                        continue;
+                    }
+                    else if (rc != WS_WANT_READ) {
+                        break;
                     }
                 }
+            }
 
-                if (readPending) {
-                    WMEMSET(shellBuffer, 0, EXAMPLE_BUFFER_SZ);
+            if (readPending) {
+                WMEMSET(shellBuffer, 0, EXAMPLE_BUFFER_SZ);
 
-                    if (ReadFile(ptyOut, shellBuffer, EXAMPLE_BUFFER_SZ, &cnt_r,
-                            NULL) != TRUE) {
-                        wolfSSH_Log(WS_LOG_INFO,
-                            "[SSHD] Error reading from pipe for console");
-                        break;
-                    }
-                    else {
-                        readPending = 0;
-                        if (cnt_r > 0) {
-                            cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId,
-                                shellBuffer, cnt_r);
-                            if (cnt_w < 0)
-                                break;
-                        }
+                if (ReadFile(ptyOut, shellBuffer, EXAMPLE_BUFFER_SZ, &cnt_r,
+                    NULL) != TRUE) {
+                    wolfSSH_Log(WS_LOG_INFO,
+                        "[SSHD] Error reading from pipe for console");
+                    break;
+                }
+                else {
+                    readPending = 0;
+                    if (cnt_r > 0) {
+                        cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId,
+                            shellBuffer, cnt_r);
+                        if (cnt_w < 0)
+                            break;
                     }
                 }
-            } while (1);
-
-            if (cmd != NULL) {
-                WFREE(cmd, NULL, DYNTYPE_SSHD);
             }
-            wolfSSH_Log(WS_LOG_INFO,
-                "[SSHD] Closing down process for console");
+        } while (1);
 
-            if (ext.lpAttributeList != NULL) {
-                HeapFree(GetProcessHeap(), 0, ext.lpAttributeList);
-            }
+        if (cmd != NULL) {
+            WFREE(cmd, NULL, DYNTYPE_SSHD);
+        }
+        wolfSSH_Log(WS_LOG_INFO,
+            "[SSHD] Closing down process for console");
 
-            if (wolfSSH_SetExitStatus(ssh, processState) !=
-                    WS_SUCCESS) {
-                wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue sending childs exit "
-                    "status");
-            }
+        if (ext.lpAttributeList != NULL) {
+            HeapFree(GetProcessHeap(), 0, ext.lpAttributeList);
+        }
 
-            ClosePseudoConsole(pCon);
-            CloseHandle(processInfo.hThread);
-            CloseHandle(wolfSSHD_GetAuthToken(conn->auth));
+        if (wolfSSH_SetExitStatus(ssh, processState) !=
+            WS_SUCCESS) {
+            wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue sending childs exit "
+                "status");
         }
+
+        CloseHandle(processInfo.hThread);
+        CloseHandle(wolfSSHD_GetAuthToken(conn->auth));
     }
+
+    RevertToSelf();
     return ret;
 }
 #else
@@ -1180,7 +1174,14 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
     byte channelBuffer[EXAMPLE_BUFFER_SZ];
     char* forcedCmd;
     int   windowFull = 0;
-    int   idle = 0;
+    int   peerConnected = 1;
+    int   stdoutEmpty = 0;
+
+    childFd = -1;
+    stdoutPipe[0] = -1;
+    stdoutPipe[1] = -1;
+    stderrPipe[0] = -1;
+    stderrPipe[1] = -1;
 
     forcedCmd = wolfSSHD_ConfigGetForcedCmd(usrConf);
 
@@ -1190,7 +1191,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
         forcedCmd = (char*)subCmd;
     }
 
-    if (forcedCmd != NULL && XSTRCMP(forcedCmd, "internal-sftp") == 0) {
+    if (forcedCmd != NULL && WSTRCMP(forcedCmd, "internal-sftp") == 0) {
         wolfSSH_Log(WS_LOG_ERROR,
                                 "[SSHD] Only SFTP connections allowed for user "
                                 "%s", wolfSSH_GetUsername(ssh));
@@ -1238,6 +1239,8 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
         if (forcedCmd) {
             close(stdoutPipe[0]);
             close(stderrPipe[0]);
+            stdoutPipe[0] = -1;
+            stderrPipe[0] = -1;
             if (dup2(stdoutPipe[1], STDOUT_FILENO) == -1) {
                 wolfSSH_Log(WS_LOG_ERROR,
                     "[SSHD] Error redirecting stdout pipe");
@@ -1310,9 +1313,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
         setenv("SHELL", pPasswd->pw_shell, 1);
 
         if (pPasswd->pw_shell) {
-            word32 shellSz = (word32)WSTRLEN(pPasswd->pw_shell);
-
-            if (shellSz < sizeof(shell)) {
+            if (WSTRLEN(pPasswd->pw_shell) < sizeof(shell)) {
                 char* cursor;
                 char* start;
 
@@ -1335,12 +1336,11 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
         }
 
         /* default to /bin/sh if user shell is not set */
-        WMEMSET(cmd, 0, sizeof(cmd));
-        if (XSTRLEN(pPasswd->pw_shell) == 0) {
-            XSNPRINTF(cmd, sizeof(cmd), "%s", "/bin/sh");
+        if (pPasswd->pw_shell && WSTRLEN(pPasswd->pw_shell)) {
+            WSNPRINTF(cmd, sizeof(cmd), "%s", pPasswd->pw_shell);
         }
         else {
-            XSNPRINTF(cmd, sizeof(cmd),"%s", pPasswd->pw_shell);
+            WSNPRINTF(cmd, sizeof(cmd), "%s", "/bin/sh");
         }
 
         errno = 0;
@@ -1391,8 +1391,9 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
 #if defined(HAVE_SYS_IOCTL_H)
     wolfSSH_DoModes(ssh->modes, ssh->modesSz, childFd);
     {
-        struct winsize s = {0};
+        struct winsize s;
 
+        WMEMSET(&s, 0, sizeof(s));
         s.ws_col = ssh->widthChar;
         s.ws_row = ssh->heightRows;
         s.ws_xpixel = ssh->widthPixels;
@@ -1408,20 +1409,24 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
         close(stderrPipe[1]);
     }
 
-    while (idle < MAX_IDLE_COUNT) {
+    while (ChildRunning || windowFull || !stdoutEmpty || peerConnected) {
         byte tmp[2];
         fd_set readFds;
+        fd_set writeFds;
         WS_SOCKET_T maxFd;
         int cnt_r;
         int cnt_w;
         int pending = 0;
 
-        idle++; /* increment idle count, gets reset if not idle */
-
         FD_ZERO(&readFds);
         FD_SET(sshFd, &readFds);
         maxFd = sshFd;
 
+        FD_ZERO(&writeFds);
+        if (windowFull) {
+            FD_SET(sshFd, &writeFds);
+        }
+
         /* select on stdout/stderr pipes with forced commands */
         if (forcedCmd) {
             FD_SET(stdoutPipe[0], &readFds);
@@ -1439,18 +1444,18 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
         }
 
         if (wolfSSH_stream_peek(ssh, tmp, 1) <= 0) {
-            rc = select((int)maxFd + 1, &readFds, NULL, NULL, NULL);
+            rc = select((int)maxFd + 1, &readFds, &writeFds, NULL, NULL);
             if (rc == -1)
                 break;
         }
         else {
             pending = 1; /* found some pending SSH data */
-            idle    = 0;
         }
 
         if (windowFull || pending || FD_ISSET(sshFd, &readFds)) {
             word32 lastChannel = 0;
 
+            windowFull = 0;
             /* The following tries to read from the first channel inside
                the stream. If the pending data in the socket is for
                another channel, this will return an error with id
@@ -1461,7 +1466,6 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
             if (cnt_r < 0) {
                 rc = wolfSSH_get_error(ssh);
                 if (rc == WS_CHAN_RXD) {
-                    idle = 0;
                     if (lastChannel == shellChannelId) {
                         cnt_r = wolfSSH_ChannelIdRead(ssh, shellChannelId,
                                 channelBuffer,
@@ -1475,6 +1479,11 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
                     }
                 }
                 else if (rc == WS_CHANNEL_CLOSED) {
+                    peerConnected = 0;
+                    continue;
+                }
+                else if (rc == WS_WANT_WRITE) {
+                    windowFull = 1;
                     continue;
                 }
                 else if (rc != WS_WANT_READ) {
@@ -1489,7 +1498,10 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
                     shellBuffer, cnt_r);
             if (cnt_w == WS_WINDOW_FULL) {
                 windowFull = 1;
-                idle = 0;
+                continue;
+            }
+            else if (cnt_w == WS_WANT_WRITE) {
+                windowFull = 1;
                 continue;
             }
             else {
@@ -1510,13 +1522,16 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
                 }
                 else {
                     if (cnt_r > 0) {
-                        idle = 0;
                         cnt_w = wolfSSH_extended_data_send(ssh, shellBuffer,
                             cnt_r);
                         if (cnt_w == WS_WINDOW_FULL) {
                             windowFull = 1;
                             continue;
                         }
+                        else if (cnt_w == WS_WANT_WRITE) {
+                            windowFull = 1;
+                            continue;
+                        }
                         else if (cnt_w < 0)
                             break;
                     }
@@ -1528,23 +1543,31 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
                 cnt_r = (int)read(stdoutPipe[0], shellBuffer,
                     sizeof shellBuffer);
                 /* This read will return 0 on EOF */
-                if (cnt_r <= 0) {
+                if (cnt_r < 0) {
                     int err = errno;
                     if (err != EAGAIN && err != 0) {
                         break;
                     }
                 }
+                else if (cnt_r == 0) {
+                    stdoutEmpty = 1;
+                }
                 else {
                     if (cnt_r > 0) {
-                        idle = 0;
                         cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId,
                                 shellBuffer, cnt_r);
                         if (cnt_w == WS_WINDOW_FULL) {
                             windowFull = 1;
                             continue;
                         }
-                        else if (cnt_w < 0)
+                        else if (cnt_w == WS_WANT_WRITE) {
+                            windowFull = 1;
+                            continue;
+                        }
+                        else if (cnt_w < 0) {
+                            kill(childPid, SIGINT);
                             break;
+                        }
                     }
                 }
             }
@@ -1561,22 +1584,27 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh,
                 }
                 else {
                     if (cnt_r > 0) {
-                        idle = 0;
                         cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId,
                                 shellBuffer, cnt_r);
                         if (cnt_w == WS_WINDOW_FULL) {
                             windowFull = 1;
                             continue;
                         }
-                        else if (cnt_w < 0)
+                        else if (cnt_w == WS_WANT_WRITE) {
+                            windowFull = 1;
+                            continue;
+                        }
+                        else if (cnt_w < 0) {
+                            kill(childPid, SIGINT);
                             break;
+                        }
                     }
                 }
             }
         }
 
-        if (ChildRunning && idle) {
-            idle = 0; /* waiting on child process */
+        if (!ChildRunning && peerConnected && stdoutEmpty && !windowFull) {
+            peerConnected = 0;
         }
     }
 
@@ -1889,7 +1917,7 @@ static void* HandleConnection(void* arg)
             #ifdef _WIN32
                 Sleep(1);
             #else
-                usleep(1);
+                usleep(100000);
             #endif
             }
 
@@ -1903,6 +1931,13 @@ static void* HandleConnection(void* arg)
     /* check if there is a response to the shutdown */
     wolfSSH_free(ssh);
     if (conn != NULL) {
+        byte sc[1024];
+        shutdown(conn->fd, 1);
+        /* Spin until socket closes. */
+        do {
+            ret = (int)recv(conn->fd, sc, 1024, 0);
+        } while (ret > 0);
+
         WCLOSESOCKET(conn->fd);
     }
     wolfSSH_Log(WS_LOG_INFO, "[SSHD] Return from closing connection = %d", ret);
@@ -2058,7 +2093,7 @@ static char* _convertHelper(WCHAR* in, void* heap) {
     if (ret != NULL) {
         size_t numConv = 0;
         if (wcstombs_s(&numConv, ret, retSz, in, retSz) != 0) {
-            XFREE(ret, heap, DYNTYPE_SSHD);
+            WFREE(ret, heap, DYNTYPE_SSHD);
             ret = NULL;
         }
     }
@@ -2082,6 +2117,7 @@ static int StartSSHD(int argc, char** argv)
 
     const char* configFile = "/etc/ssh/sshd_config";
     const char* hostKeyFile = NULL;
+    byte* banner = NULL;
 
     logFile = stderr;
     wolfSSH_SetLoggingCb(wolfSSHDLoggingCb);
@@ -2256,7 +2292,7 @@ static int StartSSHD(int argc, char** argv)
 
     if (ret == WS_SUCCESS) {
         wolfSSH_Log(WS_LOG_INFO, "[SSHD] Starting wolfSSH SSHD application");
-        ret = SetupCTX(conf, &ctx);
+        ret = SetupCTX(conf, &ctx, &banner);
     }
 
     if (ret == WS_SUCCESS) {
@@ -2345,21 +2381,21 @@ static int StartSSHD(int argc, char** argv)
                 wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue updating service status");
             }
         }
-
-        /* Create a stop event to watch on */
-        serviceStop = CreateEvent(NULL, TRUE, FALSE, NULL);
-        if (serviceStop == NULL) {
-            serviceStatus.dwControlsAccepted = 0;
-            serviceStatus.dwCurrentState = SERVICE_STOPPED;
-            serviceStatus.dwWin32ExitCode = GetLastError();
-            serviceStatus.dwCheckPoint = 1;
-
-            if (SetServiceStatus(serviceStatusHandle, &serviceStatus) == FALSE) {
-                wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue updating service status");
+        if (ret == WS_SUCCESS) {
+            /* Create a stop event to watch on */
+            serviceStop = CreateEvent(NULL, TRUE, FALSE, NULL);
+            if (serviceStop == NULL) {
+                serviceStatus.dwControlsAccepted = 0;
+                serviceStatus.dwCurrentState = SERVICE_STOPPED;
+                serviceStatus.dwWin32ExitCode = GetLastError();
+                serviceStatus.dwCheckPoint = 1;
+
+                if (SetServiceStatus(serviceStatusHandle, &serviceStatus) == FALSE) {
+                    wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue updating service status");
+                }
+                return;
             }
-            return;
         }
-
         if (cmdArgs != NULL) {
             LocalFree(cmdArgs);
         }
@@ -2395,8 +2431,8 @@ static int StartSSHD(int argc, char** argv)
 #ifdef WOLFSSL_NUCLEUS
             struct addr_struct clientAddr;
 #else
-            SOCKADDR_IN_T clientAddr;
-            socklen_t     clientAddrSz = sizeof(clientAddr);
+            struct sockaddr_in6 clientAddr;
+            socklen_t           clientAddrSz = sizeof(clientAddr);
 #endif
             conn = (WOLFSSHD_CONNECTION*)WMALLOC(sizeof(WOLFSSHD_CONNECTION), NULL, DYNTYPE_SSHD);
             if (conn == NULL) {
@@ -2417,8 +2453,16 @@ static int StartSSHD(int argc, char** argv)
                 conn->fd = (int)accept(listenFd, (struct sockaddr*)&clientAddr,
                     &clientAddrSz);
                 if (conn->fd >= 0) {
-                    inet_ntop(AF_INET, &clientAddr.sin_addr, conn->ip,
-                        INET_ADDRSTRLEN);
+                    if (clientAddr.sin6_family == AF_INET) {
+                        struct sockaddr_in* addr4 =
+                            (struct sockaddr_in*)&clientAddr;
+                        inet_ntop(AF_INET, &addr4->sin_addr, conn->ip,
+                              INET_ADDRSTRLEN);
+                    }
+                    else if (clientAddr.sin6_family == AF_INET6) {
+                        inet_ntop(AF_INET6, &clientAddr.sin6_addr, conn->ip,
+                              INET6_ADDRSTRLEN);
+                    }
                 }
 #endif
 
@@ -2426,19 +2470,34 @@ static int StartSSHD(int argc, char** argv)
 #ifdef USE_WINDOWS_API
                     unsigned long blocking = 1;
                     if (ioctlsocket(conn->fd, FIONBIO, &blocking)
-                        == SOCKET_ERROR)
-                        err_sys("ioctlsocket failed");
+                        == SOCKET_ERROR) {
+                        WLOG(WS_LOG_DEBUG, "wolfSSH non-fatal error: "
+                                "ioctlsocket failed");
+                        WCLOSESOCKET(conn->fd);
+                        WFREE(conn, NULL, DYNTYPE_SSHD);
+                        continue;
+                    }
 #elif defined(WOLFSSL_MDK_ARM) || defined(WOLFSSL_KEIL_TCP_NET) \
                         || defined (WOLFSSL_TIRTOS)|| defined(WOLFSSL_VXWORKS) || \
                         defined(WOLFSSL_NUCLEUS)
                     /* non blocking not supported, for now */
 #else
                     int flags = fcntl(conn->fd, F_GETFL, 0);
-                    if (flags < 0)
-                        err_sys("fcntl get failed");
+                    if (flags < 0) {
+                        WLOG(WS_LOG_DEBUG, "wolfSSH non-fatal error: "
+                                "fcntl get failed");
+                        WCLOSESOCKET(conn->fd);
+                        WFREE(conn, NULL, DYNTYPE_SSHD);
+                        continue;
+                    }
                     flags = fcntl(conn->fd, F_SETFL, flags | O_NONBLOCK);
-                    if (flags < 0)
-                        err_sys("fcntl set failed");
+                    if (flags < 0) {
+                        WLOG(WS_LOG_DEBUG, "wolfSSH non-fatal error: "
+                                "fcntl set failed");
+                        WCLOSESOCKET(conn->fd);
+                        WFREE(conn, NULL, DYNTYPE_SSHD);
+                        continue;
+                    }
 #endif
                 }
                 ret = NewConnection(conn);
@@ -2469,7 +2528,10 @@ static int StartSSHD(int argc, char** argv)
     }
 #endif
 
-    CleanupCTX(conf, &ctx);
+    CleanupCTX(conf, &ctx, &banner);
+    if (banner) {
+        WFREE(banner, NULL, DYNTYPE_STRING);
+    }
     wolfSSHD_ConfigFree(conf);
     wolfSSHD_AuthFreeUser(auth);
     wolfSSH_Cleanup();
@@ -2487,6 +2549,159 @@ static int StartSSHD(int argc, char** argv)
 #endif
 }
 
+#ifdef _WIN32
+/* Used to setup a console and run command as a user.
+ * returns the process exit value */
+static int SetupConsole(char* inCmd)
+{
+    HANDLE sOut;
+    HANDLE sIn;
+    HPCON pCon = 0;
+    COORD cord = { 80,24 }; /* Default to 80x24. Updated later. */
+    STARTUPINFOEXW ext;
+    int ret = WS_SUCCESS;
+    PWSTR cmd    = NULL;
+    size_t cmdSz = 0;
+    PROCESS_INFORMATION processInfo;
+    size_t sz = 0;
+    DWORD processState = 0;
+    PCSTR shellCmd = "c:\\windows\\system32\\cmd.exe";
+
+    if (inCmd == NULL) {
+        return -1;
+    }
+
+    sIn  = GetStdHandle(STD_INPUT_HANDLE);
+
+    if (WSTRCMP(shellCmd, inCmd) != 0) {
+        /* if not opening a shell, pipe virtual terminal sequences to 'nul' */
+        if (CreatePseudoConsole(cord, sIn, INVALID_HANDLE_VALUE, 0, &pCon) != S_OK) {
+            ret = WS_FATAL_ERROR;
+        }
+        else {
+            CloseHandle(sIn);
+        }
+    }
+    else
+    {
+        /* if opening a shell, pipe virtual terminal sequences back to calling process */
+        sOut = GetStdHandle(STD_OUTPUT_HANDLE);
+        if (CreatePseudoConsole(cord, sIn, sOut, 0, &pCon) != S_OK) {
+            ret = WS_FATAL_ERROR;
+        }
+        else {
+            CloseHandle(sIn);
+            CloseHandle(sOut);
+        }
+    }
+
+    /* setup startup extended info for pseudo terminal */
+    ZeroMemory(&ext, sizeof(ext));
+    if (ret == WS_SUCCESS) {
+        ext.StartupInfo.cb = sizeof(STARTUPINFOEX);
+        (void)InitializeProcThreadAttributeList(NULL, 1, 0, &sz);
+        if (sz == 0) {
+            ret = WS_FATAL_ERROR;
+        }
+
+        if (ret == WS_SUCCESS) {
+            /* Using HeapAlloc for better support when possibly passing
+               memory between Windows Modules */
+            ext.lpAttributeList =
+                (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sz);
+            if (ext.lpAttributeList == NULL) {
+                ret = WS_FATAL_ERROR;
+            }
+        }
+
+        if (ret == WS_SUCCESS) {
+            if (InitializeProcThreadAttributeList(ext.lpAttributeList, 1, 0,
+                &sz) != TRUE) {
+                ret = WS_FATAL_ERROR;
+            }
+        }
+
+        if (ret == WS_SUCCESS) {
+            if (UpdateProcThreadAttribute(ext.lpAttributeList, 0,
+                PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE,
+                pCon, sizeof(HPCON), NULL, NULL) != TRUE) {
+                ret = WS_FATAL_ERROR;
+            }
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        cmdSz = WSTRLEN(inCmd) + 1; /* +1 for terminator */
+        cmd   = (PWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(wchar_t) * cmdSz);
+        if (cmd == NULL) {
+            ret = WS_MEMORY_E;
+        }
+        else {
+            size_t numConv = 0;
+
+            WMEMSET(cmd, 0, sizeof(wchar_t) * cmdSz);
+            mbstowcs_s(&numConv, cmd, cmdSz, inCmd, strlen(inCmd));
+        }
+    }
+
+    ZeroMemory(&processInfo, sizeof(processInfo));
+    if (ret == WS_SUCCESS) {
+        if (CreateProcessW(NULL, cmd,
+            NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, NULL,
+            &ext.StartupInfo, &processInfo) != TRUE) {
+            return WS_FATAL_ERROR;
+        }
+        else {
+            DWORD ava = 0;
+
+            WaitForInputIdle(processInfo.hProcess, 1000);
+
+            do {
+                /* wait indefinitly for console process to exit */
+                if (ava == 0) {
+                    if (WaitForSingleObject(processInfo.hProcess, INFINITE) == WAIT_FAILED) {
+                        break;
+                    }
+                }
+
+                /* check if process is still running and give time to drain pipes */
+                if (GetExitCodeProcess(processInfo.hProcess, &processState)
+                    == TRUE) {
+                    if (processState != STILL_ACTIVE) {
+                        Sleep(100); /* give the stdout/stderr of process a
+                                     * little time to write to pipe */
+                        if (PeekNamedPipe(GetStdHandle(STD_OUTPUT_HANDLE), NULL, 0, NULL, &ava, NULL)
+                            == TRUE) {
+                            if (ava > 0) {
+                                /* if data still pending then continue
+                                 * sending it over SSH */
+                                continue;
+                            }
+                        }
+                        break;
+                    }
+                }
+            } while (1);
+            CloseHandle(processInfo.hThread);
+        }
+    }
+
+    if (cmd != NULL) {
+        HeapFree(GetProcessHeap(), 0, cmd);
+    }
+
+    if (ext.lpAttributeList != NULL) {
+        HeapFree(GetProcessHeap(), 0, ext.lpAttributeList);
+    }
+
+    if (pCon != 0) {
+        ClosePseudoConsole(pCon);
+    }
+
+    return processState;
+}
+#endif /* _WIN32 */
+
 int main(int argc, char** argv)
 {
 #ifdef _WIN32
@@ -2496,6 +2711,13 @@ int main(int argc, char** argv)
         if (WSTRCMP(argv[i], "-D") == 0) {
             isService = 0;
         }
+        if (WSTRCMP(argv[i], "-r") == 0) {
+            if (argc < i + 1) {
+                /* was expecting command to run after -r argument */
+                return -1;
+            }
+            return SetupConsole(argv[i + 1]);
+        }
     }
 
     if (isService) {
diff --git a/configure.ac b/configure.ac
index 9e7b1abcc..617ff0523 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,9 +1,9 @@
 # wolfssh
-# Copyright (C) 2014-2023 wolfSSL Inc.
+# Copyright (C) 2014-2024 wolfSSL Inc.
 # All right reserved.
 
-AC_COPYRIGHT([Copyright (C) 2014-2023 wolfSSL Inc.])
-AC_INIT([wolfssh],[1.4.15],[support@wolfssl.com],[wolfssh],[https://www.wolfssl.com])
+AC_COPYRIGHT([Copyright (C) 2014-2024 wolfSSL Inc.])
+AC_INIT([wolfssh],[1.4.18],[support@wolfssl.com],[wolfssh],[https://www.wolfssl.com])
 AC_PREREQ([2.63])
 AC_CONFIG_AUX_DIR([build-aux])
 
@@ -18,18 +18,19 @@ AC_ARG_PROGRAM
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_HEADERS([config.h])
 
-WOLFSSH_LIBRARY_VERSION=15:2:7
-#                       | | |
-#                +------+ | +---+
-#                |        |     |
+WOLFSSH_LIBRARY_VERSION=17:0:10
+#                        | | |
+#                  +-----+ | +----+
+#                  |       |      |
 #               current:revision:age
-#                |        |     |
-#                |        |     +- increment if interfaces have been added
-#                |        |        set to zero if interfaces have been removed
-#                |        |        or changed
-#                |        +- increment if source code has changed
-#                |           set to zero if current is incremented
-#                +- increment if interfaces have been added, removed or changed
+#                  |       |      |
+#                  |       |      +- increment if interfaces have been added
+#                  |       |      +- set to zero if interfaces have been
+#                  |       |           removed or changed
+#                  |       +- increment if source code has changed
+#                  |       +- set to zero if current is incremented
+#                  +- increment if interfaces have been added, removed
+#                       or changed
 AC_SUBST([WOLFSSH_LIBRARY_VERSION])
 
 LT_PREREQ([2.2])
@@ -193,7 +194,7 @@ AC_ARG_ENABLE([fwd],
 # pseudo-terminal
 AC_ARG_ENABLE([term],
     [AS_HELP_STRING([--disable-term],[Disable pseudo-terminal support (default: enabled)])],
-    [ENABLED_PTERM=$enableval],[ENABLED_PTERM=yes])
+    [ENABLED_TERM=$enableval],[ENABLED_TERM=yes])
 
 # shell support
 AC_ARG_ENABLE([shell],
@@ -250,7 +251,7 @@ AS_IF([test "x$ENABLED_SFTP" = "xyes"],
       [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SFTP"])
 AS_IF([test "x$ENABLED_FWD" = "xyes"],
       [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_FWD"])
-AS_IF([test "x$ENABLED_PTERM" = "xyes"],
+AS_IF([test "x$ENABLED_TERM" = "xyes"],
       [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_TERM"])
 AS_IF([test "x$ENABLED_SHELL" = "xyes"],
       [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SHELL"])
@@ -354,7 +355,7 @@ AS_ECHO(["   Features"])
 AS_ECHO(["   * Inline Code:               $ENABLED_INLINE"])
 AS_ECHO(["   * Small stack:               $ENABLED_SMALLSTACK"])
 AS_ECHO(["   * keygen:                    $ENABLED_KEYGEN"])
-AS_ECHO(["   * psuedo-terminal:           $ENABLED_PTERM"])
+AS_ECHO(["   * psuedo-terminal:           $ENABLED_TERM"])
 AS_ECHO(["   * echoserver shell support:  $ENABLED_SHELL"])
 AS_ECHO(["   * scp:                       $ENABLED_SCP"])
 AS_ECHO(["   * sftp:                      $ENABLED_SFTP"])
diff --git a/examples/client/client.c b/examples/client/client.c
index 4d56575f6..e37468c6f 100644
--- a/examples/client/client.c
+++ b/examples/client/client.c
@@ -1,6 +1,6 @@
 /* client.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -117,6 +117,8 @@ static void ShowUsage(void)
     printf(" -A <filename> filename for DER CA certificate to verify host\n");
     printf(" -X            Ignore IP checks on peer vs peer certificate\n");
 #endif
+    printf(" -E            List all possible algos\n");
+    printf(" -k            set the list of key algos to use\n");
 }
 
 
@@ -365,6 +367,26 @@ static THREAD_RET readPeer(void* in)
     FD_SET(fd, &errSet);
 
 #ifdef USE_WINDOWS_API
+    if (args->rawMode == 0) {
+        DWORD wrd;
+
+        /* get console mode will fail on handles that are not a console,
+         * i.e. if the stdout is being redirected to a file */
+        if (GetConsoleMode(stdoutHandle, &wrd) != FALSE) {
+            /* depend on the terminal to process VT characters */
+            #ifndef _WIN32_WINNT_WIN10
+                /* support for virtual terminal processing was introduced in windows 10 */
+                #define _WIN32_WINNT_WIN10 0x0A00
+            #endif
+            #if defined(WINVER) && (WINVER >= _WIN32_WINNT_WIN10)
+                wrd |= (ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT);
+            #endif
+            if (SetConsoleMode(stdoutHandle, wrd) == FALSE) {
+                err_sys("Unable to set console mode");
+            }
+        }
+    }
+
     /* set handle to use for window resize */
     wc_LockMutex(&args->lock);
     wolfSSH_SetTerminalResizeCtx(args->ssh, stdoutHandle);
@@ -444,29 +466,21 @@ static THREAD_RET readPeer(void* in)
                 }
             }
             else {
+            #ifdef USE_WINDOWS_API
+                DWORD writtn = 0;
+            #endif
                 buf[bufSz - 1] = '\0';
 
             #ifdef USE_WINDOWS_API
-                if (args->rawMode == 0) {
-                    ret = wolfSSH_ConvertConsole(args->ssh, stdoutHandle, buf,
-                            ret);
-                    if (ret != WS_SUCCESS && ret != WS_WANT_READ) {
-                        err_sys("issue with print out");
-                    }
-                    if (ret == WS_WANT_READ) {
-                        ret = 0;
-                    }
-                }
-                else {
-                    printf("%s", buf);
-                    WFFLUSH(stdout);
+                if (WriteFile(stdoutHandle, buf, bufSz, &writtn, NULL) == FALSE) {
+                    err_sys("Failed to write to stdout handle");
                 }
             #else
                 if (write(STDOUT_FILENO, buf, ret) < 0) {
                     perror("write to stdout error ");
                 }
-                WFFLUSH(stdout);
             #endif
+                WFFLUSH(stdout);
             }
             if (wolfSSH_stream_peek(args->ssh, buf, bufSz) <= 0) {
                 bytes = 0; /* read it all */
@@ -624,7 +638,9 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
     const char* password = NULL;
     const char* cmd      = NULL;
     const char* privKeyName = NULL;
+    const char* keyList = NULL;
     byte imExit = 0;
+    byte listAlgos = 0;
     byte nonBlock = 0;
     byte keepOpen = 0;
 #ifdef USE_WINDOWS_API
@@ -641,7 +657,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
 
     (void)keepOpen;
 
-    while ((ch = mygetopt(argc, argv, "?ac:h:i:j:p:tu:xzNP:RJ:A:Xe")) != -1) {
+    while ((ch = mygetopt(argc, argv, "?ac:h:i:j:p:tu:xzNP:RJ:A:XeEk:")) != -1) {
         switch (ch) {
             case 'h':
                 host = myoptarg;
@@ -701,6 +717,10 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
             #endif
         #endif
 
+            case 'E':
+                listAlgos = 1;
+                break;
+
             case 'x':
                 /* exit after successful connection without read/write */
                 imExit = 1;
@@ -710,6 +730,10 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
                 nonBlock = 1;
                 break;
 
+            case 'k':
+                keyList = myoptarg;
+                break;
+
         #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS)
             case 'c':
                 cmd = myoptarg;
@@ -756,7 +780,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
         err_sys("If setting priv key, need pub key.");
     }
 
-    ret = ClientSetPrivateKey(privKeyName, userEcc);
+    ret = ClientSetPrivateKey(privKeyName, userEcc, NULL);
     if (ret != 0) {
         err_sys("Error setting private key");
     }
@@ -764,12 +788,12 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
 #ifdef WOLFSSH_CERTS
     /* passed in certificate to use */
     if (certName) {
-        ret = ClientUseCert(certName);
+        ret = ClientUseCert(certName, NULL);
     }
     else
 #endif
     if (pubKeyName) {
-        ret = ClientUsePubKey(pubKeyName, userEcc);
+        ret = ClientUsePubKey(pubKeyName, userEcc, NULL);
     }
     if (ret != 0) {
         err_sys("Error setting public key");
@@ -779,6 +803,12 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
     if (ctx == NULL)
         err_sys("Couldn't create wolfSSH client context.");
 
+    if (keyList) {
+        if (wolfSSH_CTX_SetAlgoListKey(ctx, NULL) != WS_SUCCESS) {
+            err_sys("Error setting key list.\n");
+        }
+    }
+
     if (((func_args*)args)->user_auth == NULL)
         wolfSSH_SetUserAuth(ctx, ClientUserAuth);
     else
@@ -825,8 +855,57 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
     if (ret != WS_SUCCESS)
         err_sys("Couldn't set the username.");
 
+    if (listAlgos) {
+        word32 idx = 0;
+        const char* current = NULL;
+
+        printf("KEX:\n");
+        do {
+            current = wolfSSH_QueryKex(&idx);
+            if (current) {
+                printf("\t%d: %s\n", idx, current);
+            }
+        } while (current != NULL);
+        printf("Set KEX: %s\n\n", wolfSSH_GetAlgoListKex(ssh));
+
+        idx = 0;
+        printf("Key:\n");
+        do {
+            current = wolfSSH_QueryKey(&idx);
+            if (current) {
+                printf("\t%d: %s\n", idx, current);
+            }
+        } while (current != NULL);
+        printf("Set Key: %s\n\n", wolfSSH_GetAlgoListKey(ssh));
+
+        idx = 0;
+        printf("Cipher:\n");
+        do {
+            current = wolfSSH_QueryCipher(&idx);
+            if (current) {
+                printf("\t%d: %s\n", idx, current);
+            }
+        } while (current != NULL);
+        printf("Set Cipher: %s\n\n", wolfSSH_GetAlgoListCipher(ssh));
+
+        idx = 0;
+        printf("Mac:\n");
+        do {
+            current = wolfSSH_QueryMac(&idx);
+            if (current) {
+                printf("\t%d: %s\n", idx, current);
+            }
+        } while (current != NULL);
+        printf("Set Mac: %s\n", wolfSSH_GetAlgoListMac(ssh));
+
+        wolfSSH_free(ssh);
+        wolfSSH_CTX_free(ctx);
+        WOLFSSL_RETURN_FROM_THREAD(0);
+    }
+
     build_addr(&clientAddr, host, port);
-    tcp_socket(&sockFd);
+    tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family);
+
     ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz);
     if (ret != 0)
         err_sys("Couldn't connect to server.");
@@ -977,7 +1056,8 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
     }
     ret = wolfSSH_shutdown(ssh);
     /* do not continue on with shutdown process if peer already disconnected */
-    if (ret != WS_SOCKET_ERROR_E && wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E) {
+    if (ret != WS_SOCKET_ERROR_E && wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E
+            && wolfSSH_get_error(ssh) != WS_CHANNEL_CLOSED) {
         if (ret != WS_SUCCESS) {
             err_sys("Sending the shutdown messages failed.");
         }
@@ -1000,7 +1080,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args)
         err_sys("Closing client stream failed");
     }
 
-    ClientFreeBuffers(pubKeyName, privKeyName);
+    ClientFreeBuffers(pubKeyName, privKeyName, NULL);
 #if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS)
     wc_ecc_fp_free();  /* free per thread cache */
 #endif
diff --git a/examples/client/client.h b/examples/client/client.h
index 80aa61eeb..c02d80d9d 100644
--- a/examples/client/client.h
+++ b/examples/client/client.h
@@ -1,6 +1,6 @@
 /* client.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/examples/client/common.c b/examples/client/common.c
index 97051386c..c4281ab4f 100644
--- a/examples/client/common.c
+++ b/examples/client/common.c
@@ -1,6 +1,6 @@
 /* common.c
  *
- * Copyright (C) 2014-2022 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -241,7 +241,8 @@ static const unsigned int hanselPrivateEccSz = 223;
 
 #if defined(WOLFSSH_CERTS)
 
-static int load_der_file(const char* filename, byte** out, word32* outSz)
+static int load_der_file(const char* filename, byte** out, word32* outSz,
+        void* heap)
 {
     WFILE* file;
     byte* in;
@@ -267,7 +268,7 @@ static int load_der_file(const char* filename, byte** out, word32* outSz)
         return -1;
     }
 
-    in = (byte*)WMALLOC(inSz, NULL, 0);
+    in = (byte*)WMALLOC(inSz, heap, 0);
     if (in == NULL) {
         WFCLOSE(NULL, file);
         return -1;
@@ -276,7 +277,7 @@ static int load_der_file(const char* filename, byte** out, word32* outSz)
     ret = (int)WFREAD(NULL, in, 1, inSz, file);
     if (ret <= 0 || (word32)ret != inSz) {
         ret = -1;
-        WFREE(in, NULL, 0);
+        WFREE(in, heap, 0);
         in = 0;
         inSz = 0;
     }
@@ -403,7 +404,8 @@ int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx)
                                 current->ipString);
                             WLOG(WS_LOG_DEBUG,
                                 "\texpecting host IP : %s", (char*)ctx);
-                            if (XSTRCMP(ctx, current->ipString) == 0) {
+                            if (XSTRCMP((const char*)ctx,
+                                        current->ipString) == 0) {
                                 WLOG(WS_LOG_DEBUG, "\tmatched!");
                                 ipMatch = 1;
                             }
@@ -651,19 +653,20 @@ int ClientSetEcho(int type)
 
 /* Set certificate to use and public key.
  * returns 0 on success */
-int ClientUseCert(const char* certName)
+int ClientUseCert(const char* certName, void* heap)
 {
     int ret = 0;
 
     if (certName != NULL) {
     #ifdef WOLFSSH_CERTS
-        ret = load_der_file(certName, &userPublicKey, &userPublicKeySz);
+        ret = load_der_file(certName, &userPublicKey, &userPublicKeySz, heap);
         if (ret == 0) {
             userPublicKeyType = publicKeyType;
             userPublicKeyTypeSz = (word32)WSTRLEN((const char*)publicKeyType);
             pubKeyLoaded = 1;
         }
     #else
+        (void)heap;
         fprintf(stderr, "Certificate support not compiled in");
         ret = WS_NOT_COMPILED;
     #endif
@@ -675,7 +678,7 @@ int ClientUseCert(const char* certName)
 
 /* Reads the private key to use from file name privKeyName.
  * returns 0 on success */
-int ClientSetPrivateKey(const char* privKeyName, int userEcc)
+int ClientSetPrivateKey(const char* privKeyName, int userEcc, void* heap)
 {
     int ret = 0;
 
@@ -684,14 +687,14 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc)
         #ifndef WOLFSSH_NO_ECC
             ret = wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz,
                     WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz,
-                    &userPrivateKeyType, &userPrivateKeyTypeSz, NULL);
+                    &userPrivateKeyType, &userPrivateKeyTypeSz, heap);
         #endif
         }
         else {
         #ifndef WOLFSSH_NO_RSA
             ret = wolfSSH_ReadKey_buffer(hanselPrivateRsa, hanselPrivateRsaSz,
                     WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz,
-                    &userPrivateKeyType, &userPrivateKeyTypeSz, NULL);
+                    &userPrivateKeyType, &userPrivateKeyTypeSz, heap);
         #endif
         }
         isPrivate = 1;
@@ -702,7 +705,7 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc)
         ret = wolfSSH_ReadKey_file(privKeyName,
                 (byte**)&userPrivateKey, &userPrivateKeySz,
                 (const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz,
-                &isPrivate, NULL);
+                &isPrivate, heap);
     #else
         printf("file system not compiled in!\n");
         ret = NOT_COMPILED_IN;
@@ -715,7 +718,7 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc)
 
 /* Set public key to use
  * returns 0 on success */
-int ClientUsePubKey(const char* pubKeyName, int userEcc)
+int ClientUsePubKey(const char* pubKeyName, int userEcc, void* heap)
 {
     int ret = 0;
 
@@ -728,7 +731,7 @@ int ClientUsePubKey(const char* pubKeyName, int userEcc)
             ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicEcc,
                     (word32)strlen(hanselPublicEcc), WOLFSSH_FORMAT_SSH,
                     &p, &userPublicKeySz,
-                    &userPublicKeyType, &userPublicKeyTypeSz, NULL);
+                    &userPublicKeyType, &userPublicKeyTypeSz, heap);
         #endif
         }
         else {
@@ -736,7 +739,7 @@ int ClientUsePubKey(const char* pubKeyName, int userEcc)
             ret = wolfSSH_ReadKey_buffer((const byte*)hanselPublicRsa,
                     (word32)strlen(hanselPublicRsa), WOLFSSH_FORMAT_SSH,
                     &p, &userPublicKeySz,
-                    &userPublicKeyType, &userPublicKeyTypeSz, NULL);
+                    &userPublicKeyType, &userPublicKeyTypeSz, heap);
         #endif
         }
         isPrivate = 1;
@@ -747,7 +750,7 @@ int ClientUsePubKey(const char* pubKeyName, int userEcc)
         ret = wolfSSH_ReadKey_file(pubKeyName,
                 &userPublicKey, &userPublicKeySz,
                 (const byte**)&userPublicKeyType, &userPublicKeyTypeSz,
-                &isPrivate, NULL);
+                &isPrivate, heap);
     #else
         printf("file system not compiled in!\n");
         ret = -1;
@@ -770,7 +773,7 @@ int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert)
         byte* der = NULL;
         word32 derSz;
 
-        ret = load_der_file(caCert, &der, &derSz);
+        ret = load_der_file(caCert, &der, &derSz, ctx->heap);
         if (ret == 0) {
             if (wolfSSH_CTX_AddRootCert_buffer(ctx, der, derSz,
                 WOLFSSH_FORMAT_ASN1) != WS_SUCCESS) {
@@ -789,13 +792,14 @@ int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert)
 }
 
 
-void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName)
+void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName,
+        void* heap)
 {
     if (pubKeyName != NULL && userPublicKey != NULL) {
-        WFREE(userPublicKey, NULL, DYNTYPE_PRIVKEY);
+        WFREE(userPublicKey, heap, DYNTYPE_PRIVKEY);
     }
 
     if (privKeyName != NULL && userPrivateKey != NULL) {
-        WFREE(userPrivateKey, NULL, DYNTYPE_PRIVKEY);
+        WFREE(userPrivateKey, heap, DYNTYPE_PRIVKEY);
     }
 }
diff --git a/examples/client/common.h b/examples/client/common.h
index 68c36efe2..395d4288a 100644
--- a/examples/client/common.h
+++ b/examples/client/common.h
@@ -1,6 +1,6 @@
 /* common.h
  *
- * Copyright (C) 2014-2022 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -21,16 +21,17 @@
 #ifndef WOLFSSH_COMMON_H
 #define WOLFSSH_COMMON_H
 int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert);
-int ClientUsePubKey(const char* pubKeyName, int userEcc);
-int ClientSetPrivateKey(const char* privKeyName, int userEcc);
-int ClientUseCert(const char* certName);
+int ClientUsePubKey(const char* pubKeyName, int userEcc, void* heap);
+int ClientSetPrivateKey(const char* privKeyName, int userEcc, void* heap);
+int ClientUseCert(const char* certName, void* heap);
 int ClientSetEcho(int type);
 int ClientUserAuth(byte authType,
                       WS_UserAuthData* authData,
                       void* ctx);
 int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx);
 void ClientIPOverride(int flag);
-void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName);
+void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName,
+        void* heap);
 
 #endif /* WOLFSSH_COMMON_H */
 
diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c
index 3d9b542e0..2075d4858 100644
--- a/examples/echoserver/echoserver.c
+++ b/examples/echoserver/echoserver.c
@@ -1,6 +1,6 @@
 /* echoserver.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -289,6 +289,7 @@ static int callbackReqFailure(WOLFSSH *ssh, void *buf, word32 sz, void *ctx)
     return WS_SUCCESS;
 }
 
+
 static void *global_req(void *ctx)
 {
     int ret;
@@ -328,6 +329,50 @@ static void *global_req(void *ctx)
 #endif
 
 
+static void printKeyCompleteText(WOLFSSH* ssh, WS_Text id, const char* tag)
+{
+    char str[200];
+    size_t strSz = sizeof(str);
+    size_t ret;
+
+    ret = wolfSSH_GetText(ssh, id, str, strSz);
+    if (ret == strSz) {
+        printf("\tString size was not large enough for %s\n", tag);
+    }
+    printf("\t%-30s : %s\n", tag, str);
+}
+
+
+static void callbackKeyingComplete(void* ctx)
+{
+    WOLFSSH* ssh = (WOLFSSH*)ctx;
+
+    if (ssh != NULL) {
+        printf("Keying Complete:\n");
+        printKeyCompleteText(ssh, WOLFSSH_TEXT_KEX_ALGO,
+                                    "WOLFSSH_TEXT_KEX_ALGO");
+
+        printKeyCompleteText(ssh, WOLFSSH_TEXT_KEX_CURVE,
+                                    "WOLFSSH_TEXT_KEX_CURVE");
+
+        printKeyCompleteText(ssh, WOLFSSH_TEXT_KEX_HASH,
+                                    "WOLFSSH_TEXT_KEX_HASH");
+
+        printKeyCompleteText(ssh, WOLFSSH_TEXT_CRYPTO_IN_CIPHER,
+                                    "WOLFSSH_TEXT_CRYPTO_IN_CIPHER");
+
+        printKeyCompleteText(ssh, WOLFSSH_TEXT_CRYPTO_IN_MAC,
+                                    "WOLFSSH_TEXT_CRYPTO_IN_MAC");
+
+        printKeyCompleteText(ssh, WOLFSSH_TEXT_CRYPTO_OUT_CIPHER,
+                                    "WOLFSSH_TEXT_CRYPTO_OUT_CIPHER");
+
+        printKeyCompleteText(ssh, WOLFSSH_TEXT_CRYPTO_OUT_MAC,
+                                    "WOLFSSH_TEXT_CRYPTO_OUT_MAC");
+    }
+}
+
+
 #ifdef WOLFSSH_AGENT
 
 static const char EnvNameAuthPort[] = "SSH_AUTH_SOCK";
@@ -609,6 +654,87 @@ static int termios_show(int fd)
 #endif /* SHELL_DEBUG */
 
 
+#ifdef WOLFSSH_STATIC_MEMORY
+    #ifndef WOLFSSL_STATIC_MEMORY
+        #error Requires the static memory functions from wolfSSL
+    #endif
+    #if defined(WOLFSSH_SCP) || defined(WOLFSSH_SHELL) || defined(WOLFSSH_FWD)
+        #warning Static memory configuration for SFTP, results may vary.
+    #endif
+    typedef WOLFSSL_HEAP_HINT ES_HEAP_HINT;
+
+     /* This static buffer is tuned for building with SFTP only. The static
+      * buffer size is calulated by multiplying the pairs of sizeList items
+      * and distList items and summing (32*64 + 128*118 + ...) and adding
+      * the sum of the distList values times the sizeof wc_Memory (rounded up
+      * to a word, 24). This total was 288kb plus change, rounded up to 289. */
+    #ifndef ES_STATIC_SIZES
+        #define ES_STATIC_SIZES 32,128,384,800,3120,8400,17552,32846,131072
+    #endif
+    #ifndef ES_STATIC_DISTS
+        #define ES_STATIC_DISTS 64,118,3,4,6,2,2,2,1
+    #endif
+    #ifndef ES_STATIC_LISTSZ
+        #define ES_STATIC_LISTSZ 9
+    #endif
+    #ifndef ES_STATIC_BUFSZ
+        #define ES_STATIC_BUFSZ (289*1024)
+    #endif
+    static const word32 static_sizeList[] = {ES_STATIC_SIZES};
+    static const word32 static_distList[] = {ES_STATIC_DISTS};
+    static byte static_buffer[ES_STATIC_BUFSZ];
+
+    static void wolfSSH_MemoryPrintStats(ES_HEAP_HINT* hint)
+    {
+        if (hint != NULL) {
+            word16 i;
+            WOLFSSL_MEM_STATS stats;
+
+            wolfSSL_GetMemStats(hint->memory, &stats);
+
+            /* print to stderr so is on the same pipe as WOLFSSL_DEBUG */
+            fprintf(stderr, "Total mallocs        = %d\n", stats.totalAlloc);
+            fprintf(stderr, "Total frees          = %d\n", stats.totalFr);
+            fprintf(stderr, "Current mallocs      = %d\n", stats.curAlloc);
+            fprintf(stderr, "Available IO         = %d\n", stats.avaIO);
+            fprintf(stderr, "Max con. handshakes  = %d\n", stats.maxHa);
+            fprintf(stderr, "Max con. IO          = %d\n", stats.maxIO);
+            fprintf(stderr, "State of memory blocks: size : available\n");
+            for (i = 0; i < WOLFMEM_MAX_BUCKETS; i++) {
+                fprintf(stderr, "                    %8d : %d\n",
+                        stats.blockSz[i], stats.avaBlock[i]);
+            }
+        }
+    }
+
+    static void wolfSSH_MemoryConnPrintStats(ES_HEAP_HINT* hint)
+    {
+        if (hint != NULL) {
+            WOLFSSL_MEM_CONN_STATS* stats = hint->stats;
+
+            /* fill out statistics if wanted and WOLFMEM_TRACK_STATS flag */
+            if (hint->memory->flag & WOLFMEM_TRACK_STATS
+                    && hint->stats != NULL) {
+                fprintf(stderr, "peak connection memory    = %d\n",
+                        stats->peakMem);
+                fprintf(stderr, "current memory in use     = %d\n",
+                        stats->curMem);
+                fprintf(stderr, "peak connection allocs    = %d\n",
+                        stats->peakAlloc);
+                fprintf(stderr, "current connection allocs = %d\n",
+                        stats->curAlloc);
+                fprintf(stderr, "total connection allocs   = %d\n",
+                        stats->totalAlloc);
+                fprintf(stderr, "total connection frees    = %d\n\n",
+                        stats->totalFr);
+            }
+        }
+    }
+#else
+    typedef void ES_HEAP_HINT;
+#endif
+
+
 int ChildRunning = 0;
 
 #ifdef WOLFSSH_SHELL
@@ -738,6 +864,25 @@ static int ssh_worker(thread_ctx_t* threadCtx)
         ChildRunning = 1;
 #endif
 
+#if defined(WOLFSSH_TERM) && defined(WOLFSSH_SHELL)
+    /* set initial size of terminal based on saved size */
+#if defined(HAVE_SYS_IOCTL_H)
+    wolfSSH_DoModes(ssh->modes, ssh->modesSz, childFd);
+    {
+        struct winsize s = {0};
+
+        s.ws_col = ssh->widthChar;
+        s.ws_row = ssh->heightRows;
+        s.ws_xpixel = ssh->widthPixels;
+        s.ws_ypixel = ssh->heightPixels;
+
+        ioctl(childFd, TIOCSWINSZ, &s);
+    }
+#endif /* HAVE_SYS_IOCTL_H */
+
+        wolfSSH_SetTerminalResizeCtx(ssh, (void*)&childFd);
+#endif /* WOLFSSH_TERM && WOLFSSH_SHELL */
+
         while (ChildRunning) {
             fd_set readFds;
             WS_SOCKET_T maxFd;
@@ -1196,11 +1341,8 @@ static int sftp_worker(thread_ctx_t* threadCtx)
         }
         else if (selected == WS_SELECT_TIMEOUT) {
             timeout = TEST_SFTP_TIMEOUT_LONG;
-            continue;
         }
-
-        if (ret == WS_WANT_READ || ret == WS_WANT_WRITE ||
-                selected == WS_SELECT_RECV_READY) {
+        else if (selected == WS_SELECT_RECV_READY) {
             ret = wolfSSH_worker(ssh, NULL);
             error = wolfSSH_get_error(ssh);
             if (ret == WS_REKEYING) {
@@ -1213,7 +1355,6 @@ static int sftp_worker(thread_ctx_t* threadCtx)
                 error == WS_WINDOW_FULL) {
                 timeout = TEST_SFTP_TIMEOUT;
                 ret = error;
-                continue;
             }
 
             if (error == WS_EOF) {
@@ -1278,7 +1419,8 @@ static int NonBlockSSH_accept(WOLFSSH* ssh)
 
     while ((ret != WS_SUCCESS
                 && ret != WS_SCP_COMPLETE && ret != WS_SFTP_COMPLETE)
-            && (error == WS_WANT_READ || error == WS_WANT_WRITE)) {
+            && (error == WS_WANT_READ || error == WS_WANT_WRITE ||
+                error == WS_AUTH_PENDING)) {
 
         if (error == WS_WANT_READ)
             printf("... server would read block\n");
@@ -1288,7 +1430,8 @@ static int NonBlockSSH_accept(WOLFSSH* ssh)
         select_ret = tcp_select(sockfd, 1);
         if (select_ret == WS_SELECT_RECV_READY  ||
             select_ret == WS_SELECT_ERROR_READY ||
-            error      == WS_WANT_WRITE)
+            error      == WS_WANT_WRITE ||
+            error      == WS_AUTH_PENDING)
         {
             ret = wolfSSH_accept(ssh);
             error = wolfSSH_get_error(ssh);
@@ -1310,11 +1453,16 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
 
     passwdRetry = MAX_PASSWD_RETRY;
 
-    if (!threadCtx->nonBlock)
+    if (!threadCtx->nonBlock) {
         ret = wolfSSH_accept(threadCtx->ssh);
-    else
+        if (wolfSSH_get_error(threadCtx->ssh) == WS_AUTH_PENDING) {
+            printf("Auth pending error, use -N for non blocking\n");
+            printf("Trying to close down the connection\n");
+        }
+    }
+    else {
         ret = NonBlockSSH_accept(threadCtx->ssh);
-
+    }
 #ifdef WOLFSSH_SCP
     /* finish off SCP operation */
     if (ret == WS_SCP_INIT) {
@@ -1423,6 +1571,11 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs)
         threadCtx->fwdCbCtx.originName = NULL;
     }
 #endif
+
+#ifdef WOLFSSH_STATIC_MEMORY
+    wolfSSH_MemoryConnPrintStats(threadCtx->ssh->ctx->heap);
+#endif
+
     wolfSSH_free(threadCtx->ssh);
 
     if (ret != 0) {
@@ -1456,21 +1609,19 @@ static int load_file(const char* fileName, byte* buf, word32* bufSz)
     fileSz = (word32)WFTELL(NULL, file);
     WREWIND(NULL, file);
 
-    if (fileSz > *bufSz) {
-        if (buf == NULL)
-            *bufSz = fileSz;
+    if (buf == NULL || fileSz > *bufSz) {
+        *bufSz = fileSz;
         WFCLOSE(NULL, file);
         return 0;
     }
 
     readSz = (word32)WFREAD(NULL, buf, 1, fileSz, file);
+    WFCLOSE(NULL, file);
+
     if (readSz < fileSz) {
-        WFCLOSE(NULL, file);
-        return 0;
+        fileSz = 0;
     }
 
-    WFCLOSE(NULL, file);
-
     return fileSz;
 }
 #endif /* NO_FILESYSTEM */
@@ -1928,6 +2079,7 @@ static int wsUserAuthResult(byte res,
 }
 
 
+static int userAuthWouldBlock = 0;
 static int wsUserAuth(byte authType,
                       WS_UserAuthData* authData,
                       void* ctx)
@@ -1941,6 +2093,12 @@ static int wsUserAuth(byte authType,
         return WOLFSSH_USERAUTH_FAILURE;
     }
 
+    if (userAuthWouldBlock > 0) {
+        printf("User Auth would block ....\n");
+        userAuthWouldBlock--;
+        return WOLFSSH_USERAUTH_WOULD_BLOCK;
+    }
+
     if (authType != WOLFSSH_USERAUTH_PASSWORD &&
 #ifdef WOLFSSH_ALLOW_USERAUTH_NONE
         authType != WOLFSSH_USERAUTH_NONE &&
@@ -2156,6 +2314,8 @@ static void ShowUsage(void)
 #ifdef WOLFSSH_CERTS
     printf(" -a <file>     load in a root CA certificate file\n");
 #endif
+    printf(" -k            set the list of key algos to use\n");
+    printf(" -b <num>      test user auth would block\n");
 }
 
 
@@ -2194,6 +2354,8 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
     WS_SOCKET_T listenFd = WOLFSSH_SOCKET_INVALID;
     word32 defaultHighwater = EXAMPLE_HIGHWATER_MARK;
     word32 threadCount = 0;
+    const char* keyList = NULL;
+    ES_HEAP_HINT* heap = NULL;
     int multipleConnections = 1;
     int userEcc = 0;
     int peerEcc = 0;
@@ -2215,7 +2377,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
     serverArgs->return_code = EXIT_SUCCESS;
 
     if (argc > 0) {
-        const char* optlist = "?1a:d:efEp:R:Ni:j:I:J:K:P:";
+        const char* optlist = "?1a:d:efEp:R:Ni:j:I:J:K:P:k:b:";
         myoptind = 0;
         while ((ch = mygetopt(argc, argv, optlist)) != -1) {
             switch (ch) {
@@ -2237,6 +2399,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
                     userEcc = 1;
                     break;
 
+                case 'k' :
+                    keyList = myoptarg;
+                    break;
+
                 case 'E':
                     peerEcc = 1;
                     break;
@@ -2295,6 +2461,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
                     passwdList = StrListAdd(passwdList, myoptarg);
                     break;
 
+                case 'b':
+                    userAuthWouldBlock = atoi(myoptarg);
+                    break;
+
                 default:
                     ShowUsage();
                     serverArgs->return_code = MY_EX_USAGE;
@@ -2327,11 +2497,32 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
         ES_ERROR("Couldn't initialize wolfSSH.\n");
     }
 
-    ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
+    #ifdef WOLFSSH_STATIC_MEMORY
+    {
+        int ret;
+
+        ret = wc_LoadStaticMemory_ex(&heap,
+                ES_STATIC_LISTSZ, static_sizeList, static_distList,
+                static_buffer, sizeof(static_buffer),
+                WOLFMEM_GENERAL|WOLFMEM_TRACK_STATS, 0);
+        if (ret != 0) {
+            ES_ERROR("Couldn't set up static memory pool.\n");
+        }
+    }
+    #endif /* WOLFSSH_STATIC_MEMORY */
+
+    ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, heap);
     if (ctx == NULL) {
         ES_ERROR("Couldn't allocate SSH CTX data.\n");
     }
 
+    wolfSSH_SetKeyingCompletionCb(ctx, callbackKeyingComplete);
+    if (keyList) {
+        if (wolfSSH_CTX_SetAlgoListKey(ctx, keyList) != WS_SUCCESS) {
+            ES_ERROR("Error setting key list.\n");
+        }
+    }
+
     WMEMSET(&pwMapList, 0, sizeof(pwMapList));
     if (serverArgs->user_auth == NULL)
         wolfSSH_SetUserAuth(ctx, wsUserAuth);
@@ -2565,7 +2756,11 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
             WFREE(threadCtx, NULL, 0);
             ES_ERROR("Couldn't allocate SSH data.\n");
         }
+    #ifdef WOLFSSH_STATIC_MEMORY
+        wolfSSH_MemoryConnPrintStats(heap);
+    #endif
         wolfSSH_SetUserAuthCtx(ssh, &pwMapList);
+        wolfSSH_SetKeyingCompletionCbCtx(ssh, (void*)ssh);
         /* Use the session object for its own highwater callback ctx */
         if (defaultHighwater > 0) {
             wolfSSH_SetHighwaterCtx(ssh, (void*)ssh);
@@ -2641,6 +2836,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
     wc_FreeMutex(&doneLock);
     PwMapListDelete(&pwMapList);
     wolfSSH_CTX_free(ctx);
+#ifdef WOLFSSH_STATIC_MEMORY
+    wolfSSH_MemoryPrintStats(heap);
+#endif
+
     if (wolfSSH_Cleanup() != WS_SUCCESS) {
         ES_ERROR("Couldn't clean up wolfSSH.\n");
     }
diff --git a/examples/echoserver/echoserver.h b/examples/echoserver/echoserver.h
index 704206db9..45fcb4325 100644
--- a/examples/echoserver/echoserver.h
+++ b/examples/echoserver/echoserver.h
@@ -1,6 +1,6 @@
 /* echoserver.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/examples/echoserver/include.am b/examples/echoserver/include.am
index 532f0f7bb..4192d605c 100644
--- a/examples/echoserver/include.am
+++ b/examples/echoserver/include.am
@@ -7,5 +7,5 @@ examples_echoserver_echoserver_SOURCES      = examples/echoserver/echoserver.c \
                                               examples/echoserver/echoserver.h
 examples_echoserver_echoserver_LDADD        = src/libwolfssh.la
 examples_echoserver_echoserver_DEPENDENCIES = src/libwolfssh.la
-examples_echoserver_echoserver_CFLAGS       = $(AM_CFLAGS)
+examples_echoserver_echoserver_CFLAGS       = $(AM_CFLAGS) ${AM_CPPFLAGS}
 endif
diff --git a/examples/portfwd/portfwd.c b/examples/portfwd/portfwd.c
index 173d31899..07bb95da9 100644
--- a/examples/portfwd/portfwd.c
+++ b/examples/portfwd/portfwd.c
@@ -1,6 +1,6 @@
 /* portfwd.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -380,12 +380,14 @@ THREAD_RETURN WOLFSSH_THREAD portfwd_worker(void* args)
     if (ret != WS_SUCCESS)
         err_sys("Couldn't set the username.");
 
+    /* Socket to SSH peer. */
     build_addr(&hostAddr, host, port);
+    tcp_socket(&sshFd, ((struct sockaddr_in *)&hostAddr)->sin_family);
+
+    /* Receive from client application or connect to server application. */
     build_addr(&fwdFromHostAddr, fwdFromHost, fwdFromPort);
+    tcp_socket(&listenFd, ((struct sockaddr_in *)&fwdFromHostAddr)->sin_family);
 
-    tcp_socket(&sshFd); /* Socket to SSH peer. */
-    tcp_socket(&listenFd); /* Either receive from client application or connect
-                              to server application. */
     tcp_listen(&listenFd, &fwdFromPort, 1);
 
     printf("Connecting to the SSH server...\n");
diff --git a/examples/portfwd/wolfssh_portfwd.h b/examples/portfwd/wolfssh_portfwd.h
index 3a3c5e47a..996360b29 100644
--- a/examples/portfwd/wolfssh_portfwd.h
+++ b/examples/portfwd/wolfssh_portfwd.h
@@ -1,6 +1,6 @@
 /* wolfssh_portfwd.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/examples/scpclient/scpclient.c b/examples/scpclient/scpclient.c
index a23fdef46..042a5b7f4 100644
--- a/examples/scpclient/scpclient.c
+++ b/examples/scpclient/scpclient.c
@@ -1,6 +1,6 @@
 /* scpclient.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -217,7 +217,7 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
         err_sys("Empty path values");
     }
 
-    ret = ClientSetPrivateKey(privKeyName, 0);
+    ret = ClientSetPrivateKey(privKeyName, 0, NULL);
     if (ret != 0) {
         err_sys("Error setting private key");
     }
@@ -225,12 +225,12 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
 #ifdef WOLFSSH_CERTS
     /* passed in certificate to use */
     if (certName) {
-        ret = ClientUseCert(certName);
+        ret = ClientUseCert(certName, NULL);
     }
     else
 #endif
     {
-        ret = ClientUsePubKey(pubKeyName, 0);
+        ret = ClientUsePubKey(pubKeyName, 0, NULL);
     }
     if (ret != 0) {
         err_sys("Error setting public key");
@@ -253,13 +253,13 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
 #endif /* WOLFSSH_CERTS */
 
     wolfSSH_CTX_SetPublicKeyCheck(ctx, ClientPublicKeyCheck);
-    wolfSSH_SetPublicKeyCheckCtx(ssh, (void*)host);
-
 
     ssh = wolfSSH_new(ctx);
     if (ssh == NULL)
         err_sys("Couldn't create wolfSSH session.");
 
+    wolfSSH_SetPublicKeyCheckCtx(ssh, (void*)host);
+
     if (password != NULL)
         wolfSSH_SetUserAuthCtx(ssh, (void*)password);
 
@@ -280,8 +280,9 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
     {
         printf("IPV4 address\n");
         build_addr(&clientAddr, host, port);
-        tcp_socket(&sockFd);
-        ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz);
+        tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family);
+        ret = connect(sockFd, (const struct sockaddr *)&clientAddr,
+                      clientAddrSz);
     }
 
     if (ret != 0)
@@ -312,7 +313,9 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
 
     ret = wolfSSH_shutdown(ssh);
     /* do not continue on with shutdown process if peer already disconnected */
-    if (ret != WS_SOCKET_ERROR_E && wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E) {
+    if (ret != WS_CHANNEL_CLOSED && ret != WS_SOCKET_ERROR_E &&
+            wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E &&
+            wolfSSH_get_error(ssh) != WS_CHANNEL_CLOSED) {
         if (ret != WS_SUCCESS) {
             err_sys("Sending the shutdown messages failed.");
         }
@@ -324,10 +327,12 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args)
     WCLOSESOCKET(sockFd);
     wolfSSH_free(ssh);
     wolfSSH_CTX_free(ctx);
-    if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E)
+    if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E &&
+            ret != WS_CHANNEL_CLOSED) {
         err_sys("Closing scp stream failed. Connection could have been closed by peer");
+    }
 
-    ClientFreeBuffers(pubKeyName, privKeyName);
+    ClientFreeBuffers(pubKeyName, privKeyName, NULL);
 #if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS)
     wc_ecc_fp_free();  /* free per thread cache */
 #endif
diff --git a/examples/scpclient/scpclient.h b/examples/scpclient/scpclient.h
index dcbd9508b..c46e4ce27 100644
--- a/examples/scpclient/scpclient.h
+++ b/examples/scpclient/scpclient.h
@@ -1,6 +1,6 @@
 /* scpclient.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/examples/sftpclient/sftpclient.c b/examples/sftpclient/sftpclient.c
index 01a2308b6..eec4db1a4 100644
--- a/examples/sftpclient/sftpclient.c
+++ b/examples/sftpclient/sftpclient.c
@@ -1,6 +1,6 @@
 /* sftpclient.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -63,6 +63,36 @@ static char* workingDir;
 #define AUTOPILOT_PUT 2
 
 
+#ifdef WOLFSSH_STATIC_MEMORY
+    #include <wolfssl/wolfcrypt/memory.h>
+
+    typedef WOLFSSL_HEAP_HINT SFTPC_HEAP_HINT;
+
+     /* This static buffer is tuned for building with SFTP only. The static
+      * buffer size is calulated by multiplying the pairs of sizeList items
+      * and distList items and summing (32*50 + 128*100 + ...) and adding
+      * the sum of the distList values times the sizeof wc_Memory (rounded up
+      * to a word, 24). This total was 268kb plus change, rounded up to 269. */
+    #ifndef SFTPC_STATIC_SIZES
+        #define SFTPC_STATIC_SIZES 64,128,384,800,3120,8400,17552,33104,131072
+    #endif
+    #ifndef SFTPC_STATIC_DISTS
+        #define SFTPC_STATIC_DISTS 60,100,4,6,5,2,1,2,1
+    #endif
+    #ifndef SFTPC_STATIC_LISTSZ
+        #define SFTPC_STATIC_LISTSZ 9
+    #endif
+    #ifndef SFTPC_STATIC_BUFSZ
+        #define SFTPC_STATIC_BUFSZ (269*1024)
+    #endif
+    static const word32 static_sizeList[] = {SFTPC_STATIC_SIZES};
+    static const word32 static_distList[] = {SFTPC_STATIC_DISTS};
+    static byte static_buffer[SFTPC_STATIC_BUFSZ];
+#else /* WOLFSSH_STATIC_MEMORY */
+    typedef void SFTPC_HEAP_HINT;
+#endif /* WOLFSSH_STATIC_MEMORY */
+
+
 static void err_msg(const char* s)
 {
     printf("%s\n", s);
@@ -1143,7 +1173,7 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
     char* pubKeyName = NULL;
     char* certName = NULL;
     char* caCert   = NULL;
-
+    SFTPC_HEAP_HINT* heap = NULL;
 
     int     argc = ((func_args*)args)->argc;
     char**  argv = ((func_args*)args)->argv;
@@ -1263,7 +1293,17 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
     }
 #endif
 
-    ret = ClientSetPrivateKey(privKeyName, userEcc);
+#ifdef WOLFSSH_STATIC_MEMORY
+    ret = wc_LoadStaticMemory_ex(&heap,
+            SFTPC_STATIC_LISTSZ, static_sizeList, static_distList,
+            static_buffer, sizeof(static_buffer),
+            WOLFMEM_GENERAL, 0);
+    if (ret != 0) {
+        err_sys("Couldn't set up static memory pool.\n");
+    }
+#endif /* WOLFSSH_STATIC_MEMORY */
+
+    ret = ClientSetPrivateKey(privKeyName, userEcc, heap);
     if (ret != 0) {
         err_sys("Error setting private key");
     }
@@ -1271,18 +1311,18 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
 #ifdef WOLFSSH_CERTS
     /* passed in certificate to use */
     if (certName) {
-        ret = ClientUseCert(certName);
+        ret = ClientUseCert(certName, heap);
     }
     else
 #endif
     {
-        ret = ClientUsePubKey(pubKeyName, 0);
+        ret = ClientUsePubKey(pubKeyName, 0, heap);
     }
     if (ret != 0) {
         err_sys("Error setting public key");
     }
 
-    ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
+    ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, heap);
     if (ctx == NULL)
         err_sys("Couldn't create wolfSSH client context.");
 
@@ -1326,7 +1366,8 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
         err_sys("Couldn't set the username.");
 
     build_addr(&clientAddr, host, port);
-    tcp_socket(&sockFd);
+    tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family);
+
     ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz);
     if (ret != 0)
         err_sys("Couldn't connect to server.");
@@ -1394,7 +1435,7 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args)
         ((func_args*)args)->return_code = ret;
     }
 
-    ClientFreeBuffers(pubKeyName, privKeyName);
+    ClientFreeBuffers(pubKeyName, privKeyName, heap);
 #if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS)
     wc_ecc_fp_free();  /* free per thread cache */
 #endif
diff --git a/examples/sftpclient/sftpclient.h b/examples/sftpclient/sftpclient.h
index 1d515a695..03e1e55c9 100644
--- a/examples/sftpclient/sftpclient.h
+++ b/examples/sftpclient/sftpclient.h
@@ -1,6 +1,6 @@
 /* sftpclient.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Espressif/ESP-IDF/default_espressif_options.h b/ide/Espressif/ESP-IDF/default_espressif_options.h
index b1cac4f08..e77d81fff 100644
--- a/ide/Espressif/ESP-IDF/default_espressif_options.h
+++ b/ide/Espressif/ESP-IDF/default_espressif_options.h
@@ -1,7 +1,7 @@
 /* wolfssl options.h
  * generated from configure options
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/CMakeLists.txt b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/CMakeLists.txt
index 09e7badc4..c920d7cea 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/CMakeLists.txt
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/CMakeLists.txt
@@ -1,22 +1,21 @@
 #  [wolfSSH Project]/CMakeLists.txt
 #
-#  Copyright (C) 2006-2023 WOLFSSL Inc.
+#  Copyright (C) 2014-2024 wolfSSL Inc.
 #
-#  This file is part of WOLFSSH.
+#  This file is part of wolfSSH.
 #
-#  WOLFSSH is free software; you can redistribute it and/or modify
+#  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 2 of the License, or
+#  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,
+#  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 this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+#  along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
 #
 # cmake for WOLFSSH Espressif projects
 #
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/README.md b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/README.md
index 8ed8996cc..770bce636 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/README.md
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/README.md
@@ -1,4 +1,4 @@
-# wolfSSL Server Project
+# wolfSSH Server Project
 
 This is an example wolfSSH Server based on the minimally viable wolfSSL [template](../wolfssh_template/README.md)
 
@@ -8,8 +8,6 @@ and the instructions in [wolfssh README.md](https://github.com/wolfSSL/wolfssh#r
 To connect:
 
 ```bash
-TODO
-
 ssh -p 22222 jack@192.168.1.32
 ```
 
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssh/CMakeLists.txt b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssh/CMakeLists.txt
index c3e5d37ab..11b8d6a49 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssh/CMakeLists.txt
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssh/CMakeLists.txt
@@ -1,22 +1,21 @@
 #  [wolfSSL Project]/components/wolfssh/CMakeLists.txt
 #
-#  Copyright (C) 2006-2023 WOLFSSL Inc.
+#  Copyright (C) 2014-2024 wolfSSL Inc.
 #
-#  This file is part of WOLFSSH.
+#  This file is part of wolfSSH.
 #
-#  WOLFSSH is free software; you can redistribute it and/or modify
+#  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 2 of the License, or
+#  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,
+#  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 this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+#  along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
 #
 # cmake for WOLFSSH Espressif projects v5.6.6 r1
 #
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssl/CMakeLists.txt b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssl/CMakeLists.txt
index d58704b84..6ee31d2bf 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssl/CMakeLists.txt
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssl/CMakeLists.txt
@@ -1,25 +1,24 @@
 #
-#  Copyright (C) 2006-2023 wolfSSL Inc.
+#  Copyright (C) 2014-2024 wolfSSL Inc.
 #
-#  This file is part of wolfSSL.
+#  This file is part of wolfSSH.
 #
-#  wolfSSL is free software; you can redistribute it and/or modify
+#  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 2 of the License, or
+#  the Free Software Foundation; either version 3 of the License, or
 #  (at your option) any later version.
 #
-#  wolfSSL is distributed in the hope that it will be useful,
+#  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 this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+#  along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
 #
 # cmake for wolfssl Espressif projects
 #
-# Version 5.6.4.016 for improved manual setting of WOLFSSL_ROOT + ESP8266 support; optional esp-timer / driver components
+# Version 5.7.0 template update + THIS_IDF_PATH
 #
 # See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html
 #
@@ -35,11 +34,18 @@ set(CMAKE_CURRENT_SOURCE_DIR ".")
 # set(COMPONENT_REQUIRES lwip) # we typically don't need lwip directly in wolfssl component
 
 # Optionally set your source to wolfSSL in your project CMakeLists.txt like this:
-# set(WOLFSSL_ROOT "c:/test/blogtest/wolfssl" )
+# set(WOLFSSL_ROOT "c:/test/my_wolfssl" )
 
 if ( "${WOLFSSL_ROOT}" STREQUAL "")
     set(WOLFSSL_ROOT "$ENV{WOLFSSL_ROOT}" )
 endif()
+
+if(  "$ENV{IDF_PATH}" STREQUAL "" )
+     message(FATAL_ERROR "IDF_PATH Environment variable not set!")
+else()
+    string(REPLACE "\\" "/" THIS_IDF_PATH "$ENV{IDF_PATH}")
+endif()
+
 # Optional compiler definitions to help with system name detection (typically printed by app diagnostics)
 if(VERBOSE_COMPONENT_MESSAGES)
     if(WIN32)
@@ -195,16 +201,47 @@ function(FIND_WOLFSSL_DIRECTORY OUTPUT_FOUND_WOLFSSL_DIRECTORY)
             return()
         endif()
 
+        # Maintain CURRENT_SEARCH_DIR, but check various suffixes with CURRENT_SEARCH_DIR_ALT
         if( THIS_USER )
             # Check for "wolfssl-[username]" subdirectory as we recurse up the directory tree
             set(CURRENT_SEARCH_DIR_ALT ${CURRENT_SEARCH_DIR}/wolfssl-${THIS_USER})
-            message(STATUS "Looking in ${CURRENT_SEARCH_DIR}")
+            message(STATUS "Looking in ${CURRENT_SEARCH_DIR_ALT}")
+
+            IS_WOLFSSL_SOURCE("${CURRENT_SEARCH_DIR_ALT}" FOUND_WOLFSSL )
+            if ( FOUND_WOLFSSL )
+                message(STATUS "Found wolfssl in user-suffix CURRENT_SEARCH_DIR_ALT = ${CURRENT_SEARCH_DIR_ALT}")
+                set(CURRENT_SEARCH_DIR "${CURRENT_SEARCH_DIR_ALT}")
+                set(${OUTPUT_FOUND_WOLFSSL_DIRECTORY} ${CURRENT_SEARCH_DIR} PARENT_SCOPE)
+                return()
+            endif()
+        endif()
+
+        if ( FOUND_WOLFSSL )
+            # if we already found the source, skip attempt of "wolfssl-master"
+        else()
+            set(CURRENT_SEARCH_DIR_ALT ${CURRENT_SEARCH_DIR}/wolfssl-master)
+            message(STATUS "Looking in ${CURRENT_SEARCH_DIR_ALT}")
+
+            IS_WOLFSSL_SOURCE("${CURRENT_SEARCH_DIR_ALT}" FOUND_WOLFSSL )
+            if ( FOUND_WOLFSSL )
+                message(STATUS "Found wolfssl in master-suffix CURRENT_SEARCH_DIR_ALT = ${CURRENT_SEARCH_DIR_ALT}")
+                set(CURRENT_SEARCH_DIR "${CURRENT_SEARCH_DIR_ALT}")
+                set(${OUTPUT_FOUND_WOLFSSL_DIRECTORY} ${CURRENT_SEARCH_DIR} PARENT_SCOPE)
+                return()
+            endif()
+        endif()
+
+        if ( FOUND_WOLFSSL )
+            # if we already found the source, skip attempt of "wolfssl"
+        else()
+            set(CURRENT_SEARCH_DIR_ALT ${CURRENT_SEARCH_DIR}/wolfssl)
+            message(STATUS "Looking in ${CURRENT_SEARCH_DIR_ALT}")
 
-            #if(EXISTS ${CURRENT_SEARCH_DIR_ALT} AND IS_DIRECTORY ${CURRENT_SEARCH_DIR_ALT} AND EXISTS "${CURRENT_SEARCH_DIR_ALT}/wolfcrypt/src")
             IS_WOLFSSL_SOURCE("${CURRENT_SEARCH_DIR_ALT}" FOUND_WOLFSSL )
             if ( FOUND_WOLFSSL )
-               message(STATUS "Found wolfssl in user-suffix CURRENT_SEARCH_DIR_ALT = ${CURRENT_SEARCH_DIR_ALT}")
-                set(${OUTPUT_FOUND_WOLFSSL_DIRECTORY} ${CURRENT_SEARCH_DIR_ALT} PARENT_SCOPE)
+                message(STATUS "Found wolfssl in CURRENT_SEARCH_DIR_ALT = ${CURRENT_SEARCH_DIR_ALT}")
+                set(CURRENT_SEARCH_DIR "${CURRENT_SEARCH_DIR_ALT}")
+                set(${OUTPUT_FOUND_WOLFSSL_DIRECTORY} ${CURRENT_SEARCH_DIR} PARENT_SCOPE)
                 return()
             endif()
         endif()
@@ -224,7 +261,8 @@ function(FIND_WOLFSSL_DIRECTORY OUTPUT_FOUND_WOLFSSL_DIRECTORY)
         get_filename_component(CURRENT_SEARCH_DIR "${CURRENT_SEARCH_DIR}" DIRECTORY)
         message(STATUS "Next CURRENT_SEARCH_DIR = ${CURRENT_SEARCH_DIR}")
         if( "${PRIOR_SEARCH_DIR}" STREQUAL "${CURRENT_SEARCH_DIR}" )
-            # when the search directory is empty, we'll give up
+            # When the parent is current directory, cannot go any further. We didn't find wolfssl.
+            # When the search directory is empty, we'll give up.
             set(CURRENT_SEARCH_DIR "")
         endif()
     endwhile()
@@ -240,13 +278,42 @@ endfunction()
 #   FIND_WOLFSSL_DIRECTORY(WOLFSSL_ROOT)
 #
 
+message(STATUS "CONFIG_TARGET_PLATFORM = ${CONFIG_TARGET_PLATFORM}")
+
+if (0)
+    get_cmake_property(_variableNames VARIABLES)
+    list (SORT _variableNames)
+    message(STATUS "")
+    message(STATUS "ALL VARIABLES BEGIN")
+    message(STATUS "")
+    foreach (_variableName ${_variableNames})
+        message(STATUS "${_variableName}=${${_variableName}}")
+    endforeach()
+    message(STATUS "")
+    message(STATUS "ALL VARIABLES END")
+    message(STATUS "")
+endif()
+
+if ( ("${CONFIG_TARGET_PLATFORM}" STREQUAL "esp8266") OR ("${IDF_TARGET}" STREQUAL "esp8266") )
+    # There's no esp_timer, no driver components for the ESP8266
+    message(STATUS "Early expansion EXCLUDES esp_timer: ${THIS_INCLUDE_TIMER}")
+    message(STATUS "Early expansion EXCLUDES driver: ${THIS_INCLUDE_DRIVER}")
+    set(THIS_INCLUDE_TIMER "")
+    set(THIS_INCLUDE_DRIVER "")
+else()
+    message(STATUS "Early expansion includes esp_timer: ${THIS_INCLUDE_TIMER}")
+    message(STATUS "Early expansion includes driver: ${THIS_INCLUDE_DRIVER}")
+    set(THIS_INCLUDE_TIMER "esp_timer")
+    set(THIS_INCLUDE_DRIVER "driver")
+endif()
+
 if(CMAKE_BUILD_EARLY_EXPANSION)
     message(STATUS "wolfssl component CMAKE_BUILD_EARLY_EXPANSION:")
     idf_component_register(
                             REQUIRES "${COMPONENT_REQUIRES}"
                             PRIV_REQUIRES # esp_hw_support
-                                          "${THIS_INCLUDE_TIMER}"
-                                          "${THIS_INCLUDE_DRIVER}" # this will typically only be needed for wolfSSL benchmark
+                                          ${THIS_INCLUDE_TIMER}
+                                          ${THIS_INCLUDE_DRIVER} # this will typically only be needed for wolfSSL benchmark
                            )
 
 else()
@@ -255,18 +322,8 @@ else()
     message(STATUS "wolfssl component config:")
     message(STATUS "************************************************************************************************")
 
-    if ( "${CONFIG_TARGET_PLATFORM}" STREQUAL "esp8266")
-        # There's no esp_timer, no driver components for the ESP8266
-        set(THIS_INCLUDE_TIMER "")
-        set(THIS_INCLUDE_DRIVER "")
-    else()
-        set(THIS_INCLUDE_TIMER "esp_timer")
-        set(THIS_INCLUDE_DRIVER "driver")
-    endif()
-
     # search for wolfSSL
-    # TODO allow for cmake prior def
-
+    FIND_WOLFSSL_DIRECTORY(WOLFSSL_ROOT)
     if(WOLFSSL_ROOT)
         IS_WOLFSSL_SOURCE("${WOLFSSL_ROOT}" FOUND_WOLFSSL)
         if(FOUND_WOLFSSL)
@@ -288,8 +345,8 @@ else()
     else()
         message(STATUS "Failed: wolfssl directory not found.")
         # Abort. We need wolfssl _somewhere_.
-        message(FATAL_ERROR "Could not find wolfssl in ${WOLFSSL_ROOT}.\n"
-                            "Try setting WOLFSSL_ROOT environment variable or git clone.")
+        message(FATAL_ERROR "Could not find wolfssl in any parent directory named wolfssl-${THIS_USER}, wolfssl-master, or wolfssl.\n"
+                            "Try setting WOLFSSL_ROOT environment variable, cmake variable in project, copy source, or use managed components.")
         # Abort CMake after fatal error.
     endif()
 
@@ -327,6 +384,7 @@ else()
 
     # wolfSSL user_settings.h is in the local project.
     set(WOLFSSL_PROJECT_DIR "${CMAKE_HOME_DIRECTORY}/components/wolfssl")
+    # add_definitions(-DWOLFSSL_USER_SETTINGS_DIR="${WOLFSSL_PROJECT_DIR}/include/user_settings.h")
 
     string(REPLACE "/" "//" STR_WOLFSSL_PROJECT_DIR "${WOLFSSL_PROJECT_DIR}")
     add_definitions(-DWOLFSSL_USER_SETTINGS_DIR="${STR_WOLFSSL_PROJECT_DIR}//include//user_settings.h")
@@ -485,10 +543,10 @@ else()
             message(STATUS "Could not find RTOS path")
         endif()
     endif()
-
+    message(STATUS "THIS_IDF_PATH = $THIS_IDF_PATH")
     # wolfSSL-specific include directories
     set(COMPONENT_ADD_INCLUDEDIRS
-        "./include" # this is the location of local project wolfssl user_settings.h
+        "./include" # this is the location of wolfssl user_settings.h
         "\"${WOLFSSL_ROOT}/\""
         "\"${WOLFSSL_ROOT}/wolfssl/\""
         "\"${WOLFSSL_ROOT}/wolfssl/wolfcrypt/\""
@@ -515,9 +573,14 @@ else()
         "\"${WOLFSSL_ROOT}/src/ssl_bn.c\""      # included by ssl.c
         "\"${WOLFSSL_ROOT}/src/ssl_certman.c\"" # included by ssl.c
         "\"${WOLFSSL_ROOT}/src/ssl_crypto.c\""  # included by ssl.c
+        "\"${WOLFSSL_ROOT}/src/ssl_load.c\""    # included by ssl.c
         "\"${WOLFSSL_ROOT}/src/ssl_misc.c\""    # included by ssl.c
+        "\"${WOLFSSL_ROOT}/src/ssl_p7p12.c\""   # included by ssl.c
+        "\"${WOLFSSL_ROOT}/src/ssl_sess.c\""    # included by ssl.c
         "\"${WOLFSSL_ROOT}/src/x509.c\""
         "\"${WOLFSSL_ROOT}/src/x509_str.c\""
+        "\"${WOLFSSL_ROOT}/wolfcrypt/src/ext_kyber.c\""     # external Kyber disabled by default
+        "\"${WOLFSSL_ROOT}/wolfssl/wolfcrypt/ext_kyber.h\"" # external Kyber disabled by default
         "\"${WOLFSSL_ROOT}/wolfcrypt/src/evp.c\""
         "\"${WOLFSSL_ROOT}/wolfcrypt/src/misc.c\""
         "\"${WOLFSSL_ROOT}/wolfcrypt/src/sp_sm2_arm32.c\""
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssl/include/user_settings.h b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssl/include/user_settings.h
index 70c35493c..d5509d72d 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssl/include/user_settings.h
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/components/wolfssl/include/user_settings.h
@@ -1,22 +1,21 @@
 /* user_settings.h
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <sdkconfig.h> /* essential to chip set detection */
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/CMakeLists.txt b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/CMakeLists.txt
index d58c2ae1c..0945f3222 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/CMakeLists.txt
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/CMakeLists.txt
@@ -1,22 +1,21 @@
 #  [wolfSSL Project]/main/CMakeLists.txt
 #
-#  Copyright (C) 2006-2023 WOLFSSL Inc.
+#  Copyright (C) 2014-2024 wolfSSL Inc.
 #
-#  This file is part of WOLFSSH.
+#  This file is part of wolfSSH.
 #
-#  WOLFSSH is free software; you can redistribute it and/or modify
+#  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 2 of the License, or
+#  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,
+#  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 this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+#  along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
 #
 # cmake for WOLFSSH Espressif projects
 #
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c
index 2eae60762..49c90d42d 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/echoserver.c
@@ -1,6 +1,6 @@
 /* echoserver.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/echoserver.h b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/echoserver.h
index ac7e17cf2..91a9bf2dc 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/echoserver.h
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/echoserver.h
@@ -1,6 +1,6 @@
 /* echoserver.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -25,7 +25,7 @@
 #include <types.h>
 
 #ifndef WOLFSSH_THREAD
-    #define WOLFSSH_THREAD
+    #define WOLFSSH_THREAD WOLFSSL_THREAD
 #endif
 
 THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args);
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/main.h b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/main.h
index 7e07ec1df..73d227693 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/main.h
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/main.h
@@ -1,22 +1,21 @@
 /* template main.h
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 #ifndef _MAIN_H_
 #define _MAIN_H_
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/time_helper.h b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/time_helper.h
index a47f94001..e244ddd17 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/time_helper.h
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/time_helper.h
@@ -1,21 +1,20 @@
 /*
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 /* common Espressif time_helper v5.6.3.001 */
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/wifi_connect.h b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/wifi_connect.h
index a0014d4c3..9ac4d7f58 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/wifi_connect.h
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/include/wifi_connect.h
@@ -1,22 +1,21 @@
 /* wifi_connect.h
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 #ifndef _WIFI_CONNECT_H_
 #define _WIFI_CONNECT_H_
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/main.c b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/main.c
index 238720af9..b1c9835a7 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/main.c
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/main.c
@@ -1,22 +1,21 @@
 /* main.c
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 #include "sdkconfig.h"
 #include "main.h"
@@ -35,16 +34,18 @@
     #warning "Check components/wolfssl/include"
 #endif
 
+/* wolfSSH */
+#include <wolfssh/test.h>
+
 /* this project */
 #include "wifi_connect.h"
 #include "time_helper.h"
 
 static const char* const TAG = "My Project";
 
-
 void app_main(void)
 {
-    void*  args = {0};
+    func_args args = {0};
     int ret = ESP_OK;
 
     ESP_LOGI(TAG, "------------ wolfSSL wolfSSH template Example ----------");
@@ -158,6 +159,7 @@ void app_main(void)
     /* TODO: Consider pulling in wolfSSH server.c example source automatically:
      * Keep in mind the nature of this example as an Espressif Component.
      * See https://github.com/wolfSSL/wolfssh/tree/master/examples/server */
+    memset(&args, 0, sizeof(func_args));
     echoserver_test(&args);
 
     ESP_LOGI(TAG, "\n\nDone!"
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/time_helper.c b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/time_helper.c
index 0abf70c5a..498c53d78 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/time_helper.c
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/time_helper.c
@@ -1,22 +1,21 @@
 /* time_helper.c
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 /* common Espressif time_helper v5.6.3.002 */
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/wifi_connect.c b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/wifi_connect.c
index 384a86274..973eb4fff 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/wifi_connect.c
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_echoserver/main/wifi_connect.c
@@ -1,22 +1,21 @@
 /* wifi_connect.c
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
  #include "wifi_connect.h"
 
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssh/CMakeLists.txt b/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssh/CMakeLists.txt
index c3e5d37ab..11b8d6a49 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssh/CMakeLists.txt
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssh/CMakeLists.txt
@@ -1,22 +1,21 @@
 #  [wolfSSL Project]/components/wolfssh/CMakeLists.txt
 #
-#  Copyright (C) 2006-2023 WOLFSSL Inc.
+#  Copyright (C) 2014-2024 wolfSSL Inc.
 #
-#  This file is part of WOLFSSH.
+#  This file is part of wolfSSH.
 #
-#  WOLFSSH is free software; you can redistribute it and/or modify
+#  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 2 of the License, or
+#  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,
+#  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 this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+#  along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
 #
 # cmake for WOLFSSH Espressif projects v5.6.6 r1
 #
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssl/CMakeLists.txt b/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssl/CMakeLists.txt
index d58704b84..6ee31d2bf 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssl/CMakeLists.txt
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssl/CMakeLists.txt
@@ -1,25 +1,24 @@
 #
-#  Copyright (C) 2006-2023 wolfSSL Inc.
+#  Copyright (C) 2014-2024 wolfSSL Inc.
 #
-#  This file is part of wolfSSL.
+#  This file is part of wolfSSH.
 #
-#  wolfSSL is free software; you can redistribute it and/or modify
+#  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 2 of the License, or
+#  the Free Software Foundation; either version 3 of the License, or
 #  (at your option) any later version.
 #
-#  wolfSSL is distributed in the hope that it will be useful,
+#  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 this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+#  along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
 #
 # cmake for wolfssl Espressif projects
 #
-# Version 5.6.4.016 for improved manual setting of WOLFSSL_ROOT + ESP8266 support; optional esp-timer / driver components
+# Version 5.7.0 template update + THIS_IDF_PATH
 #
 # See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/build-system.html
 #
@@ -35,11 +34,18 @@ set(CMAKE_CURRENT_SOURCE_DIR ".")
 # set(COMPONENT_REQUIRES lwip) # we typically don't need lwip directly in wolfssl component
 
 # Optionally set your source to wolfSSL in your project CMakeLists.txt like this:
-# set(WOLFSSL_ROOT "c:/test/blogtest/wolfssl" )
+# set(WOLFSSL_ROOT "c:/test/my_wolfssl" )
 
 if ( "${WOLFSSL_ROOT}" STREQUAL "")
     set(WOLFSSL_ROOT "$ENV{WOLFSSL_ROOT}" )
 endif()
+
+if(  "$ENV{IDF_PATH}" STREQUAL "" )
+     message(FATAL_ERROR "IDF_PATH Environment variable not set!")
+else()
+    string(REPLACE "\\" "/" THIS_IDF_PATH "$ENV{IDF_PATH}")
+endif()
+
 # Optional compiler definitions to help with system name detection (typically printed by app diagnostics)
 if(VERBOSE_COMPONENT_MESSAGES)
     if(WIN32)
@@ -195,16 +201,47 @@ function(FIND_WOLFSSL_DIRECTORY OUTPUT_FOUND_WOLFSSL_DIRECTORY)
             return()
         endif()
 
+        # Maintain CURRENT_SEARCH_DIR, but check various suffixes with CURRENT_SEARCH_DIR_ALT
         if( THIS_USER )
             # Check for "wolfssl-[username]" subdirectory as we recurse up the directory tree
             set(CURRENT_SEARCH_DIR_ALT ${CURRENT_SEARCH_DIR}/wolfssl-${THIS_USER})
-            message(STATUS "Looking in ${CURRENT_SEARCH_DIR}")
+            message(STATUS "Looking in ${CURRENT_SEARCH_DIR_ALT}")
+
+            IS_WOLFSSL_SOURCE("${CURRENT_SEARCH_DIR_ALT}" FOUND_WOLFSSL )
+            if ( FOUND_WOLFSSL )
+                message(STATUS "Found wolfssl in user-suffix CURRENT_SEARCH_DIR_ALT = ${CURRENT_SEARCH_DIR_ALT}")
+                set(CURRENT_SEARCH_DIR "${CURRENT_SEARCH_DIR_ALT}")
+                set(${OUTPUT_FOUND_WOLFSSL_DIRECTORY} ${CURRENT_SEARCH_DIR} PARENT_SCOPE)
+                return()
+            endif()
+        endif()
+
+        if ( FOUND_WOLFSSL )
+            # if we already found the source, skip attempt of "wolfssl-master"
+        else()
+            set(CURRENT_SEARCH_DIR_ALT ${CURRENT_SEARCH_DIR}/wolfssl-master)
+            message(STATUS "Looking in ${CURRENT_SEARCH_DIR_ALT}")
+
+            IS_WOLFSSL_SOURCE("${CURRENT_SEARCH_DIR_ALT}" FOUND_WOLFSSL )
+            if ( FOUND_WOLFSSL )
+                message(STATUS "Found wolfssl in master-suffix CURRENT_SEARCH_DIR_ALT = ${CURRENT_SEARCH_DIR_ALT}")
+                set(CURRENT_SEARCH_DIR "${CURRENT_SEARCH_DIR_ALT}")
+                set(${OUTPUT_FOUND_WOLFSSL_DIRECTORY} ${CURRENT_SEARCH_DIR} PARENT_SCOPE)
+                return()
+            endif()
+        endif()
+
+        if ( FOUND_WOLFSSL )
+            # if we already found the source, skip attempt of "wolfssl"
+        else()
+            set(CURRENT_SEARCH_DIR_ALT ${CURRENT_SEARCH_DIR}/wolfssl)
+            message(STATUS "Looking in ${CURRENT_SEARCH_DIR_ALT}")
 
-            #if(EXISTS ${CURRENT_SEARCH_DIR_ALT} AND IS_DIRECTORY ${CURRENT_SEARCH_DIR_ALT} AND EXISTS "${CURRENT_SEARCH_DIR_ALT}/wolfcrypt/src")
             IS_WOLFSSL_SOURCE("${CURRENT_SEARCH_DIR_ALT}" FOUND_WOLFSSL )
             if ( FOUND_WOLFSSL )
-               message(STATUS "Found wolfssl in user-suffix CURRENT_SEARCH_DIR_ALT = ${CURRENT_SEARCH_DIR_ALT}")
-                set(${OUTPUT_FOUND_WOLFSSL_DIRECTORY} ${CURRENT_SEARCH_DIR_ALT} PARENT_SCOPE)
+                message(STATUS "Found wolfssl in CURRENT_SEARCH_DIR_ALT = ${CURRENT_SEARCH_DIR_ALT}")
+                set(CURRENT_SEARCH_DIR "${CURRENT_SEARCH_DIR_ALT}")
+                set(${OUTPUT_FOUND_WOLFSSL_DIRECTORY} ${CURRENT_SEARCH_DIR} PARENT_SCOPE)
                 return()
             endif()
         endif()
@@ -224,7 +261,8 @@ function(FIND_WOLFSSL_DIRECTORY OUTPUT_FOUND_WOLFSSL_DIRECTORY)
         get_filename_component(CURRENT_SEARCH_DIR "${CURRENT_SEARCH_DIR}" DIRECTORY)
         message(STATUS "Next CURRENT_SEARCH_DIR = ${CURRENT_SEARCH_DIR}")
         if( "${PRIOR_SEARCH_DIR}" STREQUAL "${CURRENT_SEARCH_DIR}" )
-            # when the search directory is empty, we'll give up
+            # When the parent is current directory, cannot go any further. We didn't find wolfssl.
+            # When the search directory is empty, we'll give up.
             set(CURRENT_SEARCH_DIR "")
         endif()
     endwhile()
@@ -240,13 +278,42 @@ endfunction()
 #   FIND_WOLFSSL_DIRECTORY(WOLFSSL_ROOT)
 #
 
+message(STATUS "CONFIG_TARGET_PLATFORM = ${CONFIG_TARGET_PLATFORM}")
+
+if (0)
+    get_cmake_property(_variableNames VARIABLES)
+    list (SORT _variableNames)
+    message(STATUS "")
+    message(STATUS "ALL VARIABLES BEGIN")
+    message(STATUS "")
+    foreach (_variableName ${_variableNames})
+        message(STATUS "${_variableName}=${${_variableName}}")
+    endforeach()
+    message(STATUS "")
+    message(STATUS "ALL VARIABLES END")
+    message(STATUS "")
+endif()
+
+if ( ("${CONFIG_TARGET_PLATFORM}" STREQUAL "esp8266") OR ("${IDF_TARGET}" STREQUAL "esp8266") )
+    # There's no esp_timer, no driver components for the ESP8266
+    message(STATUS "Early expansion EXCLUDES esp_timer: ${THIS_INCLUDE_TIMER}")
+    message(STATUS "Early expansion EXCLUDES driver: ${THIS_INCLUDE_DRIVER}")
+    set(THIS_INCLUDE_TIMER "")
+    set(THIS_INCLUDE_DRIVER "")
+else()
+    message(STATUS "Early expansion includes esp_timer: ${THIS_INCLUDE_TIMER}")
+    message(STATUS "Early expansion includes driver: ${THIS_INCLUDE_DRIVER}")
+    set(THIS_INCLUDE_TIMER "esp_timer")
+    set(THIS_INCLUDE_DRIVER "driver")
+endif()
+
 if(CMAKE_BUILD_EARLY_EXPANSION)
     message(STATUS "wolfssl component CMAKE_BUILD_EARLY_EXPANSION:")
     idf_component_register(
                             REQUIRES "${COMPONENT_REQUIRES}"
                             PRIV_REQUIRES # esp_hw_support
-                                          "${THIS_INCLUDE_TIMER}"
-                                          "${THIS_INCLUDE_DRIVER}" # this will typically only be needed for wolfSSL benchmark
+                                          ${THIS_INCLUDE_TIMER}
+                                          ${THIS_INCLUDE_DRIVER} # this will typically only be needed for wolfSSL benchmark
                            )
 
 else()
@@ -255,18 +322,8 @@ else()
     message(STATUS "wolfssl component config:")
     message(STATUS "************************************************************************************************")
 
-    if ( "${CONFIG_TARGET_PLATFORM}" STREQUAL "esp8266")
-        # There's no esp_timer, no driver components for the ESP8266
-        set(THIS_INCLUDE_TIMER "")
-        set(THIS_INCLUDE_DRIVER "")
-    else()
-        set(THIS_INCLUDE_TIMER "esp_timer")
-        set(THIS_INCLUDE_DRIVER "driver")
-    endif()
-
     # search for wolfSSL
-    # TODO allow for cmake prior def
-
+    FIND_WOLFSSL_DIRECTORY(WOLFSSL_ROOT)
     if(WOLFSSL_ROOT)
         IS_WOLFSSL_SOURCE("${WOLFSSL_ROOT}" FOUND_WOLFSSL)
         if(FOUND_WOLFSSL)
@@ -288,8 +345,8 @@ else()
     else()
         message(STATUS "Failed: wolfssl directory not found.")
         # Abort. We need wolfssl _somewhere_.
-        message(FATAL_ERROR "Could not find wolfssl in ${WOLFSSL_ROOT}.\n"
-                            "Try setting WOLFSSL_ROOT environment variable or git clone.")
+        message(FATAL_ERROR "Could not find wolfssl in any parent directory named wolfssl-${THIS_USER}, wolfssl-master, or wolfssl.\n"
+                            "Try setting WOLFSSL_ROOT environment variable, cmake variable in project, copy source, or use managed components.")
         # Abort CMake after fatal error.
     endif()
 
@@ -327,6 +384,7 @@ else()
 
     # wolfSSL user_settings.h is in the local project.
     set(WOLFSSL_PROJECT_DIR "${CMAKE_HOME_DIRECTORY}/components/wolfssl")
+    # add_definitions(-DWOLFSSL_USER_SETTINGS_DIR="${WOLFSSL_PROJECT_DIR}/include/user_settings.h")
 
     string(REPLACE "/" "//" STR_WOLFSSL_PROJECT_DIR "${WOLFSSL_PROJECT_DIR}")
     add_definitions(-DWOLFSSL_USER_SETTINGS_DIR="${STR_WOLFSSL_PROJECT_DIR}//include//user_settings.h")
@@ -485,10 +543,10 @@ else()
             message(STATUS "Could not find RTOS path")
         endif()
     endif()
-
+    message(STATUS "THIS_IDF_PATH = $THIS_IDF_PATH")
     # wolfSSL-specific include directories
     set(COMPONENT_ADD_INCLUDEDIRS
-        "./include" # this is the location of local project wolfssl user_settings.h
+        "./include" # this is the location of wolfssl user_settings.h
         "\"${WOLFSSL_ROOT}/\""
         "\"${WOLFSSL_ROOT}/wolfssl/\""
         "\"${WOLFSSL_ROOT}/wolfssl/wolfcrypt/\""
@@ -515,9 +573,14 @@ else()
         "\"${WOLFSSL_ROOT}/src/ssl_bn.c\""      # included by ssl.c
         "\"${WOLFSSL_ROOT}/src/ssl_certman.c\"" # included by ssl.c
         "\"${WOLFSSL_ROOT}/src/ssl_crypto.c\""  # included by ssl.c
+        "\"${WOLFSSL_ROOT}/src/ssl_load.c\""    # included by ssl.c
         "\"${WOLFSSL_ROOT}/src/ssl_misc.c\""    # included by ssl.c
+        "\"${WOLFSSL_ROOT}/src/ssl_p7p12.c\""   # included by ssl.c
+        "\"${WOLFSSL_ROOT}/src/ssl_sess.c\""    # included by ssl.c
         "\"${WOLFSSL_ROOT}/src/x509.c\""
         "\"${WOLFSSL_ROOT}/src/x509_str.c\""
+        "\"${WOLFSSL_ROOT}/wolfcrypt/src/ext_kyber.c\""     # external Kyber disabled by default
+        "\"${WOLFSSL_ROOT}/wolfssl/wolfcrypt/ext_kyber.h\"" # external Kyber disabled by default
         "\"${WOLFSSL_ROOT}/wolfcrypt/src/evp.c\""
         "\"${WOLFSSL_ROOT}/wolfcrypt/src/misc.c\""
         "\"${WOLFSSL_ROOT}/wolfcrypt/src/sp_sm2_arm32.c\""
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssl/include/user_settings.h b/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssl/include/user_settings.h
index 41f588a01..6d0e197ef 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssl/include/user_settings.h
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_template/components/wolfssl/include/user_settings.h
@@ -1,22 +1,21 @@
 /* user_settings.h
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <sdkconfig.h> /* essential to chip set detection */
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/CMakeLists.txt b/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/CMakeLists.txt
index 9ae9d6bf7..0021fd7e3 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/CMakeLists.txt
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/CMakeLists.txt
@@ -1,22 +1,21 @@
 #  [wolfSSL Project]/main/CMakeLists.txt
 #
-#  Copyright (C) 2006-2023 WOLFSSL Inc.
+#  Copyright (C) 2014-2024 wolfSSL Inc.
 #
-#  This file is part of WOLFSSH.
+#  This file is part of wolfSSH.
 #
-#  WOLFSSH is free software; you can redistribute it and/or modify
+#  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 2 of the License, or
+#  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,
+#  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 this program; if not, write to the Free Software
-#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+#  along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
 #
 # cmake for WOLFSSH Espressif projects
 #
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/include/main.h b/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/include/main.h
index 7e07ec1df..73d227693 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/include/main.h
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/include/main.h
@@ -1,22 +1,21 @@
 /* template main.h
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 #ifndef _MAIN_H_
 #define _MAIN_H_
diff --git a/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/main.c b/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/main.c
index 6204cc398..af6f87cce 100644
--- a/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/main.c
+++ b/ide/Espressif/ESP-IDF/examples/wolfssh_template/main/main.c
@@ -1,22 +1,21 @@
 /* main.c
  *
- * Copyright (C) 2006-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
- * This file is part of wolfSSL.
+ * This file is part of wolfSSH.
  *
- * wolfSSL is free software; you can redistribute it and/or modify
+ * 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 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
- * wolfSSL is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
+ * along with wolfSSH.  If not, see <http://www.gnu.org/licenses/>.
  */
 #include "main.h"
 
diff --git a/ide/IAR-EWARM/Projects/lib/myFilesystem.h b/ide/IAR-EWARM/Projects/lib/myFilesystem.h
index 0fcdacc87..0a38ea469 100644
--- a/ide/IAR-EWARM/Projects/lib/myFilesystem.h
+++ b/ide/IAR-EWARM/Projects/lib/myFilesystem.h
@@ -1,6 +1,6 @@
 /* dummy_filesystem.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Renesas/cs+/common/strings.h b/ide/Renesas/cs+/common/strings.h
index 0c46b4f10..efe786a23 100644
--- a/ide/Renesas/cs+/common/strings.h
+++ b/ide/Renesas/cs+/common/strings.h
@@ -1,6 +1,6 @@
 /* strings.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Renesas/cs+/common/unistd.h b/ide/Renesas/cs+/common/unistd.h
index 71943bce1..d108f898a 100644
--- a/ide/Renesas/cs+/common/unistd.h
+++ b/ide/Renesas/cs+/common/unistd.h
@@ -1,6 +1,6 @@
 /* unistd.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Renesas/cs+/common/user_settings.h b/ide/Renesas/cs+/common/user_settings.h
index 8553ce715..5e43a8264 100644
--- a/ide/Renesas/cs+/common/user_settings.h
+++ b/ide/Renesas/cs+/common/user_settings.h
@@ -1,6 +1,6 @@
 /* user_settings.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Renesas/cs+/common/wolfssh_csplus_usersettings.h b/ide/Renesas/cs+/common/wolfssh_csplus_usersettings.h
index 2a54ccaea..c37ec20f8 100644
--- a/ide/Renesas/cs+/common/wolfssh_csplus_usersettings.h
+++ b/ide/Renesas/cs+/common/wolfssh_csplus_usersettings.h
@@ -1,6 +1,6 @@
 /* wolfssh_csplus_usersettings..h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Renesas/cs+/demo_server/wolfssh_demo.c b/ide/Renesas/cs+/demo_server/wolfssh_demo.c
index b9f6b0aea..866be2f76 100644
--- a/ide/Renesas/cs+/demo_server/wolfssh_demo.c
+++ b/ide/Renesas/cs+/demo_server/wolfssh_demo.c
@@ -1,6 +1,6 @@
 /* wolfssh_demo.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Renesas/cs+/demo_server/wolfssh_demo.h b/ide/Renesas/cs+/demo_server/wolfssh_demo.h
index 2eeb34e02..e9becd139 100644
--- a/ide/Renesas/cs+/demo_server/wolfssh_demo.h
+++ b/ide/Renesas/cs+/demo_server/wolfssh_demo.h
@@ -1,6 +1,6 @@
 /* wolfssh_demo.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/Renesas/cs+/demo_server/wolfssh_dummy.c b/ide/Renesas/cs+/demo_server/wolfssh_dummy.c
index 4ccac07a8..3edaff78e 100644
--- a/ide/Renesas/cs+/demo_server/wolfssh_dummy.c
+++ b/ide/Renesas/cs+/demo_server/wolfssh_dummy.c
@@ -1,6 +1,6 @@
 /* wolfssh_dummy.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/STM32CUBE/default_conf.ftl b/ide/STM32CUBE/default_conf.ftl
index 0de5bf134..78e70f564 100644
--- a/ide/STM32CUBE/default_conf.ftl
+++ b/ide/STM32CUBE/default_conf.ftl
@@ -106,6 +106,7 @@ extern ${variable.value} ${variable.name};
 #define WOLFSSH_LOG_PRINTF
 #define WOLFSSL_LOG_PRINTF
 #define fprintf(err, ... ) printf(__VA_ARGS__)
+#define WFFLUSH fflush
 
 #define BENCH_EMBEDDED
 #define NO_WRITEV
@@ -138,8 +139,6 @@ extern ${variable.value} ${variable.name};
 #define HAVE_AESGCM
 #define WOLFSSL_SHA512
 #define HAVE_ECC
-#define HAVE_CURVE25519
-#define CURVE25519_SMALL
 #define HAVE_ED25519
 
 #define WOLFSSH_IGNORE_FILE_WARN
diff --git a/ide/STM32CUBE/main.c b/ide/STM32CUBE/main.c
index 76155abd0..3827fd826 100644
--- a/ide/STM32CUBE/main.c
+++ b/ide/STM32CUBE/main.c
@@ -1,6 +1,6 @@
 /* main.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/STM32CUBE/myFilesystem.h b/ide/STM32CUBE/myFilesystem.h
index 31eefd454..cfacbc9dc 100644
--- a/ide/STM32CUBE/myFilesystem.h
+++ b/ide/STM32CUBE/myFilesystem.h
@@ -1,6 +1,6 @@
 /* myFilesystem.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/STM32CUBE/userio_template.h b/ide/STM32CUBE/userio_template.h
index 4b8144288..049335e00 100644
--- a/ide/STM32CUBE/userio_template.h
+++ b/ide/STM32CUBE/userio_template.h
@@ -1,6 +1,6 @@
 /* userio_template.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/ide/STM32CUBE/wolfssh_test.c b/ide/STM32CUBE/wolfssh_test.c
index ba9c5a44c..1090dff5e 100644
--- a/ide/STM32CUBE/wolfssh_test.c
+++ b/ide/STM32CUBE/wolfssh_test.c
@@ -1,6 +1,6 @@
 /* wolfssh_test.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -47,8 +47,10 @@ void wolfSSHTest(const void* argument)
 
     printf("Running wolfSSH Tests...\n");
 
-    if (wolfSSH_TestsuiteTest(0, NULL))
-        ret = -1;
+    /* TODO: Uncomment once proper threading abstractions have been implemented
+     * in wolfSSL */
+    /* if (wolfSSH_TestsuiteTest(0, NULL))
+        ret = -1; */
     if (wolfSSH_UnitTest(0, NULL))
         ret = -1;
     if (wolfSSH_ApiTest(0, NULL))
diff --git a/ide/STM32CUBE/wolfssh_test.h b/ide/STM32CUBE/wolfssh_test.h
index 063027b2c..fc88dc6a5 100644
--- a/ide/STM32CUBE/wolfssh_test.h
+++ b/ide/STM32CUBE/wolfssh_test.h
@@ -1,6 +1,6 @@
 /* wolfssh_test.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/keys/id_barney b/keys/id_barney
new file mode 100644
index 000000000..07c504bb3
--- /dev/null
+++ b/keys/id_barney
@@ -0,0 +1,7 @@
+-----BEGIN OPENSSH PRIVATE KEY-----
+b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
+QyNTUxOQAAACBs8gsipHiL/VP0nvJOeDeR0EYF9AXtXnjGlGmqHru5NQAAAJghFgrDIRYK
+wwAAAAtzc2gtZWQyNTUxOQAAACBs8gsipHiL/VP0nvJOeDeR0EYF9AXtXnjGlGmqHru5NQ
+AAAEDuTSTiIfkHZlxI+gjjETACk3F3PPU7jgOHG6NH/THSXWzyCyKkeIv9U/Se8k54N5HQ
+RgX0Be1eeMaUaaoeu7k1AAAAEGJhcm5leUBsb2NhbGhvc3QBAgMEBQ==
+-----END OPENSSH PRIVATE KEY-----
diff --git a/keys/id_barney.pub b/keys/id_barney.pub
new file mode 100644
index 000000000..64a15f34b
--- /dev/null
+++ b/keys/id_barney.pub
@@ -0,0 +1 @@
+ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGzyCyKkeIv9U/Se8k54N5HQRgX0Be1eeMaUaaoeu7k1 barney@localhost
diff --git a/keys/putty_rsa.ppk b/keys/putty_rsa.ppk
new file mode 100644
index 000000000..c12c5fbea
--- /dev/null
+++ b/keys/putty_rsa.ppk
@@ -0,0 +1,26 @@
+PuTTY-User-Key-File-3: ssh-rsa
+Encryption: none
+Comment: rsa-key-20240604
+Public-Lines: 6
+AAAAB3NzaC1yc2EAAAADAQABAAABAQDEbENolVsJ9W/mfKF1G+j/xKiL0g+BhVLH
+JP3fOYpXRur5x5kdselmlnklpnzqxQcp+5uv89XfqhILDMNJRhffIKvOYa2AHdEg
+ML/FjtLwgiruM6sCA+NZ1MbBHRUqzsPdMlEqZp0kMBpldtUgcwNoyT3TD0zxPNk7
+ZaVl5KTZi3c5KBr11SpT5HsxPLRGN0XwjEZpxu6nfPAdg4R1/rW1vJDHJfU/ZvJb
+GcJvDls5OWvFMbaGzhq/JgWmrSRYjYlKpNBnGpvCm61ZbABBoUVyWUGbNgWjeVjw
+apW/Ycw9Mb9+u3jVtFPquU3loMXDyXGslclhwH/k8pJjt+g7jAN1
+Private-Lines: 14
+AAABAGyMC8Bq8VGSkhFEhJFMKDnX+vCx2CHShMlKtwU6LipHJal9VS9k1z/7Hd3h
+oJy431mjEwlsbZ/Zw3jZx73hf2WuD2PQ9OmdEKmCZygM4qNIu+LBKNrHPUeyX1fu
+83ihpPnDSblt1Z9e+edigSsahCLPO1w8019pKf86D+o8LaGOCgWrgAhxzlESQSHj
+d5c7C08qOTjOTfSCrUGX6X8vbuVN62sejd7stw/hznNSfKXxGNS36U4PAFA3ISkD
+TD3ZYKNDHogfxWbnQdQBykw90OQCn/k05U1ibih4dE7o2C+1Nd+gJBfoUFoz0DcT
+LILn9MC7TazgFvfsZ/8eV9hPZm0AAACBAOiNzN6TJvvE2rE0/NFTjXw6Rr0DpCDE
+px3IOHaX6sDR+w8h7/Hu1VdnAhNPZndslzW8B0x+yfIE3jxUds3rl5sF4Q54/POj
+PnPSNrdP6xFFznxen6TyLxg4DNnlirBBQRPFg6dqtv3SKenVyGLWuzOgCV+oajBh
+vnXHJIIMSFRHAAAAgQDYOeymt6Ubi5shUNHpTfvbRMh08Uhlb6R2wkDDBLcDJEHd
+h0+4nlNC3I/5OMyGrtPa0zwdEdUNTOKXT3sHC5g/mCOvh3Nk2pcMBr2kK4nR2jKK
+oDY6czAlHk3Egd1WAz00Vm+DRKlKOzkPbnYk66cbtmIOPfyBoMv3Ce/wtWM0YwAA
+AIEA2hkI2Px9OgtDRjl9Q/ACzTrEytucBtr8sbfDEB9xJo6KfQSvSM+JTs6ZwyDq
+xGYnAgfExL6jAziHuDoPOY2ypk9narnVvbT7YnR/unI7w2hKOA4wzwDg2ttjTd2H
+p/TeCUiHrrVPe6Q9KkfXMFngbYnt11nN5p6JFKOuzMLg224=
+Private-MAC: 8eead3c876b6feb64a80d9d7573ffc1ab89bb272091a38fe85c962d47400c7cb
diff --git a/keys/putty_rsa.pub b/keys/putty_rsa.pub
new file mode 100644
index 000000000..447b7cc45
--- /dev/null
+++ b/keys/putty_rsa.pub
@@ -0,0 +1 @@
+ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDEbENolVsJ9W/mfKF1G+j/xKiL0g+BhVLHJP3fOYpXRur5x5kdselmlnklpnzqxQcp+5uv89XfqhILDMNJRhffIKvOYa2AHdEgML/FjtLwgiruM6sCA+NZ1MbBHRUqzsPdMlEqZp0kMBpldtUgcwNoyT3TD0zxPNk7ZaVl5KTZi3c5KBr11SpT5HsxPLRGN0XwjEZpxu6nfPAdg4R1/rW1vJDHJfU/ZvJbGcJvDls5OWvFMbaGzhq/JgWmrSRYjYlKpNBnGpvCm61ZbABBoUVyWUGbNgWjeVjwapW/Ycw9Mb9+u3jVtFPquU3loMXDyXGslclhwH/k8pJjt+g7jAN1 rsa-key-20240604
diff --git a/scripts/scp.test b/scripts/scp.test
index 093db6845..84e228fab 100755
--- a/scripts/scp.test
+++ b/scripts/scp.test
@@ -133,6 +133,22 @@ else
     exit 1
 fi
 
+echo "Test of sending a file that does not exist"
+touch $PWD/scripts/empty
+./examples/echoserver/echoserver -1 -R $ready_file &
+server_pid=$!
+create_port
+./examples/scpclient/wolfscp -u jill -P upthehill -p $port -L $PWD/does-not-exist:$PWD/empty
+RESULT=$?
+remove_ready_file
+rm -f $PWD/scripts/empty
+
+if test $RESULT -eq 0; then
+    echo -e "\n\nshould fail out sending a file that does not exist"
+    do_cleanup
+    exit 1
+fi
+
 echo -e "\nALL Tests Passed"
 
 exit 0
diff --git a/src/agent.c b/src/agent.c
index 78fc73c06..bfda4930f 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -1,6 +1,6 @@
 /* agent.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/src/certman.c b/src/certman.c
index 584f95525..40b8eba7a 100644
--- a/src/certman.c
+++ b/src/certman.c
@@ -1,6 +1,6 @@
 /* certman.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/src/internal.c b/src/internal.c
index d4db4265c..b2d46ac40 100644
--- a/src/internal.c
+++ b/src/internal.c
@@ -1,6 +1,6 @@
 /* internal.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -33,17 +33,19 @@
 #include <wolfssh/ssh.h>
 #include <wolfssh/internal.h>
 #include <wolfssh/log.h>
+#include <wolfssl/version.h>
 #include <wolfssl/wolfcrypt/asn.h>
 #ifndef WOLFSSH_NO_DH
     #include <wolfssl/wolfcrypt/dh.h>
 #endif
+#include <wolfssl/wolfcrypt/curve25519.h>
+#include <wolfssl/wolfcrypt/ed25519.h>
 #ifdef WOLFSSH_CERTS
     #include <wolfssl/wolfcrypt/error-crypt.h>
 #endif
 #include <wolfssl/wolfcrypt/rsa.h>
 #include <wolfssl/wolfcrypt/ecc.h>
 #include <wolfssl/wolfcrypt/hmac.h>
-#include <wolfssl/wolfcrypt/integer.h>
 #include <wolfssl/wolfcrypt/signature.h>
 
 #ifdef WOLFSSH_HAVE_LIBOQS
@@ -69,6 +71,11 @@
     use of the function if the flag isn't set. If using wolfCrypt v4.5.0 or
     later, and not building with configure, set this flag.
     default: off
+  WOLFSSH_NO_SHA1_SOFT_DISABLE
+    SHA-1 is normally soft-disabled. The default configuration will not
+    advertise the availability of SHA-1 based algorithms during KEX. SHA-1
+    algorithms still work. Setting this flag will advertise SHA-1 based
+    algorithms during KEX by default.
   WOLFSSH_NO_SHA1
     Set when SHA1 is disabled. Set to disable use of SHA1 in HMAC and digital
     signature support.
@@ -97,9 +104,6 @@
   WOLFSSH_NO_ECDH_SHA2_NISTP521
     Set when ECC or SHA2-512 are disabled. Set to disable use of ECDHE key
     exchange with prime NISTP521.
-  WOLFSSH_NO_ECDH_SHA2_ED25519
-    Set when ED25519 or SHA2-256 are disabled. Set to disable use of ECDHE key
-    exchange with prime ED25519. (It just decodes the ID for output.)
   WOLFSSH_NO_RSA
     Set when RSA is disabled. Set to disable use of RSA server and user
     authentication.
@@ -143,6 +147,9 @@
     algorithms off.
   WOLFSSH_KEY_QUANTITY_REQ
     Number of keys required to be in an OpenSSH-style key wrapper.
+  WOLFSSH_NO_CURVE25519_SHA256
+    Set when Curve25519 or SHA2-256 are disabled in wolfSSL. Set to disable use
+    of Curve25519 key exchange.
 */
 
 static const char sshProtoIdStr[] = "SSH-2.0-wolfSSHv"
@@ -436,6 +443,15 @@ const char* GetErrorString(int err)
         case WS_SFTP_NOT_FILE_E:
             return "not a regular file";
 
+        case WS_MSGID_NOT_ALLOWED_E:
+            return "message not allowed before user authentication";
+
+        case WS_ED25519_E:
+            return "Ed25519 buffer error";
+
+        case WS_AUTH_PENDING:
+            return "userauth is still pending (callback would block)";
+
         default:
             return "Unknown error code";
     }
@@ -552,17 +568,224 @@ static void HandshakeInfoFree(HandshakeInfo* hs, void* heap)
 }
 
 
-#ifdef DEBUG_WOLFSSH
+#ifndef NO_WOLFSSH_SERVER
+INLINE static int IsMessageAllowedServer(WOLFSSH *ssh, byte msg)
+{
+    /* Has client userauth started? */
+    if (ssh->acceptState < ACCEPT_KEYED) {
+        if (msg > MSGID_KEXDH_LIMIT) {
+            return 0;
+        }
+    }
+    /* Is server userauth complete? */
+    if (ssh->acceptState < ACCEPT_SERVER_USERAUTH_SENT) {
+        /* Explicitly check for messages not allowed before user
+         * authentication has comleted. */
+        if (msg >= MSGID_USERAUTH_LIMIT) {
+            WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by server "
+                    "before user authentication is complete", msg);
+            return 0;
+        }
+        /* Explicitly check for the user authentication messages that
+         * only the server sends, it shouldn't receive them. */
+        if (msg > MSGID_USERAUTH_RESTRICT) {
+            WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by server "
+                    "during user authentication", msg);
+            return 0;
+        }
+    }
+    else {
+        if (msg >= MSGID_USERAUTH_RESTRICT && msg < MSGID_USERAUTH_LIMIT) {
+            WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by server "
+                    "after user authentication", msg);
+            return 0;
+        }
+    }
+
+    return 1;
+}
+#endif /* NO_WOLFSSH_SERVER */
+
+
+#ifndef NO_WOLFSSH_CLIENT
+INLINE static int IsMessageAllowedClient(WOLFSSH *ssh, byte msg)
+{
+    /* Has client userauth started? */
+    if (ssh->connectState < CONNECT_CLIENT_KEXDH_INIT_SENT) {
+        if (msg >= MSGID_KEXDH_LIMIT) {
+            return 0;
+        }
+    }
+    /* Is client userauth complete? */
+    if (ssh->connectState < CONNECT_SERVER_USERAUTH_ACCEPT_DONE) {
+        /* Explicitly check for messages not allowed before user
+         * authentication has comleted. */
+        if (msg >= MSGID_USERAUTH_LIMIT) {
+            WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by client "
+                    "before user authentication is complete", msg);
+            return 0;
+        }
+        /* Explicitly check for the user authentication message that
+         * only the client sends, it shouldn't receive it. */
+        if (msg == MSGID_USERAUTH_RESTRICT) {
+            WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by client "
+                    "during user authentication", msg);
+            return 0;
+        }
+    }
+    else {
+        if (msg >= MSGID_USERAUTH_RESTRICT && msg < MSGID_USERAUTH_LIMIT) {
+            WLOG(WS_LOG_DEBUG, "Message ID %u not allowed by client "
+                    "after user authentication", msg);
+            return 0;
+        }
+    }
+    return 1;
+}
+#endif /* NO_WOLFSSH_CLIENT */
+
+
+INLINE static int IsMessageAllowed(WOLFSSH *ssh, byte msg)
+{
+#ifndef NO_WOLFSSH_SERVER
+    if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
+        return IsMessageAllowedServer(ssh, msg);
+    }
+#endif /* NO_WOLFSSH_SERVER */
+#ifndef NO_WOLFSSH_CLIENT
+    if (ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) {
+        return IsMessageAllowedClient(ssh, msg);
+    }
+#endif /* NO_WOLFSSH_CLIENT */
+    return 0;
+}
+
+
+static const char cannedKexAlgoNames[] =
+#if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
+    "ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org,"
+#endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+    "curve25519-sha256,"
+#endif
+#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP521)
+    "ecdh-sha2-nistp521,"
+#endif
+#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP384)
+    "ecdh-sha2-nistp384,"
+#endif
+#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256)
+    "ecdh-sha2-nistp256,"
+#endif
+#if !defined(WOLFSSH_NO_DH_GEX_SHA256)
+    "diffie-hellman-group-exchange-sha256,"
+#endif
+#ifdef WOLFSSH_NO_SHA1_SOFT_DISABLE
+    #if !defined(WOLFSSH_NO_DH_GROUP14_SHA1)
+        "diffie-hellman-group14-sha1,"
+    #endif
+    #if !defined(WOLFSSH_NO_DH_GROUP1_SHA1)
+        "diffie-hellman-group1-sha1,"
+    #endif
+#endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */
+    "";
+
+#ifndef WOLFSSH_NO_SSH_RSA_SHA1
+    #ifdef WOLFSSH_CERTS
+        static const char cannedKeyAlgoX509RsaNames[] = "x509v3-ssh-rsa";
+    #endif
+#endif
+#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
+    static const char cannedKeyAlgoEcc256Names[] = "ecdsa-sha2-nistp256";
+    #ifdef WOLFSSH_CERTS
+        static const char cannedKeyAlgoX509Ecc256Names[] =
+                "x509v3-ecdsa-sha2-nistp256";
+    #endif
+#endif
+#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
+    static const char cannedKeyAlgoEcc384Names[] = "ecdsa-sha2-nistp384";
+    #ifdef WOLFSSH_CERTS
+        static const char cannedKeyAlgoX509Ecc384Names[] =
+                "x509v3-ecdsa-sha2-nistp384";
+    #endif
+#endif
+#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
+    static const char cannedKeyAlgoEcc521Names[] = "ecdsa-sha2-nistp521";
+    #ifdef WOLFSSH_CERTS
+        static const char cannedKeyAlgoX509Ecc521Names[] =
+                "x509v3-ecdsa-sha2-nistp521";
+    #endif
+#endif
+#ifndef WOLFSSH_NO_SSH_RSA_SHA1
+    /* Used for both the signature algorithm and the RSA key format. */
+    static const char cannedKeyAlgoSshRsaNames[] = "ssh-rsa";
+#endif
+#ifndef WOLFSSH_NO_RSA_SHA2_256
+    static const char cannedKeyAlgoRsaSha2_256Names[] = "rsa-sha2-256";
+#endif
+#ifndef WOLFSSH_NO_RSA_SHA2_512
+    static const char cannedKeyAlgoRsaSha2_512Names[] = "rsa-sha2-512";
+#endif
+#ifndef WOLFSSH_NO_ED25519
+    static const char cannedKeyAlgoEd25519Name[] = "ssh-ed25519";
+#endif
+
+static const char cannedKeyAlgoNames[] =
+#ifndef WOLFSSH_NO_ED25519
+    "ssh-ed25519,"
+#endif /* WOLFSSH_NO_ED25519 */
+#ifndef WOLFSSH_NO_RSA_SHA2_256
+    "rsa-sha2-256,"
+#endif/* WOLFSSH_NO_RSA_SHA2_256 */
+#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
+    "ecdsa-sha2-nistp256,"
+#endif /* WOLFSSH_NO_ECDSA_SHA2_NISTP256 */
+#ifdef WOLFSSH_CERTS
+    #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
+        "x509v3-ecdsa-sha2-nistp256,"
+    #endif /* WOLFSSH_NO_ECDSA_SHA2_NISTP256 */
+    #ifdef WOLFSSH_NO_SHA1_SOFT_DISABLE
+        "x509v3-ssh-rsa,"
+    #endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */
+#endif /* WOLFSSH_CERTS */
+#ifdef WOLFSSH_NO_SHA1_SOFT_DISABLE
+    "ssh-rsa,"
+#endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */
+    "";
+
+static const char cannedEncAlgoNames[] =
+#if !defined(WOLFSSH_NO_AES_GCM)
+    "aes256-gcm@openssh.com,"
+    "aes192-gcm@openssh.com,"
+    "aes128-gcm@openssh.com,"
+#endif
+#if !defined(WOLFSSH_NO_AES_CTR)
+    "aes256-ctr,"
+    "aes192-ctr,"
+    "aes128-ctr,"
+#endif
+#if !defined(WOLFSSH_NO_AES_CBC)
+    "aes256-cbc,"
+    "aes192-cbc,"
+    "aes128-cbc,"
+#endif
+    "";
 
-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 = (word32)sizeof(cannedBanner) - 1;
+static const char cannedMacAlgoNames[] =
+#if !defined(WOLFSSH_NO_HMAC_SHA2_256)
+    "hmac-sha2-256,"
+#endif
+#if defined(WOLFSSH_NO_SHA1_SOFT_DISABLE)
+    #if !defined(WOLFSSH_NO_HMAC_SHA1_96)
+        "hmac-sha1-96,"
+    #endif
+    #if !defined(WOLFSSH_NO_HMAC_SHA1)
+        "hmac-sha1,"
+    #endif
+#endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */
+    "";
 
-#endif /* DEBUG_WOLFSSH */
+static const char cannedNoneNames[] = "none";
 
 
 WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap)
@@ -590,10 +813,8 @@ WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap)
     ctx->scpRecvCb = wsScpRecvCallback;
     ctx->scpSendCb = wsScpSendCallback;
 #endif /* WOLFSSH_SCP */
-#ifdef DEBUG_WOLFSSH
-    ctx->banner = cannedBanner;
-    ctx->bannerSz = cannedBannerSz;
-#endif /* DEBUG_WOLFSSH */
+    ctx->banner = NULL;
+    ctx->bannerSz = 0;
 #ifdef WOLFSSH_CERTS
     ctx->certMan = wolfSSH_CERTMAN_new(ctx->heap);
     if (ctx->certMan == NULL)
@@ -602,6 +823,13 @@ WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap)
     ctx->windowSz = DEFAULT_WINDOW_SZ;
     ctx->maxPacketSz = DEFAULT_MAX_PACKET_SZ;
     ctx->sshProtoIdStr = sshProtoIdStr;
+    ctx->algoListKex = cannedKexAlgoNames;
+    if (side == WOLFSSH_ENDPOINT_CLIENT) {
+        ctx->algoListKey = cannedKeyAlgoNames;
+    }
+    ctx->algoListCipher = cannedEncAlgoNames;
+    ctx->algoListMac = cannedMacAlgoNames;
+    ctx->algoListKeyAccepted = cannedKeyAlgoNames;
 
     count = (word32)(sizeof(ctx->privateKey)
             / sizeof(ctx->privateKey[0]));
@@ -660,17 +888,17 @@ void CtxResourceFree(WOLFSSH_CTX* ctx)
 static int WS_TermResize(WOLFSSH* ssh, word32 col, word32 row, word32 colP,
     word32 rowP, void* usrCtx)
 {
-    HPCON* term = (HPCON*)usrCtx;
+    HANDLE* term = (HANDLE*)usrCtx;
     int ret = WS_SUCCESS;
 
     if (term != NULL) {
-        HRESULT ret;
-        COORD sz;
+        char cmd[20];
+        int cmdSz = 20;
+        DWORD wrtn = 0;
 
-        sz.X = col;
-        sz.Y = row;
-        ret = ResizePseudoConsole(*term, sz);
-        if (ret != S_OK) {
+        /* VT control sequence for resizing window */
+        cmdSz = snprintf(cmd, cmdSz, "\x1b[8;%d;%dt", row, col);
+        if (WriteFile(*term, cmd, cmdSz, &wrtn, 0) != TRUE) {
             WLOG(WS_LOG_ERROR, "Issue with pseudo console resize");
             ret = WS_FATAL_ERROR;
         }
@@ -726,6 +954,30 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx)
         return ssh;
     heap = ctx->heap;
 
+#ifdef WOLFSSH_STATIC_MEMORY
+    if (heap != NULL) {
+        WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap;
+
+        if (hint->memory->flag & WOLFMEM_TRACK_STATS) {
+            WOLFSSL_MEM_CONN_STATS* stats = NULL;
+
+            stats = (WOLFSSL_MEM_CONN_STATS*)WMALLOC(
+                    sizeof(WOLFSSL_MEM_CONN_STATS),
+                    heap, DYNTYPE_SSH);
+            if (stats == NULL) {
+                WLOG(WS_LOG_DEBUG, "SshInit: Cannot track memory stats.\n");
+                return NULL;
+            }
+
+            XMEMSET(stats, 0, sizeof(WOLFSSL_MEM_CONN_STATS));
+            if (hint->stats != NULL) {
+                WFREE(hint->stats, heap, DYNTYPE_SSH);
+            }
+            hint->stats = stats;
+        }
+    }
+#endif /* WOLFSSH_STATIC_MEMORY */
+
     handshake = HandshakeInfoNew(heap);
     rng = (WC_RNG*)WMALLOC(sizeof(WC_RNG), heap, DYNTYPE_RNG);
     if (handshake == NULL || rng == NULL) {
@@ -777,10 +1029,17 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx)
     ssh->kSz         = (word32)sizeof(ssh->k);
     ssh->handshake   = handshake;
     ssh->connectChannelId = WOLFSSH_SESSION_SHELL;
+    ssh->algoListKex = ctx->algoListKex;
+    ssh->algoListKey = ctx->algoListKey;
+    ssh->algoListCipher = ctx->algoListCipher;
+    ssh->algoListMac = ctx->algoListMac;
+    ssh->algoListKeyAccepted = ctx->algoListKeyAccepted;
 #ifdef WOLFSSH_SCP
     ssh->scpRequestState = SCP_PARSE_COMMAND;
     ssh->scpConfirmMsg   = NULL;
     ssh->scpConfirmMsgSz = 0;
+    ssh->scpRecvMsg      = NULL;
+    ssh->scpRecvMsgSz    = 0;
     ssh->scpRecvCtx      = NULL;
     #if !defined(WOLFSSH_SCP_USER_CALLBACKS) && !defined(NO_FILESYSTEM)
     ssh->scpSendCtx      = &(ssh->scpSendCbCtx);
@@ -813,6 +1072,8 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx)
 #endif
 #endif
 
+    ssh->keyingCompletionCtx = (void*)ssh;
+
     if (BufferInit(&ssh->inputBuffer, 0, ctx->heap) != WS_SUCCESS  ||
         BufferInit(&ssh->outputBuffer, 0, ctx->heap) != WS_SUCCESS ||
         BufferInit(&ssh->extDataBuffer, 0, ctx->heap) != WS_SUCCESS) {
@@ -898,6 +1159,22 @@ void SshResourceFree(WOLFSSH* ssh, void* heap)
         ssh->sftpDefaultPath = NULL;
     }
 #endif
+#ifdef WOLFSSH_TERM
+    if (ssh->modes) {
+        WFREE(ssh->modes, ssh->ctx->heap, DYNTYPE_STRING);
+        ssh->modesSz = 0;
+    }
+#endif
+#ifdef WOLFSSH_STATIC_MEMORY
+    if (heap) {
+        WOLFSSL_HEAP_HINT* hint = (WOLFSSL_HEAP_HINT*)heap;
+        if (hint->memory->flag & WOLFMEM_TRACK_STATS
+                && hint->stats != NULL) {
+            WFREE(hint->stats, heap, DYNTYPE_SSH);
+            hint->stats = NULL;
+        }
+    }
+#endif
 }
 
 
@@ -905,6 +1182,7 @@ typedef struct WS_KeySignature {
     byte keySigId;
     word32 sigSz;
     const char *name;
+    void *heap;
     word32 nameSz;
     union {
 #ifndef WOLFSSH_NO_RSA
@@ -916,6 +1194,11 @@ typedef struct WS_KeySignature {
         struct {
             ecc_key key;
         } ecc;
+#endif
+#ifndef WOLFSSH_NO_ED25519
+        struct {
+            ed25519_key key;
+        } ed25519;
 #endif
     } ks;
 } WS_KeySignature;
@@ -1029,6 +1312,31 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap)
             wc_ecc_free(&key->ks.ecc.key);
         }
 #endif /* WOLFSSH_NO_ECDSA */
+#if !defined(WOLFSSH_NO_ED25519)
+    if (key != NULL) {
+        if (key->keySigId == ID_UNKNOWN) {
+            idx = 0;
+            ret = wc_ed25519_init_ex(&key->ks.ed25519.key, heap, INVALID_DEVID);
+
+            if(ret == 0) {
+                if (isPrivate) {
+                    ret = wc_Ed25519PrivateKeyDecode(in, &idx,
+                            &key->ks.ed25519.key, inSz);
+                }
+                else {
+                    ret = wc_Ed25519PublicKeyDecode(in, &idx,
+                            &key->ks.ed25519.key, inSz);
+                }
+            }
+
+            /* If decode was successful, this is a Ed25519 key. */
+            if(ret == 0)
+                key->keySigId = ID_ED25519;
+
+            wc_ed25519_free(&key->ks.ed25519.key);
+        }
+    }
+#endif /* WOLFSSH_NO_ED25519 */
 
         if (key->keySigId == ID_UNKNOWN) {
             ret = WS_UNIMPLEMENTED_E;
@@ -1045,10 +1353,57 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap)
 
 
 #ifndef WOLFSSH_NO_RSA
+
+#if (LIBWOLFSSL_VERSION_HEX > WOLFSSL_V5_7_0) && !defined(HAVE_FIPS)
+/*
+ * The function wc_RsaPrivateKeyDecodeRaw() is available
+ * from wolfSSL after v5.7.0.
+ */
+
+/*
+ * Utility for GetOpenSshKey() to read in RSA keys.
+ */
+static int GetOpenSshKeyRsa(RsaKey* key,
+        const byte* buf, word32 len, word32* idx)
+{
+    const byte *n, *e, *d, *u, *p, *q;
+    word32 nSz, eSz, dSz, uSz, pSz, qSz;
+    int ret;
+
+    ret = wc_InitRsaKey(key, NULL);
+    if (ret == WS_SUCCESS)
+        ret = GetMpint(&nSz, &n, buf, len, idx);
+    if (ret == WS_SUCCESS)
+        ret = GetMpint(&eSz, &e, buf, len, idx);
+    if (ret == WS_SUCCESS)
+        ret = GetMpint(&dSz, &d, buf, len, idx);
+    if (ret == WS_SUCCESS)
+        ret = GetMpint(&uSz, &u, buf, len, idx);
+    if (ret == WS_SUCCESS)
+        ret = GetMpint(&pSz, &p, buf, len, idx);
+    if (ret == WS_SUCCESS)
+        ret = GetMpint(&qSz, &q, buf, len, idx);
+    if (ret == WS_SUCCESS)
+        ret = wc_RsaPrivateKeyDecodeRaw(n, nSz, e, eSz, d, dSz,
+                u, uSz, p, pSz, q, qSz, NULL, 0, NULL, 0, key);
+
+    if (ret != WS_SUCCESS)
+        ret = WS_RSA_E;
+
+    return ret;
+}
+
+#else /* LIBWOLFSSL_VERSION_HEX > WOLFSSL_V5_7_0 */
+
+#include <wolfssl/wolfcrypt/wolfmath.h>
+
 /*
  * Utility function to read an Mpint from the stream directly into a mp_int.
+ * The RsaKey members u, dP, and dQ do not exist when wolfCrypt is built
+ * with RSA_LOW_MEM. (That mode of wolfCrypt isn't using the extra values
+ * for the Chinese Remainder Theorem.)
  */
-static INLINE int GetMpintToMp(mp_int* mp,
+static int GetMpintToMp(mp_int* mp,
         const byte* buf, word32 len, word32* idx)
 {
     const byte* val = NULL;
@@ -1063,12 +1418,13 @@ static INLINE int GetMpintToMp(mp_int* mp,
 }
 
 
+#ifndef RSA_LOW_MEM
 /*
- * For the given RSA key, calculate p^-1 and q^-1. wolfCrypt's RSA
- * code expects them, but the OpenSSH format key doesn't store them.
- * TODO: Add a RSA read function to wolfCrypt to handle this condition.
+ * For the given RSA key, calculate d mod(p-1) and d mod(q-1).
+ * wolfCrypt's RSA code expects them, but the OpenSSH format key
+ * doesn't store them.
  */
-static INLINE int CalcRsaInverses(RsaKey* key)
+static int CalcRsaDX(RsaKey* key)
 {
     mp_int m;
     int ret;
@@ -1087,6 +1443,7 @@ static INLINE int CalcRsaInverses(RsaKey* key)
 
     return ret;
 }
+#endif
 
 /*
  * Utility for GetOpenSshKey() to read in RSA keys.
@@ -1103,26 +1460,37 @@ static int GetOpenSshKeyRsa(RsaKey* key,
         ret = GetMpintToMp(&key->e, buf, len, idx);
     if (ret == WS_SUCCESS)
         ret = GetMpintToMp(&key->d, buf, len, idx);
+#ifndef RSA_LOW_MEM
     if (ret == WS_SUCCESS)
         ret = GetMpintToMp(&key->u, buf, len, idx);
+#else
+    /* Skipping the u value in the key. */
+    if (ret == WS_SUCCESS)
+        ret = GetSkip(buf, len, idx);
+#endif
     if (ret == WS_SUCCESS)
         ret = GetMpintToMp(&key->p, buf, len, idx);
     if (ret == WS_SUCCESS)
         ret = GetMpintToMp(&key->q, buf, len, idx);
 
+#ifndef RSA_LOW_MEM
     /* Calculate dP and dQ for wolfCrypt. */
     if (ret == WS_SUCCESS)
-        ret = CalcRsaInverses(key);
+        ret = CalcRsaDX(key);
+#endif
 
     if (ret != WS_SUCCESS)
         ret = WS_RSA_E;
 
     return ret;
 }
-#endif
+
+#endif /* LIBWOLFSSL_VERSION_HEX > WOLFSSL_V5_7_0 */
+
+#endif /* WOLFSSH_NO_RSA */
 
 
-#if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECC)
+#ifndef WOLFSSH_NO_ECDSA
 /*
  * Utility for GetOpenSshKey() to read in ECDSA keys.
  */
@@ -1152,6 +1520,36 @@ static int GetOpenSshKeyEcc(ecc_key* key,
 }
 #endif
 
+#ifndef WOLFSSH_NO_ED25519
+/*
+ * Utility for GetOpenSshKey() to read in Ed25519 keys.
+ */
+static int GetOpenSshKeyEd25519(ed25519_key* key,
+        const byte* buf, word32 len, word32* idx)
+{
+    const byte *priv = NULL, *pub = NULL;
+    word32 privSz = 0, pubSz = 0;
+    int ret;
+
+    ret = wc_ed25519_init_ex(key, key->heap, INVALID_DEVID);
+
+    /* OpenSSH key formatting stores the public key, ENC(A), and the
+     * private key (k) concatenated with the public key, k || ENC(A). */
+    if (ret == WS_SUCCESS)
+        ret = GetStringRef(&pubSz, &pub, buf, len, idx); /* ENC(A) */
+    if (ret == WS_SUCCESS)
+        ret = GetStringRef(&privSz, &priv, buf, len, idx); /* k || ENC(A) */
+
+    if (ret == WS_SUCCESS)
+        ret = wc_ed25519_import_private_key(priv, privSz - pubSz,
+                pub, pubSz, key);
+
+    if (ret != WS_SUCCESS)
+        ret = WS_ECC_E;
+
+    return ret;
+}
+#endif
 /*
  * Decodes an OpenSSH format key.
  */
@@ -1234,11 +1632,19 @@ static int GetOpenSshKey(WS_KeySignature *key,
                                         str, strSz, &subIdx);
                                 break;
                         #endif
-                        #if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECC)
+                        #ifndef WOLFSSH_NO_ECDSA
                             case ID_ECDSA_SHA2_NISTP256:
+                            case ID_ECDSA_SHA2_NISTP384:
+                            case ID_ECDSA_SHA2_NISTP521:
                                 ret = GetOpenSshKeyEcc(&key->ks.ecc.key,
                                         str, strSz, &subIdx);
                                 break;
+                        #endif
+                        #ifndef WOLFSSH_NO_ED25519
+                            case ID_ED25519:
+                                ret = GetOpenSshKeyEd25519(&key->ks.ed25519.key,
+                                        str, strSz, &subIdx);
+                                break;
                         #endif
                             default:
                                 ret = WS_UNIMPLEMENTED_E;
@@ -1303,6 +1709,7 @@ int IdentifyOpenSshKey(const byte* in, word32 inSz, void* heap)
     }
     else {
         WMEMSET(key, 0, sizeof(*key));
+        key->heap = heap;
         key->keySigId = ID_NONE;
 
         ret = GetOpenSshKey(key, in, inSz, &idx);
@@ -1411,13 +1818,15 @@ static void RefreshPublicKeyAlgo(WOLFSSH_CTX* ctx)
                 publicKeyAlgoCount++;
             }
         #endif
-        #ifndef WOLFSSH_NO_SSH_RSA_SHA1
-            if (publicKeyAlgoCount < WOLFSSH_MAX_PUB_KEY_ALGO) {
-                *publicKeyAlgo = ID_SSH_RSA;
-                publicKeyAlgo++;
-                publicKeyAlgoCount++;
-            }
-        #endif
+        #ifdef WOLFSSH_NO_SHA1_SOFT_DISABLE
+            #ifndef WOLFSSH_NO_SSH_RSA_SHA1
+                if (publicKeyAlgoCount < WOLFSSH_MAX_PUB_KEY_ALGO) {
+                    *publicKeyAlgo = ID_SSH_RSA;
+                    publicKeyAlgo++;
+                    publicKeyAlgoCount++;
+                }
+            #endif /* WOLFSSH_NO_SSH_RSA_SHA1 */
+        #endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */
         }
         else {
             if (publicKeyAlgoCount < WOLFSSH_MAX_PUB_KEY_ALGO) {
@@ -1955,140 +2364,144 @@ static int GenerateKeys(WOLFSSH* ssh, byte hashId, byte doKeyPad)
 
 typedef struct {
     byte id;
+    byte type;
     const char* name;
 } NameIdPair;
 
 
 static const NameIdPair NameIdMap[] = {
-    { ID_NONE, "none" },
+    { ID_NONE, TYPE_OTHER, "none" },
 
     /* Encryption IDs */
 #ifndef WOLFSSH_NO_AES_CBC
-    { ID_AES128_CBC, "aes128-cbc" },
-    { ID_AES192_CBC, "aes192-cbc" },
-    { ID_AES256_CBC, "aes256-cbc" },
+    { ID_AES128_CBC, TYPE_CIPHER, "aes128-cbc" },
+    { ID_AES192_CBC, TYPE_CIPHER, "aes192-cbc" },
+    { ID_AES256_CBC, TYPE_CIPHER, "aes256-cbc" },
 #endif
 #ifndef WOLFSSH_NO_AES_CTR
-    { ID_AES128_CTR, "aes128-ctr" },
-    { ID_AES192_CTR, "aes192-ctr" },
-    { ID_AES256_CTR, "aes256-ctr" },
+    { ID_AES128_CTR, TYPE_CIPHER, "aes128-ctr" },
+    { ID_AES192_CTR, TYPE_CIPHER, "aes192-ctr" },
+    { ID_AES256_CTR, TYPE_CIPHER, "aes256-ctr" },
 #endif
 #ifndef WOLFSSH_NO_AES_GCM
-    { ID_AES128_GCM, "aes128-gcm@openssh.com" },
-    { ID_AES192_GCM, "aes192-gcm@openssh.com" },
-    { ID_AES256_GCM, "aes256-gcm@openssh.com" },
+    { ID_AES128_GCM, TYPE_CIPHER, "aes128-gcm@openssh.com" },
+    { ID_AES192_GCM, TYPE_CIPHER, "aes192-gcm@openssh.com" },
+    { ID_AES256_GCM, TYPE_CIPHER, "aes256-gcm@openssh.com" },
 #endif
 
     /* Integrity IDs */
 #ifndef WOLFSSH_NO_HMAC_SHA1
-    { ID_HMAC_SHA1, "hmac-sha1" },
+    { ID_HMAC_SHA1, TYPE_MAC, "hmac-sha1" },
 #endif
 #ifndef WOLFSSH_NO_HMAC_SHA1_96
-    { ID_HMAC_SHA1_96, "hmac-sha1-96" },
+    { ID_HMAC_SHA1_96, TYPE_MAC, "hmac-sha1-96" },
 #endif
 #ifndef WOLFSSH_NO_HMAC_SHA2_256
-    { ID_HMAC_SHA2_256, "hmac-sha2-256" },
+    { ID_HMAC_SHA2_256, TYPE_MAC, "hmac-sha2-256" },
 #endif
 
     /* Key Exchange IDs */
 #ifndef WOLFSSH_NO_DH_GROUP1_SHA1
-    { ID_DH_GROUP1_SHA1, "diffie-hellman-group1-sha1" },
+    { ID_DH_GROUP1_SHA1, TYPE_KEX, "diffie-hellman-group1-sha1" },
 #endif
 #ifndef WOLFSSH_NO_DH_GROUP14_SHA1
-    { ID_DH_GROUP14_SHA1, "diffie-hellman-group14-sha1" },
+    { ID_DH_GROUP14_SHA1, TYPE_KEX, "diffie-hellman-group14-sha1" },
 #endif
 #ifndef WOLFSSH_NO_DH_GEX_SHA256
-    { ID_DH_GEX_SHA256, "diffie-hellman-group-exchange-sha256" },
+    { ID_DH_GEX_SHA256, TYPE_KEX, "diffie-hellman-group-exchange-sha256" },
 #endif
 #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
-    { ID_ECDH_SHA2_NISTP256, "ecdh-sha2-nistp256" },
+    { ID_ECDH_SHA2_NISTP256, TYPE_KEX, "ecdh-sha2-nistp256" },
 #endif
 #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
-    { ID_ECDH_SHA2_NISTP384, "ecdh-sha2-nistp384" },
+    { ID_ECDH_SHA2_NISTP384, TYPE_KEX, "ecdh-sha2-nistp384" },
 #endif
 #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
-    { ID_ECDH_SHA2_NISTP521, "ecdh-sha2-nistp521" },
-#endif
-#ifndef WOLFSSH_NO_ECDH_SHA2_ED25519
-    { ID_ECDH_SHA2_ED25519, "curve25519-sha256" },
-    { ID_ECDH_SHA2_ED25519_LIBSSH, "curve25519-sha256@libssh.org" },
+    { ID_ECDH_SHA2_NISTP521, TYPE_KEX, "ecdh-sha2-nistp521" },
 #endif
 #ifndef WOLFSSH_NO_DH_GEX_SHA256
-    { ID_DH_GROUP14_SHA256, "diffie-hellman-group14-sha256" },
+    { ID_DH_GROUP14_SHA256, TYPE_KEX, "diffie-hellman-group14-sha256" },
 #endif
 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
     /* We use kyber-512 here to achieve interop with OQS's fork. */
-    { ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256,
+    { ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256, TYPE_KEX,
         "ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org" },
 #endif
-    { ID_EXTINFO_S, "ext-info-s" },
-    { ID_EXTINFO_C, "ext-info-c" },
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+    /* See RFC 8731 */
+    { ID_CURVE25519_SHA256, TYPE_KEX, "curve25519-sha256" },
+#endif
+    { ID_EXTINFO_S, TYPE_OTHER, "ext-info-s" },
+    { ID_EXTINFO_C, TYPE_OTHER, "ext-info-c" },
 
     /* Public Key IDs */
 #ifndef WOLFSSH_NO_RSA
-    { ID_SSH_RSA, "ssh-rsa" },
+    { ID_SSH_RSA, TYPE_KEY, "ssh-rsa" },
 #ifndef WOLFSSH_NO_RSA_SHA2_256
-    { ID_RSA_SHA2_256, "rsa-sha2-256" },
+    { ID_RSA_SHA2_256, TYPE_KEY, "rsa-sha2-256" },
 #endif
 #ifndef WOLFSSH_NO_RSA_SHA2_512
-    { ID_RSA_SHA2_512, "rsa-sha2-512" },
+    { ID_RSA_SHA2_512, TYPE_KEY, "rsa-sha2-512" },
 #endif
 #endif /* WOLFSSH_NO_RSA */
 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
-    { ID_ECDSA_SHA2_NISTP256, "ecdsa-sha2-nistp256" },
+    { ID_ECDSA_SHA2_NISTP256, TYPE_KEY, "ecdsa-sha2-nistp256" },
 #endif
 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
-    { ID_ECDSA_SHA2_NISTP384, "ecdsa-sha2-nistp384" },
+    { ID_ECDSA_SHA2_NISTP384, TYPE_KEY, "ecdsa-sha2-nistp384" },
 #endif
 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
-    { ID_ECDSA_SHA2_NISTP521, "ecdsa-sha2-nistp521" },
+    { ID_ECDSA_SHA2_NISTP521, TYPE_KEY, "ecdsa-sha2-nistp521" },
+#endif
+#ifndef WOLFSSH_NO_ED25519
+    { ID_ED25519, TYPE_KEY, "ssh-ed25519" },
 #endif
 #ifdef WOLFSSH_CERTS
 #ifndef WOLFSSH_NO_SSH_RSA_SHA1
-    { ID_X509V3_SSH_RSA, "x509v3-ssh-rsa" },
+    { ID_X509V3_SSH_RSA, TYPE_KEY, "x509v3-ssh-rsa" },
 #endif
 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
-    { ID_X509V3_ECDSA_SHA2_NISTP256, "x509v3-ecdsa-sha2-nistp256" },
+    { ID_X509V3_ECDSA_SHA2_NISTP256, TYPE_KEY, "x509v3-ecdsa-sha2-nistp256" },
 #endif
 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
-    { ID_X509V3_ECDSA_SHA2_NISTP384, "x509v3-ecdsa-sha2-nistp384" },
+    { ID_X509V3_ECDSA_SHA2_NISTP384, TYPE_KEY, "x509v3-ecdsa-sha2-nistp384" },
 #endif
 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
-    { ID_X509V3_ECDSA_SHA2_NISTP521, "x509v3-ecdsa-sha2-nistp521" },
+    { ID_X509V3_ECDSA_SHA2_NISTP521, TYPE_KEY, "x509v3-ecdsa-sha2-nistp521" },
 #endif
 #endif /* WOLFSSH_CERTS */
 
     /* Service IDs */
-    { ID_SERVICE_USERAUTH, "ssh-userauth" },
-    { ID_SERVICE_CONNECTION, "ssh-connection" },
+    { ID_SERVICE_USERAUTH, TYPE_OTHER, "ssh-userauth" },
+    { ID_SERVICE_CONNECTION, TYPE_OTHER, "ssh-connection" },
 
     /* UserAuth IDs */
-    { ID_USERAUTH_PASSWORD, "password" },
-    { ID_USERAUTH_PUBLICKEY, "publickey" },
+    { ID_USERAUTH_PASSWORD, TYPE_OTHER, "password" },
+    { ID_USERAUTH_PUBLICKEY, TYPE_OTHER, "publickey" },
 
     /* Channel Type IDs */
-    { ID_CHANTYPE_SESSION, "session" },
+    { ID_CHANTYPE_SESSION, TYPE_OTHER, "session" },
 #ifdef WOLFSSH_FWD
-    { ID_CHANTYPE_TCPIP_FORWARD, "forwarded-tcpip" },
-    { ID_CHANTYPE_TCPIP_DIRECT, "direct-tcpip" },
+    { ID_CHANTYPE_TCPIP_FORWARD, TYPE_OTHER, "forwarded-tcpip" },
+    { ID_CHANTYPE_TCPIP_DIRECT, TYPE_OTHER, "direct-tcpip" },
 #endif /* WOLFSSH_FWD */
 #ifdef WOLFSSH_AGENT
-    { ID_CHANTYPE_AUTH_AGENT, "auth-agent@openssh.com" },
+    { ID_CHANTYPE_AUTH_AGENT, TYPE_OTHER, "auth-agent@openssh.com" },
 #endif /* WOLFSSH_AGENT */
 
     /* Global Request IDs */
 #ifdef WOLFSSH_FWD
-    { ID_GLOBREQ_TCPIP_FWD, "tcpip-forward" },
-    { ID_GLOBREQ_TCPIP_FWD_CANCEL, "cancel-tcpip-forward" },
+    { ID_GLOBREQ_TCPIP_FWD, TYPE_OTHER, "tcpip-forward" },
+    { ID_GLOBREQ_TCPIP_FWD_CANCEL, TYPE_OTHER, "cancel-tcpip-forward" },
 #endif /* WOLFSSH_FWD */
 
     /* Ext Info IDs */
-    { ID_EXTINFO_SERVER_SIG_ALGS, "server-sig-algs" },
+    { ID_EXTINFO_SERVER_SIG_ALGS, TYPE_OTHER, "server-sig-algs" },
 
     /* Curve Name IDs */
-    { ID_CURVE_NISTP256, "nistp256" },
-    { ID_CURVE_NISTP384, "nistp384" },
-    { ID_CURVE_NISTP521, "nistp521" },
+    { ID_CURVE_NISTP256, TYPE_OTHER, "nistp256" },
+    { ID_CURVE_NISTP384, TYPE_OTHER, "nistp384" },
+    { ID_CURVE_NISTP521, TYPE_OTHER, "nistp521" },
 };
 
 
@@ -2126,6 +2539,29 @@ const char* IdToName(byte id)
 }
 
 
+const char* NameByIndexType(byte type, word32* index)
+{
+    const char* name = NULL;
+
+    if (index != NULL) {
+        word32 i, mapSz;
+
+        mapSz = (word32)(sizeof(NameIdMap)/sizeof(NameIdPair));
+
+        for (i = *index; i < mapSz; i++) {
+            if (NameIdMap[i].type == type) {
+                name = NameIdMap[i].name;
+                break;
+            }
+        }
+
+        *index = i + 1;
+    }
+
+    return name;
+}
+
+
 WOLFSSH_CHANNEL* ChannelNew(WOLFSSH* ssh, byte channelType,
                             word32 initialWindowSz, word32 maxPacketSz)
 {
@@ -2568,7 +3004,7 @@ static int GetInputText(WOLFSSH* ssh, byte** pEol)
     int gotLine = 0;
     int inSz = 255;
     int in;
-    char *eol;
+    char *eol = NULL;
 
     if (GrowBuffer(&ssh->inputBuffer, inSz) < 0)
         return WS_MEMORY_E;
@@ -3013,51 +3449,23 @@ static int GetNameList(byte* idList, word32* idListSz,
     return ret;
 }
 
-static const byte cannedEncAlgo[] = {
-#ifndef WOLFSSH_NO_AES_GCM
-    ID_AES256_GCM,
-    ID_AES192_GCM,
-    ID_AES128_GCM,
-#endif
-#ifndef WOLFSSH_NO_AES_CTR
-    ID_AES256_CTR,
-    ID_AES192_CTR,
-    ID_AES128_CTR,
-#endif
-#ifndef WOLFSSH_NO_AES_CBC
-    ID_AES256_CBC,
-    ID_AES192_CBC,
-    ID_AES128_CBC,
-#endif
-};
-
-static const byte cannedMacAlgo[] = {
-#ifndef WOLFSSH_NO_HMAC_SHA2_256
-    ID_HMAC_SHA2_256,
-#endif
-#ifndef WOLFSSH_NO_HMAC_SHA1_96
-    ID_HMAC_SHA1_96,
-#endif
-#ifndef WOLFSSH_NO_HMAC_SHA1
-    ID_HMAC_SHA1,
-#endif
-};
-
 static const byte  cannedKeyAlgoClient[] = {
 #ifdef WOLFSSH_CERTS
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
-    ID_X509V3_ECDSA_SHA2_NISTP521,
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
-    ID_X509V3_ECDSA_SHA2_NISTP384,
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
-    ID_X509V3_ECDSA_SHA2_NISTP256,
-#endif
-#ifndef WOLFSSH_NO_SSH_RSA_SHA1
-    ID_X509V3_SSH_RSA,
-#endif
-#endif
+    #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
+        ID_X509V3_ECDSA_SHA2_NISTP521,
+    #endif
+    #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
+        ID_X509V3_ECDSA_SHA2_NISTP384,
+    #endif
+    #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
+        ID_X509V3_ECDSA_SHA2_NISTP256,
+    #endif
+    #ifdef WOLFSSH_NO_SHA1_SOFT_DISABLE
+        #ifndef WOLFSSH_NO_SSH_RSA_SHA1
+            ID_X509V3_SSH_RSA,
+        #endif /* WOLFSSH_NO_SSH_RSA_SHA1 */
+    #endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */
+#endif /* WOLFSSH_CERTS */
 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
     ID_ECDSA_SHA2_NISTP521,
 #endif
@@ -3073,39 +3481,17 @@ static const byte  cannedKeyAlgoClient[] = {
 #ifndef WOLFSSH_NO_RSA_SHA2_256
     ID_RSA_SHA2_256,
 #endif
-#ifndef WOLFSSH_NO_SSH_RSA_SHA1
-    ID_SSH_RSA,
-#endif
-};
-
-static const byte cannedKexAlgo[] = {
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-    ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256,
-#endif
-#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
-    ID_ECDH_SHA2_NISTP521,
-#endif
-#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
-    ID_ECDH_SHA2_NISTP384,
-#endif
-#ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
-    ID_ECDH_SHA2_NISTP256,
-#endif
-#ifndef WOLFSSH_NO_DH_GEX_SHA256
-    ID_DH_GEX_SHA256,
-#endif
-#ifndef WOLFSSH_NO_DH_GROUP14_SHA1
-    ID_DH_GROUP14_SHA1,
-#endif
-#ifndef WOLFSSH_NO_DH_GROUP1_SHA1
-    ID_DH_GROUP1_SHA1,
+#ifdef WOLFSSH_NO_SHA1_SOFT_DISABLE
+    #ifndef WOLFSSH_NO_SSH_RSA_SHA1
+        ID_SSH_RSA,
+    #endif /* WOLFSSH_NO_SSH_RSA_SHA1 */
+#endif /* WOLFSSH_NO_SHA1_SOFT_DISABLE */
+#ifndef WOLFSSH_NO_ED25519
+    ID_ED25519,
 #endif
 };
 
-static const word32 cannedEncAlgoSz = (word32)sizeof(cannedEncAlgo);
-static const word32 cannedMacAlgoSz = (word32)sizeof(cannedMacAlgo);
 static const word32 cannedKeyAlgoClientSz = (word32)sizeof(cannedKeyAlgoClient);
-static const word32 cannedKexAlgoSz = (word32)sizeof(cannedKexAlgo);
 
 
 static byte MatchIdLists(int side, const byte* left, word32 leftSz,
@@ -3234,8 +3620,7 @@ static INLINE byte KeySzForId(byte id)
     }
 }
 
-
-static INLINE enum wc_HashType HashForId(byte id)
+INLINE enum wc_HashType HashForId(byte id)
 {
     switch (id) {
 
@@ -3276,11 +3661,19 @@ static INLINE enum wc_HashType HashForId(byte id)
         case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
             return WC_HASH_TYPE_SHA256;
 #endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+        case ID_CURVE25519_SHA256:
+            return WC_HASH_TYPE_SHA256;
+#endif
 #ifndef WOLFSSH_NO_RSA_SHA2_256
         case ID_RSA_SHA2_256:
             return WC_HASH_TYPE_SHA256;
 #endif
 
+#ifndef WOLFSSH_NO_ED25519
+        case ID_ED25519:
+            return WC_HASH_TYPE_SHA512;
+#endif
         /* SHA2-384 */
 #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP384
         case ID_ECDH_SHA2_NISTP384:
@@ -3318,7 +3711,7 @@ static INLINE enum wc_HashType HashForId(byte id)
 
 
 #if !defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_ECDH)
-static INLINE int wcPrimeForId(byte id)
+int wcPrimeForId(byte id)
 {
     switch (id) {
 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
@@ -3341,6 +3734,10 @@ static INLINE int wcPrimeForId(byte id)
         case ID_ECDSA_SHA2_NISTP384:
             return ECC_SECP384R1;
 #endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+        case ID_CURVE25519_SHA256:
+            return ECC_X25519;
+#endif
 #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP521
         case ID_ECDH_SHA2_NISTP521:
             return ECC_SECP521R1;
@@ -3368,6 +3765,10 @@ static INLINE const char *PrimeNameForId(byte id)
 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
         case ID_ECDSA_SHA2_NISTP521:
             return "nistp521";
+#endif
+#ifndef WOLFSSH_NO_ED25519
+        case ID_ED25519:
+            return "ed25519";
 #endif
         default:
             return "unknown";
@@ -3391,13 +3792,29 @@ static INLINE byte AeadModeForId(byte id)
 }
 
 
+static word32 AlgoListSz(const char* algoList)
+{
+    word32 algoListSz;
+
+    algoListSz = (word32)WSTRLEN(algoList);
+    if (algoList[algoListSz-1] == ',') {
+        --algoListSz;
+    }
+
+    return algoListSz;
+}
+
+
 static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
 {
     int ret = WS_SUCCESS;
     int side = WOLFSSH_ENDPOINT_SERVER;
     byte algoId;
     byte list[24] = {ID_NONE};
+    byte cannedList[24] = {ID_NONE};
     word32 listSz;
+    word32 cannedListSz;
+    word32 cannedAlgoNamesSz;
     word32 skipSz;
     word32 begin;
 
@@ -3447,32 +3864,37 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         WLOG(WS_LOG_DEBUG, "DKI: KEX Algorithms");
         listSz = (word32)sizeof(list);
         ret = GetNameList(list, &listSz, buf, len, &begin);
-        if (ret == WS_SUCCESS) {
-            ssh->handshake->kexIdGuess = list[0];
-            algoId = MatchIdLists(side, list, listSz,
-                    cannedKexAlgo, cannedKexAlgoSz);
-            if (algoId == ID_UNKNOWN) {
-                WLOG(WS_LOG_DEBUG, "Unable to negotiate KEX Algo");
-                ret = WS_MATCH_KEX_ALGO_E;
-            }
-            else {
-                ssh->handshake->kexId = algoId;
-                ssh->handshake->kexHashId = HashForId(algoId);
-            }
+    }
+    if (ret == WS_SUCCESS) {
+        cannedAlgoNamesSz = AlgoListSz(ssh->algoListKex);
+        cannedListSz = (word32)sizeof(cannedList);
+        ret = GetNameListRaw(cannedList, &cannedListSz,
+                (const byte*)ssh->algoListKex, cannedAlgoNamesSz);
+    }
+    if (ret == WS_SUCCESS) {
+        ssh->handshake->kexIdGuess = list[0];
+        algoId = MatchIdLists(side, list, listSz,
+                cannedList, cannedListSz);
+        if (algoId == ID_UNKNOWN) {
+            WLOG(WS_LOG_DEBUG, "Unable to negotiate KEX Algo");
+            ret = WS_MATCH_KEX_ALGO_E;
         }
+    }
+    if (ret == WS_SUCCESS) {
+        ssh->kexId = ssh->handshake->kexId = algoId;
+        ssh->handshake->kexHashId = HashForId(algoId);
+    }
+    /* Extension Info Flag */
+    if (ret == WS_SUCCESS) {
+        /* Only checking for this is we are server. Our client does
+         * not have anything to say to a server, yet. */
+        if (side == WOLFSSH_ENDPOINT_SERVER) {
+            byte extInfo;
 
-        /* Extension Info Flag */
-        if (ret == WS_SUCCESS) {
-            /* Only checking for this is we are server. Our client does
-             * not have anything to say to a server, yet. */
-            if (side == WOLFSSH_ENDPOINT_SERVER) {
-                byte extInfo;
-
-                /* Match the client accepts extInfo. */
-                algoId = ID_EXTINFO_C;
-                extInfo = MatchIdLists(side, list, listSz, &algoId, 1);
-                ssh->sendExtInfo = extInfo == algoId;
-            }
+            /* Match the client accepts extInfo. */
+            algoId = ID_EXTINFO_C;
+            extInfo = MatchIdLists(side, list, listSz, &algoId, 1);
+            ssh->sendExtInfo = extInfo == algoId;
         }
     }
 
@@ -3481,27 +3903,27 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         WLOG(WS_LOG_DEBUG, "DKI: Server Host Key Algorithms");
         listSz = (word32)sizeof(list);
         ret = GetNameList(list, &listSz, buf, len, &begin);
-        if (ret == WS_SUCCESS) {
-            const byte *cannedKeyAlgo = NULL;
-            word32 cannedKeyAlgoSz = 0;
-
-            if (side == WOLFSSH_ENDPOINT_SERVER) {
-                cannedKeyAlgo = ssh->ctx->publicKeyAlgo;
-                cannedKeyAlgoSz = ssh->ctx->publicKeyAlgoCount;
-            }
-            else {
-                cannedKeyAlgo = cannedKeyAlgoClient;
-                cannedKeyAlgoSz = cannedKeyAlgoClientSz;
-            }
-            algoId = MatchIdLists(side, list, listSz,
-                                  cannedKeyAlgo, cannedKeyAlgoSz);
-            if (algoId == ID_UNKNOWN) {
-                WLOG(WS_LOG_DEBUG, "Unable to negotiate Server Host Key Algo");
-                return WS_MATCH_KEY_ALGO_E;
-            }
-            else {
-                ssh->handshake->pubKeyId = algoId;
-            }
+    }
+    if (ret == WS_SUCCESS) {
+        if (side == WOLFSSH_ENDPOINT_SERVER && !ssh->algoListKey) {
+            cannedListSz = ssh->ctx->publicKeyAlgoCount;
+            WMEMCPY(cannedList, ssh->ctx->publicKeyAlgo, cannedListSz);
+        }
+        else {
+            cannedAlgoNamesSz = AlgoListSz(ssh->algoListKey);
+            cannedListSz = (word32)sizeof(cannedList);
+            ret = GetNameListRaw(cannedList, &cannedListSz,
+                    (const byte*)ssh->algoListKey, cannedAlgoNamesSz);
+        }
+    }
+    if (ret == WS_SUCCESS) {
+        algoId = MatchIdLists(side, list, listSz, cannedList, cannedListSz);
+        if (algoId == ID_UNKNOWN) {
+            WLOG(WS_LOG_DEBUG, "Unable to negotiate Server Host Key Algo");
+            return WS_MATCH_KEY_ALGO_E;
+        }
+        else {
+            ssh->handshake->pubKeyId = algoId;
         }
     }
 
@@ -3510,13 +3932,18 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Client to Server");
         listSz = (word32)sizeof(list);
         ret = GetNameList(list, &listSz, buf, len, &begin);
-        if (ret == WS_SUCCESS) {
-            algoId = MatchIdLists(side, list, listSz,
-                    cannedEncAlgo, cannedEncAlgoSz);
-            if (algoId == ID_UNKNOWN) {
-                WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo C2S");
-                ret = WS_MATCH_ENC_ALGO_E;
-            }
+    }
+    if (ret == WS_SUCCESS) {
+        cannedAlgoNamesSz = AlgoListSz(ssh->algoListCipher);
+        cannedListSz = (word32)sizeof(cannedList);
+        ret = GetNameListRaw(cannedList, &cannedListSz,
+                (const byte*)ssh->algoListCipher, cannedAlgoNamesSz);
+    }
+    if (ret == WS_SUCCESS) {
+        algoId = MatchIdLists(side, list, listSz, cannedList, cannedListSz);
+        if (algoId == ID_UNKNOWN) {
+            WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo C2S");
+            ret = WS_MATCH_ENC_ALGO_E;
         }
     }
 
@@ -3525,30 +3952,33 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         WLOG(WS_LOG_DEBUG, "DKI: Enc Algorithms - Server to Client");
         listSz = (word32)sizeof(list);
         ret = GetNameList(list, &listSz, buf, len, &begin);
-        if (MatchIdLists(side, list, listSz, &algoId, 1) == ID_UNKNOWN) {
+    }
+    if (ret == WS_SUCCESS) {
+        algoId = MatchIdLists(side, list, listSz, &algoId, 1);
+        if (algoId == ID_UNKNOWN) {
             WLOG(WS_LOG_DEBUG, "Unable to negotiate Encryption Algo S2C");
             ret = WS_MATCH_ENC_ALGO_E;
         }
+    }
+    if (ret == WS_SUCCESS) {
+        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->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 {
 #ifndef WOLFSSH_NO_AEAD
-                ssh->handshake->keys.ivSz =
-                    ssh->handshake->peerKeys.ivSz =
-                    AEAD_NONCE_SZ;
-                ssh->handshake->macSz = ssh->handshake->blockSz;
+            ssh->handshake->keys.ivSz =
+                ssh->handshake->peerKeys.ivSz =
+                AEAD_NONCE_SZ;
+            ssh->handshake->macSz = ssh->handshake->blockSz;
 #endif
-            }
         }
     }
 
@@ -3557,9 +3987,15 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Client to Server");
         listSz = (word32)sizeof(list);
         ret = GetNameList(list, &listSz, buf, len, &begin);
-        if (ret == WS_SUCCESS && !ssh->aeadMode) {
+    }
+    if (ret == WS_SUCCESS && !ssh->handshake->aeadMode) {
+        cannedAlgoNamesSz = AlgoListSz(ssh->algoListMac);
+        cannedListSz = (word32)sizeof(cannedList);
+        ret = GetNameListRaw(cannedList, &cannedListSz,
+                (const byte*)ssh->algoListMac, cannedAlgoNamesSz);
+        if (ret == WS_SUCCESS) {
             algoId = MatchIdLists(side, list, listSz,
-                    cannedMacAlgo, cannedMacAlgoSz);
+                    cannedList, cannedListSz);
             if (algoId == ID_UNKNOWN) {
                 WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo C2S");
                 ret = WS_MATCH_MAC_ALGO_E;
@@ -3572,18 +4008,19 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         WLOG(WS_LOG_DEBUG, "DKI: MAC Algorithms - Server to Client");
         listSz = (word32)sizeof(list);
         ret = GetNameList(list, &listSz, buf, len, &begin);
-        if (ret == WS_SUCCESS && !ssh->handshake->aeadMode) {
-            if (MatchIdLists(side, list, listSz, &algoId, 1) == ID_UNKNOWN) {
-                WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo S2C");
-                ret = WS_MATCH_MAC_ALGO_E;
-            }
-            else {
-                ssh->handshake->macId = algoId;
-                ssh->handshake->macSz = MacSzForId(algoId);
-                ssh->handshake->keys.macKeySz =
-                    ssh->handshake->peerKeys.macKeySz =
-                    KeySzForId(algoId);
-            }
+    }
+    if (ret == WS_SUCCESS && !ssh->handshake->aeadMode) {
+        algoId = MatchIdLists(side, list, listSz, &algoId, 1);
+        if (algoId == ID_UNKNOWN) {
+            WLOG(WS_LOG_DEBUG, "Unable to negotiate MAC Algo S2C");
+            ret = WS_MATCH_MAC_ALGO_E;
+        }
+        else {
+            ssh->handshake->macId = algoId;
+            ssh->handshake->macSz = MacSzForId(algoId);
+            ssh->handshake->keys.macKeySz =
+                ssh->handshake->peerKeys.macKeySz =
+                KeySzForId(algoId);
         }
     }
 
@@ -3595,11 +4032,12 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Client to Server");
         listSz = (word32)sizeof(list);
         ret = GetNameList(list, &listSz, buf, len, &begin);
-        if (ret == WS_SUCCESS) {
-            if (MatchIdLists(side, list, listSz, &algoId, 1) == ID_UNKNOWN) {
-                WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo C2S");
-                ret = WS_INVALID_ALGO_ID;
-            }
+    }
+    if (ret == WS_SUCCESS) {
+        algoId = MatchIdLists(side, list, listSz, &algoId, 1);
+        if (algoId == ID_UNKNOWN) {
+            WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo C2S");
+            ret = WS_INVALID_ALGO_ID;
         }
     }
 
@@ -3608,11 +4046,12 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         WLOG(WS_LOG_DEBUG, "DKI: Compression Algorithms - Server to Client");
         listSz = (word32)sizeof(list);
         ret = GetNameList(list, &listSz, buf, len, &begin);
-        if (ret == WS_SUCCESS) {
-            if (MatchIdLists(side, list, listSz, &algoId, 1) == ID_UNKNOWN) {
-                WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo S2C");
-                ret = WS_INVALID_ALGO_ID;
-            }
+    }
+    if (ret == WS_SUCCESS) {
+        algoId = MatchIdLists(side, list, listSz, &algoId, 1);
+        if (algoId == ID_UNKNOWN) {
+            WLOG(WS_LOG_DEBUG, "Unable to negotiate Compression Algo S2C");
+            ret = WS_INVALID_ALGO_ID;
         }
     }
 
@@ -3654,7 +4093,7 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         wc_HashAlg* hash = &ssh->handshake->kexHash;
         enum wc_HashType hashId = (enum wc_HashType)ssh->handshake->kexHashId;
         byte scratchLen[LENGTH_SZ];
-        word32 strSz;
+        word32 strSz = 0;
 
         if (!ssh->isKeying) {
             WLOG(WS_LOG_DEBUG, "Keying initiated");
@@ -3681,7 +4120,8 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         }
 
         if (ret == WS_SUCCESS) {
-            ret = HashUpdate(hash, hashId, (const byte*)ssh->ctx->sshProtoIdStr, strSz);
+            ret = HashUpdate(hash, hashId,
+                    (const byte*)ssh->ctx->sshProtoIdStr, strSz);
         }
 
         if (ret == WS_SUCCESS) {
@@ -3913,7 +4353,10 @@ static int DoKexDhInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
 
 
 struct wolfSSH_sigKeyBlock {
-    byte useRsa;
+    byte useRsa:1;
+    byte useEcc:1;
+    byte useEd25519:1;
+    byte keyAllocated:1;
     word32 keySz;
     union {
 #ifndef WOLFSSH_NO_RSA
@@ -3925,6 +4368,11 @@ struct wolfSSH_sigKeyBlock {
         struct {
             ecc_key key;
         } ecc;
+#endif
+#ifndef WOLFSSH_NO_ED25519
+        struct {
+            ed25519_key key;
+        } ed25519;
 #endif
     } sk;
 };
@@ -3968,8 +4416,10 @@ static int ParseRSAPubKey(WOLFSSH *ssh,
                                        &sigKeyBlock_ptr->sk.rsa.key);
     }
 
-    if (ret == 0)
+    if (ret == 0) {
         sigKeyBlock_ptr->keySz = (word32)sizeof(sigKeyBlock_ptr->sk.rsa.key);
+        sigKeyBlock_ptr->keyAllocated = 1;
+    }
     else
         ret = WS_RSA_E;
 #else
@@ -3990,7 +4440,7 @@ static int ParseECCPubKey(WOLFSSH *ssh,
 #ifndef WOLFSSH_NO_ECDSA
     const byte* q;
     word32 qSz, pubKeyIdx = 0;
-    int primeId;
+    int primeId = 0;
     word32 scratch;
 
     ret = wc_ecc_init_ex(&sigKeyBlock_ptr->sk.ecc.key, ssh->ctx->heap,
@@ -4030,6 +4480,7 @@ static int ParseECCPubKey(WOLFSSH *ssh,
         if (ret == 0) {
             sigKeyBlock_ptr->keySz =
                 (word32)sizeof(sigKeyBlock_ptr->sk.ecc.key);
+            sigKeyBlock_ptr->keyAllocated = 1;
         }
         else
             ret = WS_ECC_E;
@@ -4045,61 +4496,105 @@ static int ParseECCPubKey(WOLFSSH *ssh,
 }
 
 
+/* Parse out a RAW Ed25519 public key from buffer */
+static int ParseEd25519PubKey(WOLFSSH *ssh,
+        struct wolfSSH_sigKeyBlock *sigKeyBlock_ptr,
+        byte *pubKey, word32 pubKeySz)
+#ifndef WOLFSSH_NO_ED25519
+{
+    int ret;
+    const byte* encA;
+    word32 encASz, pubKeyIdx = 0;
+
+    ret = wc_ed25519_init_ex(&sigKeyBlock_ptr->sk.ed25519.key,
+            ssh->ctx->heap, INVALID_DEVID);
+    if (ret != 0)
+        ret = WS_ED25519_E;
+
+    /* Skip the algo name */
+    if (ret == WS_SUCCESS) {
+        ret = GetSkip(pubKey, pubKeySz, &pubKeyIdx);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = GetStringRef(&encASz, &encA, pubKey, pubKeySz, &pubKeyIdx);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = wc_ed25519_import_public(encA, encASz,
+                &sigKeyBlock_ptr->sk.ed25519.key);
+        if (ret != 0)
+            ret = WS_ED25519_E;
+    }
+    return ret;
+}
+#else
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(sigKeyBlock_ptr);
+    WOLFSSH_UNUSED(pubKey);
+    WOLFSSH_UNUSED(pubKeySz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif
+
+
 #ifdef WOLFSSH_CERTS
-/* finds the leaf certificate and verifies it with known CA's
+/* finds the leaf certificate and optionally the bounds of the cert chain,
  * returns WS_SUCCESS on success */
-static int ParseAndVerifyCert(WOLFSSH* ssh, byte* in, word32 inSz,
-    byte** leafOut, word32* leafOutSz)
+static int ParseCertChain(byte* in, word32 inSz,
+        byte** certChain, word32* certChainSz, word32* certCount,
+        byte** leafOut, word32* leafOutSz)
 {
     int ret;
-    word32 l = 0, m = 0;
+    word32 sz = 0, idx = 0;
     word32 ocspCount = 0;
-    word32 certCount = 0;
-    byte*  certChain = NULL;
-    word32 certChainSz = 0;
-    word32 count;
+    byte*  chain = NULL;
+    word32 chainSz = 0;
+    word32 count, countIdx;
 
     /* Skip the name */
-    ret = GetSize(&l, in, inSz, &m);
-    m += l;
+    ret = GetSize(&sz, in, inSz, &idx);
 
     if (ret == WS_SUCCESS) {
+        idx += sz;
+
         /* Get the cert count */
-        ret = GetUint32(&certCount, in, inSz, &m);
+        ret = GetUint32(&count, in, inSz, &idx);
     }
 
     if (ret == WS_SUCCESS) {
-        WLOG(WS_LOG_INFO, "Peer sent certificate count of %d", certCount);
-        certChain = in + m;
-
-        for (count = certCount; count > 0; count--) {
-            word32 certSz = 0;
+        WLOG(WS_LOG_INFO, "Peer sent certificate count of %d", count);
+        chain = in + idx;
 
-            ret = GetSize(&certSz, in, inSz, &m);
+        for (countIdx = count; countIdx > 0; countIdx--) {
+            ret = GetSize(&sz, in, inSz, &idx);
             if (ret != WS_SUCCESS) {
                 break;
             }
-            WLOG(WS_LOG_INFO, "Adding certificate size %u", certSz);
+            WLOG(WS_LOG_INFO, "Adding certificate size %u", sz);
 
             /* store leaf cert size to present to user callback */
-            if (count == certCount && leafOut != NULL) {
-                *leafOutSz = certSz;
-                *leafOut   = in + m;
+            if (countIdx == count) {
+                if (leafOut != NULL && leafOutSz != NULL) {
+                    *leafOutSz = sz;
+                    *leafOut = in + idx;
+                }
             }
-            certChainSz += certSz + UINT32_SZ;
-            m += certSz;
+            chainSz += sz + UINT32_SZ;
+            idx += sz;
         }
 
         /* get OCSP count */
         if (ret == WS_SUCCESS) {
-            ret = GetUint32(&ocspCount, in, inSz, &m);
+            ret = GetUint32(&ocspCount, in, inSz, &idx);
         }
 
         if (ret == WS_SUCCESS) {
             WLOG(WS_LOG_INFO, "Peer sent OCSP count of %u", ocspCount);
 
             /* RFC 6187 section 2.1 OCSP count must not exceed cert count */
-            if (ocspCount > certCount) {
+            if (ocspCount > count) {
                 WLOG(WS_LOG_ERROR, "Error more OCSP then Certs");
                 ret = WS_FATAL_ERROR;
             }
@@ -4113,7 +4608,36 @@ static int ParseAndVerifyCert(WOLFSSH* ssh, byte* in, word32 inSz,
         }
     }
 
-    /* verify the certificate chain */
+    if (ret == WS_SUCCESS) {
+        if (certChain != NULL && certChainSz != NULL && certCount != NULL) {
+            *certChain = chain;
+            *certChainSz = chainSz;
+            *certCount = count;
+        }
+    }
+
+    return ret;
+}
+
+
+static int ParseLeafCert(byte* in, word32 inSz,
+        byte** leafOut, word32* leafOutSz)
+{
+    return ParseCertChain(in, inSz, NULL, NULL, NULL, leafOut, leafOutSz);
+}
+
+
+static int ParseCertChainVerify(WOLFSSH* ssh, byte* in, word32 inSz,
+        byte** leafOut, word32* leafOutSz)
+{
+    byte *certChain = NULL;
+    word32 certChainSz = 0, certCount = 0;
+    int ret;
+
+    ret = ParseCertChain(in, inSz,
+            &certChain, &certChainSz, &certCount,
+            leafOut, leafOutSz);
+
     if (ret == WS_SUCCESS) {
         ret = wolfSSH_CERTMAN_VerifyCerts_buffer(ssh->ctx->certMan,
                     certChain, certChainSz, certCount);
@@ -4134,7 +4658,7 @@ static int ParsePubKeyCert(WOLFSSH* ssh, byte* in, word32 inSz, byte** out,
     byte*  leaf   = NULL;
     word32 leafSz = 0;
 
-    ret = ParseAndVerifyCert(ssh, in, inSz, &leaf, &leafSz);
+    ret = ParseCertChainVerify(ssh, in, inSz, &leaf, &leafSz);
     if (ret == WS_SUCCESS) {
         int error = 0;
         struct DecodedCert dCert;
@@ -4190,8 +4714,10 @@ static int ParseECCPubKeyCert(WOLFSSH *ssh,
         if (error == 0)
             error = wc_EccPublicKeyDecode(der, &idx,
                 &sigKeyBlock_ptr->sk.ecc.key, derSz);
-        if (error == 0)
+        if (error == 0) {
             sigKeyBlock_ptr->keySz = (word32)sizeof(sigKeyBlock_ptr->sk.ecc.key);
+            sigKeyBlock_ptr->keyAllocated = 1;
+        }
         if (error != 0)
             ret = error;
         WFREE(der, NULL, 0);
@@ -4228,6 +4754,7 @@ static int ParseRSAPubKeyCert(WOLFSSH *ssh,
         if (error == 0) {
             sigKeyBlock_ptr->keySz =
                 (word32)sizeof(sigKeyBlock_ptr->sk.rsa.key);
+            sigKeyBlock_ptr->keyAllocated = 1;
         }
         if (error != 0)
             ret = error;
@@ -4271,7 +4798,7 @@ static int ParsePubKey(WOLFSSH *ssh,
         case ID_ECDSA_SHA2_NISTP256:
         case ID_ECDSA_SHA2_NISTP384:
         case ID_ECDSA_SHA2_NISTP521:
-            sigKeyBlock_ptr->useRsa = 0;
+            sigKeyBlock_ptr->useEcc = 1;
             ret = ParseECCPubKey(ssh, sigKeyBlock_ptr, pubKey, pubKeySz);
             break;
 
@@ -4279,11 +4806,16 @@ static int ParsePubKey(WOLFSSH *ssh,
         case ID_X509V3_ECDSA_SHA2_NISTP256:
         case ID_X509V3_ECDSA_SHA2_NISTP384:
         case ID_X509V3_ECDSA_SHA2_NISTP521:
-            sigKeyBlock_ptr->useRsa = 0;
+            sigKeyBlock_ptr->useEcc = 1;
             ret = ParseECCPubKeyCert(ssh, sigKeyBlock_ptr, pubKey, pubKeySz);
             break;
     #endif
 
+        case ID_ED25519:
+            sigKeyBlock_ptr->useEd25519 = 1;
+            ret = ParseEd25519PubKey(ssh, sigKeyBlock_ptr, pubKey, pubKeySz);
+            break;
+
         default:
             ret = WS_INVALID_ALGO_ID;
     }
@@ -4292,38 +4824,375 @@ static int ParsePubKey(WOLFSSH *ssh,
 }
 
 
-static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+static void FreePubKey(struct wolfSSH_sigKeyBlock *p)
 {
-    struct wolfSSH_sigKeyBlock *sigKeyBlock_ptr = NULL;
-    wc_HashAlg* hash = NULL;
-    byte* pubKey = NULL;
-    byte* f = NULL;
-    byte* sig;
-    word32 pubKeySz;
-    word32 fSz;
-    word32 sigSz;
-    word32 scratch;
-    word32 begin;
-    int ret = WS_SUCCESS;
-    enum wc_HashType hashId;
-    byte scratchLen[LENGTH_SZ];
-    byte kPad = 0;
-    byte keyAllocated = 0;
+    if (p && p->keyAllocated) {
+        if (p->useRsa) {
+        #ifndef WOLFSSH_NO_RSA
+            wc_FreeRsaKey(&p->sk.rsa.key);
+        #endif
+        }
+        else if (p->useEcc) {
+        #ifndef WOLFSSH_NO_ECDSA
+            wc_ecc_free(&p->sk.ecc.key);
+        #endif
+        }
+        p->keyAllocated = 0;
+    }
+}
+
+
+/* KeyAgreeDh_client
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ */
+static int KeyAgreeDh_client(WOLFSSH* ssh, byte hashId,
+        const byte* f, word32 fSz)
+#ifndef WOLFSSH_NO_DH
+{
+    int ret;
+
+    WLOG(WS_LOG_DEBUG, "Entering KeyAgreeDh_client()");
+    WOLFSSH_UNUSED(hashId);
+
+    PRIVATE_KEY_UNLOCK();
+    ret = wc_DhAgree(&ssh->handshake->privKey.dh,
+                     ssh->k, &ssh->kSz,
+                     ssh->handshake->x, ssh->handshake->xSz,
+                     f, fSz);
+    PRIVATE_KEY_LOCK();
+    if (ret != 0) {
+        WLOG(WS_LOG_ERROR,
+                "Generate DH shared secret failed, %d", ret);
+        ret = WS_CRYPTO_FAILED;
+    }
+    ForceZero(ssh->handshake->x, ssh->handshake->xSz);
+    wc_FreeDhKey(&ssh->handshake->privKey.dh);
+
+    WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeDh_client(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_DH */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(hashId);
+    WOLFSSH_UNUSED(f);
+    WOLFSSH_UNUSED(fSz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_DH */
+
+
+/* KeyAgreeEcdh_client
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ */
+static int KeyAgreeEcdh_client(WOLFSSH* ssh, byte hashId,
+        const byte* f, word32 fSz)
 #ifndef WOLFSSH_NO_ECDH
+{
+    int ret = WS_SUCCESS;
     ecc_key *key_ptr = NULL;
     #ifndef WOLFSSH_SMALL_STACK
         ecc_key key_s;
     #endif
-#endif
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-    byte sharedSecretHashSz = 0;
-    byte *sharedSecretHash = NULL;
-#endif
 
-    WLOG(WS_LOG_DEBUG, "Entering DoKexDhReply()");
+    WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdh_client()");
+    WOLFSSH_UNUSED(hashId);
 
-    if (ssh == NULL || ssh->handshake == NULL || buf == NULL ||
-            len == 0 || idx == NULL) {
+    #ifdef WOLFSSH_SMALL_STACK
+        key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key),
+                ssh->ctx->heap, DYNTYPE_PRIVKEY);
+        if (key_ptr == NULL) {
+            ret = WS_MEMORY_E;
+        }
+    #else /* ! WOLFSSH_SMALL_STACK */
+        key_ptr = &key_s;
+    #endif /* WOLFSSH_SMALL_STACK */
+    ret = wc_ecc_init(key_ptr);
+    #ifdef HAVE_WC_ECC_SET_RNG
+    if (ret == 0)
+        ret = wc_ecc_set_rng(key_ptr, ssh->rng);
+    #endif
+    if (ret == 0)
+        ret = wc_ecc_import_x963(f, fSz, key_ptr);
+    if (ret == 0) {
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc,
+                key_ptr, ssh->k, &ssh->kSz);
+        PRIVATE_KEY_LOCK();
+        if (ret != 0) {
+            WLOG(WS_LOG_ERROR,
+                    "Generate ECC shared secret failed, %d", ret);
+            ret = WS_CRYPTO_FAILED;
+        }
+    }
+    wc_ecc_free(key_ptr);
+    #ifdef WOLFSSH_SMALL_STACK
+    if (key_ptr) {
+        WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY);
+    }
+    #endif
+    wc_ecc_free(&ssh->handshake->privKey.ecc);
+
+    WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdh_client(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_ECDH */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(hashId);
+    WOLFSSH_UNUSED(f);
+    WOLFSSH_UNUSED(fSz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_ECDH */
+
+
+/* KeyAgreeCurve25519_client
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ */
+static int KeyAgreeCurve25519_client(WOLFSSH* ssh, byte hashId,
+        const byte* f, word32 fSz)
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+{
+    int ret;
+    curve25519_key pub;
+
+    WLOG(WS_LOG_DEBUG, "Entering KeyAgreeCurve25519_client()");
+    WOLFSSH_UNUSED(hashId);
+
+    ret = wc_curve25519_init(&pub);
+    if (ret == 0) {
+        ret = wc_curve25519_check_public(f, fSz,
+                EC25519_LITTLE_ENDIAN);
+    }
+
+    if (ret == 0) {
+        ret = wc_curve25519_import_public_ex(f, fSz, &pub,
+                EC25519_LITTLE_ENDIAN);
+    }
+
+    if (ret == 0) {
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_curve25519_shared_secret_ex(
+                  &ssh->handshake->privKey.curve25519, &pub,
+                  ssh->k, &ssh->kSz, EC25519_LITTLE_ENDIAN);
+        PRIVATE_KEY_LOCK();
+        if (ret != 0) {
+            WLOG(WS_LOG_ERROR,
+                    "Gen curve25519 shared secret failed, %d", ret);
+            ret = WS_CRYPTO_FAILED;
+        }
+    }
+
+    wc_curve25519_free(&pub);
+    wc_curve25519_free(&ssh->handshake->privKey.curve25519);
+
+    WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeCurve25519_client(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_CURVE25519_SHA256 */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(hashId);
+    WOLFSSH_UNUSED(f);
+    WOLFSSH_UNUSED(fSz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_CURVE25519_SHA256 */
+
+
+/* KeyAgreeEcdhKyber1_client
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ */
+static int KeyAgreeEcdhKyber1_client(WOLFSSH* ssh, byte hashId,
+        const byte* f, word32 fSz)
+#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
+{
+    int ret = WS_SUCCESS;
+    byte sharedSecretHashSz = 0;
+    byte *sharedSecretHash = NULL;
+    ecc_key *key_ptr = NULL;
+    OQS_KEM *kem;
+
+    #ifndef WOLFSSH_SMALL_STACK
+        ecc_key key_s;
+    #endif
+    #ifdef WOLFSSH_SMALL_STACK
+        key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key),
+                ssh->ctx->heap, DYNTYPE_PRIVKEY);
+        if (key_ptr == NULL) {
+            ret = WS_MEMORY_E;
+        }
+    #else /* ! WOLFSSH_SMALL_STACK */
+        key_ptr = &key_s;
+    #endif /* WOLFSSH_SMALL_STACK */
+
+    WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdhKyber1_client()");
+
+    /* This is a a hybrid of ECDHE and a post-quantum KEM. In this
+     * case, I need to generated the ECC shared secret and
+     * decapsulate the ciphertext of the post-quantum KEM. */
+    kem = OQS_KEM_new(OQS_KEM_alg_kyber_512);
+    if (kem == NULL) {
+        ret = WS_MEMORY_E;
+    }
+
+    if ((ret == 0) && (fSz <= (word32)kem->length_ciphertext)) {
+        ret = WS_BUFFER_E;
+    }
+
+    if (ret == 0) {
+        ret = wc_ecc_init(key_ptr);
+    }
+    #ifdef HAVE_WC_ECC_SET_RNG
+    if (ret == 0) {
+        ret = wc_ecc_set_rng(key_ptr, ssh->rng);
+    }
+    #endif
+    if (ret == 0) {
+        ret = wc_ecc_import_x963(f + kem->length_ciphertext,
+                  fSz - (word32)kem->length_ciphertext,
+                  key_ptr);
+    }
+
+    if (ret == 0) {
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc,
+                   key_ptr, ssh->k + kem->length_shared_secret,
+                   &ssh->kSz);
+        PRIVATE_KEY_LOCK();
+    }
+    wc_ecc_free(key_ptr);
+    #ifdef WOLFSSH_SMALL_STACK
+    if (key_ptr) {
+        WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY);
+    }
+    #endif
+    wc_ecc_free(&ssh->handshake->privKey.ecc);
+
+    if (ret == 0) {
+        if (OQS_KEM_decaps(kem, ssh->k, f, ssh->handshake->x)
+            != OQS_SUCCESS) {
+            ret = WS_ERROR;
+        }
+    }
+
+    if (ret == 0) {
+        ssh->kSz += kem->length_shared_secret;
+    } else {
+        ssh->kSz = 0;
+        WLOG(WS_LOG_ERROR,
+             "Generate ECC-kyber (decap) shared secret failed, %d",
+             ret);
+    }
+
+    if (kem != NULL) {
+        OQS_KEM_free(kem);
+    }
+
+    /* Replace the concatenated shared secrets with the hash. That
+     * will become the new shared secret. */
+    if (ret == 0) {
+        sharedSecretHashSz = wc_HashGetDigestSize(hashId);
+        sharedSecretHash = (byte *)WMALLOC(sharedSecretHashSz,
+                                           ssh->ctx->heap,
+                                           DYNTYPE_PRIVKEY);
+        if (sharedSecretHash == NULL) {
+            ret = WS_MEMORY_E;
+        }
+    }
+
+    if (ret == 0) {
+        ret = wc_Hash(hashId, ssh->k, ssh->kSz, sharedSecretHash,
+                      sharedSecretHashSz);
+    }
+
+    if (ret == 0) {
+        XMEMCPY(ssh->k, sharedSecretHash, sharedSecretHashSz);
+        ssh->kSz = sharedSecretHashSz;
+    }
+
+    if (sharedSecretHash) {
+        ForceZero(sharedSecretHash, sharedSecretHashSz);
+        WFREE(sharedSecretHash, ssh->ctx->heap, DYNTYPE_PRIVKEY);
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhKyber1_client(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(hashId);
+    WOLFSSH_UNUSED(f);
+    WOLFSSH_UNUSED(fSz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
+
+
+/* KeyAgree_client
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ */
+static int KeyAgree_client(WOLFSSH* ssh, byte hashId, const byte* f, word32 fSz)
+{
+    int ret;
+
+    /* reset size here because a previous shared secret could
+     * potentially be smaller by a byte than usual and cause buffer
+     * issues with re-key */
+    ssh->kSz = MAX_KEX_KEY_SZ;
+
+    if (ssh->handshake->useDh) {
+        ret = KeyAgreeDh_client(ssh, hashId, f, fSz);
+    }
+    else if (ssh->handshake->useEcc) {
+        ret = KeyAgreeEcdh_client(ssh, hashId, f, fSz);
+    }
+    else if (ssh->handshake->useCurve25519) {
+        ret = KeyAgreeCurve25519_client(ssh, hashId, f, fSz);
+    }
+    else if (ssh->handshake->useEccKyber) {
+        ret = KeyAgreeEcdhKyber1_client(ssh, hashId, f, fSz);
+    }
+    else {
+        ret = WS_INVALID_ALGO_ID;
+    }
+    return ret;
+}
+
+
+static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
+{
+    struct wolfSSH_sigKeyBlock *sigKeyBlock_ptr = NULL;
+    wc_HashAlg* hash = NULL;
+    byte* pubKey = NULL;
+    byte* f = NULL;
+    byte* sig;
+    word32 pubKeySz;
+    word32 fSz;
+    word32 sigSz;
+    word32 scratch;
+    word32 begin;
+    int ret = WS_SUCCESS;
+    enum wc_HashType hashId;
+    byte scratchLen[LENGTH_SZ];
+    byte kPad = 0;
+
+    WLOG(WS_LOG_DEBUG, "Entering DoKexDhReply()");
+
+    if (ssh == NULL || ssh->handshake == NULL || buf == NULL ||
+            len == 0 || idx == NULL) {
         ret = WS_BAD_ARGUMENT;
         WLOG(WS_LOG_DEBUG, "Leaving DoKexDhReply(), ret = %d", ret);
         return ret;
@@ -4494,24 +5363,10 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         if (sigKeyBlock_ptr == NULL) {
             ret = WS_MEMORY_E;
         }
-
-#ifdef WOLFSSH_SMALL_STACK
-#ifndef WOLFSSH_NO_ECDSA
-        key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), ssh->ctx->heap,
-                DYNTYPE_PRIVKEY);
-        if (key_ptr == NULL) {
-            ret = WS_MEMORY_E;
-        }
-#endif /* WOLFSSH_NO_ECDSA */
-
-#else /* ! WOLFSSH_SMALL_STACK */
-#ifndef WOLFSSH_NO_ECDSA
-        key_ptr = &key_s;
-#endif
-#endif
     }
 
     if (ret == WS_SUCCESS) {
+        WMEMSET(sigKeyBlock_ptr, 0, sizeof(*sigKeyBlock_ptr));
         sig = buf + begin;
         begin += sigSz;
         *idx = begin;
@@ -4519,151 +5374,14 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         ret = ParsePubKey(ssh, sigKeyBlock_ptr, pubKey, pubKeySz);
         /* Generate and hash in the shared secret */
         if (ret == WS_SUCCESS) {
-            /* Remember that the key needs to be freed */
-            keyAllocated = 1;
-            /* reset size here because a previous shared secret could
-             * potentially be smaller by a byte than usual and cause buffer
-             * issues with re-key */
-            ssh->kSz = MAX_KEX_KEY_SZ;
-            if (!ssh->handshake->useEcc
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-                && !ssh->handshake->useEccKyber
-#endif
-               ) {
-#ifndef WOLFSSH_NO_DH
-                PRIVATE_KEY_UNLOCK();
-                ret = wc_DhAgree(&ssh->handshake->privKey.dh,
-                                 ssh->k, &ssh->kSz,
-                                 ssh->handshake->x, ssh->handshake->xSz,
-                                 f, fSz);
-                PRIVATE_KEY_LOCK();
-                ForceZero(ssh->handshake->x, ssh->handshake->xSz);
-                wc_FreeDhKey(&ssh->handshake->privKey.dh);
-                if (ret != 0) {
-                    WLOG(WS_LOG_ERROR,
-                            "Generate DH shared secret failed, %d", ret);
-                }
-#else
-                ret = WS_INVALID_ALGO_ID;
-#endif
-            }
-            else if (ssh->handshake->useEcc) {
-#ifndef WOLFSSH_NO_ECDH
-                ret = wc_ecc_init(key_ptr);
-#ifdef HAVE_WC_ECC_SET_RNG
-                if (ret == 0)
-                    ret = wc_ecc_set_rng(key_ptr, ssh->rng);
-#endif
-                if (ret == 0)
-                    ret = wc_ecc_import_x963(f, fSz, key_ptr);
-                if (ret == 0) {
-                    PRIVATE_KEY_UNLOCK();
-                    ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc,
-                                               key_ptr, ssh->k, &ssh->kSz);
-                    PRIVATE_KEY_LOCK();
-                }
-                wc_ecc_free(key_ptr);
-                wc_ecc_free(&ssh->handshake->privKey.ecc);
-                if (ret != 0) {
-                    WLOG(WS_LOG_ERROR,
-                            "Generate ECC shared secret failed, %d", ret);
-                }
-#else
-                ret = WS_INVALID_ALGO_ID;
-#endif
-            }
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-            else if (ssh->handshake->useEccKyber) {
-                /* This is a a hybrid of ECDHE and a post-quantum KEM. In this
-                 * case, I need to generated the ECC shared secret and
-                 * decapsulate the ciphertext of the post-quantum KEM. */
-                OQS_KEM* kem = OQS_KEM_new(OQS_KEM_alg_kyber_512);
-                if (kem == NULL) {
-                    ret = WS_INVALID_ALGO_ID;
-                }
-
-                if ((ret == 0) && (fSz <= (word32)kem->length_ciphertext)) {
-                    ret = WS_BUFFER_E;
-                }
-
-                if (ret == 0) {
-                    ret = wc_ecc_init(key_ptr);
-                }
-#ifdef HAVE_WC_ECC_SET_RNG
-                if (ret == 0) {
-                    ret = wc_ecc_set_rng(key_ptr, ssh->rng);
-                }
-#endif
-                if (ret == 0) {
-                    ret = wc_ecc_import_x963(f + kem->length_ciphertext,
-                              fSz - (word32)kem->length_ciphertext, key_ptr);
-                }
-
-                if (ret == 0) {
-                    PRIVATE_KEY_UNLOCK();
-                    ret = wc_ecc_shared_secret(&ssh->handshake->privKey.ecc,
-                               key_ptr, ssh->k + kem->length_shared_secret,
-                               &ssh->kSz);
-                    PRIVATE_KEY_LOCK();
-                }
-                wc_ecc_free(key_ptr);
-                wc_ecc_free(&ssh->handshake->privKey.ecc);
-
-                if (ret == 0) {
-                    if (OQS_KEM_decaps(kem, ssh->k, f, ssh->handshake->x)
-                        != OQS_SUCCESS) {
-                        ret = WS_ERROR;
-                    }
-                }
-
-                if (ret == 0) {
-                    ssh->kSz += kem->length_shared_secret;
-                } else {
-                    ssh->kSz = 0;
-                    WLOG(WS_LOG_ERROR,
-                         "Generate ECC-kyber (decap) shared secret failed, %d",
-                         ret);
-                }
-
-                if (kem != NULL) {
-                    OQS_KEM_free(kem);
-                }
-
-                /* Replace the concatenated shared secrets with the hash. That
-                 * will become the new shared secret. */
-                if (ret == 0) {
-                    sharedSecretHashSz = wc_HashGetDigestSize(hashId);
-                    sharedSecretHash = (byte *)WMALLOC(sharedSecretHashSz,
-                                                       ssh->ctx->heap,
-                                                       DYNTYPE_PRIVKEY);
-                    if (sharedSecretHash == NULL) {
-                        ret = WS_MEMORY_E;
-                    }
-                }
-
-                if (ret == 0) {
-                    ret = wc_Hash(hashId, ssh->k, ssh->kSz, sharedSecretHash,
-                                  sharedSecretHashSz);
-                }
-
-                if (ret == 0) {
-                    XMEMCPY(ssh->k, sharedSecretHash, sharedSecretHashSz);
-                    ssh->kSz = sharedSecretHashSz;
-                }
-            }
-#endif /* !WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
-            else {
-                ret = WS_INVALID_ALGO_ID;
-            }
+            ret = KeyAgree_client(ssh, hashId, f, fSz);
         }
 
         /* Hash in the shared secret K. */
-        if (ret == 0
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-            && !ssh->handshake->useEccKyber
-#endif
-        ) {
-            ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
+        if (ret == WS_SUCCESS) {
+            if (!ssh->handshake->useEccKyber) {
+                ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
+            }
         }
 
         if (ret == 0) {
@@ -4727,12 +5445,12 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
                 }
             }
             if (ret == WS_SUCCESS) {
+                sig = sig + begin;
+                /* In the fuzz, sigSz ends up 1 and it has issues. */
+                sigSz = scratch;
+
                 if (sigKeyBlock_ptr->useRsa) {
 #ifndef WOLFSSH_NO_RSA
-                    sig = sig + begin;
-                    /* In the fuzz, sigSz ends up 1 and it has issues. */
-                    sigSz = scratch;
-
                     if (sigSz < MIN_RSA_SIG_SZ) {
                         WLOG(WS_LOG_DEBUG, "Provided signature is too small.");
                         ret = WS_RSA_E;
@@ -4759,15 +5477,13 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
                     }
 #endif
                 }
-                else {
+                else if (sigKeyBlock_ptr->useEcc) {
 #ifndef WOLFSSH_NO_ECDSA
                     const byte* r;
                     const byte* s;
                     word32 rSz, sSz, asnSigSz;
                     byte asnSig[256];
 
-                    sig = sig + begin;
-                    sigSz = scratch;
                     begin = 0;
                     asnSigSz = (word32)sizeof(asnSig);
                     XMEMSET(asnSig, 0, asnSigSz);
@@ -4795,29 +5511,32 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
                     }
 #endif
                 }
+                else if (sigKeyBlock_ptr->useEd25519) {
+#ifndef WOLFSSH_NO_ED25519
+                    int res = 0;
+
+                    ret = wc_ed25519_verify_msg(sig, sigSz,
+                            ssh->h, ssh->hSz, &res,
+                            &sigKeyBlock_ptr->sk.ed25519.key);
+                    if (ret != 0 || res != 1) {
+                        WLOG(WS_LOG_DEBUG,
+                            "DoKexDhReply: Signature Verify fail (%d)",
+                            ret);
+                        ret = WS_ED25519_E;
+                    }
+#endif /* WOLFSSH_NO_ED25519 */
+                }
+                else {
+                    ret = WS_INVALID_ALGO_ID;
+                }
             }
         }
-
-        if (keyAllocated) {
-            if (sigKeyBlock_ptr->useRsa) {
-#ifndef WOLFSSH_NO_RSA
-                wc_FreeRsaKey(&sigKeyBlock_ptr->sk.rsa.key);
-#endif
-            }
-            else {
-#ifndef WOLFSSH_NO_ECDSA
-                wc_ecc_free(&sigKeyBlock_ptr->sk.ecc.key);
-#endif
-            }
-        }
-    }
+        FreePubKey(sigKeyBlock_ptr);
+    }
 
     if (ret == WS_SUCCESS) {
-        int useKeyPadding = 1;
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-        useKeyPadding = !ssh->handshake->useEccKyber;
-#endif
-        ret = GenerateKeys(ssh, hashId, useKeyPadding);
+        /* If we aren't using EccKyber, use padding. */
+        ret = GenerateKeys(ssh, hashId, !ssh->handshake->useEccKyber);
     }
 
     if (ret == WS_SUCCESS)
@@ -4825,12 +5544,6 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
 
     if (sigKeyBlock_ptr)
         WFREE(sigKeyBlock_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY);
-#ifdef WOLFSSH_SMALL_STACK
-    #ifndef WOLFSSH_NO_ECDSA
-    if (key_ptr)
-        WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY);
-    #endif
-#endif
     WLOG(WS_LOG_DEBUG, "Leaving DoKexDhReply(), ret = %d", ret);
     return ret;
 }
@@ -4911,6 +5624,9 @@ static int DoNewKeys(WOLFSSH* ssh, byte* buf, word32 len, word32* idx)
         HandshakeInfoFree(ssh->handshake, ssh->ctx->heap);
         ssh->handshake = NULL;
         WLOG(WS_LOG_DEBUG, "Keying completed");
+
+        if (ssh->ctx->keyingCompletionCb)
+            ssh->ctx->keyingCompletionCb(ssh->keyingCompletionCtx);
     }
 
     return ret;
@@ -5392,6 +6108,10 @@ static int DoUserAuthRequestNone(WOLFSSH* ssh, WS_UserAuthData* authData,
                 ret = WS_USER_AUTH_E;
                 #endif
             }
+            else if (ret == WOLFSSH_USERAUTH_WOULD_BLOCK) {
+                WLOG(WS_LOG_DEBUG, "DUARN: userauth callback would block");
+                ret = WS_AUTH_PENDING;
+            }
             else {
                 WLOG(WS_LOG_DEBUG, "DUARN: none check failed, retry");
                 ret = SendUserAuthFailure(ssh, 0);
@@ -5477,6 +6197,10 @@ static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData,
                 #endif
                 ret = WS_USER_AUTH_E;
             }
+            else if (ret == WOLFSSH_USERAUTH_WOULD_BLOCK) {
+                WLOG(WS_LOG_DEBUG, "DUARPW: userauth callback would block");
+                ret = WS_AUTH_PENDING;
+            }
             else {
                 WLOG(WS_LOG_DEBUG, "DUARPW: password check failed, retry");
                 authFailure = 1;
@@ -5495,7 +6219,7 @@ static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData,
     if (authFailure || partialSuccess) {
         ret = SendUserAuthFailure(ssh, partialSuccess);
     }
-    else {
+    else if (ret == WS_SUCCESS) {
         ssh->clientState = CLIENT_USERAUTH_DONE;
     }
 
@@ -5510,24 +6234,19 @@ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
                                 enum wc_HashType hashId, byte* digest,
                                 word32 digestSz)
 {
-    enum wc_HashType enmhashId = hashId;
-    byte *checkDigest = NULL;
-    byte *encDigest = NULL;
-    int checkDigestSz;
     const byte* publicKeyType;
-    word32 publicKeyTypeSz = 0;
-    const byte* n;
-    word32 nSz = 0;
-    const byte* e = NULL;
-    word32 eSz = 0;
     const byte* sig;
+    word32 publicKeyTypeSz = 0;
     word32 sigSz;
+    word32 encDigestSz;
     word32 i = 0;
     int ret = WS_SUCCESS;
-    RsaKey *key_ptr = NULL;
-
-#ifndef WOLFSSH_SMALL_STACK
-    byte s_checkDigest[MAX_ENCODED_SIG_SZ];
+#ifdef WOLFSSH_SMALL_STACK
+    byte* encDigest = NULL;
+    RsaKey* key = NULL;
+#else
+    byte encDigest[MAX_ENCODED_SIG_SZ];
+    RsaKey key[1];
 #endif
 
     WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestRsa()");
@@ -5538,24 +6257,22 @@ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
         ret = WS_BAD_ARGUMENT;
     }
 
-    if (ret == WS_SUCCESS) {
 #ifdef WOLFSSH_SMALL_STACK
-        checkDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, ssh->ctx->heap,
-                DYNTYPE_BUFFER);
-        if (checkDigest == NULL)
+    if (ret == WS_SUCCESS) {
+        encDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ,
+                ssh->ctx->heap, DYNTYPE_BUFFER);
+        if (encDigest == NULL)
             ret = WS_MEMORY_E;
-#else
-        checkDigest = s_checkDigest;
-#endif
-        key_ptr = (RsaKey*)WMALLOC(sizeof(RsaKey), ssh->ctx->heap,
-                DYNTYPE_PUBKEY);
-        if (key_ptr == NULL)
+    }
+    if (ret == WS_SUCCESS) {
+        key = (RsaKey*)WMALLOC(sizeof(RsaKey), ssh->ctx->heap, DYNTYPE_PUBKEY);
+        if (key == NULL)
             ret = WS_MEMORY_E;
     }
+#endif
 
     if (ret == WS_SUCCESS) {
-        WMEMSET(checkDigest, 0, MAX_ENCODED_SIG_SZ);
-        ret = wc_InitRsaKey(key_ptr, ssh->ctx->heap);
+        ret = wc_InitRsaKey(key, ssh->ctx->heap);
         if (ret == 0) {
             ret = WS_SUCCESS;
         }
@@ -5577,19 +6294,27 @@ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
         }
     }
 
+    /* Load up the key. */
     if (ret == WS_SUCCESS) {
-        ret = GetMpint(&eSz, &e, pk->publicKey, pk->publicKeySz, &i);
-    }
+        const byte* n = NULL;
+        word32 nSz = 0;
+        const byte* e = NULL;
+        word32 eSz = 0;
 
-    if (ret == WS_SUCCESS) {
-        ret = GetMpint(&nSz, &n, pk->publicKey, pk->publicKeySz, &i);
-    }
+        if (ret == WS_SUCCESS) {
+            ret = GetMpint(&eSz, &e, pk->publicKey, pk->publicKeySz, &i);
+        }
 
-    if (ret == WS_SUCCESS) {
-        ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, key_ptr);
-        if (ret != 0) {
-            WLOG(WS_LOG_DEBUG, "Could not decode public key");
-            ret = WS_CRYPTO_FAILED;
+        if (ret == WS_SUCCESS) {
+            ret = GetMpint(&nSz, &n, pk->publicKey, pk->publicKeySz, &i);
+        }
+
+        if (ret == WS_SUCCESS) {
+            ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, key);
+            if (ret != 0) {
+                WLOG(WS_LOG_DEBUG, "Could not decode public key");
+                ret = WS_CRYPTO_FAILED;
+            }
         }
     }
 
@@ -5615,55 +6340,22 @@ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
     }
 
     if (ret == WS_SUCCESS) {
-        checkDigestSz = wc_RsaSSL_Verify(sig, sigSz, checkDigest,
-                                         MAX_ENCODED_SIG_SZ, key_ptr);
-        if (checkDigestSz <= 0) {
-            WLOG(WS_LOG_DEBUG, "Could not verify signature");
-            ret = WS_CRYPTO_FAILED;
-        }
+        encDigestSz = wc_EncodeSignature(encDigest,
+                digest, digestSz, wc_HashGetOID(hashId));
+        ret = wolfSSH_RsaVerify(sig, sigSz, encDigest, encDigestSz,
+                key, ssh->ctx->heap, "DoUserAuthRequestRsa");
     }
 
-    if (ret == WS_SUCCESS) {
-        word32 encDigestSz;
-        volatile int compare;
-        volatile int sizeCompare;
+    wc_FreeRsaKey(key);
 #ifdef WOLFSSH_SMALL_STACK
-        encDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, ssh->ctx->heap,
-                DYNTYPE_BUFFER);
-        if (encDigest == NULL)
-            ret = WS_MEMORY_E;
-
-        if (ret == WS_SUCCESS)
-#else
-        byte s_encDigest[MAX_ENCODED_SIG_SZ];
-        encDigest = s_encDigest;
-#endif
-        {
-            WMEMSET(encDigest, 0, MAX_ENCODED_SIG_SZ);
-            encDigestSz = wc_EncodeSignature(encDigest, digest,
-                                         wc_HashGetDigestSize(enmhashId),
-                                         wc_HashGetOID(enmhashId));
-
-            compare = ConstantCompare(encDigest, checkDigest,
-                                  encDigestSz);
-            sizeCompare = encDigestSz != (word32)checkDigestSz;
-
-            if ((compare == 0) && (sizeCompare == 0))
-                ret = WS_SUCCESS;
-            else
-                ret = WS_RSA_E;
-        }
-    }
-    if (key_ptr != NULL) {
-        wc_FreeRsaKey(key_ptr);
-        WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY);
+    if (key) {
+        WFREE(key, ssh->ctx->heap, DYNTYPE_PUBKEY);
     }
-#ifdef WOLFSSH_SMALL_STACK
-    if (checkDigest)
-        WFREE(checkDigest, ssh->ctx->heap, DYNTYPE_BUFFER);
-    if (encDigest)
+    if (encDigest) {
         WFREE(encDigest, ssh->ctx->heap, DYNTYPE_BUFFER);
+    }
 #endif
+
     WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestRsa(), ret = %d", ret);
     return ret;
 }
@@ -5675,21 +6367,19 @@ static int DoUserAuthRequestRsaCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
                                     enum wc_HashType hashId, byte* digest,
                                     word32 digestSz)
 {
-    enum wc_HashType enmhashId = hashId;
-    byte *checkDigest = NULL;
-    byte *encDigest = NULL;
-    int checkDigestSz;
     const byte* publicKeyType;
-    word32 publicKeyTypeSz = 0;
     const byte* sig;
-    word32 sigSz = 0;
+    word32 publicKeyTypeSz = 0;
+    word32 sigSz;
+    word32 encDigestSz;
     word32 i = 0;
     int ret = WS_SUCCESS;
-    RsaKey *key_ptr = NULL;
-
-#ifndef WOLFSSH_SMALL_STACK
-    byte s_checkDigest[MAX_ENCODED_SIG_SZ];
-    RsaKey s_key;
+#ifdef WOLFSSH_SMALL_STACK
+    byte* encDigest = NULL;
+    RsaKey* key = NULL;
+#else
+    byte encDigest[MAX_ENCODED_SIG_SZ];
+    RsaKey key[1];
 #endif
 
     WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestRsaCert()");
@@ -5700,27 +6390,28 @@ static int DoUserAuthRequestRsaCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
         ret = WS_BAD_ARGUMENT;
     }
 
-    if (ret == WS_SUCCESS) {
 #ifdef WOLFSSH_SMALL_STACK
-    checkDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, ssh->ctx->heap,
-            DYNTYPE_BUFFER);
-    key_ptr = (RsaKey*)WMALLOC(sizeof(RsaKey), ssh->ctx->heap,
-            DYNTYPE_PUBKEY);
-    if (checkDigest == NULL || key_ptr == NULL)
-        ret = WS_MEMORY_E;
-#else
-    checkDigest = s_checkDigest;
-    key_ptr = &s_key;
-#endif
+    if (ret == WS_SUCCESS) {
+        encDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ,
+                ssh->ctx->heap, DYNTYPE_BUFFER);
+        if (encDigest == NULL)
+            ret = WS_MEMORY_E;
     }
+    if (ret == WS_SUCCESS) {
+        key = (RsaKey*)WMALLOC(sizeof(RsaKey), ssh->ctx->heap, DYNTYPE_PUBKEY);
+        if (key == NULL)
+            ret = WS_MEMORY_E;
+    }
+#endif
 
     if (ret == WS_SUCCESS) {
-        ret = wc_InitRsaKey(key_ptr, ssh->ctx->heap);
+        ret = wc_InitRsaKey(key, ssh->ctx->heap);
         if (ret == 0) {
             ret = WS_SUCCESS;
         }
     }
 
+    /* Load up the key. */
     if (ret == WS_SUCCESS) {
         byte*  pub = NULL;
         word32 pubSz;
@@ -5743,8 +6434,12 @@ static int DoUserAuthRequestRsaCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
         }
 
         if (ret == 0) {
-            word32 idx = 0;
-            ret = wc_RsaPublicKeyDecode(pub, &idx, key_ptr, pubSz);
+            i = 0;
+            ret = wc_RsaPublicKeyDecode(pub, &i, key, pubSz);
+            if (ret != 0) {
+                WLOG(WS_LOG_DEBUG, "Could not decode public key");
+                ret = WS_CRYPTO_FAILED;
+            }
         }
 
         if (pub != NULL)
@@ -5752,13 +6447,8 @@ static int DoUserAuthRequestRsaCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
         wc_FreeDecodedCert(&cert);
     }
 
-    if (ret != 0) {
-        WLOG(WS_LOG_DEBUG, "Could not decode public key");
-        ret = WS_CRYPTO_FAILED;
-    }
-
     if (ret == WS_SUCCESS) {
-        int keySz = wc_RsaEncryptSize(key_ptr) * 8;
+        int keySz = wc_RsaEncryptSize(key) * 8;
         if (keySz < 2048) {
             WLOG(WS_LOG_DEBUG, "Key size too small (%d)", keySz);
             ret = WS_CERT_KEY_SIZE_E;
@@ -5767,69 +6457,42 @@ static int DoUserAuthRequestRsaCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
 
     if (ret == WS_SUCCESS) {
         i = 0;
-        /* First check that the signature's public key type matches the one
-         * we are expecting. */
-        ret = GetSize(&publicKeyTypeSz, pk->signature, pk->signatureSz, &i);
+        /* Check that the signature's pubkey type matches the expected one. */
+        ret = GetStringRef(&publicKeyTypeSz, &publicKeyType,
+                pk->signature, pk->signatureSz, &i);
     }
 
     if (ret == WS_SUCCESS) {
-        publicKeyType = pk->signature + i;
-        i += publicKeyTypeSz;
-        WOLFSSH_UNUSED(publicKeyType);
-    }
-
-    if (ret == WS_SUCCESS)
-        ret = GetSize(&sigSz, pk->signature, pk->signatureSz, &i);
+        if (publicKeyTypeSz != pk->publicKeyTypeSz &&
+            WMEMCMP(publicKeyType, pk->publicKeyType, publicKeyTypeSz) != 0) {
 
-    if (ret == WS_SUCCESS) {
-        sig = pk->signature + i;
-        checkDigestSz = wc_RsaSSL_Verify(sig, sigSz, checkDigest,
-                                         MAX_ENCODED_SIG_SZ, key_ptr);
-        if (checkDigestSz <= 0) {
-            WLOG(WS_LOG_DEBUG, "Could not verify signature");
-            ret = WS_CRYPTO_FAILED;
+            WLOG(WS_LOG_DEBUG,
+                 "Signature's type does not match public key type");
+            ret = WS_INVALID_ALGO_ID;
         }
     }
 
     if (ret == WS_SUCCESS) {
-        word32 encDigestSz;
-        volatile int compare;
-        volatile int sizeCompare;
-#ifdef WOLFSSH_SMALL_STACK
-        encDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, ssh->ctx->heap,
-                DYNTYPE_BUFFER);
-        if (encDigest == NULL)
-            ret = WS_MEMORY_E;
-
-        if (ret == WS_SUCCESS)
-#else
-        byte s_encDigest[MAX_ENCODED_SIG_SZ];
-        encDigest = s_encDigest;
-#endif
-        {
-            encDigestSz = wc_EncodeSignature(encDigest, digest,
-                                         wc_HashGetDigestSize(enmhashId),
-                                         wc_HashGetOID(enmhashId));
-
-            compare = ConstantCompare(encDigest, checkDigest,
-                                  encDigestSz);
-            sizeCompare = encDigestSz != (word32)checkDigestSz;
+        ret = GetMpint(&sigSz, &sig, pk->signature, pk->signatureSz, &i);
+    }
 
-            if ((compare == 0) && (sizeCompare == 0))
-                ret = WS_SUCCESS;
-            else
-                ret = WS_RSA_E;
-        }
+    if (ret == WS_SUCCESS) {
+        encDigestSz = wc_EncodeSignature(encDigest,
+                digest, digestSz, wc_HashGetOID(hashId));
+        ret = wolfSSH_RsaVerify(sig, sigSz, encDigest, encDigestSz,
+                key, ssh->ctx->heap, "DoUserAuthRequestRsaCert");
     }
 
-    if (key_ptr)
-        wc_FreeRsaKey(key_ptr);
+    wc_FreeRsaKey(key);
 #ifdef WOLFSSH_SMALL_STACK
-    if (key_ptr)
-        WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY);
-    if (checkDigest)
-        WFREE(checkDigest, ssh->ctx->heap, DYNTYPE_BUFFER);
+    if (key) {
+        WFREE(key, ssh->ctx->heap, DYNTYPE_PUBKEY);
+    }
+    if (encDigest) {
+        WFREE(encDigest, ssh->ctx->heap, DYNTYPE_BUFFER);
+    }
 #endif
+
     WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestRsaCert(), ret = %d", ret);
     return ret;
 }
@@ -6162,6 +6825,154 @@ static int DoUserAuthRequestEccCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk,
 #endif /* ! WOLFSSH_NO_ECDSA */
 
 
+#ifndef WOLFSSH_NO_ED25519
+static int DoUserAuthRequestEd25519(WOLFSSH* ssh,
+        WS_UserAuthData_PublicKey* pk, WS_UserAuthData* authData)
+{
+    const byte* publicKeyType;
+    byte temp[32];
+    word32 publicKeyTypeSz = 0;
+    word32 sz, qSz;
+    word32 i = 0;
+    int ret = WS_SUCCESS;
+    ed25519_key *key_ptr = NULL;
+#ifndef WOLFSSH_SMALL_STACK
+    ed25519_key s_key;
+#endif
+
+    WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestEd25519()");
+
+    if (ssh == NULL || ssh->ctx == NULL || pk == NULL || authData == NULL) {
+        ret = WS_BAD_ARGUMENT;
+    }
+
+    if (ret == WS_SUCCESS) {
+#ifdef WOLFSSH_SMALL_STACK
+    key_ptr = (ed25519_key*)WMALLOC(sizeof(ed25519_key), ssh->ctx->heap,
+            DYNTYPE_PUBKEY);
+    if (key_ptr == NULL)
+        ret = WS_MEMORY_E;
+#else
+    key_ptr = &s_key;
+#endif
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = wc_ed25519_init_ex(key_ptr, ssh->ctx->heap, INVALID_DEVID);
+        if (ret == 0) {
+            ret = WS_SUCCESS;
+        }
+    }
+
+    /* First check that the public key's type matches the one we are
+     * expecting. */
+    if (ret == WS_SUCCESS)
+        ret = GetSize(&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 = GetSize(&qSz, pk->publicKey, pk->publicKeySz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = wc_ed25519_import_public(pk->publicKey + i, qSz, key_ptr);
+    }
+
+    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 = GetSize(&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 = GetSize(&sz, pk->signature, pk->signatureSz, &i);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = wc_ed25519_verify_msg_init(pk->signature + i, sz,
+                key_ptr, (byte)Ed25519, NULL, 0);
+    }
+
+    if (ret == WS_SUCCESS) {
+        c32toa(ssh->sessionIdSz, temp);
+        ret = wc_ed25519_verify_msg_update(temp, UINT32_SZ, key_ptr);
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = wc_ed25519_verify_msg_update(ssh->sessionId, ssh->sessionIdSz,
+                key_ptr);
+    }
+
+    if(ret == WS_SUCCESS) {
+        temp[0] = MSGID_USERAUTH_REQUEST;
+        ret = wc_ed25519_verify_msg_update(temp, MSG_ID_SZ, key_ptr);
+    }
+
+    /* The rest of the fields in the signature are already
+    * in the buffer. Just need to account for the sizes. */
+    if(ret == WS_SUCCESS) {
+        ret = wc_ed25519_verify_msg_update(pk->dataToSign,
+                                    authData->usernameSz +
+                                    authData->serviceNameSz +
+                                    authData->authNameSz + BOOLEAN_SZ +
+                                    pk->publicKeyTypeSz + pk->publicKeySz +
+                                    (UINT32_SZ * 5), key_ptr);
+    }
+
+    if(ret == WS_SUCCESS) {
+        int status = 0;
+        ret = wc_ed25519_verify_msg_final(pk->signature + i, sz,
+                &status, key_ptr);
+        if (ret != 0) {
+            WLOG(WS_LOG_DEBUG, "Could not verify signature");
+            ret = WS_CRYPTO_FAILED;
+        }
+        else
+            ret = status ? WS_SUCCESS : WS_ED25519_E;
+    }
+
+    if (key_ptr) {
+        wc_ed25519_free(key_ptr);
+#ifdef WOLFSSH_SMALL_STACK
+        WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY);
+#endif
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEd25519(), ret = %d", ret);
+    return ret;
+}
+#endif /* !WOLFSSH_NO_ED25519 */
+
 #if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
 /* Utility for DoUserAuthRequest() */
 static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
@@ -6261,22 +7072,29 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
 
     #ifdef WOLFSSH_CERTS
     if (ret == WS_SUCCESS && !authFailure) {
-        if (pkTypeId == ID_X509V3_SSH_RSA ||
-            pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP256 ||
-            pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384 ||
-            pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP521) {
-            byte  *cert   = NULL;
+        if (pkTypeId == ID_X509V3_SSH_RSA
+                || pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP256
+                || pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384
+                || pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP521) {
+            byte *cert = NULL;
             word32 certSz = 0;
 
-            ret = ParseAndVerifyCert(ssh, (byte*)pubKeyBlob, pubKeyBlobSz,
-                    &cert, &certSz);
-            if (ret == WS_SUCCESS) {
-                authData->sf.publicKey.publicKey = cert;
+            if (hasSig) {
+                ret = ParseCertChainVerify(ssh,
+                        (byte*)pubKeyBlob, pubKeyBlobSz, &cert, &certSz);
+            }
+            else {
+                ret = ParseLeafCert((byte*)pubKeyBlob, pubKeyBlobSz,
+                        &cert, &certSz);
+            }
+            if (ret == WS_SUCCESS) {
+                authData->sf.publicKey.publicKey = cert;
                 authData->sf.publicKey.publicKeySz = certSz;
                 authData->sf.publicKey.isCert = 1;
             }
             else {
-                WLOG(WS_LOG_DEBUG, "DUARPK: client cert not verified");
+                WLOG(WS_LOG_DEBUG, "DUARPK: cannot parse client cert chain");
+                ret = WS_SUCCESS;
                 authFailure = 1;
             }
         }
@@ -6289,6 +7107,7 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
             ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY,
                                        authData, ssh->userAuthCtx);
             WLOG(WS_LOG_DEBUG, "DUARPK: callback result = %d", ret);
+
         #ifdef DEBUG_WOLFSSH
             switch (ret) {
                 case WOLFSSH_USERAUTH_SUCCESS:
@@ -6319,19 +7138,28 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
                     WLOG(WS_LOG_DEBUG, "DUARPK: user auth partial success");
                     break;
 
+                case WOLFSSH_USERAUTH_WOULD_BLOCK:
+                    WLOG(WS_LOG_DEBUG, "DUARPK: userauth callback would block");
+                    break;
+
                 default:
                     WLOG(WS_LOG_DEBUG,
                         "Unexpected return value from Auth callback");
             }
         #endif
 
-            if (ret == WOLFSSH_USERAUTH_PARTIAL_SUCCESS) {
-                partialSuccess = 1;
+            if (ret == WOLFSSH_USERAUTH_WOULD_BLOCK) {
+                ret = WS_AUTH_PENDING;
             }
-            else if (ret != WOLFSSH_USERAUTH_SUCCESS) {
-                authFailure = 1;
+            else {
+                if (ret == WOLFSSH_USERAUTH_PARTIAL_SUCCESS) {
+                    partialSuccess = 1;
+                }
+                else if (ret != WOLFSSH_USERAUTH_SUCCESS) {
+                    authFailure = 1;
+                }
+                ret = WS_SUCCESS;
             }
-            ret = WS_SUCCESS;
         }
         else {
             WLOG(WS_LOG_DEBUG, "DUARPK: no userauth callback set");
@@ -6346,98 +7174,107 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
                     pubKeyAlgo, pubKeyAlgoSz, pubKeyBlob, pubKeyBlobSz);
         }
         else {
-            wc_HashAlg hash;
-            byte digest[WC_MAX_DIGEST_SIZE];
-            word32 digestSz = 0;
-            enum wc_HashType hashId = WC_HASH_TYPE_SHA;
+            if (pkTypeId == ID_ED25519) {
+#ifndef WOLFSSH_NO_ED25519
+                ret = DoUserAuthRequestEd25519(ssh,
+                        &authData->sf.publicKey, authData);
+#else
+                ret = WS_INVALID_ALGO_ID;
+#endif
+            } else {
+                wc_HashAlg hash;
+                byte digest[WC_MAX_DIGEST_SIZE];
+                word32 digestSz = 0;
+                enum wc_HashType hashId = WC_HASH_TYPE_SHA;
 
-            if (ret == WS_SUCCESS) {
-                hashId = HashForId(pkTypeId);
-                WMEMSET(digest, 0, sizeof(digest));
-                ret = wc_HashGetDigestSize(hashId);
-                if (ret > 0) {
-                    digestSz = ret;
-                    ret = 0;
+                if (ret == WS_SUCCESS) {
+                    hashId = HashForId(pkTypeId);
+                    WMEMSET(digest, 0, sizeof(digest));
+                    ret = wc_HashGetDigestSize(hashId);
+                    if (ret > 0) {
+                        digestSz = ret;
+                        ret = 0;
+                    }
                 }
-            }
 
-            if (ret == 0)
-                ret = wc_HashInit(&hash, hashId);
+                if (ret == 0)
+                    ret = wc_HashInit(&hash, hashId);
 
-            if (ret == 0) {
-                c32toa(ssh->sessionIdSz, digest);
-                ret = HashUpdate(&hash, hashId, digest, UINT32_SZ);
-            }
+                if (ret == 0) {
+                    c32toa(ssh->sessionIdSz, digest);
+                    ret = HashUpdate(&hash, hashId, digest, UINT32_SZ);
+                }
 
-            if (ret == 0)
-                ret = HashUpdate(&hash, hashId,
-                                    ssh->sessionId, ssh->sessionIdSz);
+                if (ret == 0)
+                    ret = HashUpdate(&hash, hashId,
+                                        ssh->sessionId, ssh->sessionIdSz);
 
-            if (ret == 0) {
-                digest[0] = MSGID_USERAUTH_REQUEST;
-                ret = HashUpdate(&hash, hashId, digest, MSG_ID_SZ);
-            }
+                if (ret == 0) {
+                    digest[0] = MSGID_USERAUTH_REQUEST;
+                    ret = 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, which total
-             * the length of the buffer minus the signature and size of
-             * signature. */
-            if (ret == 0) {
-                ret = HashUpdate(&hash, hashId,
-                        authData->sf.publicKey.dataToSign,
-                        len - sigSz - LENGTH_SZ);
-            }
-            if (ret == 0) {
-                ret = wc_HashFinal(&hash, hashId, digest);
+                /* The rest of the fields in the signature are already
+                 * in the buffer. Just need to account for the sizes, which
+                 * total the length of the buffer minus the signature and
+                 * size of signature. */
+                if (ret == 0) {
+                    ret = HashUpdate(&hash, hashId,
+                            authData->sf.publicKey.dataToSign,
+                            len - sigSz - LENGTH_SZ);
+                }
+                if (ret == 0) {
+                    ret = wc_HashFinal(&hash, hashId, digest);
 
-                if (ret != 0)
-                    ret = WS_CRYPTO_FAILED;
-                else
-                    ret = WS_SUCCESS;
-            }
-            wc_HashFree(&hash, hashId);
+                    if (ret != 0)
+                        ret = WS_CRYPTO_FAILED;
+                    else
+                        ret = WS_SUCCESS;
+                }
+                wc_HashFree(&hash, hashId);
 
-            if (ret == WS_SUCCESS) {
-                WLOG(WS_LOG_DEBUG, "Verify user signature type: %s",
-                        IdToName(pkTypeId));
-
-                switch (pkTypeId) {
-                    #ifndef WOLFSSH_NO_RSA
-                    case ID_SSH_RSA:
-                    case ID_RSA_SHA2_256:
-                    case ID_RSA_SHA2_512:
-                        ret = DoUserAuthRequestRsa(ssh,
-                                &authData->sf.publicKey,
-                                hashId, digest, digestSz);
-                        break;
-                    #ifdef WOLFSSH_CERTS
-                    case ID_X509V3_SSH_RSA:
-                        ret = DoUserAuthRequestRsaCert(ssh,
-                                &authData->sf.publicKey,
-                                hashId, digest, digestSz);
-                        break;
-                    #endif
-                    #endif
-                    #ifndef WOLFSSH_NO_ECDSA
-                    case ID_ECDSA_SHA2_NISTP256:
-                    case ID_ECDSA_SHA2_NISTP384:
-                    case ID_ECDSA_SHA2_NISTP521:
-                        ret = DoUserAuthRequestEcc(ssh,
-                                &authData->sf.publicKey,
-                                hashId, digest, digestSz);
-                        break;
-                    #ifdef WOLFSSH_CERTS
-                    case ID_X509V3_ECDSA_SHA2_NISTP256:
-                    case ID_X509V3_ECDSA_SHA2_NISTP384:
-                    case ID_X509V3_ECDSA_SHA2_NISTP521:
-                        ret = DoUserAuthRequestEccCert(ssh,
-                                &authData->sf.publicKey,
-                                hashId, digest, digestSz);
-                        break;
-                    #endif
-                    #endif
-                    default:
-                        ret = WS_INVALID_ALGO_ID;
+                if (ret == WS_SUCCESS) {
+                    WLOG(WS_LOG_DEBUG, "Verify user signature type: %s",
+                            IdToName(pkTypeId));
+
+                    switch (pkTypeId) {
+                        #ifndef WOLFSSH_NO_RSA
+                        case ID_SSH_RSA:
+                        case ID_RSA_SHA2_256:
+                        case ID_RSA_SHA2_512:
+                            ret = DoUserAuthRequestRsa(ssh,
+                                    &authData->sf.publicKey,
+                                    hashId, digest, digestSz);
+                            break;
+                        #ifdef WOLFSSH_CERTS
+                        case ID_X509V3_SSH_RSA:
+                            ret = DoUserAuthRequestRsaCert(ssh,
+                                    &authData->sf.publicKey,
+                                    hashId, digest, digestSz);
+                            break;
+                        #endif
+                        #endif
+                        #ifndef WOLFSSH_NO_ECDSA
+                        case ID_ECDSA_SHA2_NISTP256:
+                        case ID_ECDSA_SHA2_NISTP384:
+                        case ID_ECDSA_SHA2_NISTP521:
+                            ret = DoUserAuthRequestEcc(ssh,
+                                    &authData->sf.publicKey,
+                                    hashId, digest, digestSz);
+                            break;
+                        #ifdef WOLFSSH_CERTS
+                        case ID_X509V3_ECDSA_SHA2_NISTP256:
+                        case ID_X509V3_ECDSA_SHA2_NISTP384:
+                        case ID_X509V3_ECDSA_SHA2_NISTP521:
+                            ret = DoUserAuthRequestEccCert(ssh,
+                                    &authData->sf.publicKey,
+                                    hashId, digest, digestSz);
+                            break;
+                        #endif
+                        #endif
+                        default:
+                            ret = WS_INVALID_ALGO_ID;
+                    }
                 }
             }
 
@@ -6458,24 +7295,19 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData,
                         WLOG(WS_LOG_DEBUG, "DUARPK: user overriding success");
                         authFailure = 1;
                     }
-                    else {
-                        ssh->clientState = CLIENT_USERAUTH_DONE;
-                    }
                 }
-                else {
+                if (!authFailure && !partialSuccess) {
                     ssh->clientState = CLIENT_USERAUTH_DONE;
                 }
             }
         }
     }
 
-    if (ret == WS_SUCCESS) {
-        if (authFailure) {
-            ret = SendUserAuthFailure(ssh, 0);
-        }
-        else if (partialSuccess && hasSig) {
-            ret = SendUserAuthFailure(ssh, 1);
-        }
+    if (authFailure) {
+        ret = SendUserAuthFailure(ssh, 0);
+    }
+    else if (partialSuccess && hasSig) {
+        ret = SendUserAuthFailure(ssh, 1);
     }
 
     WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPublicKey(), ret = %d", ret);
@@ -6923,6 +7755,9 @@ static int DoChannelOpen(WOLFSSH* ssh,
         else {
             ChannelUpdatePeer(newChannel, peerChannelId,
                           peerInitialWindowSz, peerMaxPacketSz);
+            if (ssh->ctx->channelOpenCb) {
+                ret = ssh->ctx->channelOpenCb(newChannel, ssh->channelOpenCtx);
+            }
             if (ssh->channelListSz == 0)
                 ssh->defaultPeerChannelId = peerChannelId;
         #ifdef WOLFSSH_FWD
@@ -6953,16 +7788,18 @@ static int DoChannelOpen(WOLFSSH* ssh,
         const char *description = NULL;
 
         if (fail_reason == OPEN_ADMINISTRATIVELY_PROHIBITED)
-            description = "Each session cannot have more than one channel open.";
+            description = "Administratively prohibited.";
         else if (fail_reason == OPEN_UNKNOWN_CHANNEL_TYPE)
             description = "Channel type not supported.";
         else if (fail_reason == OPEN_RESOURCE_SHORTAGE)
             description = "Not enough resources.";
 
-        if (description != NULL)
-            ret = SendChannelOpenFail(ssh, peerChannelId, fail_reason, description, "en");
+        if (description != NULL) {
+            ret = SendChannelOpenFail(ssh, peerChannelId,
+                    fail_reason, description, "en");
+        }
         else
-            ret = SendRequestSuccess(ssh, 0);
+            ret = SendRequestSuccess(ssh, 0); /* XXX Is this right? */
     }
 
 #ifdef WOLFSSH_FWD
@@ -7018,6 +7855,12 @@ static int DoChannelOpenConf(WOLFSSH* ssh,
         ret = ChannelUpdatePeer(channel, peerChannelId,
                             peerInitialWindowSz, peerMaxPacketSz);
 
+    if (ret == WS_SUCCESS) {
+        if (ssh->ctx->channelOpenConfCb != NULL) {
+            ret = ssh->ctx->channelOpenConfCb(channel, ssh->channelOpenCtx);
+        }
+    }
+
     if (ret == WS_SUCCESS) {
         ssh->serverState = SERVER_CHANNEL_OPEN_DONE;
         ssh->defaultPeerChannelId = peerChannelId;
@@ -7031,6 +7874,7 @@ static int DoChannelOpenConf(WOLFSSH* ssh,
 static int DoChannelOpenFail(WOLFSSH* ssh,
                              byte* buf, word32 len, word32* idx)
 {
+    WOLFSSH_CHANNEL* channel = NULL;
     char desc[80];
     word32 begin, channelId, reasonId, descSz, langSz;
     int ret = WS_SUCCESS;
@@ -7064,6 +7908,19 @@ static int DoChannelOpenFail(WOLFSSH* ssh,
             WLOG(WS_LOG_INFO, "description: %s", desc);
         }
 
+        if (ssh->ctx->channelOpenFailCb != NULL) {
+            channel = ChannelFind(ssh, channelId, WS_CHANNEL_ID_SELF);
+
+            if (channel != NULL) {
+                ret = ssh->ctx->channelOpenFailCb(channel, ssh->channelOpenCtx);
+            }
+            else {
+                ret = WS_INVALID_CHANID;
+            }
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
         ret = ChannelRemove(ssh, channelId, WS_CHANNEL_ID_SELF);
     }
 
@@ -7095,6 +7952,12 @@ static int DoChannelEof(WOLFSSH* ssh,
             ret = WS_INVALID_CHANID;
     }
 
+    if (ret == WS_SUCCESS) {
+        if (ssh->ctx->channelEofCb) {
+            ssh->ctx->channelEofCb(channel, ssh->channelEofCtx);
+        }
+    }
+
     if (ret == WS_SUCCESS) {
         channel->eofRxd = 1;
         if (!channel->eofTxd) {
@@ -7128,6 +7991,12 @@ static int DoChannelClose(WOLFSSH* ssh,
             ret = WS_INVALID_CHANID;
     }
 
+    if (ret == WS_SUCCESS) {
+        if (ssh->ctx->channelCloseCb) {
+            ssh->ctx->channelCloseCb(channel, ssh->channelCloseCtx);
+        }
+    }
+
     if (ret == WS_SUCCESS) {
         if (!channel->closeTxd) {
             ret = SendChannelClose(ssh, channel->peerChannel);
@@ -7345,7 +8214,8 @@ int wolfSSH_DoModes(const byte* modes, word32 modesSz, int fd)
                 TTY_SET_FLAG(term.c_lflag, ONLCR, arg);
                 break;
             case WOLFSSH_OCRNL:
-                TTY_SET_FLAG(term.c_lflag, OCRNL, arg);
+                /* keep as default, adjusting removes echo over shell */
+                /* TTY_SET_FLAG(term.c_lflag, OCRNL, arg); */
                 break;
             case WOLFSSH_ONOCR:
                 TTY_SET_FLAG(term.c_lflag, ONOCR, arg);
@@ -7400,7 +8270,7 @@ static int DoChannelRequest(WOLFSSH* ssh,
     word32 typeSz;
     char type[32];
     byte wantReply;
-    int ret;
+    int ret, rej = 0;
 
     WLOG(WS_LOG_DEBUG, "Entering DoChannelRequest()");
 
@@ -7429,8 +8299,53 @@ static int DoChannelRequest(WOLFSSH* ssh,
         WLOG(WS_LOG_DEBUG, "  type = %s", type);
         WLOG(WS_LOG_DEBUG, "  wantReply = %u", wantReply);
 
-#ifdef WOLFSSH_TERM
-        if (WSTRNCMP(type, "pty-req", typeSz) == 0) {
+        if (WSTRNCMP(type, "env", typeSz) == 0) {
+            char name[WOLFSSH_MAX_NAMESZ];
+            word32 nameSz;
+            char value[32];
+            word32 valueSz;
+
+            name[0] = 0;
+            value[0] = 0;
+            nameSz = (word32)sizeof(name);
+            valueSz = (word32)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);
+        }
+        else if (WSTRNCMP(type, "shell", typeSz) == 0) {
+            channel->sessionType = WOLFSSH_SESSION_SHELL;
+            if (ssh->ctx->channelReqShellCb) {
+                rej = ssh->ctx->channelReqShellCb(channel, ssh->channelReqCtx);
+            }
+            ssh->clientState = CLIENT_DONE;
+        }
+        else if (WSTRNCMP(type, "exec", typeSz) == 0) {
+            ret = GetStringAlloc(ssh->ctx->heap, &channel->command,
+                    buf, len, &begin);
+            channel->sessionType = WOLFSSH_SESSION_EXEC;
+            if (ssh->ctx->channelReqExecCb) {
+                rej = ssh->ctx->channelReqExecCb(channel, ssh->channelReqCtx);
+            }
+            ssh->clientState = CLIENT_DONE;
+
+            WLOG(WS_LOG_DEBUG, "  command = %s", channel->command);
+        }
+        else if (WSTRNCMP(type, "subsystem", typeSz) == 0) {
+            ret = GetStringAlloc(ssh->ctx->heap, &channel->command,
+                    buf, len, &begin);
+            channel->sessionType = WOLFSSH_SESSION_SUBSYSTEM;
+            if (ssh->ctx->channelReqSubsysCb) {
+                rej = ssh->ctx->channelReqSubsysCb(channel, ssh->channelReqCtx);
+            }
+            ssh->clientState = CLIENT_DONE;
+
+            WLOG(WS_LOG_DEBUG, "  subsystem = %s", channel->command);
+        }
+        #ifdef WOLFSSH_TERM
+        else if (WSTRNCMP(type, "pty-req", typeSz) == 0) {
             char term[32];
             const byte* modes;
             word32 termSz, modesSz = 0;
@@ -7449,7 +8364,8 @@ static int DoChannelRequest(WOLFSSH* ssh,
             if (ret == WS_SUCCESS)
                 ret = GetStringRef(&modesSz, &modes, buf, len, &begin);
             if (ret == WS_SUCCESS) {
-                ssh->modes = (byte*)WMALLOC(modesSz, ssh->ctx->heap, 0);
+                ssh->modes = (byte*)WMALLOC(modesSz,
+                        ssh->ctx->heap, DYNTYPE_STRING);
                 if (ssh->modes == NULL)
                     ret = WS_MEMORY_E;
             }
@@ -7474,54 +8390,8 @@ static int DoChannelRequest(WOLFSSH* ssh,
                 }
             }
         }
-        else
-#endif /* WOLFSSH_TERM */
-        if (WSTRNCMP(type, "env", typeSz) == 0) {
-            char name[WOLFSSH_MAX_NAMESZ];
-            word32 nameSz;
-            char value[32];
-            word32 valueSz;
-
-            name[0] = 0;
-            value[0] = 0;
-            nameSz = (word32)sizeof(name);
-            valueSz = (word32)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);
-        }
-        else if (WSTRNCMP(type, "shell", typeSz) == 0) {
-            channel->sessionType = WOLFSSH_SESSION_SHELL;
-            ssh->clientState = CLIENT_DONE;
-        }
-        else if (WSTRNCMP(type, "exec", typeSz) == 0) {
-            ret = GetStringAlloc(ssh->ctx->heap, &channel->command,
-                    buf, len, &begin);
-            channel->sessionType = WOLFSSH_SESSION_EXEC;
-            ssh->clientState = CLIENT_DONE;
-
-            WLOG(WS_LOG_DEBUG, "  command = %s", channel->command);
-        }
-        else if (WSTRNCMP(type, "subsystem", typeSz) == 0) {
-            ret = GetStringAlloc(ssh->ctx->heap, &channel->command,
-                    buf, len, &begin);
-            channel->sessionType = WOLFSSH_SESSION_SUBSYSTEM;
-            ssh->clientState = CLIENT_DONE;
-
-            WLOG(WS_LOG_DEBUG, "  subsystem = %s", channel->command);
-        }
-#ifdef WOLFSSH_AGENT
-        else if (WSTRNCMP(type, "auth-agent-req@openssh.com", typeSz) == 0) {
-            WLOG(WS_LOG_AGENT, "  ssh-agent");
-            if (ssh->ctx->agentCb != NULL)
-                ssh->useAgent = 1;
-            else
-                WLOG(WS_LOG_AGENT, "Agent callback not set, not using.");
-        }
-#endif /* WOLFSSH_AGENT */
-#if defined(WOLFSSH_SHELL) && defined(WOLFSSH_TERM)
+        #endif /* WOLFSSH_TERM */
+        #if defined(WOLFSSH_SHELL) && defined(WOLFSSH_TERM)
         else if (WSTRNCMP(type, "window-change", typeSz) == 0) {
             word32 widthChar, heightRows, widthPixels, heightPixels;
 
@@ -7551,8 +8421,8 @@ static int DoChannelRequest(WOLFSSH* ssh,
                 }
             }
         }
-#endif /* WOLFSSH_SHELL && WOLFSSH_TERM */
-#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL)
+        #endif /* WOLFSSH_SHELL && WOLFSSH_TERM */
+        #if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL)
         else if (WSTRNCMP(type, "exit-status", typeSz) == 0) {
             ret = GetUint32(&ssh->exitStatus, buf, len, &begin);
             WLOG(WS_LOG_AGENT, "Got exit status %u.", ssh->exitStatus);
@@ -7585,16 +8455,30 @@ static int DoChannelRequest(WOLFSSH* ssh,
                 ret = GetString(sig, &sigSz, buf, len, &begin);
             }
         }
-#endif
+        #endif /* WOLFSSH_TERM or WOLFSSH_SHELL */
+        #ifdef WOLFSSH_AGENT
+        else if (WSTRNCMP(type, "auth-agent-req@openssh.com", typeSz) == 0) {
+            WLOG(WS_LOG_AGENT, "  ssh-agent");
+            if (ssh->ctx->agentCb != NULL)
+                ssh->useAgent = 1;
+            else
+                WLOG(WS_LOG_AGENT, "Agent callback not set, not using.");
+        }
+        #endif /* WOLFSSH_AGENT */
     }
 
-    if (ret == WS_SUCCESS)
+    if (ret == WS_SUCCESS) {
         *idx = len;
+    }
 
     if (wantReply) {
         int replyRet;
 
-        replyRet = SendChannelSuccess(ssh, channelId, (ret == WS_SUCCESS));
+        if (rej) {
+            WLOG(WS_LOG_DEBUG, "Callback rejecting channel request.");
+        }
+        replyRet = SendChannelSuccess(ssh, channelId,
+                (ret == WS_SUCCESS && !rej));
         if (replyRet != WS_SUCCESS)
             ret = replyRet;
     }
@@ -7825,6 +8709,10 @@ static int DoPacket(WOLFSSH* ssh, byte* bufferConsumed)
         return WS_OVERFLOW_E;
     }
 
+    if (!IsMessageAllowed(ssh, msg)) {
+        return WS_MSGID_NOT_ALLOWED_E;
+    }
+
     switch (msg) {
 
         case MSGID_DISCONNECT:
@@ -8020,18 +8908,21 @@ static int DoPacket(WOLFSSH* ssh, byte* bufferConsumed)
             ret = SendUnimplemented(ssh);
     }
 
-    if (payloadSz > 0) {
-        idx += payloadIdx;
-        if (idx + padSz > len) {
-            WLOG(WS_LOG_DEBUG, "Not enough data in buffer for pad.");
-            ret = WS_BUFFER_E;
+    /* if the auth is still pending, don't discard the packet data */
+    if (ret != WS_AUTH_PENDING) {
+        if (payloadSz > 0) {
+            idx += payloadIdx;
+            if (idx + padSz > len) {
+                WLOG(WS_LOG_DEBUG, "Not enough data in buffer for pad.");
+                ret = WS_BUFFER_E;
+            }
         }
-    }
 
-    idx += padSz;
-    ssh->inputBuffer.idx = idx;
-    ssh->peerSeq++;
-    *bufferConsumed = 1;
+        idx += padSz;
+        ssh->inputBuffer.idx = idx;
+        ssh->peerSeq++;
+        *bufferConsumed = 1;
+    }
 
     return ret;
 }
@@ -8804,6 +9695,24 @@ static INLINE void CopyNameList(byte* buf, word32* idx,
 }
 
 
+static INLINE void CopyNameListPlus(byte* buf, word32* idx,
+        const char* src, word32 srcSz, const char* plus, word32 plusSz)
+{
+    word32 begin = *idx;
+
+    c32toa(srcSz + plusSz, buf + begin);
+    begin += LENGTH_SZ;
+    WMEMCPY(buf + begin, src, srcSz);
+    begin += srcSz;
+    if (plusSz) {
+        WMEMCPY(buf + begin, plus, plusSz);
+    }
+    begin += plusSz;
+
+    *idx = begin;
+}
+
+
 /*
  * Iterates over a list of ID values and builds a string of names.
  *
@@ -8852,179 +9761,20 @@ static int BuildNameList(char* buf, word32 bufSz,
 }
 
 
-static const char cannedEncAlgoNames[] =
-#if !defined(WOLFSSH_NO_AES_GCM)
-    "aes256-gcm@openssh.com,"
-    "aes192-gcm@openssh.com,"
-    "aes128-gcm@openssh.com,"
-#endif
-#if !defined(WOLFSSH_NO_AES_CTR)
-    "aes256-ctr,"
-    "aes192-ctr,"
-    "aes128-ctr,"
-#endif
-#if !defined(WOLFSSH_NO_AES_CBC)
-    "aes256-cbc,"
-    "aes192-cbc,"
-    "aes128-cbc,"
-#endif
-    "";
+int SendKexInit(WOLFSSH* ssh)
+{
+    byte* output = NULL;
+    byte* payload = NULL;
+    char* keyAlgoNames = NULL;
+    const char* kexAlgoNamesPlus = NULL;
+    word32 idx = 0, payloadSz = 0,
+            kexAlgoNamesSz = 0, kexAlgoNamesPlusSz = 0,
+            keyAlgoNamesSz = 0, encAlgoNamesSz = 0,
+            macAlgoNamesSz = 0, noneNamesSz = 0;
 
-static const char cannedMacAlgoNames[] =
-#if !defined(WOLFSSH_NO_HMAC_SHA2_256)
-    "hmac-sha2-256,"
-#endif
-#if !defined(WOLFSSH_NO_HMAC_SHA1_96)
-    "hmac-sha1-96,"
-#endif
-#if !defined(WOLFSSH_NO_HMAC_SHA1)
-    "hmac-sha1,"
-#endif
-    "";
+    int ret = WS_SUCCESS;
 
-
-#ifndef WOLFSSH_NO_SSH_RSA_SHA1
-    #ifdef WOLFSSH_CERTS
-        static const char cannedKeyAlgoX509RsaNames[] = "x509v3-ssh-rsa";
-    #endif
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
-    static const char cannedKeyAlgoEcc256Names[] = "ecdsa-sha2-nistp256";
-    #ifdef WOLFSSH_CERTS
-        static const char cannedKeyAlgoX509Ecc256Names[] =
-                "x509v3-ecdsa-sha2-nistp256";
-    #endif
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
-    static const char cannedKeyAlgoEcc384Names[] = "ecdsa-sha2-nistp384";
-    #ifdef WOLFSSH_CERTS
-        static const char cannedKeyAlgoX509Ecc384Names[] =
-                "x509v3-ecdsa-sha2-nistp384";
-    #endif
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
-    static const char cannedKeyAlgoEcc521Names[] = "ecdsa-sha2-nistp521";
-    #ifdef WOLFSSH_CERTS
-        static const char cannedKeyAlgoX509Ecc521Names[] =
-                "x509v3-ecdsa-sha2-nistp521";
-    #endif
-#endif
-#ifndef WOLFSSH_NO_SSH_RSA_SHA1
-    /* Used for both the signature algorithm and the RSA key format. */
-    static const char cannedKeyAlgoSshRsaNames[] = "ssh-rsa";
-#endif
-#ifndef WOLFSSH_NO_RSA_SHA2_256
-    static const char cannedKeyAlgoRsaSha2_256Names[] = "rsa-sha2-256";
-#endif
-#ifndef WOLFSSH_NO_RSA_SHA2_512
-    static const char cannedKeyAlgoRsaSha2_512Names[] = "rsa-sha2-512";
-#endif
-
-#ifdef WOLFSSH_CERTS
-static const char cannedKeyAlgoNames[] =
-   "rsa-sha2-256,x509v3-ssh-rsa,ecdsa-sha2-nistp256,x509v3-ecdsa-sha2-nistp256";
-#else
-static const char cannedKeyAlgoNames[] = "rsa-sha2-256,ecdsa-sha2-nistp256";
-#endif
-
-#if 0
-static const char cannedKexAlgoNames[] =
-#if !defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
-    "ecdh-nistp256-kyber-512r3-sha256-d00@openquantumsafe.org,"
-#endif
-#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP521)
-    "ecdh-sha2-nistp521,"
-#endif
-#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP384)
-    "ecdh-sha2-nistp384,"
-#endif
-#if !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256)
-    "ecdh-sha2-nistp256,"
-#endif
-#if !defined(WOLFSSH_NO_DH_GEX_SHA256)
-    "diffie-hellman-group-exchange-sha256,"
-#endif
-#if !defined(WOLFSSH_NO_DH_GROUP14_SHA1)
-    "diffie-hellman-group14-sha1,"
-#endif
-#if !defined(WOLFSSH_NO_DH_GROUP1_SHA1)
-    "diffie-hellman-group1-sha1,"
-#endif
-    "";
-#endif
-
-
-static const char cannedNoneNames[] = "none";
-
-/* -1 for the null, some are -2 for the null and comma */
-static const word32 cannedEncAlgoNamesSz =
-        (word32)sizeof(cannedEncAlgoNames) - 2;
-static const word32 cannedMacAlgoNamesSz =
-        (word32)sizeof(cannedMacAlgoNames) - 2;
-#if 0
-static const word32 cannedKexAlgoNamesSz =
-        (word32)sizeof(cannedKexAlgoNames) - 2;
-#endif
-static const word32 cannedNoneNamesSz =
-        (word32)sizeof(cannedNoneNames) - 1;
-
-#define KEY_ALGO_SIZE_GUESS 28
-
-#ifndef WOLFSSH_NO_SSH_RSA_SHA1
-    static const word32 cannedKeyAlgoSshRsaNamesSz =
-            (word32)sizeof(cannedKeyAlgoSshRsaNames) - 1;
-#endif
-#ifndef WOLFSSH_NO_RSA_SHA2_256
-    static const word32 cannedKeyAlgoRsaSha2_256NamesSz =
-            (word32)sizeof(cannedKeyAlgoRsaSha2_256Names) - 1;
-#endif
-#ifndef WOLFSSH_NO_RSA_SHA2_512
-    static const word32 cannedKeyAlgoRsaSha2_512NamesSz =
-            (word32)sizeof(cannedKeyAlgoRsaSha2_512Names) - 1;
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
-    static const word32 cannedKeyAlgoEcc256NamesSz =
-            (word32)sizeof(cannedKeyAlgoEcc256Names) - 1;
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
-    static const word32 cannedKeyAlgoEcc384NamesSz =
-            (word32)sizeof(cannedKeyAlgoEcc384Names) - 1;
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
-    static const word32 cannedKeyAlgoEcc521NamesSz =
-            (word32)sizeof(cannedKeyAlgoEcc521Names) - 1;
-#endif
-#ifdef WOLFSSH_CERTS
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
-    static const word32 cannedKeyAlgoX509Ecc256NamesSz =
-            (word32)sizeof(cannedKeyAlgoX509Ecc256Names) - 1;
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
-    static const word32 cannedKeyAlgoX509Ecc384NamesSz =
-            (word32)sizeof(cannedKeyAlgoX509Ecc384Names) - 1;
-#endif
-#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
-    static const word32 cannedKeyAlgoX509Ecc521NamesSz =
-            (word32)sizeof(cannedKeyAlgoX509Ecc521Names) - 1;
-#endif
-#endif /* WOLFSSH_CERTS */
-
-static const word32 cannedKeyAlgoNamesSz =
-        (word32)sizeof(cannedKeyAlgoNames) - 1;
-
-
-int SendKexInit(WOLFSSH* ssh)
-{
-    byte* output = NULL;
-    byte* payload = NULL;
-    char* kexAlgoNames = NULL;
-    char* keyAlgoNames = NULL;
-    const byte* algo = NULL;
-    word32 algoCount = 0, idx = 0, payloadSz = 0,
-            kexAlgoNamesSz = 0, keyAlgoNamesSz = 0;
-    int ret = WS_SUCCESS;
-
-    WLOG(WS_LOG_DEBUG, "Entering SendKexInit()");
+    WLOG(WS_LOG_DEBUG, "Entering SendKexInit()");
 
     if (ssh == NULL)
         ret = WS_BAD_ARGUMENT;
@@ -9047,64 +9797,47 @@ int SendKexInit(WOLFSSH* ssh)
     }
 
     if (ret == WS_SUCCESS) {
-        byte algoList[8];
-        word32 algoListSz;
-
-        WMEMCPY(algoList, cannedKexAlgo, cannedKexAlgoSz);
-        algoListSz = cannedKexAlgoSz;
-        if (ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) {
-            algoList[cannedKexAlgoSz] = ID_EXTINFO_C;
-            algoListSz++;
-        }
-
-        kexAlgoNamesSz = BuildNameList(NULL, 0, algoList, algoListSz) + 1;
-        kexAlgoNames = (char*)WMALLOC(kexAlgoNamesSz,
-                ssh->ctx->heap, DYNTYPE_STRING);
-        if (kexAlgoNames == NULL) {
-            ret = WS_MEMORY_E;
-        }
-
-        if (ret == WS_SUCCESS) {
-            ret = BuildNameList(kexAlgoNames, kexAlgoNamesSz,
-                    algoList, algoListSz);
-            if (ret > 0) {
-                kexAlgoNamesSz = (word32)ret;
-                ret = WS_SUCCESS;
+        if (!ssh->algoListKey && ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
+            keyAlgoNamesSz = BuildNameList(NULL, 0,
+                    ssh->ctx->publicKeyAlgo,
+                    ssh->ctx->publicKeyAlgoCount)
+                + 1;
+            keyAlgoNames = (char*)WMALLOC(keyAlgoNamesSz,
+                    ssh->ctx->heap, DYNTYPE_STRING);
+            if (keyAlgoNames) {
+                ret = BuildNameList(keyAlgoNames, keyAlgoNamesSz,
+                    ssh->ctx->publicKeyAlgo,
+                    ssh->ctx->publicKeyAlgoCount);
+                if (ret > 0) {
+                    keyAlgoNamesSz = (word32)ret;
+                    ret = WS_SUCCESS;
+                }
+            }
+            else {
+                ret = WS_MEMORY_E;
             }
         }
     }
 
     if (ret == WS_SUCCESS) {
-        if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
-            algoCount = ssh->ctx->publicKeyAlgoCount;
-            algo = ssh->ctx->publicKeyAlgo;
-        }
-        else {
-            algoCount = cannedKeyAlgoClientSz;
-            algo = cannedKeyAlgoClient;
-        }
-        keyAlgoNamesSz = BuildNameList(NULL, 0, algo, algoCount) + 1;
-        keyAlgoNames = (char*)WMALLOC(keyAlgoNamesSz,
-                ssh->ctx->heap, DYNTYPE_STRING);
-        if (keyAlgoNames == NULL) {
-            ret = WS_MEMORY_E;
+        if (ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) {
+            kexAlgoNamesPlus = ",ext-info-c";
+            kexAlgoNamesPlusSz = (word32)WSTRLEN(kexAlgoNamesPlus);
         }
-    }
 
-    if (ret == WS_SUCCESS) {
-        ret = BuildNameList(keyAlgoNames, keyAlgoNamesSz, algo, algoCount);
-        if (ret > 0) {
-            keyAlgoNamesSz = (word32)ret;
-            ret = WS_SUCCESS;
+        kexAlgoNamesSz = AlgoListSz(ssh->algoListKex);
+        encAlgoNamesSz = AlgoListSz(ssh->algoListCipher);
+        if (!keyAlgoNames) {
+            keyAlgoNamesSz = AlgoListSz(ssh->algoListKey);
         }
-    }
-
-    if (ret == WS_SUCCESS) {
+        else {
+            keyAlgoNamesSz = AlgoListSz(keyAlgoNames);
+        }
+        macAlgoNamesSz = AlgoListSz(ssh->algoListMac);
+        noneNamesSz = AlgoListSz(cannedNoneNames);
         payloadSz = MSG_ID_SZ + COOKIE_SZ + (LENGTH_SZ * 11) + BOOLEAN_SZ +
-                   kexAlgoNamesSz + keyAlgoNamesSz +
-                   (cannedEncAlgoNamesSz * 2) +
-                   (cannedMacAlgoNamesSz * 2) +
-                   (cannedNoneNamesSz * 2);
+            + kexAlgoNamesSz + kexAlgoNamesPlusSz + keyAlgoNamesSz
+            + (encAlgoNamesSz * 2) + (macAlgoNamesSz * 2) + (noneNamesSz * 2);
         ret = PreparePacket(ssh, payloadSz);
     }
 
@@ -9124,14 +9857,21 @@ int SendKexInit(WOLFSSH* ssh)
 
         idx += COOKIE_SZ;
 
-        CopyNameList(output, &idx, kexAlgoNames, kexAlgoNamesSz);
-        CopyNameList(output, &idx, keyAlgoNames, keyAlgoNamesSz);
-        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);
+        CopyNameListPlus(output, &idx,
+                ssh->algoListKex, kexAlgoNamesSz,
+                kexAlgoNamesPlus, kexAlgoNamesPlusSz);
+        if (!keyAlgoNames) {
+            CopyNameList(output, &idx, ssh->algoListKey, keyAlgoNamesSz);
+        }
+        else {
+            CopyNameList(output, &idx, keyAlgoNames, keyAlgoNamesSz);
+        }
+        CopyNameList(output, &idx, ssh->algoListCipher, encAlgoNamesSz);
+        CopyNameList(output, &idx, ssh->algoListCipher, encAlgoNamesSz);
+        CopyNameList(output, &idx, ssh->algoListMac, macAlgoNamesSz);
+        CopyNameList(output, &idx, ssh->algoListMac, macAlgoNamesSz);
+        CopyNameList(output, &idx, cannedNoneNames, noneNamesSz);
+        CopyNameList(output, &idx, cannedNoneNames, noneNamesSz);
         c32toa(0, output + idx); /* Languages - Client To Server (0) */
         idx += LENGTH_SZ;
         c32toa(0, output + idx); /* Languages - Server To Client (0) */
@@ -9159,8 +9899,9 @@ int SendKexInit(WOLFSSH* ssh)
         }
     }
 
-    WFREE(kexAlgoNames, ssh->ctx->heap, DYNTYPE_STRING);
-    WFREE(keyAlgoNames, ssh->ctx->heap, DYNTYPE_STRING);
+    if (keyAlgoNames) {
+        WFREE(keyAlgoNames, ssh->ctx->heap, DYNTYPE_STRING);
+    }
 
     if (ret == WS_SUCCESS) {
         /* increase amount to be sent only if BundlePacket will be called */
@@ -9211,6 +9952,18 @@ struct wolfSSH_sigKeyBlockFull {
                 const char *primeName;
                 word32 primeNameSz;
             } ecc;
+
+#ifndef WOLFSSH_NO_ED25519
+            struct {
+                ed25519_key key;
+                word32 keyBlobSz;
+                const char *keyBlobName;
+                word32 keyBlobNameSz;
+                byte q[ED25519_PUB_KEY_SIZE+1];
+                word32 qSz;
+                byte qPad;
+            } ed;
+#endif
 #endif
         } sk;
 };
@@ -9569,6 +10322,58 @@ static int SendKexGetSigningKey(WOLFSSH* ssh,
                             sigKeyBlock_ptr->sk.ecc.qSz);
             }
             break;
+
+        #ifndef WOLFSSH_NO_ED25519
+        case ID_ED25519:
+            WLOG(WS_LOG_DEBUG, "Using Ed25519 Host key");
+
+            /* Decode the user-configured ED25519 private key. */
+            sigKeyBlock_ptr->sk.ed.qSz = sizeof(sigKeyBlock_ptr->sk.ed.q);
+
+            ret = wc_ed25519_init(&sigKeyBlock_ptr->sk.ed.key);
+
+            scratch = 0;
+            if (ret == 0)
+                ret = wc_Ed25519PrivateKeyDecode(ssh->ctx->privateKey[keyIdx].key, &scratch, &sigKeyBlock_ptr->sk.ed.key, ssh->ctx->privateKey[keyIdx].keySz);
+
+            if (ret == 0)
+                ret = wc_ed25519_export_public(&sigKeyBlock_ptr->sk.ed.key,
+                                                sigKeyBlock_ptr->sk.ed.q,
+                                                &sigKeyBlock_ptr->sk.ed.qSz );
+
+            /* Hash in the length of the public key block. */
+            if (ret == 0) {
+                sigKeyBlock_ptr->sz = (LENGTH_SZ * 2) +
+                                 sigKeyBlock_ptr->pubKeyFmtNameSz +
+                                 sigKeyBlock_ptr->sk.ed.qSz;
+                c32toa(sigKeyBlock_ptr->sz, scratchLen);
+                ret = wc_HashUpdate(hash, hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            /* Hash in the length of the key type string. */
+            if (ret == 0) {
+                c32toa(sigKeyBlock_ptr->pubKeyFmtNameSz, scratchLen);
+                ret = wc_HashUpdate(hash, hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            /* Hash in the key type string. */
+            if (ret == 0)
+                ret = wc_HashUpdate(hash, hashId,
+                                    (byte*)sigKeyBlock_ptr->pubKeyFmtName,
+                                    sigKeyBlock_ptr->pubKeyFmtNameSz);
+            /* Hash in the length of the public key. */
+            if (ret == 0) {
+                c32toa(sigKeyBlock_ptr->sk.ed.qSz, scratchLen);
+                ret = wc_HashUpdate(hash, hashId,
+                                    scratchLen, LENGTH_SZ);
+            }
+            /* Hash in the public key. */
+            if (ret == 0)
+                ret = wc_HashUpdate(hash, hashId,
+                                    sigKeyBlock_ptr->sk.ed.q,
+                                    sigKeyBlock_ptr->sk.ed.qSz);
+            break;
+        #endif
         #endif
 
             default:
@@ -9725,100 +10530,808 @@ static INLINE byte SigTypeForId(byte id)
 /*
  * wolfSSH_RsaVerify
  * sig - signature to verify
- * sigSz - signature to verify size
- * digest - encoded digest for verification
- * digestSz - encoded digest size
+ * sigSz - signature size
+ * encDigest - encoded digest for verification
+ * encDigestSz - encoded digest size
  * key - key used to sign and verify signature
  * heap - allocation heap
  * loc - calling function for logging
+ *
+ * Takes the provided digest of type digestId and converts it to an
+ * encoded digest. Then verifies the signature, comparing the output
+ * digest and compares it.
  */
-int wolfSSH_RsaVerify(byte *sig, word32 sigSz,
-        const byte* digest, word32 digestSz,
+int wolfSSH_RsaVerify(const byte *sig, word32 sigSz,
+        const byte* encDigest, word32 encDigestSz,
         RsaKey* key, void* heap, const char* loc)
 {
-    byte* check;
+    byte* checkSig = NULL;
+    int checkDigestSz;
+    word32 keySz;
     int ret = WS_SUCCESS;
+#ifdef WOLFSSH_SMALL_STACK
+    byte* checkDigest = NULL;
+#else
+    byte checkDigest[MAX_ENCODED_SIG_SZ];
+#endif
 
-    check = (byte*)WMALLOC(digestSz, heap, DYNTYPE_TEMP);
-    if (check == NULL) {
-        ret = WS_MEMORY_E;
+    keySz = (word32)wc_RsaEncryptSize(key);
+
+    if (ret == WS_SUCCESS) {
+        checkSig = (byte*)WMALLOC(keySz, heap, DYNTYPE_TEMP);
+        if (checkSig == NULL)
+            ret = WS_MEMORY_E;
     }
-    else {
-        int checkSz;
+#ifdef WOLFSSH_SMALL_STACK
+    if (ret == WS_SUCCESS) {
+        checkDigest = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap, DYNTYPE_TEMP);
+        if (checkDigest == NULL)
+            ret = WS_MEMORY_E;
+    }
+#endif
+
+    /* Normalize the peer's signature. Some SSH implementations remove
+     * leading zeros on the signatures they encode. We need to pad the
+     * front of the signature to the key size. */
+    if (ret == WS_SUCCESS) {
+        word32 offset;
 
-        checkSz = wc_RsaSSL_Verify(sig, sigSz, check, digestSz, key);
-        if (checkSz < 0
-                || (word32)checkSz != digestSz
-                || WMEMCMP(digest, check, digestSz) != 0) {
-            WLOG(WS_LOG_DEBUG, "%s: %s", loc, "Bad RSA Sign Verify");
+        if (keySz > sigSz) {
+            offset = keySz - sigSz;
+        }
+        else {
+            sigSz = keySz;
+            offset = 0;
+        }
+
+        WMEMSET(checkSig, 0, offset);
+        WMEMCPY(checkSig + offset, sig, sigSz);
+    }
+
+    if (ret == WS_SUCCESS) {
+        volatile int sizeCompare;
+        volatile int compare;
+
+        checkDigestSz = wc_RsaSSL_Verify(checkSig, keySz,
+                checkDigest, MAX_ENCODED_SIG_SZ, key);
+
+        sizeCompare = checkDigestSz > 0 && encDigestSz != (word32)checkDigestSz;
+        compare = ConstantCompare(encDigest, checkDigest, encDigestSz);
+
+        if (checkDigestSz < 0 || sizeCompare || compare) {
+            WLOG(WS_LOG_DEBUG, "%s: %s", loc, "Bad RSA Verify");
             ret = WS_RSA_E;
         }
-        ForceZero(check, digestSz);
-        WFREE(check, heap, DYNTYPE_TEMP);
     }
 
+#ifdef WOLFSSH_SMALL_STACK
+    if (checkDigest)
+        WFREE(checkDigest, heap, DYNTYPE_TEMP);
+#endif
+    if (checkSig)
+        WFREE(checkSig, heap, DYNTYPE_TEMP);
     return ret;
 }
 #endif /* WOLFSSH_NO_RSA */
 
 
-/* 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)
+/* KeyAgreeDh_server
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ */
+static int KeyAgreeDh_server(WOLFSSH* ssh, byte hashId, byte* f, word32* fSz)
+#ifndef WOLFSSH_NO_DH
 {
     int ret = WS_SUCCESS;
-    void *heap  = NULL;
-    byte *f_ptr = NULL, *sig_ptr = NULL;
-    byte scratchLen[LENGTH_SZ];
-    word32 fSz = KEX_F_SIZE;
-    word32 sigSz = KEX_SIG_SIZE;
-    byte useEcc = 0;
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-    byte useEccKyber = 0;
-    byte sharedSecretHashSz = 0;
-    byte *sharedSecretHash = NULL;
-#endif
-    byte fPad = 0;
-    byte kPad = 0;
-    word32 sigBlockSz = 0;
-    word32 payloadSz = 0;
-    byte* output = NULL;
-    word32 idx = 0;
-    word32 keyIdx = 0;
-    byte msgId = MSGID_KEXDH_REPLY;
-    enum wc_HashType hashId = WC_HASH_TYPE_NONE;
-    wc_HashAlg* hash = NULL;
-    struct wolfSSH_sigKeyBlockFull *sigKeyBlock_ptr = NULL;
-#ifndef WOLFSSH_SMALL_STACK
-    byte f_s[KEX_F_SIZE];
-    byte sig_s[KEX_SIG_SIZE];
+    byte *y_ptr = NULL;
+    const byte* primeGroup = NULL;
+    const byte* generator = NULL;
+    word32 ySz = MAX_KEX_KEY_SZ;
+    word32 primeGroupSz = 0;
+    word32 generatorSz = 0;
+    #ifdef WOLFSSH_SMALL_STACK
+    DhKey *privKey = (DhKey*)WMALLOC(sizeof(DhKey), ssh->ctx->heap,
+            DYNTYPE_PRIVKEY);
+    y_ptr = (byte*)WMALLOC(ySz, ssh->ctx->heap, DYNTYPE_PRIVKEY);
+    if (privKey == NULL || y_ptr == NULL)
+        ret = WS_MEMORY_E;
+    #else
+    DhKey privKey[1];
+    byte y_s[MAX_KEX_KEY_SZ];
+    y_ptr = y_s;
+    #endif
 
-    f_ptr = f_s;
-    sig_ptr = sig_s;
-#endif
-    WLOG(WS_LOG_DEBUG, "Entering SendKexDhReply()");
+    WLOG(WS_LOG_DEBUG, "Entering KeyAgreeDh_server()");
+    WOLFSSH_UNUSED(hashId);
 
     if (ret == WS_SUCCESS) {
-        if (ssh == NULL || ssh->ctx == NULL || ssh->handshake == NULL) {
-            ret = WS_BAD_ARGUMENT;
+        ret = GetDHPrimeGroup(ssh->handshake->kexId, &primeGroup,
+            &primeGroupSz, &generator, &generatorSz);
+
+        if (ret == WS_SUCCESS) {
+            ssh->primeGroupSz = primeGroupSz;
+            ret = wc_InitDhKey(privKey);
+        }
+        if (ret == 0)
+            ret = wc_DhSetKey(privKey, primeGroup, primeGroupSz,
+                    generator, generatorSz);
+        if (ret == 0)
+            ret = wc_DhGenerateKeyPair(privKey, ssh->rng,
+                    y_ptr, &ySz, f, fSz);
+        if (ret == 0) {
+            PRIVATE_KEY_UNLOCK();
+            ret = wc_DhAgree(privKey, ssh->k, &ssh->kSz, y_ptr, ySz,
+                    ssh->handshake->e, ssh->handshake->eSz);
+            PRIVATE_KEY_LOCK();
         }
+        ForceZero(y_ptr, ySz);
+        wc_FreeDhKey(privKey);
     }
-
-    if (ret == WS_SUCCESS) {
-        heap = ssh->ctx->heap;
+    #ifdef WOLFSSH_SMALL_STACK
+    if (y_ptr)
+        WFREE(y_ptr, ssh->ctx->heap, DYNTYPE_PRIVKEY);
+    if (privKey) {
+        WFREE(privKey, ssh->ctx->heap, DYNTYPE_PRIVKEY);
     }
-
-#ifdef WOLFSSH_SMALL_STACK
-    f_ptr = (byte*)WMALLOC(KEX_F_SIZE, heap, DYNTYPE_BUFFER);
-    sig_ptr = (byte*)WMALLOC(KEX_SIG_SIZE, heap, DYNTYPE_BUFFER);
+    #endif
+    WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeDh_server(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_DH */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(hashId);
+    WOLFSSH_UNUSED(f);
+    WOLFSSH_UNUSED(fSz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_DH */
+
+
+/* KeyAgreeEcdh_server
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ */
+static int KeyAgreeEcdh_server(WOLFSSH* ssh, byte hashId, byte* f, word32* fSz)
+#ifndef WOLFSSH_NO_ECDH
+{
+    int ret = WS_SUCCESS;
+    void* heap;
+#ifdef WOLFSSH_SMALL_STACK
+    ecc_key *pubKey = NULL, *privKey = NULL;
+    pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap,
+            DYNTYPE_PUBKEY);
+    privKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap,
+            DYNTYPE_PRIVKEY);
+    if (pubKey == NULL || privKey == NULL) {
+        ret = WS_MEMORY_E;
+    }
+#else
+    ecc_key pubKey[1];
+    ecc_key privKey[1];
+#endif
+    int primeId;
+
+    WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdh_server()");
+    WOLFSSH_UNUSED(hashId);
+
+    heap = ssh->ctx->heap;
+    primeId  = wcPrimeForId(ssh->handshake->kexId);
+    if (primeId == ECC_CURVE_INVALID)
+        ret = WS_INVALID_PRIME_CURVE;
+
+    if (ret == 0)
+        ret = wc_ecc_init_ex(pubKey, heap, INVALID_DEVID);
+    if (ret == 0)
+        ret = wc_ecc_init_ex(privKey, heap, INVALID_DEVID);
+#ifdef HAVE_WC_ECC_SET_RNG
+    if (ret == 0)
+        ret = wc_ecc_set_rng(privKey, ssh->rng);
+#endif
+
+    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) {
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_ecc_export_x963(privKey, f, fSz);
+        PRIVATE_KEY_LOCK();
+    }
+    if (ret == 0) {
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_ecc_shared_secret(privKey, pubKey,
+                                   ssh->k, &ssh->kSz);
+        PRIVATE_KEY_LOCK();
+    }
+    wc_ecc_free(privKey);
+    wc_ecc_free(pubKey);
+#ifdef WOLFSSH_SMALL_STACK
+    WFREE(pubKey, heap, DYNTYPE_PUBKEY);
+    WFREE(privKey, heap, DYNTYPE_PRIVKEY);
+    pubKey  = NULL;
+    privKey = NULL;
+#endif
+    WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdh_server(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_ECDH */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(hashId);
+    WOLFSSH_UNUSED(f);
+    WOLFSSH_UNUSED(fSz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_ECDH */
+
+
+/* KeyAgreeCurve25519_server
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ */
+static int KeyAgreeCurve25519_server(WOLFSSH* ssh, byte hashId,
+        byte* f, word32* fSz)
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+{
+    int ret = WS_SUCCESS;
+    void* heap = ssh->ctx->heap;
+#ifdef WOLFSSH_SMALL_STACK
+    curve25519_key *pubKey = NULL, *privKey = NULL;
+    pubKey = (curve25519_key*)WMALLOC(sizeof(curve25519_key),
+            heap, DYNTYPE_PUBKEY);
+    privKey = (curve25519_key*)WMALLOC(sizeof(curve25519_key),
+            heap, DYNTYPE_PRIVKEY);
+    if (pubKey == NULL || privKey == NULL) {
+        ret = WS_MEMORY_E;
+    }
+#else
+    curve25519_key pubKey[1], privKey[1];
+#endif
+
+    WLOG(WS_LOG_DEBUG, "Entering KeyAgreeCurve25519_server()");
+    WOLFSSH_UNUSED(hashId);
+
+    if (ret == 0)
+        ret = wc_curve25519_init_ex(pubKey, heap, INVALID_DEVID);
+    if (ret == 0)
+        ret = wc_curve25519_init_ex(privKey, heap, INVALID_DEVID);
+    if (ret == 0)
+        ret = wc_curve25519_check_public(ssh->handshake->e,
+                ssh->handshake->eSz, EC25519_LITTLE_ENDIAN);
+    if (ret == 0)
+        ret = wc_curve25519_import_public_ex(
+                ssh->handshake->e, ssh->handshake->eSz,
+                pubKey, EC25519_LITTLE_ENDIAN);
+
+    if (ret == 0)
+        ret = wc_curve25519_make_key(ssh->rng, CURVE25519_KEYSIZE, privKey);
+
+    if (ret == 0) {
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_curve25519_export_public_ex(privKey,
+                f, fSz, EC25519_LITTLE_ENDIAN);
+        PRIVATE_KEY_LOCK();
+    }
+
+    if (ret == 0) {
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_curve25519_shared_secret_ex(privKey, pubKey,
+                  ssh->k, &ssh->kSz, EC25519_LITTLE_ENDIAN);
+        PRIVATE_KEY_LOCK();
+    }
+    wc_curve25519_free(privKey);
+    wc_curve25519_free(pubKey);
+#ifdef WOLFSSH_SMALL_STACK
+    WFREE(pubKey, heap, DYNTYPE_PUBKEY);
+    WFREE(privKey, heap, DYNTYPE_PRIVKEY);
+    pubKey  = NULL;
+    privKey = NULL;
+#endif
+    WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeCurve25519_server(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_CURVE25519_SHA256 */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(hashId);
+    WOLFSSH_UNUSED(f);
+    WOLFSSH_UNUSED(fSz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_CURVE25519_SHA256 */
+
+
+/* KeyAgreeEcdhKyber1_server
+ * hashId - wolfCrypt hash type ID used
+ * f - peer public key
+ * fSz - peer public key size
+ *
+ * This is a hybrid KEM. In this case, I need to generate my ECC
+ * keypair, send the public one, use the private one to generate
+ * the shared secret, use the post-quantum public key to
+ * generate and encapsulate the shared secret and send the
+ * ciphertext.
+ */
+static int KeyAgreeEcdhKyber1_server(WOLFSSH* ssh, byte hashId,
+        byte* f, word32* fSz)
+#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
+{
+    int ret = WS_SUCCESS;
+    byte sharedSecretHashSz = 0;
+    byte *sharedSecretHash = NULL;
+    OQS_KEM* kem = NULL;
+    ecc_key* pubKey = NULL;
+    ecc_key* privKey = NULL;
+    int primeId;
+#ifndef WOLFSSH_SMALL_STACK
+    ecc_key eccKeys[2];
+#endif
+
+    WLOG(WS_LOG_DEBUG, "Entering KeyAgreeEcdhKyber1_server()");
+
+#ifdef WOLFSSH_SMALL_STACK
+    pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key),
+            ssh->ctx->heap, DYNTYPE_PUBKEY);
+    privKey = (ecc_key*)WMALLOC(sizeof(ecc_key),
+            ssh->ctx->heap, DYNTYPE_PRIVKEY);
+    if (pubKey == NULL || privKey == NULL) {
+        ret = WS_MEMORY_E;
+    }
+#else
+    pubKey = &eccKeys[0];
+    privKey = &eccKeys[1];
+#endif
+
+    if (ret == 0) {
+        XMEMSET(pubKey, 0, sizeof(*pubKey));
+        XMEMSET(privKey, 0, sizeof(*privKey));
+
+        primeId = wcPrimeForId(ssh->handshake->kexId);
+        if (primeId == ECC_CURVE_INVALID)
+            ret = WS_INVALID_PRIME_CURVE;
+    }
+
+    if (ret == 0) {
+        kem = OQS_KEM_new(OQS_KEM_alg_kyber_512);
+        if (kem == NULL) {
+            ret = WS_MEMORY_E;
+        }
+    }
+
+    if ((ret == 0) &&
+        (ssh->handshake->eSz <= (word32)kem->length_public_key)) {
+        ret = WS_BUFFER_E;
+    }
+
+    if (ret == 0) {
+        if (OQS_KEM_encaps(kem, f, ssh->k, ssh->handshake->e) != OQS_SUCCESS) {
+            ret = WS_PUBKEY_REJECTED_E;
+            WLOG(WS_LOG_ERROR,
+                 "Generate ECC-kyber (encap) shared secret failed, %d",
+                 ret);
+            *fSz = 0;
+            ssh->kSz = 0;
+        }
+    }
+
+    if (ret == 0) {
+        *fSz -= kem->length_ciphertext;
+        ssh->kSz -= kem->length_shared_secret;
+    }
+    else {
+        *fSz = 0;
+        ssh->kSz = 0;
+        WLOG(WS_LOG_ERROR,
+             "Generate ECC-kyber (encap) shared secret failed, %d",
+             ret);
+    }
+
+    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);
+    }
+#ifdef HAVE_WC_ECC_SET_RNG
+    if (ret == 0) {
+        ret = wc_ecc_set_rng(privKey, ssh->rng);
+    }
+#endif
+    if (ret == 0) {
+        ret = wc_ecc_import_x963_ex(
+            ssh->handshake->e + kem->length_public_key,
+            ssh->handshake->eSz - (word32)kem->length_public_key,
+            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) {
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_ecc_export_x963(privKey, f + kem->length_ciphertext, fSz);
+        PRIVATE_KEY_LOCK();
+        *fSz += kem->length_ciphertext;
+    }
+    if (ret == 0) {
+        word32 tmp_kSz = ssh->kSz;
+        PRIVATE_KEY_UNLOCK();
+        ret = wc_ecc_shared_secret(privKey, pubKey,
+                  ssh->k + kem->length_shared_secret, &tmp_kSz);
+        PRIVATE_KEY_LOCK();
+        ssh->kSz = (word32)kem->length_shared_secret + tmp_kSz;
+    }
+    wc_ecc_free(privKey);
+    wc_ecc_free(pubKey);
+#ifdef WOLFSSH_SMALL_STACK
+    if (pubKey)
+        WFREE(pubKey, ssh->ctx->heap, DYNTYPE_PUBKEY);
+    if (privKey)
+        WFREE(privKey, ssh->ctx->heap, DYNTYPE_PUBKEY);
+#endif
+    if (kem != NULL) {
+        OQS_KEM_free(kem);
+        kem = NULL;
+    }
+
+    /* Replace the concatenated shared secrets with the hash. That
+     * will become the new shared secret.*/
+    if (ret == 0) {
+        sharedSecretHashSz = wc_HashGetDigestSize(hashId);
+        sharedSecretHash = (byte *)WMALLOC(sharedSecretHashSz,
+                ssh->ctx->heap, DYNTYPE_PRIVKEY);
+        if (sharedSecretHash == NULL) {
+            ret = WS_MEMORY_E;
+        }
+    }
+    if (ret == 0) {
+        ret = wc_Hash(hashId, ssh->k, ssh->kSz, sharedSecretHash,
+                      sharedSecretHashSz);
+    }
+    if (ret == 0) {
+        XMEMCPY(ssh->k, sharedSecretHash, sharedSecretHashSz);
+        ssh->kSz = sharedSecretHashSz;
+    }
+
+    if (sharedSecretHash) {
+        ForceZero(sharedSecretHash, sharedSecretHashSz);
+        WFREE(sharedSecretHash, ssh->ctx->heap, DYNTYPE_PRIVKEY);
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving KeyAgreeEcdhKyber1_server(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(hashId);
+    WOLFSSH_UNUSED(f);
+    WOLFSSH_UNUSED(fSz);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
+
+
+static int SignHRsa(WOLFSSH* ssh, byte* sig, word32* sigSz,
+        struct wolfSSH_sigKeyBlockFull *sigKey)
+#ifndef WOLFSSH_NO_RSA
+{
+    void* heap;
+    byte* encSig = NULL;
+    byte digest[WC_MAX_DIGEST_SIZE];
+    word32 digestSz = (word32)sizeof(digest);
+    word32 encSigSz;
+    int ret = WS_SUCCESS;
+    enum wc_HashType hashId;
+#ifndef WOLFSSH_SMALL_STACK
+    byte encSig_s[MAX_ENCODED_SIG_SZ];
+#endif
+
+    WLOG(WS_LOG_DEBUG, "Entering SignHRsa()");
+
+    heap = ssh->ctx->heap;
+#ifdef WOLFSSH_SMALL_STACK
+    encSig = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap, DYNTYPE_TEMP);
+    if (encSig == NULL) {
+        ret = WS_MEMORY_E;
+    }
+#else
+    encSig = encSig_s;
+#endif
+
+    if (ret == WS_SUCCESS) {
+        hashId = HashForId(ssh->handshake->pubKeyId);
+        digestSz = wc_HashGetDigestSize(hashId);
+
+        ret = wc_Hash(hashId, ssh->h, ssh->hSz, digest, digestSz);
+        if (ret != 0) {
+            ret = WS_CRYPTO_FAILED;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = wc_EncodeSignature(encSig, digest, digestSz,
+                wc_HashGetOID(hashId));
+        if (ret <= 0) {
+            WLOG(WS_LOG_DEBUG, "SignHRsa: Bad Encode Sig");
+            ret = WS_CRYPTO_FAILED;
+        }
+        else {
+            encSigSz = (word32)ret;
+            ret = WS_SUCCESS;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_INFO, "Signing hash with %s.",
+            IdToName(ssh->handshake->pubKeyId));
+        ret = wc_RsaSSL_Sign(encSig, encSigSz, sig,
+                KEX_SIG_SIZE, &sigKey->sk.rsa.key,
+                ssh->rng);
+        if (ret <= 0) {
+            WLOG(WS_LOG_DEBUG, "SignHRsa: Bad RSA Sign");
+            ret = WS_RSA_E;
+        }
+        else {
+            *sigSz = (word32)ret;
+            ret = WS_SUCCESS;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = wolfSSH_RsaVerify(sig, *sigSz, encSig, encSigSz,
+                &sigKey->sk.rsa.key, heap, "SignHRsa");
+    }
+
+    #ifdef WOLFSSH_SMALL_STACK
+    if (encSig != NULL)
+        WFREE(encSig, heap, DYNTYPE_TEMP);
+    #endif
+    WLOG(WS_LOG_DEBUG, "Leaving SignHRsa(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_RSA */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(sig);
+    WOLFSSH_UNUSED(sigSz);
+    WOLFSSH_UNUSED(sigKey);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_RSA */
+
+
+static int SignHEcdsa(WOLFSSH* ssh, byte* sig, word32* sigSz,
+        struct wolfSSH_sigKeyBlockFull *sigKey)
+#ifndef WOLFSSH_NO_ECDSA
+{
+#ifdef WOLFSSH_SMALL_STACK
+    void* heap = NULL;
+#endif
+    byte *r = NULL, *s = NULL;
+    byte digest[WC_MAX_DIGEST_SIZE];
+    word32 digestSz = (word32)sizeof(digest);
+    int ret = WS_SUCCESS;
+    enum wc_HashType hashId;
+    word32 rSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ,
+           sSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ;
+    byte rPad, sPad;
+#ifndef WOLFSSH_SMALL_STACK
+    byte r_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ];
+    byte s_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ];
+#endif
+
+    WLOG(WS_LOG_DEBUG, "Entering SignHEcdsa()");
+
+    hashId = HashForId(ssh->handshake->pubKeyId);
+    digestSz = wc_HashGetDigestSize(hashId);
+
+    ret = wc_Hash(hashId, ssh->h, ssh->hSz, digest, digestSz);
+    if (ret != 0) {
+        ret = WS_CRYPTO_FAILED;
+    }
+
+    if (ret == WS_SUCCESS) {
+        WLOG(WS_LOG_INFO, "Signing hash with %s.",
+                IdToName(ssh->handshake->pubKeyId));
+        ret = wc_ecc_sign_hash(digest, digestSz, sig, sigSz, ssh->rng,
+                &sigKey->sk.ecc.key);
+        if (ret != MP_OKAY) {
+            WLOG(WS_LOG_DEBUG, "SignHEcdsa: Bad ECDSA Sign");
+            ret = WS_ECC_E;
+        }
+        else {
+            ret = WS_SUCCESS;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+#ifdef WOLFSSH_SMALL_STACK
+        heap = ssh->ctx->heap;
+        r = (byte*)WMALLOC(rSz, heap, DYNTYPE_BUFFER);
+        s = (byte*)WMALLOC(sSz, heap, DYNTYPE_BUFFER);
+        if (r == NULL || s == NULL) {
+            ret = WS_MEMORY_E;
+        }
+#else
+        r = r_s;
+        s = s_s;
+#endif
+    }
+
+    if (ret == WS_SUCCESS) {
+        ret = wc_ecc_sig_to_rs(sig, *sigSz, r, &rSz, s, &sSz);
+        if (ret != 0) {
+            ret = WS_ECC_E;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        int 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);
+    }
+
+    #ifdef WOLFSSH_SMALL_STACK
+        if (r)
+            WFREE(r, heap, DYNTYPE_BUFFER);
+        if (s)
+            WFREE(s, heap, DYNTYPE_BUFFER);
+    #endif
+
+    WLOG(WS_LOG_DEBUG, "Leaving SignHEcdsa(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_ECDSA */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(sig);
+    WOLFSSH_UNUSED(sigSz);
+    WOLFSSH_UNUSED(sigKey);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_ECDSA */
+
+
+static int SignHEd25519(WOLFSSH* ssh, byte* sig, word32* sigSz,
+        struct wolfSSH_sigKeyBlockFull *sigKey)
+#ifndef WOLFSSH_NO_ED25519
+{
+    int ret;
+
+    WLOG(WS_LOG_DEBUG, "Entering SignHEd25519()");
+
+    ret = wc_ed25519_sign_msg(ssh->h, ssh->hSz, sig, sigSz, &sigKey->sk.ed.key);
+    if (ret != 0) {
+        WLOG(WS_LOG_DEBUG,
+                "SignHEd5519: Bad ED25519 Sign (error: %d)", ret);
+        ret = WS_ECC_E;
+    }
+
+    WLOG(WS_LOG_DEBUG, "Leaving SignHEd25519(), ret = %d", ret);
+    return ret;
+}
+#else /* WOLFSSH_NO_ED25519 */
+{
+    WOLFSSH_UNUSED(ssh);
+    WOLFSSH_UNUSED(sig);
+    WOLFSSH_UNUSED(sigSz);
+    WOLFSSH_UNUSED(sigKey);
+    return WS_INVALID_ALGO_ID;
+}
+#endif /* WOLFSSH_NO_ED25519 */
+
+
+static int SignH(WOLFSSH* ssh, byte* sig, word32* sigSz,
+        struct wolfSSH_sigKeyBlockFull *sigKey)
+{
+    int ret;
+
+    switch (sigKey->pubKeyId) {
+        case ID_SSH_RSA:
+        case ID_X509V3_SSH_RSA:
+        case ID_RSA_SHA2_256:
+        case ID_RSA_SHA2_512:
+            ret = SignHRsa(ssh, sig, sigSz, sigKey);
+            break;
+        case ID_ECDSA_SHA2_NISTP256:
+        case ID_ECDSA_SHA2_NISTP384:
+        case ID_ECDSA_SHA2_NISTP521:
+        case ID_X509V3_ECDSA_SHA2_NISTP256:
+        case ID_X509V3_ECDSA_SHA2_NISTP384:
+        case ID_X509V3_ECDSA_SHA2_NISTP521:
+            ret = SignHEcdsa(ssh, sig, sigSz, sigKey);
+            break;
+        case ID_ED25519:
+            ret = SignHEd25519(ssh, sig, sigSz, sigKey);
+            break;
+        default:
+            ret = WS_INVALID_ALGO_ID;
+    }
+
+    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)
+{
+    int ret = WS_SUCCESS;
+    void *heap  = NULL;
+    byte *f_ptr = NULL, *sig_ptr = NULL;
+    byte scratchLen[LENGTH_SZ];
+    word32 fSz = KEX_F_SIZE;
+    word32 sigSz = KEX_SIG_SIZE;
+    byte fPad = 0;
+    byte kPad = 0;
+    word32 sigBlockSz = 0;
+    word32 payloadSz = 0;
+    byte* output = NULL;
+    word32 idx = 0;
+    word32 keyIdx = 0;
+    enum wc_HashType hashId = WC_HASH_TYPE_NONE;
+    wc_HashAlg* hash = NULL;
+    struct wolfSSH_sigKeyBlockFull *sigKeyBlock_ptr = NULL;
+#ifndef WOLFSSH_SMALL_STACK
+    byte f_s[KEX_F_SIZE];
+    byte sig_s[KEX_SIG_SIZE];
+#endif
+    byte msgId = 0;
+    byte useDh = 0;
+    byte useEcc = 0;
+    byte useCurve25519 = 0;
+    byte useEccKyber = 0;
+
+    WLOG(WS_LOG_DEBUG, "Entering SendKexDhReply()");
+
+    if (ret == WS_SUCCESS) {
+        if (ssh == NULL || ssh->ctx == NULL || ssh->handshake == NULL) {
+            ret = WS_BAD_ARGUMENT;
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        heap = ssh->ctx->heap;
+    }
+
+#ifdef WOLFSSH_SMALL_STACK
+    f_ptr = (byte*)WMALLOC(KEX_F_SIZE, heap, DYNTYPE_BUFFER);
+    sig_ptr = (byte*)WMALLOC(KEX_SIG_SIZE, heap, DYNTYPE_BUFFER);
     if (f_ptr == NULL || sig_ptr == NULL)
         ret = WS_MEMORY_E;
+#else
+    f_ptr = f_s;
+    sig_ptr = sig_s;
 #endif
 
     sigKeyBlock_ptr = (struct wolfSSH_sigKeyBlockFull*)WMALLOC(
@@ -9848,6 +11361,24 @@ int SendKexDhReply(WOLFSSH* ssh)
                 (word32)WSTRLEN(sigKeyBlock_ptr->pubKeyFmtName);
 
         switch (ssh->handshake->kexId) {
+#ifndef WOLFSSH_NO_DH_GROUP1_SHA1
+            case ID_DH_GROUP1_SHA1:
+                useDh = 1;
+                msgId = MSGID_KEXDH_REPLY;
+                break;
+#endif
+#ifndef WOLFSSH_NO_DH_GROUP14_SHA1
+            case ID_DH_GROUP14_SHA1:
+                useDh = 1;
+                msgId = MSGID_KEXDH_REPLY;
+                break;
+#endif
+#ifndef WOLFSSH_NO_DH_GEX_SHA256
+            case ID_DH_GEX_SHA256:
+                useDh = 1;
+                msgId = MSGID_KEXDH_GEX_REPLY;
+                break;
+#endif
 #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256
             case ID_ECDH_SHA2_NISTP256:
                 useEcc = 1;
@@ -9866,313 +11397,69 @@ int SendKexDhReply(WOLFSSH* ssh)
                 msgId = MSGID_KEXDH_REPLY;
                 break;
 #endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+            case ID_CURVE25519_SHA256:
+                useCurve25519 = 1;
+                msgId = MSGID_KEXDH_REPLY;
+                break;
+#endif
 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
             case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
                 useEccKyber = 1; /* Only support level 1 for now. */
                 msgId = MSGID_KEXKEM_REPLY;
                 break;
 #endif
+            default:
+                ret = WS_INVALID_ALGO_ID;
         }
+    }
 
+    if (ret == WS_SUCCESS) {
         hash = &ssh->handshake->kexHash;
         hashId = (enum wc_HashType)ssh->handshake->kexHashId;
-    }
 
-    if (ret == WS_SUCCESS) {
         for (keyIdx = 0; keyIdx < ssh->ctx->privateKeyCount; keyIdx++) {
-            if (ssh->ctx->privateKey[keyIdx].publicKeyFmt
-                    == sigKeyBlock_ptr->pubKeyFmtId) {
-                break;
-            }
-        }
-        if (keyIdx == ssh->ctx->privateKeyCount) {
-            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) {
-        ret = SendKexGetSigningKey(ssh, sigKeyBlock_ptr, hashId, hash, keyIdx);
-    }
-
-    if (ret == WS_SUCCESS) {
-        /* reset size here because a previous shared secret could potentially be
-         * smaller by a byte than usual and cause buffer issues with re-key */
-        if (ret == 0)
-            ssh->kSz = MAX_KEX_KEY_SZ;
-
-        /* 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
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-                && !useEccKyber
-#endif
-               ) {
-#ifndef WOLFSSH_NO_DH
-                byte *y_ptr = NULL;
-                const byte* primeGroup = NULL;
-                const byte* generator = NULL;
-                word32 ySz = MAX_KEX_KEY_SZ;
-                word32 primeGroupSz = 0;
-                word32 generatorSz = 0;
-            #ifdef WOLFSSH_SMALL_STACK
-                DhKey *privKey = (DhKey*)WMALLOC(sizeof(DhKey), heap,
-                        DYNTYPE_PRIVKEY);
-                y_ptr = (byte*)WMALLOC(ySz, heap, DYNTYPE_PRIVKEY);
-                if (privKey == NULL || y_ptr == NULL)
-                    ret = WS_MEMORY_E;
-            #else
-                DhKey privKey[1];
-                byte y_s[MAX_KEX_KEY_SZ];
-                y_ptr = y_s;
-            #endif
-                if (ret == WS_SUCCESS) {
-                    ret = GetDHPrimeGroup(ssh->handshake->kexId, &primeGroup,
-                        &primeGroupSz, &generator, &generatorSz);
-                    #ifndef WOLFSSH_NO_DH_GEX_SHA256
-                    if (ssh->handshake->kexId == ID_DH_GEX_SHA256)
-                        msgId = MSGID_KEXDH_GEX_REPLY;
-                    #endif
-
-                    if (ret == WS_SUCCESS) {
-                        ret = wc_InitDhKey(privKey);
-                    }
-                    if (ret == 0)
-                        ret = wc_DhSetKey(privKey, primeGroup, primeGroupSz,
-                                generator, generatorSz);
-                    if (ret == 0)
-                        ret = wc_DhGenerateKeyPair(privKey, ssh->rng,
-                                y_ptr, &ySz, f_ptr, &fSz);
-                    if (ret == 0) {
-                        PRIVATE_KEY_UNLOCK();
-                        ret = wc_DhAgree(privKey, ssh->k, &ssh->kSz, y_ptr, ySz,
-                                ssh->handshake->e, ssh->handshake->eSz);
-                        PRIVATE_KEY_LOCK();
-                    }
-                    ForceZero(y_ptr, ySz);
-                    wc_FreeDhKey(privKey);
-                }
-            #ifdef WOLFSSH_SMALL_STACK
-                if (y_ptr)
-                    WFREE(y_ptr, heap, DYNTYPE_PRIVKEY);
-                if (privKey) {
-                    WFREE(privKey, heap, DYNTYPE_PRIVKEY);
-                }
-            #endif
-#endif /* ! WOLFSSH_NO_DH */
-            }
-            else if (useEcc) {
-#if !defined(WOLFSSH_NO_ECDH)
-            #ifdef WOLFSSH_SMALL_STACK
-                ecc_key *pubKey = NULL, *privKey = NULL;
-                pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap,
-                        DYNTYPE_PUBKEY);
-                privKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap,
-                        DYNTYPE_PRIVKEY);
-                if (pubKey == NULL || privKey == NULL) {
-                    ret = WS_MEMORY_E;
-                }
-            #else
-                ecc_key pubKey[1];
-                ecc_key privKey[1];
-            #endif
-                int primeId;
-
-                primeId  = wcPrimeForId(ssh->handshake->kexId);
-                if (primeId == ECC_CURVE_INVALID)
-                    ret = WS_INVALID_PRIME_CURVE;
-
-                if (ret == 0)
-                    ret = wc_ecc_init_ex(pubKey, heap, INVALID_DEVID);
-                if (ret == 0)
-                    ret = wc_ecc_init_ex(privKey, heap, INVALID_DEVID);
-#ifdef HAVE_WC_ECC_SET_RNG
-                if (ret == 0)
-                    ret = wc_ecc_set_rng(privKey, ssh->rng);
-#endif
-
-                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) {
-                    PRIVATE_KEY_UNLOCK();
-                    ret = wc_ecc_export_x963(privKey, f_ptr, &fSz);
-                    PRIVATE_KEY_LOCK();
-                }
-                if (ret == 0) {
-                    PRIVATE_KEY_UNLOCK();
-                    ret = wc_ecc_shared_secret(privKey, pubKey,
-                                               ssh->k, &ssh->kSz);
-                    PRIVATE_KEY_LOCK();
-                }
-                wc_ecc_free(privKey);
-                wc_ecc_free(pubKey);
-            #ifdef WOLFSSH_SMALL_STACK
-                WFREE(pubKey, heap, DYNTYPE_PUBKEY);
-                WFREE(privKey, heap, DYNTYPE_PRIVKEY);
-                pubKey  = NULL;
-                privKey = NULL;
-            #endif
-#endif /* !defined(WOLFSSH_NO_ECDH) */
-            }
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-            else if (useEccKyber) {
-                /* This is a hybrid KEM. In this case, I need to generate my ECC
-                 * keypair, send the public one, use the private one to generate
-                 * the shared secret, use the post-quantum public key to
-                 * generate and encapsulate the shared secret and send the
-                 * ciphertext. */
-                OQS_KEM* kem = NULL;
-                int primeId;
-                ret = 0;
-            #ifdef WOLFSSH_SMALL_STACK
-                ecc_key *pubKey = NULL, *privKey = NULL;
-                pubKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap,
-                        DYNTYPE_PUBKEY);
-                privKey = (ecc_key*)WMALLOC(sizeof(ecc_key), heap,
-                        DYNTYPE_PRIVKEY);
-                if (pubKey == NULL || privKey == NULL) {
-                    ret = WS_MEMORY_E;
-                }
-            #else
-                ecc_key pubKey[1];
-                ecc_key privKey[1];
-            #endif
-
-                if (ret == 0) {
-                    XMEMSET(pubKey, 0, sizeof(*pubKey));
-                    XMEMSET(privKey, 0, sizeof(*privKey));
-
-                    primeId = wcPrimeForId(ssh->handshake->kexId);
-                    if (primeId == ECC_CURVE_INVALID)
-                        ret = WS_INVALID_PRIME_CURVE;
-                }
-
-                if (ret == 0) {
-                    kem = OQS_KEM_new(OQS_KEM_alg_kyber_512);
-                    if (kem == NULL) {
-                        ret = WS_INVALID_ALGO_ID;
-                    }
-                }
-
-                if ((ret == 0) &&
-                    (ssh->handshake->eSz <= (word32)kem->length_public_key)) {
-                    ret = WS_BUFFER_E;
-                }
-
-                if (ret == 0) {
-                    if (OQS_KEM_encaps(kem, f_ptr, ssh->k,
-                        ssh->handshake->e) != OQS_SUCCESS) {
-                        ret = WS_PUBKEY_REJECTED_E;
-                    }
-                }
-
-                if (ret == 0) {
-                    fSz -= kem->length_ciphertext;
-                    ssh->kSz -= kem->length_shared_secret;
-                }
-                else {
-                    fSz = 0;
-                    ssh->kSz = 0;
-                    WLOG(WS_LOG_ERROR,
-                         "Generate ECC-kyber (encap) shared secret failed, %d",
-                         ret);
-                }
-
-                if (ret == 0) {
-                    ret = wc_ecc_init_ex(pubKey, heap, INVALID_DEVID);
-                }
-                if (ret == 0) {
-                    ret = wc_ecc_init_ex(privKey, heap, INVALID_DEVID);
-                }
-#ifdef HAVE_WC_ECC_SET_RNG
-                if (ret == 0) {
-                    ret = wc_ecc_set_rng(privKey, ssh->rng);
-                }
-#endif
-                if (ret == 0) {
-                    ret = wc_ecc_import_x963_ex(
-                        ssh->handshake->e + kem->length_public_key,
-                        ssh->handshake->eSz - (word32)kem->length_public_key,
-                        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) {
-                    PRIVATE_KEY_UNLOCK();
-                    ret = wc_ecc_export_x963(privKey,
-                              f_ptr + kem->length_ciphertext, &fSz);
-                    fSz += kem->length_ciphertext;
-                    PRIVATE_KEY_LOCK();
-                }
-                if (ret == 0) {
-                    word32 tmp_kSz = ssh->kSz;
-                    PRIVATE_KEY_UNLOCK();
-                    ret = wc_ecc_shared_secret(privKey, pubKey,
-                              ssh->k + kem->length_shared_secret, &tmp_kSz);
-                    PRIVATE_KEY_LOCK();
-                    ssh->kSz = (word32)kem->length_shared_secret + tmp_kSz;
-                }
-                wc_ecc_free(privKey);
-                wc_ecc_free(pubKey);
-            #ifdef WOLFSSH_SMALL_STACK
-                WFREE(pubKey, heap, DYNTYPE_PUBKEY);
-                WFREE(privKey, heap, DYNTYPE_PRIVKEY);
-                pubKey  = NULL;
-                privKey = NULL;
-            #endif
-                if (kem != NULL) {
-                    OQS_KEM_free(kem);
-                    kem = NULL;
-                }
+            if (ssh->ctx->privateKey[keyIdx].publicKeyFmt
+                    == sigKeyBlock_ptr->pubKeyFmtId) {
+                break;
+            }
+        }
+        if (keyIdx == ssh->ctx->privateKeyCount) {
+            ret = WS_INVALID_ALGO_ID;
+        }
+    }
 
-                /* Replace the concatenated shared secrets with the hash. That
-                 * will become the new shared secret.*/
-                if (ret == 0) {
-                    sharedSecretHashSz = wc_HashGetDigestSize(hashId);
-                    sharedSecretHash = (byte *)WMALLOC(sharedSecretHashSz, heap,
-                                                       DYNTYPE_PRIVKEY);
-                    if (sharedSecretHash == NULL) {
-                        ret = WS_MEMORY_E;
-                    }
-                }
-                if (ret == 0) {
-                    ret = wc_Hash(hashId, ssh->k, ssh->kSz, sharedSecretHash,
-                                  sharedSecretHashSz);
-                }
-                if (ret == 0) {
-                    XMEMCPY(ssh->k, sharedSecretHash, sharedSecretHashSz);
-                    ssh->kSz = sharedSecretHashSz;
-                }
+    /* 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) {
+        ret = SendKexGetSigningKey(ssh, sigKeyBlock_ptr, hashId, hash, keyIdx);
+    }
+
+    if (ret == WS_SUCCESS) {
+        /* reset size here because a previous shared secret could potentially be
+         * smaller by a byte than usual and cause buffer issues with re-key */
+        ssh->kSz = MAX_KEX_KEY_SZ;
 
-                WFREE(sharedSecretHash, heap, DYNTYPE_PRIVKEY);
-                sharedSecretHash = NULL;
+        /* 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 (useDh) {
+                ret = KeyAgreeDh_server(ssh, hashId, f_ptr, &fSz);
             }
-#endif /* !WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256 */
-            else {
-                /* This should never happen */
-                ret = WS_ERROR;
+            else if (useEcc) {
+                ret = KeyAgreeEcdh_server(ssh, hashId, f_ptr, &fSz);
+            }
+            if (useCurve25519) {
+                ret = KeyAgreeCurve25519_server(ssh, hashId, f_ptr, &fSz);
+            }
+            else if (useEccKyber) {
+                ret = KeyAgreeEcdhKyber1_server(ssh, hashId, f_ptr, &fSz);
             }
         }
 
         /* Hash in the server's DH f-value. */
-        if (ret == 0
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-            && !useEccKyber
-#endif
-           ) {
+        if (ret == 0 && (useDh || useEcc)) {
             ret = CreateMpint(f_ptr, &fSz, &fPad);
         }
         if (ret == 0) {
@@ -10188,11 +11475,7 @@ int SendKexDhReply(WOLFSSH* ssh)
         }
 
         /* Hash in the shared secret K. */
-        if (ret == 0
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-            && !useEccKyber
-#endif
-           ) {
+        if (ret == 0 && !useEccKyber) {
             ret = CreateMpint(ssh->k, &ssh->kSz, &kPad);
         }
         if (ret == 0) {
@@ -10233,142 +11516,7 @@ int SendKexDhReply(WOLFSSH* ssh)
 
     /* Sign h with the server's private key. */
     if (ret == WS_SUCCESS) {
-        wc_HashAlg digestHash;
-        byte digest[WC_MAX_DIGEST_SIZE];
-        enum wc_HashType sigHashId;
-
-        sigHashId = HashForId(ssh->handshake->pubKeyId);
-        ret = wc_HashInit(&digestHash, sigHashId);
-        if (ret == 0)
-            ret = HashUpdate(&digestHash, sigHashId, ssh->h, ssh->hSz);
-        if (ret == 0)
-            ret = wc_HashFinal(&digestHash, sigHashId, digest);
-        if (ret != 0)
-            ret = WS_CRYPTO_FAILED;
-        wc_HashFree(&digestHash, sigHashId);
-
-        if (ret == WS_SUCCESS) {
-            if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA
-        #ifdef WOLFSSH_CERTS
-             || sigKeyBlock_ptr->pubKeyId == ID_X509V3_SSH_RSA
-        #endif
-             || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_256
-             || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_512
-            ) {
-#ifndef WOLFSSH_NO_RSA
-                word32 encSigSz;
-            #ifdef WOLFSSH_SMALL_STACK
-                byte *encSig = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap,
-                        DYNTYPE_TEMP);
-                if (encSig == NULL) {
-                    ret = WS_MEMORY_E;
-                }
-
-                if (ret == WS_SUCCESS)
-            #else
-                byte encSig[MAX_ENCODED_SIG_SZ];
-            #endif
-                {
-                    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 %s.",
-                            IdToName(ssh->handshake->pubKeyId));
-                        sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig_ptr,
-                                KEX_SIG_SIZE, &sigKeyBlock_ptr->sk.rsa.key,
-                                ssh->rng);
-                        if (sigSz <= 0) {
-                            WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign");
-                            ret = WS_RSA_E;
-                        }
-                        else {
-                            ret = wolfSSH_RsaVerify(sig_ptr, sigSz,
-                                    encSig, encSigSz,
-                                    &sigKeyBlock_ptr->sk.rsa.key,
-                                    heap, "SendKexDhReply");
-                        }
-                    }
-                #ifdef WOLFSSH_SMALL_STACK
-                    WFREE(encSig, heap, DYNTYPE_TEMP);
-                #endif
-                }
-#endif /* WOLFSSH_NO_RSA */
-            }
-            else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256
-                  || sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384
-                  || sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521
-#ifdef WOLFSSH_CERTS
-                  || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP256
-                  || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP384
-                  || sigKeyBlock_ptr->pubKeyId == ID_X509V3_ECDSA_SHA2_NISTP521
-#endif
-            ) {
-#ifndef WOLFSSH_NO_ECDSA
-                WLOG(WS_LOG_INFO, "Signing hash with %s.",
-                        IdToName(ssh->handshake->pubKeyId));
-                sigSz = KEX_SIG_SIZE;
-                ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId),
-                                       sig_ptr, &sigSz,
-                                       ssh->rng, &sigKeyBlock_ptr->sk.ecc.key);
-                if (ret != MP_OKAY) {
-                    WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad ECDSA Sign");
-                    ret = WS_ECC_E;
-                }
-                else {
-                    byte *r_ptr = NULL, *s_ptr = NULL;
-                    word32 rSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ;
-                    word32 sSz = MAX_ECC_BYTES + ECC_MAX_PAD_SZ;
-                    byte rPad;
-                    byte sPad;
-
-                    #ifdef WOLFSSH_SMALL_STACK
-                        r_ptr = (byte*)WMALLOC(rSz, heap, DYNTYPE_BUFFER);
-                        s_ptr = (byte*)WMALLOC(sSz, heap, DYNTYPE_BUFFER);
-                        if (r_ptr == NULL || s_ptr == NULL)
-                            ret = WS_MEMORY_E;
-                    #else
-                        byte r_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ];
-                        byte s_s[MAX_ECC_BYTES + ECC_MAX_PAD_SZ];
-                        r_ptr = r_s;
-                        s_ptr = s_s;
-                    #endif
-                    if (ret == WS_SUCCESS) {
-                        ret = wc_ecc_sig_to_rs(sig_ptr, sigSz,
-                                r_ptr, &rSz, s_ptr, &sSz);
-                    }
-                    if (ret == 0) {
-                        idx = 0;
-                        rPad = (r_ptr[0] & 0x80) ? 1 : 0;
-                        sPad = (s_ptr[0] & 0x80) ? 1 : 0;
-                        sigSz = (LENGTH_SZ * 2) + rSz + rPad + sSz + sPad;
-
-                        c32toa(rSz + rPad, sig_ptr + idx);
-                        idx += LENGTH_SZ;
-                        if (rPad)
-                            sig_ptr[idx++] = 0;
-                        WMEMCPY(sig_ptr + idx, r_ptr, rSz);
-                        idx += rSz;
-                        c32toa(sSz + sPad, sig_ptr + idx);
-                        idx += LENGTH_SZ;
-                        if (sPad)
-                            sig_ptr[idx++] = 0;
-                        WMEMCPY(sig_ptr + idx, s_ptr, sSz);
-                    }
-                    #ifdef WOLFSSH_SMALL_STACK
-                        if (r_ptr)
-                            WFREE(r_ptr, heap, DYNTYPE_BUFFER);
-                        if (s_ptr)
-                            WFREE(s_ptr, heap, DYNTYPE_BUFFER);
-                    #endif
-                }
-#endif /* WOLFSSH_NO_ECDSA */
-            }
-        }
+        ret = SignH(ssh, sig_ptr, &sigSz, sigKeyBlock_ptr);
     }
 
     if (sigKeyBlock_ptr != NULL) {
@@ -10382,16 +11530,18 @@ int SendKexDhReply(WOLFSSH* ssh)
                 || sigKeyBlock_ptr->pubKeyFmtId == ID_ECDSA_SHA2_NISTP521) {
 #ifndef WOLFSSH_NO_ECDSA
             wc_ecc_free(&sigKeyBlock_ptr->sk.ecc.key);
+#endif
+        }
+        else if (sigKeyBlock_ptr->pubKeyId == ID_ED25519) {
+#if !defined(WOLFSSH_NO_ED25519)
+            wc_ed25519_free(&sigKeyBlock_ptr->sk.ed.key);
 #endif
         }
     }
 
     if (ret == WS_SUCCESS) {
-        int doKeyPadding = 1;
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-        doKeyPadding = !useEccKyber;
-#endif
-        ret = GenerateKeys(ssh, hashId, doKeyPadding);
+        /* If we aren't using EccKyber, use padding. */
+        ret = GenerateKeys(ssh, hashId, !useEccKyber);
     }
 
     /* Get the buffer, copy the packet data, once f is laid into the buffer,
@@ -10409,21 +11559,22 @@ int SendKexDhReply(WOLFSSH* ssh)
 
         output[idx++] = msgId;
 
+        /* Copy the key block size into the buffer */
+        c32toa(sigKeyBlock_ptr->sz, output + idx);
+        idx += LENGTH_SZ;
+
+        /* Copy the key name into the buffer */
+        c32toa(sigKeyBlock_ptr->pubKeyFmtNameSz, output + idx);
+        idx += LENGTH_SZ;
+        WMEMCPY(output + idx, sigKeyBlock_ptr->pubKeyFmtName, sigKeyBlock_ptr->pubKeyFmtNameSz);
+        idx += sigKeyBlock_ptr->pubKeyFmtNameSz;
+
         /* add host public key */
         switch (sigKeyBlock_ptr->pubKeyFmtId) {
             case ID_SSH_RSA:
             {
 #ifndef WOLFSSH_NO_RSA
             /* Copy the rsaKeyBlock into the buffer. */
-            c32toa(sigKeyBlock_ptr->sz, output + idx);
-            idx += LENGTH_SZ;
-            c32toa(sigKeyBlock_ptr->pubKeyFmtNameSz, output + idx);
-            idx += LENGTH_SZ;
-            WMEMCPY(output + idx,
-                    sigKeyBlock_ptr->pubKeyFmtName,
-                    sigKeyBlock_ptr->pubKeyFmtNameSz);
-            idx += sigKeyBlock_ptr->pubKeyFmtNameSz;
-
             c32toa(sigKeyBlock_ptr->sk.rsa.eSz + sigKeyBlock_ptr->sk.rsa.ePad,
                    output + idx);
             idx += LENGTH_SZ;
@@ -10448,15 +11599,6 @@ int SendKexDhReply(WOLFSSH* ssh)
             {
 #ifndef WOLFSSH_NO_ECDSA
             /* Copy the ecdsaKeyBlock into the buffer. */
-            c32toa(sigKeyBlock_ptr->sz, output + idx);
-            idx += LENGTH_SZ;
-            c32toa(sigKeyBlock_ptr->pubKeyFmtNameSz, output + idx);
-            idx += LENGTH_SZ;
-            WMEMCPY(output + idx,
-                    sigKeyBlock_ptr->pubKeyFmtName,
-                    sigKeyBlock_ptr->pubKeyFmtNameSz);
-            idx += sigKeyBlock_ptr->pubKeyFmtNameSz;
-
             c32toa(sigKeyBlock_ptr->sk.ecc.primeNameSz, output + idx);
             idx += LENGTH_SZ;
             WMEMCPY(output + idx, sigKeyBlock_ptr->sk.ecc.primeName,
@@ -10471,6 +11613,19 @@ int SendKexDhReply(WOLFSSH* ssh)
             }
             break;
 
+            case ID_ED25519:
+            {
+#if !defined(WOLFSSH_NO_ED25519)
+            /* Copy the edKeyBlock into the buffer. */
+            c32toa(sigKeyBlock_ptr->sk.ed.qSz, output + idx);
+            idx += LENGTH_SZ;
+            WMEMCPY(output + idx, sigKeyBlock_ptr->sk.ed.q,
+                    sigKeyBlock_ptr->sk.ed.qSz);
+            idx += sigKeyBlock_ptr->sk.ed.qSz;
+#endif
+            }
+            break;
+
         #ifdef WOLFSSH_CERTS
             case ID_X509V3_SSH_RSA:
             case ID_X509V3_ECDSA_SHA2_NISTP256:
@@ -10762,6 +11917,7 @@ int SendKexDhInit(WOLFSSH* ssh)
     switch (ssh->handshake->kexId) {
 #ifndef WOLFSSH_NO_DH_GROUP1_SHA1
         case ID_DH_GROUP1_SHA1:
+            ssh->handshake->useDh = 1;
             primeGroup = dhPrimeGroup1;
             primeGroupSz = dhPrimeGroup1Sz;
             generator = dhGenerator;
@@ -10770,6 +11926,7 @@ int SendKexDhInit(WOLFSSH* ssh)
 #endif
 #ifndef WOLFSSH_NO_DH_GROUP14_SHA1
         case ID_DH_GROUP14_SHA1:
+            ssh->handshake->useDh = 1;
             primeGroup = dhPrimeGroup14;
             primeGroupSz = dhPrimeGroup14Sz;
             generator = dhGenerator;
@@ -10778,6 +11935,7 @@ int SendKexDhInit(WOLFSSH* ssh)
 #endif
 #ifndef WOLFSSH_NO_DH_GEX_SHA256
         case ID_DH_GEX_SHA256:
+            ssh->handshake->useDh = 1;
             primeGroup = ssh->handshake->primeGroup;
             primeGroupSz = ssh->handshake->primeGroupSz;
             generator = ssh->handshake->generator;
@@ -10803,6 +11961,12 @@ int SendKexDhInit(WOLFSSH* ssh)
             msgId = MSGID_KEXECDH_INIT;
             break;
 #endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+        case ID_CURVE25519_SHA256:
+            ssh->handshake->useCurve25519 = 1;
+            msgId = MSGID_KEXECDH_INIT;
+            break;
+#endif
 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
         case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
             /* Only support level 1 for now. */
@@ -10821,6 +11985,9 @@ int SendKexDhInit(WOLFSSH* ssh)
 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
             && !ssh->handshake->useEccKyber
 #endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+            && !ssh->handshake->useCurve25519
+#endif
 ) {
 #ifndef WOLFSSH_NO_DH
             DhKey* privKey = &ssh->handshake->privKey.dh;
@@ -10836,6 +12003,23 @@ int SendKexDhInit(WOLFSSH* ssh)
                                            e, &eSz);
 #endif
         }
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+        else if (ssh->handshake->useCurve25519) {
+            curve25519_key* privKey = &ssh->handshake->privKey.curve25519;
+            if (ret == 0)
+                ret = wc_curve25519_init_ex(privKey, ssh->ctx->heap,
+                                            INVALID_DEVID);
+            if (ret == 0)
+                ret = wc_curve25519_make_key(ssh->rng, CURVE25519_KEYSIZE,
+                                             privKey);
+            if (ret == 0) {
+                PRIVATE_KEY_UNLOCK();
+                ret = wc_curve25519_export_public_ex(privKey, e, &eSz,
+                          EC25519_LITTLE_ENDIAN);
+                PRIVATE_KEY_LOCK();
+            }
+        }
+#endif /* ! WOLFSSH_NO_CURVE25519_SHA256 */
         else if (ssh->handshake->useEcc
 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
                  || ssh->handshake->useEccKyber
@@ -10907,6 +12091,10 @@ int SendKexDhInit(WOLFSSH* ssh)
 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
         && !ssh->handshake->useEccKyber
 #endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+        && !ssh->handshake->useCurve25519
+#endif
+
        ) {
         ret = CreateMpint(e, &eSz, &ePad);
     }
@@ -11243,12 +12431,14 @@ int SendServiceAccept(WOLFSSH* ssh, byte serviceId)
 
 #define WS_EXTINFO_EXTENSION_COUNT 1
 static const char serverSigAlgsName[] = "server-sig-algs";
-static word32 serverSigAlgsNameSz = (word32)sizeof(serverSigAlgsName) - 1;
+
 
 int SendExtInfo(WOLFSSH* ssh)
 {
     byte* output;
     word32 idx;
+    word32 keyAlgoNamesSz = 0;
+    word32 serverSigAlgsNameSz = 0;
     int ret = WS_SUCCESS;
 
     WLOG(WS_LOG_DEBUG, "Entering SendExtInfo()");
@@ -11258,8 +12448,10 @@ int SendExtInfo(WOLFSSH* ssh)
     }
 
     if (ret == WS_SUCCESS) {
+        keyAlgoNamesSz = AlgoListSz(ssh->algoListKeyAccepted);
+        serverSigAlgsNameSz = AlgoListSz(serverSigAlgsName);
         ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + (LENGTH_SZ * 2)
-                + serverSigAlgsNameSz + cannedKeyAlgoNamesSz);
+                + serverSigAlgsNameSz + keyAlgoNamesSz);
     }
 
     if (ret == WS_SUCCESS) {
@@ -11275,10 +12467,10 @@ int SendExtInfo(WOLFSSH* ssh)
         WMEMCPY(output + idx, serverSigAlgsName, serverSigAlgsNameSz);
         idx += serverSigAlgsNameSz;
 
-        c32toa(cannedKeyAlgoNamesSz, output + idx);
+        c32toa(keyAlgoNamesSz, output + idx);
         idx += LENGTH_SZ;
-        WMEMCPY(output + idx, cannedKeyAlgoNames, cannedKeyAlgoNamesSz);
-        idx += cannedKeyAlgoNamesSz;
+        WMEMCPY(output + idx, ssh->algoListKeyAccepted, keyAlgoNamesSz);
+        idx += keyAlgoNamesSz;
 
         ssh->outputBuffer.length = idx;
 
@@ -11475,19 +12667,16 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh,
                 #ifndef WOLFSSH_NO_SSH_RSA_SHA1
                 case ID_SSH_RSA:
                     names = cannedKeyAlgoSshRsaNames;
-                    namesSz = cannedKeyAlgoSshRsaNamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_RSA_SHA2_256
                 case ID_RSA_SHA2_256:
                     names = cannedKeyAlgoRsaSha2_256Names;
-                    namesSz = cannedKeyAlgoRsaSha2_256NamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_RSA_SHA2_512
                 case ID_RSA_SHA2_512:
                     names = cannedKeyAlgoRsaSha2_512Names;
-                    namesSz = cannedKeyAlgoRsaSha2_512NamesSz;
                     break;
                 #endif
                 default:
@@ -11496,6 +12685,7 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh,
             }
 
             if (ret == WS_SUCCESS) {
+                namesSz = (word32)WSTRLEN(names);
                 c32toa(keySig->sigSz + namesSz + LENGTH_SZ * 2, output + begin);
                 begin += LENGTH_SZ;
                 c32toa(namesSz, output + begin);
@@ -11909,19 +13099,16 @@ static int BuildUserAuthRequestEcc(WOLFSSH* ssh,
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
                 case ID_ECDSA_SHA2_NISTP256:
                     names = cannedKeyAlgoEcc256Names;
-                    namesSz = cannedKeyAlgoEcc256NamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
                 case ID_ECDSA_SHA2_NISTP384:
                     names = cannedKeyAlgoEcc384Names;
-                    namesSz = cannedKeyAlgoEcc384NamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
                 case ID_ECDSA_SHA2_NISTP521:
                     names = cannedKeyAlgoEcc521Names;
-                    namesSz = cannedKeyAlgoEcc521NamesSz;
                     break;
                 #endif
                 default:
@@ -11930,6 +13117,8 @@ static int BuildUserAuthRequestEcc(WOLFSSH* ssh,
             }
 
             if (ret == WS_SUCCESS) {
+                namesSz = (word32)WSTRLEN(names);
+
                 c32toa(rSz + rPad + sSz + sPad + namesSz + LENGTH_SZ * 4,
                         output + begin);
                 begin += LENGTH_SZ;
@@ -12153,37 +13342,31 @@ static int BuildUserAuthRequestEccCert(WOLFSSH* ssh,
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
                 case ID_ECDSA_SHA2_NISTP256:
                     names = cannedKeyAlgoEcc256Names;
-                    namesSz = cannedKeyAlgoEcc256NamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
                 case ID_ECDSA_SHA2_NISTP384:
                     names = cannedKeyAlgoEcc384Names;
-                    namesSz = cannedKeyAlgoEcc384NamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
                 case ID_ECDSA_SHA2_NISTP521:
                     names = cannedKeyAlgoEcc521Names;
-                    namesSz = cannedKeyAlgoEcc521NamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
                 case ID_X509V3_ECDSA_SHA2_NISTP256:
                     names = cannedKeyAlgoX509Ecc256Names;
-                    namesSz = cannedKeyAlgoX509Ecc256NamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384
                 case ID_X509V3_ECDSA_SHA2_NISTP384:
                     names = cannedKeyAlgoX509Ecc384Names;
-                    namesSz = cannedKeyAlgoX509Ecc384NamesSz;
                     break;
                 #endif
                 #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521
                 case ID_X509V3_ECDSA_SHA2_NISTP521:
                     names = cannedKeyAlgoX509Ecc521Names;
-                    namesSz = cannedKeyAlgoX509Ecc521NamesSz;
                     break;
                 #endif
                 default:
@@ -12192,6 +13375,8 @@ static int BuildUserAuthRequestEccCert(WOLFSSH* ssh,
             }
 
             if (ret == WS_SUCCESS) {
+                namesSz = (word32)WSTRLEN(names);
+
                 c32toa(rSz + rPad + sSz + sPad + namesSz+ LENGTH_SZ * 4,
                         output + begin);
                 begin += LENGTH_SZ;
@@ -12242,7 +13427,168 @@ static int BuildUserAuthRequestEccCert(WOLFSSH* ssh,
 #endif /* WOLFSSH_NO_ECDSA */
 
 
-#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA)
+#ifndef WOLFSSH_NO_ED25519
+
+static int PrepareUserAuthRequestEd25519(WOLFSSH* ssh, word32* payloadSz,
+        const WS_UserAuthData* authData, WS_KeySignature* keySig)
+{
+    int ret = WS_SUCCESS;
+
+    WLOG(WS_LOG_DEBUG, "Entering PrepareUserAuthRequestEd25519()");
+    if (ssh == NULL || payloadSz == NULL || authData == NULL || keySig == NULL)
+        ret = WS_BAD_ARGUMENT;
+
+    if (ret == WS_SUCCESS)
+        ret = wc_ed25519_init_ex(&keySig->ks.ed25519.key,
+                keySig->heap, INVALID_DEVID);
+
+    if (ret == 0) {
+        word32 idx = 0;
+        #ifdef WOLFSSH_AGENT
+        if (ssh->agentEnabled) {
+            /* XXX: Pending */
+        }
+        else
+        #endif
+        {
+            ret = GetOpenSshKey(keySig,
+                    authData->sf.publicKey.privateKey,
+                    authData->sf.publicKey.privateKeySz, &idx);
+        }
+    }
+
+    if (ret == WS_SUCCESS) {
+        if (authData->sf.publicKey.hasSignature) {
+            int sigSz = wc_ed25519_sig_size(&keySig->ks.ed25519.key);
+
+            if (sigSz >= 0) {
+                *payloadSz += (LENGTH_SZ * 3) + (word32)sigSz +
+                        authData->sf.publicKey.publicKeyTypeSz;
+                keySig->sigSz = sigSz;
+            }
+            else
+                ret = sigSz;
+        }
+    }
+
+    WLOG(WS_LOG_DEBUG,
+            "Leaving PrepareUserAuthRequestEd25519(), ret = %d", ret);
+    return ret;
+}
+
+
+static int BuildUserAuthRequestEd25519(WOLFSSH* ssh,
+        byte* output, word32* idx,
+        const WS_UserAuthData* authData,
+        const byte* sigStart, word32 sigStartIdx,
+        WS_KeySignature* keySig)
+{
+    word32 begin;
+    int ret = WS_SUCCESS;
+    byte* sig;
+    word32 sigSz = ED25519_SIG_SIZE;
+    byte* checkData = NULL;
+    word32 checkDataSz = 0;
+#ifndef WOLFSSH_SMALL_STACK
+    byte sig_s[ED25519_SIG_SIZE];
+#endif
+
+    WLOG(WS_LOG_DEBUG, "Entering BuildUserAuthRequestEd25519()");
+    if (ssh == NULL || output == NULL || idx == NULL || authData == NULL ||
+            sigStart == NULL || keySig == NULL) {
+        ret = WS_BAD_ARGUMENT;
+        return ret;
+    }
+
+#ifdef WOLFSSH_SMALL_STACK
+    sig = (byte*)WMALLOC(sigSz, keySig->heap, DYNTYPE_BUFFER);
+    if (sig == NULL)
+        ret = WS_MEMORY_E;
+#else
+    sig = sig_s;
+#endif
+
+    begin = *idx;
+
+    if (ret == WS_SUCCESS) {
+        checkDataSz = LENGTH_SZ + ssh->sessionIdSz + (begin - sigStartIdx);
+        checkData = (byte*)WMALLOC(checkDataSz, keySig->heap, DYNTYPE_TEMP);
+        if (checkData == NULL)
+            ret = WS_MEMORY_E;
+    }
+
+    if (ret == WS_SUCCESS) {
+        word32 i = 0;
+
+        c32toa(ssh->sessionIdSz, checkData + i);
+        i += LENGTH_SZ;
+        WMEMCPY(checkData + i, ssh->sessionId, ssh->sessionIdSz);
+        i += ssh->sessionIdSz;
+        WMEMCPY(checkData + i, sigStart, begin - sigStartIdx);
+    }
+
+    #ifdef WOLFSSH_AGENT
+    if (ssh->agentEnabled) {
+        /* XXX: Pending */
+    }
+    else
+    #endif
+    {
+        if (ret == WS_SUCCESS) {
+            WLOG(WS_LOG_INFO, "Signing with Ed25519.");
+            ret = wc_ed25519_sign_msg(checkData, checkDataSz,
+                    sig, &sigSz, &keySig->ks.ed25519.key);
+
+            if (ret != WS_SUCCESS) {
+                WLOG(WS_LOG_DEBUG, "SUAR: Bad ED25519 Sign");
+                ret = WS_ED25519_E;
+            }
+        }
+
+        if (ret == WS_SUCCESS) {
+            const char* name = cannedKeyAlgoEd25519Name;
+            word32 nameSz = (word32)WSTRLEN(name);
+
+            c32toa(LENGTH_SZ * 2 + nameSz + sigSz, output + begin);
+            begin += LENGTH_SZ;
+
+            c32toa(nameSz, output + begin);
+            begin += LENGTH_SZ;
+
+            WMEMCPY(output + begin, name, nameSz);
+            begin += nameSz;
+
+            c32toa(sigSz, output + begin);
+            begin += LENGTH_SZ;
+
+            WMEMCPY(output + begin, sig, sigSz);
+            begin += sigSz;
+        }
+    }
+
+    if (ret == WS_SUCCESS)
+        *idx = begin;
+
+    if (checkData != NULL) {
+        ForceZero(checkData, checkDataSz);
+        WFREE(checkData, keySig->heap, DYNTYPE_TEMP);
+    }
+
+#ifdef WOLFSSH_SMALL_STACK
+    if (sig)
+        WFREE(sig, keySig->heap, DYNTYPE_BUFFER);
+#endif
+
+    WLOG(WS_LOG_DEBUG,
+            "Leaving BuildUserAuthRequestEd25519(), ret = %d", ret);
+    return ret;
+}
+
+#endif /* WOLFSSH_NO_ED25519 */
+
+
+#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA) \
+    || !defined(WOLFSSH_NO_ED25519)
 static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz,
         const WS_UserAuthData* authData, WS_KeySignature* keySig)
 {
@@ -12268,7 +13614,8 @@ static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz,
         #ifndef WOLFSSH_NO_RSA_SHA2_256
             algoId[algoIdSz++] = ID_RSA_SHA2_256;
         #endif
-        #ifndef WOLFSSH_NO_SSH_RSA_SHA1
+        #if !defined(WOLFSSH_NO_SSH_RSA_SHA1) \
+            && defined(WOLFSSH_NO_SHA1_SOFT_DISABLE)
             algoId[algoIdSz++] = ID_SSH_RSA;
         #endif
         }
@@ -12280,7 +13627,7 @@ static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz,
         matchId = MatchIdLists(WOLFSSH_ENDPOINT_CLIENT, algoId, algoIdSz,
                 ssh->peerSigId, ssh->peerSigIdSz);
         if (matchId == ID_UNKNOWN) {
-            ret = WS_MATCH_KEX_ALGO_E;
+            ret = WS_MATCH_KEY_ALGO_E;
         }
         keySig->keySigId = matchId;
         keySig->name = IdToName(matchId);
@@ -12325,6 +13672,12 @@ static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz,
                 break;
             #endif
             #endif
+            #ifndef WOLFSSH_NO_ED25519
+            case ID_ED25519:
+                ret = PrepareUserAuthRequestEd25519(ssh,
+                        payloadSz, authData, keySig);
+                break;
+            #endif
             default:
                 ret = WS_INVALID_ALGO_ID;
         }
@@ -12434,6 +13787,21 @@ static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh,
                     break;
                 #endif
                 #endif
+                #ifndef WOLFSSH_NO_ED25519
+                case ID_ED25519:
+                    c32toa(pk->publicKeyTypeSz, output + begin);
+                    begin += LENGTH_SZ;
+                    WMEMCPY(output + begin,
+                            pk->publicKeyType, pk->publicKeyTypeSz);
+                    begin += pk->publicKeyTypeSz;
+                    c32toa(pk->publicKeySz, output + begin);
+                    begin += LENGTH_SZ;
+                    WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz);
+                    begin += pk->publicKeySz;
+                    ret = BuildUserAuthRequestEd25519(ssh, output, &begin,
+                        authData, sigStart, sigStartIdx, keySig);
+                    break;
+                #endif
                 default:
                     ret = WS_INVALID_ALGO_ID;
             }
@@ -12486,6 +13854,7 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig)
     if (ret == WS_SUCCESS) {
         WMEMSET(keySig_ptr, 0, sizeof(WS_KeySignature));
         keySig_ptr->keySigId = ID_NONE;
+        keySig_ptr->heap = ssh->ctx->heap;
 
         if (ssh->ctx->userAuthCb != NULL) {
             WLOG(WS_LOG_DEBUG, "SUAR: Calling the userauth callback");
@@ -12655,7 +14024,7 @@ int SendUserAuthFailure(WOLFSSH* ssh, byte partialSuccess)
     byte* output;
     word32 idx;
     int ret = WS_SUCCESS;
-    int   authSz;
+    int   authSz = 0;
     char  authStr[MAX_AUTH_STRING];
 
     WLOG(WS_LOG_DEBUG, "Entering SendUserAuthFailure()");
@@ -13178,7 +14547,7 @@ int SendChannelEow(WOLFSSH* ssh, word32 peerChannelId)
     byte* output;
     const char* str = "eow@openssh.com";
     word32 idx;
-    word32 strSz;
+    word32 strSz = 0;
     int      ret = WS_SUCCESS;
     WOLFSSH_CHANNEL* channel = NULL;
 
@@ -13235,7 +14604,7 @@ int SendChannelExit(WOLFSSH* ssh, word32 peerChannelId, int status)
     byte* output;
     const char* str = "exit-status";
     word32 idx;
-    word32 strSz;
+    word32 strSz = 0;
     int      ret = WS_SUCCESS;
     WOLFSSH_CHANNEL* channel = NULL;
 
diff --git a/src/io.c b/src/io.c
index a3e6af1cd..fb25ec37d 100644
--- a/src/io.c
+++ b/src/io.c
@@ -1,6 +1,6 @@
 /* io.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/src/keygen.c b/src/keygen.c
index 46b0431ee..2fb4b3a98 100644
--- a/src/keygen.c
+++ b/src/keygen.c
@@ -1,6 +1,6 @@
 /* keygen.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/src/log.c b/src/log.c
index 2f66d5a9c..2c96c5d9d 100644
--- a/src/log.c
+++ b/src/log.c
@@ -1,6 +1,6 @@
 /* log.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/src/misc.c b/src/misc.c
index f78eb72a3..5e4579b8e 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -1,6 +1,6 @@
 /* misc.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/src/port.c b/src/port.c
index 035019080..b38d498a1 100644
--- a/src/port.c
+++ b/src/port.c
@@ -1,6 +1,6 @@
 /* port.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -476,7 +476,8 @@ int WS_DeleteFileA(const char* fileName, void* heap)
 
 #endif /* USE_WINDOWS_API WOLFSSH_SFTP WOLFSSH_SCP */
 
-#if defined(WOLFSSH_ZEPHYR) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
+#if !defined(NO_FILESYSTEM) && \
+    defined(WOLFSSH_ZEPHYR) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
 
 int wssh_z_fstat(const char *p, struct fs_dirent *b)
 {
diff --git a/src/ssh.c b/src/ssh.c
index 99edfd898..2f7def5fb 100644
--- a/src/ssh.c
+++ b/src/ssh.c
@@ -1,6 +1,6 @@
 /* ssh.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -75,7 +75,8 @@ int wolfSSH_Init(void)
 #ifdef WC_RNG_SEED_CB
     wc_SetSeed_Cb(wc_GenerateSeed);
 #endif
-#if defined(WOLFSSH_ZEPHYR) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
+#if !defined(NO_FILESYSTEM) && defined(WOLFSSH_ZEPHYR) && \
+        (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
     if (wssh_z_fds_init() != 0)
         ret = WS_CRYPTO_FAILED;
 #endif
@@ -93,7 +94,8 @@ int wolfSSH_Cleanup(void)
 
     if (wolfCrypt_Cleanup() != 0)
         ret = WS_CRYPTO_FAILED;
-#if defined(WOLFSSH_ZEPHYR) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
+#if !defined(NO_FILESYSTEM) && defined(WOLFSSH_ZEPHYR) && \
+        (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
     if (wssh_z_fds_cleanup() != 0)
         ret = WS_CRYPTO_FAILED;
 #endif
@@ -416,7 +418,7 @@ int wolfSSH_accept(WOLFSSH* ssh)
         return WS_BAD_ARGUMENT;
 
     /* clear want read/writes for retry */
-    if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE)
+    if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE || ssh->error == WS_AUTH_PENDING)
         ssh->error = 0;
 
     if (ssh->error != 0) {
@@ -1002,7 +1004,7 @@ int wolfSSH_shutdown(WOLFSSH* ssh)
 
     if (ssh != NULL && ssh->channelList == NULL) {
         WLOG(WS_LOG_DEBUG, "channel list was already removed");
-        ret = WS_SUCCESS;
+        ret = WS_CHANNEL_CLOSED;
     }
 
     WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_shutdown(), ret = %d", ret);
@@ -1691,7 +1693,7 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out,
         ret = WS_SUCCESS;
     }
     else {
-        WLOG(WS_LOG_DEBUG, "unable to identify key");
+        WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key");
         if (*out == NULL) {
             WFREE(newKey, heap, DYNTYPE_PRIVKEY);
         }
@@ -1748,7 +1750,7 @@ static int DoPemKey(const byte* in, word32 inSz, byte** out,
         ret = WS_SUCCESS;
     }
     else {
-        WLOG(WS_LOG_DEBUG, "unable to identify key");
+        WLOG(WS_LOG_DEBUG, "Unable to identify PEM key");
         if (*out == NULL) {
             WFREE(newKey, heap, DYNTYPE_PRIVKEY);
         }
@@ -1804,7 +1806,7 @@ static int DoOpenSshKey(const byte* in, word32 inSz, byte** out,
         ret = WS_SUCCESS;
     }
     else {
-        WLOG(WS_LOG_DEBUG, "unable to identify key");
+        WLOG(WS_LOG_DEBUG, "Unable to identify key");
         if (*out == NULL) {
             WFREE(newKey, heap, DYNTYPE_PRIVKEY);
         }
@@ -1899,18 +1901,15 @@ int wolfSSH_ReadKey_file(const char* name,
         ret = WS_BAD_FILE_E;
     }
     else {
-        if (WSTRNSTR((const char*)in,
-                    "ssh-rsa", inSz) == (const char*)in ||
-                WSTRNSTR((const char*)in,
-                    "ecdsa-sha2-nistp", inSz) == (const char*)in) {
+        if (WSTRNSTR((const char*)in, "ssh-rsa", inSz) == (const char*)in
+                || WSTRNSTR((const char*)in,
+                    "ecdsa-sha2-nistp", inSz) == (const char*)in
+                || WSTRNSTR((const char*)in,
+                    "ssh-ed25519", inSz) == (const char*)in) {
             *isPrivate = 0;
             format = WOLFSSH_FORMAT_SSH;
             in[inSz] = 0;
         }
-#if 0
-        else if (WSTRNSTR((const char*)in, PrivBeginOpenSSH, inSz) != NULL &&
-                WSTRNSTR((const char*)in, PrivEndOpenSSH, inSz) != NULL) {
-#endif
         else if (WSTRNSTR((const char*)in, PrivBeginOpenSSH, inSz) != NULL) {
             *isPrivate = 1;
             format = WOLFSSH_FORMAT_OPENSSH;
@@ -1939,6 +1938,296 @@ int wolfSSH_ReadKey_file(const char* name,
 
 #endif
 
+
+int wolfSSH_CTX_SetAlgoListKex(WOLFSSH_CTX* ctx, const char* list)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx) {
+        ctx->algoListKex = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_CTX_GetAlgoListKex(WOLFSSH_CTX* ctx)
+{
+    const char* list = NULL;
+
+    if (ctx) {
+        list = ctx->algoListKex;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_SetAlgoListKex(WOLFSSH* ssh, const char* list)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh) {
+        ssh->algoListKex = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_GetAlgoListKex(WOLFSSH* ssh)
+{
+    const char* list = NULL;
+
+    if (ssh) {
+        list = ssh->algoListKex;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_CTX_SetAlgoListKey(WOLFSSH_CTX* ctx, const char* list)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx) {
+        ctx->algoListKey = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_CTX_GetAlgoListKey(WOLFSSH_CTX* ctx)
+{
+    const char* list = NULL;
+
+    if (ctx) {
+        list = ctx->algoListKey;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_SetAlgoListKey(WOLFSSH* ssh, const char* list)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh) {
+        ssh->algoListKey = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_GetAlgoListKey(WOLFSSH* ssh)
+{
+    const char* list = NULL;
+
+    if (ssh) {
+        list = ssh->algoListKey;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_CTX_SetAlgoListCipher(WOLFSSH_CTX* ctx, const char* list)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx) {
+        ctx->algoListCipher = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_CTX_GetAlgoListCipher(WOLFSSH_CTX* ctx)
+{
+    const char* list = NULL;
+
+    if (ctx) {
+        list = ctx->algoListCipher;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_SetAlgoListCipher(WOLFSSH* ssh, const char* list)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh) {
+        ssh->algoListCipher = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_GetAlgoListCipher(WOLFSSH* ssh)
+{
+    const char* list = NULL;
+
+    if (ssh) {
+        list = ssh->algoListCipher;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_CTX_SetAlgoListMac(WOLFSSH_CTX* ctx, const char* list)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx) {
+        ctx->algoListMac = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_CTX_GetAlgoListMac(WOLFSSH_CTX* ctx)
+{
+    const char* list = NULL;
+
+    if (ctx) {
+        list = ctx->algoListMac;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_SetAlgoListMac(WOLFSSH* ssh, const char* list)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh) {
+        ssh->algoListMac = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_GetAlgoListMac(WOLFSSH* ssh)
+{
+    const char* list = NULL;
+
+    if (ssh) {
+        list = ssh->algoListMac;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_CTX_SetAlgoListKeyAccepted(WOLFSSH_CTX* ctx, const char* list)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx) {
+        ctx->algoListKeyAccepted = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_CTX_GetAlgoListKeyAccepted(WOLFSSH_CTX* ctx)
+{
+    const char* list = NULL;
+
+    if (ctx) {
+        list = ctx->algoListKeyAccepted;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_SetAlgoListKeyAccepted(WOLFSSH* ssh, const char* list)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh) {
+        ssh->algoListKeyAccepted = list;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_GetAlgoListKeyAccepted(WOLFSSH* ssh)
+{
+    const char* list = NULL;
+
+    if (ssh) {
+        list = ssh->algoListKeyAccepted;
+    }
+
+    return list;
+}
+
+
+int wolfSSH_CheckAlgoName(const char* name)
+{
+    int ret = WS_INVALID_ALGO_ID;
+
+    if (name) {
+        word32 nameSz = (word32)WSTRLEN(name);
+        if (NameToId(name, nameSz) != ID_UNKNOWN) {
+            ret = WS_SUCCESS;
+        }
+    }
+
+    return ret;
+}
+
+
+const char* wolfSSH_QueryKex(word32* index)
+{
+    return NameByIndexType(TYPE_KEX, index);
+}
+
+
+const char* wolfSSH_QueryKey(word32* index)
+{
+    return NameByIndexType(TYPE_KEY, index);
+}
+
+
+const char* wolfSSH_QueryCipher(word32* index)
+{
+    return NameByIndexType(TYPE_CIPHER, index);
+}
+
+
+const char* wolfSSH_QueryMac(word32* index)
+{
+    return NameByIndexType(TYPE_MAC, index);
+}
+
+
 int wolfSSH_CTX_SetBanner(WOLFSSH_CTX* ctx,
                           const char* newBanner)
 {
@@ -2549,6 +2838,444 @@ int wolfSSH_ChannelGetEof(WOLFSSH_CHANNEL* channel)
     return eof;
 }
 
+static const char* HashNameForId(byte id)
+{
+    enum wc_HashType hash = HashForId(id);
+
+    if (hash == WC_HASH_TYPE_SHA)
+        return "SHA-1";
+
+    if (hash == WC_HASH_TYPE_SHA256)
+        return "SHA-256";
+
+    if (hash == WC_HASH_TYPE_SHA384)
+        return "SHA-384";
+
+    if (hash == WC_HASH_TYPE_SHA512)
+        return "SHA-512";
+
+    return "";
+}
+
+static const char* CurveNameForId(byte id)
+{
+#if !defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_ECDH)
+    switch (wcPrimeForId(id)) {
+        case ECC_SECP256R1:
+            return "nistp256";
+
+        case ECC_SECP384R1:
+            return "nistp384";
+
+        case ECC_SECP521R1:
+            return "nistp521";
+
+#ifdef HAVE_CURVE25519
+        case ECC_X25519:
+            return "Curve25519";
+#endif
+    }
+#endif
+    return "";
+}
+
+static const char* CipherNameForId(byte id)
+{
+    switch (id) {
+        case ID_AES128_CBC:
+            return "AES-128 CBC";
+
+        case ID_AES192_CBC:
+            return "AES-192 CBC";
+
+        case ID_AES256_CBC:
+            return "AES-256 CBC";
+
+        case ID_AES128_CTR:
+            return "AES-128 SDCTR";
+
+        case ID_AES192_CTR:
+            return "AES-192 SDCTR";
+
+        case ID_AES256_CTR:
+            return "AES-256 SDCTR";
+
+        case ID_AES128_GCM:
+            return "AES-128 GCM";
+
+        case ID_AES192_GCM:
+            return "AES-192 GCM";
+
+        case ID_AES256_GCM:
+            return "AES-256 GCM";
+    }
+
+    return "";
+}
+
+static const char* MacNameForId(byte macid, byte cipherid)
+{
+    if (macid != ID_NONE) {
+        switch (macid) {
+            case ID_HMAC_SHA1:
+                return "HMAC-SHA-1";
+
+            case ID_HMAC_SHA1_96:
+                return "HMAC-SHA-1-96";
+
+            case ID_HMAC_SHA2_256:
+                return "HMAC-SHA-256";
+        }
+    }
+    else {
+        switch (cipherid) {
+            case ID_AES128_GCM:
+                return "AES128 GCM (in ETM mode)";
+
+            case ID_AES192_GCM:
+                return "AES192 GCM (in ETM mode)";
+
+            case ID_AES256_GCM:
+                return "AES256 GCM (in ETM mode)";
+        }
+    }
+
+    return "";
+}
+
+size_t wolfSSH_GetText(WOLFSSH *ssh, WS_Text id, char *str, size_t strSz)
+{
+    int ret = 0;
+
+#ifndef WOLFSSH_NO_DH
+    static const char standard_dh_format[] =
+        "%d-bit Diffie-Hellman with standard group %d";
+#endif
+
+    if (!ssh || str == NULL || strSz <= 0)
+        return 0;
+
+    switch (id) {
+        case WOLFSSH_TEXT_KEX_HASH:
+            ret = WSNPRINTF(str, strSz, "%s", HashNameForId(ssh->kexId));
+            break;
+
+        case WOLFSSH_TEXT_KEX_CURVE:
+            ret = WSNPRINTF(str, strSz, "%s", CurveNameForId(ssh->kexId));
+            break;
+
+        case WOLFSSH_TEXT_CRYPTO_IN_CIPHER:
+            ret = WSNPRINTF(str, strSz, "%s",
+                CipherNameForId(ssh->peerEncryptId));
+            break;
+
+        case WOLFSSH_TEXT_CRYPTO_OUT_CIPHER:
+            ret = WSNPRINTF(str, strSz, "%s", CipherNameForId(ssh->encryptId));
+            break;
+
+        case WOLFSSH_TEXT_CRYPTO_IN_MAC:
+            ret = WSNPRINTF(str, strSz, "%s", MacNameForId(ssh->peerMacId,
+                ssh->peerEncryptId));
+            break;
+
+        case WOLFSSH_TEXT_CRYPTO_OUT_MAC:
+            ret = WSNPRINTF(str, strSz, "%s", MacNameForId(ssh->macId,
+                ssh->encryptId));
+            break;
+
+        case WOLFSSH_TEXT_KEX_ALGO:
+            switch (ssh->kexId) {
+                case ID_ECDH_SHA2_NISTP256:
+                case ID_ECDH_SHA2_NISTP384:
+                case ID_ECDH_SHA2_NISTP521:
+                case ID_ECDH_SHA2_ED25519:
+                case ID_ECDH_SHA2_ED25519_LIBSSH:
+            #ifndef WOLFSSH_NO_CURVE25519_SHA256
+                case ID_CURVE25519_SHA256:
+            #endif
+                    ret = WSNPRINTF(str, strSz, "%s", "ECDH");
+                    break;
+
+            #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
+                case ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256:
+                    ret = WSNPRINTF(str, strSz, "%s", "Kyber1");
+                    break;
+            #endif
+
+            #ifndef WOLFSSH_NO_DH
+                case ID_DH_GROUP1_SHA1:
+                    ret = WSNPRINTF(str, strSz, standard_dh_format,
+                        ssh->primeGroupSz*8, 1);
+                    break;
+
+                case ID_DH_GROUP14_SHA1:
+                case ID_DH_GROUP14_SHA256:
+                    ret = WSNPRINTF(str, strSz, standard_dh_format,
+                        ssh->primeGroupSz*8, 14);
+                    break;
+
+                case ID_DH_GEX_SHA256:
+                    ret = WSNPRINTF(str, strSz,
+                        "%d-bit Diffie-Hellman with server-supplied group",
+                        ssh->primeGroupSz*8);
+                    break;
+            #endif /* !WOLFSSH_NO_DH */
+
+                case ID_EXTINFO_S:
+                    ret = WSNPRINTF(str, strSz, "Server extensions KEX");
+                    break;
+
+                case ID_EXTINFO_C:
+                    ret = WSNPRINTF(str, strSz, "Client extensions KEX");
+                    break;
+
+            }
+            break;
+    }
+
+    return ret < 0 ? 0 : (size_t)ret;
+}
+
+void wolfSSH_SetKeyingCompletionCb(WOLFSSH_CTX* ctx, WS_CallbackKeyingCompletion cb)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetKeyingCompletionCb()");
+
+    if (ctx)
+        ctx->keyingCompletionCb = cb;
+}
+
+void wolfSSH_SetKeyingCompletionCbCtx(WOLFSSH* ssh, void* ctx)
+{
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetKeyingCompletionCbCtx()");
+
+    if (ssh)
+        ssh->keyingCompletionCtx = ctx;
+}
+
+
+WS_SessionType wolfSSH_ChannelGetSessionType(const WOLFSSH_CHANNEL* channel)
+{
+    WS_SessionType type = WOLFSSH_SESSION_UNKNOWN;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelGetType()");
+
+    if (channel) {
+        type = (WS_SessionType)channel->sessionType;
+    }
+
+    return type;
+}
+
+
+const char* wolfSSH_ChannelGetSessionCommand(const WOLFSSH_CHANNEL* channel)
+{
+    const char* cmd = NULL;
+
+    WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChannelGetCommand()");
+
+    if (channel) {
+        cmd = channel->command;
+    }
+
+    return cmd;
+}
+
+
+int wolfSSH_CTX_SetChannelOpenCb(WOLFSSH_CTX* ctx, WS_CallbackChannelOpen cb)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx != NULL) {
+        ctx->channelOpenCb = cb;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+int wolfSSH_CTX_SetChannelOpenRespCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelOpen confCb, WS_CallbackChannelOpen failCb)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx != NULL) {
+        ctx->channelOpenConfCb = confCb;
+        ctx->channelOpenFailCb = failCb;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+int wolfSSH_CTX_SetChannelReqShellCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelReq cb)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx != NULL) {
+        ctx->channelReqShellCb = cb;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+int wolfSSH_CTX_SetChannelReqExecCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelReq cb)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx != NULL) {
+        ctx->channelReqExecCb = cb;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+int wolfSSH_CTX_SetChannelReqSubsysCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelReq cb)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx != NULL) {
+        ctx->channelReqSubsysCb = cb;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+int wolfSSH_SetChannelOpenCtx(WOLFSSH* ssh, void* ctx)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh != NULL) {
+        ssh->channelOpenCtx = ctx;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+void* wolfSSH_GetChannelOpenCtx(WOLFSSH* ssh)
+{
+    void* ctx = NULL;
+
+    if (ssh != NULL) {
+        ctx = ssh->channelOpenCtx;
+    }
+
+    return ctx;
+}
+
+
+int wolfSSH_SetChannelReqCtx(WOLFSSH* ssh, void* ctx)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh != NULL) {
+        ssh->channelReqCtx = ctx;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+void* wolfSSH_GetChannelReqCtx(WOLFSSH* ssh)
+{
+    void* ctx = NULL;
+
+    if (ssh != NULL) {
+        ctx = ssh->channelReqCtx;
+    }
+
+    return ctx;
+}
+
+
+int wolfSSH_CTX_SetChannelEofCb(WOLFSSH_CTX* ctx, WS_CallbackChannelEof cb)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx != NULL) {
+        ctx->channelEofCb = cb;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+int wolfSSH_SetChannelEofCtx(WOLFSSH* ssh, void* ctx)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh != NULL) {
+        ssh->channelEofCtx = ctx;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+void* wolfSSH_GetChannelEofCtx(WOLFSSH* ssh)
+{
+    void* ctx = NULL;
+
+    if (ssh != NULL) {
+        ctx = ssh->channelEofCtx;
+    }
+
+    return ctx;
+}
+
+
+int wolfSSH_CTX_SetChannelCloseCb(WOLFSSH_CTX* ctx, WS_CallbackChannelClose cb)
+{
+    int ret = WS_SSH_CTX_NULL_E;
+
+    if (ctx != NULL) {
+        ctx->channelCloseCb = cb;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+int wolfSSH_SetChannelCloseCtx(WOLFSSH* ssh, void* ctx)
+{
+    int ret = WS_SSH_NULL_E;
+
+    if (ssh != NULL) {
+        ssh->channelCloseCtx = ctx;
+        ret = WS_SUCCESS;
+    }
+
+    return ret;
+}
+
+
+void* wolfSSH_GetChannelCloseCtx(WOLFSSH* ssh)
+{
+    void* ctx = NULL;
+
+    if (ssh != NULL) {
+        ctx = ssh->channelCloseCtx;
+    }
+
+    return ctx;
+}
+
 
 #if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \
     !defined(NO_WOLFSSH_SERVER)
diff --git a/src/wolfscp.c b/src/wolfscp.c
index d51912cfc..26beaa9c7 100644
--- a/src/wolfscp.c
+++ b/src/wolfscp.c
@@ -1,6 +1,6 @@
 /* wolfscp.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -57,6 +57,8 @@ static int ScpPushDir(void *fs, ScpSendCtx* ctx, const char* path, void* heap);
 static int ScpPopDir(void *fs, ScpSendCtx* ctx, void* heap);
 #endif
 
+#define WOLFSSH_MODE_MASK 0777
+
 const char scpError[] = "scp error: %s, %d";
 const char scpState[] = "scp state: %s";
 
@@ -96,7 +98,8 @@ int DoScpSink(WOLFSSH* ssh)
 
                 ssh->scpConfirm = ssh->ctx->scpRecvCb(ssh,
                         WOLFSSH_SCP_NEW_REQUEST, ssh->scpBasePath,
-                        NULL, 0, 0, 0, 0, NULL, 0, 0, wolfSSH_GetScpRecvCtx(ssh));
+                        NULL, 0, 0, 0, 0, NULL, 0, 0,
+                        wolfSSH_GetScpRecvCtx(ssh));
                 continue;
 
             case SCP_RECEIVE_MESSAGE:
@@ -186,12 +189,9 @@ int DoScpSink(WOLFSSH* ssh)
                         ssh->scpATime, ssh->scpFileSz, ssh->scpFileBuffer,
                         ssh->scpFileBufferSz, ssh->scpFileOffset,
                         wolfSSH_GetScpRecvCtx(ssh));
-
                 ssh->scpFileOffset += ssh->scpFileBufferSz;
 
-                /* shrink and reset recv buffer */
-                WFREE(ssh->scpFileBuffer, ssh->ctx->heap, DYNTYPE_BUFFER);
-                ssh->scpFileBuffer = NULL;
+                /* reset recv buffer */
                 ssh->scpFileBufferSz = 0;
 
                 if (ssh->scpConfirm != WS_SCP_CONTINUE) {
@@ -315,7 +315,8 @@ static int SendScpFileHeader(WOLFSSH* ssh)
 #ifndef WSCPFILEHDR
     WMEMSET(buf, 0, sizeof(buf));
     WSNPRINTF(buf, sizeof(buf), "C%04o %u %s\n",
-              ssh->scpFileMode, ssh->scpFileSz, ssh->scpFileName);
+              ssh->scpFileMode & WOLFSSH_MODE_MASK,
+              ssh->scpFileSz, ssh->scpFileName);
     filehdr = buf;
 #else
     filehdr = WSCPFILEHDR(ssh);
@@ -350,8 +351,9 @@ static int SendScpEnterDirectory(WOLFSSH* ssh)
 
     WMEMSET(buf, 0, sizeof(buf));
 
-    WSNPRINTF(buf, sizeof(buf), "D%04o 0 %s\n", ssh->scpFileMode,
-              ssh->scpFileName);
+    WSNPRINTF(buf, sizeof(buf), "D%04o 0 %s\n",
+            ssh->scpFileMode & WOLFSSH_MODE_MASK,
+            ssh->scpFileName);
 
     bufSz = (int)WSTRLEN(buf);
 
@@ -726,7 +728,11 @@ int DoScpRequest(WOLFSSH* ssh)
         /* Peer MUST send back a SSH_MSG_CHANNEL_CLOSE unless already
             sent*/
         ret = wolfSSH_stream_read(ssh, buf, 1);
-        if (ret != WS_EOF) {
+        if (ret == WS_SOCKET_ERROR_E || ret == WS_CHANNEL_CLOSED) {
+            WLOG(WS_LOG_DEBUG, scpState, "Peer hung up, but SCP is done");
+            ret = WS_SUCCESS;
+        }
+        else if (ret != WS_EOF) {
             WLOG(WS_LOG_DEBUG, scpState, "Did not receive EOF packet");
         }
         else {
@@ -1437,8 +1443,6 @@ int ReceiveScpMessage(WOLFSSH* ssh)
             break;
     }
 
-    WFREE(ssh->scpRecvMsg, ssh->ctx->heap, DYNTYPE_STRING);
-    ssh->scpRecvMsg = NULL;
     ssh->scpRecvMsgSz = 0;
 
     return ret;
@@ -1447,34 +1451,28 @@ int ReceiveScpMessage(WOLFSSH* ssh)
 int ReceiveScpFile(WOLFSSH* ssh)
 {
     int partSz, ret = WS_SUCCESS;
-    byte* part;
 
     if (ssh == NULL)
         return WS_BAD_ARGUMENT;
 
+    /* We don't want to over-read the buffer. The file data is
+     * terminated by the sender with a nul which is checked later. */
     partSz = min(ssh->scpFileSz - ssh->scpFileOffset, DEFAULT_SCP_BUFFER_SZ);
 
     /* don't even bother reading if read size is 0 */
     if (partSz == 0) return ret;
 
-    part = (byte*)WMALLOC(partSz, ssh->ctx->heap, DYNTYPE_BUFFER);
-    if (part == NULL)
-        ret = WS_MEMORY_E;
+    if (ssh->scpFileBuffer == NULL) {
+        ssh->scpFileBuffer = (byte*)WMALLOC(DEFAULT_SCP_BUFFER_SZ,
+                ssh->ctx->heap, DYNTYPE_BUFFER);
+        if (ssh->scpFileBuffer == NULL)
+            ret = WS_MEMORY_E;
+    }
 
     if (ret == WS_SUCCESS) {
-        WMEMSET(part, 0, partSz);
-
-        ret = wolfSSH_stream_read(ssh, part, partSz);
+        ret = wolfSSH_stream_read(ssh, ssh->scpFileBuffer, partSz);
         if (ret > 0) {
-            if (ssh->scpFileBuffer != NULL) {
-                WFREE(ssh->scpFileBuffer, ssh->ctx->heap, DYNTYPE_BUFFER);
-                ssh->scpFileBuffer = NULL;
-                ssh->scpFileBufferSz = 0;
-            }
-            ssh->scpFileBuffer = part;
             ssh->scpFileBufferSz = ret;
-        } else {
-            WFREE(part, ssh->ctx->heap, DYNTYPE_BUFFER);
         }
     }
 
@@ -1657,70 +1655,107 @@ int wolfSSH_SCP_connect(WOLFSSH* ssh, byte* cmd)
     return ret;
 }
 
-static int wolfSSH_SCP_cmd(WOLFSSH* ssh, const char* localName,
-        const char* remoteName, byte dir)
+
+static char* MakeScpCmd(const char* name, char dir, void* heap)
+{
+    char* cmd;
+    int sz;
+
+    sz = WSNPRINTF(NULL, 0, "scp -%c %s", dir, name) + 1;
+    if (sz <= 0) {
+        return NULL;
+    }
+    cmd = (char*)WMALLOC(sz, heap, DYNTYPE_STRING);
+    if (cmd == NULL) {
+        return NULL;
+    }
+    sz = WSNPRINTF(cmd, sz, "scp -%c %s", dir, name);
+    if (sz <= 0) {
+        WFREE(cmd, heap, DYNTYPE_STRING);
+        return NULL;
+    }
+
+    return cmd;
+}
+
+
+int wolfSSH_SCP_to(WOLFSSH* ssh, const char* src, const char* dst)
 {
-    char* cmd = NULL;
-    word32 remoteNameSz, cmdSz;
     int ret = WS_SUCCESS;
 
-    if (ssh == NULL || localName == NULL || remoteName == NULL)
-        return WS_BAD_ARGUMENT;
+    /* dst is passed to the server in the scp -t command */
+    /* src is used locally to fopen and read for copy to */
 
-    if (dir != 't' && dir != 'f')
+    if (ssh == NULL || src == NULL || dst == NULL)
         return WS_BAD_ARGUMENT;
 
-    remoteNameSz = (word32)WSTRLEN(remoteName);
-    cmdSz = remoteNameSz + (word32)WSTRLEN("scp -5 ") + 1;
-    cmd = (char*)WMALLOC(cmdSz, ssh->ctx->heap, DYNTYPE_STRING);
-
-    /* Need to set up the context for the local interaction callback. */
+    if (ssh->scpState == SCP_SETUP) {
+        char* cmd = MakeScpCmd(dst, 't', ssh->ctx->heap);
+        if (cmd == NULL) {
+            WLOG(WS_LOG_SCP, "Cannot allocate scp command");
+            ssh->error = WS_MEMORY_E;
+            return WS_ERROR;
+        }
 
-    if (cmd != NULL) {
-        WSNPRINTF(cmd, cmdSz, "scp -%c %s", dir, remoteName);
-        ssh->scpBasePath = localName;
+        ssh->scpBasePath = src;
         ret = wolfSSH_SCP_connect(ssh, (byte*)cmd);
         if (ret == WS_SUCCESS) {
-            if (dir == 't') {
-                ssh->scpState = SCP_SOURCE_BEGIN;
-                ssh->scpRequestState = SCP_SOURCE;
-                ret = DoScpSource(ssh);
-            }
-            else {
-                cmdSz = (word32)WSTRLEN(localName);
-                ret = ParseBasePathHelper(ssh, cmdSz);
-                if (ret == WS_SUCCESS) {
-                    ssh->scpState = SCP_SINK_BEGIN;
-                    ssh->scpRequestState = SCP_SINK;
-                    ret = DoScpSink(ssh);
-                }
-            }
+            ssh->scpState = SCP_SOURCE_BEGIN;
+            ssh->scpRequestState = SCP_SOURCE;
+        }
+        if (cmd) {
+            WFREE(cmd, ssh->ctx->heap, DYNTYPE_STRING);
         }
-        WFREE(cmd, ssh->ctx->heap, DYNTYPE_STRING);
     }
-    else {
-        WLOG(WS_LOG_SCP, "Cannot build scp command");
-        ssh->error = WS_MEMORY_E;
-        ret = WS_ERROR;
+    if (ssh->scpState != SCP_SETUP) {
+        if (ret == WS_SUCCESS) {
+            ret = DoScpSource(ssh);
+        }
     }
 
     return ret;
 }
 
 
-int wolfSSH_SCP_to(WOLFSSH* ssh, const char* src, const char* dst)
-{
-    return wolfSSH_SCP_cmd(ssh, src, dst, 't');
-    /* dst is passed to the server in the scp -t command */
-    /* src is used locally to fopen and read for copy to */
-}
-
-
 int wolfSSH_SCP_from(WOLFSSH* ssh, const char* src, const char* dst)
 {
-    return wolfSSH_SCP_cmd(ssh, dst, src, 'f');
+    int ret = WS_SUCCESS;
+
     /* src is passed to the server in the scp -f command */
     /* dst is used locally to fopen and write for copy from */
+
+    if (ssh == NULL || src == NULL || dst == NULL)
+        return WS_BAD_ARGUMENT;
+
+    if (ssh->scpState == SCP_SETUP) {
+        char* cmd = MakeScpCmd(src, 'f', ssh->ctx->heap);
+        if (cmd == NULL) {
+            WLOG(WS_LOG_SCP, "Cannot allocate scp command");
+            ssh->error = WS_MEMORY_E;
+            return WS_ERROR;
+        }
+
+        ssh->scpBasePath = dst;
+        ret = wolfSSH_SCP_connect(ssh, (byte*)cmd);
+        if (ret == WS_SUCCESS) {
+            word32 srcSz = (word32)WSTRLEN(src);
+            ret = ParseBasePathHelper(ssh, srcSz);
+        }
+        if (ret == WS_SUCCESS) {
+            ssh->scpState = SCP_SINK_BEGIN;
+            ssh->scpRequestState = SCP_SINK;
+        }
+        if (cmd) {
+            WFREE(cmd, ssh->ctx->heap, DYNTYPE_STRING);
+        }
+    }
+    if (ssh->scpState != SCP_SETUP) {
+        if (ret == WS_SUCCESS) {
+            ret = DoScpSink(ssh);
+        }
+    }
+
+    return ret;
 }
 #endif /* ! NO_WOLFSSH_CLIENT */
 
@@ -2114,7 +2149,7 @@ static int GetFileStats(void *fs, ScpSendCtx* ctx, const char* fileName,
                         word64* mTime, word64* aTime, int* fileMode)
 {
     int ret = WS_SUCCESS;
-    
+
     WOLFSSH_UNUSED(fs);
 
     if (ctx == NULL || fileName == NULL || mTime == NULL ||
@@ -2630,6 +2665,7 @@ static int ScpProcessEntry(WOLFSSH* ssh, char* fileName, word64* mTime,
  *     WS_SCP_EXIT_DIR_FINAL       - return when recursive directory transfer
  *                                   is complete.
  *     WS_SCP_ABORT                - abort file transfer request
+ *     WS_BAD_FILE_E               - local file open error hit
  */
 int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest,
         char* fileName, word32 fileNameSz, word64* mTime, word64* aTime,
@@ -2668,7 +2704,7 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest,
 
                 WLOG(WS_LOG_ERROR, "scp: unable to open file, abort");
                 wolfSSH_SetScpErrorMsg(ssh, "unable to open file for reading");
-                ret = WS_SCP_ABORT;
+                ret = WS_BAD_FILE_E;
             }
 
             if (ret == WS_SUCCESS) {
@@ -2690,17 +2726,21 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest,
             if (ret == WS_SUCCESS)
                 ret = ExtractFileName(peerRequest, fileName, fileNameSz);
 
-            if (ret == WS_SUCCESS && sendCtx != NULL && sendCtx->fp != NULL) {
-                /* If it is an empty file, do not read. */
-                if (*totalFileSz != 0) {
-                    ret = (word32)WFREAD(ssh->fs, buf, 1, bufSz, sendCtx->fp);
-                    if (ret == 0) { /* handle unexpected case */
-                        ret = WS_EOF;
+            if (ret == WS_SUCCESS) {
+                if (sendCtx != NULL && sendCtx->fp != NULL) {
+                    /* If it is an empty file, do not read. */
+                    if (*totalFileSz != 0) {
+                        ret = (word32)WFREAD(ssh->fs, buf, 1, bufSz,
+                                             sendCtx->fp);
+                        if (ret == 0) { /* handle unexpected case */
+                            ret = WS_EOF;
+                        }
                     }
+                } else {
+                    WLOG(WS_LOG_ERROR,
+                                      "scp: error extracting file name, abort");
+                    ret = WS_SCP_ABORT;
                 }
-            } else {
-                WLOG(WS_LOG_ERROR, "scp: error extracting file name, abort");
-                ret = WS_SCP_ABORT;
             }
 
             /* keep fp open if no errors and transfer will continue */
@@ -2841,9 +2881,10 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath,
             recvBuffer->mode = fileMode;
             if (recvBuffer->status) {
                 if (recvBuffer->status(ssh, fileName, WOLFSSH_SCP_NEW_FILE,
-                            recvBuffer) != WS_SUCCESS)
+                            recvBuffer) != WS_SUCCESS) {
                     WLOG(WS_LOG_ERROR, "scp: status of new file failed, abort");
                     ret = WS_SCP_ABORT;
+                }
             }
             break;
 
@@ -2865,9 +2906,10 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath,
             recvBuffer->fileSz += sz;
             if (recvBuffer->status) {
                 if (recvBuffer->status(ssh, recvBuffer->name,
-                            WOLFSSH_SCP_FILE_PART, recvBuffer) != WS_SUCCESS)
+                            WOLFSSH_SCP_FILE_PART, recvBuffer) != WS_SUCCESS) {
                     WLOG(WS_LOG_ERROR, "scp: bad status, abort");
                     ret = WS_SCP_ABORT;
+                }
             }
             break;
 
@@ -2876,9 +2918,10 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath,
             recvBuffer->mTime = 0; /* @TODO set time if wanted */
             if (recvBuffer->status) {
                 if (recvBuffer->status(ssh, recvBuffer->name,
-                            WOLFSSH_SCP_FILE_DONE, recvBuffer) != WS_SUCCESS)
+                            WOLFSSH_SCP_FILE_DONE, recvBuffer) != WS_SUCCESS) {
                     WLOG(WS_LOG_ERROR, "scp: bad status, abort");
                     ret = WS_SCP_ABORT;
+                }
             }
             break;
 
diff --git a/src/wolfsftp.c b/src/wolfsftp.c
index b2ecf1fd6..2638e15ff 100644
--- a/src/wolfsftp.c
+++ b/src/wolfsftp.c
@@ -1,6 +1,6 @@
 /* wolfsftp.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -611,9 +611,9 @@ static int wolfSSH_SFTP_buffer_create(WOLFSSH* ssh, WS_SFTP_BUFFER* buffer,
 }
 
 
-/* Used to clear and free all states. Should be when returning errors or success
- * Must be called when free'ing the SFTP. For now static since only used in
- * wolfsftp.c
+/* Used to clear and free all states. Should be when returning errors or
+ * success. Must be called when free'ing the SFTP. For now static since only
+ * used in wolfsftp.c
  *
  * Note: Most cases an error will free all states and a success will free
  *       specific state ID.
@@ -1017,7 +1017,8 @@ static int SFTP_ServerRecvInit(WOLFSSH* ssh) {
 
     state = ssh->recvInitState;
     if (state == NULL) {
-        state = (WS_SFTP_RECV_INIT_STATE*)WMALLOC(sizeof(WS_SFTP_RECV_INIT_STATE),
+        state = (WS_SFTP_RECV_INIT_STATE*)WMALLOC(
+                sizeof(WS_SFTP_RECV_INIT_STATE),
                 ssh->ctx->heap, DYNTYPE_SFTP_STATE);
         if (state == NULL) {
             ssh->error = WS_MEMORY_E;
@@ -1029,7 +1030,8 @@ static int SFTP_ServerRecvInit(WOLFSSH* ssh) {
 
     switch (ssh->sftpState) {
         case SFTP_BEGIN:
-            ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer, RECV_INIT_SIZE);
+            ret = wolfSSH_SFTP_buffer_read(ssh,
+                    &state->buffer, RECV_INIT_SIZE);
             if (ret < 0) {
                 return WS_FATAL_ERROR;
             }
@@ -1039,8 +1041,8 @@ static int SFTP_ServerRecvInit(WOLFSSH* ssh) {
                 return WS_FATAL_ERROR;
             }
 
-            if (SFTP_GetSz(state->buffer.data, &sz,
-                        MSG_ID_SZ + UINT32_SZ, WOLFSSH_MAX_SFTP_RECV) != WS_SUCCESS) {
+            if (SFTP_GetSz(state->buffer.data, &sz, MSG_ID_SZ + UINT32_SZ,
+                        WOLFSSH_MAX_SFTP_RECV) != WS_SUCCESS) {
                 wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL);
                 return WS_BUFFER_E;
             }
@@ -1054,11 +1056,12 @@ static int SFTP_ServerRecvInit(WOLFSSH* ssh) {
             }
 
             ato32(state->buffer.data + LENGTH_SZ + MSG_ID_SZ, &version);
-            /* versions greater than WOLFSSH_SFTP_VERSION should fall back to ours
-             * versions less than WOLFSSH_SFTP_VERSION we should bail out on or
-             * implement a fall back */
+            /* versions greater than WOLFSSH_SFTP_VERSION should fall back to
+             * ours versions less than WOLFSSH_SFTP_VERSION we should bail out
+             * on or implement a fall back */
             if (version < WOLFSSH_SFTP_VERSION) {
-                WLOG(WS_LOG_SFTP, "Unsupported SFTP version, sending version 3");
+                WLOG(WS_LOG_SFTP,
+                        "Unsupported SFTP version, sending version 3");
                 wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL);
                 return WS_VERSION_E;
             }
@@ -1072,7 +1075,8 @@ static int SFTP_ServerRecvInit(WOLFSSH* ssh) {
         case SFTP_EXT:
             /* silently ignore extensions if not supported */
             if (state->extSz > 0) {
-                ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer, (int)state->extSz);
+                ret = wolfSSH_SFTP_buffer_read(ssh,
+                        &state->buffer, (int)state->extSz);
                 if (ret < 0) {
                     return WS_FATAL_ERROR;
                 }
@@ -1505,8 +1509,10 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh)
                     WLOG(WS_LOG_SFTP, "Unknown packet type [%d] received",
                             state->type);
                     if (wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_FAILURE,
-                                state->reqId, "Unknown/Unsupported packet type",
-                                "English", NULL, (word32*)&maxSz) != WS_SIZE_ONLY) {
+                                state->reqId,
+                                "Unknown/Unsupported packet type",
+                                "English", NULL, (word32*)&maxSz)
+                            != WS_SIZE_ONLY) {
                         wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV);
                         return WS_FATAL_ERROR;
                     }
@@ -1533,7 +1539,7 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh)
                     }
 
                     if (ret == WS_SUCCESS) {
-                        /* set send out buffer, "state->data" is taken by ssh  */
+                        /* set send out buffer, state data is taken by ssh */
                         wolfSSH_SFTP_RecvSetSend(ssh,
                             wolfSSH_SFTP_buffer_data(&state->buffer),
                             wolfSSH_SFTP_buffer_size(&state->buffer));
@@ -2042,11 +2048,12 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
     }
 
     {
-        WS_SFTP_FILEATRB fileAtr = { 0 };
+        WS_SFTP_FILEATRB fileAtr;
+        WMEMSET(&fileAtr, 0, sizeof(fileAtr));
         if (SFTP_GetAttributes(ssh->fs,
-                        dir, &fileAtr, 1, ssh->ctx->heap) == WS_SUCCESS) {
-            if ((fileAtr.per & FILEATRB_PER_MASK_TYPE) != FILEATRB_PER_FILE) {
-                WLOG(WS_LOG_SFTP, "Not a file");
+                        dir, &fileAtr, 0, ssh->ctx->heap) == WS_SUCCESS) {
+            if ((fileAtr.per & FILEATRB_PER_MASK_TYPE)
+                        != FILEATRB_PER_FILE) {
                 ssh->error = WS_SFTP_NOT_FILE_E;
 
                 res = naf;
@@ -2079,7 +2086,8 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
 
 #ifdef WOLFSSH_STOREHANDLE
     if (ret == WS_SUCCESS) {
-        if ((ret = SFTP_AddHandleNode(ssh, (byte*)&fd, sizeof(WFD), dir)) != WS_SUCCESS) {
+        if ((ret = SFTP_AddHandleNode(ssh, (byte*)&fd, sizeof(WFD), dir))
+                != WS_SUCCESS) {
             WLOG(WS_LOG_SFTP, "Unable to store handle");
             res = ier;
             if (wolfSSH_SFTP_CreateStatus(ssh, WOLFSSH_FTP_FAILURE, reqId, res,
@@ -2599,7 +2607,15 @@ static int SFTP_CreateLongName(WS_SFTPNAME* name)
         word32 tmp = atr->per;
 
         i = 0;
-        perm[i++] = (tmp & 0x4000)?'d':'-';
+        if (tmp & FILEATRB_PER_DIR) {
+            perm[i++] = 'd';
+        }
+        else if (tmp & FILEATRB_PER_LINK) {
+            perm[i++] = 'l';
+        }
+        else {
+            perm[i++] = '-';
+        }
         perm[i++] = (tmp & 0x100)?'r':'-';
         perm[i++] = (tmp & 0x080)?'w':'-';
         perm[i++] = (tmp & 0x040)?'x':'-';
@@ -2618,7 +2634,8 @@ static int SFTP_CreateLongName(WS_SFTPNAME* name)
     totalSz += name->fSz; /* size of file name */
     totalSz += 7; /* for all ' ' spaces */
     totalSz += 3 + 8 + 8; /* linkCount + uid + gid */
-    WSNPRINTF(sizeStr, sizeof(sizeStr) - 1, "%8lld", ((long long int)atr->sz[1] << 32) + (long long int)(atr->sz[0]));
+    WSNPRINTF(sizeStr, sizeof(sizeStr) - 1, "%8lld",
+            ((long long int)atr->sz[1] << 32) + (long long int)(atr->sz[0]));
     totalSz += (int)WSTRLEN(sizeStr);
 #else
     totalSz = name->fSz;
@@ -2642,11 +2659,45 @@ static int SFTP_CreateLongName(WS_SFTPNAME* name)
     return WS_SUCCESS;
 }
 
+#if defined(WOLFSSH_SFTP_NAME_READDIR)
+/* helper function that gets file information from reading directory.
+ * Internally uses SFTP_Name_readdir to delegate the work to the User
+ * Filesystem.
+ *
+ * returns WS_SUCCESS on success
+ */
+static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
+                                    char* dirName)
+{
+    WOLFSSH_UNUSED(dirName);
+    int res;
 
-#ifdef WOLFSSL_NUCLEUS
+    if (dir == NULL || ssh == NULL || out == NULL) {
+        return WS_BAD_ARGUMENT;
+    }
+
+    res = SFTP_Name_readdir(ssh->fs, dir, out);
+    if (res != WS_SUCCESS) {
+        return res;
+    }
+
+    if (out->fName == NULL) {
+        return WS_MEMORY_E;
+    }
+
+    /* Use attributes and fName to create long name */
+    if (SFTP_CreateLongName(out) != WS_SUCCESS) {
+        WLOG(WS_LOG_DEBUG, "Error creating long name for %s", out->fName);
+        WFREE(out->fName, out->heap, DYNTYPE_SFTP);
+        return WS_FATAL_ERROR;
+    }
+
+    return WS_SUCCESS;
+}
+
+#elif defined(WOLFSSL_NUCLEUS)
 /* For Nucleus port
  * helper function that gets file information from reading directory
- * @TODO allow user to override
  *
  * returns WS_SUCCESS on success
  */
@@ -2687,8 +2738,8 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
         special = 1;
     }
 
-    /* use long name on Nucleus because sfname has only the file name and in all
-     * caps */
+    /* use long name on Nucleus because sfname has only the file name and in
+     * all caps */
     sz = (int)WSTRLEN(dir->lfname);
     out->fName = (char*)WMALLOC(sz + 1, out->heap, DYNTYPE_SFTP);
     if (out->fName == NULL) {
@@ -2756,7 +2807,6 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
 #elif defined(FREESCALE_MQX)
 /* Freescale MQX 4.2
   * helper function that gets file information from reading directory
-  * @TODO allow user to override
   *
   * returns WS_SUCCESS on success */
 static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
@@ -2835,7 +2885,6 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
 #elif defined(USE_WINDOWS_API)
 
 /* helper function that gets file information from reading directory
-* @TODO allow user to override
 *
 * returns WS_SUCCESS on success
 */
@@ -3066,7 +3115,6 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
 #else
 
 /* helper function that gets file information from reading directory
- * @TODO allow user to override
  *
  * returns WS_SUCCESS on success
  */
@@ -3113,7 +3161,7 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out,
             return WS_FATAL_ERROR;
         }
 
-        if (SFTP_GetAttributes(ssh->fs, s, &out->atrb, 0, ssh->ctx->heap)
+        if (SFTP_GetAttributes(ssh->fs, s, &out->atrb, 1, ssh->ctx->heap)
                 != WS_SUCCESS) {
             WLOG(WS_LOG_SFTP, "Unable to get attribute values for %s",
                     out->fName);
@@ -4195,7 +4243,8 @@ static WS_HANDLE_LIST* SFTP_GetHandleNode(WOLFSSH* ssh, byte* handle,
 
     /* for Nucleus need to find name from handle */
     while (cur != NULL) {
-        if(handleSz == cur->handleSz && WMEMCMP(handle, cur->handle, handleSz) == 0) {
+        if (handleSz == cur->handleSz
+                && WMEMCMP(handle, cur->handle, handleSz) == 0) {
             break; /* found handle */
         }
         cur = cur->next;
@@ -4256,7 +4305,8 @@ int SFTP_RemoveHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz)
 
     cur = SFTP_GetHandleNode(ssh, handle, handleSz);
     if (cur == NULL) {
-        WLOG(WS_LOG_SFTP, "Fatal Error! Trying to remove a handle that was not in the list");
+        WLOG(WS_LOG_SFTP,
+            "Fatal Error! Trying to remove a handle that was not in the list");
         return WS_FATAL_ERROR;
     }
 
@@ -4488,9 +4538,9 @@ int SFTP_GetAttributes(void* fs, const char* fileName, WS_SFTP_FILEATRB* atr,
 
     atr->flags |= WOLFSSH_FILEATRB_PERM;
     atr->per = 0555 |
-        (stats.dwFileAttributes | FILE_ATTRIBUTE_READONLY ? 0 : 0200);
-    atr->per |= (stats.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0x4000:
-        FILEATRB_PER_FILE;
+        ((stats.dwFileAttributes | FILE_ATTRIBUTE_READONLY) ? 0 : 0200);
+    atr->per |= ((stats.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+        ? FILEATRB_PER_DIR : FILEATRB_PER_FILE);
 
 #if 0
     /* @TODO handle the constellation of possible Windows FILETIMEs */
@@ -4944,7 +4994,8 @@ int wolfSSH_SFTP_RecvFSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
     }
 
     if (ret == WS_SUCCESS) {
-        if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, out) != WS_SUCCESS) {
+        if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, out)
+                != WS_SUCCESS) {
             return WS_FATAL_ERROR;
         }
         SFTP_SetAttributes(ssh, out + WOLFSSH_SFTP_HEADER, sz, &atr);
@@ -5035,7 +5086,8 @@ int wolfSSH_SFTP_RecvSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
         }
     }
     else {
-        if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, out) != WS_SUCCESS) {
+        if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, out)
+                != WS_SUCCESS) {
             WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER);
             return WS_FATAL_ERROR;
         }
@@ -5121,7 +5173,8 @@ int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
         }
     }
     else {
-        if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, out) != WS_SUCCESS) {
+        if (SFTP_SetHeader(ssh, reqId, WOLFSSH_FTP_ATTRS, sz, out)
+                != WS_SUCCESS) {
             WFREE(out, ssh->ctx->heap, DYNTYPE_BUFFER);
             return WS_FATAL_ERROR;
         }
@@ -5133,7 +5186,8 @@ int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
     return ret;
 }
 
-#if !defined(USE_WINDOWS_API) && !defined(WOLFSSH_ZEPHYR) && !defined(WOLFSSH_SFTP_SETMODE)
+#if !defined(USE_WINDOWS_API) && !defined(WOLFSSH_ZEPHYR) \
+    && !defined(WOLFSSH_SFTP_SETMODE)
 /* Set the files mode
  * return WS_SUCCESS on success */
 static int SFTP_SetMode(void* fs, char* name, word32 mode) {
@@ -5145,7 +5199,8 @@ static int SFTP_SetMode(void* fs, char* name, word32 mode) {
 }
 #endif
 
-#if !defined(USE_WINDOWS_API) && !defined(WOLFSSH_ZEPHYR) && !defined(WOLFSSH_SFTP_SETMODEHANDLE)
+#if !defined(USE_WINDOWS_API) && !defined(WOLFSSH_ZEPHYR) \
+    && !defined(WOLFSSH_SFTP_SETMODEHANDLE)
 /* Set the files mode
  * return WS_SUCCESS on success */
 static int SFTP_SetModeHandle(void* fs, WFD handle, word32 mode) {
@@ -5161,7 +5216,8 @@ static int SFTP_SetModeHandle(void* fs, WFD handle, word32 mode) {
 
 /* sets a files attributes
  * returns WS_SUCCESS on success */
-static int SFTP_SetFileAttributes(WOLFSSH* ssh, char* name, WS_SFTP_FILEATRB* atr)
+static int SFTP_SetFileAttributes(WOLFSSH* ssh,
+        char* name, WS_SFTP_FILEATRB* atr)
 {
     int ret = WS_SUCCESS;
 
@@ -5201,7 +5257,8 @@ static int SFTP_SetFileAttributes(WOLFSSH* ssh, char* name, WS_SFTP_FILEATRB* at
 
 /* sets a files attributes
  * returns WS_SUCCESS on success */
-static int SFTP_SetFileAttributesHandle(WOLFSSH* ssh, WFD handle, WS_SFTP_FILEATRB* atr)
+static int SFTP_SetFileAttributesHandle(WOLFSSH* ssh,
+        WFD handle, WS_SFTP_FILEATRB* atr)
 {
     int ret = WS_SUCCESS;
 
@@ -6153,7 +6210,8 @@ static WS_SFTPNAME* wolfSSH_SFTP_DoName(WOLFSSH* ssh)
                 count--;
                 if (tmp == NULL) {
                     /* error case free list and exit */
-                    WLOG(WS_LOG_SFTP, "Memory error when creating new name structure");
+                    WLOG(WS_LOG_SFTP,
+                            "Memory error when creating new name structure");
                     ret = WS_MEMORY_E;
                     break;
                 }
@@ -6170,7 +6228,8 @@ static WS_SFTPNAME* wolfSSH_SFTP_DoName(WOLFSSH* ssh)
                 }
                 tmp->fSz = sz;
                 if (sz > 0) {
-                    tmp->fName = (char*)WMALLOC(sz + 1, tmp->heap, DYNTYPE_SFTP);
+                    tmp->fName = (char*)WMALLOC(sz + 1,
+                            tmp->heap, DYNTYPE_SFTP);
                     if (tmp->fName == NULL) {
                         ret = WS_MEMORY_E;
                         break;
@@ -6439,8 +6498,9 @@ WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir)
         case STATE_LS_REALPATH:
             state->name = wolfSSH_SFTP_RealPath(ssh, dir);
             if (state->name == NULL) {
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE &&
-                    ssh->error != WS_REKEYING) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE
+                        && ssh->error != WS_REKEYING) {
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_LS);
                 }
                 return NULL;
@@ -6452,8 +6512,9 @@ WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir)
             if (wolfSSH_SFTP_OpenDir(ssh, (byte*)state->name->fName,
                         state->name->fSz) != WS_SUCCESS) {
                 WLOG(WS_LOG_SFTP, "Unable to open directory");
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE &&
-                    ssh->error != WS_REKEYING) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE
+                        && ssh->error != WS_REKEYING) {
                     wolfSSH_SFTPNAME_list_free(state->name); state->name = NULL;
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_LS);
                 }
@@ -6469,8 +6530,9 @@ WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir)
             if (wolfSSH_SFTP_GetHandle(ssh, state->handle, (word32*)&state->sz)
                     != WS_SUCCESS) {
                 WLOG(WS_LOG_SFTP, "Unable to get handle");
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE &&
-                    ssh->error != WS_REKEYING) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE
+                        && ssh->error != WS_REKEYING) {
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_LS);
                 }
                 return NULL;
@@ -6483,8 +6545,9 @@ WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir)
              * times so we have to assign to state->name later. */
             names = wolfSSH_SFTP_ReadDir(ssh, state->handle, state->sz);
             if (names == NULL) {
-                if (ssh->error == WS_WANT_READ || ssh->error == WS_WANT_WRITE ||
-                    ssh->error == WS_REKEYING) {
+                if (ssh->error == WS_WANT_READ
+                        || ssh->error == WS_WANT_WRITE
+                        || ssh->error == WS_REKEYING) {
                     return NULL;
                 }
                 WLOG(WS_LOG_SFTP, "Error reading directory");
@@ -6508,12 +6571,12 @@ WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir)
                     if (ssh->error == WS_WANT_READ
                         || ssh->error == WS_WANT_WRITE
                         || ssh->error == WS_REKEYING) {
-                        /* State does not change so we will get back to this case
-                         * clause in non-blocking mode. */
+                        /* State does not change so we will get back to this
+                         * case clause in non-blocking mode. */
                         return NULL;
                     }
                     WLOG(WS_LOG_SFTP, "Error reading directory");
-                    /* fall through because the handle should always be closed. */
+                    /* fall through, the handle should always be closed */
                 }
             }
 
@@ -6525,8 +6588,9 @@ WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir)
             if (wolfSSH_SFTP_Close(ssh, state->handle, state->sz)
                     != WS_SUCCESS) {
                 WLOG(WS_LOG_SFTP, "Error closing handle");
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE &&
-                    ssh->error != WS_REKEYING) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE
+                        && ssh->error != WS_REKEYING) {
                     wolfSSH_SFTPNAME_list_free(state->name);
                     state->name = NULL;
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_LS);
@@ -6576,7 +6640,8 @@ int wolfSSH_SFTP_CHMOD(WOLFSSH* ssh, char* n, char* oct)
         case STATE_CHMOD_GET:
             /* get current attributes of path */
             if ((ret = wolfSSH_SFTP_STAT(ssh, n, &state->atr)) != WS_SUCCESS) {
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE) {
                     break;
                 }
                 return ret;
@@ -6618,6 +6683,7 @@ static int SFTP_STAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr, byte type)
 {
     WS_SFTP_LSTAT_STATE* state = NULL;
     int ret;
+    int ret_fatal = 0;
     word32 localIdx;
 
     WLOG(WS_LOG_SFTP, "Entering SFTP_STAT()");
@@ -6659,6 +6725,7 @@ static int SFTP_STAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr, byte type)
                             ssh->error == WS_WANT_WRITE)
                         return WS_FATAL_ERROR;
                     else {
+                        ret_fatal = 1;
                         state->state = STATE_LSTAT_CLEANUP;
                         continue;
                     }
@@ -6677,13 +6744,14 @@ static int SFTP_STAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr, byte type)
                         return WS_FATAL_ERROR;
                     else {
                         state->state = STATE_LSTAT_CLEANUP;
+                        ret_fatal = 1;
                         continue;
                     }
                 }
 
                 state->state = STATE_LSTAT_CHECK_REQ_ID;
-                if (wolfSSH_SFTP_buffer_create(ssh, &state->buffer, ret) !=
-                        WS_SUCCESS) {
+                if (wolfSSH_SFTP_buffer_create(ssh, &state->buffer, ret)
+                        != WS_SUCCESS) {
                     ssh->error = WS_MEMORY_E;
                     return WS_FATAL_ERROR;
                 }
@@ -6758,7 +6826,10 @@ static int SFTP_STAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr, byte type)
                     WFREE(ssh->lstatState, ssh->ctx->heap, DYNTYPE_SFTP_STATE);
                     ssh->lstatState = NULL;
                 }
-                return WS_SUCCESS;
+                if(ret_fatal)
+                    return WS_FATAL_ERROR;
+                else
+                    return WS_SUCCESS;
 
             default:
                 WLOG(WS_LOG_SFTP, "Bad SFTP LSTAT state, program error");
@@ -6871,7 +6942,8 @@ int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
         /* send header and type specific data */
         case STATE_SET_ATR_SEND:
             if (wolfSSH_SFTP_buffer_send(ssh, &state->buffer) < 0) {
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE) {
                     ret = WS_FATAL_ERROR;
                     break;
                 }
@@ -6888,7 +6960,8 @@ int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
         case STATE_SET_ATR_GET:
             maxSz = SFTP_GetHeader(ssh, &state->reqId, &type, &state->buffer);
             if (maxSz <= 0) {
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE) {
                     ret = WS_FATAL_ERROR;
                     break;
                 }
@@ -6901,8 +6974,8 @@ int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
                 break;
             }
 
-            if (wolfSSH_SFTP_buffer_create(ssh, &state->buffer, maxSz) !=
-                    WS_SUCCESS) {
+            if (wolfSSH_SFTP_buffer_create(ssh, &state->buffer, maxSz)
+                    != WS_SUCCESS) {
                 ret = WS_MEMORY_E;
                 break;
             }
@@ -6915,7 +6988,8 @@ int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
             ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer,
                     wolfSSH_SFTP_buffer_size(&state->buffer));
             if (ret < 0) {
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE) {
                     ret = WS_FATAL_ERROR;
                     break;
                 }
@@ -7264,7 +7338,8 @@ int wolfSSH_SFTP_SendWritePacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
 
             case STATE_SEND_WRITE_DO_STATUS:
                 WLOG(WS_LOG_SFTP, "SFTP SEND_WRITE STATE: DO_STATUS");
-                status = wolfSSH_SFTP_DoStatus(ssh, state->reqId, &state->buffer);
+                status = wolfSSH_SFTP_DoStatus(ssh,
+                        state->reqId, &state->buffer);
                 if (status < 0) {
                     ret = WS_FATAL_ERROR;
                 }
@@ -7521,7 +7596,8 @@ int wolfSSH_SFTP_SendReadPacket(WOLFSSH* ssh, byte* handle, word32 handleSz,
                         return WS_FATAL_ERROR;
                     }
                     wolfSSH_SFTP_buffer_rewind(&state->buffer);
-                    ret = wolfSSH_SFTP_DoStatus(ssh, state->reqId, &state->buffer);
+                    ret = wolfSSH_SFTP_DoStatus(ssh,
+                            state->reqId, &state->buffer);
                     wolfSSH_SFTP_buffer_free(ssh, &state->buffer);
                     if (ret == WOLFSSH_FTP_OK || ret == WOLFSSH_FTP_EOF) {
                         WLOG(WS_LOG_SFTP, "OK or EOF found");
@@ -7742,7 +7818,8 @@ WS_SFTPNAME* wolfSSH_SFTP_ReadDir(WOLFSSH* ssh, byte* handle,
         case STATE_READDIR_NAME:
             name = wolfSSH_SFTP_DoName(ssh);
             if (name == NULL) {
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE) {
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_READDIR);
                 }
                 return NULL;
@@ -8003,7 +8080,8 @@ int wolfSSH_SFTP_Rename(WOLFSSH* ssh, const char* old, const char* nw)
                 /* add old name to the packet */
                 wolfSSH_SFTP_buffer_seek(&state->buffer, 0,
                         WOLFSSH_SFTP_HEADER);
-                wolfSSH_SFTP_buffer_c32toa(&state->buffer, (word32)WSTRLEN(old));
+                wolfSSH_SFTP_buffer_c32toa(&state->buffer,
+                        (word32)WSTRLEN(old));
                 WMEMCPY(wolfSSH_SFTP_buffer_data(&state->buffer) +
                     wolfSSH_SFTP_buffer_idx(&state->buffer),
                     (byte*)old, WSTRLEN(old));
@@ -8012,7 +8090,8 @@ int wolfSSH_SFTP_Rename(WOLFSSH* ssh, const char* old, const char* nw)
                     (word32)WSTRLEN(old));
 
                 /* add new name to the packet */
-                wolfSSH_SFTP_buffer_c32toa(&state->buffer, (word32)WSTRLEN(nw));
+                wolfSSH_SFTP_buffer_c32toa(&state->buffer,
+                        (word32)WSTRLEN(nw));
                 WMEMCPY(wolfSSH_SFTP_buffer_data(&state->buffer) +
                     wolfSSH_SFTP_buffer_idx(&state->buffer),
                     (byte*)nw, WSTRLEN(nw));
@@ -8051,7 +8130,8 @@ int wolfSSH_SFTP_Rename(WOLFSSH* ssh, const char* old, const char* nw)
             case STATE_RENAME_GET_HEADER:
                 WLOG(WS_LOG_SFTP, "SFTP RENAME STATE: GET_HEADER");
                 /* Get response */
-                ret = SFTP_GetHeader(ssh, &state->reqId, &type, &state->buffer);
+                ret = SFTP_GetHeader(ssh, &state->reqId,
+                        &type, &state->buffer);
                 if (ret <= 0) {
                     if (ssh->error == WS_WANT_READ ||
                             ssh->error == WS_WANT_WRITE) {
@@ -8179,7 +8259,8 @@ int wolfSSH_SFTP_Remove(WOLFSSH* ssh, char* f)
         case STATE_RM_LSTAT:
             /* check file is there to be removed */
             if ((ret = wolfSSH_SFTP_LSTAT(ssh, f, &atrb)) != WS_SUCCESS) {
-                if (ssh->error != WS_WANT_WRITE && ssh->error != WS_WANT_READ) {
+                if (ssh->error != WS_WANT_WRITE
+                        && ssh->error != WS_WANT_READ) {
                     WLOG(WS_LOG_SFTP, "Error verifying file");
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_RM);
                 }
@@ -8192,7 +8273,8 @@ int wolfSSH_SFTP_Remove(WOLFSSH* ssh, char* f)
             ret = SendPacketType(ssh, WOLFSSH_FTP_REMOVE, (byte*)f,
                     (word32)WSTRLEN(f));
             if (ret != WS_SUCCESS) {
-                if (ssh->error != WS_WANT_WRITE && ssh->error != WS_WANT_READ) {
+                if (ssh->error != WS_WANT_WRITE
+                        && ssh->error != WS_WANT_READ) {
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_RM);
                 }
                 return ret;
@@ -8203,7 +8285,8 @@ int wolfSSH_SFTP_Remove(WOLFSSH* ssh, char* f)
         case STATE_RM_GET:
             ret = SFTP_GetHeader(ssh, &state->reqId, &type, &state->buffer);
             if (ret <= 0 || type != WOLFSSH_FTP_STATUS) {
-                if (ssh->error != WS_WANT_WRITE && ssh->error != WS_WANT_READ) {
+                if (ssh->error != WS_WANT_WRITE
+                        && ssh->error != WS_WANT_READ) {
                     WLOG(WS_LOG_SFTP, "Unexpected packet type");
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_RM);
                 }
@@ -8221,7 +8304,8 @@ int wolfSSH_SFTP_Remove(WOLFSSH* ssh, char* f)
             ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer,
                     wolfSSH_SFTP_buffer_size(&state->buffer));
             if (ret < 0) {
-                if (ssh->error != WS_WANT_WRITE && ssh->error != WS_WANT_READ) {
+                if (ssh->error != WS_WANT_WRITE
+                        && ssh->error != WS_WANT_READ) {
                     WLOG(WS_LOG_SFTP, "Unexpected packet type");
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_RM);
                 }
@@ -8282,7 +8366,8 @@ int wolfSSH_SFTP_RMDIR(WOLFSSH* ssh, char* dir)
             ret = SendPacketType(ssh, WOLFSSH_FTP_RMDIR, (byte*)dir,
                 (word32)WSTRLEN(dir));
             if (ret != WS_SUCCESS) {
-                if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE) {
+                if (ssh->error != WS_WANT_READ
+                        && ssh->error != WS_WANT_WRITE) {
                     wolfSSH_SFTP_ClearState(ssh, STATE_ID_RMDIR);
                 }
                 return ret;
@@ -8515,8 +8600,8 @@ int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from,
                 NO_BREAK;
 
             case STATE_GET_LSTAT:
-                WLOG(WS_LOG_SFTP, "SFTP GET STATE: LSTAT");
-                ret = wolfSSH_SFTP_LSTAT(ssh, from, &state->attrib);
+                WLOG(WS_LOG_SFTP, "SFTP GET STATE: STAT");
+                ret = wolfSSH_SFTP_STAT(ssh, from, &state->attrib);
                 if (ret != WS_SUCCESS) {
                     if (ssh->error == WS_WANT_READ ||
                             ssh->error == WS_WANT_WRITE)
@@ -8526,7 +8611,9 @@ int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from,
                     continue;
                 }
                 if ((state->attrib.per & FILEATRB_PER_MASK_TYPE)
-                        != FILEATRB_PER_FILE) {
+                        != FILEATRB_PER_FILE
+                    && (state->attrib.per & FILEATRB_PER_MASK_TYPE)
+                        != FILEATRB_PER_LINK) {
                     WLOG(WS_LOG_SFTP, "Not a file");
                     ssh->error = WS_SFTP_NOT_FILE_E;
                     ret = WS_FATAL_ERROR;
@@ -8627,8 +8714,8 @@ int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from,
                         {
                             DWORD bytesWritten = 0;
                             if ((WriteFile(state->fileHandle, state->r, sz,
-                                         &bytesWritten, &state->offset) == 0) ||
-                                    ((DWORD)sz != bytesWritten))
+                                         &bytesWritten, &state->offset) == 0)
+                                    || ((DWORD)sz != bytesWritten))
                                 {
                                 WLOG(WS_LOG_SFTP, "Error writing to file");
                                 ssh->error = WS_BAD_FILE_E;
@@ -8767,7 +8854,8 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume,
                 WLOG(WS_LOG_SFTP, "SFTP PUT STATE: OPEN LOCAL");
             #ifndef USE_WINDOWS_API
                 {
-                    WS_SFTP_FILEATRB fileAtr = { 0 };
+                    WS_SFTP_FILEATRB fileAtr;
+                    WMEMSET(&fileAtr, 0, sizeof(fileAtr));
                     if (SFTP_GetAttributes(ssh->fs,
                                 from, &fileAtr, 1, ssh->ctx->heap)
                             == WS_SUCCESS) {
diff --git a/src/wolfterm.c b/src/wolfterm.c
index 659256838..691ff388a 100644
--- a/src/wolfterm.c
+++ b/src/wolfterm.c
@@ -1,6 +1,6 @@
 /* wolfterm.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/tests/api.c b/tests/api.c
index 0e483e204..ce6a02ab7 100644
--- a/tests/api.c
+++ b/tests/api.c
@@ -1,6 +1,6 @@
 /* api.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -28,6 +28,7 @@
     #include <wolfssl/options.h>
 #endif
 #include <wolfssl/wolfcrypt/wc_port.h>
+#include <wolfssh/port.h>
 
 #include <stdio.h>
 #include <wolfssh/ssh.h>
@@ -38,8 +39,9 @@
 
 #ifdef WOLFSSH_SFTP
     #define WOLFSSH_TEST_LOCKING
-    #define WOLFSSH_TEST_THREADING
-
+    #ifndef SINGLE_THREADED
+        #define WOLFSSH_TEST_THREADING
+    #endif
     #define WOLFSSH_TEST_SERVER
     #define WOLFSSH_TEST_ECHOSERVER
 #endif
@@ -120,13 +122,13 @@ char* myoptarg = NULL;
 #define AssertStrLE(x, y) AssertStr(x, y, <=,  >)
 
 #define AssertPtr(x, y, op, er) do {                                           \
-    PRAGMA_GCC_DIAG_PUSH;                                                      \
+    PRAGMA_GCC_DIAG_PUSH                                                       \
       /* remarkably, without this inhibition, */                               \
       /* the _Pragma()s make the declarations warn. */                         \
-    PRAGMA_GCC("GCC diagnostic ignored \"-Wdeclaration-after-statement\"");    \
+    PRAGMA_GCC("GCC diagnostic ignored \"-Wdeclaration-after-statement\"")     \
       /* inhibit "ISO C forbids conversion of function pointer */              \
       /* to object pointer type [-Werror=pedantic]" */                         \
-    PRAGMA_GCC("GCC diagnostic ignored \"-Wpedantic\"");                       \
+    PRAGMA_GCC("GCC diagnostic ignored \"-Wpedantic\"")                        \
     void* _x = (void*)(x);                                                     \
     void* _y = (void*)(y);                                                     \
     Assert(_x op _y, ("%s " #op " %s", #x, #y), ("%p " #er " %p", _x, _y));    \
@@ -921,7 +923,7 @@ static void sftp_client_connect(WOLFSSH_CTX** ctx, WOLFSSH** ssh, int port)
     }
 
     build_addr(&clientAddr, host, port);
-    tcp_socket(&sockFd);
+    tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family);
     ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz);
     if (ret != 0){
         wolfSSH_free(*ssh);
@@ -954,7 +956,7 @@ static void test_wolfSSH_SFTP_SendReadPacket(void)
     func_args ser;
     tcp_ready ready;
     int argsCount;
-    int clientFd;
+    WS_SOCKET_T clientFd;
 
     const char* args[10];
     WOLFSSH_CTX* ctx = NULL;
@@ -1065,7 +1067,7 @@ static void test_wolfSSH_SFTP_SendReadPacket(void)
 
     /* close client socket down */
     clientFd = wolfSSH_get_fd(ssh);
-    close(clientFd);
+    WCLOSESOCKET(clientFd);
 
     wolfSSH_free(ssh);
     wolfSSH_CTX_free(ctx);
@@ -1305,8 +1307,258 @@ static void test_wolfSSH_RealPath(void)
 #else
 static void test_wolfSSH_RealPath(void) { ; }
 #endif
+
+
+static void test_wolfSSH_SetAlgoList(void)
+{
+    const char* newKexList = "diffie-hellman-group1-sha1,ecdh-sha2-nistp521";
+    const char* newKeyList = "rsa-sha2-512,ecdsa-sha2-nistp521";
+    const char* newCipherList = "aes128-ctr,aes128-cbc";
+    const char* newMacList = "hmac-sha1";
+    const char* newKeyAccList = "ssh-rsa";
+    const char* defaultKexList = NULL;
+    const char* defaultKeyList = NULL;
+    const char* defaultCipherList = NULL;
+    const char* defaultMacList = NULL;
+    const char* defaultKeyAccList = NULL;
+    const char* checkKexList = NULL;
+    const char* checkKeyList = NULL;
+    const char* checkCipherList = NULL;
+    const char* checkMacList = NULL;
+    const char* checkKeyAccList = NULL;
+    const char* rawKey = NULL;
+    WOLFSSH_CTX* ctx;
+    WOLFSSH* ssh;
+    byte* key;
+    word32 keySz;
+
+    /* Create a ctx object. */
+    ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
+    AssertNotNull(ctx);
+
+    /* Check that the ctx's default algo lists are not null */
+    defaultKexList = wolfSSH_CTX_GetAlgoListKex(ctx);
+    AssertNotNull(defaultKexList);
+
+    defaultKeyList = wolfSSH_CTX_GetAlgoListKey(ctx);
+    AssertNotNull(defaultKeyList);
+
+    defaultCipherList = wolfSSH_CTX_GetAlgoListCipher(ctx);
+    AssertNotNull(defaultCipherList);
+
+    defaultMacList = wolfSSH_CTX_GetAlgoListMac(ctx);
+    AssertNotNull(defaultMacList);
+
+    defaultKeyAccList = wolfSSH_CTX_GetAlgoListKeyAccepted(ctx);
+    AssertNotNull(defaultKeyAccList);
+
+    /* Create a new ssh object. */
+    ssh = wolfSSH_new(ctx);
+    AssertNotNull(ssh);
+
+    /* Check that the ssh's default algo lists match the ctx's algo lists. */
+    checkKexList = wolfSSH_GetAlgoListKex(ssh);
+    AssertPtrEq(checkKexList, defaultKexList);
+
+    checkKeyList = wolfSSH_GetAlgoListKey(ssh);
+    AssertPtrEq(checkKeyList, defaultKeyList);
+
+    checkCipherList = wolfSSH_GetAlgoListCipher(ssh);
+    AssertPtrEq(checkCipherList, defaultCipherList);
+
+    checkMacList = wolfSSH_GetAlgoListMac(ssh);
+    AssertPtrEq(checkMacList, defaultMacList);
+
+    checkKeyAccList = wolfSSH_GetAlgoListKeyAccepted(ssh);
+    AssertPtrEq(checkKeyAccList, defaultKeyAccList);
+
+    /* Set the ssh's algo lists, check they match new value. */
+    wolfSSH_SetAlgoListKex(ssh, newKexList);
+    checkKexList = wolfSSH_GetAlgoListKex(ssh);
+    AssertPtrEq(checkKexList, newKexList);
+
+    wolfSSH_SetAlgoListKey(ssh, newKeyList);
+    checkKeyList = wolfSSH_GetAlgoListKey(ssh);
+    AssertPtrEq(checkKeyList, newKeyList);
+
+    wolfSSH_SetAlgoListCipher(ssh, newCipherList);
+    checkCipherList = wolfSSH_GetAlgoListCipher(ssh);
+    AssertPtrEq(checkCipherList, newCipherList);
+
+    wolfSSH_SetAlgoListMac(ssh, newMacList);
+    checkMacList = wolfSSH_GetAlgoListMac(ssh);
+    AssertPtrEq(checkMacList, newMacList);
+
+    wolfSSH_SetAlgoListKeyAccepted(ssh, newKeyAccList);
+    checkKeyAccList = wolfSSH_GetAlgoListKeyAccepted(ssh);
+    AssertPtrEq(checkKeyAccList, newKeyAccList);
+
+    /* Delete the ssh. */
+    wolfSSH_free(ssh);
+
+    /* Set new algo lists on the ctx. */
+    wolfSSH_CTX_SetAlgoListKex(ctx, newKexList);
+    defaultKexList = wolfSSH_CTX_GetAlgoListKex(ctx);
+    AssertPtrEq(defaultKexList, newKexList);
+
+    wolfSSH_CTX_SetAlgoListKey(ctx, newKeyList);
+    defaultKeyList = wolfSSH_CTX_GetAlgoListKey(ctx);
+    AssertPtrEq(checkKeyList, newKeyList);
+
+    wolfSSH_CTX_SetAlgoListCipher(ctx, newCipherList);
+    defaultCipherList = wolfSSH_CTX_GetAlgoListCipher(ctx);
+    AssertNotNull(defaultCipherList);
+
+    wolfSSH_CTX_SetAlgoListMac(ctx, newMacList);
+    defaultMacList = wolfSSH_CTX_GetAlgoListMac(ctx);
+    AssertNotNull(defaultMacList);
+
+    wolfSSH_CTX_SetAlgoListKeyAccepted(ctx, newKeyAccList);
+    defaultKeyAccList = wolfSSH_CTX_GetAlgoListKeyAccepted(ctx);
+    AssertNotNull(defaultKeyAccList);
+
+    /* Create a new ssh object. */
+    ssh = wolfSSH_new(ctx);
+    AssertNotNull(ssh);
+
+    /* Check that the ssh's default algo lists match the ctx's algo lists. */
+    checkKexList = wolfSSH_GetAlgoListKex(ssh);
+    AssertPtrEq(checkKexList, defaultKexList);
+
+    checkKeyList = wolfSSH_GetAlgoListKey(ssh);
+    AssertPtrEq(checkKeyList, defaultKeyList);
+
+    checkCipherList = wolfSSH_GetAlgoListCipher(ssh);
+    AssertPtrEq(checkCipherList, defaultCipherList);
+
+    checkMacList = wolfSSH_GetAlgoListMac(ssh);
+    AssertPtrEq(checkMacList, defaultMacList);
+
+    checkKeyAccList = wolfSSH_GetAlgoListKeyAccepted(ssh);
+    AssertPtrEq(checkKeyAccList, defaultKeyAccList);
+
+    /* Cleanup */
+    wolfSSH_free(ssh);
+    wolfSSH_CTX_free(ctx);
+
+    /* Create a ctx object. */
+    ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
+    AssertNotNull(ctx);
+
+    /* Check server ctx's key list is NULL. */
+    defaultKeyList = wolfSSH_CTX_GetAlgoListKey(ctx);
+    AssertNull(defaultKeyList);
+    defaultKeyAccList = wolfSSH_CTX_GetAlgoListKeyAccepted(ctx);
+    AssertNotNull(defaultKeyAccList);
+
+    /* Create a new ssh object. */
+    ssh = wolfSSH_new(ctx);
+    AssertNotNull(ssh);
+
+    /* Check server ssh's key list is NULL. */
+    checkKeyList = wolfSSH_GetAlgoListKey(ssh);
+    AssertNull(checkKeyList);
+
+    /* Delete the ssh. */
+    wolfSSH_free(ssh);
+
+    /* Set key on ctx. */
+#if !defined(WOLFSSH_NO_ECDSA)
+    rawKey = serverKeyEccDer;
+#elif !defined(WOLFSSH_NO_RSA)
+    rawKey = serverKeyRsaDer;
+#endif
+    AssertNotNull(rawKey);
+    AssertIntEQ(0,
+            ConvertHexToBin(rawKey, &key, &keySz,
+                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
+    AssertIntEQ(WS_SUCCESS,
+            wolfSSH_CTX_UsePrivateKey_buffer(ctx,
+                key, keySz, WOLFSSH_FORMAT_ASN1));
+
+    /* Check ctx's key algo list is still null. */
+    checkKeyList = wolfSSH_CTX_GetAlgoListKey(ctx);
+    AssertNull(checkKeyList);
+
+    /* Create a new ssh object. */
+    ssh = wolfSSH_new(ctx);
+    AssertNotNull(ssh);
+
+    /* Check ssh's key algo list is null. */
+    checkKeyList = wolfSSH_GetAlgoListKey(ssh);
+    AssertNull(checkKeyList);
+
+    /* Set a new list on ssh. */
+    wolfSSH_SetAlgoListKey(ssh, newKeyList);
+    checkKeyList = wolfSSH_GetAlgoListKey(ssh);
+    AssertPtrEq(checkKeyList, newKeyList);
+
+    /* Cleanup */
+    wolfSSH_free(ssh);
+    wolfSSH_CTX_free(ctx);
+    FreeBins(key, NULL, NULL, NULL);
+}
+
+
+static void test_wolfSSH_QueryAlgoList(void)
+{
+    const char* name;
+    word32 i, j;
+    int k;
+
+    i = 0;
+    name = NULL;
+    do {
+        name = wolfSSH_QueryKex(&i);
+        AssertIntNE(i, 0);
+    } while (name != NULL);
+
+    i = 0;
+    name = NULL;
+    do {
+        name = wolfSSH_QueryKey(&i);
+        AssertIntNE(i, 0);
+    } while (name != NULL);
+
+    i = 0;
+    name = NULL;
+    do {
+        name = wolfSSH_QueryCipher(&i);
+        AssertIntNE(i, 0);
+    } while (name != NULL);
+
+    i = 0;
+    name = NULL;
+    do {
+        name = wolfSSH_QueryMac(&i);
+        AssertIntNE(i, 0);
+    } while (name != NULL);
+
+    /* This test case picks up where the index left off. */
+    j = i;
+    name = wolfSSH_QueryKex(&i);
+    AssertNull(name);
+    i = j;
+    name = wolfSSH_QueryKey(&i);
+    AssertNull(name);
+    i = j;
+    name = wolfSSH_QueryCipher(&i);
+    AssertNull(name);
+    i = j;
+    name = wolfSSH_QueryMac(&i);
+    AssertNull(name);
+
+    k = wolfSSH_CheckAlgoName("ssh-rsa");
+    AssertIntEQ(WS_SUCCESS, k);
+
+    k = wolfSSH_CheckAlgoName("not-an-algo@wolfssl.com");
+    AssertIntEQ(WS_INVALID_ALGO_ID, k);
+}
+
+
 #endif /* WOLFSSH_TEST_BLOCK */
 
+
 int wolfSSH_ApiTest(int argc, char** argv)
 {
     (void)argc;
@@ -1337,6 +1589,8 @@ int wolfSSH_ApiTest(int argc, char** argv)
     test_wolfSSH_CTX_UseCert_buffer();
     test_wolfSSH_CertMan();
     test_wolfSSH_ReadKey();
+    test_wolfSSH_QueryAlgoList();
+    test_wolfSSH_SetAlgoList();
 
     /* SCP tests */
     test_wolfSSH_SCP_CB();
diff --git a/tests/api.h b/tests/api.h
index a59be0814..8c8142561 100644
--- a/tests/api.h
+++ b/tests/api.h
@@ -1,6 +1,6 @@
 /* api.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/tests/include.am b/tests/include.am
index c3d7e7508..e8b1b548e 100644
--- a/tests/include.am
+++ b/tests/include.am
@@ -18,6 +18,9 @@ endif
 if BUILD_SFTP
 tests_unit_test_CPPFLAGS     += -DWOLFSSH_SFTP
 endif
+if BUILD_TERM
+tests_unit_test_CPPFLAGS     += -DWOLFSSH_TERM
+endif
 if BUILD_SHELL
 tests_unit_test_CPPFLAGS     += -DWOLFSSH_SHELL
 endif
@@ -45,6 +48,9 @@ endif
 if BUILD_SFTP
 tests_api_test_CPPFLAGS     += -DWOLFSSH_SFTP
 endif
+if BUILD_TERM
+tests_api_test_CPPFLAGS     += -DWOLFSSH_TERM
+endif
 if BUILD_SHELL
 tests_api_test_CPPFLAGS     += -DWOLFSSH_SHELL
 endif
@@ -77,6 +83,9 @@ endif
 if BUILD_SFTP
 tests_testsuite_test_CPPFLAGS     += -DWOLFSSH_SFTP
 endif
+if BUILD_TERM
+tests_testsuite_test_CPPFLAGS     += -DWOLFSSH_TERM
+endif
 if BUILD_SHELL
 tests_testsuite_test_CPPFLAGS     += -DWOLFSSH_SHELL
 endif
diff --git a/tests/sftp.c b/tests/sftp.c
index 1e4aa067e..b0aa3a751 100644
--- a/tests/sftp.c
+++ b/tests/sftp.c
@@ -1,6 +1,6 @@
 /* sftp.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -23,6 +23,11 @@
 #endif
 
 #include <stdio.h>
+#ifdef WOLFSSL_USER_SETTINGS
+    #include <wolfssl/wolfcrypt/settings.h>
+#else
+    #include <wolfssl/options.h>
+#endif
 #include <wolfssh/settings.h>
 
 #if defined(WOLFSSH_SFTP) && !defined(SINGLE_THREADED)
@@ -181,7 +186,9 @@ int wolfSSH_SftpTest(int flag)
     int argsCount;
 
     const char* args[10];
+#ifndef USE_WINDOWS_API
     char  portNumber[8];
+#endif
 
     THREAD_TYPE serThread;
 
diff --git a/tests/sftp.h b/tests/sftp.h
index cb1860ba3..d69e2433a 100644
--- a/tests/sftp.h
+++ b/tests/sftp.h
@@ -1,6 +1,6 @@
 /* sftp.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/tests/testsuite.c b/tests/testsuite.c
index c9441b24d..6ab4a8507 100644
--- a/tests/testsuite.c
+++ b/tests/testsuite.c
@@ -1,6 +1,6 @@
 /* testsuite.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -22,12 +22,6 @@
     #include <config.h>
 #endif
 
-#define WOLFSSH_TEST_CLIENT
-#define WOLFSSH_TEST_SERVER
-#define WOLFSSH_TEST_THREADING
-#define WOLFSSH_TEST_LOCKING
-
-
 #include <stdio.h>
 
 #ifdef WOLFSSL_USER_SETTINGS
@@ -36,6 +30,13 @@
     #include <wolfssl/options.h>
 #endif
 
+#define WOLFSSH_TEST_CLIENT
+#define WOLFSSH_TEST_SERVER
+#ifndef SINGLE_THREADED
+    #define WOLFSSH_TEST_THREADING
+#endif
+#define WOLFSSH_TEST_LOCKING
+
 #include <wolfssh/settings.h>
 #include <wolfssh/ssh.h>
 #include <wolfssh/test.h>
diff --git a/tests/testsuite.h b/tests/testsuite.h
index 7391237fa..c40bd09ec 100644
--- a/tests/testsuite.h
+++ b/tests/testsuite.h
@@ -1,6 +1,6 @@
 /* testsuite.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/tests/unit.c b/tests/unit.c
index 5c9898d3f..335813b6d 100644
--- a/tests/unit.c
+++ b/tests/unit.c
@@ -1,6 +1,6 @@
 /* unit.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/tests/unit.h b/tests/unit.h
index 39a1767ab..364a1925d 100644
--- a/tests/unit.h
+++ b/tests/unit.h
@@ -1,6 +1,6 @@
 /* unit.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/agent.h b/wolfssh/agent.h
index 97f3220a7..c3d6412aa 100644
--- a/wolfssh/agent.h
+++ b/wolfssh/agent.h
@@ -1,6 +1,6 @@
 /* agent.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/certman.h b/wolfssh/certman.h
index a4fe2c016..63e4542de 100644
--- a/wolfssh/certman.h
+++ b/wolfssh/certman.h
@@ -1,6 +1,6 @@
 /* certman.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/certs_test.h b/wolfssh/certs_test.h
index a5de5752a..1d60530ff 100644
--- a/wolfssh/certs_test.h
+++ b/wolfssh/certs_test.h
@@ -1,6 +1,6 @@
 /* certs_test.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/error.h b/wolfssh/error.h
index d1fdfdd27..9d3832fa3 100644
--- a/wolfssh/error.h
+++ b/wolfssh/error.h
@@ -1,6 +1,6 @@
 /* error.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -132,8 +132,11 @@ enum WS_ErrorCodes {
     WS_KEY_CHECK_VAL_E      = -1091, /* OpenSSH key check value fail */
     WS_KEY_FORMAT_E         = -1092, /* OpenSSH key format fail */
     WS_SFTP_NOT_FILE_E      = -1093, /* Not a regular file */
-    
-    WS_LAST_E               = -1093  /* Update this to indicate last error */
+    WS_MSGID_NOT_ALLOWED_E  = -1094, /* Message not allowed before userauth */
+    WS_ED25519_E            = -1095, /* Ed25519 failure */
+    WS_AUTH_PENDING         = -1096, /* User authentication still pending */
+
+    WS_LAST_E               = -1096  /* Update this to indicate last error */
 };
 
 
diff --git a/wolfssh/internal.h b/wolfssh/internal.h
index 8b8c41ec7..286acaf9d 100644
--- a/wolfssh/internal.h
+++ b/wolfssh/internal.h
@@ -1,6 +1,6 @@
 /* internal.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -37,6 +37,7 @@
 #include <wolfssl/wolfcrypt/dh.h>
 #include <wolfssl/wolfcrypt/ecc.h>
 #include <wolfssl/wolfcrypt/rsa.h>
+#include <wolfssl/wolfcrypt/curve25519.h>
 #ifdef WOLFSSH_SCP
     #include <wolfssh/wolfscp.h>
 #endif
@@ -67,14 +68,9 @@ extern "C" {
 
 
 /*
- * Force some options. Do not want ssh-rsa with SHA1 at anymore. Not ready
- * for rsa-sha2-512 yet.
+ * Not ready for rsa-sha2-512 yet.
  */
 
-#undef WOLFSSH_NO_SSH_RSA_SHA1
-#ifndef WOLFSSH_YES_SSH_RSA_SHA1
-    #define WOLFSSH_NO_SSH_RSA_SHA1
-#endif
 #undef WOLFSSH_NO_RSA_SHA2_512
 #ifndef WOLFSSH_YES_RSA_SHA2_512
     #define WOLFSSH_NO_RSA_SHA2_512
@@ -109,6 +105,13 @@ extern "C" {
     #define WOLFSSH_NO_SHA1
 #endif
 
+#if !defined(HAVE_ED25519) \
+    || !defined(WOLFSSL_ED25519_STREAMING_VERIFY) \
+    || !defined(HAVE_ED25519_KEY_IMPORT) \
+    || !defined(HAVE_ED25519_KEY_EXPORT)
+    #undef WOLFSSH_NO_ED25519
+    #define WOLFSSH_NO_ED25519
+#endif
 
 #if defined(NO_HMAC) || defined(WOLFSSH_NO_SHA1)
     #undef WOLFSSH_NO_HMAC_SHA1
@@ -156,15 +159,15 @@ extern "C" {
     #undef WOLFSSH_NO_ECDH_SHA2_NISTP521
     #define WOLFSSH_NO_ECDH_SHA2_NISTP521
 #endif
-#if !defined(HAVE_ED25519) || defined(NO_SHA256) || 1
-    /* ED25519 isn't supported yet. Force disabled. */
-    #undef WOLFSSH_NO_ECDH_SHA2_ED25519
-    #define WOLFSSH_NO_ECDH_SHA2_ED25519
-#endif
-#if !defined(WOLFSSH_HAVE_LIBOQS) || defined(NO_SHA256)
+#if !defined(WOLFSSH_HAVE_LIBOQS) || defined(NO_SHA256) \
+    || defined(WOLFSSH_NO_ECDH_SHA2_NISTP256)
     #undef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
     #define WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
 #endif
+#if !defined(HAVE_CURVE25519) || defined(NO_SHA256)
+    #undef WOLFSSH_NO_CURVE25519_SHA256
+    #define WOLFSSH_NO_CURVE25519_SHA256
+#endif
 
 #if defined(WOLFSSH_NO_DH_GROUP1_SHA1) && \
     defined(WOLFSSH_NO_DH_GROUP14_SHA1) && \
@@ -172,8 +175,8 @@ extern "C" {
     defined(WOLFSSH_NO_ECDH_SHA2_NISTP256) && \
     defined(WOLFSSH_NO_ECDH_SHA2_NISTP384) && \
     defined(WOLFSSH_NO_ECDH_SHA2_NISTP521) && \
-    defined(WOLFSSH_NO_ECDH_SHA2_ED25519) && \
-    defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256)
+    defined(WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256) && \
+    defined(WOLFSSH_NO_CURVE25519_SHA256)
     #error "You need at least one key agreement algorithm."
 #endif
 
@@ -223,7 +226,8 @@ extern "C" {
     defined(WOLFSSH_NO_RSA_SHA2_512) && \
     defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) && \
     defined(WOLFSSH_NO_ECDSA_SHA2_NISTP384) && \
-    defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521)
+    defined(WOLFSSH_NO_ECDSA_SHA2_NISTP521) && \
+    defined(WOLFSSH_NO_ED25519)
     #error "You need at least one signing algorithm."
 #endif
 
@@ -312,6 +316,9 @@ enum {
     ID_DH_GROUP14_SHA256,
 #ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
     ID_ECDH_NISTP256_KYBER_LEVEL1_SHA256,
+#endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+    ID_CURVE25519_SHA256,
 #endif
     ID_EXTINFO_S, /* Pseudo-KEX to indicate server extensions. */
     ID_EXTINFO_C, /* Pseudo-KEX to indicate client extensions. */
@@ -323,6 +330,7 @@ enum {
     ID_ECDSA_SHA2_NISTP256,
     ID_ECDSA_SHA2_NISTP384,
     ID_ECDSA_SHA2_NISTP521,
+    ID_ED25519,
     ID_X509V3_SSH_RSA,
     ID_X509V3_ECDSA_SHA2_NISTP256,
     ID_X509V3_ECDSA_SHA2_NISTP384,
@@ -356,6 +364,11 @@ enum {
 };
 
 
+enum NameIdType {
+    TYPE_KEX, TYPE_KEY, TYPE_CIPHER, TYPE_MAC, TYPE_OTHER
+};
+
+
 #define WOLFSSH_MAX_NAMESZ 32
 
 #ifndef WOLFSSH_MAX_CHN_NAMESZ
@@ -430,8 +443,10 @@ enum {
     #define WOLFSSH_KEY_QUANTITY_REQ 1
 #endif
 
-WOLFSSH_LOCAL byte NameToId(const char*, word32);
-WOLFSSH_LOCAL const char* IdToName(byte);
+
+WOLFSSH_LOCAL byte NameToId(const char* name, word32 nameSz);
+WOLFSSH_LOCAL const char* IdToName(byte id);
+WOLFSSH_LOCAL const char* NameByIndexType(byte type, word32* index);
 
 
 #define STATIC_BUFFER_LEN AES_BLOCK_SIZE
@@ -483,6 +498,14 @@ struct WOLFSSH_CTX {
     WS_CallbackGlobalReq globalReqCb; /* Global Request Callback */
     WS_CallbackReqSuccess reqSuccessCb; /* Global Request Success Callback */
     WS_CallbackReqSuccess reqFailureCb; /* Global Request Failure Callback */
+    WS_CallbackChannelOpen channelOpenCb;     /* Channel Open Requested */
+    WS_CallbackChannelOpen channelOpenConfCb; /* Channel Open Confirm */
+    WS_CallbackChannelOpen channelOpenFailCb; /* Channel Open Fail */
+    WS_CallbackChannelReq channelReqShellCb; /* Channel Request "Shell" */
+    WS_CallbackChannelReq channelReqExecCb; /* Channel Request "Exec" */
+    WS_CallbackChannelReq channelReqSubsysCb; /* Channel Request "Subsystem" */
+    WS_CallbackChannelEof channelEofCb; /* Channel Eof Callback */
+    WS_CallbackChannelClose channelCloseCb; /* Channel Close Callback */
 #ifdef WOLFSSH_SCP
     WS_CallbackScpRecv scpRecvCb;     /* SCP receive callback */
     WS_CallbackScpSend scpSendCb;     /* SCP send callback */
@@ -507,6 +530,11 @@ struct WOLFSSH_CTX {
     word32 highwaterMark;
     const char* banner;
     const char* sshProtoIdStr;
+    const char* algoListKex;
+    const char* algoListKey;
+    const char* algoListCipher;
+    const char* algoListMac;
+    const char* algoListKeyAccepted;
     word32 bannerSz;
     word32 windowSz;
     word32 maxPacketSz;
@@ -515,6 +543,7 @@ struct WOLFSSH_CTX {
 #ifdef WOLFSSH_AGENT
     byte agentEnabled;
 #endif /* WOLFSSH_AGENT */
+    WS_CallbackKeyingCompletion keyingCompletionCb;
 };
 
 
@@ -567,17 +596,20 @@ typedef struct HandshakeInfo {
     word32 generatorSz;
 #endif
 
-    byte useEcc;
-#ifndef WOLFSSH_NO_ECDH_NISTP256_KYBER_LEVEL1_SHA256
-    byte useEccKyber;
-#endif
+    byte useDh:1;
+    byte useEcc:1;
+    byte useEccKyber:1;
+    byte useCurve25519:1;
 
     union {
 #ifndef WOLFSSH_NO_DH
         DhKey dh;
 #endif
-#if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH)
+#ifndef WOLFSSH_NO_ECDH
         ecc_key ecc;
+#endif
+#ifndef WOLFSSH_NO_CURVE25519_SHA256
+        curve25519_key curve25519;
 #endif
     } privKey;
 } HandshakeInfo;
@@ -634,15 +666,24 @@ struct WOLFSSH {
     word32 rxCount;
     word32 highwaterMark;
     byte highwaterFlag;    /* Set when highwater CB called */
-    void* highwaterCtx;
+    void* highwaterCtx;    /* Highwater CB context */
     void* globalReqCtx;    /* Global Request CB context */
     void* reqSuccessCtx;   /* Global Request Sucess CB context */
     void* reqFailureCtx;   /* Global Request Failure CB context */
+    void* channelOpenCtx;  /* Channel Open CB context */
+    void* channelReqCtx;   /* Channel Request CB context */
+    void* channelEofCtx;   /* Channel EOF CB context */
+    void* channelCloseCtx; /* Channel Close CB context */
     void* fs;              /* File system handle */
     word32 curSz;
     word32 seq;
     word32 peerSeq;
     word32 packetStartIdx; /* Current send packet start index */
+    const char* algoListKex;
+    const char* algoListKey;
+    const char* algoListCipher;
+    const char* algoListMac;
+    const char* algoListKeyAccepted;
     byte acceptState;
     byte connectState;
     byte clientState;
@@ -697,6 +738,7 @@ struct WOLFSSH {
     byte isClosed;
     byte clientOpenSSH;
 
+    byte kexId;
     byte blockSz;
     byte encryptId;
     byte macId;
@@ -707,6 +749,9 @@ struct WOLFSSH {
     byte peerMacId;
     byte peerMacSz;
     byte peerAeadMode;
+#ifndef WOLFSSH_NO_DH
+    word32 primeGroupSz;
+#endif
 
     Ciphers encryptCipher;
     Ciphers decryptCipher;
@@ -825,6 +870,7 @@ struct WOLFSSH {
 #if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL)
     word32 exitStatus;
 #endif
+    void* keyingCompletionCtx;
 };
 
 
@@ -961,6 +1007,10 @@ WOLFSSH_LOCAL int SendChannelExitStatus(WOLFSSH* ssh, word32 channelId,
     word32 exitStatus);
 WOLFSSH_LOCAL int GenerateKey(byte, byte, byte*, word32, const byte*, word32,
                               const byte*, word32, const byte*, word32, byte doKeyPad);
+#if !defined(WOLFSSH_NO_ECDSA) || !defined(WOLFSSH_NO_ECDH)
+WOLFSSH_LOCAL int wcPrimeForId(byte);
+#endif
+WOLFSSH_LOCAL enum wc_HashType HashForId(byte);
 
 
 enum AcceptStates {
@@ -1096,6 +1146,21 @@ enum WS_MessageIds {
 };
 
 
+/* Allows the server to receive up to KEXDH GEX Request during KEX. */
+#define MSGID_KEXDH_LIMIT MSGID_KEXDH_GEX_REQUEST
+
+/* The endpoints should not allow message IDs greater than or
+ * equal to msgid 80 before user authentication is complete.
+ * Per RFC 4252 section 6. */
+#define MSGID_USERAUTH_LIMIT 80
+
+/* The client should only send the user auth request message
+ * (50), it should not accept it. The server should only receive
+ * the user auth request message, it should not accept the other
+ * user auth messages, it sends them. (>50) */
+#define MSGID_USERAUTH_RESTRICT 50
+
+
 #define CHANNEL_EXTENDED_DATA_STDERR WOLFSSH_EXT_DATA_STDERR
 
 
@@ -1149,7 +1214,8 @@ enum WS_BufferTypes {
 #define SCP_CONFIRM_FATAL 0x02   /* binary 2 */
 
 enum WS_ScpStates {
-    SCP_PARSE_COMMAND = 0,
+    SCP_SETUP = 0,
+    SCP_PARSE_COMMAND,
     SCP_SINK,
     SCP_SINK_BEGIN,
     SCP_TRANSFER,
@@ -1206,8 +1272,9 @@ WOLFSSH_LOCAL int wsScpSendCallback(WOLFSSH*, int, const char*, char*, word32,
 
 WOLFSSH_LOCAL int wolfSSH_CleanPath(WOLFSSH* ssh, char* in);
 #ifndef WOLFSSH_NO_RSA
-WOLFSSH_LOCAL int wolfSSH_RsaVerify(byte *sig, word32 sigSz,
-        const byte* digest, word32 digestSz,
+WOLFSSH_LOCAL int wolfSSH_RsaVerify(
+        const byte *sig, word32 sigSz,
+        const byte* encDigest, word32 encDigestSz,
         RsaKey* key, void* heap, const char* loc);
 #endif
 WOLFSSH_LOCAL void DumpOctetString(const byte*, word32);
@@ -1278,6 +1345,10 @@ enum TerminalModes {
 };
 #endif /* WOLFSSH_TERM */
 
+
+#define WOLFSSL_V5_7_0 0x05007000
+
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/wolfssh/keygen.h b/wolfssh/keygen.h
index 52b4f34bb..9cf58e7c7 100644
--- a/wolfssh/keygen.h
+++ b/wolfssh/keygen.h
@@ -1,6 +1,6 @@
 /* keygen.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/log.h b/wolfssh/log.h
index a51d37835..26f202124 100644
--- a/wolfssh/log.h
+++ b/wolfssh/log.h
@@ -1,6 +1,6 @@
 /* log.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/misc.h b/wolfssh/misc.h
index d0501cf8a..55dd6cf80 100644
--- a/wolfssh/misc.h
+++ b/wolfssh/misc.h
@@ -1,6 +1,6 @@
 /* misc.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/port.h b/wolfssh/port.h
index 3a4820efd..b65d32fd6 100644
--- a/wolfssh/port.h
+++ b/wolfssh/port.h
@@ -1,6 +1,6 @@
 /* port.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/settings.h b/wolfssh/settings.h
index e4aa01664..12e7527df 100644
--- a/wolfssh/settings.h
+++ b/wolfssh/settings.h
@@ -1,6 +1,6 @@
 /* settings.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h
index 218a44484..8f6a2a115 100644
--- a/wolfssh/ssh.h
+++ b/wolfssh/ssh.h
@@ -1,6 +1,6 @@
 /* ssh.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -90,11 +90,87 @@ WOLFSSH_API int wolfSSH_ReadKey_file(const char* name,
         byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
         byte* isPrivate, void* heap);
 
+WOLFSSH_API int wolfSSH_CTX_SetAlgoListKex(WOLFSSH_CTX* ctx, const char* list);
+WOLFSSH_API const char* wolfSSH_CTX_GetAlgoListKex(WOLFSSH_CTX* ctx);
+WOLFSSH_API int wolfSSH_SetAlgoListKex(WOLFSSH* ssh, const char* list);
+WOLFSSH_API const char* wolfSSH_GetAlgoListKex(WOLFSSH* ssh);
+
+WOLFSSH_API int wolfSSH_CTX_SetAlgoListKey(WOLFSSH_CTX* ctx, const char* list);
+WOLFSSH_API const char* wolfSSH_CTX_GetAlgoListKey(WOLFSSH_CTX* ctx);
+WOLFSSH_API int wolfSSH_SetAlgoListKey(WOLFSSH* ssh, const char* list);
+WOLFSSH_API const char* wolfSSH_GetAlgoListKey(WOLFSSH* ssh);
+
+WOLFSSH_API int wolfSSH_CTX_SetAlgoListCipher(WOLFSSH_CTX* ctx,
+        const char* list);
+WOLFSSH_API const char* wolfSSH_CTX_GetAlgoListCipher(WOLFSSH_CTX* ctx);
+WOLFSSH_API int wolfSSH_SetAlgoListCipher(WOLFSSH* ssh, const char* list);
+WOLFSSH_API const char* wolfSSH_GetAlgoListCipher(WOLFSSH* ssh);
+
+WOLFSSH_API int wolfSSH_CTX_SetAlgoListMac(WOLFSSH_CTX* ctx, const char* list);
+WOLFSSH_API const char* wolfSSH_CTX_GetAlgoListMac(WOLFSSH_CTX* ctx);
+WOLFSSH_API int wolfSSH_SetAlgoListMac(WOLFSSH* ssh, const char* list);
+WOLFSSH_API const char* wolfSSH_GetAlgoListMac(WOLFSSH* ssh);
+
+WOLFSSH_API int wolfSSH_CTX_SetAlgoListKeyAccepted(WOLFSSH_CTX* ctx,
+        const char* list);
+WOLFSSH_API const char* wolfSSH_CTX_GetAlgoListKeyAccepted(WOLFSSH_CTX* ctx);
+WOLFSSH_API int wolfSSH_SetAlgoListKeyAccepted(WOLFSSH* ssh, const char* list);
+WOLFSSH_API const char* wolfSSH_GetAlgoListKeyAccepted(WOLFSSH* ssh);
+
+WOLFSSH_API int wolfSSH_CheckAlgoName(const char* name);
+
+WOLFSSH_API const char* wolfSSH_QueryKex(word32* index);
+WOLFSSH_API const char* wolfSSH_QueryKey(word32* index);
+WOLFSSH_API const char* wolfSSH_QueryCipher(word32* index);
+WOLFSSH_API const char* wolfSSH_QueryMac(word32* index);
+
+typedef enum WS_Text {
+	WOLFSSH_TEXT_KEX_ALGO,
+	WOLFSSH_TEXT_KEX_CURVE,
+	WOLFSSH_TEXT_KEX_HASH,
+
+	WOLFSSH_TEXT_CRYPTO_IN_CIPHER,
+	WOLFSSH_TEXT_CRYPTO_IN_MAC,
+	WOLFSSH_TEXT_CRYPTO_OUT_CIPHER,
+	WOLFSSH_TEXT_CRYPTO_OUT_MAC,
+} WS_Text;
+
+/*
+ * Outputs the c-string representation of the data entry identified by the id to
+ * the character string str, writing no more than strSz bytes, including the
+ * terminating null byte ('\0').
+ *
+ * Returns the number of characters written (excluding the null byte used to end
+ * output to strings), unless the output was truncated, in which case the return
+ * value is the number of characters (excluding the terminating null byte) which
+ * would have been written to the final string if enough space had been
+ * available.
+ *
+ * Thus, a return value of strSz or more means that the output was truncated.
+ */
+
+WOLFSSH_API size_t wolfSSH_GetText(WOLFSSH *ssh, WS_Text id, char *str,
+        size_t strSz);
+
+typedef void (*WS_CallbackKeyingCompletion)(void *);
+WOLFSSH_API void wolfSSH_SetKeyingCompletionCb(WOLFSSH_CTX*,
+        WS_CallbackKeyingCompletion);
+WOLFSSH_API void wolfSSH_SetKeyingCompletionCbCtx(WOLFSSH*,
+        void*);
 
 #define WS_CHANNEL_ID_SELF 0
 #define WS_CHANNEL_ID_PEER 1
 
 
+typedef enum {
+    WOLFSSH_SESSION_UNKNOWN = 0,
+    WOLFSSH_SESSION_SHELL,
+    WOLFSSH_SESSION_EXEC,
+    WOLFSSH_SESSION_SUBSYSTEM,
+    WOLFSSH_SESSION_TERMINAL,
+} WS_SessionType;
+
+
 typedef enum WS_FwdCbAction {
     WOLFSSH_FWD_LOCAL_SETUP,
     WOLFSSH_FWD_LOCAL_CLEANUP,
@@ -142,7 +218,41 @@ WOLFSSH_API int wolfSSH_ChannelRead(WOLFSSH_CHANNEL*, byte*, word32);
 WOLFSSH_API int wolfSSH_ChannelSend(WOLFSSH_CHANNEL*, const byte*, word32);
 WOLFSSH_API int wolfSSH_ChannelExit(WOLFSSH_CHANNEL*);
 WOLFSSH_API int wolfSSH_ChannelGetEof(WOLFSSH_CHANNEL*);
-
+WOLFSSH_API WS_SessionType wolfSSH_ChannelGetSessionType(
+        const WOLFSSH_CHANNEL* channel);
+WOLFSSH_API const char* wolfSSH_ChannelGetSessionCommand(
+        const WOLFSSH_CHANNEL* channel);
+
+/* Channel callbacks */
+typedef int (*WS_CallbackChannelOpen)(WOLFSSH_CHANNEL* channel, void* ctx);
+WOLFSSH_API int wolfSSH_CTX_SetChannelOpenCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelOpen cb);
+WOLFSSH_API int wolfSSH_CTX_SetChannelOpenRespCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelOpen confCb, WS_CallbackChannelOpen failCb);
+WOLFSSH_API int wolfSSH_SetChannelOpenCtx(WOLFSSH* ssh, void* ctx);
+WOLFSSH_API void* wolfSSH_GetChannelOpenCtx(WOLFSSH* ssh);
+
+typedef int (*WS_CallbackChannelReq)(WOLFSSH_CHANNEL* channel, void* ctx);
+WOLFSSH_API int wolfSSH_CTX_SetChannelReqShellCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelReq cb);
+WOLFSSH_API int wolfSSH_CTX_SetChannelReqExecCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelReq cb);
+WOLFSSH_API int wolfSSH_CTX_SetChannelReqSubsysCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelReq cb);
+WOLFSSH_API int wolfSSH_SetChannelReqCtx(WOLFSSH* ssh, void* ctx);
+WOLFSSH_API void* wolfSSH_GetChannelReqCtx(WOLFSSH* ssh);
+
+typedef int (*WS_CallbackChannelEof)(WOLFSSH_CHANNEL* channel, void* ctx);
+WOLFSSH_API int wolfSSH_CTX_SetChannelEofCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelEof cb);
+WOLFSSH_API int wolfSSH_SetChannelEofCtx(WOLFSSH* ssh, void* ctx);
+WOLFSSH_API void* wolfSSH_GetChannelEofCtx(WOLFSSH* ssh);
+
+typedef int (*WS_CallbackChannelClose)(WOLFSSH_CHANNEL* channel, void* ctx);
+WOLFSSH_API int wolfSSH_CTX_SetChannelCloseCb(WOLFSSH_CTX* ctx,
+        WS_CallbackChannelClose cb);
+WOLFSSH_API int wolfSSH_SetChannelCloseCtx(WOLFSSH* ssh, void* ctx);
+WOLFSSH_API void* wolfSSH_GetChannelCloseCtx(WOLFSSH* ssh);
 
 WOLFSSH_API int wolfSSH_get_error(const WOLFSSH*);
 WOLFSSH_API const char* wolfSSH_get_error_name(const WOLFSSH*);
@@ -275,13 +385,6 @@ WOLFSSH_API int wolfSSH_KDF(byte, byte, byte*, word32, const byte*, word32,
 WOLFSSH_API int wolfSSH_ConvertConsole(WOLFSSH*, WOLFSSH_HANDLE, byte*, word32);
 #endif
 
-typedef enum {
-    WOLFSSH_SESSION_UNKNOWN = 0,
-    WOLFSSH_SESSION_SHELL,
-    WOLFSSH_SESSION_EXEC,
-    WOLFSSH_SESSION_SUBSYSTEM,
-    WOLFSSH_SESSION_TERMINAL,
-} WS_SessionType;
 
 WOLFSSH_API int wolfSSH_DoModes(const byte* modes, word32 modesSz, int fd);
 WOLFSSH_API WS_SessionType wolfSSH_GetSessionType(const WOLFSSH*);
@@ -333,7 +436,8 @@ enum WS_UserAuthResults
     WOLFSSH_USERAUTH_INVALID_PASSWORD,
     WOLFSSH_USERAUTH_REJECTED,
     WOLFSSH_USERAUTH_INVALID_PUBLICKEY,
-    WOLFSSH_USERAUTH_PARTIAL_SUCCESS
+    WOLFSSH_USERAUTH_PARTIAL_SUCCESS,
+    WOLFSSH_USERAUTH_WOULD_BLOCK
 };
 
 enum WS_DisconnectReasonCodes {
diff --git a/wolfssh/test.h b/wolfssh/test.h
index d1b6ac100..c0467a9bf 100644
--- a/wolfssh/test.h
+++ b/wolfssh/test.h
@@ -1,6 +1,6 @@
 /* test.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -221,7 +221,7 @@
 
 #ifdef USE_WINDOWS_API
     #define WCLOSESOCKET(s) closesocket(s)
-    #define WSTARTTCP() do { WSADATA wsd; WSAStartup(0x0002, &wsd); } while(0)
+    #define WSTARTTCP() do { WSADATA wsd; (void)WSAStartup(0x0002, &wsd); } while(0)
 #elif defined(MICROCHIP_TCPIP) || defined(MICROCHIP_MPLAB_HARMONY)
     #ifdef MICROCHIP_MPLAB_HARMONY
         #define WCLOSESOCKET(s) TCPIP_TCP_Close((s))
@@ -519,7 +519,7 @@ static INLINE void build_addr(SOCKADDR_IN_T* addr, const char* peer,
 
             memset(&hints, 0, sizeof(hints));
 
-            hints.ai_family   = AF_INET_V;
+            hints.ai_family   = AF_UNSPEC;
             hints.ai_socktype = SOCK_STREAM;
             hints.ai_protocol = IPPROTO_TCP;
 
@@ -546,8 +546,10 @@ static INLINE void build_addr(SOCKADDR_IN_T* addr, const char* peer,
 #endif
 
 
-static INLINE void tcp_socket(WS_SOCKET_T* sockFd)
+static INLINE void tcp_socket(WS_SOCKET_T* sockFd, int targetProtocol)
 {
+    /* targetProtocol is only used if none of these platforms are defined. */
+    WOLFSSH_UNUSED(targetProtocol);
 #ifdef MICROCHIP_MPLAB_HARMONY
     /* creates socket in listen or connect */
     *sockFd = 0;
@@ -558,7 +560,7 @@ static INLINE void tcp_socket(WS_SOCKET_T* sockFd)
 #elif defined(WOLFSSL_NUCLEUS)
     *sockFd = NU_Socket(NU_FAMILY_IP, NU_TYPE_STREAM, 0);
 #else
-    *sockFd = socket(AF_INET_V, SOCK_STREAM, 0);
+    *sockFd = socket(targetProtocol, SOCK_STREAM, 0);
 #endif
 
 #ifdef USE_WINDOWS_API
@@ -637,8 +639,7 @@ static INLINE void tcp_listen(WS_SOCKET_T* sockfd, word16* port, int useAnyAddr)
     /* 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);
-
+    tcp_socket(sockfd, ((struct sockaddr_in *)&addr)->sin_family);
 #if !defined(USE_WINDOWS_API) && !defined(WOLFSSL_MDK_ARM) \
                               && !defined(WOLFSSL_KEIL_TCP_NET) \
                               && !defined(WOLFSSL_NUCLEUS) \
@@ -933,7 +934,9 @@ static INLINE void WaitTcpReady(tcp_ready* ready)
         #endif
     #endif
 #else
-    #define WOLFSSH_THREAD WOLFSSL_THREAD
+    #ifndef WOLFSSH_THREAD
+        #define WOLFSSH_THREAD WOLFSSL_THREAD
+    #endif
 #endif
 
 #if (LIBWOLFSSL_VERSION_HEX < WOLFSSL_V5_6_4) \
@@ -1133,6 +1136,9 @@ static int Base16_Decode(const byte* in, word32 inLen,
     word32 inIdx = 0;
     word32 outIdx = 0;
 
+    if (in == NULL || out == NULL || outLen == NULL)
+        return WS_BAD_ARGUMENT;
+
     if (inLen == 1 && *outLen && in) {
         byte b = in[inIdx] - 0x30;  /* 0 starts at 0x30 */
 
diff --git a/wolfssh/version.h b/wolfssh/version.h
index 3c3e9cfdc..2ec51af01 100644
--- a/wolfssh/version.h
+++ b/wolfssh/version.h
@@ -1,6 +1,6 @@
 /* version.h.in
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -35,8 +35,8 @@
 extern "C" {
 #endif
 
-#define LIBWOLFSSH_VERSION_STRING "1.4.15"
-#define LIBWOLFSSH_VERSION_HEX 0x01004015
+#define LIBWOLFSSH_VERSION_STRING "1.4.18"
+#define LIBWOLFSSH_VERSION_HEX 0x01004018
 
 #ifdef __cplusplus
 }
diff --git a/wolfssh/version.h.in b/wolfssh/version.h.in
index e9a5cf04b..b44be3403 100644
--- a/wolfssh/version.h.in
+++ b/wolfssh/version.h.in
@@ -1,6 +1,6 @@
 /* version.h.in
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/visibility.h b/wolfssh/visibility.h
index ae41a312b..fa9da6819 100644
--- a/wolfssh/visibility.h
+++ b/wolfssh/visibility.h
@@ -1,6 +1,6 @@
 /* visibility.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/wolfscp.h b/wolfssh/wolfscp.h
index fedf57719..eba468dc2 100644
--- a/wolfssh/wolfscp.h
+++ b/wolfssh/wolfscp.h
@@ -1,6 +1,6 @@
 /* wolfscp.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/wolfssh/wolfsftp.h b/wolfssh/wolfsftp.h
index 8fbbdbb77..48dc90e4d 100644
--- a/wolfssh/wolfsftp.h
+++ b/wolfssh/wolfsftp.h
@@ -1,6 +1,6 @@
 /* wolfsftp.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -128,6 +128,7 @@ struct WS_SFTP_FILEATRB_EX {
 
 #define FILEATRB_PER_MASK_TYPE 0770000
 #define FILEATRB_PER_FILE      0100000
+#define FILEATRB_PER_LINK      0120000
 #define FILEATRB_PER_DEV_CHAR  0020000
 #define FILEATRB_PER_DIR       0040000
 #define FILEATRB_PER_DEV_BLOCK 0060000
diff --git a/zephyr/samples/tests/prj.conf b/zephyr/samples/tests/prj.conf
index 8bdd3f29b..6b679071a 100644
--- a/zephyr/samples/tests/prj.conf
+++ b/zephyr/samples/tests/prj.conf
@@ -2,11 +2,11 @@
 CONFIG_MAIN_STACK_SIZE=32768
 CONFIG_ENTROPY_GENERATOR=y
 CONFIG_INIT_STACKS=y
-CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=131072
+CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=524288
 
 # Enable wolfSSH
 CONFIG_WOLFSSH=y
-CONFIG_WOLFSSH_SETTINGS_FILE="samples/tests/user_settings.h"
+CONFIG_WOLFSSH_SETTINGS_FILE="samples/tests/wolfssh_user_settings.h"
 CONFIG_WOLFSSH_SFTP_DEFAULT_DIR="/RAM:"
 
 # Pthreads
diff --git a/zephyr/samples/tests/prj_nofs.conf b/zephyr/samples/tests/prj_nofs.conf
new file mode 100644
index 000000000..4b628e8e8
--- /dev/null
+++ b/zephyr/samples/tests/prj_nofs.conf
@@ -0,0 +1,66 @@
+# Kernel options
+CONFIG_MAIN_STACK_SIZE=32768
+CONFIG_ENTROPY_GENERATOR=y
+CONFIG_INIT_STACKS=y
+CONFIG_COMMON_LIBC_MALLOC_ARENA_SIZE=131072
+
+# Enable wolfSSH
+CONFIG_WOLFSSH=y
+CONFIG_WOLFSSH_SETTINGS_FILE="samples/tests/wolfssh_user_settings_nofs.h"
+
+# Pthreads
+CONFIG_PTHREAD_IPC=y
+
+# Clock for time()
+CONFIG_POSIX_CLOCK=y
+
+# Networking
+CONFIG_NETWORKING=y
+CONFIG_NET_TEST=y
+CONFIG_NET_IPV4=y
+CONFIG_NET_IPV6=n
+CONFIG_NET_TCP=y
+CONFIG_NET_SOCKETS=y
+CONFIG_NET_SOCKETS_POSIX_NAMES=y
+
+CONFIG_NET_TEST=y
+CONFIG_NET_LOOPBACK=y
+
+# Network driver config
+CONFIG_TEST_RANDOM_GENERATOR=y
+
+# Network address config
+CONFIG_NET_CONFIG_SETTINGS=y
+CONFIG_NET_CONFIG_NEED_IPV4=y
+CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
+CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"
+CONFIG_NET_CONFIG_MY_IPV4_GW="192.0.2.2"
+
+CONFIG_NET_PKT_TX_COUNT=10
+
+# Logging
+CONFIG_PRINTK=y
+CONFIG_LOG=y
+CONFIG_LOG_MODE_IMMEDIATE=y
+#CONFIG_WOLFSSH_DEBUG=y
+#CONFIG_WOLFSSL_DEBUG=y
+#CONFIG_DEBUG=y
+
+# Enable logging using RTT and UART
+#CONFIG_CBPRINTF_LIBC_SUBSTS=y
+#CONFIG_CBPRINTF_FP_SUPPORT=y
+#CONFIG_CONSOLE=y
+#CONFIG_LOG_BACKEND_UART=y
+#CONFIG_LOG_BUFFER_SIZE=15360
+
+# TLS configuration
+CONFIG_WOLFSSL=y
+CONFIG_WOLFSSL_BUILTIN=y
+CONFIG_WOLFSSL_SETTINGS_FILE="samples/tests/wolfssl_user_settings_nofs.h"
+
+CONFIG_WOLFSSL_TLS_VERSION_1_2=y
+CONFIG_WOLFSSL_KEY_EXCHANGE_ALL_ENABLED=y
+CONFIG_WOLFSSL_CIPHER_ALL_ENABLED=y
+CONFIG_WOLFSSL_MAC_ALL_ENABLED=y
+CONFIG_WOLFSSL_HMAC_DRBG_ENABLED=y
+
diff --git a/zephyr/samples/tests/sample.yaml b/zephyr/samples/tests/sample.yaml
index 4f6f359cf..23abc38b9 100644
--- a/zephyr/samples/tests/sample.yaml
+++ b/zephyr/samples/tests/sample.yaml
@@ -13,3 +13,9 @@ tests:
     platform_allow: qemu_x86
     integration_platforms:
       - qemu_x86
+  sample.lib.wolfssh_nofs_tests:
+    timeout: 200
+    platform_allow: qemu_x86
+    extra_args: CONF_FILE="prj_nofs.conf"
+    integration_platforms:
+      - qemu_x86
diff --git a/zephyr/samples/tests/tests.c b/zephyr/samples/tests/tests.c
index 56d18c91b..69a8e3b26 100644
--- a/zephyr/samples/tests/tests.c
+++ b/zephyr/samples/tests/tests.c
@@ -1,6 +1,6 @@
 /* tests.c
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
@@ -37,14 +37,15 @@
 #include <stdio.h>
 #include <wolfssh/settings.h>
 #include <wolfssh/ssh.h>
-#include <zephyr/fs/fs.h>
-#include <zephyr/storage/disk_access.h>
-#include <ff.h>
-
 
+#ifndef NO_FILESYSTEM
 #ifndef CONFIG_FAT_FILESYSTEM_ELM
 #error "This test is designed for FAT FS"
 #endif
+#include <zephyr/fs/fs.h>
+#include <zephyr/storage/disk_access.h>
+#include <ff.h>
+#endif
 
 #define CHECK_TEST_RETURN(func) do {                \
     printf("\tRunning %s... ", #func);              \
@@ -60,6 +61,7 @@
 int main(void)
 {
     int ret = 0;
+#ifndef NO_FILESYSTEM
     static FATFS fat_fs;
     static struct fs_mount_t mnt_point = {
         .type = FS_FATFS,
@@ -89,6 +91,7 @@ int main(void)
                             < 0);
 
     CHECK_TEST_RETURN(fs_close(&zfp));
+#endif
 
     CHECK_TEST_RETURN(wolfSSH_UnitTest(0, NULL));
     CHECK_TEST_RETURN(wolfSSH_TestsuiteTest(0, NULL));
diff --git a/zephyr/samples/tests/user_settings.h b/zephyr/samples/tests/wolfssh_user_settings.h
similarity index 97%
rename from zephyr/samples/tests/user_settings.h
rename to zephyr/samples/tests/wolfssh_user_settings.h
index 0f755a549..1cfbb585f 100644
--- a/zephyr/samples/tests/user_settings.h
+++ b/zephyr/samples/tests/wolfssh_user_settings.h
@@ -1,6 +1,6 @@
 /* user_settings.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/zephyr/samples/tests/wolfssh_user_settings_nofs.h b/zephyr/samples/tests/wolfssh_user_settings_nofs.h
new file mode 100644
index 000000000..5df7f407c
--- /dev/null
+++ b/zephyr/samples/tests/wolfssh_user_settings_nofs.h
@@ -0,0 +1,68 @@
+/* user_settings.h
+ *
+ * Copyright (C) 2014-2024 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/>.
+ */
+
+#ifndef WOLFSSH_USER_SETTINGS_H
+#define WOLFSSH_USER_SETTINGS_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <wolfssl/wolfcrypt/types.h>
+
+#undef WOLFSSH_SCP
+#define WOLFSSH_SCP
+
+#undef NO_APITEST_MAIN_DRIVER
+#define NO_APITEST_MAIN_DRIVER
+
+#undef NO_TESTSUITE_MAIN_DRIVER
+#define NO_TESTSUITE_MAIN_DRIVER
+
+#undef NO_UNITTEST_MAIN_DRIVER
+#define NO_UNITTEST_MAIN_DRIVER
+
+#undef NO_MAIN_DRIVER
+#define NO_MAIN_DRIVER
+
+#undef WS_NO_SIGNAL
+#define WS_NO_SIGNAL
+
+#undef WS_USE_TEST_BUFFERS
+#define WS_USE_TEST_BUFFERS
+
+#undef NO_WOLFSSL_DIR
+#define NO_WOLFSSL_DIR
+
+#undef WOLFSSH_NO_NONBLOCKING
+#define WOLFSSH_NO_NONBLOCKING
+
+#define DEFAULT_WINDOW_SZ (128 * 128)
+#define WOLFSSH_MAX_SFTP_RW 8192
+
+#undef NO_FILESYSTEM
+#define NO_FILESYSTEM
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/zephyr/samples/tests/wolfssl_user_settings.h b/zephyr/samples/tests/wolfssl_user_settings.h
index 35cd345f6..03b12c94f 100644
--- a/zephyr/samples/tests/wolfssl_user_settings.h
+++ b/zephyr/samples/tests/wolfssl_user_settings.h
@@ -1,6 +1,6 @@
 /* wolfssl_user_settings.h
  *
- * Copyright (C) 2014-2023 wolfSSL Inc.
+ * Copyright (C) 2014-2024 wolfSSL Inc.
  *
  * This file is part of wolfSSH.
  *
diff --git a/zephyr/samples/tests/wolfssl_user_settings_nofs.h b/zephyr/samples/tests/wolfssl_user_settings_nofs.h
new file mode 100644
index 000000000..881b6b689
--- /dev/null
+++ b/zephyr/samples/tests/wolfssl_user_settings_nofs.h
@@ -0,0 +1,137 @@
+/* wolfssl_user_settings.h
+ *
+ * Copyright (C) 2014-2024 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/>.
+ */
+
+#ifndef WOLFSSL_USER_SETTINGS_H
+#define WOLFSSL_USER_SETTINGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#undef  WOLFSSL_ZEPHYR
+#define WOLFSSL_ZEPHYR
+
+#undef  TFM_TIMING_RESISTANT
+#define TFM_TIMING_RESISTANT
+
+#undef  ECC_TIMING_RESISTANT
+#define ECC_TIMING_RESISTANT
+
+#undef  WC_RSA_BLINDING
+#define WC_RSA_BLINDING
+
+#undef  HAVE_AESGCM
+#define HAVE_AESGCM
+
+#undef  WOLFSSL_SHA512
+#define WOLFSSL_SHA512
+
+#undef  WOLFSSL_SHA384
+#define WOLFSSL_SHA384
+
+#undef  NO_DSA
+#define NO_DSA
+
+#undef  HAVE_ECC
+#define HAVE_ECC
+
+#undef  TFM_ECC256
+#define TFM_ECC256
+
+#undef  WOLFSSL_BASE64_ENCODE
+#define WOLFSSL_BASE64_ENCODE
+
+#undef  NO_RC4
+#define NO_RC4
+
+#undef  WOLFSSL_SHA224
+#define WOLFSSL_SHA224
+
+#undef  WOLFSSL_SHA3
+#define WOLFSSL_SHA3
+
+#undef  HAVE_POLY1305
+#define HAVE_POLY1305
+
+#undef  HAVE_ONE_TIME_AUTH
+#define HAVE_ONE_TIME_AUTH
+
+#undef  HAVE_CHACHA
+#define HAVE_CHACHA
+
+#undef  HAVE_HASHDRBG
+#define HAVE_HASHDRBG
+
+#undef  HAVE_TLS_EXTENSIONS
+#define HAVE_TLS_EXTENSIONS
+
+#undef  HAVE_SUPPORTED_CURVES
+#define HAVE_SUPPORTED_CURVES
+
+#undef  HAVE_EXTENDED_MASTER
+#define HAVE_EXTENDED_MASTER
+
+#undef  NO_PSK
+#define NO_PSK
+
+#undef  NO_MD4
+#define NO_MD4
+
+#undef  NO_PWDBASED
+#define NO_PWDBASED
+
+#undef  USE_FAST_MATH
+#define USE_FAST_MATH
+
+#undef  WOLFSSL_NO_ASM
+#define WOLFSSL_NO_ASM
+
+#undef  WOLFSSL_X86_BUILD
+#define WOLFSSL_X86_BUILD
+
+#undef  WC_NO_ASYNC_THREADING
+#define WC_NO_ASYNC_THREADING
+
+#undef  NO_DES3
+#define NO_DES3
+
+#undef  WOLFSSL_STATIC_MEMORY
+#define WOLFSSL_STATIC_MEMORY
+
+#undef  WOLFSSL_TLS13
+#define WOLFSSL_TLS13
+
+#undef  HAVE_HKDF
+#define HAVE_HKDF
+
+#undef  WC_RSA_PSS
+#define WC_RSA_PSS
+
+#undef  HAVE_FFDHE_2048
+#define HAVE_FFDHE_2048
+
+#undef NO_FILESYSTEM
+#define NO_FILESYSTEM
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WOLFSSL_USER_SETTINGS_H */