From 8067df93309d16e218de36fc76ca268518f86ee6 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 12 Feb 2026 21:23:03 +0000 Subject: [PATCH 1/3] Add static memory (zero-malloc) support with compile-time toggle - Add --enable-dynamic-mem configure option; default is static (no malloc) - Guard wolfSPDM_New() behind WOLFSPDM_DYNAMIC_MEMORY define - Refactor Init/New/Free: Init does XMEMSET clean slate, New calls Init and sets isDynamic flag, Free uses wc_ForceZero and conditional XFREE - Add wolfSPDM_InitStatic() and WOLFSPDM_CTX_STATIC_SIZE for consumers to allocate context inline without heap allocation - Generate wolfspdm/options.h from config.h so consumers (e.g. wolfTPM) pick up WOLFSPDM_DYNAMIC_MEMORY and WOLFSPDM_NUVOTON defines - Add options.h to .gitignore (auto-generated build artifact) - Update unit_test.c and test_spdm.c for both static and dynamic paths Tested across all 4 configurations: dynamic-mem + spdm-emu: PASS static + spdm-emu: PASS dynamic-mem + Nuvoton: PASS (5/5) static + Nuvoton: PASS (5/5) --- .gitignore | 3 ++ Makefile.am | 15 ++++++-- config.h.in | 3 ++ configure.ac | 11 ++++++ src/spdm_context.c | 83 +++++++++++++++++++++++---------------------- src/spdm_crypto.c | 2 +- src/spdm_internal.h | 2 ++ src/spdm_secured.c | 9 +++-- src/spdm_session.c | 24 ++++++------- test/test_spdm.c | 26 ++++++++------ test/unit_test.c | 76 +++++++++++++++++++++-------------------- wolfspdm/spdm.h | 67 ++++++++++++++++++++++++++++-------- 12 files changed, 198 insertions(+), 123 deletions(-) diff --git a/.gitignore b/.gitignore index 5b26edc..8ca0239 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,9 @@ test/test_spdm # pkg-config generated wolfspdm.pc +# Auto-generated options header +wolfspdm/options.h + # Editor and IDE files *~ *.swp diff --git a/Makefile.am b/Makefile.am index b5b9ae3..a3e89b4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,11 +19,22 @@ endif libwolfspdm_la_CPPFLAGS = -I$(srcdir)/wolfspdm -I$(srcdir)/src libwolfspdm_la_LIBADD = -lwolfssl +# Generate options.h from config.h (propagate WOLFSPDM_ defines to consumers) +BUILT_SOURCES = wolfspdm/options.h +wolfspdm/options.h: config.h + @mkdir -p wolfspdm + @echo '/* wolfspdm/options.h — auto-generated from config.h, do not edit */' > $@ + @echo '#ifndef WOLFSPDM_OPTIONS_H' >> $@ + @echo '#define WOLFSPDM_OPTIONS_H' >> $@ + @grep '^\#define WOLFSPDM_' config.h >> $@ || true + @echo '#endif /* WOLFSPDM_OPTIONS_H */' >> $@ + # Public headers nobase_include_HEADERS = \ wolfspdm/spdm.h \ wolfspdm/spdm_types.h \ - wolfspdm/spdm_error.h + wolfspdm/spdm_error.h \ + wolfspdm/options.h # Nuvoton header (always installed, conditionally useful) if BUILD_NUVOTON @@ -53,4 +64,4 @@ EXTRA_DIST = \ LICENSE \ wolfspdm.pc.in -CLEANFILES = wolfspdm.pc +CLEANFILES = wolfspdm.pc wolfspdm/options.h diff --git a/config.h.in b/config.h.in index d5477dd..cd10d1b 100644 --- a/config.h.in +++ b/config.h.in @@ -68,6 +68,9 @@ /* Enable debug output */ #undef WOLFSPDM_DEBUG +/* Enable dynamic memory allocation */ +#undef WOLFSPDM_DYNAMIC_MEMORY + /* Enable Nuvoton TPM support */ #undef WOLFSPDM_NUVOTON diff --git a/configure.ac b/configure.ac index ad498e1..4884765 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,16 @@ if test "x$enable_nuvoton" = "xyes"; then fi AM_CONDITIONAL([BUILD_NUVOTON], [test "x$enable_nuvoton" = "xyes"]) +# Dynamic memory allocation (enables wolfSPDM_New/XMALLOC) +AC_ARG_ENABLE([dynamic-mem], + [AS_HELP_STRING([--enable-dynamic-mem], [Enable dynamic memory allocation (wolfSPDM_New)])], + [enable_dynamic_mem=$enableval], + [enable_dynamic_mem=no]) + +if test "x$enable_dynamic_mem" = "xyes"; then + AC_DEFINE([WOLFSPDM_DYNAMIC_MEMORY], [1], [Enable dynamic memory allocation]) +fi + # Output files AC_CONFIG_FILES([Makefile wolfspdm.pc]) AC_OUTPUT @@ -74,5 +84,6 @@ echo "wolfSPDM configuration summary:" echo " Version: $PACKAGE_VERSION" echo " Debug: $enable_debug" echo " Nuvoton: $enable_nuvoton" +echo " Dynamic mem: $enable_dynamic_mem" echo " wolfSSL: ${WOLFSSL_DIR:-system}" echo "" diff --git a/src/spdm_context.c b/src/spdm_context.c index ce1c4c2..68c0f42 100644 --- a/src/spdm_context.c +++ b/src/spdm_context.c @@ -29,21 +29,6 @@ * Context Management * ========================================================================== */ -WOLFSPDM_CTX* wolfSPDM_New(void) -{ - WOLFSPDM_CTX* ctx; - - ctx = (WOLFSPDM_CTX*)XMALLOC(sizeof(WOLFSPDM_CTX), NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (ctx == NULL) { - return NULL; - } - - XMEMSET(ctx, 0, sizeof(WOLFSPDM_CTX)); - ctx->state = WOLFSPDM_STATE_INIT; - - return ctx; -} - int wolfSPDM_Init(WOLFSPDM_CTX* ctx) { int rc; @@ -52,9 +37,9 @@ int wolfSPDM_Init(WOLFSPDM_CTX* ctx) return WOLFSPDM_E_INVALID_ARG; } - if (ctx->initialized) { - return WOLFSPDM_E_ALREADY_INIT; - } + /* Clean slate — do NOT read any fields before this (could be garbage) */ + XMEMSET(ctx, 0, sizeof(WOLFSPDM_CTX)); + ctx->state = WOLFSPDM_STATE_INIT; /* Initialize RNG */ rc = wc_InitRng(&ctx->rng); @@ -70,43 +55,62 @@ int wolfSPDM_Init(WOLFSPDM_CTX* ctx) ctx->reqSessionId = 0x0001; ctx->initialized = 1; - ctx->state = WOLFSPDM_STATE_INIT; + /* isDynamic remains 0 — only wolfSPDM_New sets it */ return WOLFSPDM_SUCCESS; } +#ifdef WOLFSPDM_DYNAMIC_MEMORY +WOLFSPDM_CTX* wolfSPDM_New(void) +{ + WOLFSPDM_CTX* ctx; + + ctx = (WOLFSPDM_CTX*)XMALLOC(sizeof(WOLFSPDM_CTX), NULL, + DYNAMIC_TYPE_TMP_BUFFER); + if (ctx == NULL) { + return NULL; + } + + if (wolfSPDM_Init(ctx) != WOLFSPDM_SUCCESS) { + XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); + return NULL; + } + ctx->isDynamic = 1; /* Tag AFTER Init so it isn't wiped */ + + return ctx; +} +#endif /* WOLFSPDM_DYNAMIC_MEMORY */ + void wolfSPDM_Free(WOLFSPDM_CTX* ctx) { if (ctx == NULL) { return; } +#ifdef WOLFSPDM_DYNAMIC_MEMORY + { + int wasDynamic = ctx->isDynamic; +#endif + /* Free RNG */ if (ctx->rngInitialized) { wc_FreeRng(&ctx->rng); - ctx->rngInitialized = 0; } /* Free ephemeral key */ if (ctx->ephemeralKeyInitialized) { wc_ecc_free(&ctx->ephemeralKey); - ctx->ephemeralKeyInitialized = 0; - } - - /* Zero out sensitive data */ - XMEMSET(ctx->sharedSecret, 0, sizeof(ctx->sharedSecret)); - XMEMSET(ctx->handshakeSecret, 0, sizeof(ctx->handshakeSecret)); - XMEMSET(ctx->reqHsSecret, 0, sizeof(ctx->reqHsSecret)); - XMEMSET(ctx->rspHsSecret, 0, sizeof(ctx->rspHsSecret)); - XMEMSET(ctx->reqFinishedKey, 0, sizeof(ctx->reqFinishedKey)); - XMEMSET(ctx->rspFinishedKey, 0, sizeof(ctx->rspFinishedKey)); - XMEMSET(ctx->reqDataKey, 0, sizeof(ctx->reqDataKey)); - XMEMSET(ctx->rspDataKey, 0, sizeof(ctx->rspDataKey)); - XMEMSET(ctx->reqDataIv, 0, sizeof(ctx->reqDataIv)); - XMEMSET(ctx->rspDataIv, 0, sizeof(ctx->rspDataIv)); - XMEMSET(ctx->reqPrivKey, 0, sizeof(ctx->reqPrivKey)); - - XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + + /* Zero entire struct (covers all sensitive key material) */ + wc_ForceZero(ctx, sizeof(WOLFSPDM_CTX)); + +#ifdef WOLFSPDM_DYNAMIC_MEMORY + if (wasDynamic) { + XFREE(ctx, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } + } +#endif } int wolfSPDM_GetCtxSize(void) @@ -124,9 +128,6 @@ int wolfSPDM_InitStatic(WOLFSPDM_CTX* ctx, int size) return WOLFSPDM_E_BUFFER_SMALL; } - XMEMSET(ctx, 0, sizeof(WOLFSPDM_CTX)); - ctx->state = WOLFSPDM_STATE_INIT; - return wolfSPDM_Init(ctx); } @@ -389,7 +390,7 @@ int wolfSPDM_Disconnect(WOLFSPDM_CTX* ctx) { int rc; byte txBuf[8]; - byte rxBuf[64]; + byte rxBuf[16]; /* END_SESSION_ACK: 4 bytes */ word32 txSz, rxSz; if (ctx == NULL) { diff --git a/src/spdm_crypto.c b/src/spdm_crypto.c index 52cd72e..0dd935b 100644 --- a/src/spdm_crypto.c +++ b/src/spdm_crypto.c @@ -193,7 +193,7 @@ int wolfSPDM_SignHash(WOLFSPDM_CTX* ctx, const byte* hash, word32 hashSz, ecc_key sigKey; int rc; int keyInit = 0; - byte derSig[128]; + byte derSig[104]; /* P-384 DER sig max: 2 + (2+49) + (2+49) = 104 */ word32 derSigSz = sizeof(derSig); word32 rLen, sLen; diff --git a/src/spdm_internal.h b/src/spdm_internal.h index b1ae88e..b3d880a 100644 --- a/src/spdm_internal.h +++ b/src/spdm_internal.h @@ -44,6 +44,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -75,6 +76,7 @@ struct WOLFSPDM_CTX { /* Configuration flags */ int debug; int initialized; + int isDynamic; /* Set by wolfSPDM_New(), checked by wolfSPDM_Free() */ /* Protocol mode (standard SPDM or Nuvoton) */ WOLFSPDM_MODE mode; diff --git a/src/spdm_secured.c b/src/spdm_secured.c index 6169999..f7f2bd3 100644 --- a/src/spdm_secured.c +++ b/src/spdm_secured.c @@ -107,7 +107,6 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, byte iv[WOLFSPDM_AEAD_IV_SIZE]; byte aad[16]; /* Up to 14 bytes for TCG format */ byte plainBuf[WOLFSPDM_MAX_MSG_SIZE + 16]; - byte ciphertext[WOLFSPDM_MAX_MSG_SIZE + 16]; byte tag[WOLFSPDM_AEAD_TAG_SIZE]; word32 plainBufSz; word16 recordLen; @@ -259,13 +258,13 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, return WOLFSPDM_E_CRYPTO_FAIL; } - rc = wc_AesGcmEncrypt(&aes, ciphertext, plainBuf, plainBufSz, + /* Encrypt directly into output buffer (enc + hdrSz) to avoid a copy */ + rc = wc_AesGcmEncrypt(&aes, &enc[hdrSz], plainBuf, plainBufSz, iv, WOLFSPDM_AEAD_IV_SIZE, tag, WOLFSPDM_AEAD_TAG_SIZE, aad, aadSz); if (rc != 0) { return WOLFSPDM_E_CRYPTO_FAIL; } - XMEMCPY(&enc[hdrSz], ciphertext, plainBufSz); XMEMCPY(&enc[hdrSz + plainBufSz], tag, WOLFSPDM_AEAD_TAG_SIZE); *encSz = hdrSz + plainBufSz + WOLFSPDM_AEAD_TAG_SIZE; @@ -495,8 +494,8 @@ int wolfSPDM_SecuredExchange(WOLFSPDM_CTX* ctx, const byte* cmdPlain, word32 cmdSz, byte* rspPlain, word32* rspSz) { - byte encBuf[WOLFSPDM_MAX_MSG_SIZE + 64]; - byte rxBuf[WOLFSPDM_MAX_MSG_SIZE + 64]; + byte encBuf[WOLFSPDM_MAX_MSG_SIZE + 48]; + byte rxBuf[WOLFSPDM_MAX_MSG_SIZE + 48]; word32 encSz = sizeof(encBuf); word32 rxSz = sizeof(rxBuf); int rc; diff --git a/src/spdm_session.c b/src/spdm_session.c index e0fcac1..f5aa12e 100644 --- a/src/spdm_session.c +++ b/src/spdm_session.c @@ -25,7 +25,7 @@ int wolfSPDM_GetVersion(WOLFSPDM_CTX* ctx) { byte txBuf[8]; - byte rxBuf[64]; + byte rxBuf[32]; /* VERSION: 4 hdr + 2 count + up to 8 entries * 2 = 22 */ word32 txSz = sizeof(txBuf); word32 rxSz = sizeof(rxBuf); int rc; @@ -49,8 +49,8 @@ int wolfSPDM_GetVersion(WOLFSPDM_CTX* ctx) int wolfSPDM_GetCapabilities(WOLFSPDM_CTX* ctx) { - byte txBuf[32]; - byte rxBuf[64]; + byte txBuf[24]; /* GET_CAPABILITIES: 20 bytes */ + byte rxBuf[40]; /* CAPABILITIES: 20-36 bytes */ word32 txSz = sizeof(txBuf); word32 rxSz = sizeof(rxBuf); int rc; @@ -74,8 +74,8 @@ int wolfSPDM_GetCapabilities(WOLFSPDM_CTX* ctx) int wolfSPDM_NegotiateAlgorithms(WOLFSPDM_CTX* ctx) { - byte txBuf[64]; - byte rxBuf[128]; + byte txBuf[52]; /* NEGOTIATE_ALGORITHMS: 48 bytes */ + byte rxBuf[80]; /* ALGORITHMS: ~56 bytes with struct tables */ word32 txSz = sizeof(txBuf); word32 rxSz = sizeof(rxBuf); int rc; @@ -122,7 +122,7 @@ int wolfSPDM_GetDigests(WOLFSPDM_CTX* ctx) int wolfSPDM_GetCertificate(WOLFSPDM_CTX* ctx, int slotId) { byte txBuf[16]; - byte rxBuf[2048]; + byte rxBuf[1040]; /* 8 hdr + up to 1024 cert data per chunk */ word32 txSz; word32 rxSz; word16 offset = 0; @@ -164,8 +164,8 @@ int wolfSPDM_GetCertificate(WOLFSPDM_CTX* ctx, int slotId) int wolfSPDM_KeyExchange(WOLFSPDM_CTX* ctx) { - byte txBuf[256]; - byte rxBuf[512]; + byte txBuf[192]; /* KEY_EXCHANGE: ~158 bytes */ + byte rxBuf[384]; /* KEY_EXCHANGE_RSP: ~302 bytes */ word32 txSz = sizeof(txBuf); word32 rxSz = sizeof(rxBuf); int rc; @@ -191,10 +191,10 @@ int wolfSPDM_KeyExchange(WOLFSPDM_CTX* ctx) int wolfSPDM_Finish(WOLFSPDM_CTX* ctx) { - byte finishBuf[160]; /* 148 bytes for mutual auth FINISH */ - byte encBuf[512]; /* Encrypted: FINISH + padding + tag + headers */ - byte rxBuf[256]; - byte decBuf[128]; + byte finishBuf[152]; /* 148 bytes max for mutual auth FINISH */ + byte encBuf[256]; /* Encrypted: hdr(14) + padded(160) + tag(16) = 190 max */ + byte rxBuf[128]; /* Encrypted FINISH_RSP: ~94 bytes max */ + byte decBuf[64]; /* Decrypted FINISH_RSP: 4 hdr + 48 verify = 52 */ word32 finishSz = sizeof(finishBuf); word32 encSz = sizeof(encBuf); word32 rxSz = sizeof(rxBuf); diff --git a/test/test_spdm.c b/test/test_spdm.c index 686d70e..1b1dd10 100644 --- a/test/test_spdm.c +++ b/test/test_spdm.c @@ -139,9 +139,14 @@ static void tcp_disconnect(void) } } +/* Static context buffer — sized via wolfSPDM_GetCtxSize() at runtime, + * but we need a compile-time upper bound. 16KB is generous. */ +#define CTX_BUF_SIZE 16384 +static byte g_ctxBuf[CTX_BUF_SIZE]; + int main(int argc, char* argv[]) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX* ctx = (WOLFSPDM_CTX*)g_ctxBuf; int rc; (void)argc; @@ -149,6 +154,12 @@ int main(int argc, char* argv[]) printf("wolfSPDM Test - Connecting to %s:%d\n", EMU_HOST, EMU_PORT); + if (wolfSPDM_GetCtxSize() > CTX_BUF_SIZE) { + printf("ERROR: CTX_BUF_SIZE too small (%d needed)\n", + wolfSPDM_GetCtxSize()); + return 1; + } + if (tcp_connect(EMU_HOST, EMU_PORT) < 0) { printf("ERROR: Cannot connect to emulator.\n"); printf("Start the emulator first:\n"); @@ -156,17 +167,10 @@ int main(int argc, char* argv[]) return 1; } - ctx = wolfSPDM_New(); - if (ctx == NULL) { - printf("ERROR: wolfSPDM_New failed\n"); - tcp_disconnect(); - return 1; - } - - rc = wolfSPDM_Init(ctx); + rc = wolfSPDM_InitStatic(ctx, CTX_BUF_SIZE); if (rc != WOLFSPDM_SUCCESS) { - printf("ERROR: wolfSPDM_Init failed: %s\n", wolfSPDM_GetErrorString(rc)); - wolfSPDM_Free(ctx); + printf("ERROR: wolfSPDM_InitStatic failed: %s\n", + wolfSPDM_GetErrorString(rc)); tcp_disconnect(); return 1; } diff --git a/test/unit_test.c b/test/unit_test.c index 5faf852..a7434e5 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -5,6 +5,10 @@ * Unit tests for wolfSPDM library functions. */ +#ifdef HAVE_CONFIG_H + #include +#endif + #include #include "../src/spdm_internal.h" #include @@ -40,6 +44,7 @@ static int dummy_io_cb(WOLFSPDM_CTX* ctx, const byte* txBuf, word32 txSz, /* Context Tests */ /* ========================================================================== */ +#ifdef WOLFSPDM_DYNAMIC_MEMORY static int test_context_new_free(void) { WOLFSPDM_CTX* ctx; @@ -50,7 +55,7 @@ static int test_context_new_free(void) TEST_ASSERT(ctx != NULL, "wolfSPDM_New returned NULL"); TEST_ASSERT(ctx->state == WOLFSPDM_STATE_INIT, "Initial state wrong"); - TEST_ASSERT(ctx->initialized == 0, "Should not be initialized yet"); + TEST_ASSERT(ctx->initialized == 1, "Should be initialized by New()"); wolfSPDM_Free(ctx); @@ -59,27 +64,22 @@ static int test_context_new_free(void) TEST_PASS(); } +#endif /* WOLFSPDM_DYNAMIC_MEMORY */ static int test_context_init(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; int rc; printf("test_context_init...\n"); - ctx = wolfSPDM_New(); - TEST_ASSERT(ctx != NULL, "wolfSPDM_New failed"); - rc = wolfSPDM_Init(ctx); TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "wolfSPDM_Init failed"); TEST_ASSERT(ctx->initialized == 1, "Not marked initialized"); TEST_ASSERT(ctx->rngInitialized == 1, "RNG not initialized"); TEST_ASSERT(ctx->reqCaps == WOLFSPDM_DEFAULT_REQ_CAPS, "Default caps wrong"); - /* Double init should fail */ - rc = wolfSPDM_Init(ctx); - TEST_ASSERT(rc == WOLFSPDM_E_ALREADY_INIT, "Double init should fail"); - wolfSPDM_Free(ctx); TEST_PASS(); } @@ -95,26 +95,28 @@ static int test_context_static_alloc(void) TEST_ASSERT(wolfSPDM_GetCtxSize() == (int)sizeof(WOLFSPDM_CTX), "GetCtxSize mismatch"); + /* Too small buffer should fail */ + rc = wolfSPDM_InitStatic(ctx, 10); + TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + rc = wolfSPDM_InitStatic(ctx, sizeof(buffer)); TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "InitStatic failed"); TEST_ASSERT(ctx->initialized == 1, "Static ctx not initialized"); - /* Too small buffer should fail */ - rc = wolfSPDM_InitStatic(ctx, 10); - TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + wolfSPDM_Free(ctx); /* Now safe — no XFREE on static ctx */ TEST_PASS(); } static int test_context_set_io(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; int rc; int dummy = 42; printf("test_context_set_io...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); /* Dummy callback for testing */ @@ -137,14 +139,14 @@ static int test_context_set_io(void) static int test_transcript_add_reset(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte data1[] = {0x01, 0x02, 0x03, 0x04}; byte data2[] = {0x05, 0x06, 0x07, 0x08}; int rc; printf("test_transcript_add_reset...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); TEST_ASSERT(ctx->transcriptLen == 0, "Transcript should start empty"); @@ -168,14 +170,14 @@ static int test_transcript_add_reset(void) static int test_transcript_hash(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte data[] = "test data for hashing"; byte hash[WOLFSPDM_HASH_SIZE]; int rc; printf("test_transcript_hash...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); wolfSPDM_TranscriptAdd(ctx, data, sizeof(data) - 1); @@ -196,13 +198,13 @@ static int test_transcript_hash(void) static int test_certchain_hash(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte certData[] = {0x30, 0x82, 0x01, 0x00, 0xAA, 0xBB, 0xCC, 0xDD}; int rc; printf("test_certchain_hash...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); rc = wolfSPDM_CertChainAdd(ctx, certData, sizeof(certData)); @@ -229,13 +231,13 @@ static int test_certchain_hash(void) static int test_random_generation(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte buf1[32], buf2[32]; int rc; printf("test_random_generation...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); rc = wolfSPDM_GetRandom(ctx, buf1, sizeof(buf1)); @@ -254,7 +256,8 @@ static int test_random_generation(void) static int test_ephemeral_key_generation(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte pubKeyX[WOLFSPDM_ECC_KEY_SIZE]; byte pubKeyY[WOLFSPDM_ECC_KEY_SIZE]; word32 xSz = sizeof(pubKeyX); @@ -263,7 +266,6 @@ static int test_ephemeral_key_generation(void) printf("test_ephemeral_key_generation...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); rc = wolfSPDM_GenerateEphemeralKey(ctx); @@ -372,14 +374,14 @@ static int test_build_get_version(void) static int test_build_get_capabilities(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[32]; word32 bufSz = sizeof(buf); int rc; printf("test_build_get_capabilities...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; @@ -395,14 +397,14 @@ static int test_build_get_capabilities(void) static int test_build_negotiate_algorithms(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[64]; word32 bufSz = sizeof(buf); int rc; printf("test_build_negotiate_algorithms...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; @@ -418,14 +420,14 @@ static int test_build_negotiate_algorithms(void) static int test_build_get_digests(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[16]; word32 bufSz = sizeof(buf); int rc; printf("test_build_get_digests...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; @@ -440,14 +442,14 @@ static int test_build_get_digests(void) static int test_build_get_certificate(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[16]; word32 bufSz = sizeof(buf); int rc; printf("test_build_get_certificate...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; @@ -464,14 +466,14 @@ static int test_build_get_certificate(void) static int test_build_end_session(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[16]; word32 bufSz = sizeof(buf); int rc; printf("test_build_end_session...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; @@ -526,11 +528,11 @@ static int test_error_strings(void) static int test_session_state(void) { - WOLFSPDM_CTX* ctx; + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; printf("test_session_state...\n"); - ctx = wolfSPDM_New(); wolfSPDM_Init(ctx); TEST_ASSERT(wolfSPDM_IsConnected(ctx) == 0, "Should not be connected"); @@ -562,7 +564,9 @@ int main(void) printf("===========================================\n\n"); /* Context tests */ +#ifdef WOLFSPDM_DYNAMIC_MEMORY test_context_new_free(); +#endif test_context_init(); test_context_static_alloc(); test_context_set_io(); diff --git a/wolfspdm/spdm.h b/wolfspdm/spdm.h index 4639ba0..c8725b9 100644 --- a/wolfspdm/spdm.h +++ b/wolfspdm/spdm.h @@ -22,6 +22,12 @@ #ifndef WOLFSPDM_SPDM_H #define WOLFSPDM_SPDM_H +/* Include build options (WOLFSPDM_DYNAMIC_MEMORY, WOLFSPDM_NUVOTON, etc.) + * Generated from config.h during build; installed alongside this header. */ +#ifndef HAVE_CONFIG_H + #include +#endif + #include #include @@ -68,16 +74,36 @@ typedef enum { * - No external dependencies beyond wolfCrypt * * Typical Usage: - * 1. Create context: ctx = wolfSPDM_New() - * 2. Set I/O callback: wolfSPDM_SetIO(ctx, callback, userPtr) - * 3. Initialize: wolfSPDM_Init(ctx) - * 4. Connect: wolfSPDM_Connect(ctx) - * 5. Send/Receive: wolfSPDM_SecuredExchange(ctx, ...) - * 6. Disconnect: wolfSPDM_Disconnect(ctx) - * 7. Free: wolfSPDM_Free(ctx) + * + * Static (default, zero-malloc): + * WOLFSPDM_CTX ctx; + * wolfSPDM_Init(&ctx); + * wolfSPDM_SetIO(&ctx, callback, userPtr); + * wolfSPDM_Connect(&ctx); + * wolfSPDM_SecuredExchange(&ctx, ...); + * wolfSPDM_Disconnect(&ctx); + * wolfSPDM_Free(&ctx); + * + * Dynamic (opt-in, requires --enable-dynamic-mem): + * ctx = wolfSPDM_New(); // Allocates and fully initializes + * wolfSPDM_SetIO(ctx, callback, userPtr); + * wolfSPDM_Connect(ctx); + * wolfSPDM_SecuredExchange(ctx, ...); + * wolfSPDM_Disconnect(ctx); + * wolfSPDM_Free(ctx); // Frees the allocation + * + * Note: WOLFSPDM_CTX is approximately 12KB. On embedded systems with + * small stacks, declare it as a static global rather than a local variable. * * ========================================================================== */ +/* Compile-time size for static allocation of WOLFSPDM_CTX. + * Use this when you need a buffer large enough to hold WOLFSPDM_CTX + * without access to the struct definition (e.g., in wolfTPM). + * Must be >= sizeof(WOLFSPDM_CTX). Verified at runtime by + * wolfSPDM_InitStatic(). */ +#define WOLFSPDM_CTX_STATIC_SIZE 24576 + /* Forward declaration */ struct WOLFSPDM_CTX; typedef struct WOLFSPDM_CTX WOLFSPDM_CTX; @@ -121,24 +147,34 @@ typedef int (*WOLFSPDM_IO_CB)( * Context Management * ========================================================================== */ -/** - * Allocate and return a new wolfSPDM context. - * - * @return Pointer to new context, or NULL on failure. - */ -WOLFSPDM_CTX* wolfSPDM_New(void); - /** * Initialize a wolfSPDM context for use. + * Zeroes the context and initializes all internal state. + * Works on stack, static, or dynamically-allocated contexts. * Must be called before wolfSPDM_Connect(). * + * Call wolfSPDM_Free() before re-initializing to avoid leaking the RNG. + * * @param ctx The wolfSPDM context. * @return WOLFSPDM_SUCCESS or negative error code. */ int wolfSPDM_Init(WOLFSPDM_CTX* ctx); +#ifdef WOLFSPDM_DYNAMIC_MEMORY +/** + * Allocate and fully initialize a new wolfSPDM context. + * No separate wolfSPDM_Init() call needed. + * Requires --enable-dynamic-mem at configure time. + * + * @return Pointer to new context, or NULL on failure. + */ +WOLFSPDM_CTX* wolfSPDM_New(void); +#endif + /** * Free a wolfSPDM context and all associated resources. + * Safe for both stack-allocated and dynamically-allocated contexts. + * Zeroes all sensitive key material before returning. * * @param ctx The wolfSPDM context to free. */ @@ -153,7 +189,8 @@ void wolfSPDM_Free(WOLFSPDM_CTX* ctx); int wolfSPDM_GetCtxSize(void); /** - * Initialize a statically-allocated context. + * Initialize a statically-allocated context with size check. + * Verifies the buffer is large enough, then calls wolfSPDM_Init(). * * @param ctx Pointer to pre-allocated memory of at least wolfSPDM_GetCtxSize(). * @param size Size of the provided buffer. From 83d6318633b3391b744e3b175e2b4c51721b1885 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 12 Feb 2026 21:31:59 +0000 Subject: [PATCH 2/3] Update CI workflows for dynamic memory testing - Add dynamic-mem matrix to build-test.yml for comprehensive testing. - Update memory-check.yml to include dynamic-mem matrix for valgrind tests. - Ensure all relevant CI configurations are tested with both static and dynamic memory allocation. --- .github/workflows/build-test.yml | 8 +++++--- .github/workflows/memory-check.yml | 9 +++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index fd9611e..80ea218 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -8,7 +8,7 @@ on: jobs: build: - name: ${{ matrix.os }} / debug=${{ matrix.debug }} / nuvoton=${{ matrix.nuvoton }} + name: ${{ matrix.os }} / debug=${{ matrix.debug }} / nuvoton=${{ matrix.nuvoton }} / dynamic-mem=${{ matrix.dynamic-mem }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -16,6 +16,7 @@ jobs: os: [ubuntu-latest, ubuntu-22.04] debug: [yes, no] nuvoton: [yes, no] + dynamic-mem: [yes, no] steps: - uses: actions/checkout@v4 @@ -52,7 +53,8 @@ jobs: run: | ./configure --with-wolfssl=$HOME/wolfssl-install \ ${{ matrix.debug == 'yes' && '--enable-debug' || '' }} \ - ${{ matrix.nuvoton == 'yes' && '--enable-nuvoton' || '' }} + ${{ matrix.nuvoton == 'yes' && '--enable-nuvoton' || '' }} \ + ${{ matrix.dynamic-mem == 'yes' && '--enable-dynamic-mem' || '' }} - name: Build run: make -j$(nproc) @@ -66,7 +68,7 @@ jobs: if: failure() uses: actions/upload-artifact@v4 with: - name: test-logs-${{ matrix.os }}-debug-${{ matrix.debug }}-nuvoton-${{ matrix.nuvoton }} + name: test-logs-${{ matrix.os }}-debug-${{ matrix.debug }}-nuvoton-${{ matrix.nuvoton }}-dynmem-${{ matrix.dynamic-mem }} path: | test/*.log config.log diff --git a/.github/workflows/memory-check.yml b/.github/workflows/memory-check.yml index bc8510a..430453c 100644 --- a/.github/workflows/memory-check.yml +++ b/.github/workflows/memory-check.yml @@ -8,8 +8,12 @@ on: jobs: valgrind: - name: Valgrind + name: Valgrind / dynamic-mem=${{ matrix.dynamic-mem }} runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + dynamic-mem: [yes, no] steps: - uses: actions/checkout@v4 @@ -42,7 +46,8 @@ jobs: - name: Build with debug run: | ./autogen.sh - ./configure --with-wolfssl=$HOME/wolfssl-install --enable-debug + ./configure --with-wolfssl=$HOME/wolfssl-install --enable-debug \ + ${{ matrix.dynamic-mem == 'yes' && '--enable-dynamic-mem' || '' }} make -j$(nproc) make -j$(nproc) check TESTS= From 38502d29dcfa97728dd0aefc0f7647ee0655305b Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 12 Feb 2026 21:58:05 +0000 Subject: [PATCH 3/3] Refactor: Reduce WOLFSPDM_CTX_STATIC_SIZE and improve comments --- wolfspdm/spdm.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/wolfspdm/spdm.h b/wolfspdm/spdm.h index c8725b9..85f3dc8 100644 --- a/wolfspdm/spdm.h +++ b/wolfspdm/spdm.h @@ -92,7 +92,7 @@ typedef enum { * wolfSPDM_Disconnect(ctx); * wolfSPDM_Free(ctx); // Frees the allocation * - * Note: WOLFSPDM_CTX is approximately 12KB. On embedded systems with + * Note: WOLFSPDM_CTX is approximately 22KB. On embedded systems with * small stacks, declare it as a static global rather than a local variable. * * ========================================================================== */ @@ -100,9 +100,11 @@ typedef enum { /* Compile-time size for static allocation of WOLFSPDM_CTX. * Use this when you need a buffer large enough to hold WOLFSPDM_CTX * without access to the struct definition (e.g., in wolfTPM). - * Must be >= sizeof(WOLFSPDM_CTX). Verified at runtime by - * wolfSPDM_InitStatic(). */ -#define WOLFSPDM_CTX_STATIC_SIZE 24576 + * Actual struct size: ~21.2 KB (base) / ~21.3 KB (with Nuvoton). + * Rounded up to 22 KB for platform alignment and minor future growth. + * wolfSPDM_InitStatic() verifies at runtime that the provided buffer + * is large enough; returns WOLFSPDM_E_BUFFER_SMALL if not. */ +#define WOLFSPDM_CTX_STATIC_SIZE 22528 /* Forward declaration */ struct WOLFSPDM_CTX;