diff --git a/README.md b/README.md index 357643931..b318082f0 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 +=== + +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 +[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 + +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 +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..44f3cedf7 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,247 @@ 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 == 0) { + /* 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 != 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) +{ + 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()"); + + /* 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 */ + 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 */ + if (rc == 0) { + rc = readKeyBlob(name, &tpmKeyBlob); + if (rc != 0) { + WLOG(WS_LOG_DEBUG, "Reading key blob from disk failed, rc: %d", rc); + } + } + + /* set session for authorization key */ + 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 */ + 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); + } + + /* 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) { + WLOG(WS_LOG_DEBUG, "Reading public key failed, rc: %d", rc); + } + userPublicKey = p; + } + + /* Unload SRK storage handle */ + 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 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 */ @@ -687,6 +938,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 +946,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 +955,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 +979,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 +1022,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 +1060,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..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" @@ -2855,8 +2856,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..3d71c3823 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; @@ -1250,8 +1237,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; } @@ -1280,8 +1270,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 +1303,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 +1311,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 +1323,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 */ @@ -1349,10 +1333,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; @@ -1557,6 +1548,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. */ @@ -1787,10 +1868,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 @@ -2130,9 +2212,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; @@ -7107,9 +7190,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 +7579,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 +7592,7 @@ static int DoUserAuthFailure(WOLFSSH* ssh, authType |= WOLFSSH_USERAUTH_PUBLICKEY; break; #endif +#endif /* WOLFSSH_TPM */ default: break; } @@ -11187,6 +11281,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 +12794,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 +12900,7 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, } } else - #endif + #endif /* WOLFSSH_AGENT */ { if (ret == WS_SUCCESS) { WMEMSET(digest, 0, sizeof(digest)); @@ -12845,19 +12956,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 } } } @@ -12876,7 +13012,7 @@ static int BuildUserAuthRequestRsa(WOLFSSH* ssh, } return ret; -} +} /* END BuildUserAuthRequestRsa */ #ifdef WOLFSSH_CERTS @@ -13733,7 +13869,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 +14099,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..a4fb7b161 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,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; - } + ret = IdentifyAsn1Key(in, inSz, isPrivate, heap, &key); + if (ret <= 0) { + WLOG(WS_LOG_DEBUG, "Unable to identify ASN.1 key"); } - else { - if (*outSz < inSz) { - WLOG(WS_LOG_DEBUG, "DER private key output size too small"); - return WS_BUFFER_E; + + 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; + } } - newKey = *out; - } + 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); - ret = IdentifyAsn1Key(in, inSz, 1, heap); + *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; @@ -1701,7 +1811,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 +1834,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; @@ -1735,7 +1854,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 +1936,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 +1950,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 +1967,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 +2058,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..b0000419e 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 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*);