From b272cad6cfabb18e99ee95fe7f03cbcbc155aa18 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 12 Jan 2023 17:23:02 -0800 Subject: [PATCH] Added integration of TPM 2.0 into wolfSSH * Added `--enable-tpm` build option * Added new members to `WOLFSSH_CTX` structure * Added TPM 2.0 key handling in client example * Added TPM 2.0 signing in wolfSSH internals * Added wolfSSH set methods for TPM device and key * Added TPM helper functions from wolfTPM into wolfSSH * Modified automake, wolfTPM is used as a system library --- configure.ac | 24 +++- examples/client/client.c | 226 ++++++++++++++++++++++++++++++- examples/client/include.am | 4 + examples/echoserver/echoserver.c | 5 +- src/internal.c | 15 +- src/ssh.c | 42 ++++++ wolfssh/internal.h | 7 + wolfssh/ssh.h | 20 +++ 8 files changed, 335 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index f3690da98..4a604f4a0 100644 --- a/configure.ac +++ b/configure.ac @@ -202,6 +202,11 @@ 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]) + # smallstack AC_ARG_ENABLE([smallstack], [AS_HELP_STRING([--enable-smallstack],[Enable small stack (default: disabled)])], @@ -246,12 +251,16 @@ AS_IF([test "x$ENABLED_PTERM" = "xyes"], [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_TERM"]) AS_IF([test "x$ENABLED_SHELL" = "xyes"], [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SHELL"]) -AS_IF([test "x$ENABLED_AGENT" = "xyes"],[AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_AGENT"]) -AS_IF([test "x$ENABLED_CERTS" = "xyes"],[AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_CERTS"]) +AS_IF([test "x$ENABLED_AGENT" = "xyes"], + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_AGENT"]) +AS_IF([test "x$ENABLED_CERTS" = "xyes"], + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_CERTS"]) +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"]) + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SMALL_STACK"]) AS_IF([test "x$ENABLED_SSHD" = "xyes"], - [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SSHD"]) + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SSHD"]) if test "$ENABLED_SSHD" = "yes"; then if test -n "$PAM_LIB" @@ -291,6 +300,11 @@ if test "$ENABLED_SSHD" = "yes"; then fi fi +if test "x$ENABLED_TPM" = "xyes" ; 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 + # Set the automake conditionals. AM_CONDITIONAL([BUILD_EXAMPLE_SERVERS],[test "x$ENABLED_EXAMPLES" = "xyes"]) @@ -305,6 +319,7 @@ AM_CONDITIONAL([BUILD_SHELL],[test "x$ENABLED_SHELL" = "xyes"]) AM_CONDITIONAL([BUILD_AGENT],[test "x$ENABLED_AGENT" = "xyes"]) AM_CONDITIONAL([BUILD_SSHD],[test "x$ENABLED_SSHD" = "xyes"]) AM_CONDITIONAL([BUILD_CERTS],[test "x$ENABLED_CERTS" = "xyes"]) +AM_CONDITIONAL([BUILD_TPM],[test "x$ENABLED_TPM" = "xyes"]) AX_HARDEN_CC_COMPILER_FLAGS @@ -347,6 +362,7 @@ AS_ECHO([" * scp: $ENABLED_SCP"]) AS_ECHO([" * sftp: $ENABLED_SFTP"]) AS_ECHO([" * sshd: $ENABLED_SSHD"]) 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 7bec27edf..a048fed92 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -70,6 +70,10 @@ #include #endif +#ifdef WOLFSSH_TPM + #include + #include "../../wolfTPM/examples/tpm_io.h" +#endif /* WOLFSSH_TPM */ #ifndef NO_WOLFSSH_CLIENT @@ -702,6 +706,13 @@ static THREAD_RET readInput(void* in) } +#ifdef WOLFSSH_AGENT +static inline void ato32(const byte* c, word32* u32) +{ + *u32 = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3]; +} +#endif + static THREAD_RET readPeer(void* in) { byte buf[80]; @@ -817,6 +828,132 @@ static THREAD_RET readPeer(void* in) #endif /* !SINGLE_THREADED && !WOLFSSL_NUCLEUS */ + +#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; + + /* 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; + WMEMCPY(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); + return rc; +} + + +static int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) +{ + int rc = 0; +#if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) + WFILE *fp = NULL; + size_t fileSz = 0; + size_t bytes_read = 0; + byte pubAreaBuffer[sizeof(TPM2B_PUBLIC)]; + int pubAreaSize; + + WMEMSET(key, 0, sizeof(WOLFTPM2_KEYBLOB)); + + if (WFOPEN(&fp, filename, "rb") == 0) { + WFSEEK(fp, 0, XSEEK_END); + fileSz = WFTELL(fp); + WREWIND(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 = WFREAD(&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 = WFREAD(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 = WFREAD(&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) + WFCLOSE(fp); +#else + (void)filename; + (void)key; +#endif /* !NO_FILESYSTEM && !NO_WRITE_TEMP_FILES */ + return rc; +} + +#endif /* WOLFSSH_TPM */ + #ifdef WOLFSSH_CERTS static int load_der_file(const char* filename, byte** out, word32* outSz) @@ -995,6 +1132,65 @@ static int wolfSSH_AGENT_IO_Cb(WS_AgentIoCbAction action, #endif /* WOLFSSH_AGENT */ +#ifdef WOLFSSH_TPM +static int wolfSSH_TPM_InitKey(WOLFTPM2_DEV* dev, const char* name, WOLFTPM2_KEY* tpmKey) +{ + WOLFTPM2_KEY storage; + WOLFTPM2_KEYBLOB tpmKeyBlob; + + if (wolfTPM2_Init(dev, TPM2_IoCb, NULL) != 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 */ + if (getPrimaryStoragekey(dev, &storage, TPM_ALG_RSA)) { +#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 */ + if (readKeyBlob(name, &tpmKeyBlob) != TPM_RC_SUCCESS) { +#ifdef DEBUG_WOLFSSH + printf("Reading key blob from disk failed\n"); +#endif + return WOLFSSH_TPM_FAILED_READ_KEYBLOB; + } + + /* Load the key into the TPM device */ + if (wolfTPM2_LoadKey(dev, &tpmKeyBlob, &storage.handle) != 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 + + WMEMCPY(&tpmKey->handle, &tpmKeyBlob.handle, sizeof(tpmKey->handle)); + WMEMCPY(&tpmKey->pub, &tpmKeyBlob.pub, sizeof(tpmKey->pub)); + return WOLFSSH_TPM_SUCCESS; +} + + +static void wolfSSH_TPM_Cleanup(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key) +{ + if (key != NULL) { + wolfTPM2_UnloadHandle(dev, &key->handle); + } + + if (dev != NULL) { + wolfTPM2_Cleanup(dev); + } +} +#endif /* WOLFSSH_TPM */ + + THREAD_RETURN WOLFSSH_THREAD client_test(void* args) { WOLFSSH_CTX* ctx = NULL; @@ -1022,6 +1218,10 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) byte useAgent = 0; WS_AgentCbActionCtx agentCbCtx; #endif +#ifdef WOLFSSH_TPM + WOLFTPM2_DEV tpmDev; + WOLFTPM2_KEY tpmKey; +#endif int argc = ((func_args*)args)->argc; char** argv = ((func_args*)args)->argv; @@ -1157,7 +1357,18 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (ret != 0) err_sys("Couldn't load private key buffer."); } 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 + */ + ret = wolfSSH_TPM_InitKey(&tpmDev, privKeyName, &tpmKey); + #elif !defined(NO_FILESYSTEM) userPrivateKey = NULL; /* create new buffer based on parsed input */ ret = wolfSSH_ReadKey_file(privKeyName, (byte**)&userPrivateKey, &userPrivateKeySz, @@ -1239,6 +1450,15 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (ssh == NULL) err_sys("Couldn't create wolfSSH session."); +#ifdef WOLFSSH_TPM + /* Link the TPM 2.0 device to the SSH session + * This way, SSH client operations that can + * take advantage of the hardware security + * will have access to the TPM 2.0 device */ + wolfSSH_SetTpmDev(ssh, &tpmDev); + wolfSSH_SetTpmKey(ssh, &tpmKey); +#endif + #if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) wolfSSH_SetGlobalReq(ctx, callbackGlobalReq); wolfSSH_SetGlobalReqCtx(ssh, &ssh); /* dummy ctx */ @@ -1399,6 +1619,10 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (privKeyName != NULL && userPrivateKey != NULL) { WFREE(userPrivateKey, NULL, DYNTYPE_PRIVKEY); } +#ifdef WOLFSSH_TPM + wolfSSH_TPM_Cleanup(&tpmDev, &tpmKey); +#endif + #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/include.am b/examples/client/include.am index 7e51f919e..d32db0787 100644 --- a/examples/client/include.am +++ b/examples/client/include.am @@ -7,4 +7,8 @@ examples_client_client_SOURCES = examples/client/client.c \ examples/client/client.h examples_client_client_LDADD = src/libwolfssh.la examples_client_client_DEPENDENCIES = src/libwolfssh.la + +if BUILD_TPM +examples_client_client_SOURCES += ../wolfTPM/examples/tpm_io.c +endif endif diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index aecc8c7cb..433839bc1 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -2619,8 +2619,9 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) #endif /* NO_WOLFSSH_SERVER */ - -void wolfSSL_Debugging_ON(void); +#ifdef DEBUG_WOLFSSH +extern void wolfSSL_Debugging_ON(void); +#endif int wolfSSH_Echoserver(int argc, char** argv) { diff --git a/src/internal.c b/src/internal.c index c421138ab..7f9215fb7 100644 --- a/src/internal.c +++ b/src/internal.c @@ -56,6 +56,9 @@ #include "src/misc.c" #endif +#ifdef WOLFSSH_TPM + #include +#endif /* Flags: @@ -10087,11 +10090,21 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, ret = WS_CRYPTO_FAILED; } else { - int sigSz; + int sigSz = 0; WLOG(WS_LOG_INFO, "Signing hash with RSA."); +#ifdef WOLFSSH_TPM + if (ssh->ctx->tpmDev && ssh->ctx->tpmKey) { + wolfTPM2_SignHashScheme(ssh->ctx->tpmDev, ssh->ctx->tpmKey, + encDigest, encDigestSz, + output+begin, (int*)&sigSz, + TPM_ALG_OAEP, TPM_ALG_SHA1); + } + else +#else sigSz = wc_RsaSSL_Sign(encDigest, encDigestSz, output + begin, keySig->sigSz, &keySig->ks.rsa.key, ssh->rng); +#endif if (sigSz <= 0 || (word32)sigSz != keySig->sigSz) { WLOG(WS_LOG_DEBUG, "SUAR: Bad RSA Sign"); ret = WS_RSA_E; diff --git a/src/ssh.c b/src/ssh.c index 020e94640..5a0c63d75 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -386,6 +386,48 @@ 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; +} + + +void wolfSSH_SetTpmKey(WOLFSSH* ssh, WOLFTPM2_KEY* key) +{ + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetTpmKey()"); + + if (ssh && ssh->ctx) + ssh->ctx->tpmKey = key; +} + + +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"; diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 7218fbc3b..7e40bdc5e 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -46,6 +46,9 @@ #include #endif /* WOLFSSH_CERTS */ +#ifdef WOLFSSH_TPM + #include +#endif /* WOLFSSH_TPM */ #if !defined (ALIGN16) #if defined (__GNUC__) @@ -453,6 +456,10 @@ struct WOLFSSH_CTX { #ifdef WOLFSSH_AGENT byte agentEnabled; #endif /* WOLFSSH_AGENT */ +#ifdef WOLFSSH_TPM + WOLFTPM2_DEV* tpmDev; + WOLFTPM2_KEY* tpmKey; +#endif /* WOLFSSH_TPM */ }; diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index a48b48a2b..4d1008e14 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 @@ -146,6 +150,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*); @@ -338,6 +350,14 @@ enum WS_DisconnectReasonCodes { WOLFSSH_DISCONNECT_ILLEGAL_USER_NAME = 15 }; +enum WS_TpmResults +{ + WOLFSSH_TPM_SUCCESS, + WOLFSSH_TPM_FAILED_INIT, + WOLFSSH_TPM_FAILED_LOAD_PRIMARY, + WOLFSSH_TPM_FAILED_READ_KEYBLOB, + WOLFSSH_TPM_FAILED_LOAD_KEY, +}; WOLFSSH_API int wolfSSH_RealPath(const char* defaultPath, char* in, char* out, word32 outSz);