From 77c8db6cc227faefa811d307d60875ab7a9093d5 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 1 Apr 2025 16:44:55 -0700 Subject: [PATCH 1/4] Rebase for tpm public key authentication with wolfssh --- .github/workflows/tpm-ssh.yml | 143 ++++++++++++++++ README.md | 61 ++++++- configure.ac | 14 ++ examples/client/client.c | 96 +++++++++-- examples/client/common.c | 269 +++++++++++++++++++++++++++++-- examples/client/common.h | 9 +- examples/echoserver/echoserver.c | 111 ++++++++++--- examples/scpclient/scpclient.c | 2 +- examples/sftpclient/sftpclient.c | 2 +- src/internal.c | 240 +++++++++++++++++++++------ src/ssh.c | 196 ++++++++++++++++++---- wolfssh/error.h | 2 +- wolfssh/internal.h | 44 ++++- wolfssh/ssh.h | 19 +++ 14 files changed, 1071 insertions(+), 137 deletions(-) create mode 100644 .github/workflows/tpm-ssh.yml diff --git a/.github/workflows/tpm-ssh.yml b/.github/workflows/tpm-ssh.yml new file mode 100644 index 00000000..70af3b8f --- /dev/null +++ b/.github/workflows/tpm-ssh.yml @@ -0,0 +1,143 @@ +name: TPM SSH Test + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] + +jobs: + test-tpm-ssh: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + path: wolfssh + + # Clone dependencies + - name: Clone wolfSSL + uses: actions/checkout@v4 + with: + repository: wolfSSL/wolfssl + path: wolfssl + + - name: Clone wolfTPM + uses: actions/checkout@v4 + with: + repository: wolfSSL/wolftpm + path: wolftpm + + # Install dependencies + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y libtool automake autoconf + sudo apt-get install -y build-essential git autoconf-archive \ + libcmocka-dev libssl-dev uthash-dev libglib2.0-dev \ + tpm2-tools openssh-client + + # Clone, build, and start TPM Simulator + - name: Clone and Build TPM Simulator + run: | + git clone https://github.com/kgoldman/ibmswtpm2 + cd ibmswtpm2/src + make + ./tpm_server & + sleep 2 + cd ../.. + + # Build and install wolfSSL + - name: Build wolfSSL + run: | + cd wolfssl + ./autogen.sh + ./configure --enable-wolftpm --enable-wolfssh + make + sudo make install + sudo ldconfig + cd .. + + # Build and install wolfTPM + - name: Build wolfTPM + run: | + cd wolftpm + ./autogen.sh + ./configure --enable-swtpm + make + sudo make install + sudo ldconfig + cd .. + + # Build wolfSSH + - name: Build wolfSSH + run: | + cd wolfssh + ./autogen.sh + ./configure --enable-tpm + make + sudo make install + sudo ldconfig + cd .. + + # Test TPM SSH Default Password + - name: Test TPM SSH Default Password + run: | + # Generate key with default password + cd wolftpm + ./examples/keygen/keygen keyblob.bin -rsa -t -pem -eh + + # Convert key to SSH format + ssh-keygen -f key.pem -i -m PKCS8 > ../wolfssh/key.ssh + cd .. + + # Start echoserver and wait for it to be ready + cd wolfssh + ./examples/echoserver/echoserver -1 -s key.ssh & + echo "Echoserver started with PID: $!" + sleep 2 + cd .. + + # Test client connection with default password + cd wolfssh + ./examples/client/client -i ../wolftpm/keyblob.bin -u hansel -K ThisIsMyKeyAuth + cd .. + + # Test the TPM SSH Custom Password + - name: Test TPM SSH Custom Password + run: | + # Test with custom password + cd wolftpm + ./examples/keygen/keygen keyblob2.bin -rsa -t -pem -eh -auth=custompassword + + # Convert key to SSH format + ssh-keygen -f key.pem -i -m PKCS8 > ../wolfssh/key.ssh + cd .. + + # Start echoserver and wait for it to be ready + cd wolfssh + ./examples/echoserver/echoserver -1 -s key.ssh & + echo "Echoserver started with PID: $!" + sleep 2 + cd .. + + # Test with custom password + cd wolfssh + ./examples/client/client -i ../wolftpm/keyblob2.bin -u hansel -K custompassword + cd .. + + # Cleanup + pkill -f tpm_server + sleep 2 + + # Archive artifacts for debugging + - name: Archive test artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: test-artifacts + path: | + wolftpm/keyblob.bin + wolftpm/keyblob2.bin + wolftpm/key.pem + wolfssh/key.ssh diff --git a/README.md b/README.md index b3266a83..6de7a02f 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,13 @@ Additional build options for wolfSSL are located in [chapter two](https://www.wolfssl.com/docs/wolfssl-manual/ch2/). of the wolfSSH manual. + building -------- From the wolfSSH source directory run: - $ ./autogen.sh + $ ./autogen.sh (if cloned from GitHub) $ ./configure --with-wolfssl=[/usr/local] $ make $ make check @@ -528,6 +529,64 @@ fred-cert.der would be: $ ./examples/client/client -u fred -J ./keys/fred-cert.der -i ./keys/fred-key.der +TPM PUBLIC KEY AUTHENTICATION +============================= + +When using TPM for client side public key authentication wolfSSH has dependencies +on wolfCrypt and wolfTPM. Youll also need to have a tpm simulator +[wolfTPM](https://www.wolfssl.com/products/wolftpm/) +[wolfSSL](https://www.wolfssl.com/products/wolfssl/) +You'll need to build and configure wolfTPM, wolfSSL, and wolfSSH like so: + + $ cd + $ ./autogen.sh (if cloned from GitHub) + $ + $ make + $ make check + + + wolfSSL + $ ./configure --enable-wolftpm --enable-wolfssh + wolfTPM + $ ./configure --enable-swtpm + wolfSSH + $ ./configure --enable-tpm + +For testing TPM with private rsa key you'll need to run the server from a TPM +simulator like `ibmswtpm2`. This can be done as followed: + + $ cd src + $ ./tpm_server + +Before starting the echoserver you need to run the keygen for keyblob +using the endorsment key in wolfTPM with the following commands: +Default password to `ThisIsMyKeyAuth`: + + $ ./examples/keygen/keygen keyblob.bin -rsa -t -pem -eh + +Custom password: + + $ ./examples/keygen/keygen keyblob.bin -rsa -t -pem -eh -auth= + +This will produce a key.pem TPM public key which needs to be converted the to +the ssh-rsa BASE64 username format using this command: + + $ ssh-keygen -f key.pem -i -m PKCS8 > ../wolfssh/key.ssh + +The directory `examples` contains an echoserver that any client should +be able to connect to. From wolfSSH open two terminal instances and run the +server with the key.ssh file you created in the previous step: + + $ ./examples/echoserver/echoserver -s key.ssh + +From another terminal run the client with the keyblob. Using primary endorsement key +If you used the default password for keygen you must specify the password: + + $ ./examples/client/client -i ../wolfTPM/keyblob.bin -u hansel -K ThisIsMyKeyAuth + +If you used a custom password for keygen you must specify the password you used: + + $ ./examples/client/client -i ../wolfTPM/keyblob.bin -u hansel -K WOLFSSH APPLICATIONS ==================== diff --git a/configure.ac b/configure.ac index db2d930d..9fd557e0 100644 --- a/configure.ac +++ b/configure.ac @@ -171,6 +171,16 @@ AC_ARG_ENABLE([certs], [AS_HELP_STRING([--enable-certs],[Enable X.509 cert support (default: disabled)])], [ENABLED_CERTS=$enableval],[ENABLED_CERTS=no]) +# TPM 2.0 Support +AC_ARG_ENABLE([tpm], + [AS_HELP_STRING([--enable-tpm],[Enable TPM 2.0 support (default: disabled)])], + [ENABLED_TPM=$enableval],[ENABLED_TPM=no]) + +if test "$ENABLED_TPM" != "no" +then + AC_CHECK_LIB([wolftpm],[wolfTPM2_Init],,[AC_MSG_ERROR([libwolftpm is required for ${PACKAGE}. It can be obtained from https://www.wolfssl.com/download.html/ .])]) +fi + # smallstack AC_ARG_ENABLE([smallstack], [AS_HELP_STRING([--enable-smallstack],[Enable small stack (default: disabled)])], @@ -225,6 +235,8 @@ AS_IF([test "x$ENABLED_SSHD" = "xyes"], [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SSHD"]) AS_IF([test "x$ENABLED_SSHCLIENT" = "xyes"], [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SSHCLIENT"]) +AS_IF([test "x$ENABLED_TPM" = "xyes"], + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_TPM"]) if test "$ENABLED_SSHD" = "yes"; then if test -n "$PAM_LIB" @@ -279,6 +291,7 @@ AM_CONDITIONAL([BUILD_AGENT],[test "x$ENABLED_AGENT" = "xyes"]) AM_CONDITIONAL([BUILD_SSHD],[test "x$ENABLED_SSHD" = "xyes"]) AM_CONDITIONAL([BUILD_SSHCLIENT],[test "x$ENABLED_SSHCLIENT" = "xyes"]) AM_CONDITIONAL([BUILD_CERTS],[test "x$ENABLED_CERTS" = "xyes"]) +AM_CONDITIONAL([BUILD_TPM],[test "x$ENABLED_TPM" = "xyes"]) AX_HARDEN_CC_COMPILER_FLAGS @@ -322,6 +335,7 @@ AS_ECHO([" * sftp: $ENABLED_SFTP"]) AS_ECHO([" * sshd: $ENABLED_SSHD"]) AS_ECHO([" * ssh client: $ENABLED_SSHCLIENT"]) AS_ECHO([" * agent: $ENABLED_AGENT"]) +AS_ECHO([" * TPM 2.0 support: $ENABLED_TPM"]) AS_ECHO([" * TCP/IP Forwarding: $ENABLED_FWD"]) AS_ECHO([" * X.509 Certs: $ENABLED_CERTS"]) AS_ECHO([" * Examples: $ENABLED_EXAMPLES"]) diff --git a/examples/client/client.c b/examples/client/client.c index c6cde31b..1329de39 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -74,6 +74,10 @@ #include #endif +#ifdef WOLFSSH_TPM + #include + #include +#endif /* WOLFSSH_TPM */ #ifndef NO_WOLFSSH_CLIENT @@ -89,6 +93,9 @@ static void ShowUsage(void) printf(" -p port to connect on, default %d\n", wolfSshPort); printf(" -u username to authenticate as (REQUIRED)\n"); printf(" -P password for username, prompted if omitted\n"); +#ifdef WOLFSSH_TPM + printf(" -K TPM key authentication password\n"); +#endif printf(" -i filename for the user's private key\n"); printf(" -j filename for the user's public key\n"); printf(" -x exit after successful connection without doing\n" @@ -125,7 +132,9 @@ static void ShowUsage(void) static const char* pubKeyName = NULL; -static const char* certName = NULL; +#ifdef WOLFSSH_CERTS + static const char* certName = NULL; +#endif static const char* caCert = NULL; @@ -549,7 +558,7 @@ static int wolfSSH_AGENT_DefaultActions(WS_AgentCbAction action, void* vCtx) ret = WS_AGENT_NOT_AVAILABLE; if (ret == WS_AGENT_SUCCESS) { - memset(name, 0, sizeof(struct sockaddr_un)); + WMEMSET(name, 0, sizeof(struct sockaddr_un)); name->sun_family = AF_LOCAL; strncpy(name->sun_path, sockName, sizeof(name->sun_path)); name->sun_path[sizeof(name->sun_path) - 1] = '\0'; @@ -638,6 +647,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) char* host = (char*)wolfSshIp; const char* username = NULL; const char* password = NULL; + const char* tpmKeyAuth = NULL; const char* cmd = NULL; const char* privKeyName = NULL; const char* keyList = NULL; @@ -659,7 +669,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:XeEk:q")) != -1) { + while ((ch = mygetopt(argc, argv, "?ac:h:i:j:p:tu:xzNP:RJ:A:XeEk:qK:")) != -1) { switch (ch) { case 'h': host = myoptarg; @@ -763,6 +773,12 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) break; #endif + #ifdef WOLFSSH_TPM + case 'K': + tpmKeyAuth = myoptarg; + break; + #endif + case '?': ShowUsage(); exit(EXIT_SUCCESS); @@ -781,12 +797,23 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (keepOpen) err_sys("Threading needed for terminal session\n"); #endif - +#ifndef WOLFSSH_TPM + #ifdef WOLFSSH_CERTS if ((pubKeyName == NULL && certName == NULL) && privKeyName != NULL) { err_sys("If setting priv key, need pub key."); } - - ret = ClientSetPrivateKey(privKeyName, userEcc, NULL); + #else + if (pubKeyName == NULL && privKeyName != NULL) { + err_sys("If setting priv key, need pub key."); + } + #endif +#endif +#ifdef WOLFSSH_TPM + if (tpmKeyAuth == NULL) { + err_sys("You must specify a password for the TPM key"); + } +#endif + ret = ClientSetPrivateKey(privKeyName, userEcc, NULL, tpmKeyAuth); if (ret != 0) { err_sys("Error setting private key"); } @@ -840,6 +867,10 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (ssh == NULL) err_sys("Couldn't create wolfSSH session."); +#ifdef WOLFSSH_TPM + if (tpmKeyAuth != NULL) + ClientSetTpm(ssh); +#endif #if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) wolfSSH_SetGlobalReq(ctx, callbackGlobalReq); wolfSSH_SetGlobalReqCtx(ssh, &ssh); /* dummy ctx */ @@ -850,7 +881,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) #ifdef WOLFSSH_AGENT if (useAgent) { - memset(&agentCbCtx, 0, sizeof(agentCbCtx)); + WMEMSET(&agentCbCtx, 0, sizeof(agentCbCtx)); agentCbCtx.state = AGENT_STATE_INIT; wolfSSH_set_agent_cb_ctx(ssh, &agentCbCtx); } @@ -913,28 +944,44 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) tcp_socket(&sockFd, ((struct sockaddr_in *)&clientAddr)->sin_family); ret = connect(sockFd, (const struct sockaddr *)&clientAddr, clientAddrSz); - if (ret != 0) + if (ret != 0) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Couldn't connect to server."); + } if (nonBlock) tcp_set_nonblocking(&sockFd); ret = wolfSSH_set_fd(ssh, (int)sockFd); - if (ret != WS_SUCCESS) + if (ret != WS_SUCCESS) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Couldn't set the session's socket."); + } if (cmd != NULL) { ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_EXEC, (byte*)cmd, (word32)WSTRLEN((char*)cmd)); - if (ret != WS_SUCCESS) + if (ret != WS_SUCCESS) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Couldn't set the channel type."); + } } #ifdef WOLFSSH_TERM if (keepOpen) { ret = wolfSSH_SetChannelType(ssh, WOLFSSH_SESSION_TERMINAL, NULL, 0); - if (ret != WS_SUCCESS) + if (ret != WS_SUCCESS) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Couldn't set the terminal channel type."); + } } #endif @@ -942,8 +989,12 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) ret = wolfSSH_connect(ssh); else ret = NonBlockSSH_connect(ssh); - if (ret != WS_SUCCESS) + if (ret != WS_SUCCESS) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Couldn't connect SSH stream."); + } #if !defined(SINGLE_THREADED) && !defined(WOLFSSL_NUCLEUS) && \ defined(WOLFSSH_TERM) && !defined(NO_FILESYSTEM) @@ -1040,16 +1091,23 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) #endif ret = wolfSSH_stream_send(ssh, (byte*)testString, (word32)strlen(testString)); - if (ret <= 0) + if (ret <= 0) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Couldn't send test string."); - + } do { ret = wolfSSH_stream_read(ssh, (byte*)rxBuf, sizeof(rxBuf) - 1); if (ret <= 0) { ret = wolfSSH_get_error(ssh); if (ret != WS_WANT_READ && ret != WS_WANT_WRITE && - ret != WS_CHAN_RXD) + ret != WS_CHAN_RXD) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Stream read failed."); + } } } while (ret == WS_WANT_READ || ret == WS_WANT_WRITE); @@ -1065,11 +1123,17 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) 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) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Sending the shutdown messages failed."); } ret = wolfSSH_worker(ssh, NULL); if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E && ret != WS_CHANNEL_CLOSED) { + ClientFreeBuffers(pubKeyName, privKeyName, NULL); + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); err_sys("Failed to listen for close messages from the peer."); } } @@ -1079,6 +1143,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) ((func_args*)args)->return_code = wolfSSH_GetExitStatus(ssh); #endif + ClientFreeBuffers(pubKeyName, privKeyName, NULL); wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E && @@ -1086,7 +1151,6 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) err_sys("Closing client stream failed"); } - 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/common.c b/examples/client/common.c index dc3a3c80..344f7f10 100644 --- a/examples/client/common.c +++ b/examples/client/common.c @@ -33,6 +33,12 @@ #include #include #include + +#ifdef WOLFSSH_TPM + #include + #include +#endif + #include "examples/client/common.h" #if !defined(USE_WINDOWS_API) && !defined(MICROCHIP_PIC32) && \ !defined(WOLFSSH_ZEPHYR) @@ -53,7 +59,8 @@ static byte pubKeyLoaded = 0; /* was a public key loaded */ static byte userPrivateKeyBuf[1191]; /* Size equal to hanselPrivateRsaSz. */ static byte* userPrivateKey = userPrivateKeyBuf; static word32 userPublicKeyTypeSz = 0; -static word32 userPrivateKeySz = sizeof(userPrivateKeyBuf); +static byte userPrivateKeyAlloc = 0; +static word32 userPrivateKeySz = 0; static word32 userPrivateKeyTypeSz = 0; static byte isPrivate = 0; @@ -73,6 +80,10 @@ static const byte publicKeyType[] = "x509v3-ecdsa-sha2-nistp256"; #endif +#ifdef WOLFSSH_TPM + WOLFTPM2_DEV tpmDev; + WOLFTPM2_KEY tpmKey; +#endif /* WOLFSSH_TPM */ #ifndef WOLFSSH_NO_RSA @@ -743,16 +754,235 @@ int ClientUseCert(const char* certName, void* heap) return ret; } +#ifdef WOLFSSH_TPM + +static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) +{ + int rc = 0; +#if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) + XFILE fp = NULL; + size_t fileSz = 0; + size_t bytes_read = 0; + byte pubAreaBuffer[sizeof(TPM2B_PUBLIC)]; + int pubAreaSize; + + WLOG(WS_LOG_DEBUG, "Entering readKeyBlob()"); + + XMEMSET(key, 0, sizeof(WOLFTPM2_KEYBLOB)); + + fp = XFOPEN(filename, "rb"); + if (fp != XBADFILE) { + XFSEEK(fp, 0, XSEEK_END); + fileSz = XFTELL(fp); + XREWIND(fp); + if (fileSz > sizeof(key->priv) + sizeof(key->pub)) { + printf("File size check failed\n"); + rc = BUFFER_E; goto exit; + } + printf("Reading %d bytes from %s\n", (int)fileSz, filename); + + bytes_read = XFREAD(&key->pub.size, 1, sizeof(key->pub.size), fp); + if (bytes_read != sizeof(key->pub.size)) { + printf("Read %zu, expected size marker of %zu bytes\n", + bytes_read, sizeof(key->pub.size)); + goto exit; + } + fileSz -= bytes_read; + + bytes_read = XFREAD(pubAreaBuffer, 1, sizeof(UINT16) + key->pub.size, fp); + if (bytes_read != sizeof(UINT16) + key->pub.size) { + printf("Read %zu, expected public blob %zu bytes\n", + bytes_read, sizeof(UINT16) + key->pub.size); + goto exit; + } + fileSz -= bytes_read; /* Reminder bytes for private key part */ + + /* Decode the byte stream into a publicArea structure ready for use */ + rc = TPM2_ParsePublic(&key->pub, pubAreaBuffer, + (word32)sizeof(pubAreaBuffer), &pubAreaSize); + if (rc != 0) return rc; + + if (fileSz > 0) { + printf("Reading the private part of the key\n"); + bytes_read = XFREAD(&key->priv, 1, fileSz, fp); + if (bytes_read != fileSz) { + printf("Read %zu, expected private blob %zu bytes\n", + bytes_read, fileSz); + goto exit; + } + rc = 0; /* success */ + } + + /* sanity check the sizes */ + if (pubAreaSize != (key->pub.size + (int)sizeof(key->pub.size)) || + key->priv.size > sizeof(key->priv.buffer)) { + printf("Struct size check failed (pub %d, priv %d)\n", + key->pub.size, key->priv.size); + rc = BUFFER_E; + } + } + else { + rc = BUFFER_E; + printf("File %s not found!\n", filename); + printf("Keys can be generated by running:\n" + " ./examples/keygen/keygen rsa_test_blob.raw -rsa -t\n" + " ./examples/keygen/keygen ecc_test_blob.raw -ecc -t\n"); + } + +exit: + if (fp) + XFCLOSE(fp); +#else + (void)filename; + (void)key; +#endif /* !NO_FILESYSTEM && !NO_WRITE_TEMP_FILES */ + WLOG(WS_LOG_DEBUG, "Leaving readKeyBlob(), rc = %d", rc); + return rc; +} + +static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, + WOLFTPM2_KEY* pTpmKey, const char* tpmKeyAuth) +{ + int rc = 0; + WOLFTPM2_KEY endorse; + WOLFTPM2_KEYBLOB tpmKeyBlob; + WOLFTPM2_SESSION tpmSession; + byte* p = NULL; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_TPM_InitKey()"); + + /* Initialize the TPM 2.0 device */ + if (rc == 0) { + rc = wolfTPM2_Init(dev, TPM2_IoCb, NULL); + if (rc != 0) { + WLOG(WS_LOG_DEBUG, + "TPM 2.0 Device initialization failed, rc: %d", rc); + } + } + + /* Create primary endorsement key (EK) */ + if (rc == 0) { + rc = wolfTPM2_CreateEK(dev, &endorse, TPM_ALG_RSA); + if (rc != 0) { + WLOG(WS_LOG_DEBUG, "Creating EK failed, rc: %d", rc); + } + } + + /* Create and set policy session for EK */ + if (rc == 0) { + endorse.handle.policyAuth = 1; + rc = wolfTPM2_CreateAuthSession_EkPolicy(dev, &tpmSession); + if (rc != 0) { + WLOG(WS_LOG_DEBUG, + "Creating EK policy session failed, rc: %d", rc); + } + } + + if (rc == 0) { + rc = wolfTPM2_SetAuthSession(dev, 0, &tpmSession, 0); + if (rc != 0) { + WLOG(WS_LOG_DEBUG, "Setting auth session failed, rc: %d", rc); + } + } + + /* Load the TPM 2.0 key blob from disk */ + if (rc == 0) { + rc = readKeyBlob(name, &tpmKeyBlob); + if (rc != 0) { + WLOG(WS_LOG_DEBUG, + "Reading key blob from disk failed, rc: %d", rc); + } + } + + /* Use global auth if provided */ + if (rc == 0 && tpmKeyAuth != NULL) { + tpmKeyBlob.handle.auth.size = (word32)XSTRLEN(tpmKeyAuth); + XMEMCPY(tpmKeyBlob.handle.auth.buffer, tpmKeyAuth, + tpmKeyBlob.handle.auth.size); + } + + /* Load the public key into the TPM device */ + if (rc == 0) { + rc = wolfTPM2_LoadKey(dev, &tpmKeyBlob, &endorse.handle); + if (rc != 0) { + WLOG(WS_LOG_DEBUG, "wolfTPM2_LoadKey failed, rc: %d", rc); + } else { + WLOG(WS_LOG_DEBUG, "Loaded key to 0x%x\n", + (word32)tpmKeyBlob.handle.hndl); + } + } + + /* Read the public key and extract the public key as a DER/ASN.1 */ + if (rc == 0) { + userPublicKeySz = sizeof(userPublicKeyBuf); + rc = wolfTPM2_ExportPublicKeyBuffer(dev, (WOLFTPM2_KEY*)&tpmKeyBlob, + ENCODING_TYPE_ASN1, userPublicKey, &userPublicKeySz); + if (rc != 0) { + WLOG(WS_LOG_DEBUG, "Exporting TPM key failed, rc: %d", rc); + } + } + + /* Read public key from buffer and convert key to OpenSSH format */ + if (rc == 0) { + rc = wolfSSH_ReadPublicKey_buffer(userPublicKey, userPublicKeySz, + WOLFSSH_FORMAT_ASN1, &p, &userPublicKeySz, &userPublicKeyType, + &userPublicKeyTypeSz, NULL); + if (rc == 0) { + userPublicKey = p; + } else { + WLOG(WS_LOG_DEBUG, "Reading public key failed, rc: %d", rc); + } + } + + /* Copy key info */ + if (rc == 0) { + XMEMCPY(&pTpmKey->handle, &tpmKeyBlob.handle, sizeof(pTpmKey->handle)); + XMEMCPY(&pTpmKey->pub, &tpmKeyBlob.pub, sizeof(pTpmKey->pub)); + wolfTPM2_UnloadHandle(dev, &endorse.handle); + } + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_TPM_InitKey(), rc = %d", rc); + return rc; +} + +static void wolfSSH_TPM_Cleanup(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_TPM_Cleanup()"); + if (key != NULL) { + wolfTPM2_UnloadHandle(dev, &key->handle); + } + + if (dev != NULL) { + wolfTPM2_Cleanup(dev); + } + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_TPM_Cleanup()"); +} + +/* Set the tpm device and key for the client side */ +int ClientSetTpm(WOLFSSH* ssh) +{ + if (ssh != NULL) { + wolfSSH_SetTpmDev(ssh, &tpmDev); + wolfSSH_SetTpmKey(ssh, &tpmKey); + } + return 0; +} + +#endif /* WOLFSSH_TPM */ + /* Reads the private key to use from file name privKeyName. * returns 0 on success */ -int ClientSetPrivateKey(const char* privKeyName, int userEcc, void* heap) +int ClientSetPrivateKey(const char* privKeyName, int userEcc, + void* heap, const char* tpmKeyAuth) { int ret = 0; + (void)tpmKeyAuth; /* Not used */ if (privKeyName == NULL) { if (userEcc) { #ifndef WOLFSSH_NO_ECC + userPrivateKeySz = sizeof(userPrivateKeyBuf); ret = wolfSSH_ReadKey_buffer(hanselPrivateEcc, hanselPrivateEccSz, WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, &userPrivateKeyType, &userPrivateKeyTypeSz, heap); @@ -760,6 +990,7 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc, void* heap) } else { #ifndef WOLFSSH_NO_RSA + userPrivateKeySz = sizeof(userPrivateKeyBuf); ret = wolfSSH_ReadKey_buffer(hanselPrivateRsa, hanselPrivateRsaSz, WOLFSSH_FORMAT_ASN1, &userPrivateKey, &userPrivateKeySz, &userPrivateKeyType, &userPrivateKeyTypeSz, heap); @@ -768,8 +999,23 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc, void* heap) isPrivate = 1; } else { - #ifndef NO_FILESYSTEM + #if defined(WOLFSSH_TPM) + /* Protecting the SSH Private Key using a TPM 2.0 device + * + * TPM-backed keys do not require a user buffer, because + * the private key is loaded securely inside the TPM and + * used only from within the TPM for higher security. + * + * Successfully loaded TPM key has a TPM Handle that is + * later passed to wolfSSH for use + */ + WMEMSET(&tpmDev, 0, sizeof(tpmDev)); + WMEMSET(&tpmKey, 0, sizeof(tpmKey)); + ret = wolfSSH_TPM_InitKey(&tpmDev, privKeyName, &tpmKey, tpmKeyAuth); + #elif !defined(NO_FILESYSTEM) userPrivateKey = NULL; /* create new buffer based on parsed input */ + userPrivateKeyAlloc = 1; + userPrivateKeySz = sizeof(userPrivateKeyBuf); ret = wolfSSH_ReadKey_file(privKeyName, (byte**)&userPrivateKey, &userPrivateKeySz, (const byte**)&userPrivateKeyType, &userPrivateKeyTypeSz, @@ -777,13 +1023,12 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc, void* heap) #else printf("file system not compiled in!\n"); ret = NOT_COMPILED_IN; - #endif + #endif /* WOLFSSH_TPM / NO_FILESYSTEM */ } return ret; } - /* Set public key to use * returns 0 on success */ int ClientUsePubKey(const char* pubKeyName, int userEcc, void* heap) @@ -821,8 +1066,8 @@ int ClientUsePubKey(const char* pubKeyName, int userEcc, void* heap) &isPrivate, heap); #else printf("file system not compiled in!\n"); - ret = -1; - #endif + ret = NOT_COMPILED_IN; + #endif /* NO_FILESYSTEM */ if (ret == 0) { pubKeyLoaded = 1; } @@ -859,16 +1104,20 @@ int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert) return ret; } - void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName, void* heap) { word32 entry; - if (pubKeyName != NULL && userPublicKey != NULL) { +#ifdef WOLFSSH_TPM + wolfSSH_TPM_Cleanup(&tpmDev, &tpmKey); +#endif + if (pubKeyName != NULL && userPublicKey != NULL && + userPublicKey != userPublicKeyBuf) { WFREE(userPublicKey, heap, DYNTYPE_PRIVKEY); } - if (privKeyName != NULL && userPrivateKey != NULL) { + if (privKeyName != NULL && userPrivateKey != NULL && + userPrivateKeyAlloc) { WFREE(userPrivateKey, heap, DYNTYPE_PRIVKEY); } diff --git a/examples/client/common.h b/examples/client/common.h index 395d4288..e5ab221c 100644 --- a/examples/client/common.h +++ b/examples/client/common.h @@ -22,16 +22,19 @@ #define WOLFSSH_COMMON_H int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert); int ClientUsePubKey(const char* pubKeyName, int userEcc, void* heap); -int ClientSetPrivateKey(const char* privKeyName, int userEcc, void* heap); +int ClientSetPrivateKey(const char* privKeyName, int userEcc, + void* heap, const char* tpmKeyAuth); int ClientUseCert(const char* certName, void* heap); int ClientSetEcho(int type); int ClientUserAuth(byte authType, - WS_UserAuthData* authData, - void* ctx); + 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* heap); +#ifdef WOLFSSH_TPM +int ClientSetTpm(WOLFSSH* ssh); +#endif #endif /* WOLFSSH_COMMON_H */ diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index f0016134..29a1fb47 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "examples/echoserver/echoserver.h" @@ -1783,21 +1784,24 @@ static const char samplePublicKeyEccBuffer[] = #endif #ifndef WOLFSSH_NO_RSA -static const char samplePublicKeyRsaBuffer[] = - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGho" - "MNiisWSgUs5HdHcACuHYPi2W6Z1PBFmBWT9odOrGRjoZXJfDDoPi+j8SSfDGsc/hsCmc3G" - "p2yEhUZUEkDhtOXyqjns1ickC9Gh4u80aSVtwHRnJZh9xPhSq5tLOhId4eP61s+a5pwjTj" - "nEhBaIPUJO2C/M0pFnnbZxKgJlX7t1Doy7h5eXxviymOIvaCZKU+x5OopfzM/wFkey0EPW" - "NmzI5y/+pzU5afsdeEWdiQDIQc80H6Pz8fsoFPvYSG+s4/wz0duu7yeeV1Ypoho65Zr+pE" - "nIf7dO0B8EblgWt+ud+JI8wrAhfE4x hansel\n" - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqDwRVTRVk/wjPhoo66+Mztrc31KsxDZ" - "+kAV0139PHQ+wsueNpba6jNn5o6mUTEOrxrz0LMsDJOBM7CmG0983kF4gRIihECpQ0rcjO" - "P6BSfbVTE9mfIK5IsUiZGd8SoE9kSV2pJ2FvZeBQENoAxEFk0zZL9tchPS+OCUGbK4SDjz" - "uNZl/30Mczs73N3MBzi6J1oPo7sFlqzB6ecBjK2Kpjus4Y1rYFphJnUxtKvB0s+hoaadru" - "biE57dK6BrH5iZwVLTQKux31uCJLPhiktI3iLbdlGZEctJkTasfVSsUizwVIyRjhVKmbdI" - "RGwkU38D043AR1h0mUoGCPIKuqcFMf gretel\n"; -#endif - +#ifdef WOLFSSH_TPM + static const char* sampleTpmPublicKeyRsaBuffer = ""; +#else + static const char* samplePublicKeyRsaBuffer = + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCqDwRVTRVk/wjPhoo66+Mztrc31KsxDZ" + "+kAV0139PHQ+wsueNpba6jNn5o6mUTEOrxrz0LMsDJOBM7CmG0983kF4gRIihECpQ0rcjO" + "P6BSfbVTE9mfIK5IsUiZGd8SoE9kSV2pJ2FvZeBQENoAxEFk0zZL9tchPS+OCUGbK4SDjz" + "uNZl/30Mczs73N3MBzi6J1oPo7sFlqzB6ecBjK2Kpjus4Y1rYFphJnUxtKvB0s+hoaadru" + "biE57dK6BrH5iZwVLTQKux31uCJLPhiktI3iLbdlGZEctJkTasfVSsUizwVIyRjhVKmbdI" + "RGwkU38D043AR1h0mUoGCPIKuqcFMf gretel\n" + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGho" + "MNiisWSgUs5HdHcACuHYPi2W6Z1PBFmBWT9odOrGRjoZXJfDDoPi+j8SSfDGsc/hsCmc3G" + "p2yEhUZUEkDhtOXyqjns1ickC9Gh4u80aSVtwHRnJZh9xPhSq5tLOhId4eP61s+a5pwjTj" + "nEhBaIPUJO2C/M0pFnnbZxKgJlX7t1Doy7h5eXxviymOIvaCZKU+x5OopfzM/wFkey0EPW" + "NmzI5y/+pzU5afsdeEWdiQDIQc80H6Pz8fsoFPvYSG+s4/wz0duu7yeeV1Ypoho65Zr+pE" + "nIf7dO0B8EblgWt+ud+JI8wrAhfE4x hansel\n"; +#endif /* WOLFSSH_TPM */ +#endif /* WOLFSSH_NO_RSA */ #ifdef WOLFSSH_ALLOW_USERAUTH_NONE @@ -2094,6 +2098,46 @@ static int LoadPubKeyList(StrList* strList, int format, PwMapList* mapList) } #endif +#ifdef WOLFSSH_TPM +static char* LoadTpmSshKey(const char* keyFile) +{ + FILE* file; + char* buffer = NULL; + char* ret = NULL; + long length; + + file = fopen(keyFile, "rb"); + if (!file) { + fprintf(stderr, + "Failed to open TPM key file: %s\n", keyFile); + return NULL; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + fseek(file, 0, SEEK_SET); + + buffer = (char*)WMALLOC(length + 8 + 1, NULL, DYNTYPE_BUFFER); + if (buffer) { + if (fread(buffer, 1, length, file) == (size_t)length) { + while (length > 0 && (buffer[length-1] == '\n' || + buffer[length-1] == '\r')) { + length--; + } + WMEMCPY(buffer + length, " hansel\n", 8); + buffer[length + 8] = '\0'; + ret = buffer; + } + else { + WFREE(buffer, NULL, DYNTYPE_BUFFER); + } + } + + fclose(file); + return ret; +} +#endif + static int wsUserAuthResult(byte res, WS_UserAuthData* authData, void* ctx) @@ -2357,6 +2401,7 @@ static void ShowUsage(void) " (user assumed in comment)\n"); printf(" -I :\n" " load in a SSH public key to accept from peer\n"); + printf(" -s load in a TPM public key file to replace default hansel key\n"); printf(" -J :\n" " load in an X.509 PEM cert to accept from peer\n"); printf(" -K :\n" @@ -2419,6 +2464,9 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) const char* macList = NULL; const char* cipherList = NULL; ES_HEAP_HINT* heap = NULL; + #ifdef WOLFSSH_TPM + static char* tpmKeyPath = NULL; + #endif int multipleConnections = 1; int userEcc = 0; int peerEcc = 0; @@ -2441,7 +2489,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) kbAuthData.promptCount = 0; if (argc > 0) { - const char* optlist = "?1a:d:efEp:R:Ni:j:i:I:J:K:P:k:b:x:m:c:"; + const char* optlist = "?1a:d:efEp:R:Ni:j:i:I:J:K:P:k:b:x:m:c:s:"; myoptind = 0; while ((ch = mygetopt(argc, argv, optlist)) != -1) { switch (ch) { @@ -2545,6 +2593,12 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) cipherList = myoptarg; break; + case 's': + #ifdef WOLFSSH_TPM + tpmKeyPath = myoptarg; + #endif + break; + default: ShowUsage(); serverArgs->return_code = MY_EX_USAGE; @@ -2577,6 +2631,23 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) ES_ERROR("Couldn't initialize wolfSSH.\n"); } + /* Load custom TPM key if specified */ + #ifdef WOLFSSH_TPM + if (tpmKeyPath != NULL) { + const char* newBuffer = LoadTpmSshKey(tpmKeyPath); + if (newBuffer != NULL) { + sampleTpmPublicKeyRsaBuffer = newBuffer; + } + else { + ES_ERROR("Failed to load TPM key from %s\n", tpmKeyPath); + } + printf("New sampleTpmPublicKeyRsaBuffer:\n%s\n", sampleTpmPublicKeyRsaBuffer); + } + else { + printf("No TPM key loaded\n"); + } + #endif + #ifdef WOLFSSH_STATIC_MEMORY { int ret; @@ -2791,7 +2862,11 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) } else { #ifndef WOLFSSH_NO_RSA - bufName = samplePublicKeyRsaBuffer; + #ifdef WOLFSSH_TPM + bufName = sampleTpmPublicKeyRsaBuffer; + #else + bufName = samplePublicKeyRsaBuffer; + #endif #endif } if (bufName != NULL) { @@ -2988,8 +3063,6 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) #endif /* NO_WOLFSSH_SERVER */ -void wolfSSL_Debugging_ON(void); - int wolfSSH_Echoserver(int argc, char** argv) { func_args args; diff --git a/examples/scpclient/scpclient.c b/examples/scpclient/scpclient.c index 1d6ca0b1..c4860393 100644 --- a/examples/scpclient/scpclient.c +++ b/examples/scpclient/scpclient.c @@ -218,7 +218,7 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args) err_sys("Empty path values"); } - ret = ClientSetPrivateKey(privKeyName, 0, NULL); + ret = ClientSetPrivateKey(privKeyName, 0, NULL, NULL); if (ret != 0) { err_sys("Error setting private key"); } diff --git a/examples/sftpclient/sftpclient.c b/examples/sftpclient/sftpclient.c index 744c79b7..c1baba67 100644 --- a/examples/sftpclient/sftpclient.c +++ b/examples/sftpclient/sftpclient.c @@ -1317,7 +1317,7 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args) } #endif /* WOLFSSH_STATIC_MEMORY */ - ret = ClientSetPrivateKey(privKeyName, userEcc, heap); + ret = ClientSetPrivateKey(privKeyName, userEcc, heap, NULL); if (ret != 0) { err_sys("Error setting private key"); } diff --git a/src/internal.c b/src/internal.c index 26b65c03..c6da6b8c 100644 --- a/src/internal.c +++ b/src/internal.c @@ -70,6 +70,12 @@ #endif #endif +#ifdef WOLFSSH_TPM + #include + #include +#endif + +#include /* Flags: @@ -1209,43 +1215,22 @@ void SshResourceFree(WOLFSSH* ssh, void* heap) } -typedef struct WS_KeySignature { - byte keySigId; - word32 sigSz; - const char *name; - void *heap; - word32 nameSz; - union { -#ifndef WOLFSSH_NO_RSA - struct { - RsaKey key; - } rsa; -#endif -#ifndef WOLFSSH_NO_ECDSA - struct { - ecc_key key; - } ecc; -#endif -#ifndef WOLFSSH_NO_ED25519 - struct { - ed25519_key key; - } ed25519; -#endif - } ks; -} WS_KeySignature; - - -static void wolfSSH_KEY_clean(WS_KeySignature* key) +void wolfSSH_KEY_clean(WS_KeySignature* key) { if (key != NULL) { if (key->keySigId == ID_SSH_RSA) { #ifndef WOLFSSH_NO_RSA wc_FreeRsaKey(&key->ks.rsa.key); +#endif + } + else if (key->keySigId == ID_ED25519) { +#ifndef WOLFSSH_NO_ED25519 + wc_ed25519_free(&key->ks.ed25519.key); #endif } else if (key->keySigId == ID_ECDSA_SHA2_NISTP256 || - key->keySigId == ID_ECDSA_SHA2_NISTP384 || - key->keySigId == ID_ECDSA_SHA2_NISTP521) { + key->keySigId == ID_ECDSA_SHA2_NISTP384 || + key->keySigId == ID_ECDSA_SHA2_NISTP521) { #ifndef WOLFSSH_NO_ECDSA wc_ecc_free(&key->ks.ecc.key); #endif @@ -1264,9 +1249,11 @@ static void wolfSSH_KEY_clean(WS_KeySignature* key) * @param inSz size of key * @param isPrivate indicates private or public key * @param heap heap to use for memory allocation + * @param pkey optionally return populated WS_KeySignature * @return keyId as int, WS_MEMORY_E, WS_UNIMPLEMENTED_E */ -int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) +int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap, + WS_KeySignature **pkey) { WS_KeySignature *key = NULL; word32 idx; @@ -1274,8 +1261,11 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) int dynType = isPrivate ? DYNTYPE_PRIVKEY : DYNTYPE_PUBKEY; WOLFSSH_UNUSED(dynType); - key = (WS_KeySignature*)WMALLOC(sizeof(WS_KeySignature), heap, dynType); + if (pkey != NULL) { + *pkey = NULL; + } + key = (WS_KeySignature*)WMALLOC(sizeof(WS_KeySignature), heap, dynType); if (key == NULL) { ret = WS_MEMORY_E; } @@ -1304,8 +1294,6 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) key->keySigId = ID_SSH_RSA; } } - - wc_FreeRsaKey(&key->ks.rsa.key); } #endif /* WOLFSSH_NO_RSA */ #ifndef WOLFSSH_NO_ECDSA @@ -1339,8 +1327,6 @@ 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) @@ -1348,7 +1334,7 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) idx = 0; ret = wc_ed25519_init_ex(&key->ks.ed25519.key, heap, INVALID_DEVID); - if(ret == 0) { + if (ret == 0) { if (isPrivate) { ret = wc_Ed25519PrivateKeyDecode(in, &idx, &key->ks.ed25519.key, inSz); @@ -1360,10 +1346,8 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) } /* If decode was successful, this is a Ed25519 key. */ - if(ret == 0) + if (ret == 0) key->keySigId = ID_ED25519; - - wc_ed25519_free(&key->ks.ed25519.key); } #endif /* WOLFSSH_NO_ED25519 */ @@ -1371,10 +1355,17 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) ret = WS_UNIMPLEMENTED_E; } else { + if (pkey != NULL) + *pkey = key; ret = key->keySigId; } - WFREE(key, heap, dynType); + /* if not returning key then free it */ + if (pkey == NULL || *pkey == NULL) { + wolfSSH_KEY_clean(key); + WFREE(key, heap, dynType); + key = NULL; + } } return ret; @@ -1579,6 +1570,96 @@ static int GetOpenSshKeyEd25519(ed25519_key* key, return ret; } #endif + +#ifdef WOLFSSH_TPM + +#ifndef WOLFSSH_NO_ECDSA +static int GetOpenSshPublicKeyEcc(ecc_key* key, const byte* buf, word32 len, + word32* idx) +{ + int ret = WS_CRYPTO_FAILED; + (void)key; + (void)buf; + (void)len; + (void)idx; + /* TODO: Add ECC public key: See DoUserAuthRequestEcc and wc_ecc_import_x963 */ + return ret; +} +#endif +#ifndef WOLFSSH_NO_ED25519 +static int GetOpenSshKeyPublicEd25519(ed25519_key* key, const byte* buf, + word32 len, word32* idx) +{ + int ret = WS_CRYPTO_FAILED; + (void)key; + (void)buf; + (void)len; + (void)idx; + /* TODO: Add ECC public key: See DoUserAuthRequestEd25519 and wc_ed25519_import_public */ + return ret; +} +#endif +#ifndef WOLFSSH_NO_RSA +static int GetOpenSshPublicKeyRsa(RsaKey* key, const byte* buf, word32 len, + word32* idx) +{ + int ret; + const byte *n = NULL, *e = NULL; + word32 nSz = 0, eSz = 0; + + ret = GetMpint(&eSz, &e, buf, len, idx); + if (ret == WS_SUCCESS) { + ret = GetMpint(&nSz, &n, buf, len, idx); + } + if (ret == WS_SUCCESS) { + ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, key); + if (ret != 0) { + WLOG(WS_LOG_DEBUG, "Could not decode RSA public key"); + ret = WS_CRYPTO_FAILED; + } + } + return ret; +} +#endif + +static int GetOpenSshPublicKey(WS_KeySignature *key, + const byte* buf, word32 len, word32* idx) +{ + int ret = WS_SUCCESS; + const byte* publicKeyType; + word32 publicKeyTypeSz = 0; + byte keyId; + + ret = GetStringRef(&publicKeyTypeSz, &publicKeyType, buf, len, idx); + keyId = NameToId((const char*)publicKeyType, publicKeyTypeSz); + + switch (keyId) { + #ifndef WOLFSSH_NO_RSA + case ID_SSH_RSA: + ret = GetOpenSshPublicKeyRsa(&key->ks.rsa.key, buf, len, idx); + break; + #endif + #ifndef WOLFSSH_NO_ECDSA + case ID_ECDSA_SHA2_NISTP256: + case ID_ECDSA_SHA2_NISTP384: + case ID_ECDSA_SHA2_NISTP521: + ret = GetOpenSshPublicKeyEcc(&key->ks.ecc.key, buf, len, idx); + break; + #endif + #ifndef WOLFSSH_NO_ED25519 + case ID_ED25519: + ret = GetOpenSshKeyPublicEd25519(&key->ks.ed25519.key, buf, len, idx); + break; + #endif + default: + ret = WS_UNIMPLEMENTED_E; + break; + } + return ret; +} + +#endif /* WOLFSSH_TPM */ + /* * Decodes an OpenSSH format key. */ @@ -1808,10 +1889,11 @@ static int IdentifyCert(const byte* in, word32 inSz, void* heap) } if (ret == 0) { - ret = IdentifyAsn1Key(key, keySz, 0, heap); + ret = IdentifyAsn1Key(key, keySz, 0, heap, NULL); + } + if (key != NULL) { + WFREE(key, heap, DYNTYPE_PUBKEY); } - - WFREE(key, heap, DYNTYPE_PUBKEY); if (cert != NULL) { wc_FreeDecodedCert(cert); #ifdef WOLFSSH_SMALL_STACK @@ -2151,9 +2233,10 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, /* Maybe decrypt */ if (type == BUFTYPE_PRIVKEY) { - ret = IdentifyAsn1Key(der, derSz, 1, ctx->heap); + ret = IdentifyAsn1Key(der, derSz, 1, ctx->heap, NULL); if (ret < 0) { - WFREE(der, heap, dynamicType); + if (der != NULL) + WFREE(der, heap, dynamicType); return ret; } keyId = (byte)ret; @@ -7364,9 +7447,14 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, /* Parse the public key format, signature algo, and signature blob. */ if (ret == WS_SUCCESS) { + /* Server expects openssh style ssh-rsa base64encoded public key */ begin = 0; ret = GetStringRef(&pubKeyFmtSz, &pubKeyFmt, pubKeyBlob, pubKeyBlobSz, &begin); + if (ret != WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, "DUARPK: Invalid public key format: " + "example: \"ssh-rsa\""); + } } if (hasSig) { @@ -7756,6 +7844,11 @@ static int DoUserAuthFailure(WOLFSSH* ssh, for (j = 0; j < sizeof(ssh->supportedAuth); j++) { if (authList[i] == ssh->supportedAuth[j]) { switch(authList[i]) { +#ifdef WOLFSSH_TPM + case ID_USERAUTH_PUBLICKEY: + authType |= WOLFSSH_USERAUTH_PUBLICKEY; + break; +#else /* !WOLFSSH_TPM */ case ID_USERAUTH_PASSWORD: authType |= WOLFSSH_USERAUTH_PASSWORD; break; @@ -7767,6 +7860,7 @@ static int DoUserAuthFailure(WOLFSSH* ssh, authType |= WOLFSSH_USERAUTH_PUBLICKEY; break; #endif +#endif /* WOLFSSH_TPM */ default: break; } @@ -11621,6 +11715,14 @@ static int SignHRsa(WOLFSSH* ssh, byte* sig, word32* sigSz, if (ret == WS_SUCCESS) { WLOG(WS_LOG_INFO, "Signing hash with %s.", IdToName(ssh->handshake->pubKeyId)); + #ifdef WOLFSSH_TPM + if (ssh->ctx->tpmDev && ssh->ctx->tpmKey) { + ret = wolfTPM2_SignHashScheme(ssh->ctx->tpmDev, + ssh->ctx->tpmKey, encSig, encSigSz, sig, (int*)sigSz, + TPM_ALG_RSASSA, TPM2_GetTpmHashType(hashId)); + } + else + #endif /* WOLFSSH_TPM */ ret = wc_RsaSSL_Sign(encSig, encSigSz, sig, KEX_SIG_SIZE, &sigKey->sk.rsa.key, ssh->rng); @@ -13362,6 +13464,15 @@ static int PrepareUserAuthRequestRsa(WOLFSSH* ssh, word32* payloadSz, authData->sf.publicKey.publicKeySz); } else + #endif /* WOLFSSH_AGENT */ + #ifdef WOLFSSH_TPM + if (authData->sf.publicKey.privateKey == NULL || + authData->sf.publicKey.privateKeySz == 0) { + ret = GetOpenSshPublicKey(keySig, + authData->sf.publicKey.publicKey, + authData->sf.publicKey.publicKeySz, &idx); + } + else #endif { ret = wc_RsaPrivateKeyDecode(authData->sf.publicKey.privateKey, @@ -13459,7 +13570,7 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, } } else - #endif + #endif /* WOLFSSH_AGENT */ { if (ret == WS_SUCCESS) { WMEMSET(digest, 0, sizeof(digest)); @@ -13515,19 +13626,44 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, ret = WS_CRYPTO_FAILED; } else { - int sigSz; WLOG(WS_LOG_INFO, "Signing hash with RSA."); - sigSz = wc_RsaSSL_Sign(encDigest, encDigestSz, + #ifdef WOLFSSH_TPM + int sigSz; + sigSz = keySig->sigSz; + if (ssh->ctx->tpmDev && ssh->ctx->tpmKey) { + ret = wc_RsaPad_ex(encDigest, encDigestSz, output+begin, + sigSz, RSA_BLOCK_TYPE_1, ssh->rng, WC_RSA_PKCSV15_PAD, + WC_HASH_TYPE_NONE, WC_MGF1NONE, NULL, 0, 0, 0, + ssh->ctx->heap); + if (ret == 0) { + /* private RSA operation */ + ret = wolfTPM2_RsaDecrypt(ssh->ctx->tpmDev, + ssh->ctx->tpmKey, TPM_ALG_NULL, /* no padding */ + output+begin, sigSz, output+begin, (int*)&sigSz); + ret = (ret == 0) ? sigSz : WS_RSA_E; + } + } + else { + WLOG(WS_LOG_DEBUG, "SendKexDhReply: TPM key or device not set"); + ret = WS_CRYPTO_FAILED; + } + #else /* !WOLFSSH_TPM */ + ret = wc_RsaSSL_Sign(encDigest, encDigestSz, output + begin, keySig->sigSz, &keySig->ks.rsa.key, ssh->rng); - if (sigSz <= 0 || (word32)sigSz != keySig->sigSz) { + #endif /* WOLFSSH_TPM */ + if (ret <= 0 || (word32)ret != keySig->sigSz) { WLOG(WS_LOG_DEBUG, "SUAR: Bad RSA Sign"); ret = WS_RSA_E; } else { + #ifdef WOLFSSH_TPM + ret = 0; + #else ret = wolfSSH_RsaVerify(output + begin, keySig->sigSz, encDigest, encDigestSz, &keySig->ks.rsa.key, ssh->ctx->heap, "SUAR"); + #endif } } } @@ -13546,7 +13682,7 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, } return ret; -} +} /* END BuildUserAuthRequestRsa */ #ifdef WOLFSSH_CERTS @@ -14405,7 +14541,7 @@ static int BuildUserAuthRequestEd25519(WOLFSSH* ssh, #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) + WS_UserAuthData* authData, WS_KeySignature* keySig) { int ret = WS_SUCCESS; @@ -14635,7 +14771,7 @@ static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh, } -#endif +#endif /* !WOLFSSH_NO_RSA || !WOLFSSH_NO_ECDSA || !WOLFSSH_NO_ED25519 */ int SendUserAuthKeyboardResponse(WOLFSSH* ssh) { diff --git a/src/ssh.c b/src/ssh.c index 19675106..ccf938b9 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -400,6 +400,56 @@ const char* wolfSSH_ErrorToName(int err) } +#ifdef WOLFSSH_TPM +void wolfSSH_SetTpmDev(WOLFSSH* ssh, WOLFTPM2_DEV* dev) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetTpmDev()"); + + if (ssh && ssh->ctx) + ssh->ctx->tpmDev = dev; + + if (ssh->ctx->tpmDev == NULL) { + WLOG(WS_LOG_DEBUG, "wolfSSH_SetTpmDev: Set tpm dev failed"); + } +} + + +void wolfSSH_SetTpmKey(WOLFSSH* ssh, WOLFTPM2_KEY* key) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetTpmKey()"); + + if (ssh && ssh->ctx) + ssh->ctx->tpmKey = key; + + if (ssh->ctx->tpmDev == NULL) { + WLOG(WS_LOG_DEBUG, "wolfSSH_SetTpmKey: Set tpm key failed"); + } +} + + +void* wolfSSH_GetTpmDev(WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetTpmDev()"); + + if (ssh && ssh->ctx) { + return ssh->ctx->tpmDev; + } + return NULL; +} + + +void* wolfSSH_GetTpmKey(WOLFSSH* ssh) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetTpmKey()"); + + if (ssh && ssh->ctx) { + return ssh->ctx->tpmKey; + } + return NULL; +} +#endif /* WOLFSSH_TPM */ + + #ifndef NO_WOLFSSH_SERVER const char acceptError[] = "accept error: %s, %d"; @@ -1693,42 +1743,102 @@ static int DoSshPubKey(const byte* in, word32 inSz, byte** out, static int DoAsn1Key(const byte* in, word32 inSz, byte** out, word32* outSz, const byte** outType, word32* outTypeSz, - void* heap) + int isPrivate, void* heap) { int ret = WS_SUCCESS; byte* newKey = NULL; + WS_KeySignature* key = NULL; WOLFSSH_UNUSED(heap); - if (*out == NULL) { - newKey = (byte*)WMALLOC(inSz, heap, DYNTYPE_PRIVKEY); - if (newKey == NULL) { - return WS_MEMORY_E; - } - } - else { - if (*outSz < inSz) { - WLOG(WS_LOG_DEBUG, "DER private key output size too small"); - return WS_BUFFER_E; - } - newKey = *out; + ret = IdentifyAsn1Key(in, inSz, isPrivate, heap, &key); + if (ret <= 0) { + WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key"); } - ret = IdentifyAsn1Key(in, inSz, 1, heap); + if (ret > 0 && !isPrivate) { + long e; + byte n[RSA_MAX_SIZE]; /* TODO: Handle small stack */ + word32 nSz = (word32)sizeof(n), eSz = (word32)sizeof(e); + const char* keyFormat = "ssh-rsa"; + word32 idx = 0; + int nMsb = 0; + + *outType = (const byte*)IdToName(ret); + *outTypeSz = (word32)WSTRLEN((const char*)*outType); + + ret = wc_RsaFlattenPublicKey(&key->ks.rsa.key, (byte*)&e, &eSz, n, &nSz); + if (ret == 0) { + if (n[0] & 0x80) { + /* if MSB is set need leading zero */ + nMsb = 1; + } + *outSz = LENGTH_SZ + (word32)WSTRLEN(keyFormat) + + LENGTH_SZ + eSz + + LENGTH_SZ + nSz + nMsb; + + newKey = (byte*)WMALLOC(*outSz, heap, DYNTYPE_PRIVKEY); + if (newKey == NULL) { + ret = WS_MEMORY_E; + } + } + if (ret == 0) { + /* encode the key format string */ + c32toa((word32)WSTRLEN(keyFormat), &newKey[idx]); + idx += LENGTH_SZ; + WMEMCPY(&newKey[idx], keyFormat, (word32)WSTRLEN(keyFormat)); + idx += (word32)WSTRLEN(keyFormat); + + /* encode public exponent (e) */ + c32toa(eSz, &newKey[idx]); + idx += LENGTH_SZ; + WMEMCPY(&newKey[idx], &e, eSz); + idx += eSz; + + /* encode public modulus (n) */ + c32toa(nSz + nMsb, &newKey[idx]); + idx += LENGTH_SZ; + if (nMsb) { + newKey[idx++] = 0; + } + WMEMCPY(&newKey[idx], n, nSz); + + *out = newKey; + } + } + else if (ret > 0 && isPrivate) { + if (*out == NULL) { + newKey = (byte*)WMALLOC(inSz, heap, DYNTYPE_PRIVKEY); + if (newKey == NULL) { + ret = WS_MEMORY_E; + return ret; + } + } + else { + if (*outSz < inSz) { + WLOG(WS_LOG_DEBUG, "DER private key output size too small"); + ret = WS_BUFFER_E; + return ret; + } + newKey = *out; + } - if (ret > 0) { *out = newKey; *outSz = inSz; WMEMCPY(newKey, in, inSz); *outType = (const byte*)IdToName(ret); *outTypeSz = (word32)WSTRLEN((const char*)*outType); - ret = WS_SUCCESS; } - else { - WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key"); - if (*out == NULL) { - WFREE(newKey, heap, DYNTYPE_PRIVKEY); - } + + wolfSSH_KEY_clean(key); + WFREE(key, heap, isPrivate ? DYNTYPE_PRIVKEY : DYNTYPE_PUBKEY); + + if (*out == NULL) { + WFREE(newKey, heap, DYNTYPE_PRIVKEY); + } + + if (ret > 0) { + ret = WS_SUCCESS; } return ret; @@ -1737,7 +1847,7 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, static int DoPemKey(const byte* in, word32 inSz, byte** out, word32* outSz, const byte** outType, word32* outTypeSz, - void* heap) + int isPrivate, void* heap) { int ret = WS_SUCCESS; byte* newKey = NULL; @@ -1760,7 +1870,16 @@ static int DoPemKey(const byte* in, word32 inSz, byte** out, } /* If it is PEM, convert to ASN1 then process. */ - ret = wc_KeyPemToDer(in, inSz, newKey, newKeySz, NULL); + if (isPrivate) { + ret = wc_KeyPemToDer(in, inSz, newKey, newKeySz, NULL); + } + else { + #ifdef WOLFSSH_TPM + ret = wc_PubKeyPemToDer(in, inSz, newKey, newKeySz); + #else + ret = NOT_COMPILED_IN; + #endif + } if (ret > 0) { newKeySz = (word32)ret; ret = WS_SUCCESS; @@ -1771,7 +1890,7 @@ static int DoPemKey(const byte* in, word32 inSz, byte** out, } if (ret == WS_SUCCESS) { - ret = IdentifyAsn1Key(newKey, newKeySz, 1, heap); + ret = IdentifyAsn1Key(newKey, newKeySz, 1, heap, NULL); } if (ret > 0) { @@ -1853,9 +1972,9 @@ static int DoOpenSshKey(const byte* in, word32 inSz, byte** out, to a constant string. Format indicates the format of the key, currently either SSH format (a public key) or ASN.1 in DER or PEM format (a private key). */ -int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, +int wolfSSH_ReadKey_buffer_ex(const byte* in, word32 inSz, int format, byte** out, word32* outSz, const byte** outType, word32* outTypeSz, - void* heap) + int isPrivate, void* heap) { int ret = WS_SUCCESS; @@ -1867,10 +1986,12 @@ int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, ret = DoSshPubKey(in, inSz, out, outSz, outType, outTypeSz, heap); } else if (format == WOLFSSH_FORMAT_ASN1) { - ret = DoAsn1Key(in, inSz, out, outSz, outType, outTypeSz, heap); + ret = DoAsn1Key(in, inSz, out, outSz, outType, outTypeSz, + isPrivate, heap); } else if (format == WOLFSSH_FORMAT_PEM) { - ret = DoPemKey(in, inSz, out, outSz, outType, outTypeSz, heap); + ret = DoPemKey(in, inSz, out, outSz, outType, outTypeSz, + isPrivate, heap); } else if (format == WOLFSSH_FORMAT_OPENSSH) { ret = DoOpenSshKey(in, inSz, out, outSz, outType, outTypeSz, heap); @@ -1882,6 +2003,21 @@ int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, return ret; } +int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, + byte** out, word32* outSz, const byte** outType, word32* outTypeSz, + void* heap) +{ + return wolfSSH_ReadKey_buffer_ex(in, inSz, format, out, outSz, + outType, outTypeSz, 1, heap); +} + +int wolfSSH_ReadPublicKey_buffer(const byte* in, word32 inSz, int format, + byte** out, word32* outSz, const byte** outType, word32* outTypeSz, + void* heap) +{ + return wolfSSH_ReadKey_buffer_ex(in, inSz, format, out, outSz, + outType, outTypeSz, 0, heap); +} #if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM) @@ -1958,8 +2094,8 @@ int wolfSSH_ReadKey_file(const char* name, format = WOLFSSH_FORMAT_ASN1; } - ret = wolfSSH_ReadKey_buffer(in, inSz, format, - out, outSz, outType, outTypeSz, heap); + ret = wolfSSH_ReadKey_buffer_ex(in, inSz, format, + out, outSz, outType, outTypeSz, *isPrivate, heap); } WFCLOSE(ssh->fs, file); diff --git a/wolfssh/error.h b/wolfssh/error.h index d41f840b..b0000419 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -137,7 +137,7 @@ enum WS_ErrorCodes { WS_AUTH_PENDING = -1096, /* User authentication still pending */ WS_KDF_E = -1097, /* KDF error*/ - WS_LAST_E = -1097 /* Update this to indicate last error */ + WS_LAST_E = WS_KDF_E /* Update this to indicate last error */ }; diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 974e3c1e..36e8999f 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -38,6 +38,8 @@ #include #include #include +#include + #ifdef WOLFSSH_SCP #include #endif @@ -48,6 +50,9 @@ #include #endif /* WOLFSSH_CERTS */ +#ifdef WOLFSSH_TPM + #include +#endif /* WOLFSSH_TPM */ #if !defined (ALIGN16) #if defined (__GNUC__) @@ -572,6 +577,10 @@ struct WOLFSSH_CTX { #ifdef WOLFSSH_AGENT byte agentEnabled; #endif /* WOLFSSH_AGENT */ +#ifdef WOLFSSH_TPM + WOLFTPM2_DEV* tpmDev; + WOLFTPM2_KEY* tpmKey; +#endif /* WOLFSSH_TPM */ WS_CallbackKeyingCompletion keyingCompletionCb; }; @@ -952,14 +961,43 @@ WOLFSSH_LOCAL void ChannelDelete(WOLFSSH_CHANNEL*, void*); WOLFSSH_LOCAL WOLFSSH_CHANNEL* ChannelFind(WOLFSSH*, word32, byte); WOLFSSH_LOCAL int ChannelRemove(WOLFSSH*, word32, byte); WOLFSSH_LOCAL int ChannelPutData(WOLFSSH_CHANNEL*, byte*, word32); -WOLFSSH_LOCAL int IdentifyAsn1Key(const byte* in, word32 inSz, - int isPrivate, void* heap); -WOLFSSH_LOCAL int IdentifyOpenSshKey(const byte* in, word32 inSz, void* heap); WOLFSSH_LOCAL int wolfSSH_ProcessBuffer(WOLFSSH_CTX*, const byte*, word32, int, int); WOLFSSH_LOCAL int wolfSSH_FwdWorker(WOLFSSH*); + +typedef struct WS_KeySignature { + byte keySigId; + word32 sigSz; + const char *name; + void *heap; + word32 nameSz; + union { +#ifndef WOLFSSH_NO_RSA + struct { + RsaKey key; + } rsa; +#endif +#ifndef WOLFSSH_NO_ECDSA + struct { + ecc_key key; + } ecc; +#endif +#ifndef WOLFSSH_NO_ED25519 + struct { + ed25519_key key; + } ed25519; +#endif + } ks; +} WS_KeySignature; + +WOLFSSH_LOCAL int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap, + WS_KeySignature **pkey); +WOLFSSH_LOCAL void wolfSSH_KEY_clean(WS_KeySignature* key); +WOLFSSH_LOCAL int IdentifyOpenSshKey(const byte* in, word32 inSz, void* heap); + + /* Parsing functions */ WOLFSSH_LOCAL int GetBoolean(byte* v, const byte* buf, word32 len, word32* idx); diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 40082d6c..c1aeadc3 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -39,6 +39,10 @@ #include #include +#ifdef WOLFSSH_TPM +#include +#endif + #ifdef __cplusplus extern "C" { #endif @@ -83,9 +87,16 @@ WOLFSSH_API void wolfSSH_SetHighwaterCb(WOLFSSH_CTX*, word32, WOLFSSH_API void wolfSSH_SetHighwaterCtx(WOLFSSH*, void*); WOLFSSH_API void* wolfSSH_GetHighwaterCtx(WOLFSSH*); +WOLFSSH_API int wolfSSH_ReadKey_buffer_ex(const byte* in, word32 inSz, int format, + byte** out, word32* outSz, const byte** outType, word32* outTypeSz, + int isPrivate, void* heap); + WOLFSSH_API int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, byte** out, word32* outSz, const byte** outType, word32* outTypeSz, void* heap); +WOLFSSH_API int wolfSSH_ReadPublicKey_buffer(const byte* in, word32 inSz, int format, + byte** out, word32* outSz, const byte** outType, word32* outTypeSz, + void* heap); WOLFSSH_API int wolfSSH_ReadKey_file(const char* name, byte** out, word32* outSz, const byte** outType, word32* outTypeSz, byte* isPrivate, void* heap); @@ -258,6 +269,14 @@ WOLFSSH_API int wolfSSH_get_error(const WOLFSSH*); WOLFSSH_API const char* wolfSSH_get_error_name(const WOLFSSH*); WOLFSSH_API const char* wolfSSH_ErrorToName(int); +/* TPM 2.0 integration related functions */ +#ifdef WOLFSSH_TPM +WOLFSSH_API void wolfSSH_SetTpmDev(WOLFSSH* ssh, WOLFTPM2_DEV* dev); +WOLFSSH_API void wolfSSH_SetTpmKey(WOLFSSH* ssh, WOLFTPM2_KEY* key); +WOLFSSH_API void* wolfSSH_GetTpmDev(WOLFSSH* ssh); +WOLFSSH_API void* wolfSSH_GetTpmKey(WOLFSSH* ssh); +#endif /* WOLFSSH_TPM */ + /* I/O callbacks */ typedef int (*WS_CallbackIORecv)(WOLFSSH*, void*, word32, void*); typedef int (*WS_CallbackIOSend)(WOLFSSH*, void*, word32, void*); From 69e266178f7528c3ba29fe78534d38600b9426be Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 1 Apr 2025 21:58:49 -0700 Subject: [PATCH 2/4] Used port.h macros --- .github/workflows/tpm-ssh.yml | 26 +++++++++++++------------- examples/echoserver/echoserver.c | 16 ++++++++-------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/tpm-ssh.yml b/.github/workflows/tpm-ssh.yml index 70af3b8f..111fe079 100644 --- a/.github/workflows/tpm-ssh.yml +++ b/.github/workflows/tpm-ssh.yml @@ -85,22 +85,23 @@ jobs: run: | # Generate key with default password cd wolftpm - ./examples/keygen/keygen keyblob.bin -rsa -t -pem -eh + ./examples/keygen/keygen keyblob1.bin -rsa -t -pem -eh + cp key.pem key1.pem # Save the key for first test # Convert key to SSH format - ssh-keygen -f key.pem -i -m PKCS8 > ../wolfssh/key.ssh + ssh-keygen -f key1.pem -i -m PKCS8 > ../wolfssh/key1.ssh cd .. # Start echoserver and wait for it to be ready cd wolfssh - ./examples/echoserver/echoserver -1 -s key.ssh & + ./examples/echoserver/echoserver -1 -s key1.ssh & echo "Echoserver started with PID: $!" sleep 2 cd .. # Test client connection with default password cd wolfssh - ./examples/client/client -i ../wolftpm/keyblob.bin -u hansel -K ThisIsMyKeyAuth + ./examples/client/client -i ../wolftpm/keyblob1.bin -u hansel -K ThisIsMyKeyAuth cd .. # Test the TPM SSH Custom Password @@ -109,14 +110,15 @@ jobs: # Test with custom password cd wolftpm ./examples/keygen/keygen keyblob2.bin -rsa -t -pem -eh -auth=custompassword + cp key.pem key2.pem # Save the key for second test # Convert key to SSH format - ssh-keygen -f key.pem -i -m PKCS8 > ../wolfssh/key.ssh + ssh-keygen -f key2.pem -i -m PKCS8 > ../wolfssh/key2.ssh cd .. # Start echoserver and wait for it to be ready cd wolfssh - ./examples/echoserver/echoserver -1 -s key.ssh & + ./examples/echoserver/echoserver -1 -s key2.ssh & echo "Echoserver started with PID: $!" sleep 2 cd .. @@ -126,10 +128,6 @@ jobs: ./examples/client/client -i ../wolftpm/keyblob2.bin -u hansel -K custompassword cd .. - # Cleanup - pkill -f tpm_server - sleep 2 - # Archive artifacts for debugging - name: Archive test artifacts if: always() @@ -137,7 +135,9 @@ jobs: with: name: test-artifacts path: | - wolftpm/keyblob.bin + wolftpm/keyblob1.bin wolftpm/keyblob2.bin - wolftpm/key.pem - wolfssh/key.ssh + wolftpm/key1.pem + wolftpm/key2.pem + wolfssh/key1.ssh + wolfssh/key2.ssh \ No newline at end of file diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 29a1fb47..6477eb0e 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -2101,25 +2102,24 @@ static int LoadPubKeyList(StrList* strList, int format, PwMapList* mapList) #ifdef WOLFSSH_TPM static char* LoadTpmSshKey(const char* keyFile) { - FILE* file; + WFILE* file = NULL; char* buffer = NULL; char* ret = NULL; long length; - file = fopen(keyFile, "rb"); - if (!file) { + if (WFOPEN(NULL, &file, keyFile, "rb") != 0) { fprintf(stderr, "Failed to open TPM key file: %s\n", keyFile); return NULL; } - fseek(file, 0, SEEK_END); - length = ftell(file); - fseek(file, 0, SEEK_SET); + WFSEEK(NULL, file, 0, SEEK_END); + length = WFTELL(NULL, file); + WFSEEK(NULL, file, 0, SEEK_SET); buffer = (char*)WMALLOC(length + 8 + 1, NULL, DYNTYPE_BUFFER); if (buffer) { - if (fread(buffer, 1, length, file) == (size_t)length) { + if (WFREAD(NULL, buffer, 1, length, file) == (size_t)length) { while (length > 0 && (buffer[length-1] == '\n' || buffer[length-1] == '\r')) { length--; @@ -2133,7 +2133,7 @@ static char* LoadTpmSshKey(const char* keyFile) } } - fclose(file); + WFCLOSE(NULL, file); return ret; } #endif From 3775d1843ab37afa55cfc0b7f9ba34a44c6168c9 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 1 Apr 2025 22:05:47 -0700 Subject: [PATCH 3/4] newline --- .github/workflows/tpm-ssh.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tpm-ssh.yml b/.github/workflows/tpm-ssh.yml index 111fe079..af39eb7b 100644 --- a/.github/workflows/tpm-ssh.yml +++ b/.github/workflows/tpm-ssh.yml @@ -140,4 +140,4 @@ jobs: wolftpm/key1.pem wolftpm/key2.pem wolfssh/key1.ssh - wolfssh/key2.ssh \ No newline at end of file + wolfssh/key2.ssh From a52c3b120e01280afbc22e4a81bb023c9bd291bc Mon Sep 17 00:00:00 2001 From: aidan garske Date: Wed, 2 Apr 2025 10:33:27 -0700 Subject: [PATCH 4/4] Used W port.h file operations for keyblob and added username argument so can append any username needed. --- examples/client/common.c | 33 ++++++++++++++++++-------------- examples/echoserver/echoserver.c | 19 ++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/examples/client/common.c b/examples/client/common.c index 344f7f10..3a0c5859 100644 --- a/examples/client/common.c +++ b/examples/client/common.c @@ -760,7 +760,7 @@ static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) { int rc = 0; #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) - XFILE fp = NULL; + WFILE* fp = NULL; size_t fileSz = 0; size_t bytes_read = 0; byte pubAreaBuffer[sizeof(TPM2B_PUBLIC)]; @@ -768,20 +768,25 @@ static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) WLOG(WS_LOG_DEBUG, "Entering readKeyBlob()"); - XMEMSET(key, 0, sizeof(WOLFTPM2_KEYBLOB)); + WMEMSET(key, 0, sizeof(WOLFTPM2_KEYBLOB)); + + if (WFOPEN(NULL, &fp, filename, "rb") != 0) { + printf("Failed to open file %s\n", filename); + rc = BUFFER_E; goto exit; + } + if (fp != WBADFILE) { + WFSEEK(NULL, fp, 0, WSEEK_END); + fileSz = WFTELL(NULL, fp); + WREWIND(NULL, fp); - fp = XFOPEN(filename, "rb"); - if (fp != XBADFILE) { - XFSEEK(fp, 0, XSEEK_END); - fileSz = XFTELL(fp); - XREWIND(fp); if (fileSz > sizeof(key->priv) + sizeof(key->pub)) { printf("File size check failed\n"); rc = BUFFER_E; goto exit; } printf("Reading %d bytes from %s\n", (int)fileSz, filename); - bytes_read = XFREAD(&key->pub.size, 1, sizeof(key->pub.size), fp); + bytes_read = WFREAD(NULL, &key->pub.size, 1, + sizeof(key->pub.size), fp); if (bytes_read != sizeof(key->pub.size)) { printf("Read %zu, expected size marker of %zu bytes\n", bytes_read, sizeof(key->pub.size)); @@ -789,7 +794,8 @@ static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) } fileSz -= bytes_read; - bytes_read = XFREAD(pubAreaBuffer, 1, sizeof(UINT16) + key->pub.size, fp); + bytes_read = WFREAD(NULL, pubAreaBuffer, 1, + sizeof(UINT16) + key->pub.size, fp); if (bytes_read != sizeof(UINT16) + key->pub.size) { printf("Read %zu, expected public blob %zu bytes\n", bytes_read, sizeof(UINT16) + key->pub.size); @@ -804,7 +810,7 @@ static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) if (fileSz > 0) { printf("Reading the private part of the key\n"); - bytes_read = XFREAD(&key->priv, 1, fileSz, fp); + bytes_read = WFREAD(NULL, &key->priv, 1, fileSz, fp); if (bytes_read != fileSz) { printf("Read %zu, expected private blob %zu bytes\n", bytes_read, fileSz); @@ -824,14 +830,13 @@ static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) else { rc = BUFFER_E; printf("File %s not found!\n", filename); - printf("Keys can be generated by running:\n" - " ./examples/keygen/keygen rsa_test_blob.raw -rsa -t\n" - " ./examples/keygen/keygen ecc_test_blob.raw -ecc -t\n"); + printf("Key can be generated by running:\n" + " ./examples/keygen/keygen keyblob.bin -rsa -t -pem -eh\n"); } exit: if (fp) - XFCLOSE(fp); + WFCLOSE(NULL, fp); #else (void)filename; (void)key; diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 6477eb0e..e6a702ae 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -2100,32 +2100,35 @@ static int LoadPubKeyList(StrList* strList, int format, PwMapList* mapList) #endif #ifdef WOLFSSH_TPM -static char* LoadTpmSshKey(const char* keyFile) +static char* LoadTpmSshKey(const char* keyFile, const char* username) { WFILE* file = NULL; char* buffer = NULL; char* ret = NULL; long length; + size_t usernameLen; if (WFOPEN(NULL, &file, keyFile, "rb") != 0) { fprintf(stderr, "Failed to open TPM key file: %s\n", keyFile); return NULL; } - - WFSEEK(NULL, file, 0, SEEK_END); + WFSEEK(NULL, file, 0, WSEEK_END); length = WFTELL(NULL, file); - WFSEEK(NULL, file, 0, SEEK_SET); + WREWIND(NULL, file); - buffer = (char*)WMALLOC(length + 8 + 1, NULL, DYNTYPE_BUFFER); + usernameLen = WSTRLEN(username); + buffer = (char*)WMALLOC(length + usernameLen + 2, NULL, DYNTYPE_BUFFER); if (buffer) { if (WFREAD(NULL, buffer, 1, length, file) == (size_t)length) { while (length > 0 && (buffer[length-1] == '\n' || buffer[length-1] == '\r')) { length--; } - WMEMCPY(buffer + length, " hansel\n", 8); - buffer[length + 8] = '\0'; + buffer[length] = ' '; + WMEMCPY(buffer + length + 1, username, usernameLen); + buffer[length + 1 + usernameLen] = '\n'; + buffer[length + 2 + usernameLen] = '\0'; ret = buffer; } else { @@ -2634,7 +2637,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) /* Load custom TPM key if specified */ #ifdef WOLFSSH_TPM if (tpmKeyPath != NULL) { - const char* newBuffer = LoadTpmSshKey(tpmKeyPath); + const char* newBuffer = LoadTpmSshKey(tpmKeyPath, "hansel"); if (newBuffer != NULL) { sampleTpmPublicKeyRsaBuffer = newBuffer; }