From 3fc5f4d37db60832175f6ff517f3247fdc17d60a Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 12 Dec 2024 15:45:49 -0800 Subject: [PATCH 01/10] wolfSSH public key authentication with tpm support --- README.md | 59 ++++++- configure.ac | 16 ++ examples/client/client.c | 76 ++++++-- examples/client/common.c | 294 +++++++++++++++++++++++++++++-- examples/client/common.h | 3 + examples/echoserver/echoserver.c | 2 - src/internal.c | 221 ++++++++++++++++++----- src/ssh.c | 165 +++++++++++++---- wolfssh/error.h | 9 + wolfssh/internal.h | 44 ++++- wolfssh/ssh.h | 19 ++ 11 files changed, 799 insertions(+), 109 deletions(-) diff --git a/README.md b/README.md index 357643931..f09747739 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,62 @@ fred-cert.der would be: $ ./examples/client/client -u fred -J ./keys/fred-cert.der -i ./keys/fred-key.der +TPM +=== + +wolfSSH now supports TPM support with client 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 in wolfTPM +using: + + $ ./examples/keygen/keygen keyblob.bin -rsa -t -pem + +Take key.pem and convert the TPM public key to the ssh-rsa BASE64 username format: +`ssh-keygen -f key.pem -i -m PKCS8`. Update echoserver.c user "hansel"'s public key. + +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: + + $ ./examples/echoserver/echoserver + +From another terminal run the client with the keyblob: + + $ ./examples/client/client -i ../wolfTPM/keyblob.bin -u hansel + +For debuging run server like above then: + + $ ./examples/client/client + +Set break point or just run: + + $ r -i ../wolfTPM/keyblob.bin -u hansel WOLFSSH APPLICATIONS ==================== diff --git a/configure.ac b/configure.ac index 206157756..b731ad0ab 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,10 @@ 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"]) +AS_IF([test "x$ENABLED_SMALLSTACK" = "xyes"], + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SMALL_STACK"]) if test "$ENABLED_SSHD" = "yes"; then if test -n "$PAM_LIB" @@ -279,6 +293,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 +337,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 c6cde31bc..3dd3b634e 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 @@ -125,7 +129,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 +555,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'; @@ -781,11 +787,17 @@ 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."); } - + #else + if (pubKeyName == NULL && privKeyName != NULL) { + err_sys("If setting priv key, need pub key."); + } + #endif +#endif ret = ClientSetPrivateKey(privKeyName, userEcc, NULL); if (ret != 0) { err_sys("Error setting private key"); @@ -840,6 +852,9 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (ssh == NULL) err_sys("Couldn't create wolfSSH session."); +#ifdef WOLFSSH_TPM + CLientSetTpm(ssh); +#endif #if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) wolfSSH_SetGlobalReq(ctx, callbackGlobalReq); wolfSSH_SetGlobalReqCtx(ssh, &ssh); /* dummy ctx */ @@ -850,7 +865,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 +928,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 +973,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 +1075,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 +1107,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 +1127,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 +1135,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 db27d0f96..ae5cfd5f8 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,11 +59,11 @@ 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; - #ifdef WOLFSSH_CERTS #if 0 /* compiled in for using RSA certificates instead of ECC certificate */ @@ -69,6 +75,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 @@ -677,6 +687,251 @@ int ClientUseCert(const char* certName, void* heap) return ret; } +#ifdef WOLFSSH_TPM + +#define TPM2_DEMO_STORAGE_KEY_HANDLE 0x81000200 /* Persistent Storage Key Handle (RSA) */ + +static const char gStorageKeyAuth[] = "ThisIsMyStorageKeyAuth"; + +static int getPrimaryStoragekey(WOLFTPM2_DEV* pDev, + WOLFTPM2_KEY* pStorageKey, + TPM_ALG_ID alg) +{ + int rc; + + WLOG(WS_LOG_DEBUG, "Entering getPrimaryStoragekey()"); + + /* See if SRK already exists */ + rc = wolfTPM2_ReadPublicKey(pDev, pStorageKey, TPM2_DEMO_STORAGE_KEY_HANDLE); + if (rc != 0) { + /* Create primary storage key */ + rc = wolfTPM2_CreateSRK(pDev, pStorageKey, alg, + (byte*)gStorageKeyAuth, sizeof(gStorageKeyAuth)-1); + #ifndef WOLFTPM_WINAPI + if (rc == TPM_RC_SUCCESS) { + /* Move storage key into persistent NV */ + rc = wolfTPM2_NVStoreKey(pDev, TPM_RH_OWNER, pStorageKey, + TPM2_DEMO_STORAGE_KEY_HANDLE); + } + #endif + } + else { + /* specify auth password for storage key */ + pStorageKey->handle.auth.size = sizeof(gStorageKeyAuth)-1; + XMEMCPY(pStorageKey->handle.auth.buffer, gStorageKeyAuth, + pStorageKey->handle.auth.size); + } + if (rc != 0) { + printf("Loading SRK: Storage failed 0x%x: %s\n", rc, + TPM2_GetRCString(rc)); + return rc; + } + printf("Loading SRK: Storage 0x%x (%d bytes)\n", + (word32)pStorageKey->handle.hndl, pStorageKey->pub.size); + WLOG(WS_LOG_DEBUG, "Leaving getPrimaryStoragekey(), rc = %d", rc); + return rc; +} + + +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 != TPM_RC_SUCCESS) 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) +{ + int rc; + WOLFTPM2_KEY storage; + WOLFTPM2_KEYBLOB tpmKeyBlob; + byte* p = NULL; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_TPM_InitKey()"); + + rc = wolfTPM2_Init(dev, TPM2_IoCb, NULL); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFSSH + printf("TPM 2.0 Device initialization failed\n"); + #endif + return WOLFSSH_TPM_FAILED_INIT; + } + + /* TPM 2.0 keys live under a Primary Key, acquire such key */ + rc = getPrimaryStoragekey(dev, &storage, TPM_ALG_RSA); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFSSH + printf("Acquiring a Primary TPM 2.0 Key failed\n"); + #endif + return WOLFSSH_TPM_FAILED_LOAD_PRIMARY; + } + + /* Load the TPM 2.0 key blob from disk */ + rc = readKeyBlob(name, &tpmKeyBlob); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFSSH + printf("Reading key blob from disk failed\n"); + #endif + return WOLFSSH_TPM_FAILED_READ_KEYBLOB; + } + + /* TODO: workaround until password can be supplied */ + /* consider a refactor to take a 32-bit handle and key auth password */ + static const char gKeyAuth[] = "ThisIsMyKeyAuth"; + /* set session for authorization key */ + tpmKeyBlob.handle.auth.size = (int)sizeof(gKeyAuth)-1; + XMEMCPY(tpmKeyBlob.handle.auth.buffer, gKeyAuth, tpmKeyBlob.handle.auth.size); + + /* Load the public key into the TPM device */ + rc = wolfTPM2_LoadKey(dev, &tpmKeyBlob, &storage.handle); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFSSH + printf("wolfTPM2_LoadKey failed\n"); + #endif + return WOLFSSH_TPM_FAILED_LOAD_KEY; + } + #ifdef DEBUG_WOLFSSH + printf("Loaded key to 0x%x\n", (word32)tpmKeyBlob.handle.hndl); + #endif + + /* Read the public key and extract the public key as a DER/ASN.1 */ + userPublicKeySz = sizeof(userPublicKeyBuf); + rc = wolfTPM2_ExportPublicKeyBuffer(dev, (WOLFTPM2_KEY*)&tpmKeyBlob, + ENCODING_TYPE_ASN1, userPublicKey, &userPublicKeySz); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFSSH + printf("Exporting TPM key failed\n"); + #endif + return WOLFSSH_TPM_FAILED_EXPORT_KEY; + } + + /* Read public key from the buffer and convert the key to OpenSSH format */ + rc = wolfSSH_ReadPublicKey_buffer(userPublicKey, userPublicKeySz, + WOLFSSH_FORMAT_ASN1, &p, &userPublicKeySz, &userPublicKeyType, + &userPublicKeyTypeSz, NULL); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFSSH + printf("Reading public key failed returned: %d\n", rc); + #endif + return WOLFSSH_TPM_FAILED_READ_PUBLIC_KEY; + } + userPublicKey = p; + + XMEMCPY(&pTpmKey->handle, &tpmKeyBlob.handle, sizeof(pTpmKey->handle)); + XMEMCPY(&pTpmKey->pub, &tpmKeyBlob.pub, sizeof(pTpmKey->pub)); + + /* Unload SRK storage handle */ + wolfTPM2_UnloadHandle(dev, &storage.handle); + /* Key handle is unloaded on TPM cleanup */ + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_TPM_InitKey()"); + return WS_SUCCESS; +} + +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 */ @@ -687,6 +942,7 @@ int ClientSetPrivateKey(const char* privKeyName, int userEcc, void* heap) 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); @@ -694,6 +950,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); @@ -702,8 +959,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); + #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, @@ -711,13 +983,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) @@ -755,8 +1026,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; } @@ -793,15 +1064,18 @@ int ClientLoadCA(WOLFSSH_CTX* ctx, const char* caCert) return ret; } - void ClientFreeBuffers(const char* pubKeyName, const char* privKeyName, void* heap) { - 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 395d4288a..99dc078a0 100644 --- a/examples/client/common.h +++ b/examples/client/common.h @@ -32,6 +32,9 @@ 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 97d3accfa..48eca62b3 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -2855,8 +2855,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/src/internal.c b/src/internal.c index 52fe4eef9..d6cc47527 100644 --- a/src/internal.c +++ b/src/internal.c @@ -70,6 +70,12 @@ #endif #endif +#ifdef WOLFSSH_TPM + #include + #include +#endif + +#include /* Flags: @@ -1185,43 +1191,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 @@ -1240,9 +1225,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; @@ -1251,7 +1238,6 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) WOLFSSH_UNUSED(dynType); key = (WS_KeySignature*)WMALLOC(sizeof(WS_KeySignature), heap, dynType); - if (key == NULL) { ret = WS_MEMORY_E; } @@ -1280,8 +1266,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 @@ -1315,8 +1299,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) @@ -1325,7 +1307,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); @@ -1337,10 +1319,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 */ @@ -1352,7 +1332,15 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) ret = key->keySigId; } - WFREE(key, heap, dynType); + if (pkey == NULL || ret == WS_UNIMPLEMENTED_E) { + wolfSSH_KEY_clean(key); + WFREE(key, heap, dynType); + key = NULL; + } + } + + if (pkey != NULL) { + *pkey = key; } return ret; @@ -1557,6 +1545,92 @@ static int GetOpenSshKeyEd25519(ed25519_key* key, return ret; } #endif + +#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; +} + /* * Decodes an OpenSSH format key. */ @@ -2130,7 +2204,7 @@ 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); return ret; @@ -7107,9 +7181,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) { @@ -7491,6 +7570,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; @@ -7499,6 +7583,7 @@ static int DoUserAuthFailure(WOLFSSH* ssh, authType |= WOLFSSH_USERAUTH_PUBLICKEY; break; #endif +#endif /* WOLFSSH_TPM */ default: break; } @@ -11187,6 +11272,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); @@ -12692,6 +12785,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, @@ -12789,7 +12891,7 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, } } else - #endif + #endif /* WOLFSSH_AGENT */ { if (ret == WS_SUCCESS) { WMEMSET(digest, 0, sizeof(digest)); @@ -12847,17 +12949,42 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, else { int sigSz; WLOG(WS_LOG_INFO, "Signing hash with RSA."); - sigSz = wc_RsaSSL_Sign(encDigest, encDigestSz, + #ifdef WOLFSSH_TPM + 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 } } } @@ -12876,7 +13003,7 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, } return ret; -} +} /* END BuildUserAuthRequestRsa */ #ifdef WOLFSSH_CERTS @@ -13733,7 +13860,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; @@ -13963,7 +14090,7 @@ static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh, } -#endif +#endif /* !WOLFSSH_NO_RSA || !WOLFSSH_NO_ECDSA || !WOLFSSH_NO_ED25519 */ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig) diff --git a/src/ssh.c b/src/ssh.c index 4fdff0c77..72e4ee1f0 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"; @@ -1657,42 +1707,71 @@ 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, 1, heap); - + ret = IdentifyAsn1Key(in, inSz, isPrivate, heap, &key); if (ret > 0) { - *out = newKey; - *outSz = inSz; - WMEMCPY(newKey, in, inSz); + 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 += 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); + idx += nSz; + + *out = newKey; + } + + wolfSSH_KEY_clean(key); ret = WS_SUCCESS; } else { WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key"); - if (*out == NULL) { - WFREE(newKey, heap, DYNTYPE_PRIVKEY); - } } return ret; @@ -1701,7 +1780,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; @@ -1724,7 +1803,12 @@ 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 { + ret = wc_PubKeyPemToDer(in, inSz, newKey, newKeySz); + } if (ret > 0) { newKeySz = (word32)ret; ret = WS_SUCCESS; @@ -1735,7 +1819,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) { @@ -1817,9 +1901,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; @@ -1831,10 +1915,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); @@ -1846,6 +1932,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) @@ -1922,8 +2023,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 d41f840b8..b7cdc41dc 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -138,6 +138,15 @@ enum WS_ErrorCodes { WS_KDF_E = -1097, /* KDF error*/ WS_LAST_E = -1097 /* Update this to indicate last error */ + /* TODO: Fix names and add hard coded value */ + WOLFSSH_TPM_FAILED_INIT, + WOLFSSH_TPM_FAILED_LOAD_PRIMARY, + WOLFSSH_TPM_FAILED_READ_KEYBLOB, + WOLFSSH_TPM_FAILED_EXPORT_KEY, + WOLFSSH_TPM_FAILED_LOAD_KEY, + WOLFSSH_TPM_FAILED_READ_PUBLIC_KEY, + + WS_LAST_E = WOLFSSH_TPM_FAILED_READ_PUBLIC_KEY /* Update this to indicate last error */ }; diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 731a9688d..8f6e5ed2a 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__) @@ -549,6 +554,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; }; @@ -929,14 +938,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 8f6a2a115..f09df24c1 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 644b98b4fa3458df14fa558e03b91be4f4961d01 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 12 Dec 2024 16:15:10 -0800 Subject: [PATCH 02/10] Fix for repeated WX_LAST_E --- wolfssh/error.h | 1 - 1 file changed, 1 deletion(-) diff --git a/wolfssh/error.h b/wolfssh/error.h index b7cdc41dc..f5073762a 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -137,7 +137,6 @@ 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 */ /* TODO: Fix names and add hard coded value */ WOLFSSH_TPM_FAILED_INIT, WOLFSSH_TPM_FAILED_LOAD_PRIMARY, From 3abdba763eb404e693fb7f6029ba1ae5109d7d86 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 16 Dec 2024 12:20:06 -0800 Subject: [PATCH 03/10] Fixes for build errors without tpm enabled updated readme with clearer steps --- README.md | 8 +++++--- examples/echoserver/echoserver.c | 1 + src/internal.c | 6 +++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f09747739..b318082f0 100644 --- a/README.md +++ b/README.md @@ -532,7 +532,7 @@ fred-cert.der would be: TPM === -wolfSSH now supports TPM support with client key authentication. +wolfSSH now supports 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 @@ -565,8 +565,10 @@ using: $ ./examples/keygen/keygen keyblob.bin -rsa -t -pem -Take key.pem and convert the TPM public key to the ssh-rsa BASE64 username format: -`ssh-keygen -f key.pem -i -m PKCS8`. Update echoserver.c user "hansel"'s public key. +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` +Take this BASE64 encoded public key and update the `samplePublicKeyRsaBuffer` +in `echoserver.c` with it. Make sure to the user is "hansel"'s public key. The directory `examples` contains an echoserver that any client should be able to connect to. From wolfSSH open two terminal instances and run the diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 48eca62b3..81a38052c 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "examples/echoserver/echoserver.h" diff --git a/src/internal.c b/src/internal.c index d6cc47527..127836306 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1546,6 +1546,8 @@ static int GetOpenSshKeyEd25519(ed25519_key* key, } #endif +#ifdef WOLFSSH_TPM + #ifndef WOLFSSH_NO_ECDSA static int GetOpenSshPublicKeyEcc(ecc_key* key, const byte* buf, word32 len, word32* idx) @@ -1631,6 +1633,8 @@ static int GetOpenSshPublicKey(WS_KeySignature *key, return ret; } +#endif /* WOLFSSH_TPM */ + /* * Decodes an OpenSSH format key. */ @@ -12947,9 +12951,9 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, ret = WS_CRYPTO_FAILED; } else { - int sigSz; WLOG(WS_LOG_INFO, "Signing hash with RSA."); #ifdef WOLFSSH_TPM + int sigSz; sigSz = keySig->sigSz; if (ssh->ctx->tpmDev && ssh->ctx->tpmKey) { ret = wc_RsaPad_ex(encDigest, encDigestSz, output+begin, From 436cd917d47bd4a56192cac05b4c08e0334c45c3 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 16 Dec 2024 12:43:55 -0800 Subject: [PATCH 04/10] Not enough arguments for IdentifyAsn1Key fix --- src/internal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal.c b/src/internal.c index 127836306..a93a3cf82 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1865,7 +1865,7 @@ 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); } WFREE(key, heap, DYNTYPE_PUBKEY); From fae59c7dc5186e0fb429ecccbd06f5a08dc8cda0 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 16 Dec 2024 16:37:32 -0800 Subject: [PATCH 05/10] No need for tpm specific return code will refactor passes test/unit.test --- examples/client/common.c | 14 +++++++------- src/ssh.c | 4 ++++ wolfssh/error.h | 10 +--------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/examples/client/common.c b/examples/client/common.c index ae5cfd5f8..489be8b79 100644 --- a/examples/client/common.c +++ b/examples/client/common.c @@ -816,7 +816,7 @@ static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) WLOG(WS_LOG_DEBUG, "Leaving readKeyBlob(), rc = %d", rc); return rc; } - +// make rc check cleanup at end and get rid of uneeded returns static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, WOLFTPM2_KEY* pTpmKey) { @@ -832,7 +832,7 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, #ifdef DEBUG_WOLFSSH printf("TPM 2.0 Device initialization failed\n"); #endif - return WOLFSSH_TPM_FAILED_INIT; + return WS_ERROR; } /* TPM 2.0 keys live under a Primary Key, acquire such key */ @@ -841,7 +841,7 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, #ifdef DEBUG_WOLFSSH printf("Acquiring a Primary TPM 2.0 Key failed\n"); #endif - return WOLFSSH_TPM_FAILED_LOAD_PRIMARY; + return WS_BAD_ARGUMENT; } /* Load the TPM 2.0 key blob from disk */ @@ -850,7 +850,7 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, #ifdef DEBUG_WOLFSSH printf("Reading key blob from disk failed\n"); #endif - return WOLFSSH_TPM_FAILED_READ_KEYBLOB; + return WS_DECRYPT_E; } /* TODO: workaround until password can be supplied */ @@ -866,7 +866,7 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, #ifdef DEBUG_WOLFSSH printf("wolfTPM2_LoadKey failed\n"); #endif - return WOLFSSH_TPM_FAILED_LOAD_KEY; + return WS_BAD_ARGUMENT; } #ifdef DEBUG_WOLFSSH printf("Loaded key to 0x%x\n", (word32)tpmKeyBlob.handle.hndl); @@ -880,7 +880,7 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, #ifdef DEBUG_WOLFSSH printf("Exporting TPM key failed\n"); #endif - return WOLFSSH_TPM_FAILED_EXPORT_KEY; + return WS_MEMORY_E; } /* Read public key from the buffer and convert the key to OpenSSH format */ @@ -891,7 +891,7 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, #ifdef DEBUG_WOLFSSH printf("Reading public key failed returned: %d\n", rc); #endif - return WOLFSSH_TPM_FAILED_READ_PUBLIC_KEY; + return WS_PUBKEY_REJECTED_E; } userPublicKey = p; diff --git a/src/ssh.c b/src/ssh.c index 72e4ee1f0..044513bca 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1807,7 +1807,11 @@ static int DoPemKey(const byte* in, word32 inSz, byte** out, 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; diff --git a/wolfssh/error.h b/wolfssh/error.h index f5073762a..b0000419e 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -137,15 +137,7 @@ enum WS_ErrorCodes { WS_AUTH_PENDING = -1096, /* User authentication still pending */ WS_KDF_E = -1097, /* KDF error*/ - /* TODO: Fix names and add hard coded value */ - WOLFSSH_TPM_FAILED_INIT, - WOLFSSH_TPM_FAILED_LOAD_PRIMARY, - WOLFSSH_TPM_FAILED_READ_KEYBLOB, - WOLFSSH_TPM_FAILED_EXPORT_KEY, - WOLFSSH_TPM_FAILED_LOAD_KEY, - WOLFSSH_TPM_FAILED_READ_PUBLIC_KEY, - - WS_LAST_E = WOLFSSH_TPM_FAILED_READ_PUBLIC_KEY /* Update this to indicate last error */ + WS_LAST_E = WS_KDF_E /* Update this to indicate last error */ }; From 49ec1544637e1ebc7e9ca7a3c64b9271e58e8ddf Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 17 Dec 2024 10:44:51 -0800 Subject: [PATCH 06/10] Refactor - return code fix for make check unit.test and testsuite.test passing make check with normal config --- examples/client/common.c | 111 ++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 60 deletions(-) diff --git a/examples/client/common.c b/examples/client/common.c index 489be8b79..d88079583 100644 --- a/examples/client/common.c +++ b/examples/client/common.c @@ -708,7 +708,7 @@ static int getPrimaryStoragekey(WOLFTPM2_DEV* pDev, rc = wolfTPM2_CreateSRK(pDev, pStorageKey, alg, (byte*)gStorageKeyAuth, sizeof(gStorageKeyAuth)-1); #ifndef WOLFTPM_WINAPI - if (rc == TPM_RC_SUCCESS) { + if (rc == 0) { /* Move storage key into persistent NV */ rc = wolfTPM2_NVStoreKey(pDev, TPM_RH_OWNER, pStorageKey, TPM2_DEMO_STORAGE_KEY_HANDLE); @@ -777,7 +777,7 @@ static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) /* Decode the byte stream into a publicArea structure ready for use */ rc = TPM2_ParsePublic(&key->pub, pubAreaBuffer, (word32)sizeof(pubAreaBuffer), &pubAreaSize); - if (rc != TPM_RC_SUCCESS) return rc; + if (rc != 0) return rc; if (fileSz > 0) { printf("Reading the private part of the key\n"); @@ -816,94 +816,85 @@ static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) WLOG(WS_LOG_DEBUG, "Leaving readKeyBlob(), rc = %d", rc); return rc; } -// make rc check cleanup at end and get rid of uneeded returns + static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, WOLFTPM2_KEY* pTpmKey) { - int rc; + int rc = 0; WOLFTPM2_KEY storage; WOLFTPM2_KEYBLOB tpmKeyBlob; byte* p = NULL; + /* TODO: workaround until password can be supplied */ + /* consider a refactor to take a 32-bit handle and key auth password */ + static const char gKeyAuth[] = "ThisIsMyKeyAuth"; WLOG(WS_LOG_DEBUG, "Entering wolfSSH_TPM_InitKey()"); - rc = wolfTPM2_Init(dev, TPM2_IoCb, NULL); - if (rc != TPM_RC_SUCCESS) { - #ifdef DEBUG_WOLFSSH - printf("TPM 2.0 Device initialization failed\n"); - #endif - return WS_ERROR; + /* Initilize 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); } /* TPM 2.0 keys live under a Primary Key, acquire such key */ - rc = getPrimaryStoragekey(dev, &storage, TPM_ALG_RSA); - if (rc != TPM_RC_SUCCESS) { - #ifdef DEBUG_WOLFSSH - printf("Acquiring a Primary TPM 2.0 Key failed\n"); - #endif - return WS_BAD_ARGUMENT; + if (rc == 0) { + rc = getPrimaryStoragekey(dev, &storage, TPM_ALG_RSA); + if (rc != 0) + WLOG(WS_LOG_DEBUG, "Acquiring a Primary TPM 2.0 Key failed, rc: %d", rc); } /* Load the TPM 2.0 key blob from disk */ - rc = readKeyBlob(name, &tpmKeyBlob); - if (rc != TPM_RC_SUCCESS) { - #ifdef DEBUG_WOLFSSH - printf("Reading key blob from disk failed\n"); - #endif - return WS_DECRYPT_E; + if (rc == 0) { + rc = readKeyBlob(name, &tpmKeyBlob); + if (rc != 0) + WLOG(WS_LOG_DEBUG, "Reading key blob from disk failed, rc: %d", rc); } - /* TODO: workaround until password can be supplied */ - /* consider a refactor to take a 32-bit handle and key auth password */ - static const char gKeyAuth[] = "ThisIsMyKeyAuth"; /* set session for authorization key */ - tpmKeyBlob.handle.auth.size = (int)sizeof(gKeyAuth)-1; - XMEMCPY(tpmKeyBlob.handle.auth.buffer, gKeyAuth, tpmKeyBlob.handle.auth.size); + if (rc == 0) { + tpmKeyBlob.handle.auth.size = (int)sizeof(gKeyAuth)-1; + XMEMCPY(tpmKeyBlob.handle.auth.buffer, gKeyAuth, + tpmKeyBlob.handle.auth.size); + } /* Load the public key into the TPM device */ - rc = wolfTPM2_LoadKey(dev, &tpmKeyBlob, &storage.handle); - if (rc != TPM_RC_SUCCESS) { - #ifdef DEBUG_WOLFSSH - printf("wolfTPM2_LoadKey failed\n"); - #endif - return WS_BAD_ARGUMENT; + if (rc == 0) { + rc = wolfTPM2_LoadKey(dev, &tpmKeyBlob, &storage.handle); + if (rc != 0) + WLOG(WS_LOG_DEBUG, "wolfTPM2_LoadKey failed, rc: %d", rc); + WLOG(WS_LOG_DEBUG, "Loaded key to 0x%x\n", (word32)tpmKeyBlob.handle.hndl); } - #ifdef DEBUG_WOLFSSH - printf("Loaded key to 0x%x\n", (word32)tpmKeyBlob.handle.hndl); - #endif /* Read the public key and extract the public key as a DER/ASN.1 */ - userPublicKeySz = sizeof(userPublicKeyBuf); - rc = wolfTPM2_ExportPublicKeyBuffer(dev, (WOLFTPM2_KEY*)&tpmKeyBlob, - ENCODING_TYPE_ASN1, userPublicKey, &userPublicKeySz); - if (rc != TPM_RC_SUCCESS) { - #ifdef DEBUG_WOLFSSH - printf("Exporting TPM key failed\n"); - #endif - return WS_MEMORY_E; + 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 the buffer and convert the key to OpenSSH format */ - rc = wolfSSH_ReadPublicKey_buffer(userPublicKey, userPublicKeySz, - WOLFSSH_FORMAT_ASN1, &p, &userPublicKeySz, &userPublicKeyType, - &userPublicKeyTypeSz, NULL); - if (rc != TPM_RC_SUCCESS) { - #ifdef DEBUG_WOLFSSH - printf("Reading public key failed returned: %d\n", rc); - #endif - return WS_PUBKEY_REJECTED_E; + /* 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) + WLOG(WS_LOG_DEBUG, "Reading public key failed, rc: %d", rc); + else + userPublicKey = p; } - userPublicKey = p; - - XMEMCPY(&pTpmKey->handle, &tpmKeyBlob.handle, sizeof(pTpmKey->handle)); - XMEMCPY(&pTpmKey->pub, &tpmKeyBlob.pub, sizeof(pTpmKey->pub)); /* Unload SRK storage handle */ - wolfTPM2_UnloadHandle(dev, &storage.handle); - /* Key handle is unloaded on TPM cleanup */ + if (rc == 0) { + XMEMCPY(&pTpmKey->handle, &tpmKeyBlob.handle, sizeof(pTpmKey->handle)); + XMEMCPY(&pTpmKey->pub, &tpmKeyBlob.pub, sizeof(pTpmKey->pub)); + wolfTPM2_UnloadHandle(dev, &storage.handle); + } WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_TPM_InitKey()"); - return WS_SUCCESS; + return rc; } static void wolfSSH_TPM_Cleanup(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key) From 9b583bdc8832f3e7e08ab3cfb4e7685467c1934d Mon Sep 17 00:00:00 2001 From: aidan garske Date: Tue, 17 Dec 2024 14:24:52 -0800 Subject: [PATCH 07/10] Add back private key functionality and gate with isPrivate --- src/ssh.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/ssh.c b/src/ssh.c index 044513bca..040f63137 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1716,7 +1716,11 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, WOLFSSH_UNUSED(heap); ret = IdentifyAsn1Key(in, inSz, isPrivate, heap, &key); - if (ret > 0) { + if (ret <= 0) { + WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key"); + } + + if (ret > 0 && !isPrivate) { long e; byte n[RSA_MAX_SIZE]; /* TODO: Handle small stack */ word32 nSz = (word32)sizeof(n), eSz = (word32)sizeof(e); @@ -1768,12 +1772,39 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, } wolfSSH_KEY_clean(key); - ret = WS_SUCCESS; + } + 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; + } + + *out = newKey; + *outSz = inSz; + WMEMCPY(newKey, in, inSz); + *outType = (const byte*)IdToName(ret); + *outTypeSz = (word32)WSTRLEN((const char*)*outType); } else { WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key"); + if (*out == NULL) { + WFREE(newKey, heap, DYNTYPE_PRIVKEY); + } } + if (ret > 0) + ret = WS_SUCCESS; return ret; } From e048af9e0a283aa8de64f6045a0e3aa431116070 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Wed, 18 Dec 2024 10:03:44 -0800 Subject: [PATCH 08/10] Fix for unused varaible, added checks --- examples/client/common.c | 21 +++++++++++++-------- src/internal.c | 18 +++++++++++------- src/ssh.c | 5 +++-- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/examples/client/common.c b/examples/client/common.c index d88079583..44f3cedf7 100644 --- a/examples/client/common.c +++ b/examples/client/common.c @@ -833,22 +833,25 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, /* Initilize the TPM 2.0 device */ if (rc == 0) { rc = wolfTPM2_Init(dev, TPM2_IoCb, NULL); - if (rc != 0) + if (rc != 0) { WLOG(WS_LOG_DEBUG, "TPM 2.0 Device initialization failed, rc: %d", rc); + } } /* TPM 2.0 keys live under a Primary Key, acquire such key */ if (rc == 0) { rc = getPrimaryStoragekey(dev, &storage, TPM_ALG_RSA); - if (rc != 0) + if (rc != 0) { WLOG(WS_LOG_DEBUG, "Acquiring a Primary TPM 2.0 Key failed, rc: %d", rc); + } } /* Load the TPM 2.0 key blob from disk */ if (rc == 0) { rc = readKeyBlob(name, &tpmKeyBlob); - if (rc != 0) + if (rc != 0) { WLOG(WS_LOG_DEBUG, "Reading key blob from disk failed, rc: %d", rc); + } } /* set session for authorization key */ @@ -861,8 +864,9 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, /* Load the public key into the TPM device */ if (rc == 0) { rc = wolfTPM2_LoadKey(dev, &tpmKeyBlob, &storage.handle); - if (rc != 0) + if (rc != 0) { WLOG(WS_LOG_DEBUG, "wolfTPM2_LoadKey failed, rc: %d", rc); + } WLOG(WS_LOG_DEBUG, "Loaded key to 0x%x\n", (word32)tpmKeyBlob.handle.hndl); } @@ -871,8 +875,9 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, userPublicKeySz = sizeof(userPublicKeyBuf); rc = wolfTPM2_ExportPublicKeyBuffer(dev, (WOLFTPM2_KEY*)&tpmKeyBlob, ENCODING_TYPE_ASN1, userPublicKey, &userPublicKeySz); - if (rc != 0) + 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 */ @@ -880,10 +885,10 @@ static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, rc = wolfSSH_ReadPublicKey_buffer(userPublicKey, userPublicKeySz, WOLFSSH_FORMAT_ASN1, &p, &userPublicKeySz, &userPublicKeyType, &userPublicKeyTypeSz, NULL); - if (rc != 0) + if (rc != 0) { WLOG(WS_LOG_DEBUG, "Reading public key failed, rc: %d", rc); - else - userPublicKey = p; + } + userPublicKey = p; } /* Unload SRK storage handle */ diff --git a/src/internal.c b/src/internal.c index a93a3cf82..020df2a96 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1237,6 +1237,10 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap, int dynType = isPrivate ? DYNTYPE_PRIVKEY : DYNTYPE_PUBKEY; WOLFSSH_UNUSED(dynType); + if (pkey != NULL) { + *pkey = NULL; + } + key = (WS_KeySignature*)WMALLOC(sizeof(WS_KeySignature), heap, dynType); if (key == NULL) { ret = WS_MEMORY_E; @@ -1329,6 +1333,8 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap, ret = WS_UNIMPLEMENTED_E; } else { + if (pkey != NULL) + *pkey = key; ret = key->keySigId; } @@ -1339,10 +1345,6 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap, } } - if (pkey != NULL) { - *pkey = key; - } - return ret; } @@ -1867,8 +1869,9 @@ static int IdentifyCert(const byte* in, word32 inSz, void* heap) if (ret == 0) { ret = IdentifyAsn1Key(key, keySz, 0, heap, NULL); } - - WFREE(key, heap, DYNTYPE_PUBKEY); + if (key != NULL) { + WFREE(key, heap, DYNTYPE_PUBKEY); + } if (cert != NULL) { wc_FreeDecodedCert(cert); #ifdef WOLFSSH_SMALL_STACK @@ -2210,7 +2213,8 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, if (type == BUFTYPE_PRIVKEY) { 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; diff --git a/src/ssh.c b/src/ssh.c index 040f63137..b72729114 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1766,7 +1766,6 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, newKey[idx++] = 0; } WMEMCPY(&newKey[idx], n, nSz); - idx += nSz; *out = newKey; } @@ -1803,8 +1802,10 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, } } - if (ret > 0) + if (ret > 0) { ret = WS_SUCCESS; + } + return ret; } From e153afd325249d44b51c8cbea7eeecee0ead1575 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Wed, 18 Dec 2024 10:28:16 -0800 Subject: [PATCH 09/10] Memory leak fix for key --- src/ssh.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/ssh.c b/src/ssh.c index b72729114..906880ccb 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1769,8 +1769,6 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, *out = newKey; } - - wolfSSH_KEY_clean(key); } else if (ret > 0 && isPrivate) { if (*out == NULL) { @@ -1795,11 +1793,10 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, *outType = (const byte*)IdToName(ret); *outTypeSz = (word32)WSTRLEN((const char*)*outType); } - else { - WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key"); - if (*out == NULL) { - WFREE(newKey, heap, DYNTYPE_PRIVKEY); - } + + wolfSSH_KEY_clean(key); + if (*out == NULL) { + WFREE(newKey, heap, DYNTYPE_PRIVKEY); } if (ret > 0) { From 9d2951bcf3910da3a2631d946d2245757d659f10 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Wed, 18 Dec 2024 12:12:54 -0800 Subject: [PATCH 10/10] Fix for memory leak --- src/internal.c | 3 ++- src/ssh.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/internal.c b/src/internal.c index 020df2a96..3d71c3823 100644 --- a/src/internal.c +++ b/src/internal.c @@ -1338,7 +1338,8 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap, ret = key->keySigId; } - if (pkey == NULL || ret == WS_UNIMPLEMENTED_E) { + /* if not returning key then free it */ + if (pkey == NULL || *pkey == NULL) { wolfSSH_KEY_clean(key); WFREE(key, heap, dynType); key = NULL; diff --git a/src/ssh.c b/src/ssh.c index 906880ccb..a4fb7b161 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1751,7 +1751,7 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, c32toa((word32)WSTRLEN(keyFormat), &newKey[idx]); idx += LENGTH_SZ; WMEMCPY(&newKey[idx], keyFormat, (word32)WSTRLEN(keyFormat)); - idx += WSTRLEN(keyFormat); + idx += (word32)WSTRLEN(keyFormat); /* encode public exponent (e) */ c32toa(eSz, &newKey[idx]); @@ -1795,6 +1795,8 @@ static int DoAsn1Key(const byte* in, word32 inSz, byte** out, } wolfSSH_KEY_clean(key); + WFREE(key, heap, isPrivate ? DYNTYPE_PRIVKEY : DYNTYPE_PUBKEY); + if (*out == NULL) { WFREE(newKey, heap, DYNTYPE_PRIVKEY); }