diff --git a/.github/workflows/sanitizer.yml b/.github/workflows/sanitizer.yml index f611630b..a2405cd2 100644 --- a/.github/workflows/sanitizer.yml +++ b/.github/workflows/sanitizer.yml @@ -49,6 +49,12 @@ jobs: - name: "LeakSan" cflags: "-fsanitize=leak -fno-omit-frame-pointer -g" ldflags: "-fsanitize=leak" + - name: "ASan (small stack)" + cflags: "-fsanitize=address -fno-omit-frame-pointer -g -O1" + ldflags: "-fsanitize=address" + asan_options: "detect_leaks=0" + wolftpm_extra_config: "--enable-smallstack" + wolfssl_extra_config: "--enable-smallstack" # Same base sanitizers, but linked against the latest wolfSSL -stable # release instead of master. diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index f22e60a2..e9f1dd49 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -499,7 +499,7 @@ typedef struct FWTPM_EventHashCopies { * tpm2_param_enc.c (TPM2_KDFa, TPM2_KDFe, TPM2_ParamEnc_XOR, * TPM2_ParamEnc_AESCFB). See wolftpm/tpm2_param_enc.h */ -static int FwSessionIsBoundToEntity(FWTPM_CTX* ctx, +WOLFTPM_TEST_VIS int FwSessionIsBoundToEntity(FWTPM_CTX* ctx, const FWTPM_Session* sess, TPM_HANDLE handle); static void FwLookupEntityAuth(FWTPM_CTX* ctx, TPM_HANDLE handle, const byte** authVal, int* authValSz); @@ -741,7 +741,7 @@ static int FwGetEntityName(FWTPM_CTX* ctx, TPM_HANDLE handle, return 0; } -static int FwSessionIsBoundToEntity(FWTPM_CTX* ctx, +WOLFTPM_TEST_VIS int FwSessionIsBoundToEntity(FWTPM_CTX* ctx, const FWTPM_Session* sess, TPM_HANDLE handle) { byte entityName[sizeof(sess->bindName.name)]; @@ -764,17 +764,6 @@ static int FwSessionIsBoundToEntity(FWTPM_CTX* ctx, entityAuth, entityAuthSz) == 0; } -#ifdef WOLFTPM_FWTPM_UNIT_TEST -int FWTPM_TestSessionIsBoundToEntity(FWTPM_CTX* ctx, - const FWTPM_Session* sess, TPM_HANDLE handle); - -int FWTPM_TestSessionIsBoundToEntity(FWTPM_CTX* ctx, - const FWTPM_Session* sess, TPM_HANDLE handle) -{ - return FwSessionIsBoundToEntity(ctx, sess, handle); -} -#endif - /** \brief Look up the authValue for an entity by its TPM handle. * Searches hierarchies, NV indices, loaded objects, and hash sequences. */ static void FwLookupEntityAuth(FWTPM_CTX* ctx, TPM_HANDLE handle, @@ -6031,26 +6020,6 @@ static TPM_RC FwCmd_ReadPublic(FWTPM_CTX* ctx, TPM2_Packet* cmd, return rc; } -#ifdef WOLFTPM_FWTPM_UNIT_TEST -static int gFwTestClearRngFailure; - -void FWTPM_TestSetClearRngFailure(int enabled); - -void FWTPM_TestSetClearRngFailure(int enabled) -{ - gFwTestClearRngFailure = enabled; -} -#endif - -static int FwGenerateClearSeed(WC_RNG* rng, byte* seed, word32 seedSz) -{ -#ifdef WOLFTPM_FWTPM_UNIT_TEST - if (gFwTestClearRngFailure) - return TPM_RC_FAILURE; -#endif - return wc_RNG_GenerateBlock(rng, seed, seedSz); -} - /* --- TPM2_Clear (CC 0x0126) --- */ static TPM_RC FwCmd_Clear(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, TPM2_Packet* rsp, UINT16 cmdTag) @@ -6094,10 +6063,10 @@ static TPM_RC FwCmd_Clear(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, #endif /* Generate new storage primary seed */ - rc = FwGenerateClearSeed(&ctx->rng, newOwnerSeed, + rc = wc_RNG_GenerateBlock(&ctx->rng, newOwnerSeed, sizeof(newOwnerSeed)); if (rc == 0) { - rc = FwGenerateClearSeed(&ctx->rng, newTimeEpoch, + rc = wc_RNG_GenerateBlock(&ctx->rng, newTimeEpoch, sizeof(newTimeEpoch)); } if (rc != 0) rc = TPM_RC_FAILURE; @@ -17556,7 +17525,7 @@ static int FwHexNibble(char c) /* Convert a big-endian hex value to binary. An odd leading digit is the low * nibble of the first output byte. Returns byte count, or -1 on error. */ -static int FwHexToBin(const char* hex, byte* out, int outSz) +WOLFTPM_TEST_VIS int FwHexToBin(const char* hex, byte* out, int outSz) { int i, len, decodedLen, outPos = 0; int hi, lo; @@ -17586,15 +17555,6 @@ static int FwHexToBin(const char* hex, byte* out, int outSz) return outPos; } -#ifdef WOLFTPM_FWTPM_UNIT_TEST -int FWTPM_TestHexToBin(const char* hex, byte* out, int outSz); - -int FWTPM_TestHexToBin(const char* hex, byte* out, int outSz) -{ - return FwHexToBin(hex, out, outSz); -} -#endif - /* --- TPM2_ECC_Parameters (CC 0x0178) --- * Returns curve parameters from wolfCrypt's ecc_set_type via * wc_ecc_get_curve_params(). Automatically supports P-256, P-384, diff --git a/src/fwtpm/fwtpm_io.c b/src/fwtpm/fwtpm_io.c index 2137dd99..dc3fdf98 100644 --- a/src/fwtpm/fwtpm_io.c +++ b/src/fwtpm/fwtpm_io.c @@ -375,17 +375,6 @@ static int HandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, UINT32 tssCmd) return TransportSend(ctx, FWTPM_IO_TYPE_COMMAND, clientFd, &netVal, 4); } -#ifdef WOLFTPM_FWTPM_UNIT_TEST -int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, - UINT32 tssCmd); - -int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, - UINT32 tssCmd) -{ - return HandleMssimSignal(ctx, clientFd, tssCmd); -} -#endif - /* --- Process and send TPM command response --- */ static int DispatchAndRespond(FWTPM_CTX* ctx, UINT32 cmdSize, int locality, SOCKET_T clientFd, int isSwtpm) diff --git a/tests/fwtpm_check.sh b/tests/fwtpm_check.sh index 581f0f7d..4df6b40f 100755 --- a/tests/fwtpm_check.sh +++ b/tests/fwtpm_check.sh @@ -25,6 +25,7 @@ FAIL=0 SKIP=0 STARTED_SERVER=0 SKIP_EXAMPLES=0 +SKIP_EXAMPLES_REASON="" # --- Helpers --- @@ -250,6 +251,15 @@ if [ -f "$WOLFTPM_OPTIONS" ]; then if grep -q "^#define NO_GETENV" "$WOLFTPM_OPTIONS"; then HAS_GETENV=0 fi + if [ $IS_FWTPM_MODE -eq 1 ] && \ + grep -q "^#define WOLFTPM_SMALL_STACK" "$WOLFTPM_OPTIONS"; then + # fwTPM serializes private RSA state into Create responses that exceed + # the supported 1,350-byte small-stack client response buffer. The + # unit tests use ECC for those cases; skip the general example suite, + # which intentionally exercises RSA child-key creation. + SKIP_EXAMPLES=1 + SKIP_EXAMPLES_REASON="fwTPM RSA responses exceed small-stack buffer" + fi fi # Determine mode @@ -293,6 +303,7 @@ if [ $HAS_RSA_NO_PAD -eq 0 ]; then echo " Rebuild wolfSSL with: CFLAGS=\"-DWC_RSA_NO_PADDING\"" echo " fwTPM requires WC_RSA_NO_PADDING for RSA encrypt/decrypt" SKIP_EXAMPLES=1 + SKIP_EXAMPLES_REASON="missing WC_RSA_NO_PADDING" fi # --- Auto-detect feature flags for run_examples.sh --- @@ -502,7 +513,7 @@ fi # --- Run examples --- if [ $SKIP_EXAMPLES -eq 1 ]; then - echo "SKIP: run_examples.sh (missing WC_RSA_NO_PADDING)" + echo "SKIP: run_examples.sh ($SKIP_EXAMPLES_REASON)" SKIP=$((SKIP + 1)) elif [ -x "$RUN_EXAMPLES" ]; then echo "" diff --git a/tests/fwtpm_io_test_adapter.c b/tests/fwtpm_io_test_adapter.c index d9a3c838..397d90e4 100644 --- a/tests/fwtpm_io_test_adapter.c +++ b/tests/fwtpm_io_test_adapter.c @@ -14,3 +14,12 @@ #endif #include "../src/fwtpm/fwtpm_io.c" + +int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, + UINT32 tssCmd); + +int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, + UINT32 tssCmd) +{ + return HandleMssimSignal(ctx, clientFd, tssCmd); +} diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index e5754bb2..ccbb4f8b 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -67,6 +67,15 @@ int FWTPM_IO_TestHandleMssimSignal(FWTPM_CTX* ctx, SOCKET_T clientFd, UINT32 tssCmd); #endif +#if defined(WOLFTPM_FWTPM_UNIT_TEST) && defined(HAVE_HASHDRBG) + /* wolfSSL 5.8 and older keep this Hash-DRBG state value private. */ + #ifdef WC_DRBG_FAILED + #define FWTPM_TEST_DRBG_FAILED WC_DRBG_FAILED + #else + #define FWTPM_TEST_DRBG_FAILED 2 + #endif +#endif + /* Test Fail Helpers (same as unit_tests.c) */ #ifndef NO_ABORT #ifndef XABORT @@ -1518,21 +1527,19 @@ static void test_fwtpm_getcap_ecc_curves(void) #if defined(HAVE_ECC) && !defined(FWTPM_NO_ECDH) && \ defined(WOLFTPM_FWTPM_UNIT_TEST) -int FWTPM_TestHexToBin(const char* hex, byte* out, int outSz); - static void test_fwtpm_hex_to_bin(void) { byte out[2]; XMEMSET(out, 0, sizeof(out)); - AssertIntEQ(FWTPM_TestHexToBin("abc", out, sizeof(out)), 2); + AssertIntEQ(FwHexToBin("abc", out, sizeof(out)), 2); AssertIntEQ(out[0], 0x0a); AssertIntEQ(out[1], 0xbc); - AssertIntEQ(FWTPM_TestHexToBin("f", out, sizeof(out)), 1); + AssertIntEQ(FwHexToBin("f", out, sizeof(out)), 1); AssertIntEQ(out[0], 0x0f); - AssertIntEQ(FWTPM_TestHexToBin("0g", out, sizeof(out)), -1); - AssertIntEQ(FWTPM_TestHexToBin("1234", out, 1), -1); - AssertIntEQ(FWTPM_TestHexToBin(NULL, out, sizeof(out)), -1); + AssertIntEQ(FwHexToBin("0g", out, sizeof(out)), -1); + AssertIntEQ(FwHexToBin("1234", out, 1), -1); + AssertIntEQ(FwHexToBin(NULL, out, sizeof(out)), -1); fwtpm_pass("ECC parameter hex decoding:", 0); } @@ -10343,7 +10350,7 @@ static UINT32 StartSessionHelper(FWTPM_CTX* ctx, UINT8 sessType) } #ifdef WOLFTPM_FWTPM_UNIT_TEST -int FWTPM_TestSessionIsBoundToEntity(FWTPM_CTX* ctx, +int FwSessionIsBoundToEntity(FWTPM_CTX* ctx, const FWTPM_Session* sess, TPM_HANDLE handle); static void test_fwtpm_bound_session_auth_change(void) @@ -10363,10 +10370,10 @@ static void test_fwtpm_bound_session_auth_change(void) sess.bindAuth.size = (UINT16)sizeof(oldAuth); XMEMCPY(sess.bindAuth.buffer, oldAuth, sizeof(oldAuth)); - AssertIntEQ(FWTPM_TestSessionIsBoundToEntity(&ctx, &sess, + AssertIntEQ(FwSessionIsBoundToEntity(&ctx, &sess, TPM_RH_OWNER), 1); XMEMCPY(ctx.ownerAuth.buffer, newAuth, sizeof(newAuth)); - AssertIntEQ(FWTPM_TestSessionIsBoundToEntity(&ctx, &sess, + AssertIntEQ(FwSessionIsBoundToEntity(&ctx, &sess, TPM_RH_OWNER), 0); TPM2_ForceZero(&ctx, sizeof(ctx)); @@ -14012,9 +14019,7 @@ static void test_fwtpm_clear(void) fwtpm_pass("Clear preserves endorsement seed:", 0); } -#ifdef WOLFTPM_FWTPM_UNIT_TEST -void FWTPM_TestSetClearRngFailure(int enabled); - +#if defined(WOLFTPM_FWTPM_UNIT_TEST) && defined(HAVE_HASHDRBG) /* A failed seed replacement must leave the live hierarchy state unchanged. */ static void test_fwtpm_clear_rng_failure_atomic(void) { @@ -14022,6 +14027,7 @@ static void test_fwtpm_clear_rng_failure_atomic(void) TPM2B_AUTH ownerAuth; TPM2B_AUTH endorsementAuth; byte ownerSeed[FWTPM_SEED_SIZE]; + byte rngStatus; XMEMSET(&ctx, 0, sizeof(ctx)); AssertIntEQ(fwtpm_test_startup(&ctx), TPM_RC_SUCCESS); @@ -14034,10 +14040,12 @@ static void test_fwtpm_clear_rng_failure_atomic(void) sizeof(endorsementAuth)); XMEMCPY(ownerSeed, ctx.ownerSeed, sizeof(ownerSeed)); - FWTPM_TestSetClearRngFailure(1); + /* Use wolfCrypt's real failed-DRBG path instead of a library test hook. */ + rngStatus = ctx.rng.status; + ctx.rng.status = FWTPM_TEST_DRBG_FAILED; AssertIntEQ(SendSimpleSessionCmd(&ctx, TPM_CC_Clear, TPM_RH_LOCKOUT), TPM_RC_FAILURE); - FWTPM_TestSetClearRngFailure(0); + ctx.rng.status = rngStatus; AssertIntEQ(ctx.pendingClear, 0); AssertIntEQ(XMEMCMP(&ctx.ownerAuth, &ownerAuth, sizeof(ownerAuth)), 0); AssertIntEQ(XMEMCMP(&ctx.endorsementAuth, &endorsementAuth, @@ -14050,7 +14058,7 @@ static void test_fwtpm_clear_rng_failure_atomic(void) FWTPM_Cleanup(&ctx); fwtpm_pass("Clear RNG failure is atomic:", 0); } -#endif /* WOLFTPM_FWTPM_UNIT_TEST */ +#endif /* WOLFTPM_FWTPM_UNIT_TEST && HAVE_HASHDRBG */ #ifndef FWTPM_NO_NV static int fail_nv_write(void* ctx, word32 offset, const byte* buf, @@ -18197,12 +18205,12 @@ int fwtpm_unit_tests(int argc, char *argv[]) test_fwtpm_clear(); return 0; } - #ifdef WOLFTPM_FWTPM_UNIT_TEST + #if defined(WOLFTPM_FWTPM_UNIT_TEST) && defined(HAVE_HASHDRBG) if (argc == 2 && XSTRCMP(argv[1], "clear-rng-failure") == 0) { test_fwtpm_clear_rng_failure_atomic(); return 0; } - #endif /* WOLFTPM_FWTPM_UNIT_TEST */ + #endif /* WOLFTPM_FWTPM_UNIT_TEST && HAVE_HASHDRBG */ #if !defined(FWTPM_NO_POLICY) && !defined(NO_RSA) && \ defined(WOLFSSL_KEY_GEN) if (argc == 2 && XSTRCMP(argv[1], "clear-child") == 0) { @@ -18690,9 +18698,9 @@ int fwtpm_unit_tests(int argc, char *argv[]) test_fwtpm_nv_delete_at_capacity(); test_fwtpm_nv_stclear_startup(); #endif /* !FWTPM_NO_NV */ - #ifdef WOLFTPM_FWTPM_UNIT_TEST + #if defined(WOLFTPM_FWTPM_UNIT_TEST) && defined(HAVE_HASHDRBG) test_fwtpm_clear_rng_failure_atomic(); - #endif /* WOLFTPM_FWTPM_UNIT_TEST */ + #endif /* WOLFTPM_FWTPM_UNIT_TEST && HAVE_HASHDRBG */ test_fwtpm_clear(); #if defined(WOLFTPM_FWTPM_UNIT_TEST) && !defined(_WIN32) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 957cb47f..ab1844dd 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2288,7 +2288,11 @@ static void test_wolfTPM2_BoundSession_EmptyAuth_ParamEnc(void) WOLFTPM2_KEY child; WOLFTPM2_SESSION session; TPMT_PUBLIC publicTemplate; -#if !defined(NO_RSA) +#if defined(WOLFTPM_SMALL_STACK) && defined(WOLFTPM_FWTPM_BUILD) && \ + defined(HAVE_ECC) + /* RSA Create responses exceed the reduced small-stack response buffer. */ + TPM_ALG_ID srkAlg = TPM_ALG_ECC; +#elif !defined(NO_RSA) TPM_ALG_ID srkAlg = TPM_ALG_RSA; #else TPM_ALG_ID srkAlg = TPM_ALG_ECC; @@ -2329,7 +2333,13 @@ static void test_wolfTPM2_BoundSession_EmptyAuth_ParamEnc(void) AssertIntEQ(rc, TPM_RC_SUCCESS); /* Create+load a child under the EmptyAuth SRK; fails pre-fix. */ -#if !defined(NO_RSA) +#if defined(WOLFTPM_SMALL_STACK) && defined(WOLFTPM_FWTPM_BUILD) && \ + defined(HAVE_ECC) + rc = wolfTPM2_GetKeyTemplate_ECC(&publicTemplate, + TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth | + TPMA_OBJECT_sign | TPMA_OBJECT_noDA, + TPM_ECC_NIST_P256, TPM_ALG_ECDSA); +#elif !defined(NO_RSA) rc = wolfTPM2_GetKeyTemplate_RSA(&publicTemplate, TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth | TPMA_OBJECT_sign | TPMA_OBJECT_noDA); @@ -2368,7 +2378,11 @@ static void test_wolfTPM2_CreateLoaded_ParamEnc(void) WOLFTPM2_KEYBLOB child; WOLFTPM2_SESSION session; TPMT_PUBLIC publicTemplate; -#if !defined(NO_RSA) +#if defined(WOLFTPM_SMALL_STACK) && defined(WOLFTPM_FWTPM_BUILD) && \ + defined(HAVE_ECC) + /* RSA CreateLoaded responses exceed the small-stack response buffer. */ + TPM_ALG_ID srkAlg = TPM_ALG_ECC; +#elif !defined(NO_RSA) TPM_ALG_ID srkAlg = TPM_ALG_RSA; #else TPM_ALG_ID srkAlg = TPM_ALG_ECC; @@ -2405,7 +2419,13 @@ static void test_wolfTPM2_CreateLoaded_ParamEnc(void) TPMA_SESSION_continueSession)); AssertIntEQ(rc, TPM_RC_SUCCESS); -#if !defined(NO_RSA) +#if defined(WOLFTPM_SMALL_STACK) && defined(WOLFTPM_FWTPM_BUILD) && \ + defined(HAVE_ECC) + rc = wolfTPM2_GetKeyTemplate_ECC(&publicTemplate, + TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth | + TPMA_OBJECT_sign | TPMA_OBJECT_noDA, + TPM_ECC_NIST_P256, TPM_ALG_ECDSA); +#elif !defined(NO_RSA) rc = wolfTPM2_GetKeyTemplate_RSA(&publicTemplate, TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth | TPMA_OBJECT_sign | TPMA_OBJECT_noDA); @@ -10306,7 +10326,10 @@ int unit_tests(int argc, char *argv[]) !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) test_readKeyBlob_PrivOverflow(); #endif + /* An RSA Create response does not fit the reduced small-stack buffer. */ + #if !defined(WOLFTPM_SMALL_STACK) || !defined(WOLFTPM_FWTPM_BUILD) test_wolfTPM2_KeyBlob(TPM_ALG_RSA); + #endif test_wolfTPM2_KeyBlob(TPM_ALG_ECC); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ !defined(WOLFTPM2_NO_ASN) diff --git a/wolftpm/fwtpm/fwtpm_command.h b/wolftpm/fwtpm/fwtpm_command.h index ffecf895..52d1b538 100644 --- a/wolftpm/fwtpm/fwtpm_command.h +++ b/wolftpm/fwtpm/fwtpm_command.h @@ -66,7 +66,7 @@ WOLFTPM_API int FWTPM_ProcessCommand(FWTPM_CTX* ctx, */ WOLFTPM_API void FWTPM_ResetCommandClient(FWTPM_CTX* ctx); -/* Internal crypto helpers exposed only to the fwTPM unit-test build. */ +/* Internal helpers exposed only to the fwTPM unit-test build. */ #ifdef WOLFTPM_FWTPM_UNIT_TEST #define WOLFTPM_TEST_VIS int FwHashOneShot(TPMI_ALG_HASH hashAlg, @@ -83,6 +83,9 @@ WOLFTPM_API void FWTPM_ResetCommandClient(FWTPM_CTX* ctx); const byte* authValue, int authValueSz, const TPM2B_NONCE* nonceDecrypt, const TPM2B_NONCE* nonceEncrypt, int isResponse, byte* hmacOut, int* hmacOutSz); + #ifndef FWTPM_NO_ECDH + int FwHexToBin(const char* hex, byte* out, int outSz); + #endif #else #define WOLFTPM_TEST_VIS static #endif