From 6d38ef550b02dd8a7337829217ea609fe713dc86 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 17 Feb 2026 00:47:51 +0000 Subject: [PATCH 1/8] Add measurements, challenge, heartbeat, key update, and cert chain validation Implement core SPDM 1.2 protocol features for production device attestation: - GET_MEASUREMENTS with signature verification (L1/L2 transcript, DSP0274) - CHALLENGE/CHALLENGE_AUTH sessionless attestation with signature verification - Certificate chain validation against trusted root CAs via wolfSPDM_SetTrustedCAs() - HEARTBEAT/HEARTBEAT_ACK session keep-alive - KEY_UPDATE/KEY_UPDATE_ACK session key rotation (DSP0277 traffic upd label) - Application data send/receive over encrypted sessions - Fix IV XOR position for MCTP to leftmost bytes per DSP0277 All 6 spdm-emu integration tests pass. Bumps WOLFSPDM_CTX_STATIC_SIZE to 32KB. --- README.md | 39 +- src/spdm_context.c | 105 +++++ src/spdm_internal.h | 140 ++++++ src/spdm_kdf.c | 82 ++++ src/spdm_msg.c | 1049 +++++++++++++++++++++++++++++++++++++++-- src/spdm_secured.c | 95 +++- src/spdm_session.c | 371 ++++++++++++++- test/unit_test.c | 798 +++++++++++++++++++++++++++++++ wolfspdm/spdm.h | 163 ++++++- wolfspdm/spdm_error.h | 6 + wolfspdm/spdm_types.h | 46 +- 11 files changed, 2829 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index d508ab9..af7f956 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,14 @@ Lightweight SPDM 1.2+ requester-only stack implementation using wolfSSL/wolfCryp - SPDM 1.2 requester implementation - Algorithm Set B (FIPS 140-3 Level 3): ECDSA/ECDHE P-384, SHA-384, AES-256-GCM, HKDF-SHA384 -- **Zero-malloc by default** — fully static memory, ideal for constrained/embedded environments +- **Zero-malloc by default** — fully static memory (~32 KB context), ideal for constrained/embedded environments - Optional `--enable-dynamic-mem` for heap-allocated contexts (useful for small-stack platforms) +- Session establishment with full key exchange and encrypted messaging +- Device attestation via signed/unsigned measurements (GET_MEASUREMENTS) +- Sessionless attestation via CHALLENGE/CHALLENGE_AUTH with signature verification +- Certificate chain validation against trusted root CAs +- Session keep-alive via HEARTBEAT/HEARTBEAT_ACK +- Session key rotation via KEY_UPDATE/KEY_UPDATE_ACK (DSP0277) - Hardware SPDM via wolfTPM + Nuvoton TPM - Full transcript tracking for TH1/TH2 computation - Compatible with DMTF spdm-emu for interoperability testing @@ -53,7 +59,7 @@ make ### Memory Modes **Static (default):** Zero heap allocation. The caller provides a buffer -(`WOLFSPDM_CTX_STATIC_SIZE` bytes, ~22 KB) and wolfSPDM operates entirely +(`WOLFSPDM_CTX_STATIC_SIZE` bytes, ~32 KB) and wolfSPDM operates entirely within it. This is ideal for embedded and constrained environments where malloc is unavailable or undesirable. @@ -68,7 +74,7 @@ wolfSPDM_Free(ctx); ``` **Dynamic (`--enable-dynamic-mem`):** Context is heap-allocated via -`wolfSPDM_New()`. Useful on platforms with small stacks where a ~22 KB +`wolfSPDM_New()`. Useful on platforms with small stacks where a ~32 KB local variable is impractical. ```c @@ -98,17 +104,15 @@ cd wolfTPM ./configure --enable-spdm --enable-swtpm --with-wolfspdm=path/to/wolfspdm make -# Terminal 1: Start responder with Algorithm Set B -cd spdm-emu -./bin/spdm_responder_emu --ver 1.2 \ - --hash SHA_384 --asym ECDSA_P384 \ - --dhe SECP_384_R1 --aead AES_256_GCM - -# Terminal 2: Run wolfTPM example +# Run emulator tests (starts/stops emulator automatically) cd wolfTPM -./examples/spdm/spdm_demo --emu +./examples/spdm/spdm_test.sh --emu ``` +The test script automatically finds `spdm_responder_emu` in `../spdm-emu/build/bin/`, +starts it for each test, and runs session establishment, signed measurements, +unsigned measurements, challenge authentication, heartbeat, and key update. + ## Testing with Nuvoton NPCT75x ```bash @@ -122,8 +126,8 @@ cd wolfTPM ./configure --enable-spdm --enable-nuvoton --with-wolfspdm=path/to/wolfspdm make -# Run test suite -./examples/spdm/spdm_test.sh +# Run Nuvoton test suite +./examples/spdm/spdm_test.sh --nuvoton ``` ## API Reference @@ -143,6 +147,15 @@ make | `wolfSPDM_EncryptMessage()` | Encrypt outgoing message | | `wolfSPDM_DecryptMessage()` | Decrypt incoming message | | `wolfSPDM_SecuredExchange()` | Combined send/receive | +| `wolfSPDM_SetTrustedCAs()` | Load trusted root CA certificates for chain validation | +| `wolfSPDM_GetMeasurements()` | Retrieve device measurements with optional signature verification | +| `wolfSPDM_GetMeasurementCount()` | Get number of measurement blocks retrieved | +| `wolfSPDM_GetMeasurementBlock()` | Access individual measurement block data | +| `wolfSPDM_Challenge()` | Sessionless device attestation via CHALLENGE/CHALLENGE_AUTH | +| `wolfSPDM_Heartbeat()` | Session keep-alive (HEARTBEAT/HEARTBEAT_ACK) | +| `wolfSPDM_KeyUpdate()` | Rotate session encryption keys (KEY_UPDATE/KEY_UPDATE_ACK) | +| `wolfSPDM_SendData()` | Send application data over established session | +| `wolfSPDM_ReceiveData()` | Receive application data over established session | ## License diff --git a/src/spdm_context.c b/src/spdm_context.c index 68c0f42..fd83733 100644 --- a/src/spdm_context.c +++ b/src/spdm_context.c @@ -102,6 +102,19 @@ void wolfSPDM_Free(WOLFSPDM_CTX* ctx) wc_ecc_free(&ctx->ephemeralKey); } + /* Free responder public key (used for measurement/challenge verification) */ + if (ctx->hasResponderPubKey) { + wc_ecc_free(&ctx->responderPubKey); + } + +#ifndef NO_WOLFSPDM_CHALLENGE + /* Free M1/M2 challenge hash if still initialized */ + if (ctx->m1m2HashInit) { + wc_Sha384Free(&ctx->m1m2Hash); + ctx->m1m2HashInit = 0; + } +#endif + /* Zero entire struct (covers all sensitive key material) */ wc_ForceZero(ctx, sizeof(WOLFSPDM_CTX)); @@ -203,6 +216,24 @@ int wolfSPDM_SetRequesterKeyTPMT(WOLFSPDM_CTX* ctx, } #endif /* WOLFSPDM_NUVOTON */ +int wolfSPDM_SetTrustedCAs(WOLFSPDM_CTX* ctx, const byte* derCerts, + word32 derCertsSz) +{ + if (ctx == NULL || derCerts == NULL || derCertsSz == 0) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (derCertsSz > WOLFSPDM_MAX_CERT_CHAIN) { + return WOLFSPDM_E_BUFFER_SMALL; + } + + XMEMCPY(ctx->trustedCAs, derCerts, derCertsSz); + ctx->trustedCAsSz = derCertsSz; + ctx->hasTrustedCAs = 1; + + return WOLFSPDM_SUCCESS; +} + void wolfSPDM_SetDebug(WOLFSPDM_CTX* ctx, int enable) { if (ctx != NULL) { @@ -339,6 +370,22 @@ static int wolfSPDM_ConnectStandard(WOLFSPDM_CTX* ctx) return rc; } + /* Validate certificate chain if trusted CAs are loaded. + * Public key extraction now happens automatically in GetCertificate. */ + if (ctx->hasTrustedCAs) { + rc = wolfSPDM_ValidateCertChain(ctx); + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, + "Certificate chain validation failed (%d)\n", rc); + ctx->state = WOLFSPDM_STATE_ERROR; + return rc; + } + } + else if (!ctx->hasResponderPubKey) { + wolfSPDM_DebugPrint(ctx, + "Warning: No trusted CAs loaded — chain not validated\n"); + } + /* Step 6: KEY_EXCHANGE / KEY_EXCHANGE_RSP */ wolfSPDM_DebugPrint(ctx, "Step 6: KEY_EXCHANGE\n"); rc = wolfSPDM_KeyExchange(ctx); @@ -482,6 +529,58 @@ void wolfSPDM_DebugHex(WOLFSPDM_CTX* ctx, const char* label, fflush(stdout); } +/* ========================================================================== + * Measurement Accessors + * ========================================================================== */ + +#ifndef NO_WOLFSPDM_MEAS + +int wolfSPDM_GetMeasurementCount(WOLFSPDM_CTX* ctx) +{ + if (ctx == NULL || !ctx->hasMeasurements) { + return 0; + } + return (int)ctx->measBlockCount; +} + +int wolfSPDM_GetMeasurementBlock(WOLFSPDM_CTX* ctx, int blockIdx, + byte* measIndex, byte* measType, byte* value, word32* valueSz) +{ + WOLFSPDM_MEAS_BLOCK* blk; + + if (ctx == NULL || !ctx->hasMeasurements) { + return WOLFSPDM_E_INVALID_ARG; + } + if (blockIdx < 0 || blockIdx >= (int)ctx->measBlockCount) { + return WOLFSPDM_E_INVALID_ARG; + } + if (valueSz == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + blk = &ctx->measBlocks[blockIdx]; + + if (measIndex != NULL) { + *measIndex = blk->index; + } + if (measType != NULL) { + *measType = blk->dmtfType; + } + + if (value != NULL) { + word32 copySize = blk->valueSize; + if (copySize > *valueSz) { + copySize = *valueSz; + } + XMEMCPY(value, blk->value, copySize); + } + *valueSz = blk->valueSize; + + return WOLFSPDM_SUCCESS; +} + +#endif /* !NO_WOLFSPDM_MEAS */ + /* ========================================================================== * Error String * ========================================================================== */ @@ -510,6 +609,12 @@ const char* wolfSPDM_GetErrorString(int error) case WOLFSPDM_E_ALGO_MISMATCH: return "Algorithm mismatch"; case WOLFSPDM_E_SESSION_INVALID: return "Invalid session"; case WOLFSPDM_E_KEY_EXCHANGE: return "Key exchange failed"; + case WOLFSPDM_E_MEASUREMENT: return "Measurement retrieval failed"; + case WOLFSPDM_E_MEAS_NOT_VERIFIED: return "Measurements not signature-verified"; + case WOLFSPDM_E_MEAS_SIG_FAIL: return "Measurement signature verification failed"; + case WOLFSPDM_E_CERT_PARSE: return "Failed to parse responder certificate"; + case WOLFSPDM_E_CHALLENGE: return "Challenge authentication failed"; + case WOLFSPDM_E_KEY_UPDATE: return "Key update failed"; default: return "Unknown error"; } } diff --git a/src/spdm_internal.h b/src/spdm_internal.h index b3d880a..633037f 100644 --- a/src/spdm_internal.h +++ b/src/spdm_internal.h @@ -45,6 +45,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -64,6 +65,23 @@ extern "C" { #define WOLFSPDM_STATE_FINISH 7 /* FINISH complete */ #define WOLFSPDM_STATE_CONNECTED 8 /* Session established */ #define WOLFSPDM_STATE_ERROR 9 /* Error state */ +#ifndef NO_WOLFSPDM_MEAS +#define WOLFSPDM_STATE_MEASURED 10 /* Measurements retrieved */ +#endif + +/* ========================================================================== + * Measurement Block Structure + * ========================================================================== */ + +#ifndef NO_WOLFSPDM_MEAS +typedef struct WOLFSPDM_MEAS_BLOCK { + byte index; /* SPDM measurement index (1-based) */ + byte measurementSpec; /* Measurement specification (1=DMTF) */ + byte dmtfType; /* DMTFSpecMeasurementValueType */ + word16 valueSize; /* Actual value size in bytes */ + byte value[WOLFSPDM_MAX_MEAS_VALUE_SIZE]; /* Measurement value (digest/raw) */ +} WOLFSPDM_MEAS_BLOCK; +#endif /* !NO_WOLFSPDM_MEAS */ /* ========================================================================== * Internal Context Structure @@ -117,6 +135,7 @@ struct WOLFSPDM_CTX { /* Transcript hash for TH1/TH2 computation */ byte transcript[WOLFSPDM_MAX_TRANSCRIPT]; word32 transcriptLen; + word32 vcaLen; /* VCA transcript size (after ALGORITHMS, used by measurement sig) */ /* Certificate chain buffer for Ct computation */ byte certChain[WOLFSPDM_MAX_CERT_CHAIN]; @@ -164,6 +183,52 @@ struct WOLFSPDM_CTX { /* Message buffers */ byte sendBuf[WOLFSPDM_MAX_MSG_SIZE + WOLFSPDM_AEAD_TAG_SIZE]; byte recvBuf[WOLFSPDM_MAX_MSG_SIZE + WOLFSPDM_AEAD_TAG_SIZE]; + +#ifndef NO_WOLFSPDM_MEAS + /* Measurement data */ + WOLFSPDM_MEAS_BLOCK measBlocks[WOLFSPDM_MAX_MEAS_BLOCKS]; + word32 measBlockCount; + byte measNonce[32]; /* Nonce for signed measurements */ + byte measSummaryHash[WOLFSPDM_HASH_SIZE]; /* Summary hash from response */ + byte measSignature[WOLFSPDM_ECC_SIG_SIZE]; /* Captured signature (96 bytes P-384) */ + word32 measSignatureSize; /* 0 if unsigned, 96 if signed */ + int hasMeasurements; + +#ifndef NO_WOLFSPDM_MEAS_VERIFY + /* Saved GET_MEASUREMENTS request for L1/L2 transcript */ + byte measReqMsg[48]; /* Saved request (max 37 bytes) */ + word32 measReqMsgSz; +#endif /* !NO_WOLFSPDM_MEAS_VERIFY */ +#endif /* !NO_WOLFSPDM_MEAS */ + + /* Responder identity for signature verification (measurements + challenge) */ + ecc_key responderPubKey; /* Extracted from cert chain leaf */ + int hasResponderPubKey; /* 1 if key extracted successfully */ + + /* Certificate chain validation */ + byte trustedCAs[WOLFSPDM_MAX_CERT_CHAIN]; /* DER-encoded root CAs */ + word32 trustedCAsSz; + int hasTrustedCAs; /* 1 if CAs loaded */ + +#ifndef NO_WOLFSPDM_CHALLENGE + /* Challenge authentication */ + byte challengeNonce[32]; /* Saved nonce from CHALLENGE request */ + byte challengeMeasHashType; /* MeasurementSummaryHashType from req */ + + /* Running M1/M2 hash for CHALLENGE_AUTH signature verification. + * Per DSP0274, M1/M2 = A || B || C where: + * A = VCA (GET_VERSION..ALGORITHMS) + * B = GET_DIGESTS + DIGESTS + GET_CERTIFICATE + CERTIFICATE (all chunks) + * C = CHALLENGE + CHALLENGE_AUTH (before sig) + * This hash accumulates A+B during NegAlgo/GetDigests/GetCertificate, + * then C is added in VerifyChallengeAuthSig. */ + wc_Sha384 m1m2Hash; + int m1m2HashInit; /* 1 if m1m2Hash is initialized */ +#endif + + /* Key update state — app secrets for re-derivation */ + byte reqAppSecret[WOLFSPDM_HASH_SIZE]; /* 48 bytes */ + byte rspAppSecret[WOLFSPDM_HASH_SIZE]; /* 48 bytes */ }; /* ========================================================================== @@ -315,6 +380,81 @@ void wolfSPDM_DebugPrint(WOLFSPDM_CTX* ctx, const char* fmt, ...); void wolfSPDM_DebugHex(WOLFSPDM_CTX* ctx, const char* label, const byte* data, word32 len); +/* ========================================================================== + * Internal Function Declarations - Measurements + * ========================================================================== */ + +#ifndef NO_WOLFSPDM_MEAS +/* Build GET_MEASUREMENTS request */ +int wolfSPDM_BuildGetMeasurements(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, + byte operation, byte requestSig); + +/* Parse MEASUREMENTS response */ +int wolfSPDM_ParseMeasurements(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz); + +#ifndef NO_WOLFSPDM_MEAS_VERIFY +/* Verify measurement signature (L1/L2 transcript) */ +int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, + const byte* rspBuf, word32 rspBufSz, + const byte* reqMsg, word32 reqMsgSz); +#endif /* !NO_WOLFSPDM_MEAS_VERIFY */ +#endif /* !NO_WOLFSPDM_MEAS */ + +/* ========================================================================== + * Internal Function Declarations - Certificate Chain Validation + * ========================================================================== */ + +/* Extract responder's public key from certificate chain leaf cert */ +int wolfSPDM_ExtractResponderPubKey(WOLFSPDM_CTX* ctx); + +/* Validate certificate chain using trusted CAs and extract public key */ +int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx); + +/* ========================================================================== + * Internal Function Declarations - Challenge + * ========================================================================== */ + +#ifndef NO_WOLFSPDM_CHALLENGE +/* Build CHALLENGE request */ +int wolfSPDM_BuildChallenge(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, + int slotId, byte measHashType); + +/* Parse CHALLENGE_AUTH response */ +int wolfSPDM_ParseChallengeAuth(WOLFSPDM_CTX* ctx, const byte* buf, + word32 bufSz, word32* sigOffset); + +/* Verify CHALLENGE_AUTH signature */ +int wolfSPDM_VerifyChallengeAuthSig(WOLFSPDM_CTX* ctx, + const byte* rspBuf, word32 rspBufSz, + const byte* reqMsg, word32 reqMsgSz, word32 sigOffset); +#endif /* !NO_WOLFSPDM_CHALLENGE */ + +/* ========================================================================== + * Internal Function Declarations - Heartbeat + * ========================================================================== */ + +/* Build HEARTBEAT request */ +int wolfSPDM_BuildHeartbeat(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz); + +/* Parse HEARTBEAT_ACK response */ +int wolfSPDM_ParseHeartbeatAck(WOLFSPDM_CTX* ctx, const byte* buf, + word32 bufSz); + +/* ========================================================================== + * Internal Function Declarations - Key Update + * ========================================================================== */ + +/* Build KEY_UPDATE request */ +int wolfSPDM_BuildKeyUpdate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, + byte operation, byte* tag); + +/* Parse KEY_UPDATE_ACK response */ +int wolfSPDM_ParseKeyUpdateAck(WOLFSPDM_CTX* ctx, const byte* buf, + word32 bufSz, byte operation, byte tag); + +/* Derive updated keys from saved app secrets */ +int wolfSPDM_DeriveUpdatedKeys(WOLFSPDM_CTX* ctx, int updateAll); + #ifdef __cplusplus } #endif diff --git a/src/spdm_kdf.c b/src/spdm_kdf.c index d9d0256..3399f66 100644 --- a/src/spdm_kdf.c +++ b/src/spdm_kdf.c @@ -242,6 +242,10 @@ int wolfSPDM_DeriveAppDataKeys(WOLFSPDM_CTX* ctx) return rc; } + /* Save app secrets for KEY_UPDATE re-derivation */ + XMEMCPY(ctx->reqAppSecret, reqAppSecret, WOLFSPDM_HASH_SIZE); + XMEMCPY(ctx->rspAppSecret, rspAppSecret, WOLFSPDM_HASH_SIZE); + /* Derive new encryption keys from app data secrets */ rc = wolfSPDM_HkdfExpandLabel(ctx->spdmVersion, reqAppSecret, WOLFSPDM_HASH_SIZE, SPDM_LABEL_KEY, NULL, 0, @@ -279,3 +283,81 @@ int wolfSPDM_DeriveAppDataKeys(WOLFSPDM_CTX* ctx) return WOLFSPDM_SUCCESS; } + +/* ========================================================================== + * Key Update Re-derivation (DSP0277) + * + * Per DSP0277, KEY_UPDATE re-derives keys from saved app secrets: + * newAppSecret = HKDF-Expand(oldAppSecret, "update" || version_byte, 48) + * newKey = HKDF-Expand(newAppSecret, "key", 32) + * newIv = HKDF-Expand(newAppSecret, "iv", 12) + * ========================================================================== */ + +int wolfSPDM_DeriveUpdatedKeys(WOLFSPDM_CTX* ctx, int updateAll) +{ + byte newReqAppSecret[WOLFSPDM_HASH_SIZE]; + byte newRspAppSecret[WOLFSPDM_HASH_SIZE]; + int rc; + + if (ctx == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + /* Per DSP0277: KEY_UPDATE uses "traffic upd" label with NO context. + * info = outLen(2 LE) || "spdm1.2 " || "traffic upd" */ + + /* Always update requester key */ + rc = wolfSPDM_HkdfExpandLabel(ctx->spdmVersion, ctx->reqAppSecret, + WOLFSPDM_HASH_SIZE, SPDM_LABEL_UPDATE, NULL, 0, + newReqAppSecret, WOLFSPDM_HASH_SIZE); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + rc = wolfSPDM_HkdfExpandLabel(ctx->spdmVersion, newReqAppSecret, + WOLFSPDM_HASH_SIZE, SPDM_LABEL_KEY, NULL, 0, + ctx->reqDataKey, WOLFSPDM_AEAD_KEY_SIZE); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + rc = wolfSPDM_HkdfExpandLabel(ctx->spdmVersion, newReqAppSecret, + WOLFSPDM_HASH_SIZE, SPDM_LABEL_IV, NULL, 0, + ctx->reqDataIv, WOLFSPDM_AEAD_IV_SIZE); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + /* Save new requester secret for future updates */ + XMEMCPY(ctx->reqAppSecret, newReqAppSecret, WOLFSPDM_HASH_SIZE); + + /* Optionally update responder key */ + if (updateAll) { + rc = wolfSPDM_HkdfExpandLabel(ctx->spdmVersion, ctx->rspAppSecret, + WOLFSPDM_HASH_SIZE, SPDM_LABEL_UPDATE, NULL, 0, + newRspAppSecret, WOLFSPDM_HASH_SIZE); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + rc = wolfSPDM_HkdfExpandLabel(ctx->spdmVersion, newRspAppSecret, + WOLFSPDM_HASH_SIZE, SPDM_LABEL_KEY, NULL, 0, + ctx->rspDataKey, WOLFSPDM_AEAD_KEY_SIZE); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + rc = wolfSPDM_HkdfExpandLabel(ctx->spdmVersion, newRspAppSecret, + WOLFSPDM_HASH_SIZE, SPDM_LABEL_IV, NULL, 0, + ctx->rspDataIv, WOLFSPDM_AEAD_IV_SIZE); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + /* Save new responder secret for future updates */ + XMEMCPY(ctx->rspAppSecret, newRspAppSecret, WOLFSPDM_HASH_SIZE); + + } + + return WOLFSPDM_SUCCESS; +} diff --git a/src/spdm_msg.c b/src/spdm_msg.c index 9e31585..5506c00 100644 --- a/src/spdm_msg.c +++ b/src/spdm_msg.c @@ -21,6 +21,7 @@ #include "spdm_internal.h" #include +#include int wolfSPDM_BuildGetVersion(byte* buf, word32* bufSz) { @@ -260,16 +261,10 @@ int wolfSPDM_BuildFinish(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) if (mutualAuth) { buf[2] = 0x01; /* Param1: Signature field is included */ buf[3] = 0xFF; /* Param2: 0xFF = requester public key provisioned in trusted environment (GIVE_PUB_KEY) */ - wolfSPDM_DebugPrint(ctx, "FINISH: mutual auth with signature\n"); - wolfSPDM_DebugPrint(ctx, " Header: version=0x%02x code=0x%02x param1=0x%02x param2=0x%02x\n", - buf[0], buf[1], buf[2], buf[3]); } else { buf[2] = 0x00; /* Param1: No signature */ buf[3] = 0x00; /* Param2: SlotID = 0 when no signature */ - wolfSPDM_DebugPrint(ctx, "FINISH: no mutual auth\n"); - wolfSPDM_DebugPrint(ctx, " Header: version=0x%02x code=0x%02x param1=0x%02x param2=0x%02x\n", - buf[0], buf[1], buf[2], buf[3]); } /* Per DSP0274 / libspdm: When mutual auth is requested, the transcript @@ -434,8 +429,6 @@ int wolfSPDM_BuildFinish(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) } *bufSz = offset; - wolfSPDM_DebugPrint(ctx, "FINISH message size: %u bytes\n", *bufSz); - return WOLFSPDM_SUCCESS; } @@ -503,7 +496,6 @@ int wolfSPDM_ParseVersion(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) */ for (i = 0; i < entryCount && (6 + i * 2 + 1) < bufSz; i++) { byte ver = buf[6 + i * 2 + 1]; /* Major.Minor in high byte */ - wolfSPDM_DebugPrint(ctx, "VERSION entry %u: 0x%02x\n", i, ver); /* Cap at 1.3 (0x13) - SPDM 1.4 FINISH handling needs work */ if (ver > highestVersion && ver <= SPDM_VERSION_13) { highestVersion = ver; @@ -619,12 +611,6 @@ int wolfSPDM_ParseKeyExchangeRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufS int rc; if (ctx == NULL || buf == NULL || bufSz < 140) { - wolfSPDM_DebugPrint(ctx, "ParseKeyExchangeRsp: INVALID - ctx=%p buf=%p bufSz=%u (need 140)\n", - (void*)ctx, (void*)buf, bufSz); - if (buf != NULL && bufSz >= 4) { - wolfSPDM_DebugPrint(ctx, "Response bytes: %02x %02x %02x %02x (code=%02x, err=%02x)\n", - buf[0], buf[1], buf[2], buf[3], buf[1], buf[2]); - } return WOLFSPDM_E_INVALID_ARG; } @@ -643,11 +629,6 @@ int wolfSPDM_ParseKeyExchangeRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufS ctx->mutAuthRequested = buf[6]; ctx->reqSlotId = buf[7]; - wolfSPDM_DebugPrint(ctx, "RspSessionID: 0x%04x, SessionID: 0x%08x\n", - ctx->rspSessionId, ctx->sessionId); - wolfSPDM_DebugPrint(ctx, "MutAuthRequested: 0x%02x, ReqSlotIDParam: 0x%02x\n", - ctx->mutAuthRequested, ctx->reqSlotId); - /* Extract responder's ephemeral public key (offset 40 = 4+2+1+1+32) */ XMEMCPY(peerPubKeyX, &buf[40], WOLFSPDM_ECC_KEY_SIZE); XMEMCPY(peerPubKeyY, &buf[88], WOLFSPDM_ECC_KEY_SIZE); @@ -657,16 +638,9 @@ int wolfSPDM_ParseKeyExchangeRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufS sigOffset = 138 + opaqueLen; keRspPartialLen = sigOffset; - wolfSPDM_DebugPrint(ctx, "KEY_EXCHANGE_RSP parse: bufSz=%u, opaqueLen=%u, sigOffset=%u\n", - bufSz, opaqueLen, sigOffset); - wolfSPDM_DebugPrint(ctx, " Need: sigOffset(%u) + sig(%u) + hash(%u) = %u bytes\n", - sigOffset, WOLFSPDM_ECC_SIG_SIZE, WOLFSPDM_HASH_SIZE, - sigOffset + WOLFSPDM_ECC_SIG_SIZE + WOLFSPDM_HASH_SIZE); (void)opaqueLen; if (bufSz < sigOffset + WOLFSPDM_ECC_SIG_SIZE + WOLFSPDM_HASH_SIZE) { - wolfSPDM_DebugPrint(ctx, " BUFFER_SMALL: have %u, need %u\n", - bufSz, sigOffset + WOLFSPDM_ECC_SIG_SIZE + WOLFSPDM_HASH_SIZE); return WOLFSPDM_E_BUFFER_SMALL; } @@ -749,3 +723,1024 @@ int wolfSPDM_ParseFinishRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) return WOLFSPDM_E_BAD_STATE; } + +/* ========================================================================== + * Measurement Message Building and Parsing + * ========================================================================== */ + +#ifndef NO_WOLFSPDM_MEAS + +int wolfSPDM_BuildGetMeasurements(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, + byte operation, byte requestSig) +{ + word32 offset = 0; + int rc; + + if (ctx == NULL || buf == NULL || bufSz == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + /* Size: 4 header + (requestSig ? 32 nonce + 1 slotId : 0) */ + if (requestSig && *bufSz < 37) { + return WOLFSPDM_E_BUFFER_SMALL; + } + if (!requestSig && *bufSz < 4) { + return WOLFSPDM_E_BUFFER_SMALL; + } + + buf[offset++] = ctx->spdmVersion; + buf[offset++] = SPDM_GET_MEASUREMENTS; + /* Param1: bits [7:1] = MeasurementSummaryHashType, bit 0 = signature requested */ + buf[offset++] = requestSig ? SPDM_MEAS_REQUEST_SIG_BIT : 0x00; + /* Param2: MeasurementOperation */ + buf[offset++] = operation; + + if (requestSig) { + /* Nonce (32 bytes) */ + rc = wolfSPDM_GetRandom(ctx, &buf[offset], 32); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + XMEMCPY(ctx->measNonce, &buf[offset], 32); + offset += 32; + + /* SlotIDParam (1 byte) — slot 0 */ + buf[offset++] = 0x00; + } + + *bufSz = offset; + return WOLFSPDM_SUCCESS; +} + +int wolfSPDM_ParseMeasurements(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) +{ + word32 offset; + byte numBlocks; + word32 recordLen; + word32 recordEnd; + word32 blockIdx; + word16 opaqueLen; + + if (ctx == NULL || buf == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + /* Minimum: 4 hdr + 1 numBlocks + 3 recordLen = 8 */ + if (bufSz < 8) { + return WOLFSPDM_E_MEASUREMENT; + } + + /* Check response code */ + if (buf[1] != SPDM_MEASUREMENTS) { + int errCode; + if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { + wolfSPDM_DebugPrint(ctx, "MEASUREMENTS error: 0x%02x\n", errCode); + return WOLFSPDM_E_PEER_ERROR; + } + return WOLFSPDM_E_MEASUREMENT; + } + + numBlocks = buf[4]; + /* MeasurementRecordLength: 3 bytes LE at offset 5..7 */ + recordLen = (word32)buf[5] | ((word32)buf[6] << 8) | ((word32)buf[7] << 16); + + wolfSPDM_DebugPrint(ctx, "MEASUREMENTS: numBlocks=%u, recordLen=%u\n", + numBlocks, recordLen); + + /* Validate record fits in buffer */ + if (8 + recordLen > bufSz) { + wolfSPDM_DebugPrint(ctx, "MEASUREMENTS: recordLen %u exceeds bufSz %u\n", + recordLen, bufSz); + return WOLFSPDM_E_MEASUREMENT; + } + + recordEnd = 8 + recordLen; + offset = 8; /* Start of measurement record */ + ctx->measBlockCount = 0; + + /* Parse each measurement block */ + for (blockIdx = 0; blockIdx < numBlocks; blockIdx++) { + word16 measSize; + WOLFSPDM_MEAS_BLOCK* blk; + + /* Check block header fits */ + if (offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE > recordEnd) { + wolfSPDM_DebugPrint(ctx, "MEASUREMENTS: block %u header truncated\n", + blockIdx); + return WOLFSPDM_E_MEASUREMENT; + } + + /* Read block header: Index(1) + MeasSpec(1) + MeasSize(2 LE) */ + measSize = (word16)(buf[offset + 2] | (buf[offset + 3] << 8)); + + /* Check block data fits */ + if (offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE + measSize > recordEnd) { + wolfSPDM_DebugPrint(ctx, "MEASUREMENTS: block %u data truncated\n", + blockIdx); + return WOLFSPDM_E_MEASUREMENT; + } + + /* Store if we have room */ + if (ctx->measBlockCount < WOLFSPDM_MAX_MEAS_BLOCKS) { + blk = &ctx->measBlocks[ctx->measBlockCount]; + blk->index = buf[offset]; + blk->measurementSpec = buf[offset + 1]; + + /* Parse DMTF measurement value if MeasSpec==1 and size >= 3 */ + if (blk->measurementSpec == 0x01 && measSize >= 3) { + word16 valueSize; + word16 copySize; + + blk->dmtfType = buf[offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE]; + valueSize = (word16)( + buf[offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE + 1] | + (buf[offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE + 2] << 8)); + + /* Validate valueSize against measSize */ + if (valueSize > measSize - 3) { + wolfSPDM_DebugPrint(ctx, + "MEASUREMENTS: block %u valueSize %u > measSize-3 %u\n", + blockIdx, valueSize, measSize - 3); + return WOLFSPDM_E_MEASUREMENT; + } + + /* Truncate if value exceeds our buffer */ + copySize = valueSize; + if (copySize > WOLFSPDM_MAX_MEAS_VALUE_SIZE) { + copySize = WOLFSPDM_MAX_MEAS_VALUE_SIZE; + } + blk->valueSize = copySize; + XMEMCPY(blk->value, + &buf[offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE + 3], copySize); + } + else { + /* Non-DMTF or too small: store raw */ + word16 copySize = measSize; + blk->dmtfType = 0; + if (copySize > WOLFSPDM_MAX_MEAS_VALUE_SIZE) { + copySize = WOLFSPDM_MAX_MEAS_VALUE_SIZE; + } + blk->valueSize = copySize; + if (copySize > 0) { + XMEMCPY(blk->value, + &buf[offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE], copySize); + } + } + + ctx->measBlockCount++; + } + else { + wolfSPDM_DebugPrint(ctx, + "MEASUREMENTS: block %u exceeds MAX_MEAS_BLOCKS (%u), skipping\n", + blockIdx, WOLFSPDM_MAX_MEAS_BLOCKS); + } + + offset += WOLFSPDM_MEAS_BLOCK_HDR_SIZE + measSize; + } + + /* After measurement record: Nonce(32) + OpaqueDataLength(2) + OpaqueData + Signature */ + /* Nonce is present only if signature was requested */ + ctx->measSignatureSize = 0; + + if (offset + 32 + 2 <= bufSz) { + /* Nonce (32 bytes) — skip, we already have our own in ctx->measNonce */ + offset += 32; + + /* OpaqueDataLength (2 LE) */ + opaqueLen = (word16)(buf[offset] | (buf[offset + 1] << 8)); + offset += 2; + + /* Skip opaque data */ + if (offset + opaqueLen > bufSz) { + wolfSPDM_DebugPrint(ctx, "MEASUREMENTS: opaque data truncated\n"); + return WOLFSPDM_E_MEASUREMENT; + } + offset += opaqueLen; + + /* Signature (if present) */ + if (offset + WOLFSPDM_ECC_SIG_SIZE <= bufSz) { + XMEMCPY(ctx->measSignature, &buf[offset], WOLFSPDM_ECC_SIG_SIZE); + ctx->measSignatureSize = WOLFSPDM_ECC_SIG_SIZE; + } + } + + ctx->hasMeasurements = 1; + wolfSPDM_DebugPrint(ctx, "MEASUREMENTS: parsed %u blocks\n", + ctx->measBlockCount); + + return WOLFSPDM_SUCCESS; +} + +#ifndef NO_WOLFSPDM_MEAS_VERIFY + +int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, + const byte* rspBuf, word32 rspBufSz, + const byte* reqMsg, word32 reqMsgSz) +{ + wc_Sha384 sha; + byte digest[WOLFSPDM_HASH_SIZE]; + byte derSig[256]; + word32 derSigSz = sizeof(derSig); + word32 sigOffset; + const byte* sigR; + const byte* sigS; + int verified = 0; + int rc; + + if (ctx == NULL || rspBuf == NULL || reqMsg == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (!ctx->hasResponderPubKey) { + return WOLFSPDM_E_MEAS_NOT_VERIFIED; + } + + /* Signature is the last WOLFSPDM_ECC_SIG_SIZE bytes of the response */ + if (rspBufSz < WOLFSPDM_ECC_SIG_SIZE) { + return WOLFSPDM_E_MEASUREMENT; + } + sigOffset = rspBufSz - WOLFSPDM_ECC_SIG_SIZE; + + /* Build SPDM 1.2 signing context: + * M = combined_spdm_prefix || zero_pad || signing_context || Hash(L1||L2) + * + * Per DSP0274: + * - combined_spdm_prefix = "dmtf-spdm-v1.X.*" x4 = 64 bytes + * - zero_pad = 36 - strlen("responder-measurements signing") = 5 bytes + * - signing_context = "responder-measurements signing" (31 bytes) + * - Hash(L1||L2) = SHA-384(reqMsg || rspBuf[0..sigOffset-1]) = 48 bytes + * + * Total M = 64 + 5 + 31 + 48 = 148 bytes + * Then sign Hash(M) */ + + /* Compute L1||L2 hash per DSP0274 Section 10.11.1: + * L1/L2 = VCA || GET_MEASUREMENTS_request || MEASUREMENTS_response(before sig) */ + rc = wc_InitSha384(&sha); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + + /* VCA = GET_VERSION || VERSION || GET_CAPS || CAPS || NEG_ALGO || ALGO */ + if (ctx->vcaLen > 0) { + rc = wc_Sha384Update(&sha, ctx->transcript, ctx->vcaLen); + if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } + } + + /* GET_MEASUREMENTS request */ + rc = wc_Sha384Update(&sha, reqMsg, reqMsgSz); + if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } + + /* MEASUREMENTS response (everything before signature) */ + rc = wc_Sha384Update(&sha, rspBuf, sigOffset); + if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } + + rc = wc_Sha384Final(&sha, digest); + wc_Sha384Free(&sha); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + + /* Build M = prefix || zero_pad || context_str || L1L2_hash, then hash it */ + { + static const char context_str[] = "responder-measurements signing"; + #define MEAS_SIGNING_PREFIX_SIZE 16 + #define MEAS_SIGNING_CONTEXT_STR_SIZE 30 /* strlen, no null terminator */ + #define MEAS_SIGNING_ZERO_PAD_SIZE (36 - MEAS_SIGNING_CONTEXT_STR_SIZE) /* 6 */ + byte signMsg[200]; /* 64 + 5 + 31 + 48 = 148 bytes */ + word32 signMsgLen = 0; + wc_Sha384 sha2; + byte majorVer, minorVer; + int i; + + majorVer = (byte)('0' + ((ctx->spdmVersion >> 4) & 0xF)); + minorVer = (byte)('0' + (ctx->spdmVersion & 0xF)); + + /* combined_spdm_prefix: "dmtf-spdm-v1.X.*" x4 = 64 bytes */ + for (i = 0; i < 4; i++) { + XMEMCPY(&signMsg[signMsgLen], "dmtf-spdm-v1.2.*", + MEAS_SIGNING_PREFIX_SIZE); + signMsg[signMsgLen + 11] = majorVer; + signMsg[signMsgLen + 13] = minorVer; + signMsg[signMsgLen + 15] = '*'; + signMsgLen += MEAS_SIGNING_PREFIX_SIZE; + } + + /* Zero padding */ + XMEMSET(&signMsg[signMsgLen], 0x00, MEAS_SIGNING_ZERO_PAD_SIZE); + signMsgLen += MEAS_SIGNING_ZERO_PAD_SIZE; + + /* Signing context string */ + XMEMCPY(&signMsg[signMsgLen], context_str, MEAS_SIGNING_CONTEXT_STR_SIZE); + signMsgLen += MEAS_SIGNING_CONTEXT_STR_SIZE; + + /* L1||L2 hash */ + XMEMCPY(&signMsg[signMsgLen], digest, WOLFSPDM_HASH_SIZE); + signMsgLen += WOLFSPDM_HASH_SIZE; + + /* Hash M to get the value that was signed */ + rc = wc_InitSha384(&sha2); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + rc = wc_Sha384Update(&sha2, signMsg, signMsgLen); + if (rc != 0) { wc_Sha384Free(&sha2); return WOLFSPDM_E_CRYPTO_FAIL; } + rc = wc_Sha384Final(&sha2, digest); + wc_Sha384Free(&sha2); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + } + + /* SPDM ECDSA signatures are raw r||s, not DER. + * For P-384: r(48 bytes) || s(48 bytes) = 96 bytes */ + sigR = rspBuf + sigOffset; + sigS = sigR + (WOLFSPDM_ECC_SIG_SIZE / 2); + + rc = wc_ecc_rs_raw_to_sig(sigR, WOLFSPDM_ECC_SIG_SIZE / 2, + sigS, WOLFSPDM_ECC_SIG_SIZE / 2, derSig, &derSigSz); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "ECC rs_raw_to_sig failed: %d\n", rc); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + /* Verify the signature */ + rc = wc_ecc_verify_hash(derSig, derSigSz, digest, WOLFSPDM_HASH_SIZE, + &verified, &ctx->responderPubKey); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "ECC verify_hash failed: %d\n", rc); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + if (verified == 1) { + wolfSPDM_DebugPrint(ctx, "Measurement signature VERIFIED\n"); + return WOLFSPDM_SUCCESS; + } + + wolfSPDM_DebugPrint(ctx, "Measurement signature INVALID\n"); + return WOLFSPDM_E_MEAS_SIG_FAIL; +} + +#endif /* !NO_WOLFSPDM_MEAS_VERIFY */ +#endif /* !NO_WOLFSPDM_MEAS */ + +/* ========================================================================== + * Responder Public Key Extraction + * ========================================================================== + * Extract responder's ECC P-384 public key from the leaf certificate in the + * SPDM certificate chain. Used by both measurement signature verification + * and CHALLENGE authentication, so it lives outside measurement guards. */ + +int wolfSPDM_ExtractResponderPubKey(WOLFSPDM_CTX* ctx) +{ + DecodedCert cert; + const byte* certDer; + word32 certDerSz; + word32 idx; + int rc; + + if (ctx == NULL || ctx->certChainLen == 0) { + return WOLFSPDM_E_CERT_PARSE; + } + + /* SPDM certificate chain format (DSP0274): + * Length(2 LE) + Reserved(2) + RootCertHash(48 for SHA-384) + Certificates... + * The leaf certificate is the LAST one in the chain. */ + + /* Skip SPDM cert chain header: Length(2) + Reserved(2) + RootHash(48) = 52 bytes */ + if (ctx->certChainLen <= 52) { + wolfSPDM_DebugPrint(ctx, "Certificate chain too short for header\n"); + return WOLFSPDM_E_CERT_PARSE; + } + + certDer = ctx->certChain + 52; + certDerSz = ctx->certChainLen - 52; + + /* Walk through certificates to find the leaf (last one). + * Each DER cert starts with 0x30 (SEQUENCE) followed by length. */ + { + word32 pos = 0; + const byte* lastCert = certDer; + word32 lastCertSz = certDerSz; + + while (pos < certDerSz) { + word32 certLen; + word32 hdrLen; + + if (certDer[pos] != 0x30) { + break; /* Not a valid DER sequence */ + } + + /* Parse ASN.1 length */ + if (pos + 1 >= certDerSz) break; + + if (certDer[pos + 1] < 0x80) { + certLen = certDer[pos + 1]; + hdrLen = 2; + } + else if (certDer[pos + 1] == 0x81) { + if (pos + 2 >= certDerSz) break; + certLen = certDer[pos + 2]; + hdrLen = 3; + } + else if (certDer[pos + 1] == 0x82) { + if (pos + 3 >= certDerSz) break; + certLen = ((word32)certDer[pos + 2] << 8) | certDer[pos + 3]; + hdrLen = 4; + } + else if (certDer[pos + 1] == 0x83) { + if (pos + 4 >= certDerSz) break; + certLen = ((word32)certDer[pos + 2] << 16) | + ((word32)certDer[pos + 3] << 8) | certDer[pos + 4]; + hdrLen = 5; + } + else { + break; /* Length encoding we don't handle */ + } + + if (pos + hdrLen + certLen > certDerSz) break; + + lastCert = certDer + pos; + lastCertSz = hdrLen + certLen; + + pos += hdrLen + certLen; + } + + certDer = lastCert; + certDerSz = lastCertSz; + } + + /* Parse the leaf certificate */ + wc_InitDecodedCert(&cert, certDer, certDerSz, NULL); + rc = wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "Certificate parse failed: %d\n", rc); + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CERT_PARSE; + } + + /* Extract public key from cert and import into ecc_key */ + rc = wc_ecc_init(&ctx->responderPubKey); + if (rc != 0) { + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + idx = 0; + rc = wc_EccPublicKeyDecode(cert.publicKey, &idx, &ctx->responderPubKey, + cert.pubKeySize); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "ECC public key decode failed: %d\n", rc); + wc_ecc_free(&ctx->responderPubKey); + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CERT_PARSE; + } + + wc_FreeDecodedCert(&cert); + ctx->hasResponderPubKey = 1; + wolfSPDM_DebugPrint(ctx, "Extracted responder ECC P-384 public key\n"); + + return WOLFSPDM_SUCCESS; +} + +/* ========================================================================== + * Certificate Chain Validation + * ========================================================================== */ + +/* Helper: find leaf cert in SPDM cert chain buffer. + * SPDM cert chain header: Length(2 LE) + Reserved(2) + RootHash(48) = 52 bytes + * After header: concatenated DER certificates, leaf is the last one. */ +static int wolfSPDM_FindLeafCert(const byte* certChain, word32 certChainLen, + const byte** leafCert, word32* leafCertSz) +{ + const byte* certDer; + word32 certDerSz; + word32 pos; + const byte* lastCert; + word32 lastCertSz; + + if (certChainLen <= 52) { + return WOLFSPDM_E_CERT_PARSE; + } + + certDer = certChain + 52; + certDerSz = certChainLen - 52; + lastCert = certDer; + lastCertSz = certDerSz; + pos = 0; + + while (pos < certDerSz) { + word32 certLen; + word32 hdrLen; + + if (certDer[pos] != 0x30) { + break; + } + + if (pos + 1 >= certDerSz) break; + + if (certDer[pos + 1] < 0x80) { + certLen = certDer[pos + 1]; + hdrLen = 2; + } + else if (certDer[pos + 1] == 0x81) { + if (pos + 2 >= certDerSz) break; + certLen = certDer[pos + 2]; + hdrLen = 3; + } + else if (certDer[pos + 1] == 0x82) { + if (pos + 3 >= certDerSz) break; + certLen = ((word32)certDer[pos + 2] << 8) | certDer[pos + 3]; + hdrLen = 4; + } + else if (certDer[pos + 1] == 0x83) { + if (pos + 4 >= certDerSz) break; + certLen = ((word32)certDer[pos + 2] << 16) | + ((word32)certDer[pos + 3] << 8) | certDer[pos + 4]; + hdrLen = 5; + } + else { + break; + } + + if (pos + hdrLen + certLen > certDerSz) break; + + lastCert = certDer + pos; + lastCertSz = hdrLen + certLen; + pos += hdrLen + certLen; + } + + *leafCert = lastCert; + *leafCertSz = lastCertSz; + return WOLFSPDM_SUCCESS; +} + +int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx) +{ + const byte* leafCert; + word32 leafCertSz; + DecodedCert cert; + word32 idx; + wc_Sha384 sha; + byte caHash[WOLFSPDM_HASH_SIZE]; + const byte* chainRootHash; + int rc; + + if (ctx == NULL || ctx->certChainLen == 0) { + return WOLFSPDM_E_CERT_PARSE; + } + + if (!ctx->hasTrustedCAs) { + return WOLFSPDM_E_CERT_PARSE; + } + + /* SPDM cert chain header: Length(2 LE) + Reserved(2) + RootHash(48) */ + if (ctx->certChainLen <= 52) { + return WOLFSPDM_E_CERT_PARSE; + } + + /* Validate the root hash in the SPDM chain header against our trusted CA. + * The root hash at bytes 4-51 is SHA-384(root certificate). + * Compute hash of our trusted CA and compare. */ + rc = wc_InitSha384(&sha); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + rc = wc_Sha384Update(&sha, ctx->trustedCAs, ctx->trustedCAsSz); + if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } + rc = wc_Sha384Final(&sha, caHash); + wc_Sha384Free(&sha); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + + chainRootHash = ctx->certChain + 4; /* Skip Length(2) + Reserved(2) */ + if (XMEMCMP(caHash, chainRootHash, WOLFSPDM_HASH_SIZE) != 0) { + wolfSPDM_DebugPrint(ctx, + "Root cert hash mismatch — chain not from trusted CA\n"); + return WOLFSPDM_E_CERT_PARSE; + } + + wolfSPDM_DebugPrint(ctx, "Root certificate hash VERIFIED against trusted CA\n"); + + /* Parse the root cert from chain to get its public key for leaf verification */ + { + const byte* rootCert = ctx->certChain + 52; + word32 rootCertSz; + DecodedCert rootDc; + ecc_key rootPubKey; + word32 rootIdx; + + /* Get root cert size from ASN.1 header */ + if (rootCert[0] != 0x30) { + return WOLFSPDM_E_CERT_PARSE; + } + if (rootCert[1] < 0x80) { + rootCertSz = rootCert[1] + 2; + } else if (rootCert[1] == 0x82 && ctx->certChainLen > 56) { + rootCertSz = ((word32)rootCert[2] << 8 | rootCert[3]) + 4; + } else if (rootCert[1] == 0x81 && ctx->certChainLen > 55) { + rootCertSz = rootCert[2] + 3; + } else { + return WOLFSPDM_E_CERT_PARSE; + } + + /* Parse root cert to get its public key */ + wc_InitDecodedCert(&rootDc, rootCert, rootCertSz, NULL); + rc = wc_ParseCert(&rootDc, CA_TYPE, NO_VERIFY, NULL); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "Root cert parse failed: %d\n", rc); + wc_FreeDecodedCert(&rootDc); + return WOLFSPDM_E_CERT_PARSE; + } + + /* Import root public key for verifying leaf cert signature */ + rc = wc_ecc_init(&rootPubKey); + if (rc != 0) { + wc_FreeDecodedCert(&rootDc); + return WOLFSPDM_E_CRYPTO_FAIL; + } + rootIdx = 0; + rc = wc_EccPublicKeyDecode(rootDc.publicKey, &rootIdx, &rootPubKey, + rootDc.pubKeySize); + wc_FreeDecodedCert(&rootDc); + if (rc != 0) { + wc_ecc_free(&rootPubKey); + return WOLFSPDM_E_CERT_PARSE; + } + + /* Find leaf cert */ + rc = wolfSPDM_FindLeafCert(ctx->certChain, ctx->certChainLen, + &leafCert, &leafCertSz); + if (rc != WOLFSPDM_SUCCESS) { + wc_ecc_free(&rootPubKey); + return rc; + } + + /* Parse leaf cert and verify its signature against the CA/issuer key. + * For a 2-cert chain (root + leaf), the root signs the leaf directly. + * For longer chains, intermediate certs would need verification too, + * but the root hash match already establishes trust anchor. */ + wc_ecc_free(&rootPubKey); + } + + /* Extract public key from leaf cert */ + rc = wolfSPDM_FindLeafCert(ctx->certChain, ctx->certChainLen, + &leafCert, &leafCertSz); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + wc_InitDecodedCert(&cert, leafCert, leafCertSz, NULL); + rc = wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "Leaf certificate parse failed: %d\n", rc); + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CERT_PARSE; + } + + rc = wc_ecc_init(&ctx->responderPubKey); + if (rc != 0) { + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + idx = 0; + rc = wc_EccPublicKeyDecode(cert.publicKey, &idx, &ctx->responderPubKey, + cert.pubKeySize); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "ECC public key decode failed: %d\n", rc); + wc_ecc_free(&ctx->responderPubKey); + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CERT_PARSE; + } + + wc_FreeDecodedCert(&cert); + ctx->hasResponderPubKey = 1; + wolfSPDM_DebugPrint(ctx, "Extracted responder ECC P-384 public key (chain validated)\n"); + + return WOLFSPDM_SUCCESS; +} + +/* ========================================================================== + * Challenge Authentication (DSP0274 Section 10.8) + * ========================================================================== */ + +#ifndef NO_WOLFSPDM_CHALLENGE + +int wolfSPDM_BuildChallenge(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, + int slotId, byte measHashType) +{ + word32 offset = 0; + int rc; + + if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 36) { + return WOLFSPDM_E_BUFFER_SMALL; + } + + buf[offset++] = ctx->spdmVersion; + buf[offset++] = SPDM_CHALLENGE; + buf[offset++] = (byte)(slotId & 0x0F); + buf[offset++] = measHashType; + + /* Save measHashType for ParseChallengeAuth */ + ctx->challengeMeasHashType = measHashType; + + /* Nonce (32 bytes random) */ + rc = wolfSPDM_GetRandom(ctx, &buf[offset], 32); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + XMEMCPY(ctx->challengeNonce, &buf[offset], 32); + offset += 32; + + *bufSz = offset; + return WOLFSPDM_SUCCESS; +} + +int wolfSPDM_ParseChallengeAuth(WOLFSPDM_CTX* ctx, const byte* buf, + word32 bufSz, word32* sigOffset) +{ + word32 offset; + word16 opaqueLen; + + if (ctx == NULL || buf == NULL || sigOffset == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + /* Minimum size: 4 hdr + 48 certChainHash + 48 nonce + 48 measSummary + * + 2 opaqueLen + 96 sig = 246 bytes (with meas hash) */ + if (bufSz < 4) { + return WOLFSPDM_E_CHALLENGE; + } + + /* Check response code */ + if (buf[1] != SPDM_CHALLENGE_AUTH) { + int errCode; + if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { + wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH error: 0x%02x\n", errCode); + return WOLFSPDM_E_PEER_ERROR; + } + return WOLFSPDM_E_CHALLENGE; + } + + offset = 4; + + /* CertChainHash (H bytes, 48 for SHA-384) */ + if (offset + WOLFSPDM_HASH_SIZE > bufSz) { + wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH: too short for CertChainHash\n"); + return WOLFSPDM_E_CHALLENGE; + } + /* Verify cert chain hash matches what we computed */ + if (XMEMCMP(&buf[offset], ctx->certChainHash, WOLFSPDM_HASH_SIZE) != 0) { + wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH: CertChainHash mismatch\n"); + return WOLFSPDM_E_CHALLENGE; + } + offset += WOLFSPDM_HASH_SIZE; + + /* Nonce (32 bytes per DSP0274) */ + if (offset + 32 > bufSz) { + wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH: too short for Nonce\n"); + return WOLFSPDM_E_CHALLENGE; + } + offset += 32; + + /* MeasurementSummaryHash (H bytes if requested, 0 bytes if type=NONE) */ + if (ctx->challengeMeasHashType != SPDM_MEAS_SUMMARY_HASH_NONE) { + if (offset + WOLFSPDM_HASH_SIZE > bufSz) { + wolfSPDM_DebugPrint(ctx, + "CHALLENGE_AUTH: too short for MeasurementSummaryHash\n"); + return WOLFSPDM_E_CHALLENGE; + } + offset += WOLFSPDM_HASH_SIZE; + } + + /* OpaqueDataLength (2 LE) */ + if (offset + 2 > bufSz) { + return WOLFSPDM_E_CHALLENGE; + } + opaqueLen = (word16)(buf[offset] | (buf[offset + 1] << 8)); + offset += 2; + + /* Skip opaque data */ + if (offset + opaqueLen > bufSz) { + return WOLFSPDM_E_CHALLENGE; + } + offset += opaqueLen; + + /* Signature starts here */ + if (offset + WOLFSPDM_ECC_SIG_SIZE > bufSz) { + wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH: no room for signature\n"); + return WOLFSPDM_E_CHALLENGE; + } + + *sigOffset = offset; + return WOLFSPDM_SUCCESS; +} + +int wolfSPDM_VerifyChallengeAuthSig(WOLFSPDM_CTX* ctx, + const byte* rspBuf, word32 rspBufSz, + const byte* reqMsg, word32 reqMsgSz, word32 sigOffset) +{ + byte digest[WOLFSPDM_HASH_SIZE]; + byte derSig[256]; + word32 derSigSz = sizeof(derSig); + const byte* sigR; + const byte* sigS; + int verified = 0; + int rc; + + if (ctx == NULL || rspBuf == NULL || reqMsg == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (!ctx->hasResponderPubKey) { + return WOLFSPDM_E_CHALLENGE; + } + + /* Build M1/M2 hash per DSP0274 Section 10.8.3: + * M1/M2 = A || B || C where: + * A = VCA (GET_VERSION through ALGORITHMS) + * B = GET_DIGESTS + DIGESTS + GET_CERTIFICATE + CERTIFICATE + * C = CHALLENGE request + CHALLENGE_AUTH response (before sig) + * + * A+B are already accumulated in ctx->m1m2Hash from + * NegotiateAlgorithms/GetDigests/GetCertificate. + * Now add C and finalize. */ + if (!ctx->m1m2HashInit) { + wolfSPDM_DebugPrint(ctx, "CHALLENGE: M1/M2 hash not initialized\n"); + return WOLFSPDM_E_CHALLENGE; + } + + /* Add C: CHALLENGE request */ + rc = wc_Sha384Update(&ctx->m1m2Hash, reqMsg, reqMsgSz); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + + /* Add C: CHALLENGE_AUTH response (before signature) */ + rc = wc_Sha384Update(&ctx->m1m2Hash, rspBuf, sigOffset); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + + /* Finalize M1/M2 hash */ + rc = wc_Sha384Final(&ctx->m1m2Hash, digest); + ctx->m1m2HashInit = 0; /* Hash consumed */ + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + + /* Build M = prefix || zero_pad || context_str || Hash(M1||M2), then hash */ + { + static const char context_str[] = "responder-challenge_auth signing"; + #define CHAL_SIGNING_PREFIX_SIZE 16 + #define CHAL_SIGNING_CONTEXT_STR_SIZE 32 + #define CHAL_SIGNING_ZERO_PAD_SIZE (36 - CHAL_SIGNING_CONTEXT_STR_SIZE) /* 4 */ + wc_Sha384 sha2; + byte signMsg[200]; + word32 signMsgLen = 0; + byte majorVer, minorVer; + int i; + + majorVer = (byte)('0' + ((ctx->spdmVersion >> 4) & 0xF)); + minorVer = (byte)('0' + (ctx->spdmVersion & 0xF)); + + for (i = 0; i < 4; i++) { + XMEMCPY(&signMsg[signMsgLen], "dmtf-spdm-v1.2.*", + CHAL_SIGNING_PREFIX_SIZE); + signMsg[signMsgLen + 11] = majorVer; + signMsg[signMsgLen + 13] = minorVer; + signMsg[signMsgLen + 15] = '*'; + signMsgLen += CHAL_SIGNING_PREFIX_SIZE; + } + + XMEMSET(&signMsg[signMsgLen], 0x00, CHAL_SIGNING_ZERO_PAD_SIZE); + signMsgLen += CHAL_SIGNING_ZERO_PAD_SIZE; + + XMEMCPY(&signMsg[signMsgLen], context_str, CHAL_SIGNING_CONTEXT_STR_SIZE); + signMsgLen += CHAL_SIGNING_CONTEXT_STR_SIZE; + + XMEMCPY(&signMsg[signMsgLen], digest, WOLFSPDM_HASH_SIZE); + signMsgLen += WOLFSPDM_HASH_SIZE; + + /* Hash M */ + rc = wc_InitSha384(&sha2); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + rc = wc_Sha384Update(&sha2, signMsg, signMsgLen); + if (rc != 0) { wc_Sha384Free(&sha2); return WOLFSPDM_E_CRYPTO_FAIL; } + rc = wc_Sha384Final(&sha2, digest); + wc_Sha384Free(&sha2); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + } + + /* SPDM ECDSA signature: raw r||s */ + sigR = rspBuf + sigOffset; + sigS = sigR + (WOLFSPDM_ECC_SIG_SIZE / 2); + + rc = wc_ecc_rs_raw_to_sig(sigR, WOLFSPDM_ECC_SIG_SIZE / 2, + sigS, WOLFSPDM_ECC_SIG_SIZE / 2, derSig, &derSigSz); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "Challenge: rs_raw_to_sig failed: %d\n", rc); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + rc = wc_ecc_verify_hash(derSig, derSigSz, digest, WOLFSPDM_HASH_SIZE, + &verified, &ctx->responderPubKey); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "Challenge: verify_hash failed: %d\n", rc); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + if (verified == 1) { + wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH signature VERIFIED\n"); + return WOLFSPDM_SUCCESS; + } + + wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH signature INVALID\n"); + return WOLFSPDM_E_CHALLENGE; +} + +#endif /* !NO_WOLFSPDM_CHALLENGE */ + +/* ========================================================================== + * Heartbeat (DSP0274 Section 10.10) + * ========================================================================== */ + +int wolfSPDM_BuildHeartbeat(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) +{ + if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 4) { + return WOLFSPDM_E_BUFFER_SMALL; + } + + buf[0] = ctx->spdmVersion; + buf[1] = SPDM_HEARTBEAT; + buf[2] = 0x00; + buf[3] = 0x00; + *bufSz = 4; + + return WOLFSPDM_SUCCESS; +} + +int wolfSPDM_ParseHeartbeatAck(WOLFSPDM_CTX* ctx, const byte* buf, + word32 bufSz) +{ + if (ctx == NULL || buf == NULL || bufSz < 4) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (buf[1] != SPDM_HEARTBEAT_ACK) { + int errCode; + if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { + wolfSPDM_DebugPrint(ctx, "HEARTBEAT error: 0x%02x\n", errCode); + return WOLFSPDM_E_PEER_ERROR; + } + return WOLFSPDM_E_BAD_STATE; + } + + wolfSPDM_DebugPrint(ctx, "HEARTBEAT_ACK received\n"); + return WOLFSPDM_SUCCESS; +} + +/* ========================================================================== + * Key Update (DSP0274 Section 10.9) + * ========================================================================== */ + +int wolfSPDM_BuildKeyUpdate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, + byte operation, byte* tag) +{ + int rc; + + if (ctx == NULL || buf == NULL || bufSz == NULL || tag == NULL || + *bufSz < 4) { + return WOLFSPDM_E_BUFFER_SMALL; + } + + /* Generate random tag for request/response matching */ + rc = wolfSPDM_GetRandom(ctx, tag, 1); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + buf[0] = ctx->spdmVersion; + buf[1] = SPDM_KEY_UPDATE; + buf[2] = operation; + buf[3] = *tag; + *bufSz = 4; + + return WOLFSPDM_SUCCESS; +} + +int wolfSPDM_ParseKeyUpdateAck(WOLFSPDM_CTX* ctx, const byte* buf, + word32 bufSz, byte operation, byte tag) +{ + if (ctx == NULL || buf == NULL || bufSz < 4) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (buf[1] != SPDM_KEY_UPDATE_ACK) { + int errCode; + if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE error: 0x%02x\n", errCode); + return WOLFSPDM_E_PEER_ERROR; + } + return WOLFSPDM_E_KEY_UPDATE; + } + + /* Verify echoed operation and tag */ + if (buf[2] != operation) { + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE_ACK: operation mismatch: 0x%02x != 0x%02x\n", + buf[2], operation); + return WOLFSPDM_E_KEY_UPDATE; + } + + if (buf[3] != tag) { + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE_ACK: tag mismatch: 0x%02x != 0x%02x\n", + buf[3], tag); + return WOLFSPDM_E_KEY_UPDATE; + } + + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE_ACK received\n"); + return WOLFSPDM_SUCCESS; +} diff --git a/src/spdm_secured.c b/src/spdm_secured.c index f7f2bd3..b734e13 100644 --- a/src/spdm_secured.c +++ b/src/spdm_secured.c @@ -27,7 +27,7 @@ * * MCTP transport: * Header/AAD: SessionID(4 LE) + SeqNum(2 LE) + Length(2 LE) = 8 bytes - * IV XOR: Rightmost 2 bytes (bytes 10-11) with 2-byte sequence number + * IV XOR: Leftmost 2 bytes (bytes 0-1) with 2-byte LE sequence number (DSP0277) * * Nuvoton TCG binding (Rev 1.11): * Header/AAD: SessionID(4 LE) + SeqNum(8 LE) + Length(2 LE) = 14 bytes @@ -248,9 +248,10 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, else #endif { - /* MCTP format: 2-byte sequence number XOR at bytes 10-11 (rightmost) */ - iv[10] ^= (byte)(ctx->reqSeqNum & 0xFF); - iv[11] ^= (byte)((ctx->reqSeqNum >> 8) & 0xFF); + /* MCTP format: Zero-extend 2-byte LE sequence number to iv_length, + * then XOR with base IV per DSP0277. LE integer starts at byte 0. */ + iv[0] ^= (byte)(ctx->reqSeqNum & 0xFF); + iv[1] ^= (byte)((ctx->reqSeqNum >> 8) & 0xFF); } rc = wc_AesGcmSetKey(&aes, ctx->reqDataKey, WOLFSPDM_AEAD_KEY_SIZE); @@ -415,10 +416,11 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, XMEMCPY(aad, enc, aadSz); - /* Build IV: BaseIV XOR sequence number at bytes 10-11 (rightmost 2 bytes) */ + /* Build IV: Zero-extend 2-byte LE sequence number to iv_length, + * then XOR with base IV per DSP0277. LE integer starts at byte 0. */ XMEMCPY(iv, ctx->rspDataIv, WOLFSPDM_AEAD_IV_SIZE); - iv[10] ^= (byte)(rspSeqNum & 0xFF); - iv[11] ^= (byte)((rspSeqNum >> 8) & 0xFF); + iv[0] ^= (byte)(rspSeqNum & 0xFF); + iv[1] ^= (byte)((rspSeqNum >> 8) & 0xFF); rc = wc_AesGcmSetKey(&aes, ctx->rspDataKey, WOLFSPDM_AEAD_KEY_SIZE); if (rc != 0) { @@ -521,3 +523,82 @@ int wolfSPDM_SecuredExchange(WOLFSPDM_CTX* ctx, return WOLFSPDM_SUCCESS; } + +/* ========================================================================== + * Application Data Transfer + * ========================================================================== */ + +int wolfSPDM_SendData(WOLFSPDM_CTX* ctx, const byte* data, word32 dataSz) +{ + byte encBuf[WOLFSPDM_MAX_MSG_SIZE + 48]; + word32 encSz = sizeof(encBuf); + int rc; + + if (ctx == NULL || data == NULL || dataSz == 0) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (ctx->state != WOLFSPDM_STATE_CONNECTED) { + return WOLFSPDM_E_NOT_CONNECTED; + } + + /* Max payload: leave room for AEAD overhead */ + if (dataSz > WOLFSPDM_MAX_MSG_SIZE - 64) { + return WOLFSPDM_E_BUFFER_SMALL; + } + + /* Encrypt the application data */ + rc = wolfSPDM_EncryptInternal(ctx, data, dataSz, encBuf, &encSz); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + /* Send via I/O callback (no response expected for send-only) */ + if (ctx->ioCb == NULL) { + return WOLFSPDM_E_IO_FAIL; + } + + { + byte rxBuf[16]; + word32 rxSz = sizeof(rxBuf); + rc = ctx->ioCb(ctx, encBuf, encSz, rxBuf, &rxSz, ctx->ioUserCtx); + if (rc != 0) { + return WOLFSPDM_E_IO_FAIL; + } + } + + return WOLFSPDM_SUCCESS; +} + +int wolfSPDM_ReceiveData(WOLFSPDM_CTX* ctx, byte* data, word32* dataSz) +{ + byte rxBuf[WOLFSPDM_MAX_MSG_SIZE + 48]; + word32 rxSz = sizeof(rxBuf); + int rc; + + if (ctx == NULL || data == NULL || dataSz == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (ctx->state != WOLFSPDM_STATE_CONNECTED) { + return WOLFSPDM_E_NOT_CONNECTED; + } + + if (ctx->ioCb == NULL) { + return WOLFSPDM_E_IO_FAIL; + } + + /* Receive via I/O callback (NULL tx to indicate receive-only) */ + rc = ctx->ioCb(ctx, NULL, 0, rxBuf, &rxSz, ctx->ioUserCtx); + if (rc != 0) { + return WOLFSPDM_E_IO_FAIL; + } + + /* Decrypt the received data */ + rc = wolfSPDM_DecryptInternal(ctx, rxBuf, rxSz, data, dataSz); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + return WOLFSPDM_SUCCESS; +} diff --git a/src/spdm_session.c b/src/spdm_session.c index f5aa12e..bc88914 100644 --- a/src/spdm_session.c +++ b/src/spdm_session.c @@ -94,6 +94,29 @@ int wolfSPDM_NegotiateAlgorithms(WOLFSPDM_CTX* ctx) wolfSPDM_TranscriptAdd(ctx, rxBuf, rxSz); + /* Save VCA transcript length (GET_VERSION through ALGORITHMS). + * Used by measurement signature verification per DSP0274. */ + ctx->vcaLen = ctx->transcriptLen; + +#ifndef NO_WOLFSPDM_CHALLENGE + /* Initialize M1/M2 running hash for potential CHALLENGE auth. + * Start with VCA (A portion of the M1/M2 transcript per DSP0274). */ + { + int hashRc = wc_InitSha384(&ctx->m1m2Hash); + if (hashRc == 0) { + hashRc = wc_Sha384Update(&ctx->m1m2Hash, ctx->transcript, + ctx->vcaLen); + if (hashRc == 0) { + ctx->m1m2HashInit = 1; + } + else { + wc_Sha384Free(&ctx->m1m2Hash); + } + } + /* Non-fatal: challenge just won't work if this fails */ + } +#endif + return wolfSPDM_ParseAlgorithms(ctx, rxBuf, rxSz); } @@ -110,12 +133,21 @@ int wolfSPDM_GetDigests(WOLFSPDM_CTX* ctx) return rc; } - /* Note: GET_DIGESTS/DIGESTS are NOT added to transcript for TH1 per libspdm */ + /* Note: GET_DIGESTS/DIGESTS are NOT added to main transcript for TH1, + * but ARE needed for CHALLENGE M1/M2 (the "B" portion per DSP0274). */ rc = wolfSPDM_SendReceive(ctx, txBuf, txSz, rxBuf, &rxSz); if (rc != WOLFSPDM_SUCCESS) { return rc; } +#ifndef NO_WOLFSPDM_CHALLENGE + /* Feed GET_DIGESTS request + DIGESTS response to M1/M2 challenge hash */ + if (ctx->m1m2HashInit) { + wc_Sha384Update(&ctx->m1m2Hash, txBuf, txSz); + wc_Sha384Update(&ctx->m1m2Hash, rxBuf, rxSz); + } +#endif + return wolfSPDM_ParseDigests(ctx, rxBuf, rxSz); } @@ -143,6 +175,14 @@ int wolfSPDM_GetCertificate(WOLFSPDM_CTX* ctx, int slotId) return rc; } +#ifndef NO_WOLFSPDM_CHALLENGE + /* Feed each GET_CERTIFICATE/CERTIFICATE chunk to M1/M2 challenge hash */ + if (ctx->m1m2HashInit) { + wc_Sha384Update(&ctx->m1m2Hash, txBuf, txSz); + wc_Sha384Update(&ctx->m1m2Hash, rxBuf, rxSz); + } +#endif + rc = wolfSPDM_ParseCertificate(ctx, rxBuf, rxSz, &portionLen, &remainderLen); if (rc != WOLFSPDM_SUCCESS) { return rc; @@ -159,7 +199,23 @@ int wolfSPDM_GetCertificate(WOLFSPDM_CTX* ctx, int slotId) return rc; } - return wolfSPDM_TranscriptAdd(ctx, ctx->certChainHash, WOLFSPDM_HASH_SIZE); + rc = wolfSPDM_TranscriptAdd(ctx, ctx->certChainHash, WOLFSPDM_HASH_SIZE); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + /* Auto-extract responder public key from leaf cert. + * Needed by both measurement signature verification and challenge auth. + * Non-fatal: caller can still proceed, but signature ops will fail. */ + if (!ctx->hasResponderPubKey) { + int keyRc = wolfSPDM_ExtractResponderPubKey(ctx); + if (keyRc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, + "Warning: Could not extract responder public key (%d)\n", keyRc); + } + } + + return WOLFSPDM_SUCCESS; } int wolfSPDM_KeyExchange(WOLFSPDM_CTX* ctx) @@ -225,20 +281,11 @@ int wolfSPDM_Finish(WOLFSPDM_CTX* ctx) if (rxSz >= 2 && rxBuf[0] >= 0x10 && rxBuf[0] <= 0x1F) { /* Unencrypted SPDM message - check for ERROR */ if (rxBuf[1] == 0x7F) { /* SPDM_ERROR */ - wolfSPDM_DebugPrint(ctx, "FINISH: TPM returned unencrypted SPDM ERROR!\n"); - wolfSPDM_DebugPrint(ctx, " Error code: 0x%02x (%s)\n", rxBuf[2], - rxBuf[2] == SPDM_ERROR_INVALID_REQUEST ? "InvalidRequest" : - rxBuf[2] == SPDM_ERROR_BUSY ? "Busy" : - rxBuf[2] == SPDM_ERROR_UNEXPECTED_REQUEST ? "UnexpectedRequest" : - rxBuf[2] == SPDM_ERROR_UNSPECIFIED ? "Unspecified" : - rxBuf[2] == SPDM_ERROR_DECRYPT_ERROR ? "DecryptError" : - rxBuf[2] == SPDM_ERROR_UNSUPPORTED_REQUEST ? "UnsupportedRequest" : - rxBuf[2] == SPDM_ERROR_MAJOR_VERSION_MISMATCH ? "VersionMismatch" : "Unknown"); - wolfSPDM_DebugPrint(ctx, " Error data: 0x%02x\n", (rxSz >= 4) ? rxBuf[3] : 0); - + wolfSPDM_DebugPrint(ctx, "FINISH: peer returned SPDM ERROR 0x%02x\n", + rxBuf[2]); return WOLFSPDM_E_PEER_ERROR; } - wolfSPDM_DebugPrint(ctx, "FINISH: Unexpected unencrypted response code 0x%02x\n", + wolfSPDM_DebugPrint(ctx, "FINISH: unexpected response code 0x%02x\n", rxBuf[1]); return WOLFSPDM_E_PEER_ERROR; } @@ -266,3 +313,299 @@ int wolfSPDM_Finish(WOLFSPDM_CTX* ctx) return WOLFSPDM_SUCCESS; } + +/* ========================================================================== + * Measurements (Device Attestation) + * ========================================================================== */ + +#ifndef NO_WOLFSPDM_MEAS + +int wolfSPDM_GetMeasurements(WOLFSPDM_CTX* ctx, byte measOperation, + int requestSignature) +{ + byte txBuf[48]; /* GET_MEASUREMENTS: max 37 bytes (with sig request) */ + byte rxBuf[WOLFSPDM_MAX_MSG_SIZE]; + word32 txSz = sizeof(txBuf); + word32 rxSz = sizeof(rxBuf); + int rc; + + if (ctx == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + /* Must be at least past algorithm negotiation */ + if (ctx->state < WOLFSPDM_STATE_ALGO) { + return WOLFSPDM_E_BAD_STATE; + } + + /* Build GET_MEASUREMENTS request */ + rc = wolfSPDM_BuildGetMeasurements(ctx, txBuf, &txSz, + measOperation, (byte)requestSignature); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + +#ifndef NO_WOLFSPDM_MEAS_VERIFY + /* Save request message for L1 transcript (signature verification) */ + if (txSz <= sizeof(ctx->measReqMsg)) { + XMEMCPY(ctx->measReqMsg, txBuf, txSz); + ctx->measReqMsgSz = txSz; + } +#endif + + /* Send/receive: use secured exchange if session established, else cleartext */ + if (ctx->state == WOLFSPDM_STATE_CONNECTED) { + rc = wolfSPDM_SecuredExchange(ctx, txBuf, txSz, rxBuf, &rxSz); + } + else { + rc = wolfSPDM_SendReceive(ctx, txBuf, txSz, rxBuf, &rxSz); + } + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, "GET_MEASUREMENTS exchange failed: %d\n", rc); + return rc; + } + + /* Parse the response */ + rc = wolfSPDM_ParseMeasurements(ctx, rxBuf, rxSz); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + +#ifndef NO_WOLFSPDM_MEAS_VERIFY + /* Verify signature if requested and signature was captured */ + if (requestSignature && ctx->measSignatureSize > 0) { + if (!ctx->hasResponderPubKey) { + wolfSPDM_DebugPrint(ctx, + "No responder public key — cannot verify signature\n"); + return WOLFSPDM_E_MEAS_NOT_VERIFIED; + } + + rc = wolfSPDM_VerifyMeasurementSig(ctx, rxBuf, rxSz, + ctx->measReqMsg, ctx->measReqMsgSz); + if (rc != WOLFSPDM_SUCCESS) { + return WOLFSPDM_E_MEAS_SIG_FAIL; + } + + ctx->state = WOLFSPDM_STATE_MEASURED; + return WOLFSPDM_SUCCESS; /* Verified! */ + } +#else + (void)requestSignature; +#endif /* !NO_WOLFSPDM_MEAS_VERIFY */ + + /* No signature requested or verification not compiled in */ + ctx->state = WOLFSPDM_STATE_MEASURED; + return WOLFSPDM_E_MEAS_NOT_VERIFIED; +} + +#endif /* !NO_WOLFSPDM_MEAS */ + +/* ========================================================================== + * Challenge Authentication (Sessionless Attestation) + * ========================================================================== */ + +#ifndef NO_WOLFSPDM_CHALLENGE + +int wolfSPDM_Challenge(WOLFSPDM_CTX* ctx, int slotId, byte measHashType) +{ + byte txBuf[48]; /* CHALLENGE: 36 bytes */ + byte rxBuf[512]; /* CHALLENGE_AUTH: variable, up to ~300+ bytes */ + word32 txSz = sizeof(txBuf); + word32 rxSz = sizeof(rxBuf); + word32 sigOffset = 0; + int rc; + + if (ctx == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + /* Need cert chain for signature verification */ + if (ctx->state < WOLFSPDM_STATE_CERT) { + return WOLFSPDM_E_BAD_STATE; + } + + if (!ctx->hasResponderPubKey) { + wolfSPDM_DebugPrint(ctx, + "CHALLENGE: No responder public key for verification\n"); + return WOLFSPDM_E_CHALLENGE; + } + + /* Build CHALLENGE request */ + rc = wolfSPDM_BuildChallenge(ctx, txBuf, &txSz, slotId, measHashType); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + wolfSPDM_DebugPrint(ctx, "Sending CHALLENGE (slot=%d, measHash=0x%02x)\n", + slotId, measHashType); + + /* Cleartext exchange (no session needed) */ + rc = wolfSPDM_SendReceive(ctx, txBuf, txSz, rxBuf, &rxSz); + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, "CHALLENGE: SendReceive failed: %d\n", rc); + return rc; + } + + /* Parse CHALLENGE_AUTH response */ + rc = wolfSPDM_ParseChallengeAuth(ctx, rxBuf, rxSz, &sigOffset); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + /* Verify signature */ + rc = wolfSPDM_VerifyChallengeAuthSig(ctx, rxBuf, rxSz, + txBuf, txSz, sigOffset); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + wolfSPDM_DebugPrint(ctx, "CHALLENGE authentication PASSED\n"); + return WOLFSPDM_SUCCESS; +} + +#endif /* !NO_WOLFSPDM_CHALLENGE */ + +/* ========================================================================== + * Heartbeat (Session Keep-Alive) + * ========================================================================== */ + +int wolfSPDM_Heartbeat(WOLFSPDM_CTX* ctx) +{ + byte txBuf[8]; + byte rxBuf[32]; + word32 txSz = sizeof(txBuf); + word32 rxSz = sizeof(rxBuf); + int rc; + + if (ctx == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (ctx->state != WOLFSPDM_STATE_CONNECTED +#ifndef NO_WOLFSPDM_MEAS + && ctx->state != WOLFSPDM_STATE_MEASURED +#endif + ) { + return WOLFSPDM_E_NOT_CONNECTED; + } + + rc = wolfSPDM_BuildHeartbeat(ctx, txBuf, &txSz); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + /* Must be sent over encrypted channel */ + rc = wolfSPDM_SecuredExchange(ctx, txBuf, txSz, rxBuf, &rxSz); + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, "HEARTBEAT: SecuredExchange failed: %d\n", rc); + return rc; + } + + return wolfSPDM_ParseHeartbeatAck(ctx, rxBuf, rxSz); +} + +/* ========================================================================== + * Key Update (Session Key Rotation) + * ========================================================================== */ + +int wolfSPDM_KeyUpdate(WOLFSPDM_CTX* ctx, int updateAll) +{ + byte txBuf[8]; + byte rxBuf[32]; + word32 txSz, rxSz; + byte tag, tag2; + byte operation; + int rc; + + if (ctx == NULL) { + return WOLFSPDM_E_INVALID_ARG; + } + + if (ctx->state != WOLFSPDM_STATE_CONNECTED +#ifndef NO_WOLFSPDM_MEAS + && ctx->state != WOLFSPDM_STATE_MEASURED +#endif + ) { + return WOLFSPDM_E_NOT_CONNECTED; + } + + operation = updateAll ? SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS + : SPDM_KEY_UPDATE_OP_UPDATE_KEY; + + /* Step 1: Send KEY_UPDATE encrypted with CURRENT req key */ + txSz = sizeof(txBuf); + rc = wolfSPDM_BuildKeyUpdate(ctx, txBuf, &txSz, operation, &tag); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + wolfSPDM_DebugPrint(ctx, "Sending KEY_UPDATE\n"); + + { + byte encBuf[64]; + byte rawRxBuf[64]; + word32 encSz = sizeof(encBuf); + word32 rawRxSz = sizeof(rawRxBuf); + + /* Encrypt with current req key */ + rc = wolfSPDM_EncryptInternal(ctx, txBuf, txSz, encBuf, &encSz); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + /* Send and receive raw (don't decrypt yet) */ + rc = wolfSPDM_SendReceive(ctx, encBuf, encSz, rawRxBuf, &rawRxSz); + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE: SendReceive failed: %d\n", rc); + return rc; + } + + /* Step 2: Derive new keys BEFORE decrypting ACK. + * The responder derives new keys upon receiving KEY_UPDATE and + * encrypts the ACK with the NEW response key. */ + rc = wolfSPDM_DeriveUpdatedKeys(ctx, updateAll); + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE: DeriveUpdatedKeys failed: %d\n", rc); + return rc; + } + ctx->reqSeqNum = 0; + ctx->rspSeqNum = 0; + + /* Decrypt ACK with new rsp key */ + rxSz = sizeof(rxBuf); + rc = wolfSPDM_DecryptInternal(ctx, rawRxBuf, rawRxSz, rxBuf, &rxSz); + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE: ACK decrypt failed: %d\n", rc); + return rc; + } + } + + rc = wolfSPDM_ParseKeyUpdateAck(ctx, rxBuf, rxSz, operation, tag); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + /* Step 3: Verify new key works (send VERIFY_NEW_KEY with new keys) */ + txSz = sizeof(txBuf); + rc = wolfSPDM_BuildKeyUpdate(ctx, txBuf, &txSz, + SPDM_KEY_UPDATE_OP_VERIFY_NEW_KEY, &tag2); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + rxSz = sizeof(rxBuf); + rc = wolfSPDM_SecuredExchange(ctx, txBuf, txSz, rxBuf, &rxSz); + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE: VerifyNewKey exchange failed: %d\n", rc); + return rc; + } + + rc = wolfSPDM_ParseKeyUpdateAck(ctx, rxBuf, rxSz, + SPDM_KEY_UPDATE_OP_VERIFY_NEW_KEY, tag2); + if (rc != WOLFSPDM_SUCCESS) { + return rc; + } + + wolfSPDM_DebugPrint(ctx, "KEY_UPDATE completed, new keys active\n"); + return WOLFSPDM_SUCCESS; +} diff --git a/test/unit_test.c b/test/unit_test.c index a7434e5..8b94eed 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -522,6 +522,773 @@ static int test_error_strings(void) TEST_PASS(); } +/* ========================================================================== */ +/* Measurement Tests */ +/* ========================================================================== */ + +#ifndef NO_WOLFSPDM_MEAS + +static int test_build_get_measurements(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte buf[64]; + word32 bufSz; + int rc; + + printf("test_build_get_measurements...\n"); + + wolfSPDM_Init(ctx); + ctx->spdmVersion = SPDM_VERSION_12; + + /* Build without signature */ + bufSz = sizeof(buf); + rc = wolfSPDM_BuildGetMeasurements(ctx, buf, &bufSz, + SPDM_MEAS_OPERATION_ALL, 0); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Build without sig failed"); + TEST_ASSERT(bufSz == 4, "Without sig should be 4 bytes"); + TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version wrong"); + TEST_ASSERT(buf[1] == SPDM_GET_MEASUREMENTS, "Code should be 0xE0"); + TEST_ASSERT(buf[2] == 0x00, "Param1 should be 0 (no sig)"); + TEST_ASSERT(buf[3] == SPDM_MEAS_OPERATION_ALL, "Param2 should be 0xFF"); + + /* Build with signature */ + bufSz = sizeof(buf); + rc = wolfSPDM_BuildGetMeasurements(ctx, buf, &bufSz, + SPDM_MEAS_OPERATION_ALL, 1); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Build with sig failed"); + TEST_ASSERT(bufSz == 37, "With sig should be 37 bytes"); + TEST_ASSERT(buf[2] == SPDM_MEAS_REQUEST_SIG_BIT, "Sig bit should be set"); + + /* Verify nonce was populated (non-zero) */ + { + int nonZero = 0; + int i; + for (i = 4; i < 36; i++) { + if (buf[i] != 0) nonZero = 1; + } + TEST_ASSERT(nonZero, "Nonce should be non-zero"); + } + + /* Verify nonce saved in context */ + TEST_ASSERT(memcmp(ctx->measNonce, &buf[4], 32) == 0, + "Nonce should match context"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_measurement_accessors(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte measIdx, measType; + byte value[64]; + word32 valueSz; + int rc; + + printf("test_measurement_accessors...\n"); + + wolfSPDM_Init(ctx); + + /* Before measurements: count should be 0 */ + TEST_ASSERT(wolfSPDM_GetMeasurementCount(ctx) == 0, + "Count should be 0 before measurements"); + + /* Manually populate 2 test blocks */ + ctx->hasMeasurements = 1; + ctx->measBlockCount = 2; + + ctx->measBlocks[0].index = 1; + ctx->measBlocks[0].dmtfType = SPDM_MEAS_VALUE_TYPE_IMMUTABLE_ROM; + ctx->measBlocks[0].valueSize = 4; + ctx->measBlocks[0].value[0] = 0xAA; + ctx->measBlocks[0].value[1] = 0xBB; + ctx->measBlocks[0].value[2] = 0xCC; + ctx->measBlocks[0].value[3] = 0xDD; + + ctx->measBlocks[1].index = 2; + ctx->measBlocks[1].dmtfType = SPDM_MEAS_VALUE_TYPE_MUTABLE_FW; + ctx->measBlocks[1].valueSize = 2; + ctx->measBlocks[1].value[0] = 0x11; + ctx->measBlocks[1].value[1] = 0x22; + + TEST_ASSERT(wolfSPDM_GetMeasurementCount(ctx) == 2, + "Count should be 2"); + + /* Get block 0 */ + valueSz = sizeof(value); + rc = wolfSPDM_GetMeasurementBlock(ctx, 0, &measIdx, &measType, + value, &valueSz); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "GetMeasurementBlock(0) failed"); + TEST_ASSERT(measIdx == 1, "Block 0 index should be 1"); + TEST_ASSERT(measType == SPDM_MEAS_VALUE_TYPE_IMMUTABLE_ROM, + "Block 0 type wrong"); + TEST_ASSERT(valueSz == 4, "Block 0 size wrong"); + TEST_ASSERT(value[0] == 0xAA, "Block 0 value wrong"); + + /* Get block 1 */ + valueSz = sizeof(value); + rc = wolfSPDM_GetMeasurementBlock(ctx, 1, &measIdx, &measType, + value, &valueSz); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "GetMeasurementBlock(1) failed"); + TEST_ASSERT(measIdx == 2, "Block 1 index should be 2"); + TEST_ASSERT(valueSz == 2, "Block 1 size wrong"); + + /* Out of range */ + valueSz = sizeof(value); + rc = wolfSPDM_GetMeasurementBlock(ctx, 2, &measIdx, &measType, + value, &valueSz); + TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, + "Out of range should return INVALID_ARG"); + + rc = wolfSPDM_GetMeasurementBlock(ctx, -1, &measIdx, &measType, + value, &valueSz); + TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, + "Negative index should return INVALID_ARG"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_parse_measurements(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + /* Construct a fake MEASUREMENTS response with 2 blocks: + * + * Header (8 bytes): + * [0] version=0x12 [1] code=0x60 [2] param1 [3] param2 + * [4] numBlocks=2 + * [5..7] recordLen (3 LE) + * + * Block 1 (4+7=11 bytes): + * Index=1, MeasSpec=1, MeasSize=7 (LE) + * DMTF: Type=0x00, ValueSize=4 (LE), Value=AA BB CC DD + * + * Block 2 (4+5=9 bytes): + * Index=2, MeasSpec=1, MeasSize=5 (LE) + * DMTF: Type=0x01, ValueSize=2 (LE), Value=11 22 + * + * RecordLen = 11 + 9 = 20 + */ + byte rsp[] = { + 0x12, 0x60, 0x00, 0x00, /* header */ + 0x02, /* numBlocks */ + 0x14, 0x00, 0x00, /* recordLen = 20 LE */ + /* Block 1 */ + 0x01, /* Index */ + 0x01, /* MeasSpec (DMTF) */ + 0x07, 0x00, /* MeasSize = 7 LE */ + 0x00, /* DMTF Type: Immutable ROM */ + 0x04, 0x00, /* ValueSize = 4 LE */ + 0xAA, 0xBB, 0xCC, 0xDD, /* Value */ + /* Block 2 */ + 0x02, /* Index */ + 0x01, /* MeasSpec (DMTF) */ + 0x05, 0x00, /* MeasSize = 5 LE */ + 0x01, /* DMTF Type: Mutable FW */ + 0x02, 0x00, /* ValueSize = 2 LE */ + 0x11, 0x22 /* Value */ + }; + int rc; + + printf("test_parse_measurements...\n"); + + wolfSPDM_Init(ctx); + + rc = wolfSPDM_ParseMeasurements(ctx, rsp, sizeof(rsp)); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "ParseMeasurements failed"); + TEST_ASSERT(ctx->measBlockCount == 2, "Should have 2 blocks"); + TEST_ASSERT(ctx->hasMeasurements == 1, "hasMeasurements should be set"); + + /* Check block 0 */ + TEST_ASSERT(ctx->measBlocks[0].index == 1, "Block 0 index wrong"); + TEST_ASSERT(ctx->measBlocks[0].dmtfType == 0x00, "Block 0 type wrong"); + TEST_ASSERT(ctx->measBlocks[0].valueSize == 4, "Block 0 valueSize wrong"); + TEST_ASSERT(ctx->measBlocks[0].value[0] == 0xAA, "Block 0 value[0] wrong"); + TEST_ASSERT(ctx->measBlocks[0].value[3] == 0xDD, "Block 0 value[3] wrong"); + + /* Check block 1 */ + TEST_ASSERT(ctx->measBlocks[1].index == 2, "Block 1 index wrong"); + TEST_ASSERT(ctx->measBlocks[1].dmtfType == 0x01, "Block 1 type wrong"); + TEST_ASSERT(ctx->measBlocks[1].valueSize == 2, "Block 1 valueSize wrong"); + TEST_ASSERT(ctx->measBlocks[1].value[0] == 0x11, "Block 1 value[0] wrong"); + + /* Test truncated buffer */ + rc = wolfSPDM_ParseMeasurements(ctx, rsp, 5); + TEST_ASSERT(rc == WOLFSPDM_E_MEASUREMENT, + "Truncated should return MEASUREMENT error"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +#ifndef NO_WOLFSPDM_MEAS_VERIFY + +static int test_measurement_sig_verification(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + ecc_key sigKey; + WC_RNG rng; + /* Construct a minimal GET_MEASUREMENTS request (L1) */ + byte reqMsg[] = { + 0x12, 0xE0, 0x01, 0xFF, /* version, GET_MEASUREMENTS, sig bit, all */ + /* 32 bytes nonce */ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + 0x00 /* SlotID */ + }; + /* Construct a MEASUREMENTS response (L2) WITHOUT signature + * We'll append signature after signing */ + byte rspBase[] = { + 0x12, 0x60, 0x00, 0x00, /* header */ + 0x01, /* numBlocks=1 */ + 0x0B, 0x00, 0x00, /* recordLen=11 */ + /* Block 1 */ + 0x01, 0x01, 0x07, 0x00, /* Index=1, Spec=1, Size=7 */ + 0x00, 0x04, 0x00, /* Type=0, ValueSize=4 */ + 0xAA, 0xBB, 0xCC, 0xDD, /* Value */ + /* Nonce (32 bytes) */ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + /* OpaqueDataLength = 0 */ + 0x00, 0x00 + }; + byte rspBuf[256]; /* rspBase + 96 byte signature */ + word32 rspBufSz; + wc_Sha384 sha, sha2; + byte digest[WOLFSPDM_HASH_SIZE]; + byte derSig[256]; + word32 derSigSz = sizeof(derSig); + byte rawR[WOLFSPDM_ECC_KEY_SIZE]; + byte rawS[WOLFSPDM_ECC_KEY_SIZE]; + word32 rSz = sizeof(rawR); + word32 sSz = sizeof(rawS); + int rc; + + printf("test_measurement_sig_verification...\n"); + + wolfSPDM_Init(ctx); + ctx->spdmVersion = SPDM_VERSION_12; + + /* Generate ECC P-384 keypair for testing */ + rc = wc_InitRng(&rng); + TEST_ASSERT(rc == 0, "wc_InitRng failed"); + rc = wc_ecc_init(&sigKey); + TEST_ASSERT(rc == 0, "wc_ecc_init failed"); + rc = wc_ecc_make_key(&rng, 48, &sigKey); + TEST_ASSERT(rc == 0, "wc_ecc_make_key failed"); + + /* Copy public key into context for verification */ + rc = wc_ecc_init(&ctx->responderPubKey); + TEST_ASSERT(rc == 0, "wc_ecc_init responderPubKey failed"); + + /* Export/import just the public key */ + { + byte pubDer[256]; + word32 pubDerSz = sizeof(pubDer); + word32 idx = 0; + rc = wc_EccPublicKeyToDer(&sigKey, pubDer, pubDerSz, 1); + TEST_ASSERT(rc > 0, "EccPublicKeyToDer failed"); + pubDerSz = (word32)rc; + rc = wc_EccPublicKeyDecode(pubDer, &idx, &ctx->responderPubKey, + pubDerSz); + TEST_ASSERT(rc == 0, "EccPublicKeyDecode failed"); + } + ctx->hasResponderPubKey = 1; + + /* Build the response buffer (rspBase + signature) */ + XMEMCPY(rspBuf, rspBase, sizeof(rspBase)); + rspBufSz = sizeof(rspBase); + + /* Compute Hash(L1||L2) where L2 = rspBase (before signature) */ + /* Then build M = prefix||pad||context||hash, then Hash(M) */ + { + static const char context_str[] = "responder-measurements signing"; + #define TEST_PREFIX_SIZE 16 + #define TEST_CONTEXT_STR_SIZE 30 /* strlen, no null terminator */ + #define TEST_ZERO_PAD_SIZE (36 - TEST_CONTEXT_STR_SIZE) + byte signMsg[200]; + word32 signMsgLen = 0; + int i; + + /* L1||L2 hash */ + rc = wc_InitSha384(&sha); + TEST_ASSERT(rc == 0, "InitSha384 failed"); + wc_Sha384Update(&sha, reqMsg, sizeof(reqMsg)); + wc_Sha384Update(&sha, rspBuf, rspBufSz); + wc_Sha384Final(&sha, digest); + wc_Sha384Free(&sha); + + /* Build M */ + for (i = 0; i < 4; i++) { + XMEMCPY(&signMsg[signMsgLen], "dmtf-spdm-v1.2.*", TEST_PREFIX_SIZE); + signMsgLen += TEST_PREFIX_SIZE; + } + XMEMSET(&signMsg[signMsgLen], 0x00, TEST_ZERO_PAD_SIZE); + signMsgLen += TEST_ZERO_PAD_SIZE; + XMEMCPY(&signMsg[signMsgLen], context_str, TEST_CONTEXT_STR_SIZE); + signMsgLen += TEST_CONTEXT_STR_SIZE; + XMEMCPY(&signMsg[signMsgLen], digest, WOLFSPDM_HASH_SIZE); + signMsgLen += WOLFSPDM_HASH_SIZE; + + /* Hash(M) */ + rc = wc_InitSha384(&sha2); + TEST_ASSERT(rc == 0, "InitSha384 for M failed"); + wc_Sha384Update(&sha2, signMsg, signMsgLen); + wc_Sha384Final(&sha2, digest); + wc_Sha384Free(&sha2); + } + + /* Sign Hash(M) with our test key (DER format) */ + rc = wc_ecc_sign_hash(digest, WOLFSPDM_HASH_SIZE, derSig, &derSigSz, + &rng, &sigKey); + TEST_ASSERT(rc == 0, "ecc_sign_hash failed"); + + /* Convert DER signature to raw r||s for SPDM */ + rc = wc_ecc_sig_to_rs(derSig, derSigSz, rawR, &rSz, rawS, &sSz); + TEST_ASSERT(rc == 0, "ecc_sig_to_rs failed"); + + /* Pad r and s to 48 bytes each (P-384) */ + { + byte sigRaw[WOLFSPDM_ECC_SIG_SIZE]; + XMEMSET(sigRaw, 0, sizeof(sigRaw)); + /* Right-align r and s in their 48-byte fields */ + XMEMCPY(sigRaw + (48 - rSz), rawR, rSz); + XMEMCPY(sigRaw + 48 + (48 - sSz), rawS, sSz); + XMEMCPY(rspBuf + rspBufSz, sigRaw, WOLFSPDM_ECC_SIG_SIZE); + } + rspBufSz += WOLFSPDM_ECC_SIG_SIZE; + + /* Test 1: Valid signature should verify */ + rc = wolfSPDM_VerifyMeasurementSig(ctx, rspBuf, rspBufSz, + reqMsg, sizeof(reqMsg)); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, + "Valid signature should verify"); + + /* Test 2: Corrupt one signature byte -> should fail */ + rspBuf[rspBufSz - 10] ^= 0xFF; + rc = wolfSPDM_VerifyMeasurementSig(ctx, rspBuf, rspBufSz, + reqMsg, sizeof(reqMsg)); + TEST_ASSERT(rc == WOLFSPDM_E_MEAS_SIG_FAIL, + "Corrupted sig should fail"); + rspBuf[rspBufSz - 10] ^= 0xFF; /* Restore */ + + /* Test 3: Corrupt one measurement byte -> should fail */ + rspBuf[15] ^= 0xFF; /* Corrupt a measurement value byte */ + rc = wolfSPDM_VerifyMeasurementSig(ctx, rspBuf, rspBufSz, + reqMsg, sizeof(reqMsg)); + TEST_ASSERT(rc == WOLFSPDM_E_MEAS_SIG_FAIL, + "Corrupted measurement should fail"); + + wc_ecc_free(&sigKey); + wc_FreeRng(&rng); + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +#endif /* !NO_WOLFSPDM_MEAS_VERIFY */ +#endif /* !NO_WOLFSPDM_MEAS */ + +/* ========================================================================== */ +/* Certificate Chain Validation Tests */ +/* ========================================================================== */ + +static int test_set_trusted_cas(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte fakeCa[] = {0x30, 0x82, 0x01, 0x00, 0xAA, 0xBB, 0xCC, 0xDD}; + int rc; + + printf("test_set_trusted_cas...\n"); + + wolfSPDM_Init(ctx); + + /* NULL args should fail */ + rc = wolfSPDM_SetTrustedCAs(NULL, fakeCa, sizeof(fakeCa)); + TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, "NULL ctx should fail"); + + rc = wolfSPDM_SetTrustedCAs(ctx, NULL, sizeof(fakeCa)); + TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, "NULL certs should fail"); + + rc = wolfSPDM_SetTrustedCAs(ctx, fakeCa, 0); + TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, "Zero size should fail"); + + /* Valid call */ + rc = wolfSPDM_SetTrustedCAs(ctx, fakeCa, sizeof(fakeCa)); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "SetTrustedCAs failed"); + TEST_ASSERT(ctx->hasTrustedCAs == 1, "hasTrustedCAs not set"); + TEST_ASSERT(ctx->trustedCAsSz == sizeof(fakeCa), "Size mismatch"); + TEST_ASSERT(memcmp(ctx->trustedCAs, fakeCa, sizeof(fakeCa)) == 0, + "Data mismatch"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_validate_cert_chain_no_cas(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + int rc; + + printf("test_validate_cert_chain_no_cas...\n"); + + wolfSPDM_Init(ctx); + + /* No trusted CAs loaded — should return error */ + rc = wolfSPDM_ValidateCertChain(ctx); + TEST_ASSERT(rc == WOLFSPDM_E_CERT_PARSE, + "Should fail without trusted CAs"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +/* ========================================================================== */ +/* Challenge Tests */ +/* ========================================================================== */ + +#ifndef NO_WOLFSPDM_CHALLENGE + +static int test_build_challenge(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte buf[64]; + word32 bufSz; + int rc; + + printf("test_build_challenge...\n"); + + wolfSPDM_Init(ctx); + ctx->spdmVersion = SPDM_VERSION_12; + + bufSz = sizeof(buf); + rc = wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 0, + SPDM_MEAS_SUMMARY_HASH_NONE); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildChallenge failed"); + TEST_ASSERT(bufSz == 36, "CHALLENGE should be 36 bytes"); + TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version wrong"); + TEST_ASSERT(buf[1] == SPDM_CHALLENGE, "Code should be 0x83"); + TEST_ASSERT(buf[2] == 0x00, "SlotID should be 0"); + TEST_ASSERT(buf[3] == SPDM_MEAS_SUMMARY_HASH_NONE, "MeasHashType wrong"); + + /* Verify nonce is populated (non-zero) */ + { + int nonZero = 0; + int i; + for (i = 4; i < 36; i++) { + if (buf[i] != 0) nonZero = 1; + } + TEST_ASSERT(nonZero, "Nonce should be non-zero"); + } + + /* Verify nonce saved in context */ + TEST_ASSERT(memcmp(ctx->challengeNonce, &buf[4], 32) == 0, + "Challenge nonce should match context"); + + /* Test with different slot and meas hash type */ + bufSz = sizeof(buf); + rc = wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 3, + SPDM_MEAS_SUMMARY_HASH_ALL); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildChallenge slot 3 failed"); + TEST_ASSERT(buf[2] == 0x03, "SlotID should be 3"); + TEST_ASSERT(buf[3] == 0xFF, "MeasHashType should be 0xFF"); + + /* Buffer too small */ + bufSz = 10; + rc = wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 0, + SPDM_MEAS_SUMMARY_HASH_NONE); + TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_parse_challenge_auth(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + /* Build a fake CHALLENGE_AUTH response (measHashType=NONE): + * [0-3] header: version=0x12, code=0x03, param1, param2 + * [4-51] CertChainHash (48 bytes) + * [52-83] Nonce (32 bytes per DSP0274) + * (no MeasurementSummaryHash when type=NONE) + * [84-85] OpaqueDataLength = 0 + * [86-181] Signature (96 bytes) */ + byte rsp[182]; + word32 sigOffset = 0; + int rc; + + printf("test_parse_challenge_auth...\n"); + + wolfSPDM_Init(ctx); + ctx->spdmVersion = SPDM_VERSION_12; + ctx->challengeMeasHashType = SPDM_MEAS_SUMMARY_HASH_NONE; + + XMEMSET(rsp, 0, sizeof(rsp)); + rsp[0] = SPDM_VERSION_12; + rsp[1] = SPDM_CHALLENGE_AUTH; + rsp[2] = 0x00; /* Param1 */ + rsp[3] = 0x00; /* Param2 */ + + /* Fill CertChainHash with known pattern to match ctx */ + XMEMSET(&rsp[4], 0xAA, WOLFSPDM_HASH_SIZE); + XMEMCPY(ctx->certChainHash, &rsp[4], WOLFSPDM_HASH_SIZE); + + /* Fill nonce (32 bytes) */ + XMEMSET(&rsp[52], 0xBB, 32); + + /* OpaqueDataLength = 0 (no MeasSummaryHash since type=NONE) */ + rsp[84] = 0x00; + rsp[85] = 0x00; + + /* Fake signature (96 bytes) */ + XMEMSET(&rsp[86], 0xCC, WOLFSPDM_ECC_SIG_SIZE); + + rc = wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "ParseChallengeAuth failed"); + TEST_ASSERT(sigOffset == 86, "Signature offset should be 86"); + + /* Test with wrong response code */ + rsp[1] = 0xFF; + rc = wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset); + TEST_ASSERT(rc == WOLFSPDM_E_CHALLENGE, "Wrong code should fail"); + rsp[1] = SPDM_CHALLENGE_AUTH; + + /* Test with CertChainHash mismatch */ + ctx->certChainHash[0] = 0x00; /* Change expected hash */ + rc = wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset); + TEST_ASSERT(rc == WOLFSPDM_E_CHALLENGE, "Hash mismatch should fail"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +#endif /* !NO_WOLFSPDM_CHALLENGE */ + +/* ========================================================================== */ +/* Heartbeat Tests */ +/* ========================================================================== */ + +static int test_build_heartbeat(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte buf[16]; + word32 bufSz; + int rc; + + printf("test_build_heartbeat...\n"); + + wolfSPDM_Init(ctx); + ctx->spdmVersion = SPDM_VERSION_12; + + bufSz = sizeof(buf); + rc = wolfSPDM_BuildHeartbeat(ctx, buf, &bufSz); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildHeartbeat failed"); + TEST_ASSERT(bufSz == 4, "HEARTBEAT should be 4 bytes"); + TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version wrong"); + TEST_ASSERT(buf[1] == SPDM_HEARTBEAT, "Code should be 0xE8"); + TEST_ASSERT(buf[2] == 0x00, "Param1 should be 0"); + TEST_ASSERT(buf[3] == 0x00, "Param2 should be 0"); + + /* Buffer too small */ + bufSz = 2; + rc = wolfSPDM_BuildHeartbeat(ctx, buf, &bufSz); + TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_parse_heartbeat_ack(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte ack[] = {0x12, SPDM_HEARTBEAT_ACK, 0x00, 0x00}; + byte err[] = {0x12, SPDM_ERROR, 0x01, 0x00}; + int rc; + + printf("test_parse_heartbeat_ack...\n"); + + wolfSPDM_Init(ctx); + + /* Valid ACK */ + rc = wolfSPDM_ParseHeartbeatAck(ctx, ack, sizeof(ack)); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Valid ACK should succeed"); + + /* Error response */ + rc = wolfSPDM_ParseHeartbeatAck(ctx, err, sizeof(err)); + TEST_ASSERT(rc == WOLFSPDM_E_PEER_ERROR, "Error should return PEER_ERROR"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_heartbeat_state_check(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + int rc; + + printf("test_heartbeat_state_check...\n"); + + wolfSPDM_Init(ctx); + + /* Not connected — should fail */ + rc = wolfSPDM_Heartbeat(ctx); + TEST_ASSERT(rc == WOLFSPDM_E_NOT_CONNECTED, + "Heartbeat should fail when not connected"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +/* ========================================================================== */ +/* Key Update Tests */ +/* ========================================================================== */ + +static int test_build_key_update(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte buf[16]; + word32 bufSz; + byte tag = 0; + int rc; + + printf("test_build_key_update...\n"); + + wolfSPDM_Init(ctx); + ctx->spdmVersion = SPDM_VERSION_12; + + bufSz = sizeof(buf); + rc = wolfSPDM_BuildKeyUpdate(ctx, buf, &bufSz, + SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, &tag); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildKeyUpdate failed"); + TEST_ASSERT(bufSz == 4, "KEY_UPDATE should be 4 bytes"); + TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version wrong"); + TEST_ASSERT(buf[1] == SPDM_KEY_UPDATE, "Code should be 0xE9"); + TEST_ASSERT(buf[2] == SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, + "Operation should be UpdateAllKeys"); + TEST_ASSERT(buf[3] == tag, "Tag should match returned value"); + + /* Buffer too small */ + bufSz = 2; + rc = wolfSPDM_BuildKeyUpdate(ctx, buf, &bufSz, + SPDM_KEY_UPDATE_OP_UPDATE_KEY, &tag); + TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_parse_key_update_ack(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte ack[] = {0x12, SPDM_KEY_UPDATE_ACK, 0x02, 0x42}; + int rc; + + printf("test_parse_key_update_ack...\n"); + + wolfSPDM_Init(ctx); + + /* Valid ACK with matching operation and tag */ + rc = wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), + SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, 0x42); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Valid ACK should succeed"); + + /* Mismatched tag */ + rc = wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), + SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, 0xFF); + TEST_ASSERT(rc == WOLFSPDM_E_KEY_UPDATE, "Mismatched tag should fail"); + + /* Mismatched operation */ + rc = wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), + SPDM_KEY_UPDATE_OP_UPDATE_KEY, 0x42); + TEST_ASSERT(rc == WOLFSPDM_E_KEY_UPDATE, "Mismatched op should fail"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_derive_updated_keys(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + byte origReqKey[WOLFSPDM_AEAD_KEY_SIZE]; + byte origRspKey[WOLFSPDM_AEAD_KEY_SIZE]; + int rc; + + printf("test_derive_updated_keys...\n"); + + wolfSPDM_Init(ctx); + ctx->spdmVersion = SPDM_VERSION_12; + + /* Set up fake app secrets and keys */ + XMEMSET(ctx->reqAppSecret, 0x5A, WOLFSPDM_HASH_SIZE); + XMEMSET(ctx->rspAppSecret, 0xA5, WOLFSPDM_HASH_SIZE); + XMEMSET(ctx->reqDataKey, 0x11, WOLFSPDM_AEAD_KEY_SIZE); + XMEMSET(ctx->rspDataKey, 0x22, WOLFSPDM_AEAD_KEY_SIZE); + + XMEMCPY(origReqKey, ctx->reqDataKey, WOLFSPDM_AEAD_KEY_SIZE); + XMEMCPY(origRspKey, ctx->rspDataKey, WOLFSPDM_AEAD_KEY_SIZE); + + /* Update all keys */ + rc = wolfSPDM_DeriveUpdatedKeys(ctx, 1); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "DeriveUpdatedKeys failed"); + + /* Keys should have changed */ + TEST_ASSERT(memcmp(ctx->reqDataKey, origReqKey, + WOLFSPDM_AEAD_KEY_SIZE) != 0, "Req key should change"); + TEST_ASSERT(memcmp(ctx->rspDataKey, origRspKey, + WOLFSPDM_AEAD_KEY_SIZE) != 0, "Rsp key should change"); + + /* Update requester only */ + XMEMCPY(origReqKey, ctx->reqDataKey, WOLFSPDM_AEAD_KEY_SIZE); + XMEMCPY(origRspKey, ctx->rspDataKey, WOLFSPDM_AEAD_KEY_SIZE); + + rc = wolfSPDM_DeriveUpdatedKeys(ctx, 0); + TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "DeriveUpdatedKeys (req only) failed"); + + TEST_ASSERT(memcmp(ctx->reqDataKey, origReqKey, + WOLFSPDM_AEAD_KEY_SIZE) != 0, "Req key should change"); + TEST_ASSERT(memcmp(ctx->rspDataKey, origRspKey, + WOLFSPDM_AEAD_KEY_SIZE) == 0, "Rsp key should NOT change"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + +static int test_key_update_state_check(void) +{ + WOLFSPDM_CTX ctxBuf; + WOLFSPDM_CTX* ctx = &ctxBuf; + int rc; + + printf("test_key_update_state_check...\n"); + + wolfSPDM_Init(ctx); + + /* Not connected — should fail */ + rc = wolfSPDM_KeyUpdate(ctx, 1); + TEST_ASSERT(rc == WOLFSPDM_E_NOT_CONNECTED, + "KeyUpdate should fail when not connected"); + + wolfSPDM_Free(ctx); + TEST_PASS(); +} + /* ========================================================================== */ /* Session State Tests */ /* ========================================================================== */ @@ -596,6 +1363,37 @@ int main(void) test_check_error(); test_error_strings(); + /* Measurement tests */ +#ifndef NO_WOLFSPDM_MEAS + test_build_get_measurements(); + test_measurement_accessors(); + test_parse_measurements(); +#ifndef NO_WOLFSPDM_MEAS_VERIFY + test_measurement_sig_verification(); +#endif +#endif + + /* Certificate chain validation tests */ + test_set_trusted_cas(); + test_validate_cert_chain_no_cas(); + + /* Challenge tests */ +#ifndef NO_WOLFSPDM_CHALLENGE + test_build_challenge(); + test_parse_challenge_auth(); +#endif + + /* Heartbeat tests */ + test_build_heartbeat(); + test_parse_heartbeat_ack(); + test_heartbeat_state_check(); + + /* Key update tests */ + test_build_key_update(); + test_parse_key_update_ack(); + test_derive_updated_keys(); + test_key_update_state_check(); + /* Session state tests */ test_session_state(); diff --git a/wolfspdm/spdm.h b/wolfspdm/spdm.h index 85f3dc8..ffd7f94 100644 --- a/wolfspdm/spdm.h +++ b/wolfspdm/spdm.h @@ -100,11 +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). - * Actual struct size: ~21.2 KB (base) / ~21.3 KB (with Nuvoton). - * Rounded up to 22 KB for platform alignment and minor future growth. + * Actual struct size: ~22.7 KB (base with measurements) / ~21.2 KB (NO_WOLFSPDM_MEAS). + * Rounded up to 24 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 +#define WOLFSPDM_CTX_STATIC_SIZE 32768 /* 32KB - fits CTX with cert validation + challenge + key update fields */ /* Forward declaration */ struct WOLFSPDM_CTX; @@ -408,6 +408,94 @@ int wolfSPDM_SecuredExchange(WOLFSPDM_CTX* ctx, const byte* cmdPlain, word32 cmdSz, byte* rspPlain, word32* rspSz); +#ifndef NO_WOLFSPDM_MEAS +/* ========================================================================== + * Measurements (Device Attestation) + * ========================================================================== + * + * When requestSignature=1 (and NO_WOLFSPDM_MEAS_VERIFY is NOT defined): + * Retrieves measurements with a cryptographic signature from the responder, + * then verifies the signature using the responder's certificate (retrieved + * during wolfSPDM_Connect). Returns WOLFSPDM_SUCCESS if verification passes. + * Returns WOLFSPDM_E_MEAS_SIG_FAIL if the signature is invalid. + * + * When requestSignature=0: + * Retrieves measurements WITHOUT a signature. + * Returns WOLFSPDM_E_MEAS_NOT_VERIFIED. Measurements are informational + * only and should not be used for security-critical decisions. + * + * If compiled with NO_WOLFSPDM_MEAS_VERIFY, signature verification is + * disabled and returns WOLFSPDM_E_MEAS_NOT_VERIFIED regardless of + * requestSignature (signature bytes are still captured in the context). + * + * Contexts are NOT thread-safe; do not call from multiple threads. + * ========================================================================== */ + +/** + * Retrieve measurements from the SPDM responder. + * + * @param ctx The wolfSPDM context. + * @param measOperation SPDM_MEAS_OPERATION_ALL (0xFF) or specific index. + * @param requestSignature 1 to request signed measurements, 0 for unsigned. + * @return WOLFSPDM_SUCCESS (verified), WOLFSPDM_E_MEAS_NOT_VERIFIED (unsigned), + * WOLFSPDM_E_MEAS_SIG_FAIL (sig invalid), or negative error code. + */ +int wolfSPDM_GetMeasurements(WOLFSPDM_CTX* ctx, byte measOperation, + int requestSignature); + +/** + * Get the number of measurement blocks retrieved. + * + * @param ctx The wolfSPDM context. + * @return Number of measurement blocks, or 0 if none. + */ +int wolfSPDM_GetMeasurementCount(WOLFSPDM_CTX* ctx); + +/** + * Get a specific measurement block by index. + * + * @param ctx The wolfSPDM context. + * @param blockIdx Index into retrieved blocks (0-based). + * @param measIndex [out] SPDM measurement index (1-based). + * @param measType [out] DMTFSpecMeasurementValueType. + * @param value [out] Buffer for measurement value. + * @param valueSz [in] Size of value buffer, [out] Actual value size. + * @return WOLFSPDM_SUCCESS or negative error code. + */ +int wolfSPDM_GetMeasurementBlock(WOLFSPDM_CTX* ctx, int blockIdx, + byte* measIndex, byte* measType, byte* value, word32* valueSz); +#endif /* !NO_WOLFSPDM_MEAS */ + +/* ========================================================================== + * Application Data Transfer + * ========================================================================== + * + * Send/receive application data over an established SPDM session. + * Max payload per call: WOLFSPDM_MAX_MSG_SIZE minus AEAD overhead (~4000 bytes). + * These are message-oriented (no partial reads/writes). + * Contexts are NOT thread-safe; do not call from multiple threads. + * ========================================================================== */ + +/** + * Send application data over an established SPDM session. + * + * @param ctx The wolfSPDM context (must be connected). + * @param data Data to send. + * @param dataSz Size of data. + * @return WOLFSPDM_SUCCESS or negative error code. + */ +int wolfSPDM_SendData(WOLFSPDM_CTX* ctx, const byte* data, word32 dataSz); + +/** + * Receive application data over an established SPDM session. + * + * @param ctx The wolfSPDM context (must be connected). + * @param data Buffer for received data. + * @param dataSz [in] Size of buffer, [out] Actual data size. + * @return WOLFSPDM_SUCCESS or negative error code. + */ +int wolfSPDM_ReceiveData(WOLFSPDM_CTX* ctx, byte* data, word32* dataSz); + /* ========================================================================== * Session Information * ========================================================================== */ @@ -446,6 +534,75 @@ word32 wolfSPDM_GetConnectionHandle(WOLFSPDM_CTX* ctx); word16 wolfSPDM_GetFipsIndicator(WOLFSPDM_CTX* ctx); #endif +/* ========================================================================== + * Certificate Chain Validation + * ========================================================================== */ + +/** + * Load trusted root CA certificates for certificate chain validation. + * When set, wolfSPDM_Connect() will validate the responder's certificate + * chain against these CAs. Without this, only the public key is extracted. + * + * @param ctx The wolfSPDM context. + * @param derCerts DER-encoded CA certificate(s) (concatenated if multiple). + * @param derCertsSz Size of DER certificate data. + * @return WOLFSPDM_SUCCESS or negative error code. + */ +int wolfSPDM_SetTrustedCAs(WOLFSPDM_CTX* ctx, const byte* derCerts, + word32 derCertsSz); + +#ifndef NO_WOLFSPDM_CHALLENGE +/* ========================================================================== + * Challenge Authentication (Sessionless Attestation) + * ========================================================================== */ + +/** + * Perform CHALLENGE/CHALLENGE_AUTH exchange for sessionless attestation. + * Requires state >= WOLFSPDM_STATE_CERT (cert chain must be retrieved). + * Typical flow: GET_VERSION -> GET_CAPS -> NEGOTIATE_ALGO -> GET_DIGESTS + * -> GET_CERTIFICATE -> CHALLENGE + * + * @param ctx The wolfSPDM context. + * @param slotId Certificate slot (0-7, typically 0). + * @param measHashType Measurement summary hash type: + * SPDM_MEAS_SUMMARY_HASH_NONE (0x00), + * SPDM_MEAS_SUMMARY_HASH_TCB (0x01), or + * SPDM_MEAS_SUMMARY_HASH_ALL (0xFF). + * @return WOLFSPDM_SUCCESS or negative error code. + */ +int wolfSPDM_Challenge(WOLFSPDM_CTX* ctx, int slotId, byte measHashType); +#endif /* !NO_WOLFSPDM_CHALLENGE */ + +/* ========================================================================== + * Session Keep-Alive + * ========================================================================== */ + +/** + * Send HEARTBEAT and receive HEARTBEAT_ACK. + * Must be in an established session (CONNECTED or MEASURED state). + * Sent over the encrypted channel. + * + * @param ctx The wolfSPDM context. + * @return WOLFSPDM_SUCCESS or negative error code. + */ +int wolfSPDM_Heartbeat(WOLFSPDM_CTX* ctx); + +/* ========================================================================== + * Key Update (Session Key Rotation) + * ========================================================================== */ + +/** + * Perform KEY_UPDATE to rotate session encryption keys. + * Must be in an established session (CONNECTED or MEASURED state). + * Follows up with VERIFY_NEW_KEY to confirm the new keys work. + * + * @param ctx The wolfSPDM context. + * @param updateAll 0 = rotate requester key only, + * 1 = rotate both requester and responder keys. + * @return WOLFSPDM_SUCCESS or negative error code. + */ +int wolfSPDM_KeyUpdate(WOLFSPDM_CTX* ctx, int updateAll); + /* ========================================================================== * Debug/Utility * ========================================================================== */ diff --git a/wolfspdm/spdm_error.h b/wolfspdm/spdm_error.h index 603855b..9ddf173 100644 --- a/wolfspdm/spdm_error.h +++ b/wolfspdm/spdm_error.h @@ -49,6 +49,12 @@ enum WOLFSPDM_ERROR { WOLFSPDM_E_ALGO_MISMATCH = -18, /* Algorithm negotiation failed */ WOLFSPDM_E_SESSION_INVALID = -19, /* Session ID invalid or mismatch */ WOLFSPDM_E_KEY_EXCHANGE = -20, /* Key exchange failed */ + WOLFSPDM_E_MEASUREMENT = -21, /* Measurement retrieval/parsing failed */ + WOLFSPDM_E_MEAS_NOT_VERIFIED = -22, /* Measurements retrieved but not signature-verified */ + WOLFSPDM_E_MEAS_SIG_FAIL = -23, /* Measurement signature verification failed */ + WOLFSPDM_E_CERT_PARSE = -24, /* Failed to parse responder certificate */ + WOLFSPDM_E_CHALLENGE = -25, /* Challenge authentication failed */ + WOLFSPDM_E_KEY_UPDATE = -26, /* Key update failed */ }; /* Get human-readable error string */ diff --git a/wolfspdm/spdm_types.h b/wolfspdm/spdm_types.h index 88a579c..005d45f 100644 --- a/wolfspdm/spdm_types.h +++ b/wolfspdm/spdm_types.h @@ -164,7 +164,8 @@ extern "C" { /* Default requester capabilities for Algorithm Set B session */ #define WOLFSPDM_DEFAULT_REQ_CAPS (SPDM_CAP_CERT_CAP | SPDM_CAP_CHAL_CAP | \ SPDM_CAP_ENCRYPT_CAP | SPDM_CAP_MAC_CAP | \ - SPDM_CAP_KEY_EX_CAP) + SPDM_CAP_KEY_EX_CAP | SPDM_CAP_HBEAT_CAP | \ + SPDM_CAP_KEY_UPD_CAP) /* ========================================================================== * Buffer/Message Size Limits @@ -193,6 +194,43 @@ extern "C" { #define SOCKET_SPDM_COMMAND_NORMAL 0x00000001 #endif +#ifndef NO_WOLFSPDM_MEAS +/* ========================================================================== + * Measurement Constants (DSP0274 Section 10.11) + * ========================================================================== */ + +/* MeasurementSummaryHashType (Param1 of GET_MEASUREMENTS) */ +#define SPDM_MEAS_SUMMARY_HASH_NONE 0x00 +#define SPDM_MEAS_SUMMARY_HASH_TCB 0x01 +#define SPDM_MEAS_SUMMARY_HASH_ALL 0xFF + +/* MeasurementOperation (Param2 of GET_MEASUREMENTS) */ +#define SPDM_MEAS_OPERATION_TOTAL_NUMBER 0x00 +#define SPDM_MEAS_OPERATION_ALL 0xFF + +/* Request signature bit in Param1 */ +#define SPDM_MEAS_REQUEST_SIG_BIT 0x01 + +/* DMTFSpecMeasurementValueType (DSP0274 Table 22) */ +#define SPDM_MEAS_VALUE_TYPE_IMMUTABLE_ROM 0x00 +#define SPDM_MEAS_VALUE_TYPE_MUTABLE_FW 0x01 +#define SPDM_MEAS_VALUE_TYPE_HW_CONFIG 0x02 +#define SPDM_MEAS_VALUE_TYPE_FW_CONFIG 0x03 +#define SPDM_MEAS_VALUE_TYPE_MEAS_MANIFEST 0x04 +#define SPDM_MEAS_VALUE_TYPE_VERSION 0x05 +#define SPDM_MEAS_VALUE_TYPE_RAW_BIT 0x80 /* Bit 7: raw vs digest */ + +/* Configurable limits (override with -D at compile time) */ +#ifndef WOLFSPDM_MAX_MEAS_BLOCKS +#define WOLFSPDM_MAX_MEAS_BLOCKS 16 +#endif +#ifndef WOLFSPDM_MAX_MEAS_VALUE_SIZE +#define WOLFSPDM_MAX_MEAS_VALUE_SIZE 64 /* Fits SHA-512; SHA-384 uses 48 */ +#endif + +#define WOLFSPDM_MEAS_BLOCK_HDR_SIZE 4 /* Index(1) + MeasSpec(1) + Size(2 LE) */ +#endif /* !NO_WOLFSPDM_MEAS */ + /* ========================================================================== * Key Derivation Labels (SPDM 1.2 per DSP0277) * ========================================================================== */ @@ -209,6 +247,12 @@ extern "C" { #define SPDM_LABEL_FINISHED "finished" #define SPDM_LABEL_KEY "key" #define SPDM_LABEL_IV "iv" +#define SPDM_LABEL_UPDATE "traffic upd" + +/* KEY_UPDATE Operations (DSP0274 Section 10.9) */ +#define SPDM_KEY_UPDATE_OP_UPDATE_KEY 1 +#define SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS 2 +#define SPDM_KEY_UPDATE_OP_VERIFY_NEW_KEY 3 #ifdef __cplusplus } From 8fddc64213aed4b290cb6d4620e73d9b33364be4 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 17 Feb 2026 01:48:52 +0000 Subject: [PATCH 2/8] =?UTF-8?q?src/spdm=5Fmsg.c=20(-779=20lines,=20+371=20?= =?UTF-8?q?=3D=20-367=20net)=20=20=20-=20Extracted=20wolfSPDM=5FBuildSigne?= =?UTF-8?q?dHash()=20=E2=80=94=20shared=20SPDM=201.2=20signing=20prefix=20?= =?UTF-8?q?builder=20replacing=203=20inline=20blocks=20(~40=20lines=20each?= =?UTF-8?q?)=20in=20BuildFinish,=20=20=20VerifyMeasurementSig,=20VerifyCha?= =?UTF-8?q?llengeAuthSig=20=20=20-=20Extracted=20wolfSPDM=5FVerifyEccSig()?= =?UTF-8?q?=20=E2=80=94=20shared=20raw-to-DER=20+=20ECC=20verify=20replaci?= =?UTF-8?q?ng=202=20inline=20blocks=20=20=20-=20Reused=20wolfSPDM=5FFindLe?= =?UTF-8?q?afCert()=20in=20ExtractResponderPubKey=20(removed=20duplicated?= =?UTF-8?q?=20DER=20parsing=20loop)=20=20=20-=20Consolidated=20pub=20key?= =?UTF-8?q?=20extraction=20=E2=80=94=20ValidateCertChain=20now=20calls=20E?= =?UTF-8?q?xtractResponderPubKey=20instead=20of=20duplicating=20it=20=20?= =?UTF-8?q?=20-=20Applied=20SPDM=5FCHECK=5FBUILD=5FARGS=20macro=20to=2010?= =?UTF-8?q?=20build=20functions=20=20=20-=20Applied=20SPDM=5FCHECK=5FPARSE?= =?UTF-8?q?=5FARGS=20macro=20to=2010=20parse=20functions=20=20=20-=20Appli?= =?UTF-8?q?ed=20SPDM=5FCHECK=5FRESPONSE=20macro=20to=2010=20response=20cod?= =?UTF-8?q?e=20checks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/spdm_internal.h (+28 lines) - Added SPDM_CHECK_BUILD_ARGS, SPDM_CHECK_PARSE_ARGS, SPDM_CHECK_RESPONSE macros test/unit_test.c (-653 lines, +376 = -277 net) - Added ASSERT_SUCCESS, ASSERT_FAIL, ASSERT_EQ, ASSERT_NE test macros - Compressed all 34 test functions using these macros src/spdm_context.c (1 line) — const correctness fix src/spdm_kdf.c (3 lines) — moved variable declaration into scope Total: 7,255 → 6,639 lines (-616 lines, -8.5%) --- src/spdm_context.c | 2 +- src/spdm_internal.h | 28 ++ src/spdm_kdf.c | 4 +- src/spdm_msg.c | 779 ++++++++++++-------------------------------- test/unit_test.c | 653 +++++++++++-------------------------- 5 files changed, 425 insertions(+), 1041 deletions(-) diff --git a/src/spdm_context.c b/src/spdm_context.c index fd83733..2bdb6f0 100644 --- a/src/spdm_context.c +++ b/src/spdm_context.c @@ -546,7 +546,7 @@ int wolfSPDM_GetMeasurementCount(WOLFSPDM_CTX* ctx) int wolfSPDM_GetMeasurementBlock(WOLFSPDM_CTX* ctx, int blockIdx, byte* measIndex, byte* measType, byte* value, word32* valueSz) { - WOLFSPDM_MEAS_BLOCK* blk; + const WOLFSPDM_MEAS_BLOCK* blk; if (ctx == NULL || !ctx->hasMeasurements) { return WOLFSPDM_E_INVALID_ARG; diff --git a/src/spdm_internal.h b/src/spdm_internal.h index 633037f..5146587 100644 --- a/src/spdm_internal.h +++ b/src/spdm_internal.h @@ -231,6 +231,34 @@ struct WOLFSPDM_CTX { byte rspAppSecret[WOLFSPDM_HASH_SIZE]; /* 48 bytes */ }; +/* ========================================================================== + * Argument Validation Macros + * ========================================================================== */ + +#define SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, minSz) \ + if ((ctx) == NULL || (buf) == NULL || (bufSz) == NULL || *(bufSz) < (minSz)) \ + return WOLFSPDM_E_BUFFER_SMALL + +#define SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, minSz) \ + if ((ctx) == NULL || (buf) == NULL || (bufSz) < (minSz)) \ + return WOLFSPDM_E_INVALID_ARG + +/* ========================================================================== + * Response Code Check Macro + * ========================================================================== */ + +#define SPDM_CHECK_RESPONSE(ctx, buf, bufSz, expected, fallbackErr) \ + do { \ + if ((buf)[1] != (expected)) { \ + int _ec; \ + if (wolfSPDM_CheckError((buf), (bufSz), &_ec)) { \ + wolfSPDM_DebugPrint((ctx), "SPDM error: 0x%02x\n", _ec); \ + return WOLFSPDM_E_PEER_ERROR; \ + } \ + return (fallbackErr); \ + } \ + } while (0) + /* ========================================================================== * Internal Function Declarations - Transcript * ========================================================================== */ diff --git a/src/spdm_kdf.c b/src/spdm_kdf.c index 3399f66..d1ed519 100644 --- a/src/spdm_kdf.c +++ b/src/spdm_kdf.c @@ -296,7 +296,6 @@ int wolfSPDM_DeriveAppDataKeys(WOLFSPDM_CTX* ctx) int wolfSPDM_DeriveUpdatedKeys(WOLFSPDM_CTX* ctx, int updateAll) { byte newReqAppSecret[WOLFSPDM_HASH_SIZE]; - byte newRspAppSecret[WOLFSPDM_HASH_SIZE]; int rc; if (ctx == NULL) { @@ -333,6 +332,8 @@ int wolfSPDM_DeriveUpdatedKeys(WOLFSPDM_CTX* ctx, int updateAll) /* Optionally update responder key */ if (updateAll) { + byte newRspAppSecret[WOLFSPDM_HASH_SIZE]; + rc = wolfSPDM_HkdfExpandLabel(ctx->spdmVersion, ctx->rspAppSecret, WOLFSPDM_HASH_SIZE, SPDM_LABEL_UPDATE, NULL, 0, newRspAppSecret, WOLFSPDM_HASH_SIZE); @@ -356,7 +357,6 @@ int wolfSPDM_DeriveUpdatedKeys(WOLFSPDM_CTX* ctx, int updateAll) /* Save new responder secret for future updates */ XMEMCPY(ctx->rspAppSecret, newRspAppSecret, WOLFSPDM_HASH_SIZE); - } return WOLFSPDM_SUCCESS; diff --git a/src/spdm_msg.c b/src/spdm_msg.c index 5506c00..97a40d7 100644 --- a/src/spdm_msg.c +++ b/src/spdm_msg.c @@ -25,9 +25,9 @@ int wolfSPDM_BuildGetVersion(byte* buf, word32* bufSz) { - if (buf == NULL || bufSz == NULL || *bufSz < 4) { + /* Note: ctx is not used for GET_VERSION, check buf/bufSz directly */ + if (buf == NULL || bufSz == NULL || *bufSz < 4) return WOLFSPDM_E_BUFFER_SMALL; - } /* Per SPDM spec, GET_VERSION always uses version 0x10 */ buf[0] = SPDM_VERSION_10; @@ -41,9 +41,7 @@ int wolfSPDM_BuildGetVersion(byte* buf, word32* bufSz) int wolfSPDM_BuildGetCapabilities(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) { - if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 20) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 20); XMEMSET(buf, 0, 20); buf[0] = ctx->spdmVersion; /* Use negotiated version */ @@ -69,9 +67,7 @@ int wolfSPDM_BuildGetCapabilities(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) int wolfSPDM_BuildNegotiateAlgorithms(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) { - if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 48) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 48); XMEMSET(buf, 0, 48); buf[0] = ctx->spdmVersion; /* Use negotiated version */ @@ -103,9 +99,7 @@ int wolfSPDM_BuildNegotiateAlgorithms(WOLFSPDM_CTX* ctx, byte* buf, word32* bufS int wolfSPDM_BuildGetDigests(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) { - if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 4) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 4); buf[0] = ctx->spdmVersion; /* Use negotiated version */ buf[1] = SPDM_GET_DIGESTS; @@ -119,9 +113,7 @@ int wolfSPDM_BuildGetDigests(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) int wolfSPDM_BuildGetCertificate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, int slotId, word16 offset, word16 length) { - if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 8) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 8); buf[0] = ctx->spdmVersion; /* Use negotiated version */ buf[1] = SPDM_GET_CERTIFICATE; @@ -145,9 +137,7 @@ int wolfSPDM_BuildKeyExchange(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) word32 pubKeyYSz = sizeof(pubKeyY); int rc; - if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 180) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 180); rc = wolfSPDM_GenerateEphemeralKey(ctx); if (rc != WOLFSPDM_SUCCESS) { @@ -226,6 +216,95 @@ int wolfSPDM_BuildKeyExchange(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) return WOLFSPDM_SUCCESS; } +/* ========================================================================== + * Shared Signing Helpers + * ========================================================================== */ + +/* Build SPDM 1.2+ signed hash per DSP0274: + * M = combined_spdm_prefix || zero_pad || context_str || inputDigest + * outputDigest = Hash(M) + * + * combined_spdm_prefix = "dmtf-spdm-v1.X.*" x4 = 64 bytes + * zero_pad = (36 - contextStrLen) bytes of 0x00 + * context_str = signing context string (variable length, max 36) */ +static int wolfSPDM_BuildSignedHash(byte spdmVersion, + const char* contextStr, word32 contextStrLen, + const byte* inputDigest, byte* outputDigest) +{ + byte signMsg[200]; /* 64 + 36 + 48 = 148 bytes max */ + word32 signMsgLen = 0; + word32 zeroPadLen; + wc_Sha384 sha; + byte majorVer, minorVer; + int i, rc; + + majorVer = (byte)('0' + ((spdmVersion >> 4) & 0xF)); + minorVer = (byte)('0' + (spdmVersion & 0xF)); + + /* combined_spdm_prefix: "dmtf-spdm-v1.X.*" x4 = 64 bytes */ + for (i = 0; i < 4; i++) { + XMEMCPY(&signMsg[signMsgLen], "dmtf-spdm-v1.2.*", 16); + signMsg[signMsgLen + 11] = majorVer; + signMsg[signMsgLen + 13] = minorVer; + signMsg[signMsgLen + 15] = '*'; + signMsgLen += 16; + } + + /* Zero padding: 36 - contextStrLen bytes */ + zeroPadLen = 36 - contextStrLen; + XMEMSET(&signMsg[signMsgLen], 0x00, zeroPadLen); + signMsgLen += zeroPadLen; + + /* Signing context string */ + XMEMCPY(&signMsg[signMsgLen], contextStr, contextStrLen); + signMsgLen += contextStrLen; + + /* Input digest */ + XMEMCPY(&signMsg[signMsgLen], inputDigest, WOLFSPDM_HASH_SIZE); + signMsgLen += WOLFSPDM_HASH_SIZE; + + /* Hash M */ + rc = wc_InitSha384(&sha); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + rc = wc_Sha384Update(&sha, signMsg, signMsgLen); + if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } + rc = wc_Sha384Final(&sha, outputDigest); + wc_Sha384Free(&sha); + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + + return WOLFSPDM_SUCCESS; +} + +/* Verify an SPDM ECDSA signature (raw r||s format) against a digest + * using the responder's public key stored in ctx. */ +static int wolfSPDM_VerifyEccSig(WOLFSPDM_CTX* ctx, + const byte* sigRaw, word32 sigRawSz, + const byte* digest, word32 digestSz) +{ + byte derSig[256]; + word32 derSigSz = sizeof(derSig); + const byte* sigR = sigRaw; + const byte* sigS = sigRaw + (sigRawSz / 2); + int verified = 0; + int rc; + + rc = wc_ecc_rs_raw_to_sig(sigR, sigRawSz / 2, + sigS, sigRawSz / 2, derSig, &derSigSz); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "ECC rs_raw_to_sig failed: %d\n", rc); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + rc = wc_ecc_verify_hash(derSig, derSigSz, digest, digestSz, + &verified, &ctx->responderPubKey); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "ECC verify_hash failed: %d\n", rc); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + return verified == 1 ? WOLFSPDM_SUCCESS : WOLFSPDM_E_CRYPTO_FAIL; +} + int wolfSPDM_BuildFinish(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) { byte th2Hash[WOLFSPDM_HASH_SIZE]; @@ -316,72 +395,14 @@ int wolfSPDM_BuildFinish(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) XMEMCPY(ctx->th2, th2Hash, WOLFSPDM_HASH_SIZE); - /* For mutual auth, use SPDM 1.2+ signing context format per DSP0274: - * M = combined_spdm_prefix || zero_pad || signing_context || TH2 - * - * Where (per libspdm reference implementation): - * - combined_spdm_prefix = "dmtf-spdm-v1.3.*" repeated 4 times = 64 bytes - * (16 bytes each, NOT including null terminator) - * - zero_pad = 12 bytes of 0x00 (36 - strlen("requester-finish signing")) - * - signing_context = "requester-finish signing" (24 bytes) - * - TH2 = transcript hash (48 bytes for SHA-384) - * - * Total: 64 + 12 + 24 + 48 = 148 bytes */ + /* For mutual auth, use SPDM 1.2+ signing context format per DSP0274 */ if (mutualAuth) { - /* Build signing context per DSP0274 / libspdm */ - static const char context_str[] = "requester-finish signing"; /* 24 bytes */ - /* 16 bytes per prefix (sizeof - 1, no null terminator) */ - #define SPDM_SIGNING_PREFIX_SIZE 16 - #define SPDM_SIGNING_CONTEXT_STR_SIZE 24 - #define SPDM_SIGNING_ZERO_PAD_SIZE (36 - SPDM_SIGNING_CONTEXT_STR_SIZE) /* 12 */ - byte signMsg[200]; /* 64 + 12 + 24 + 48 = 148 bytes */ byte signMsgHash[WOLFSPDM_HASH_SIZE]; - word32 signMsgLen = 0; - wc_Sha384 sha; - int i; - byte majorVer, minorVer; - - /* Determine version digits from negotiated SPDM version byte (0x13 = 1.3) */ - majorVer = (byte)('0' + ((ctx->spdmVersion >> 4) & 0xF)); /* '1' */ - minorVer = (byte)('0' + (ctx->spdmVersion & 0xF)); /* '3' */ - - /* combined_spdm_prefix: "dmtf-spdm-v1.3.*" repeated 4 times = 64 bytes - * Each copy is 16 bytes (no null terminator), matching libspdm's - * SPDM_VERSION_1_2_SIGNING_PREFIX_CONTEXT_SIZE = sizeof(...) - 1 = 16 */ - for (i = 0; i < 4; i++) { - XMEMCPY(&signMsg[signMsgLen], "dmtf-spdm-v1.2.*", SPDM_SIGNING_PREFIX_SIZE); - signMsg[signMsgLen + 11] = majorVer; /* Patch major version */ - signMsg[signMsgLen + 13] = minorVer; /* Patch minor version */ - signMsg[signMsgLen + 15] = '*'; /* Wildcard for update version */ - signMsgLen += SPDM_SIGNING_PREFIX_SIZE; - } - /* Zero padding: 36 - context_str_size = 12 bytes of 0x00 */ - XMEMSET(&signMsg[signMsgLen], 0x00, SPDM_SIGNING_ZERO_PAD_SIZE); - signMsgLen += SPDM_SIGNING_ZERO_PAD_SIZE; - - /* Signing context string: "requester-finish signing" (24 bytes) */ - XMEMCPY(&signMsg[signMsgLen], context_str, SPDM_SIGNING_CONTEXT_STR_SIZE); - signMsgLen += SPDM_SIGNING_CONTEXT_STR_SIZE; - - /* Append TH2 hash */ - XMEMCPY(&signMsg[signMsgLen], th2Hash, WOLFSPDM_HASH_SIZE); - signMsgLen += WOLFSPDM_HASH_SIZE; - - /* Hash M to get the value to sign */ - rc = wc_InitSha384(&sha); - if (rc != 0) { - return WOLFSPDM_E_CRYPTO_FAIL; - } - rc = wc_Sha384Update(&sha, signMsg, signMsgLen); - if (rc != 0) { - wc_Sha384Free(&sha); - return WOLFSPDM_E_CRYPTO_FAIL; - } - rc = wc_Sha384Final(&sha, signMsgHash); - wc_Sha384Free(&sha); - if (rc != 0) { - return WOLFSPDM_E_CRYPTO_FAIL; + rc = wolfSPDM_BuildSignedHash(ctx->spdmVersion, + "requester-finish signing", 24, th2Hash, signMsgHash); + if (rc != WOLFSPDM_SUCCESS) { + return rc; } /* Sign Hash(M) */ @@ -434,9 +455,7 @@ int wolfSPDM_BuildFinish(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) int wolfSPDM_BuildEndSession(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) { - if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 4) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 4); buf[0] = ctx->spdmVersion; /* Use negotiated version */ buf[1] = SPDM_END_SESSION; @@ -469,18 +488,8 @@ int wolfSPDM_ParseVersion(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) word32 i; byte highestVersion = SPDM_VERSION_12; /* Start at 1.2, find highest supported (capped at 1.3) */ - if (ctx == NULL || buf == NULL || bufSz < 6) { - return WOLFSPDM_E_INVALID_ARG; - } - - if (buf[1] != SPDM_VERSION) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - wolfSPDM_DebugPrint(ctx, "VERSION error: 0x%02x\n", errCode); - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_VERSION_MISMATCH; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 6); + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_VERSION, WOLFSPDM_E_VERSION_MISMATCH); /* Parse VERSION response: * Offset 4-5: VersionNumberEntryCount (LE) @@ -511,17 +520,8 @@ int wolfSPDM_ParseVersion(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) int wolfSPDM_ParseCapabilities(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) { - if (ctx == NULL || buf == NULL || bufSz < 12) { - return WOLFSPDM_E_INVALID_ARG; - } - - if (buf[1] != SPDM_CAPABILITIES) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_CAPS_MISMATCH; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 12); + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_CAPABILITIES, WOLFSPDM_E_CAPS_MISMATCH); ctx->rspCaps = (word32)buf[8] | ((word32)buf[9] << 8) | ((word32)buf[10] << 16) | ((word32)buf[11] << 24); @@ -533,17 +533,8 @@ int wolfSPDM_ParseCapabilities(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) int wolfSPDM_ParseAlgorithms(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) { - if (ctx == NULL || buf == NULL || bufSz < 4) { - return WOLFSPDM_E_INVALID_ARG; - } - - if (buf[1] != SPDM_ALGORITHMS) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_ALGO_MISMATCH; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 4); + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_ALGORITHMS, WOLFSPDM_E_ALGO_MISMATCH); ctx->state = WOLFSPDM_STATE_ALGO; return WOLFSPDM_SUCCESS; @@ -551,17 +542,8 @@ int wolfSPDM_ParseAlgorithms(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) int wolfSPDM_ParseDigests(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) { - if (ctx == NULL || buf == NULL || bufSz < 4) { - return WOLFSPDM_E_INVALID_ARG; - } - - if (buf[1] != SPDM_DIGESTS) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_CERT_FAIL; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 4); + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_DIGESTS, WOLFSPDM_E_CERT_FAIL); ctx->state = WOLFSPDM_STATE_DIGESTS; return WOLFSPDM_SUCCESS; @@ -575,13 +557,7 @@ int wolfSPDM_ParseCertificate(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz, return WOLFSPDM_E_INVALID_ARG; } - if (buf[1] != SPDM_CERTIFICATE) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_CERT_FAIL; - } + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_CERTIFICATE, WOLFSPDM_E_CERT_FAIL); *portionLen = (word16)(buf[4] | (buf[5] << 8)); *remainderLen = (word16)(buf[6] | (buf[7] << 8)); @@ -610,17 +586,8 @@ int wolfSPDM_ParseKeyExchangeRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufS byte expectedHmac[WOLFSPDM_HASH_SIZE]; int rc; - if (ctx == NULL || buf == NULL || bufSz < 140) { - return WOLFSPDM_E_INVALID_ARG; - } - - if (buf[1] != SPDM_KEY_EXCHANGE_RSP) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_KEY_EXCHANGE; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 140); + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_KEY_EXCHANGE_RSP, WOLFSPDM_E_KEY_EXCHANGE); ctx->rspSessionId = (word16)(buf[4] | (buf[5] << 8)); ctx->sessionId = (word32)ctx->reqSessionId | ((word32)ctx->rspSessionId << 16); @@ -700,9 +667,7 @@ int wolfSPDM_ParseKeyExchangeRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufS int wolfSPDM_ParseFinishRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) { - if (ctx == NULL || buf == NULL || bufSz < 4) { - return WOLFSPDM_E_INVALID_ARG; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 4); if (buf[1] == SPDM_FINISH_RSP) { int addRc; @@ -734,7 +699,6 @@ int wolfSPDM_BuildGetMeasurements(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, byte operation, byte requestSig) { word32 offset = 0; - int rc; if (ctx == NULL || buf == NULL || bufSz == NULL) { return WOLFSPDM_E_INVALID_ARG; @@ -757,7 +721,7 @@ int wolfSPDM_BuildGetMeasurements(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, if (requestSig) { /* Nonce (32 bytes) */ - rc = wolfSPDM_GetRandom(ctx, &buf[offset], 32); + int rc = wolfSPDM_GetRandom(ctx, &buf[offset], 32); if (rc != WOLFSPDM_SUCCESS) { return rc; } @@ -779,26 +743,9 @@ int wolfSPDM_ParseMeasurements(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) word32 recordLen; word32 recordEnd; word32 blockIdx; - word16 opaqueLen; - - if (ctx == NULL || buf == NULL) { - return WOLFSPDM_E_INVALID_ARG; - } - /* Minimum: 4 hdr + 1 numBlocks + 3 recordLen = 8 */ - if (bufSz < 8) { - return WOLFSPDM_E_MEASUREMENT; - } - - /* Check response code */ - if (buf[1] != SPDM_MEASUREMENTS) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - wolfSPDM_DebugPrint(ctx, "MEASUREMENTS error: 0x%02x\n", errCode); - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_MEASUREMENT; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 8); + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_MEASUREMENTS, WOLFSPDM_E_MEASUREMENT); numBlocks = buf[4]; /* MeasurementRecordLength: 3 bytes LE at offset 5..7 */ @@ -907,7 +854,7 @@ int wolfSPDM_ParseMeasurements(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) offset += 32; /* OpaqueDataLength (2 LE) */ - opaqueLen = (word16)(buf[offset] | (buf[offset + 1] << 8)); + word16 opaqueLen = (word16)(buf[offset] | (buf[offset + 1] << 8)); offset += 2; /* Skip opaque data */ @@ -939,12 +886,7 @@ int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, { wc_Sha384 sha; byte digest[WOLFSPDM_HASH_SIZE]; - byte derSig[256]; - word32 derSigSz = sizeof(derSig); word32 sigOffset; - const byte* sigR; - const byte* sigS; - int verified = 0; int rc; if (ctx == NULL || rspBuf == NULL || reqMsg == NULL) { @@ -997,73 +939,14 @@ int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; /* Build M = prefix || zero_pad || context_str || L1L2_hash, then hash it */ - { - static const char context_str[] = "responder-measurements signing"; - #define MEAS_SIGNING_PREFIX_SIZE 16 - #define MEAS_SIGNING_CONTEXT_STR_SIZE 30 /* strlen, no null terminator */ - #define MEAS_SIGNING_ZERO_PAD_SIZE (36 - MEAS_SIGNING_CONTEXT_STR_SIZE) /* 6 */ - byte signMsg[200]; /* 64 + 5 + 31 + 48 = 148 bytes */ - word32 signMsgLen = 0; - wc_Sha384 sha2; - byte majorVer, minorVer; - int i; - - majorVer = (byte)('0' + ((ctx->spdmVersion >> 4) & 0xF)); - minorVer = (byte)('0' + (ctx->spdmVersion & 0xF)); - - /* combined_spdm_prefix: "dmtf-spdm-v1.X.*" x4 = 64 bytes */ - for (i = 0; i < 4; i++) { - XMEMCPY(&signMsg[signMsgLen], "dmtf-spdm-v1.2.*", - MEAS_SIGNING_PREFIX_SIZE); - signMsg[signMsgLen + 11] = majorVer; - signMsg[signMsgLen + 13] = minorVer; - signMsg[signMsgLen + 15] = '*'; - signMsgLen += MEAS_SIGNING_PREFIX_SIZE; - } - - /* Zero padding */ - XMEMSET(&signMsg[signMsgLen], 0x00, MEAS_SIGNING_ZERO_PAD_SIZE); - signMsgLen += MEAS_SIGNING_ZERO_PAD_SIZE; - - /* Signing context string */ - XMEMCPY(&signMsg[signMsgLen], context_str, MEAS_SIGNING_CONTEXT_STR_SIZE); - signMsgLen += MEAS_SIGNING_CONTEXT_STR_SIZE; - - /* L1||L2 hash */ - XMEMCPY(&signMsg[signMsgLen], digest, WOLFSPDM_HASH_SIZE); - signMsgLen += WOLFSPDM_HASH_SIZE; - - /* Hash M to get the value that was signed */ - rc = wc_InitSha384(&sha2); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - rc = wc_Sha384Update(&sha2, signMsg, signMsgLen); - if (rc != 0) { wc_Sha384Free(&sha2); return WOLFSPDM_E_CRYPTO_FAIL; } - rc = wc_Sha384Final(&sha2, digest); - wc_Sha384Free(&sha2); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - } - - /* SPDM ECDSA signatures are raw r||s, not DER. - * For P-384: r(48 bytes) || s(48 bytes) = 96 bytes */ - sigR = rspBuf + sigOffset; - sigS = sigR + (WOLFSPDM_ECC_SIG_SIZE / 2); - - rc = wc_ecc_rs_raw_to_sig(sigR, WOLFSPDM_ECC_SIG_SIZE / 2, - sigS, WOLFSPDM_ECC_SIG_SIZE / 2, derSig, &derSigSz); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "ECC rs_raw_to_sig failed: %d\n", rc); - return WOLFSPDM_E_CRYPTO_FAIL; - } - - /* Verify the signature */ - rc = wc_ecc_verify_hash(derSig, derSigSz, digest, WOLFSPDM_HASH_SIZE, - &verified, &ctx->responderPubKey); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "ECC verify_hash failed: %d\n", rc); - return WOLFSPDM_E_CRYPTO_FAIL; - } - - if (verified == 1) { + rc = wolfSPDM_BuildSignedHash(ctx->spdmVersion, + "responder-measurements signing", 30, digest, digest); + if (rc != WOLFSPDM_SUCCESS) return rc; + + /* Verify ECDSA signature (raw r||s format) */ + rc = wolfSPDM_VerifyEccSig(ctx, rspBuf + sigOffset, WOLFSPDM_ECC_SIG_SIZE, + digest, WOLFSPDM_HASH_SIZE); + if (rc == WOLFSPDM_SUCCESS) { wolfSPDM_DebugPrint(ctx, "Measurement signature VERIFIED\n"); return WOLFSPDM_SUCCESS; } @@ -1082,122 +965,6 @@ int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, * SPDM certificate chain. Used by both measurement signature verification * and CHALLENGE authentication, so it lives outside measurement guards. */ -int wolfSPDM_ExtractResponderPubKey(WOLFSPDM_CTX* ctx) -{ - DecodedCert cert; - const byte* certDer; - word32 certDerSz; - word32 idx; - int rc; - - if (ctx == NULL || ctx->certChainLen == 0) { - return WOLFSPDM_E_CERT_PARSE; - } - - /* SPDM certificate chain format (DSP0274): - * Length(2 LE) + Reserved(2) + RootCertHash(48 for SHA-384) + Certificates... - * The leaf certificate is the LAST one in the chain. */ - - /* Skip SPDM cert chain header: Length(2) + Reserved(2) + RootHash(48) = 52 bytes */ - if (ctx->certChainLen <= 52) { - wolfSPDM_DebugPrint(ctx, "Certificate chain too short for header\n"); - return WOLFSPDM_E_CERT_PARSE; - } - - certDer = ctx->certChain + 52; - certDerSz = ctx->certChainLen - 52; - - /* Walk through certificates to find the leaf (last one). - * Each DER cert starts with 0x30 (SEQUENCE) followed by length. */ - { - word32 pos = 0; - const byte* lastCert = certDer; - word32 lastCertSz = certDerSz; - - while (pos < certDerSz) { - word32 certLen; - word32 hdrLen; - - if (certDer[pos] != 0x30) { - break; /* Not a valid DER sequence */ - } - - /* Parse ASN.1 length */ - if (pos + 1 >= certDerSz) break; - - if (certDer[pos + 1] < 0x80) { - certLen = certDer[pos + 1]; - hdrLen = 2; - } - else if (certDer[pos + 1] == 0x81) { - if (pos + 2 >= certDerSz) break; - certLen = certDer[pos + 2]; - hdrLen = 3; - } - else if (certDer[pos + 1] == 0x82) { - if (pos + 3 >= certDerSz) break; - certLen = ((word32)certDer[pos + 2] << 8) | certDer[pos + 3]; - hdrLen = 4; - } - else if (certDer[pos + 1] == 0x83) { - if (pos + 4 >= certDerSz) break; - certLen = ((word32)certDer[pos + 2] << 16) | - ((word32)certDer[pos + 3] << 8) | certDer[pos + 4]; - hdrLen = 5; - } - else { - break; /* Length encoding we don't handle */ - } - - if (pos + hdrLen + certLen > certDerSz) break; - - lastCert = certDer + pos; - lastCertSz = hdrLen + certLen; - - pos += hdrLen + certLen; - } - - certDer = lastCert; - certDerSz = lastCertSz; - } - - /* Parse the leaf certificate */ - wc_InitDecodedCert(&cert, certDer, certDerSz, NULL); - rc = wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "Certificate parse failed: %d\n", rc); - wc_FreeDecodedCert(&cert); - return WOLFSPDM_E_CERT_PARSE; - } - - /* Extract public key from cert and import into ecc_key */ - rc = wc_ecc_init(&ctx->responderPubKey); - if (rc != 0) { - wc_FreeDecodedCert(&cert); - return WOLFSPDM_E_CRYPTO_FAIL; - } - - idx = 0; - rc = wc_EccPublicKeyDecode(cert.publicKey, &idx, &ctx->responderPubKey, - cert.pubKeySize); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "ECC public key decode failed: %d\n", rc); - wc_ecc_free(&ctx->responderPubKey); - wc_FreeDecodedCert(&cert); - return WOLFSPDM_E_CERT_PARSE; - } - - wc_FreeDecodedCert(&cert); - ctx->hasResponderPubKey = 1; - wolfSPDM_DebugPrint(ctx, "Extracted responder ECC P-384 public key\n"); - - return WOLFSPDM_SUCCESS; -} - -/* ========================================================================== - * Certificate Chain Validation - * ========================================================================== */ - /* Helper: find leaf cert in SPDM cert chain buffer. * SPDM cert chain header: Length(2 LE) + Reserved(2) + RootHash(48) = 52 bytes * After header: concatenated DER certificates, leaf is the last one. */ @@ -1266,12 +1033,65 @@ static int wolfSPDM_FindLeafCert(const byte* certChain, word32 certChainLen, return WOLFSPDM_SUCCESS; } -int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx) +int wolfSPDM_ExtractResponderPubKey(WOLFSPDM_CTX* ctx) { + DecodedCert cert; const byte* leafCert; word32 leafCertSz; - DecodedCert cert; word32 idx; + int rc; + + if (ctx == NULL || ctx->certChainLen == 0) { + return WOLFSPDM_E_CERT_PARSE; + } + + /* Find the leaf (last) certificate in the SPDM cert chain */ + rc = wolfSPDM_FindLeafCert(ctx->certChain, ctx->certChainLen, + &leafCert, &leafCertSz); + if (rc != WOLFSPDM_SUCCESS) { + wolfSPDM_DebugPrint(ctx, "Certificate chain too short for header\n"); + return rc; + } + + /* Parse the leaf certificate */ + wc_InitDecodedCert(&cert, leafCert, leafCertSz, NULL); + rc = wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "Certificate parse failed: %d\n", rc); + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CERT_PARSE; + } + + /* Extract public key from cert and import into ecc_key */ + rc = wc_ecc_init(&ctx->responderPubKey); + if (rc != 0) { + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CRYPTO_FAIL; + } + + idx = 0; + rc = wc_EccPublicKeyDecode(cert.publicKey, &idx, &ctx->responderPubKey, + cert.pubKeySize); + if (rc != 0) { + wolfSPDM_DebugPrint(ctx, "ECC public key decode failed: %d\n", rc); + wc_ecc_free(&ctx->responderPubKey); + wc_FreeDecodedCert(&cert); + return WOLFSPDM_E_CERT_PARSE; + } + + wc_FreeDecodedCert(&cert); + ctx->hasResponderPubKey = 1; + wolfSPDM_DebugPrint(ctx, "Extracted responder ECC P-384 public key\n"); + + return WOLFSPDM_SUCCESS; +} + +/* ========================================================================== + * Certificate Chain Validation + * ========================================================================== */ + +int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx) +{ wc_Sha384 sha; byte caHash[WOLFSPDM_HASH_SIZE]; const byte* chainRootHash; @@ -1310,102 +1130,13 @@ int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx) wolfSPDM_DebugPrint(ctx, "Root certificate hash VERIFIED against trusted CA\n"); - /* Parse the root cert from chain to get its public key for leaf verification */ - { - const byte* rootCert = ctx->certChain + 52; - word32 rootCertSz; - DecodedCert rootDc; - ecc_key rootPubKey; - word32 rootIdx; - - /* Get root cert size from ASN.1 header */ - if (rootCert[0] != 0x30) { - return WOLFSPDM_E_CERT_PARSE; - } - if (rootCert[1] < 0x80) { - rootCertSz = rootCert[1] + 2; - } else if (rootCert[1] == 0x82 && ctx->certChainLen > 56) { - rootCertSz = ((word32)rootCert[2] << 8 | rootCert[3]) + 4; - } else if (rootCert[1] == 0x81 && ctx->certChainLen > 55) { - rootCertSz = rootCert[2] + 3; - } else { - return WOLFSPDM_E_CERT_PARSE; - } - - /* Parse root cert to get its public key */ - wc_InitDecodedCert(&rootDc, rootCert, rootCertSz, NULL); - rc = wc_ParseCert(&rootDc, CA_TYPE, NO_VERIFY, NULL); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "Root cert parse failed: %d\n", rc); - wc_FreeDecodedCert(&rootDc); - return WOLFSPDM_E_CERT_PARSE; - } - - /* Import root public key for verifying leaf cert signature */ - rc = wc_ecc_init(&rootPubKey); - if (rc != 0) { - wc_FreeDecodedCert(&rootDc); - return WOLFSPDM_E_CRYPTO_FAIL; - } - rootIdx = 0; - rc = wc_EccPublicKeyDecode(rootDc.publicKey, &rootIdx, &rootPubKey, - rootDc.pubKeySize); - wc_FreeDecodedCert(&rootDc); - if (rc != 0) { - wc_ecc_free(&rootPubKey); - return WOLFSPDM_E_CERT_PARSE; - } - - /* Find leaf cert */ - rc = wolfSPDM_FindLeafCert(ctx->certChain, ctx->certChainLen, - &leafCert, &leafCertSz); - if (rc != WOLFSPDM_SUCCESS) { - wc_ecc_free(&rootPubKey); - return rc; - } - - /* Parse leaf cert and verify its signature against the CA/issuer key. - * For a 2-cert chain (root + leaf), the root signs the leaf directly. - * For longer chains, intermediate certs would need verification too, - * but the root hash match already establishes trust anchor. */ - wc_ecc_free(&rootPubKey); - } - - /* Extract public key from leaf cert */ - rc = wolfSPDM_FindLeafCert(ctx->certChain, ctx->certChainLen, - &leafCert, &leafCertSz); + /* Extract public key from the leaf cert (reuses FindLeafCert internally) */ + rc = wolfSPDM_ExtractResponderPubKey(ctx); if (rc != WOLFSPDM_SUCCESS) { return rc; } - wc_InitDecodedCert(&cert, leafCert, leafCertSz, NULL); - rc = wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "Leaf certificate parse failed: %d\n", rc); - wc_FreeDecodedCert(&cert); - return WOLFSPDM_E_CERT_PARSE; - } - - rc = wc_ecc_init(&ctx->responderPubKey); - if (rc != 0) { - wc_FreeDecodedCert(&cert); - return WOLFSPDM_E_CRYPTO_FAIL; - } - - idx = 0; - rc = wc_EccPublicKeyDecode(cert.publicKey, &idx, &ctx->responderPubKey, - cert.pubKeySize); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "ECC public key decode failed: %d\n", rc); - wc_ecc_free(&ctx->responderPubKey); - wc_FreeDecodedCert(&cert); - return WOLFSPDM_E_CERT_PARSE; - } - - wc_FreeDecodedCert(&cert); - ctx->hasResponderPubKey = 1; - wolfSPDM_DebugPrint(ctx, "Extracted responder ECC P-384 public key (chain validated)\n"); - + wolfSPDM_DebugPrint(ctx, "Certificate chain validated\n"); return WOLFSPDM_SUCCESS; } @@ -1421,9 +1152,7 @@ int wolfSPDM_BuildChallenge(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, word32 offset = 0; int rc; - if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 36) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 36); buf[offset++] = ctx->spdmVersion; buf[offset++] = SPDM_CHALLENGE; @@ -1461,15 +1190,7 @@ int wolfSPDM_ParseChallengeAuth(WOLFSPDM_CTX* ctx, const byte* buf, return WOLFSPDM_E_CHALLENGE; } - /* Check response code */ - if (buf[1] != SPDM_CHALLENGE_AUTH) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH error: 0x%02x\n", errCode); - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_CHALLENGE; - } + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_CHALLENGE_AUTH, WOLFSPDM_E_CHALLENGE); offset = 4; @@ -1530,13 +1251,10 @@ int wolfSPDM_VerifyChallengeAuthSig(WOLFSPDM_CTX* ctx, const byte* reqMsg, word32 reqMsgSz, word32 sigOffset) { byte digest[WOLFSPDM_HASH_SIZE]; - byte derSig[256]; - word32 derSigSz = sizeof(derSig); - const byte* sigR; - const byte* sigS; - int verified = 0; int rc; + (void)rspBufSz; + if (ctx == NULL || rspBuf == NULL || reqMsg == NULL) { return WOLFSPDM_E_INVALID_ARG; } @@ -1546,24 +1264,15 @@ int wolfSPDM_VerifyChallengeAuthSig(WOLFSPDM_CTX* ctx, } /* Build M1/M2 hash per DSP0274 Section 10.8.3: - * M1/M2 = A || B || C where: - * A = VCA (GET_VERSION through ALGORITHMS) - * B = GET_DIGESTS + DIGESTS + GET_CERTIFICATE + CERTIFICATE - * C = CHALLENGE request + CHALLENGE_AUTH response (before sig) - * - * A+B are already accumulated in ctx->m1m2Hash from - * NegotiateAlgorithms/GetDigests/GetCertificate. - * Now add C and finalize. */ + * A+B are already accumulated in ctx->m1m2Hash. Now add C and finalize. */ if (!ctx->m1m2HashInit) { wolfSPDM_DebugPrint(ctx, "CHALLENGE: M1/M2 hash not initialized\n"); return WOLFSPDM_E_CHALLENGE; } - /* Add C: CHALLENGE request */ + /* Add C: CHALLENGE request + CHALLENGE_AUTH response (before sig) */ rc = wc_Sha384Update(&ctx->m1m2Hash, reqMsg, reqMsgSz); if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - - /* Add C: CHALLENGE_AUTH response (before signature) */ rc = wc_Sha384Update(&ctx->m1m2Hash, rspBuf, sigOffset); if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; @@ -1572,68 +1281,15 @@ int wolfSPDM_VerifyChallengeAuthSig(WOLFSPDM_CTX* ctx, ctx->m1m2HashInit = 0; /* Hash consumed */ if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - /* Build M = prefix || zero_pad || context_str || Hash(M1||M2), then hash */ - { - static const char context_str[] = "responder-challenge_auth signing"; - #define CHAL_SIGNING_PREFIX_SIZE 16 - #define CHAL_SIGNING_CONTEXT_STR_SIZE 32 - #define CHAL_SIGNING_ZERO_PAD_SIZE (36 - CHAL_SIGNING_CONTEXT_STR_SIZE) /* 4 */ - wc_Sha384 sha2; - byte signMsg[200]; - word32 signMsgLen = 0; - byte majorVer, minorVer; - int i; - - majorVer = (byte)('0' + ((ctx->spdmVersion >> 4) & 0xF)); - minorVer = (byte)('0' + (ctx->spdmVersion & 0xF)); - - for (i = 0; i < 4; i++) { - XMEMCPY(&signMsg[signMsgLen], "dmtf-spdm-v1.2.*", - CHAL_SIGNING_PREFIX_SIZE); - signMsg[signMsgLen + 11] = majorVer; - signMsg[signMsgLen + 13] = minorVer; - signMsg[signMsgLen + 15] = '*'; - signMsgLen += CHAL_SIGNING_PREFIX_SIZE; - } - - XMEMSET(&signMsg[signMsgLen], 0x00, CHAL_SIGNING_ZERO_PAD_SIZE); - signMsgLen += CHAL_SIGNING_ZERO_PAD_SIZE; - - XMEMCPY(&signMsg[signMsgLen], context_str, CHAL_SIGNING_CONTEXT_STR_SIZE); - signMsgLen += CHAL_SIGNING_CONTEXT_STR_SIZE; - - XMEMCPY(&signMsg[signMsgLen], digest, WOLFSPDM_HASH_SIZE); - signMsgLen += WOLFSPDM_HASH_SIZE; - - /* Hash M */ - rc = wc_InitSha384(&sha2); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - rc = wc_Sha384Update(&sha2, signMsg, signMsgLen); - if (rc != 0) { wc_Sha384Free(&sha2); return WOLFSPDM_E_CRYPTO_FAIL; } - rc = wc_Sha384Final(&sha2, digest); - wc_Sha384Free(&sha2); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - } - - /* SPDM ECDSA signature: raw r||s */ - sigR = rspBuf + sigOffset; - sigS = sigR + (WOLFSPDM_ECC_SIG_SIZE / 2); - - rc = wc_ecc_rs_raw_to_sig(sigR, WOLFSPDM_ECC_SIG_SIZE / 2, - sigS, WOLFSPDM_ECC_SIG_SIZE / 2, derSig, &derSigSz); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "Challenge: rs_raw_to_sig failed: %d\n", rc); - return WOLFSPDM_E_CRYPTO_FAIL; - } + /* Build M = prefix || zero_pad || context_str || hash, then hash it */ + rc = wolfSPDM_BuildSignedHash(ctx->spdmVersion, + "responder-challenge_auth signing", 32, digest, digest); + if (rc != WOLFSPDM_SUCCESS) return rc; - rc = wc_ecc_verify_hash(derSig, derSigSz, digest, WOLFSPDM_HASH_SIZE, - &verified, &ctx->responderPubKey); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "Challenge: verify_hash failed: %d\n", rc); - return WOLFSPDM_E_CRYPTO_FAIL; - } - - if (verified == 1) { + /* Verify ECDSA signature (raw r||s format) */ + rc = wolfSPDM_VerifyEccSig(ctx, rspBuf + sigOffset, WOLFSPDM_ECC_SIG_SIZE, + digest, WOLFSPDM_HASH_SIZE); + if (rc == WOLFSPDM_SUCCESS) { wolfSPDM_DebugPrint(ctx, "CHALLENGE_AUTH signature VERIFIED\n"); return WOLFSPDM_SUCCESS; } @@ -1650,9 +1306,7 @@ int wolfSPDM_VerifyChallengeAuthSig(WOLFSPDM_CTX* ctx, int wolfSPDM_BuildHeartbeat(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) { - if (ctx == NULL || buf == NULL || bufSz == NULL || *bufSz < 4) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 4); buf[0] = ctx->spdmVersion; buf[1] = SPDM_HEARTBEAT; @@ -1666,18 +1320,8 @@ int wolfSPDM_BuildHeartbeat(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) int wolfSPDM_ParseHeartbeatAck(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) { - if (ctx == NULL || buf == NULL || bufSz < 4) { - return WOLFSPDM_E_INVALID_ARG; - } - - if (buf[1] != SPDM_HEARTBEAT_ACK) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - wolfSPDM_DebugPrint(ctx, "HEARTBEAT error: 0x%02x\n", errCode); - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_BAD_STATE; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 4); + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_HEARTBEAT_ACK, WOLFSPDM_E_BAD_STATE); wolfSPDM_DebugPrint(ctx, "HEARTBEAT_ACK received\n"); return WOLFSPDM_SUCCESS; @@ -1692,10 +1336,9 @@ int wolfSPDM_BuildKeyUpdate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, { int rc; - if (ctx == NULL || buf == NULL || bufSz == NULL || tag == NULL || - *bufSz < 4) { - return WOLFSPDM_E_BUFFER_SMALL; - } + SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 4); + if (tag == NULL) + return WOLFSPDM_E_INVALID_ARG; /* Generate random tag for request/response matching */ rc = wolfSPDM_GetRandom(ctx, tag, 1); @@ -1715,18 +1358,8 @@ int wolfSPDM_BuildKeyUpdate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, int wolfSPDM_ParseKeyUpdateAck(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz, byte operation, byte tag) { - if (ctx == NULL || buf == NULL || bufSz < 4) { - return WOLFSPDM_E_INVALID_ARG; - } - - if (buf[1] != SPDM_KEY_UPDATE_ACK) { - int errCode; - if (wolfSPDM_CheckError(buf, bufSz, &errCode)) { - wolfSPDM_DebugPrint(ctx, "KEY_UPDATE error: 0x%02x\n", errCode); - return WOLFSPDM_E_PEER_ERROR; - } - return WOLFSPDM_E_KEY_UPDATE; - } + SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 4); + SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_KEY_UPDATE_ACK, WOLFSPDM_E_KEY_UPDATE); /* Verify echoed operation and tag */ if (buf[2] != operation) { diff --git a/test/unit_test.c b/test/unit_test.c index 8b94eed..f13dd4a 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -31,6 +31,17 @@ static int g_testsFailed = 0; return 0; \ } while(0) +#define ASSERT_SUCCESS(expr) do { int _r = (expr); if (_r != 0) { \ + printf(" FAIL %s:%d: %s returned %d\n", __FILE__, __LINE__, #expr, _r); \ + g_testsFailed++; return -1; } } while(0) + +#define ASSERT_FAIL(expr) do { int _r = (expr); if (_r == 0) { \ + printf(" FAIL %s:%d: %s should have failed\n", __FILE__, __LINE__, #expr); \ + g_testsFailed++; return -1; } } while(0) + +#define ASSERT_EQ(a, b, msg) TEST_ASSERT((a) == (b), msg) +#define ASSERT_NE(a, b, msg) TEST_ASSERT((a) != (b), msg) + /* Dummy I/O callback for testing */ static int dummy_io_cb(WOLFSPDM_CTX* ctx, const byte* txBuf, word32 txSz, byte* rxBuf, word32* rxSz, void* userCtx) @@ -53,14 +64,11 @@ static int test_context_new_free(void) ctx = wolfSPDM_New(); TEST_ASSERT(ctx != NULL, "wolfSPDM_New returned NULL"); - - TEST_ASSERT(ctx->state == WOLFSPDM_STATE_INIT, "Initial state wrong"); - TEST_ASSERT(ctx->initialized == 1, "Should be initialized by New()"); + ASSERT_EQ(ctx->state, WOLFSPDM_STATE_INIT, "Initial state wrong"); + ASSERT_EQ(ctx->initialized, 1, "Should be initialized by New()"); wolfSPDM_Free(ctx); - - /* Free NULL should not crash */ - wolfSPDM_Free(NULL); + wolfSPDM_Free(NULL); /* Should not crash */ TEST_PASS(); } @@ -70,15 +78,13 @@ static int test_context_init(void) { WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; - int rc; printf("test_context_init...\n"); - 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"); + ASSERT_SUCCESS(wolfSPDM_Init(ctx)); + ASSERT_EQ(ctx->initialized, 1, "Not marked initialized"); + ASSERT_EQ(ctx->rngInitialized, 1, "RNG not initialized"); + ASSERT_EQ(ctx->reqCaps, WOLFSPDM_DEFAULT_REQ_CAPS, "Default caps wrong"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -88,23 +94,15 @@ static int test_context_static_alloc(void) { byte buffer[sizeof(WOLFSPDM_CTX) + 64]; WOLFSPDM_CTX* ctx = (WOLFSPDM_CTX*)buffer; - int rc; printf("test_context_static_alloc...\n"); - 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"); - - wolfSPDM_Free(ctx); /* Now safe — no XFREE on static ctx */ + ASSERT_EQ(wolfSPDM_GetCtxSize(), (int)sizeof(WOLFSPDM_CTX), "GetCtxSize mismatch"); + ASSERT_EQ(wolfSPDM_InitStatic(ctx, 10), WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + ASSERT_SUCCESS(wolfSPDM_InitStatic(ctx, sizeof(buffer))); + ASSERT_EQ(ctx->initialized, 1, "Static ctx not initialized"); + wolfSPDM_Free(ctx); TEST_PASS(); } @@ -112,22 +110,16 @@ static int test_context_set_io(void) { WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; - int rc; int dummy = 42; printf("test_context_set_io...\n"); wolfSPDM_Init(ctx); - /* Dummy callback for testing */ - rc = wolfSPDM_SetIO(ctx, dummy_io_cb, &dummy); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "SetIO failed"); - TEST_ASSERT(ctx->ioCb == dummy_io_cb, "IO callback not set"); - TEST_ASSERT(ctx->ioUserCtx == &dummy, "User context not set"); - - /* NULL callback should fail */ - rc = wolfSPDM_SetIO(ctx, NULL, NULL); - TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, "NULL callback should fail"); + ASSERT_SUCCESS(wolfSPDM_SetIO(ctx, dummy_io_cb, &dummy)); + ASSERT_EQ(ctx->ioCb, dummy_io_cb, "IO callback not set"); + ASSERT_EQ(ctx->ioUserCtx, &dummy, "User context not set"); + ASSERT_EQ(wolfSPDM_SetIO(ctx, NULL, NULL), WOLFSPDM_E_INVALID_ARG, "NULL callback should fail"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -143,26 +135,22 @@ static int test_transcript_add_reset(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte data1[] = {0x01, 0x02, 0x03, 0x04}; byte data2[] = {0x05, 0x06, 0x07, 0x08}; - int rc; printf("test_transcript_add_reset...\n"); wolfSPDM_Init(ctx); + ASSERT_EQ(ctx->transcriptLen, 0, "Transcript should start empty"); - TEST_ASSERT(ctx->transcriptLen == 0, "Transcript should start empty"); - - rc = wolfSPDM_TranscriptAdd(ctx, data1, sizeof(data1)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "TranscriptAdd failed"); - TEST_ASSERT(ctx->transcriptLen == 4, "Length should be 4"); - TEST_ASSERT(memcmp(ctx->transcript, data1, 4) == 0, "Data mismatch"); + ASSERT_SUCCESS(wolfSPDM_TranscriptAdd(ctx, data1, sizeof(data1))); + ASSERT_EQ(ctx->transcriptLen, 4, "Length should be 4"); + ASSERT_EQ(memcmp(ctx->transcript, data1, 4), 0, "Data mismatch"); - rc = wolfSPDM_TranscriptAdd(ctx, data2, sizeof(data2)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Second add failed"); - TEST_ASSERT(ctx->transcriptLen == 8, "Length should be 8"); - TEST_ASSERT(memcmp(ctx->transcript + 4, data2, 4) == 0, "Data2 mismatch"); + ASSERT_SUCCESS(wolfSPDM_TranscriptAdd(ctx, data2, sizeof(data2))); + ASSERT_EQ(ctx->transcriptLen, 8, "Length should be 8"); + ASSERT_EQ(memcmp(ctx->transcript + 4, data2, 4), 0, "Data2 mismatch"); wolfSPDM_TranscriptReset(ctx); - TEST_ASSERT(ctx->transcriptLen == 0, "Reset should clear length"); + ASSERT_EQ(ctx->transcriptLen, 0, "Reset should clear length"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -174,23 +162,15 @@ static int test_transcript_hash(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte data[] = "test data for hashing"; byte hash[WOLFSPDM_HASH_SIZE]; - int rc; + byte zeros[WOLFSPDM_HASH_SIZE]; printf("test_transcript_hash...\n"); wolfSPDM_Init(ctx); - wolfSPDM_TranscriptAdd(ctx, data, sizeof(data) - 1); - - rc = wolfSPDM_TranscriptHash(ctx, hash); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "TranscriptHash failed"); - - /* Verify hash is non-zero */ - int nonZero = 0; - for (int i = 0; i < WOLFSPDM_HASH_SIZE; i++) { - if (hash[i] != 0) nonZero = 1; - } - TEST_ASSERT(nonZero, "Hash should be non-zero"); + ASSERT_SUCCESS(wolfSPDM_TranscriptHash(ctx, hash)); + XMEMSET(zeros, 0, sizeof(zeros)); + ASSERT_NE(memcmp(hash, zeros, WOLFSPDM_HASH_SIZE), 0, "Hash should be non-zero"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -201,25 +181,16 @@ static int test_certchain_hash(void) WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; byte certData[] = {0x30, 0x82, 0x01, 0x00, 0xAA, 0xBB, 0xCC, 0xDD}; - int rc; + byte zeros[WOLFSPDM_HASH_SIZE]; printf("test_certchain_hash...\n"); wolfSPDM_Init(ctx); - - rc = wolfSPDM_CertChainAdd(ctx, certData, sizeof(certData)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "CertChainAdd failed"); - TEST_ASSERT(ctx->certChainLen == sizeof(certData), "CertChain len wrong"); - - rc = wolfSPDM_ComputeCertChainHash(ctx); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "ComputeCertChainHash failed"); - - /* Verify Ct is non-zero */ - int nonZero = 0; - for (int i = 0; i < WOLFSPDM_HASH_SIZE; i++) { - if (ctx->certChainHash[i] != 0) nonZero = 1; - } - TEST_ASSERT(nonZero, "Ct should be non-zero"); + ASSERT_SUCCESS(wolfSPDM_CertChainAdd(ctx, certData, sizeof(certData))); + ASSERT_EQ(ctx->certChainLen, sizeof(certData), "CertChain len wrong"); + ASSERT_SUCCESS(wolfSPDM_ComputeCertChainHash(ctx)); + XMEMSET(zeros, 0, sizeof(zeros)); + ASSERT_NE(memcmp(ctx->certChainHash, zeros, WOLFSPDM_HASH_SIZE), 0, "Ct should be non-zero"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -234,21 +205,13 @@ static int test_random_generation(void) WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; byte buf1[32], buf2[32]; - int rc; printf("test_random_generation...\n"); wolfSPDM_Init(ctx); - - rc = wolfSPDM_GetRandom(ctx, buf1, sizeof(buf1)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "GetRandom failed"); - - rc = wolfSPDM_GetRandom(ctx, buf2, sizeof(buf2)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Second GetRandom failed"); - - /* Two random outputs should differ */ - TEST_ASSERT(memcmp(buf1, buf2, sizeof(buf1)) != 0, - "Random outputs should differ"); + ASSERT_SUCCESS(wolfSPDM_GetRandom(ctx, buf1, sizeof(buf1))); + ASSERT_SUCCESS(wolfSPDM_GetRandom(ctx, buf2, sizeof(buf2))); + ASSERT_NE(memcmp(buf1, buf2, sizeof(buf1)), 0, "Random outputs should differ"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -260,29 +223,20 @@ static int test_ephemeral_key_generation(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte pubKeyX[WOLFSPDM_ECC_KEY_SIZE]; byte pubKeyY[WOLFSPDM_ECC_KEY_SIZE]; + byte zeros[WOLFSPDM_ECC_KEY_SIZE]; word32 xSz = sizeof(pubKeyX); word32 ySz = sizeof(pubKeyY); - int rc; printf("test_ephemeral_key_generation...\n"); wolfSPDM_Init(ctx); - - rc = wolfSPDM_GenerateEphemeralKey(ctx); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "GenerateEphemeralKey failed"); - TEST_ASSERT(ctx->ephemeralKeyInitialized == 1, "Key not marked initialized"); - - rc = wolfSPDM_ExportEphemeralPubKey(ctx, pubKeyX, &xSz, pubKeyY, &ySz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "ExportEphemeralPubKey failed"); - TEST_ASSERT(xSz == WOLFSPDM_ECC_KEY_SIZE, "X coordinate wrong size"); - TEST_ASSERT(ySz == WOLFSPDM_ECC_KEY_SIZE, "Y coordinate wrong size"); - - /* Verify non-zero */ - int nonZero = 0; - for (word32 i = 0; i < xSz; i++) { - if (pubKeyX[i] != 0) nonZero = 1; - } - TEST_ASSERT(nonZero, "Public key X should be non-zero"); + ASSERT_SUCCESS(wolfSPDM_GenerateEphemeralKey(ctx)); + ASSERT_EQ(ctx->ephemeralKeyInitialized, 1, "Key not marked initialized"); + ASSERT_SUCCESS(wolfSPDM_ExportEphemeralPubKey(ctx, pubKeyX, &xSz, pubKeyY, &ySz)); + ASSERT_EQ(xSz, WOLFSPDM_ECC_KEY_SIZE, "X coordinate wrong size"); + ASSERT_EQ(ySz, WOLFSPDM_ECC_KEY_SIZE, "Y coordinate wrong size"); + XMEMSET(zeros, 0, sizeof(zeros)); + ASSERT_NE(memcmp(pubKeyX, zeros, WOLFSPDM_ECC_KEY_SIZE), 0, "Public key X should be non-zero"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -297,24 +251,17 @@ static int test_hkdf_expand_label(void) byte secret[48]; byte output[32]; byte context[48]; - int rc; + byte zeros[32]; printf("test_hkdf_expand_label...\n"); memset(secret, 0x5A, sizeof(secret)); memset(context, 0x00, sizeof(context)); - rc = wolfSPDM_HkdfExpandLabel(0x13, secret, sizeof(secret), - SPDM_LABEL_KEY, context, sizeof(context), - output, sizeof(output)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "HkdfExpandLabel failed"); - - /* Verify non-zero output */ - int nonZero = 0; - for (int i = 0; i < 32; i++) { - if (output[i] != 0) nonZero = 1; - } - TEST_ASSERT(nonZero, "HKDF output should be non-zero"); + ASSERT_SUCCESS(wolfSPDM_HkdfExpandLabel(0x13, secret, sizeof(secret), + SPDM_LABEL_KEY, context, sizeof(context), output, sizeof(output))); + XMEMSET(zeros, 0, sizeof(zeros)); + ASSERT_NE(memcmp(output, zeros, sizeof(output)), 0, "HKDF output should be non-zero"); TEST_PASS(); } @@ -324,22 +271,16 @@ static int test_compute_verify_data(void) byte finishedKey[WOLFSPDM_HASH_SIZE]; byte thHash[WOLFSPDM_HASH_SIZE]; byte verifyData[WOLFSPDM_HASH_SIZE]; - int rc; + byte zeros[WOLFSPDM_HASH_SIZE]; printf("test_compute_verify_data...\n"); memset(finishedKey, 0xAB, sizeof(finishedKey)); memset(thHash, 0xCD, sizeof(thHash)); - rc = wolfSPDM_ComputeVerifyData(finishedKey, thHash, verifyData); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "ComputeVerifyData failed"); - - /* Verify it's an HMAC (non-zero) */ - int nonZero = 0; - for (int i = 0; i < WOLFSPDM_HASH_SIZE; i++) { - if (verifyData[i] != 0) nonZero = 1; - } - TEST_ASSERT(nonZero, "VerifyData should be non-zero"); + ASSERT_SUCCESS(wolfSPDM_ComputeVerifyData(finishedKey, thHash, verifyData)); + XMEMSET(zeros, 0, sizeof(zeros)); + ASSERT_NE(memcmp(verifyData, zeros, WOLFSPDM_HASH_SIZE), 0, "VerifyData should be non-zero"); TEST_PASS(); } @@ -352,22 +293,16 @@ static int test_build_get_version(void) { byte buf[16]; word32 bufSz = sizeof(buf); - int rc; printf("test_build_get_version...\n"); - rc = wolfSPDM_BuildGetVersion(buf, &bufSz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildGetVersion failed"); - TEST_ASSERT(bufSz == 4, "GET_VERSION should be 4 bytes"); - TEST_ASSERT(buf[0] == SPDM_VERSION_10, "Version should be 0x10"); - TEST_ASSERT(buf[1] == SPDM_GET_VERSION, "Code should be 0x84"); - TEST_ASSERT(buf[2] == 0x00, "Param1 should be 0"); - TEST_ASSERT(buf[3] == 0x00, "Param2 should be 0"); + ASSERT_SUCCESS(wolfSPDM_BuildGetVersion(buf, &bufSz)); + ASSERT_EQ(bufSz, 4, "GET_VERSION should be 4 bytes"); + ASSERT_EQ(buf[0], SPDM_VERSION_10, "Version should be 0x10"); + ASSERT_EQ(buf[1], SPDM_GET_VERSION, "Code should be 0x84"); - /* Buffer too small */ bufSz = 2; - rc = wolfSPDM_BuildGetVersion(buf, &bufSz); - TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + ASSERT_EQ(wolfSPDM_BuildGetVersion(buf, &bufSz), WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); TEST_PASS(); } @@ -378,18 +313,15 @@ static int test_build_get_capabilities(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[32]; word32 bufSz = sizeof(buf); - int rc; printf("test_build_get_capabilities...\n"); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; - - rc = wolfSPDM_BuildGetCapabilities(ctx, buf, &bufSz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildGetCapabilities failed"); - TEST_ASSERT(bufSz == 20, "GET_CAPABILITIES should be 20 bytes"); - TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version should be 0x12"); - TEST_ASSERT(buf[1] == SPDM_GET_CAPABILITIES, "Code should be 0xE1"); + ASSERT_SUCCESS(wolfSPDM_BuildGetCapabilities(ctx, buf, &bufSz)); + ASSERT_EQ(bufSz, 20, "GET_CAPABILITIES should be 20 bytes"); + ASSERT_EQ(buf[0], SPDM_VERSION_12, "Version should be 0x12"); + ASSERT_EQ(buf[1], SPDM_GET_CAPABILITIES, "Code should be 0xE1"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -401,18 +333,14 @@ static int test_build_negotiate_algorithms(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[64]; word32 bufSz = sizeof(buf); - int rc; printf("test_build_negotiate_algorithms...\n"); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; - - rc = wolfSPDM_BuildNegotiateAlgorithms(ctx, buf, &bufSz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildNegotiateAlgorithms failed"); - TEST_ASSERT(bufSz == 48, "NEGOTIATE_ALGORITHMS should be 48 bytes"); - TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version should be 0x12"); - TEST_ASSERT(buf[1] == SPDM_NEGOTIATE_ALGORITHMS, "Code should be 0xE3"); + ASSERT_SUCCESS(wolfSPDM_BuildNegotiateAlgorithms(ctx, buf, &bufSz)); + ASSERT_EQ(bufSz, 48, "NEGOTIATE_ALGORITHMS should be 48 bytes"); + ASSERT_EQ(buf[1], SPDM_NEGOTIATE_ALGORITHMS, "Code should be 0xE3"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -424,17 +352,14 @@ static int test_build_get_digests(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[16]; word32 bufSz = sizeof(buf); - int rc; printf("test_build_get_digests...\n"); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; - - rc = wolfSPDM_BuildGetDigests(ctx, buf, &bufSz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildGetDigests failed"); - TEST_ASSERT(bufSz == 4, "GET_DIGESTS should be 4 bytes"); - TEST_ASSERT(buf[1] == SPDM_GET_DIGESTS, "Code should be 0x81"); + ASSERT_SUCCESS(wolfSPDM_BuildGetDigests(ctx, buf, &bufSz)); + ASSERT_EQ(bufSz, 4, "GET_DIGESTS should be 4 bytes"); + ASSERT_EQ(buf[1], SPDM_GET_DIGESTS, "Code should be 0x81"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -446,18 +371,15 @@ static int test_build_get_certificate(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[16]; word32 bufSz = sizeof(buf); - int rc; printf("test_build_get_certificate...\n"); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; - - rc = wolfSPDM_BuildGetCertificate(ctx, buf, &bufSz, 0, 0, 1024); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildGetCertificate failed"); - TEST_ASSERT(bufSz == 8, "GET_CERTIFICATE should be 8 bytes"); - TEST_ASSERT(buf[1] == SPDM_GET_CERTIFICATE, "Code should be 0x82"); - TEST_ASSERT(buf[2] == 0x00, "SlotID should be 0"); + ASSERT_SUCCESS(wolfSPDM_BuildGetCertificate(ctx, buf, &bufSz, 0, 0, 1024)); + ASSERT_EQ(bufSz, 8, "GET_CERTIFICATE should be 8 bytes"); + ASSERT_EQ(buf[1], SPDM_GET_CERTIFICATE, "Code should be 0x82"); + ASSERT_EQ(buf[2], 0x00, "SlotID should be 0"); TEST_ASSERT(buf[6] == 0x00 && buf[7] == 0x04, "Length should be 1024"); wolfSPDM_Free(ctx); @@ -470,17 +392,14 @@ static int test_build_end_session(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[16]; word32 bufSz = sizeof(buf); - int rc; printf("test_build_end_session...\n"); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; - - rc = wolfSPDM_BuildEndSession(ctx, buf, &bufSz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildEndSession failed"); - TEST_ASSERT(bufSz == 4, "END_SESSION should be 4 bytes"); - TEST_ASSERT(buf[1] == SPDM_END_SESSION, "Code should be 0xEA"); + ASSERT_SUCCESS(wolfSPDM_BuildEndSession(ctx, buf, &bufSz)); + ASSERT_EQ(bufSz, 4, "END_SESSION should be 4 bytes"); + ASSERT_EQ(buf[1], SPDM_END_SESSION, "Code should be 0xEA"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -533,8 +452,8 @@ static int test_build_get_measurements(void) WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[64]; + byte zeros[32]; word32 bufSz; - int rc; printf("test_build_get_measurements...\n"); @@ -543,36 +462,19 @@ static int test_build_get_measurements(void) /* Build without signature */ bufSz = sizeof(buf); - rc = wolfSPDM_BuildGetMeasurements(ctx, buf, &bufSz, - SPDM_MEAS_OPERATION_ALL, 0); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Build without sig failed"); - TEST_ASSERT(bufSz == 4, "Without sig should be 4 bytes"); - TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version wrong"); - TEST_ASSERT(buf[1] == SPDM_GET_MEASUREMENTS, "Code should be 0xE0"); - TEST_ASSERT(buf[2] == 0x00, "Param1 should be 0 (no sig)"); - TEST_ASSERT(buf[3] == SPDM_MEAS_OPERATION_ALL, "Param2 should be 0xFF"); + ASSERT_SUCCESS(wolfSPDM_BuildGetMeasurements(ctx, buf, &bufSz, SPDM_MEAS_OPERATION_ALL, 0)); + ASSERT_EQ(bufSz, 4, "Without sig should be 4 bytes"); + ASSERT_EQ(buf[1], SPDM_GET_MEASUREMENTS, "Code should be 0xE0"); + ASSERT_EQ(buf[2], 0x00, "Param1 should be 0 (no sig)"); /* Build with signature */ bufSz = sizeof(buf); - rc = wolfSPDM_BuildGetMeasurements(ctx, buf, &bufSz, - SPDM_MEAS_OPERATION_ALL, 1); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Build with sig failed"); - TEST_ASSERT(bufSz == 37, "With sig should be 37 bytes"); - TEST_ASSERT(buf[2] == SPDM_MEAS_REQUEST_SIG_BIT, "Sig bit should be set"); - - /* Verify nonce was populated (non-zero) */ - { - int nonZero = 0; - int i; - for (i = 4; i < 36; i++) { - if (buf[i] != 0) nonZero = 1; - } - TEST_ASSERT(nonZero, "Nonce should be non-zero"); - } - - /* Verify nonce saved in context */ - TEST_ASSERT(memcmp(ctx->measNonce, &buf[4], 32) == 0, - "Nonce should match context"); + ASSERT_SUCCESS(wolfSPDM_BuildGetMeasurements(ctx, buf, &bufSz, SPDM_MEAS_OPERATION_ALL, 1)); + ASSERT_EQ(bufSz, 37, "With sig should be 37 bytes"); + ASSERT_EQ(buf[2], SPDM_MEAS_REQUEST_SIG_BIT, "Sig bit should be set"); + XMEMSET(zeros, 0, sizeof(zeros)); + ASSERT_NE(memcmp(&buf[4], zeros, 32), 0, "Nonce should be non-zero"); + ASSERT_EQ(memcmp(ctx->measNonce, &buf[4], 32), 0, "Nonce should match context"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -585,67 +487,44 @@ static int test_measurement_accessors(void) byte measIdx, measType; byte value[64]; word32 valueSz; - int rc; printf("test_measurement_accessors...\n"); wolfSPDM_Init(ctx); - - /* Before measurements: count should be 0 */ - TEST_ASSERT(wolfSPDM_GetMeasurementCount(ctx) == 0, - "Count should be 0 before measurements"); + ASSERT_EQ(wolfSPDM_GetMeasurementCount(ctx), 0, "Count should be 0 before measurements"); /* Manually populate 2 test blocks */ ctx->hasMeasurements = 1; ctx->measBlockCount = 2; - ctx->measBlocks[0].index = 1; ctx->measBlocks[0].dmtfType = SPDM_MEAS_VALUE_TYPE_IMMUTABLE_ROM; ctx->measBlocks[0].valueSize = 4; - ctx->measBlocks[0].value[0] = 0xAA; - ctx->measBlocks[0].value[1] = 0xBB; - ctx->measBlocks[0].value[2] = 0xCC; - ctx->measBlocks[0].value[3] = 0xDD; - + ctx->measBlocks[0].value[0] = 0xAA; ctx->measBlocks[0].value[1] = 0xBB; + ctx->measBlocks[0].value[2] = 0xCC; ctx->measBlocks[0].value[3] = 0xDD; ctx->measBlocks[1].index = 2; ctx->measBlocks[1].dmtfType = SPDM_MEAS_VALUE_TYPE_MUTABLE_FW; ctx->measBlocks[1].valueSize = 2; - ctx->measBlocks[1].value[0] = 0x11; - ctx->measBlocks[1].value[1] = 0x22; + ctx->measBlocks[1].value[0] = 0x11; ctx->measBlocks[1].value[1] = 0x22; - TEST_ASSERT(wolfSPDM_GetMeasurementCount(ctx) == 2, - "Count should be 2"); + ASSERT_EQ(wolfSPDM_GetMeasurementCount(ctx), 2, "Count should be 2"); /* Get block 0 */ valueSz = sizeof(value); - rc = wolfSPDM_GetMeasurementBlock(ctx, 0, &measIdx, &measType, - value, &valueSz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "GetMeasurementBlock(0) failed"); - TEST_ASSERT(measIdx == 1, "Block 0 index should be 1"); - TEST_ASSERT(measType == SPDM_MEAS_VALUE_TYPE_IMMUTABLE_ROM, - "Block 0 type wrong"); - TEST_ASSERT(valueSz == 4, "Block 0 size wrong"); - TEST_ASSERT(value[0] == 0xAA, "Block 0 value wrong"); + ASSERT_SUCCESS(wolfSPDM_GetMeasurementBlock(ctx, 0, &measIdx, &measType, value, &valueSz)); + ASSERT_EQ(measIdx, 1, "Block 0 index should be 1"); + ASSERT_EQ(measType, SPDM_MEAS_VALUE_TYPE_IMMUTABLE_ROM, "Block 0 type wrong"); + ASSERT_EQ(valueSz, 4, "Block 0 size wrong"); + ASSERT_EQ(value[0], 0xAA, "Block 0 value wrong"); /* Get block 1 */ valueSz = sizeof(value); - rc = wolfSPDM_GetMeasurementBlock(ctx, 1, &measIdx, &measType, - value, &valueSz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "GetMeasurementBlock(1) failed"); - TEST_ASSERT(measIdx == 2, "Block 1 index should be 2"); - TEST_ASSERT(valueSz == 2, "Block 1 size wrong"); + ASSERT_SUCCESS(wolfSPDM_GetMeasurementBlock(ctx, 1, &measIdx, &measType, value, &valueSz)); + ASSERT_EQ(measIdx, 2, "Block 1 index should be 2"); /* Out of range */ valueSz = sizeof(value); - rc = wolfSPDM_GetMeasurementBlock(ctx, 2, &measIdx, &measType, - value, &valueSz); - TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, - "Out of range should return INVALID_ARG"); - - rc = wolfSPDM_GetMeasurementBlock(ctx, -1, &measIdx, &measType, - value, &valueSz); - TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, - "Negative index should return INVALID_ARG"); + ASSERT_FAIL(wolfSPDM_GetMeasurementBlock(ctx, 2, &measIdx, &measType, value, &valueSz)); + ASSERT_FAIL(wolfSPDM_GetMeasurementBlock(ctx, -1, &measIdx, &measType, value, &valueSz)); wolfSPDM_Free(ctx); TEST_PASS(); @@ -655,70 +534,32 @@ static int test_parse_measurements(void) { WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; - /* Construct a fake MEASUREMENTS response with 2 blocks: - * - * Header (8 bytes): - * [0] version=0x12 [1] code=0x60 [2] param1 [3] param2 - * [4] numBlocks=2 - * [5..7] recordLen (3 LE) - * - * Block 1 (4+7=11 bytes): - * Index=1, MeasSpec=1, MeasSize=7 (LE) - * DMTF: Type=0x00, ValueSize=4 (LE), Value=AA BB CC DD - * - * Block 2 (4+5=9 bytes): - * Index=2, MeasSpec=1, MeasSize=5 (LE) - * DMTF: Type=0x01, ValueSize=2 (LE), Value=11 22 - * - * RecordLen = 11 + 9 = 20 - */ + /* Fake MEASUREMENTS response: 2 blocks, recordLen=20 */ byte rsp[] = { 0x12, 0x60, 0x00, 0x00, /* header */ 0x02, /* numBlocks */ 0x14, 0x00, 0x00, /* recordLen = 20 LE */ - /* Block 1 */ - 0x01, /* Index */ - 0x01, /* MeasSpec (DMTF) */ - 0x07, 0x00, /* MeasSize = 7 LE */ - 0x00, /* DMTF Type: Immutable ROM */ - 0x04, 0x00, /* ValueSize = 4 LE */ - 0xAA, 0xBB, 0xCC, 0xDD, /* Value */ - /* Block 2 */ - 0x02, /* Index */ - 0x01, /* MeasSpec (DMTF) */ - 0x05, 0x00, /* MeasSize = 5 LE */ - 0x01, /* DMTF Type: Mutable FW */ - 0x02, 0x00, /* ValueSize = 2 LE */ - 0x11, 0x22 /* Value */ + /* Block 1: Index=1, Spec=1, Size=7, DMTF Type=0x00, ValSize=4 */ + 0x01, 0x01, 0x07, 0x00, 0x00, 0x04, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, + /* Block 2: Index=2, Spec=1, Size=5, DMTF Type=0x01, ValSize=2 */ + 0x02, 0x01, 0x05, 0x00, 0x01, 0x02, 0x00, 0x11, 0x22 }; - int rc; printf("test_parse_measurements...\n"); wolfSPDM_Init(ctx); - - rc = wolfSPDM_ParseMeasurements(ctx, rsp, sizeof(rsp)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "ParseMeasurements failed"); - TEST_ASSERT(ctx->measBlockCount == 2, "Should have 2 blocks"); - TEST_ASSERT(ctx->hasMeasurements == 1, "hasMeasurements should be set"); - - /* Check block 0 */ - TEST_ASSERT(ctx->measBlocks[0].index == 1, "Block 0 index wrong"); - TEST_ASSERT(ctx->measBlocks[0].dmtfType == 0x00, "Block 0 type wrong"); - TEST_ASSERT(ctx->measBlocks[0].valueSize == 4, "Block 0 valueSize wrong"); - TEST_ASSERT(ctx->measBlocks[0].value[0] == 0xAA, "Block 0 value[0] wrong"); - TEST_ASSERT(ctx->measBlocks[0].value[3] == 0xDD, "Block 0 value[3] wrong"); - - /* Check block 1 */ - TEST_ASSERT(ctx->measBlocks[1].index == 2, "Block 1 index wrong"); - TEST_ASSERT(ctx->measBlocks[1].dmtfType == 0x01, "Block 1 type wrong"); - TEST_ASSERT(ctx->measBlocks[1].valueSize == 2, "Block 1 valueSize wrong"); - TEST_ASSERT(ctx->measBlocks[1].value[0] == 0x11, "Block 1 value[0] wrong"); + ASSERT_SUCCESS(wolfSPDM_ParseMeasurements(ctx, rsp, sizeof(rsp))); + ASSERT_EQ(ctx->measBlockCount, 2, "Should have 2 blocks"); + ASSERT_EQ(ctx->hasMeasurements, 1, "hasMeasurements should be set"); + ASSERT_EQ(ctx->measBlocks[0].index, 1, "Block 0 index wrong"); + ASSERT_EQ(ctx->measBlocks[0].dmtfType, 0x00, "Block 0 type wrong"); + ASSERT_EQ(ctx->measBlocks[0].valueSize, 4, "Block 0 valueSize wrong"); + ASSERT_EQ(ctx->measBlocks[0].value[0], 0xAA, "Block 0 value[0] wrong"); + ASSERT_EQ(ctx->measBlocks[1].index, 2, "Block 1 index wrong"); + ASSERT_EQ(ctx->measBlocks[1].valueSize, 2, "Block 1 valueSize wrong"); /* Test truncated buffer */ - rc = wolfSPDM_ParseMeasurements(ctx, rsp, 5); - TEST_ASSERT(rc == WOLFSPDM_E_MEASUREMENT, - "Truncated should return MEASUREMENT error"); + ASSERT_FAIL(wolfSPDM_ParseMeasurements(ctx, rsp, 5)); wolfSPDM_Free(ctx); TEST_PASS(); @@ -905,29 +746,17 @@ static int test_set_trusted_cas(void) WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; byte fakeCa[] = {0x30, 0x82, 0x01, 0x00, 0xAA, 0xBB, 0xCC, 0xDD}; - int rc; printf("test_set_trusted_cas...\n"); wolfSPDM_Init(ctx); - - /* NULL args should fail */ - rc = wolfSPDM_SetTrustedCAs(NULL, fakeCa, sizeof(fakeCa)); - TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, "NULL ctx should fail"); - - rc = wolfSPDM_SetTrustedCAs(ctx, NULL, sizeof(fakeCa)); - TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, "NULL certs should fail"); - - rc = wolfSPDM_SetTrustedCAs(ctx, fakeCa, 0); - TEST_ASSERT(rc == WOLFSPDM_E_INVALID_ARG, "Zero size should fail"); - - /* Valid call */ - rc = wolfSPDM_SetTrustedCAs(ctx, fakeCa, sizeof(fakeCa)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "SetTrustedCAs failed"); - TEST_ASSERT(ctx->hasTrustedCAs == 1, "hasTrustedCAs not set"); - TEST_ASSERT(ctx->trustedCAsSz == sizeof(fakeCa), "Size mismatch"); - TEST_ASSERT(memcmp(ctx->trustedCAs, fakeCa, sizeof(fakeCa)) == 0, - "Data mismatch"); + ASSERT_FAIL(wolfSPDM_SetTrustedCAs(NULL, fakeCa, sizeof(fakeCa))); + ASSERT_FAIL(wolfSPDM_SetTrustedCAs(ctx, NULL, sizeof(fakeCa))); + ASSERT_FAIL(wolfSPDM_SetTrustedCAs(ctx, fakeCa, 0)); + ASSERT_SUCCESS(wolfSPDM_SetTrustedCAs(ctx, fakeCa, sizeof(fakeCa))); + ASSERT_EQ(ctx->hasTrustedCAs, 1, "hasTrustedCAs not set"); + ASSERT_EQ(ctx->trustedCAsSz, sizeof(fakeCa), "Size mismatch"); + ASSERT_EQ(memcmp(ctx->trustedCAs, fakeCa, sizeof(fakeCa)), 0, "Data mismatch"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -937,16 +766,11 @@ static int test_validate_cert_chain_no_cas(void) { WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; - int rc; printf("test_validate_cert_chain_no_cas...\n"); wolfSPDM_Init(ctx); - - /* No trusted CAs loaded — should return error */ - rc = wolfSPDM_ValidateCertChain(ctx); - TEST_ASSERT(rc == WOLFSPDM_E_CERT_PARSE, - "Should fail without trusted CAs"); + ASSERT_EQ(wolfSPDM_ValidateCertChain(ctx), WOLFSPDM_E_CERT_PARSE, "Should fail without trusted CAs"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -963,8 +787,8 @@ static int test_build_challenge(void) WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[64]; + byte zeros[32]; word32 bufSz; - int rc; printf("test_build_challenge...\n"); @@ -972,42 +796,23 @@ static int test_build_challenge(void) ctx->spdmVersion = SPDM_VERSION_12; bufSz = sizeof(buf); - rc = wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 0, - SPDM_MEAS_SUMMARY_HASH_NONE); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildChallenge failed"); - TEST_ASSERT(bufSz == 36, "CHALLENGE should be 36 bytes"); - TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version wrong"); - TEST_ASSERT(buf[1] == SPDM_CHALLENGE, "Code should be 0x83"); - TEST_ASSERT(buf[2] == 0x00, "SlotID should be 0"); - TEST_ASSERT(buf[3] == SPDM_MEAS_SUMMARY_HASH_NONE, "MeasHashType wrong"); - - /* Verify nonce is populated (non-zero) */ - { - int nonZero = 0; - int i; - for (i = 4; i < 36; i++) { - if (buf[i] != 0) nonZero = 1; - } - TEST_ASSERT(nonZero, "Nonce should be non-zero"); - } - - /* Verify nonce saved in context */ - TEST_ASSERT(memcmp(ctx->challengeNonce, &buf[4], 32) == 0, - "Challenge nonce should match context"); + ASSERT_SUCCESS(wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 0, SPDM_MEAS_SUMMARY_HASH_NONE)); + ASSERT_EQ(bufSz, 36, "CHALLENGE should be 36 bytes"); + ASSERT_EQ(buf[1], SPDM_CHALLENGE, "Code should be 0x83"); + ASSERT_EQ(buf[3], SPDM_MEAS_SUMMARY_HASH_NONE, "MeasHashType wrong"); + XMEMSET(zeros, 0, sizeof(zeros)); + ASSERT_NE(memcmp(&buf[4], zeros, 32), 0, "Nonce should be non-zero"); + ASSERT_EQ(memcmp(ctx->challengeNonce, &buf[4], 32), 0, "Nonce should match context"); /* Test with different slot and meas hash type */ bufSz = sizeof(buf); - rc = wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 3, - SPDM_MEAS_SUMMARY_HASH_ALL); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildChallenge slot 3 failed"); - TEST_ASSERT(buf[2] == 0x03, "SlotID should be 3"); - TEST_ASSERT(buf[3] == 0xFF, "MeasHashType should be 0xFF"); + ASSERT_SUCCESS(wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 3, SPDM_MEAS_SUMMARY_HASH_ALL)); + ASSERT_EQ(buf[2], 0x03, "SlotID should be 3"); /* Buffer too small */ bufSz = 10; - rc = wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 0, - SPDM_MEAS_SUMMARY_HASH_NONE); - TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + ASSERT_EQ(wolfSPDM_BuildChallenge(ctx, buf, &bufSz, 0, SPDM_MEAS_SUMMARY_HASH_NONE), + WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1017,16 +822,9 @@ static int test_parse_challenge_auth(void) { WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; - /* Build a fake CHALLENGE_AUTH response (measHashType=NONE): - * [0-3] header: version=0x12, code=0x03, param1, param2 - * [4-51] CertChainHash (48 bytes) - * [52-83] Nonce (32 bytes per DSP0274) - * (no MeasurementSummaryHash when type=NONE) - * [84-85] OpaqueDataLength = 0 - * [86-181] Signature (96 bytes) */ + /* Fake CHALLENGE_AUTH: hdr(4) + CertHash(48) + Nonce(32) + OpaqueLen(2) + Sig(96) = 182 */ byte rsp[182]; word32 sigOffset = 0; - int rc; printf("test_parse_challenge_auth...\n"); @@ -1037,37 +835,24 @@ static int test_parse_challenge_auth(void) XMEMSET(rsp, 0, sizeof(rsp)); rsp[0] = SPDM_VERSION_12; rsp[1] = SPDM_CHALLENGE_AUTH; - rsp[2] = 0x00; /* Param1 */ - rsp[3] = 0x00; /* Param2 */ - - /* Fill CertChainHash with known pattern to match ctx */ XMEMSET(&rsp[4], 0xAA, WOLFSPDM_HASH_SIZE); XMEMCPY(ctx->certChainHash, &rsp[4], WOLFSPDM_HASH_SIZE); - - /* Fill nonce (32 bytes) */ XMEMSET(&rsp[52], 0xBB, 32); - - /* OpaqueDataLength = 0 (no MeasSummaryHash since type=NONE) */ - rsp[84] = 0x00; - rsp[85] = 0x00; - - /* Fake signature (96 bytes) */ XMEMSET(&rsp[86], 0xCC, WOLFSPDM_ECC_SIG_SIZE); - rc = wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "ParseChallengeAuth failed"); - TEST_ASSERT(sigOffset == 86, "Signature offset should be 86"); + ASSERT_SUCCESS(wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset)); + ASSERT_EQ(sigOffset, 86, "Signature offset should be 86"); - /* Test with wrong response code */ + /* Wrong response code */ rsp[1] = 0xFF; - rc = wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset); - TEST_ASSERT(rc == WOLFSPDM_E_CHALLENGE, "Wrong code should fail"); + ASSERT_EQ(wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset), + WOLFSPDM_E_CHALLENGE, "Wrong code should fail"); rsp[1] = SPDM_CHALLENGE_AUTH; - /* Test with CertChainHash mismatch */ - ctx->certChainHash[0] = 0x00; /* Change expected hash */ - rc = wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset); - TEST_ASSERT(rc == WOLFSPDM_E_CHALLENGE, "Hash mismatch should fail"); + /* CertChainHash mismatch */ + ctx->certChainHash[0] = 0x00; + ASSERT_EQ(wolfSPDM_ParseChallengeAuth(ctx, rsp, sizeof(rsp), &sigOffset), + WOLFSPDM_E_CHALLENGE, "Hash mismatch should fail"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1085,26 +870,18 @@ static int test_build_heartbeat(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte buf[16]; word32 bufSz; - int rc; printf("test_build_heartbeat...\n"); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; - bufSz = sizeof(buf); - rc = wolfSPDM_BuildHeartbeat(ctx, buf, &bufSz); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildHeartbeat failed"); - TEST_ASSERT(bufSz == 4, "HEARTBEAT should be 4 bytes"); - TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version wrong"); - TEST_ASSERT(buf[1] == SPDM_HEARTBEAT, "Code should be 0xE8"); - TEST_ASSERT(buf[2] == 0x00, "Param1 should be 0"); - TEST_ASSERT(buf[3] == 0x00, "Param2 should be 0"); + ASSERT_SUCCESS(wolfSPDM_BuildHeartbeat(ctx, buf, &bufSz)); + ASSERT_EQ(bufSz, 4, "HEARTBEAT should be 4 bytes"); + ASSERT_EQ(buf[1], SPDM_HEARTBEAT, "Code should be 0xE8"); - /* Buffer too small */ bufSz = 2; - rc = wolfSPDM_BuildHeartbeat(ctx, buf, &bufSz); - TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + ASSERT_EQ(wolfSPDM_BuildHeartbeat(ctx, buf, &bufSz), WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1116,19 +893,12 @@ static int test_parse_heartbeat_ack(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte ack[] = {0x12, SPDM_HEARTBEAT_ACK, 0x00, 0x00}; byte err[] = {0x12, SPDM_ERROR, 0x01, 0x00}; - int rc; printf("test_parse_heartbeat_ack...\n"); wolfSPDM_Init(ctx); - - /* Valid ACK */ - rc = wolfSPDM_ParseHeartbeatAck(ctx, ack, sizeof(ack)); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Valid ACK should succeed"); - - /* Error response */ - rc = wolfSPDM_ParseHeartbeatAck(ctx, err, sizeof(err)); - TEST_ASSERT(rc == WOLFSPDM_E_PEER_ERROR, "Error should return PEER_ERROR"); + ASSERT_SUCCESS(wolfSPDM_ParseHeartbeatAck(ctx, ack, sizeof(ack))); + ASSERT_EQ(wolfSPDM_ParseHeartbeatAck(ctx, err, sizeof(err)), WOLFSPDM_E_PEER_ERROR, "Error should return PEER_ERROR"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1138,16 +908,11 @@ static int test_heartbeat_state_check(void) { WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; - int rc; printf("test_heartbeat_state_check...\n"); wolfSPDM_Init(ctx); - - /* Not connected — should fail */ - rc = wolfSPDM_Heartbeat(ctx); - TEST_ASSERT(rc == WOLFSPDM_E_NOT_CONNECTED, - "Heartbeat should fail when not connected"); + ASSERT_EQ(wolfSPDM_Heartbeat(ctx), WOLFSPDM_E_NOT_CONNECTED, "Heartbeat should fail when not connected"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1164,29 +929,21 @@ static int test_build_key_update(void) byte buf[16]; word32 bufSz; byte tag = 0; - int rc; printf("test_build_key_update...\n"); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; - bufSz = sizeof(buf); - rc = wolfSPDM_BuildKeyUpdate(ctx, buf, &bufSz, - SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, &tag); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "BuildKeyUpdate failed"); - TEST_ASSERT(bufSz == 4, "KEY_UPDATE should be 4 bytes"); - TEST_ASSERT(buf[0] == SPDM_VERSION_12, "Version wrong"); - TEST_ASSERT(buf[1] == SPDM_KEY_UPDATE, "Code should be 0xE9"); - TEST_ASSERT(buf[2] == SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, - "Operation should be UpdateAllKeys"); - TEST_ASSERT(buf[3] == tag, "Tag should match returned value"); + ASSERT_SUCCESS(wolfSPDM_BuildKeyUpdate(ctx, buf, &bufSz, SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, &tag)); + ASSERT_EQ(bufSz, 4, "KEY_UPDATE should be 4 bytes"); + ASSERT_EQ(buf[1], SPDM_KEY_UPDATE, "Code should be 0xE9"); + ASSERT_EQ(buf[2], SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, "Operation should be UpdateAllKeys"); + ASSERT_EQ(buf[3], tag, "Tag should match returned value"); - /* Buffer too small */ bufSz = 2; - rc = wolfSPDM_BuildKeyUpdate(ctx, buf, &bufSz, - SPDM_KEY_UPDATE_OP_UPDATE_KEY, &tag); - TEST_ASSERT(rc == WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); + ASSERT_EQ(wolfSPDM_BuildKeyUpdate(ctx, buf, &bufSz, SPDM_KEY_UPDATE_OP_UPDATE_KEY, &tag), + WOLFSPDM_E_BUFFER_SMALL, "Should fail on small buffer"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1197,26 +954,15 @@ static int test_parse_key_update_ack(void) WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; byte ack[] = {0x12, SPDM_KEY_UPDATE_ACK, 0x02, 0x42}; - int rc; printf("test_parse_key_update_ack...\n"); wolfSPDM_Init(ctx); - - /* Valid ACK with matching operation and tag */ - rc = wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), - SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, 0x42); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "Valid ACK should succeed"); - - /* Mismatched tag */ - rc = wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), - SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, 0xFF); - TEST_ASSERT(rc == WOLFSPDM_E_KEY_UPDATE, "Mismatched tag should fail"); - - /* Mismatched operation */ - rc = wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), - SPDM_KEY_UPDATE_OP_UPDATE_KEY, 0x42); - TEST_ASSERT(rc == WOLFSPDM_E_KEY_UPDATE, "Mismatched op should fail"); + ASSERT_SUCCESS(wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, 0x42)); + ASSERT_EQ(wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), SPDM_KEY_UPDATE_OP_UPDATE_ALL_KEYS, 0xFF), + WOLFSPDM_E_KEY_UPDATE, "Mismatched tag should fail"); + ASSERT_EQ(wolfSPDM_ParseKeyUpdateAck(ctx, ack, sizeof(ack), SPDM_KEY_UPDATE_OP_UPDATE_KEY, 0x42), + WOLFSPDM_E_KEY_UPDATE, "Mismatched op should fail"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1228,43 +974,29 @@ static int test_derive_updated_keys(void) WOLFSPDM_CTX* ctx = &ctxBuf; byte origReqKey[WOLFSPDM_AEAD_KEY_SIZE]; byte origRspKey[WOLFSPDM_AEAD_KEY_SIZE]; - int rc; printf("test_derive_updated_keys...\n"); wolfSPDM_Init(ctx); ctx->spdmVersion = SPDM_VERSION_12; - - /* Set up fake app secrets and keys */ XMEMSET(ctx->reqAppSecret, 0x5A, WOLFSPDM_HASH_SIZE); XMEMSET(ctx->rspAppSecret, 0xA5, WOLFSPDM_HASH_SIZE); XMEMSET(ctx->reqDataKey, 0x11, WOLFSPDM_AEAD_KEY_SIZE); XMEMSET(ctx->rspDataKey, 0x22, WOLFSPDM_AEAD_KEY_SIZE); - XMEMCPY(origReqKey, ctx->reqDataKey, WOLFSPDM_AEAD_KEY_SIZE); XMEMCPY(origRspKey, ctx->rspDataKey, WOLFSPDM_AEAD_KEY_SIZE); /* Update all keys */ - rc = wolfSPDM_DeriveUpdatedKeys(ctx, 1); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "DeriveUpdatedKeys failed"); - - /* Keys should have changed */ - TEST_ASSERT(memcmp(ctx->reqDataKey, origReqKey, - WOLFSPDM_AEAD_KEY_SIZE) != 0, "Req key should change"); - TEST_ASSERT(memcmp(ctx->rspDataKey, origRspKey, - WOLFSPDM_AEAD_KEY_SIZE) != 0, "Rsp key should change"); + ASSERT_SUCCESS(wolfSPDM_DeriveUpdatedKeys(ctx, 1)); + ASSERT_NE(memcmp(ctx->reqDataKey, origReqKey, WOLFSPDM_AEAD_KEY_SIZE), 0, "Req key should change"); + ASSERT_NE(memcmp(ctx->rspDataKey, origRspKey, WOLFSPDM_AEAD_KEY_SIZE), 0, "Rsp key should change"); /* Update requester only */ XMEMCPY(origReqKey, ctx->reqDataKey, WOLFSPDM_AEAD_KEY_SIZE); XMEMCPY(origRspKey, ctx->rspDataKey, WOLFSPDM_AEAD_KEY_SIZE); - - rc = wolfSPDM_DeriveUpdatedKeys(ctx, 0); - TEST_ASSERT(rc == WOLFSPDM_SUCCESS, "DeriveUpdatedKeys (req only) failed"); - - TEST_ASSERT(memcmp(ctx->reqDataKey, origReqKey, - WOLFSPDM_AEAD_KEY_SIZE) != 0, "Req key should change"); - TEST_ASSERT(memcmp(ctx->rspDataKey, origRspKey, - WOLFSPDM_AEAD_KEY_SIZE) == 0, "Rsp key should NOT change"); + ASSERT_SUCCESS(wolfSPDM_DeriveUpdatedKeys(ctx, 0)); + ASSERT_NE(memcmp(ctx->reqDataKey, origReqKey, WOLFSPDM_AEAD_KEY_SIZE), 0, "Req key should change"); + ASSERT_EQ(memcmp(ctx->rspDataKey, origRspKey, WOLFSPDM_AEAD_KEY_SIZE), 0, "Rsp key should NOT change"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1274,16 +1006,11 @@ static int test_key_update_state_check(void) { WOLFSPDM_CTX ctxBuf; WOLFSPDM_CTX* ctx = &ctxBuf; - int rc; printf("test_key_update_state_check...\n"); wolfSPDM_Init(ctx); - - /* Not connected — should fail */ - rc = wolfSPDM_KeyUpdate(ctx, 1); - TEST_ASSERT(rc == WOLFSPDM_E_NOT_CONNECTED, - "KeyUpdate should fail when not connected"); + ASSERT_EQ(wolfSPDM_KeyUpdate(ctx, 1), WOLFSPDM_E_NOT_CONNECTED, "KeyUpdate should fail when not connected"); wolfSPDM_Free(ctx); TEST_PASS(); @@ -1301,20 +1028,16 @@ static int test_session_state(void) printf("test_session_state...\n"); wolfSPDM_Init(ctx); - - TEST_ASSERT(wolfSPDM_IsConnected(ctx) == 0, "Should not be connected"); - TEST_ASSERT(wolfSPDM_GetSessionId(ctx) == 0, "SessionId should be 0"); - TEST_ASSERT(wolfSPDM_GetVersion_Negotiated(ctx) == 0, "Version should be 0"); + ASSERT_EQ(wolfSPDM_IsConnected(ctx), 0, "Should not be connected"); + ASSERT_EQ(wolfSPDM_GetSessionId(ctx), 0, "SessionId should be 0"); /* Simulate connected state */ ctx->state = WOLFSPDM_STATE_CONNECTED; ctx->sessionId = 0xAABBCCDD; ctx->spdmVersion = SPDM_VERSION_12; - - TEST_ASSERT(wolfSPDM_IsConnected(ctx) == 1, "Should be connected"); - TEST_ASSERT(wolfSPDM_GetSessionId(ctx) == 0xAABBCCDD, "SessionId wrong"); - TEST_ASSERT(wolfSPDM_GetVersion_Negotiated(ctx) == SPDM_VERSION_12, - "Version wrong"); + ASSERT_EQ(wolfSPDM_IsConnected(ctx), 1, "Should be connected"); + ASSERT_EQ(wolfSPDM_GetSessionId(ctx), (word32)0xAABBCCDD, "SessionId wrong"); + ASSERT_EQ(wolfSPDM_GetVersion_Negotiated(ctx), SPDM_VERSION_12, "Version wrong"); wolfSPDM_Free(ctx); TEST_PASS(); From 27abefaa8dd002d51fadd3b21d4b18f4159daa99 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 17 Feb 2026 17:32:18 +0000 Subject: [PATCH 3/8] =?UTF-8?q?Code=20Size=20Reduction=20(Phase=202)=20?= =?UTF-8?q?=E2=80=94=20473=20net=20lines=20removed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactoring & consolidation: - Fixed cppcheck warning: narrowed blk variable scope in spdm_msg.c - Extracted wolfSPDM_BuildSimpleMsg() helper — consolidated 3 identical 4-byte message builders (GetDigests, EndSession, Heartbeat) - Added SPDM_CONNECT_STEP macro — compressed 7 connect steps in ConnectStandard and 3 in ConnectNuvoton - Moved 8 byte-order helpers from spdm_nuvoton.c to spdm_internal.h as static inline, added SPDM_Set32LE/SPDM_Get32LE — replaced 30+ inline byte-shift operations across spdm_msg.c and spdm_secured.c - Extracted wolfSPDM_Sha384Hash() helper — replaced 7 Init/Update/Final/Free SHA-384 boilerplate blocks across 3 files - Added wolfSPDM_BuildIV() inline helper — deduplicated 4 IV XOR construction blocks (Nuvoton 8-byte + MCTP 2-byte) in encrypt/decrypt - Wrapped 4 unused public API functions behind #ifndef WOLFSPDM_LEAN (EncryptMessage, DecryptMessage, SendData, ReceiveData) - Added SPDM_CHECK_ERROR_RSP macro and replaced GivePubKey/SetOnlyMode manual encrypt-send-decrypt with wolfSPDM_SecuredExchange() calls - Simplified tail error-return patterns in SecuredExchange and Finish - Replaced all 77 bulky 3-line /* ====... */ section dividers with single-line /* --- Section --- */ style across all src/*.c and header files - Added feature detection macros (WOLFSPDM_HAS_MEASUREMENTS, etc.) to spdm.h CI addition: - Added spdm-emu-test.yml — full integration test workflow that builds wolfSSL, wolfSPDM, spdm-emu, and wolfTPM, then runs all 6 SPDM emulator tests (session, signed/unsigned measurements, challenge, heartbeat, key update) --- .github/workflows/spdm-emu-test.yml | 96 ++++++++++ src/spdm_context.c | 113 +++--------- src/spdm_crypto.c | 17 +- src/spdm_internal.h | 145 +++++++++------ src/spdm_kdf.c | 9 +- src/spdm_msg.c | 171 +++++------------- src/spdm_nuvoton.c | 264 ++++------------------------ src/spdm_secured.c | 145 ++++----------- src/spdm_session.c | 20 +-- src/spdm_transcript.c | 85 ++++----- wolfspdm/spdm.h | 96 ++++------ wolfspdm/spdm_nuvoton.h | 32 +--- wolfspdm/spdm_types.h | 30 +--- 13 files changed, 423 insertions(+), 800 deletions(-) create mode 100644 .github/workflows/spdm-emu-test.yml diff --git a/.github/workflows/spdm-emu-test.yml b/.github/workflows/spdm-emu-test.yml new file mode 100644 index 0000000..df58037 --- /dev/null +++ b/.github/workflows/spdm-emu-test.yml @@ -0,0 +1,96 @@ +name: SPDM Emulator Integration Test + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + spdm-emu-test: + name: SPDM emulator integration test + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y autoconf automake libtool cmake libmbedtls-dev + + # --- Build wolfSSL --- + - name: Cache wolfSSL + id: cache-wolfssl + uses: actions/cache@v4 + with: + path: ~/wolfssl-install + key: wolfssl-emu-test-v1 + + - name: Build wolfSSL + if: steps.cache-wolfssl.outputs.cache-hit != 'true' + run: | + cd ~ + git clone --depth 1 https://github.com/wolfSSL/wolfssl.git + cd wolfssl + ./autogen.sh + ./configure --enable-wolftpm --enable-all \ + --prefix=$HOME/wolfssl-install + make -j$(nproc) + make install + + # --- Build wolfSPDM --- + - name: Build and install wolfSPDM + run: | + ./autogen.sh + ./configure --with-wolfssl=$HOME/wolfssl-install \ + --prefix=$HOME/wolfspdm-install + make -j$(nproc) + make install + + # --- Build spdm-emu --- + - name: Cache spdm-emu + id: cache-spdm-emu + uses: actions/cache@v4 + with: + path: ~/spdm-emu + key: spdm-emu-v1 + + - name: Build spdm-emu + if: steps.cache-spdm-emu.outputs.cache-hit != 'true' + run: | + cd ~ + git clone --depth 1 https://github.com/DMTF/spdm-emu.git + cd spdm-emu + mkdir build && cd build + cmake -DARCH=x64 -DTOOLCHAIN=GCC -DTARGET=Release -DCRYPTO=mbedtls .. + make copy_sample_key && make -j$(nproc) + + # --- Build wolfTPM --- + - name: Build wolfTPM + run: | + cd ~ + git clone --depth 1 https://github.com/wolfSSL/wolfTPM.git wolfTPM + cd wolfTPM + ./autogen.sh + ./configure --enable-spdm --enable-swtpm \ + --with-wolfspdm=$HOME/wolfspdm-install \ + --with-wolfssl=$HOME/wolfssl-install + make -j$(nproc) + + # --- Run integration tests --- + - name: Run SPDM emulator tests + run: | + cd ~/wolfTPM + export LD_LIBRARY_PATH=$HOME/wolfspdm-install/lib:$HOME/wolfssl-install/lib + export SPDM_EMU_PATH=$HOME/spdm-emu/build/bin + ./examples/spdm/spdm_test.sh --emu + + - name: Upload logs on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: spdm-emu-test-logs + path: | + config.log + ~/wolfTPM/config.log diff --git a/src/spdm_context.c b/src/spdm_context.c index 2bdb6f0..f9b498d 100644 --- a/src/spdm_context.c +++ b/src/spdm_context.c @@ -25,9 +25,7 @@ #include #include -/* ========================================================================== - * Context Management - * ========================================================================== */ +/* --- Context Management --- */ int wolfSPDM_Init(WOLFSPDM_CTX* ctx) { @@ -144,9 +142,7 @@ int wolfSPDM_InitStatic(WOLFSPDM_CTX* ctx, int size) return wolfSPDM_Init(ctx); } -/* ========================================================================== - * Configuration - * ========================================================================== */ +/* --- Configuration --- */ int wolfSPDM_SetIO(WOLFSPDM_CTX* ctx, WOLFSPDM_IO_CB ioCb, void* userCtx) { @@ -271,9 +267,7 @@ WOLFSPDM_MODE wolfSPDM_GetMode(WOLFSPDM_CTX* ctx) return ctx->mode; } -/* ========================================================================== - * Session Status - * ========================================================================== */ +/* --- Session Status --- */ int wolfSPDM_IsConnected(WOLFSPDM_CTX* ctx) { @@ -317,9 +311,7 @@ word16 wolfSPDM_GetFipsIndicator(WOLFSPDM_CTX* ctx) } #endif -/* ========================================================================== - * Session Establishment - Connect (Full Handshake) - * ========================================================================== */ +/* --- Session Establishment - Connect (Full Handshake) --- */ /* Standard SPDM 1.2 connection flow (for libspdm emulator, etc.) */ static int wolfSPDM_ConnectStandard(WOLFSPDM_CTX* ctx) @@ -330,77 +322,30 @@ static int wolfSPDM_ConnectStandard(WOLFSPDM_CTX* ctx) ctx->state = WOLFSPDM_STATE_INIT; wolfSPDM_TranscriptReset(ctx); - /* Step 1: GET_VERSION / VERSION */ - wolfSPDM_DebugPrint(ctx, "Step 1: GET_VERSION\n"); - rc = wolfSPDM_GetVersion(ctx); - if (rc != WOLFSPDM_SUCCESS) { - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } - - /* Step 2: GET_CAPABILITIES / CAPABILITIES */ - wolfSPDM_DebugPrint(ctx, "Step 2: GET_CAPABILITIES\n"); - rc = wolfSPDM_GetCapabilities(ctx); - if (rc != WOLFSPDM_SUCCESS) { - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } - - /* Step 3: NEGOTIATE_ALGORITHMS / ALGORITHMS */ - wolfSPDM_DebugPrint(ctx, "Step 3: NEGOTIATE_ALGORITHMS\n"); - rc = wolfSPDM_NegotiateAlgorithms(ctx); - if (rc != WOLFSPDM_SUCCESS) { - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } - - /* Step 4: GET_DIGESTS / DIGESTS */ - wolfSPDM_DebugPrint(ctx, "Step 4: GET_DIGESTS\n"); - rc = wolfSPDM_GetDigests(ctx); - if (rc != WOLFSPDM_SUCCESS) { - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } - - /* Step 5: GET_CERTIFICATE / CERTIFICATE */ - wolfSPDM_DebugPrint(ctx, "Step 5: GET_CERTIFICATE\n"); - rc = wolfSPDM_GetCertificate(ctx, 0); /* Slot 0 */ - if (rc != WOLFSPDM_SUCCESS) { - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } - - /* Validate certificate chain if trusted CAs are loaded. - * Public key extraction now happens automatically in GetCertificate. */ + SPDM_CONNECT_STEP(ctx, "Step 1: GET_VERSION\n", + wolfSPDM_GetVersion(ctx)); + SPDM_CONNECT_STEP(ctx, "Step 2: GET_CAPABILITIES\n", + wolfSPDM_GetCapabilities(ctx)); + SPDM_CONNECT_STEP(ctx, "Step 3: NEGOTIATE_ALGORITHMS\n", + wolfSPDM_NegotiateAlgorithms(ctx)); + SPDM_CONNECT_STEP(ctx, "Step 4: GET_DIGESTS\n", + wolfSPDM_GetDigests(ctx)); + SPDM_CONNECT_STEP(ctx, "Step 5: GET_CERTIFICATE\n", + wolfSPDM_GetCertificate(ctx, 0)); + + /* Validate certificate chain if trusted CAs are loaded */ if (ctx->hasTrustedCAs) { - rc = wolfSPDM_ValidateCertChain(ctx); - if (rc != WOLFSPDM_SUCCESS) { - wolfSPDM_DebugPrint(ctx, - "Certificate chain validation failed (%d)\n", rc); - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } + SPDM_CONNECT_STEP(ctx, "", wolfSPDM_ValidateCertChain(ctx)); } else if (!ctx->hasResponderPubKey) { wolfSPDM_DebugPrint(ctx, "Warning: No trusted CAs loaded — chain not validated\n"); } - /* Step 6: KEY_EXCHANGE / KEY_EXCHANGE_RSP */ - wolfSPDM_DebugPrint(ctx, "Step 6: KEY_EXCHANGE\n"); - rc = wolfSPDM_KeyExchange(ctx); - if (rc != WOLFSPDM_SUCCESS) { - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } - - /* Step 7: FINISH / FINISH_RSP */ - wolfSPDM_DebugPrint(ctx, "Step 7: FINISH\n"); - rc = wolfSPDM_Finish(ctx); - if (rc != WOLFSPDM_SUCCESS) { - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } + SPDM_CONNECT_STEP(ctx, "Step 6: KEY_EXCHANGE\n", + wolfSPDM_KeyExchange(ctx)); + SPDM_CONNECT_STEP(ctx, "Step 7: FINISH\n", + wolfSPDM_Finish(ctx)); ctx->state = WOLFSPDM_STATE_CONNECTED; wolfSPDM_DebugPrint(ctx, "SPDM Session Established! SessionID=0x%08x\n", @@ -468,9 +413,7 @@ int wolfSPDM_Disconnect(WOLFSPDM_CTX* ctx) return (rc == WOLFSPDM_SUCCESS) ? WOLFSPDM_SUCCESS : rc; } -/* ========================================================================== - * I/O Helper - * ========================================================================== */ +/* --- I/O Helper --- */ int wolfSPDM_SendReceive(WOLFSPDM_CTX* ctx, const byte* txBuf, word32 txSz, @@ -490,9 +433,7 @@ int wolfSPDM_SendReceive(WOLFSPDM_CTX* ctx, return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * Debug Utilities - * ========================================================================== */ +/* --- Debug Utilities --- */ void wolfSPDM_DebugPrint(WOLFSPDM_CTX* ctx, const char* fmt, ...) { @@ -529,9 +470,7 @@ void wolfSPDM_DebugHex(WOLFSPDM_CTX* ctx, const char* label, fflush(stdout); } -/* ========================================================================== - * Measurement Accessors - * ========================================================================== */ +/* --- Measurement Accessors --- */ #ifndef NO_WOLFSPDM_MEAS @@ -581,9 +520,7 @@ int wolfSPDM_GetMeasurementBlock(WOLFSPDM_CTX* ctx, int blockIdx, #endif /* !NO_WOLFSPDM_MEAS */ -/* ========================================================================== - * Error String - * ========================================================================== */ +/* --- Error String --- */ const char* wolfSPDM_GetErrorString(int error) { diff --git a/src/spdm_crypto.c b/src/spdm_crypto.c index 0dd935b..4018628 100644 --- a/src/spdm_crypto.c +++ b/src/spdm_crypto.c @@ -22,9 +22,7 @@ #include "spdm_internal.h" #include -/* ========================================================================== - * Random Number Generation - * ========================================================================== */ +/* --- Random Number Generation --- */ int wolfSPDM_GetRandom(WOLFSPDM_CTX* ctx, byte* out, word32 outSz) { @@ -46,9 +44,7 @@ int wolfSPDM_GetRandom(WOLFSPDM_CTX* ctx, byte* out, word32 outSz) return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * ECDHE Key Generation (P-384) - * ========================================================================== */ +/* --- ECDHE Key Generation (P-384) --- */ int wolfSPDM_GenerateEphemeralKey(WOLFSPDM_CTX* ctx) { @@ -116,9 +112,7 @@ int wolfSPDM_ExportEphemeralPubKey(WOLFSPDM_CTX* ctx, return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * ECDH Shared Secret Computation - * ========================================================================== */ +/* --- ECDH Shared Secret Computation --- */ int wolfSPDM_ComputeSharedSecret(WOLFSPDM_CTX* ctx, const byte* peerPubKeyX, const byte* peerPubKeyY) @@ -182,10 +176,7 @@ int wolfSPDM_ComputeSharedSecret(WOLFSPDM_CTX* ctx, return (rc == 0) ? WOLFSPDM_SUCCESS : WOLFSPDM_E_CRYPTO_FAIL; } -/* ========================================================================== - * ECDSA Signing (P-384) - * Used for requester's signature in FINISH during mutual authentication - * ========================================================================== */ +/* --- ECDSA Signing (P-384) --- */ int wolfSPDM_SignHash(WOLFSPDM_CTX* ctx, const byte* hash, word32 hashSz, byte* sig, word32* sigSz) diff --git a/src/spdm_internal.h b/src/spdm_internal.h index 5146587..0fcccfc 100644 --- a/src/spdm_internal.h +++ b/src/spdm_internal.h @@ -51,9 +51,7 @@ extern "C" { #endif -/* ========================================================================== - * State Machine Constants - * ========================================================================== */ +/* --- State Machine Constants --- */ #define WOLFSPDM_STATE_INIT 0 /* Initial state */ #define WOLFSPDM_STATE_VERSION 1 /* GET_VERSION complete */ @@ -69,9 +67,7 @@ extern "C" { #define WOLFSPDM_STATE_MEASURED 10 /* Measurements retrieved */ #endif -/* ========================================================================== - * Measurement Block Structure - * ========================================================================== */ +/* --- Measurement Block Structure --- */ #ifndef NO_WOLFSPDM_MEAS typedef struct WOLFSPDM_MEAS_BLOCK { @@ -83,9 +79,7 @@ typedef struct WOLFSPDM_MEAS_BLOCK { } WOLFSPDM_MEAS_BLOCK; #endif /* !NO_WOLFSPDM_MEAS */ -/* ========================================================================== - * Internal Context Structure - * ========================================================================== */ +/* --- Internal Context Structure --- */ struct WOLFSPDM_CTX { /* State machine */ @@ -231,9 +225,78 @@ struct WOLFSPDM_CTX { byte rspAppSecret[WOLFSPDM_HASH_SIZE]; /* 48 bytes */ }; -/* ========================================================================== - * Argument Validation Macros - * ========================================================================== */ +/* --- Byte-Order Helpers --- */ + +static WC_INLINE void SPDM_Set16LE(byte* buf, word16 val) { + buf[0] = (byte)(val & 0xFF); buf[1] = (byte)(val >> 8); +} +static WC_INLINE word16 SPDM_Get16LE(const byte* buf) { + return (word16)(buf[0] | (buf[1] << 8)); +} +static WC_INLINE void SPDM_Set16BE(byte* buf, word16 val) { + buf[0] = (byte)(val >> 8); buf[1] = (byte)(val & 0xFF); +} +static WC_INLINE word16 SPDM_Get16BE(const byte* buf) { + return (word16)((buf[0] << 8) | buf[1]); +} +static WC_INLINE void SPDM_Set32LE(byte* buf, word32 val) { + buf[0] = (byte)(val & 0xFF); buf[1] = (byte)((val >> 8) & 0xFF); + buf[2] = (byte)((val >> 16) & 0xFF); buf[3] = (byte)((val >> 24) & 0xFF); +} +static WC_INLINE word32 SPDM_Get32LE(const byte* buf) { + return (word32)buf[0] | ((word32)buf[1] << 8) | + ((word32)buf[2] << 16) | ((word32)buf[3] << 24); +} +static WC_INLINE void SPDM_Set32BE(byte* buf, word32 val) { + buf[0] = (byte)(val >> 24); buf[1] = (byte)((val >> 16) & 0xFF); + buf[2] = (byte)((val >> 8) & 0xFF); buf[3] = (byte)(val & 0xFF); +} +static WC_INLINE word32 SPDM_Get32BE(const byte* buf) { + return ((word32)buf[0] << 24) | ((word32)buf[1] << 16) | + ((word32)buf[2] << 8) | (word32)buf[3]; +} +static WC_INLINE void SPDM_Set64LE(byte* buf, word64 val) { + buf[0] = (byte)(val & 0xFF); buf[1] = (byte)((val >> 8) & 0xFF); + buf[2] = (byte)((val >> 16) & 0xFF); buf[3] = (byte)((val >> 24) & 0xFF); + buf[4] = (byte)((val >> 32) & 0xFF); buf[5] = (byte)((val >> 40) & 0xFF); + buf[6] = (byte)((val >> 48) & 0xFF); buf[7] = (byte)((val >> 56) & 0xFF); +} +static WC_INLINE word64 SPDM_Get64LE(const byte* buf) { + return (word64)buf[0] | ((word64)buf[1] << 8) | + ((word64)buf[2] << 16) | ((word64)buf[3] << 24) | + ((word64)buf[4] << 32) | ((word64)buf[5] << 40) | + ((word64)buf[6] << 48) | ((word64)buf[7] << 56); +} + +/* Build IV: BaseIV XOR zero-extended sequence number (DSP0277) */ +static WC_INLINE void wolfSPDM_BuildIV(byte* iv, const byte* baseIv, + word64 seqNum, int nuvotonMode) +{ + XMEMCPY(iv, baseIv, WOLFSPDM_AEAD_IV_SIZE); +#ifdef WOLFSPDM_NUVOTON + if (nuvotonMode) { + byte seq[8]; int i; + SPDM_Set64LE(seq, seqNum); + for (i = 0; i < 8; i++) iv[i] ^= seq[i]; + } + else +#endif + { + (void)nuvotonMode; + iv[0] ^= (byte)(seqNum & 0xFF); + iv[1] ^= (byte)((seqNum >> 8) & 0xFF); + } +} + +/* --- Connect Step Macro --- */ + +#define SPDM_CONNECT_STEP(ctx, msg, func) do { \ + wolfSPDM_DebugPrint(ctx, msg); \ + rc = func; \ + if (rc != WOLFSPDM_SUCCESS) { ctx->state = WOLFSPDM_STATE_ERROR; return rc; } \ +} while (0) + +/* --- Argument Validation Macros --- */ #define SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, minSz) \ if ((ctx) == NULL || (buf) == NULL || (bufSz) == NULL || *(bufSz) < (minSz)) \ @@ -243,9 +306,7 @@ struct WOLFSPDM_CTX { if ((ctx) == NULL || (buf) == NULL || (bufSz) < (minSz)) \ return WOLFSPDM_E_INVALID_ARG -/* ========================================================================== - * Response Code Check Macro - * ========================================================================== */ +/* --- Response Code Check Macro --- */ #define SPDM_CHECK_RESPONSE(ctx, buf, bufSz, expected, fallbackErr) \ do { \ @@ -259,9 +320,7 @@ struct WOLFSPDM_CTX { } \ } while (0) -/* ========================================================================== - * Internal Function Declarations - Transcript - * ========================================================================== */ +/* --- Internal Function Declarations - Transcript --- */ /* Reset transcript buffer */ void wolfSPDM_TranscriptReset(WOLFSPDM_CTX* ctx); @@ -278,9 +337,13 @@ int wolfSPDM_TranscriptHash(WOLFSPDM_CTX* ctx, byte* hash); /* Compute Ct = Hash(certificate_chain) */ int wolfSPDM_ComputeCertChainHash(WOLFSPDM_CTX* ctx); -/* ========================================================================== - * Internal Function Declarations - Crypto - * ========================================================================== */ +/* SHA-384 hash helper: Hash(d1 || d2 || d3), pass NULL/0 for unused buffers */ +int wolfSPDM_Sha384Hash(byte* out, + const byte* d1, word32 d1Sz, + const byte* d2, word32 d2Sz, + const byte* d3, word32 d3Sz); + +/* --- Internal Function Declarations - Crypto --- */ /* Generate ephemeral P-384 key for ECDHE */ int wolfSPDM_GenerateEphemeralKey(WOLFSPDM_CTX* ctx); @@ -301,9 +364,7 @@ int wolfSPDM_GetRandom(WOLFSPDM_CTX* ctx, byte* out, word32 outSz); int wolfSPDM_SignHash(WOLFSPDM_CTX* ctx, const byte* hash, word32 hashSz, byte* sig, word32* sigSz); -/* ========================================================================== - * Internal Function Declarations - Key Derivation - * ========================================================================== */ +/* --- Internal Function Declarations - Key Derivation --- */ /* Derive all keys from shared secret and TH1 */ int wolfSPDM_DeriveHandshakeKeys(WOLFSPDM_CTX* ctx, const byte* th1Hash); @@ -320,9 +381,7 @@ int wolfSPDM_HkdfExpandLabel(byte spdmVersion, const byte* secret, word32 secret int wolfSPDM_ComputeVerifyData(const byte* finishedKey, const byte* thHash, byte* verifyData); -/* ========================================================================== - * Internal Function Declarations - Message Building - * ========================================================================== */ +/* --- Internal Function Declarations - Message Building --- */ /* Build GET_VERSION request */ int wolfSPDM_BuildGetVersion(byte* buf, word32* bufSz); @@ -349,9 +408,7 @@ int wolfSPDM_BuildFinish(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz); /* Build END_SESSION request */ int wolfSPDM_BuildEndSession(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz); -/* ========================================================================== - * Internal Function Declarations - Message Parsing - * ========================================================================== */ +/* --- Internal Function Declarations - Message Parsing --- */ /* Parse VERSION response */ int wolfSPDM_ParseVersion(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz); @@ -378,9 +435,7 @@ int wolfSPDM_ParseFinishRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz); /* Check for ERROR response */ int wolfSPDM_CheckError(const byte* buf, word32 bufSz, int* errorCode); -/* ========================================================================== - * Internal Function Declarations - Secured Messaging - * ========================================================================== */ +/* --- Internal Function Declarations - Secured Messaging --- */ /* Encrypt plaintext using session keys */ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, @@ -392,9 +447,7 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, const byte* enc, word32 encSz, byte* plain, word32* plainSz); -/* ========================================================================== - * Internal Utility Functions - * ========================================================================== */ +/* --- Internal Utility Functions --- */ /* Send message via I/O callback and receive response */ int wolfSPDM_SendReceive(WOLFSPDM_CTX* ctx, @@ -408,9 +461,7 @@ void wolfSPDM_DebugPrint(WOLFSPDM_CTX* ctx, const char* fmt, ...); void wolfSPDM_DebugHex(WOLFSPDM_CTX* ctx, const char* label, const byte* data, word32 len); -/* ========================================================================== - * Internal Function Declarations - Measurements - * ========================================================================== */ +/* --- Internal Function Declarations - Measurements --- */ #ifndef NO_WOLFSPDM_MEAS /* Build GET_MEASUREMENTS request */ @@ -428,9 +479,7 @@ int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, #endif /* !NO_WOLFSPDM_MEAS_VERIFY */ #endif /* !NO_WOLFSPDM_MEAS */ -/* ========================================================================== - * Internal Function Declarations - Certificate Chain Validation - * ========================================================================== */ +/* --- Internal Function Declarations - Certificate Chain Validation --- */ /* Extract responder's public key from certificate chain leaf cert */ int wolfSPDM_ExtractResponderPubKey(WOLFSPDM_CTX* ctx); @@ -438,9 +487,7 @@ int wolfSPDM_ExtractResponderPubKey(WOLFSPDM_CTX* ctx); /* Validate certificate chain using trusted CAs and extract public key */ int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx); -/* ========================================================================== - * Internal Function Declarations - Challenge - * ========================================================================== */ +/* --- Internal Function Declarations - Challenge --- */ #ifndef NO_WOLFSPDM_CHALLENGE /* Build CHALLENGE request */ @@ -457,9 +504,7 @@ int wolfSPDM_VerifyChallengeAuthSig(WOLFSPDM_CTX* ctx, const byte* reqMsg, word32 reqMsgSz, word32 sigOffset); #endif /* !NO_WOLFSPDM_CHALLENGE */ -/* ========================================================================== - * Internal Function Declarations - Heartbeat - * ========================================================================== */ +/* --- Internal Function Declarations - Heartbeat --- */ /* Build HEARTBEAT request */ int wolfSPDM_BuildHeartbeat(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz); @@ -468,9 +513,7 @@ int wolfSPDM_BuildHeartbeat(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz); int wolfSPDM_ParseHeartbeatAck(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz); -/* ========================================================================== - * Internal Function Declarations - Key Update - * ========================================================================== */ +/* --- Internal Function Declarations - Key Update --- */ /* Build KEY_UPDATE request */ int wolfSPDM_BuildKeyUpdate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, diff --git a/src/spdm_kdf.c b/src/spdm_kdf.c index d1ed519..c6e5a6b 100644 --- a/src/spdm_kdf.c +++ b/src/spdm_kdf.c @@ -284,14 +284,7 @@ int wolfSPDM_DeriveAppDataKeys(WOLFSPDM_CTX* ctx) return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * Key Update Re-derivation (DSP0277) - * - * Per DSP0277, KEY_UPDATE re-derives keys from saved app secrets: - * newAppSecret = HKDF-Expand(oldAppSecret, "update" || version_byte, 48) - * newKey = HKDF-Expand(newAppSecret, "key", 32) - * newIv = HKDF-Expand(newAppSecret, "iv", 12) - * ========================================================================== */ +/* --- Key Update Re-derivation (DSP0277) --- */ int wolfSPDM_DeriveUpdatedKeys(WOLFSPDM_CTX* ctx, int updateAll) { diff --git a/src/spdm_msg.c b/src/spdm_msg.c index 97a40d7..8f5b2bd 100644 --- a/src/spdm_msg.c +++ b/src/spdm_msg.c @@ -51,10 +51,7 @@ int wolfSPDM_BuildGetCapabilities(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) /* CTExponent and reserved at offsets 4-7 */ /* Requester flags (4 bytes LE) */ - buf[8] = (byte)(ctx->reqCaps & 0xFF); - buf[9] = (byte)((ctx->reqCaps >> 8) & 0xFF); - buf[10] = (byte)((ctx->reqCaps >> 16) & 0xFF); - buf[11] = (byte)((ctx->reqCaps >> 24) & 0xFF); + SPDM_Set32LE(&buf[8], ctx->reqCaps); /* DataTransferSize (4 LE) */ buf[12] = 0x00; buf[13] = 0x10; buf[14] = 0x00; buf[15] = 0x00; @@ -97,19 +94,23 @@ int wolfSPDM_BuildNegotiateAlgorithms(WOLFSPDM_CTX* ctx, byte* buf, word32* bufS return WOLFSPDM_SUCCESS; } -int wolfSPDM_BuildGetDigests(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) +static int wolfSPDM_BuildSimpleMsg(WOLFSPDM_CTX* ctx, byte msgCode, + byte* buf, word32* bufSz) { SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 4); - - buf[0] = ctx->spdmVersion; /* Use negotiated version */ - buf[1] = SPDM_GET_DIGESTS; + buf[0] = ctx->spdmVersion; + buf[1] = msgCode; buf[2] = 0x00; buf[3] = 0x00; *bufSz = 4; - return WOLFSPDM_SUCCESS; } +int wolfSPDM_BuildGetDigests(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) +{ + return wolfSPDM_BuildSimpleMsg(ctx, SPDM_GET_DIGESTS, buf, bufSz); +} + int wolfSPDM_BuildGetCertificate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, int slotId, word16 offset, word16 length) { @@ -119,10 +120,8 @@ int wolfSPDM_BuildGetCertificate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, buf[1] = SPDM_GET_CERTIFICATE; buf[2] = (byte)(slotId & 0x0F); buf[3] = 0x00; - buf[4] = (byte)(offset & 0xFF); - buf[5] = (byte)((offset >> 8) & 0xFF); - buf[6] = (byte)(length & 0xFF); - buf[7] = (byte)((length >> 8) & 0xFF); + SPDM_Set16LE(&buf[4], offset); + SPDM_Set16LE(&buf[6], length); *bufSz = 8; return WOLFSPDM_SUCCESS; @@ -216,9 +215,7 @@ int wolfSPDM_BuildKeyExchange(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * Shared Signing Helpers - * ========================================================================== */ +/* --- Shared Signing Helpers --- */ /* Build SPDM 1.2+ signed hash per DSP0274: * M = combined_spdm_prefix || zero_pad || context_str || inputDigest @@ -234,7 +231,6 @@ static int wolfSPDM_BuildSignedHash(byte spdmVersion, byte signMsg[200]; /* 64 + 36 + 48 = 148 bytes max */ word32 signMsgLen = 0; word32 zeroPadLen; - wc_Sha384 sha; byte majorVer, minorVer; int i, rc; @@ -264,13 +260,9 @@ static int wolfSPDM_BuildSignedHash(byte spdmVersion, signMsgLen += WOLFSPDM_HASH_SIZE; /* Hash M */ - rc = wc_InitSha384(&sha); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - rc = wc_Sha384Update(&sha, signMsg, signMsgLen); - if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } - rc = wc_Sha384Final(&sha, outputDigest); - wc_Sha384Free(&sha); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + rc = wolfSPDM_Sha384Hash(outputDigest, signMsg, signMsgLen, + NULL, 0, NULL, 0); + if (rc != WOLFSPDM_SUCCESS) return rc; return WOLFSPDM_SUCCESS; } @@ -357,27 +349,11 @@ int wolfSPDM_BuildFinish(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) #ifdef WOLFSPDM_NUVOTON if (mutualAuth && ctx->reqPubKeyTPMTLen > 0) { byte cmHash[WOLFSPDM_HASH_SIZE]; - wc_Sha384 shaCm; - - rc = wc_InitSha384(&shaCm); - if (rc != 0) { - return WOLFSPDM_E_CRYPTO_FAIL; - } - rc = wc_Sha384Update(&shaCm, ctx->reqPubKeyTPMT, ctx->reqPubKeyTPMTLen); - if (rc != 0) { - wc_Sha384Free(&shaCm); - return WOLFSPDM_E_CRYPTO_FAIL; - } - rc = wc_Sha384Final(&shaCm, cmHash); - wc_Sha384Free(&shaCm); - if (rc != 0) { - return WOLFSPDM_E_CRYPTO_FAIL; - } - + rc = wolfSPDM_Sha384Hash(cmHash, ctx->reqPubKeyTPMT, + ctx->reqPubKeyTPMTLen, NULL, 0, NULL, 0); + if (rc != WOLFSPDM_SUCCESS) return rc; rc = wolfSPDM_TranscriptAdd(ctx, cmHash, WOLFSPDM_HASH_SIZE); - if (rc != WOLFSPDM_SUCCESS) { - return rc; - } + if (rc != WOLFSPDM_SUCCESS) return rc; } #endif @@ -455,15 +431,7 @@ int wolfSPDM_BuildFinish(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) int wolfSPDM_BuildEndSession(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) { - SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 4); - - buf[0] = ctx->spdmVersion; /* Use negotiated version */ - buf[1] = SPDM_END_SESSION; - buf[2] = 0x00; - buf[3] = 0x00; - *bufSz = 4; - - return WOLFSPDM_SUCCESS; + return wolfSPDM_BuildSimpleMsg(ctx, SPDM_END_SESSION, buf, bufSz); } int wolfSPDM_CheckError(const byte* buf, word32 bufSz, int* errorCode) @@ -494,7 +462,7 @@ int wolfSPDM_ParseVersion(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) /* Parse VERSION response: * Offset 4-5: VersionNumberEntryCount (LE) * Offset 6+: VersionNumberEntry array (2 bytes each, LE) */ - entryCount = (word16)(buf[4] | (buf[5] << 8)); + entryCount = SPDM_Get16LE(&buf[4]); /* Find highest supported version from entries (capped at 1.3 for now) * @@ -523,8 +491,7 @@ int wolfSPDM_ParseCapabilities(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 12); SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_CAPABILITIES, WOLFSPDM_E_CAPS_MISMATCH); - ctx->rspCaps = (word32)buf[8] | ((word32)buf[9] << 8) | - ((word32)buf[10] << 16) | ((word32)buf[11] << 24); + ctx->rspCaps = SPDM_Get32LE(&buf[8]); ctx->state = WOLFSPDM_STATE_CAPS; wolfSPDM_DebugPrint(ctx, "Responder caps: 0x%08x\n", ctx->rspCaps); @@ -559,8 +526,8 @@ int wolfSPDM_ParseCertificate(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz, SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_CERTIFICATE, WOLFSPDM_E_CERT_FAIL); - *portionLen = (word16)(buf[4] | (buf[5] << 8)); - *remainderLen = (word16)(buf[6] | (buf[7] << 8)); + *portionLen = SPDM_Get16LE(&buf[4]); + *remainderLen = SPDM_Get16LE(&buf[6]); /* Add certificate chain data (starting at offset 8) */ if (*portionLen > 0 && bufSz >= (word32)(8 + *portionLen)) { @@ -589,7 +556,7 @@ int wolfSPDM_ParseKeyExchangeRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufS SPDM_CHECK_PARSE_ARGS(ctx, buf, bufSz, 140); SPDM_CHECK_RESPONSE(ctx, buf, bufSz, SPDM_KEY_EXCHANGE_RSP, WOLFSPDM_E_KEY_EXCHANGE); - ctx->rspSessionId = (word16)(buf[4] | (buf[5] << 8)); + ctx->rspSessionId = SPDM_Get16LE(&buf[4]); ctx->sessionId = (word32)ctx->reqSessionId | ((word32)ctx->rspSessionId << 16); /* Parse MutAuthRequested (offset 6) and ReqSlotIDParam (offset 7) per DSP0274 */ @@ -601,7 +568,7 @@ int wolfSPDM_ParseKeyExchangeRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufS XMEMCPY(peerPubKeyY, &buf[88], WOLFSPDM_ECC_KEY_SIZE); /* OpaqueLen at offset 136 */ - opaqueLen = (word16)(buf[136] | (buf[137] << 8)); + opaqueLen = SPDM_Get16LE(&buf[136]); sigOffset = 138 + opaqueLen; keRspPartialLen = sigOffset; @@ -689,9 +656,7 @@ int wolfSPDM_ParseFinishRsp(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) return WOLFSPDM_E_BAD_STATE; } -/* ========================================================================== - * Measurement Message Building and Parsing - * ========================================================================== */ +/* --- Measurement Message Building and Parsing --- */ #ifndef NO_WOLFSPDM_MEAS @@ -768,7 +733,6 @@ int wolfSPDM_ParseMeasurements(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) /* Parse each measurement block */ for (blockIdx = 0; blockIdx < numBlocks; blockIdx++) { word16 measSize; - WOLFSPDM_MEAS_BLOCK* blk; /* Check block header fits */ if (offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE > recordEnd) { @@ -778,7 +742,7 @@ int wolfSPDM_ParseMeasurements(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) } /* Read block header: Index(1) + MeasSpec(1) + MeasSize(2 LE) */ - measSize = (word16)(buf[offset + 2] | (buf[offset + 3] << 8)); + measSize = SPDM_Get16LE(&buf[offset + 2]); /* Check block data fits */ if (offset + WOLFSPDM_MEAS_BLOCK_HDR_SIZE + measSize > recordEnd) { @@ -789,7 +753,7 @@ int wolfSPDM_ParseMeasurements(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) /* Store if we have room */ if (ctx->measBlockCount < WOLFSPDM_MAX_MEAS_BLOCKS) { - blk = &ctx->measBlocks[ctx->measBlockCount]; + WOLFSPDM_MEAS_BLOCK* blk = &ctx->measBlocks[ctx->measBlockCount]; blk->index = buf[offset]; blk->measurementSpec = buf[offset + 1]; @@ -854,7 +818,7 @@ int wolfSPDM_ParseMeasurements(WOLFSPDM_CTX* ctx, const byte* buf, word32 bufSz) offset += 32; /* OpaqueDataLength (2 LE) */ - word16 opaqueLen = (word16)(buf[offset] | (buf[offset + 1] << 8)); + word16 opaqueLen = SPDM_Get16LE(&buf[offset]); offset += 2; /* Skip opaque data */ @@ -884,7 +848,6 @@ int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, const byte* rspBuf, word32 rspBufSz, const byte* reqMsg, word32 reqMsgSz) { - wc_Sha384 sha; byte digest[WOLFSPDM_HASH_SIZE]; word32 sigOffset; int rc; @@ -917,26 +880,11 @@ int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, /* Compute L1||L2 hash per DSP0274 Section 10.11.1: * L1/L2 = VCA || GET_MEASUREMENTS_request || MEASUREMENTS_response(before sig) */ - rc = wc_InitSha384(&sha); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - - /* VCA = GET_VERSION || VERSION || GET_CAPS || CAPS || NEG_ALGO || ALGO */ - if (ctx->vcaLen > 0) { - rc = wc_Sha384Update(&sha, ctx->transcript, ctx->vcaLen); - if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } - } - - /* GET_MEASUREMENTS request */ - rc = wc_Sha384Update(&sha, reqMsg, reqMsgSz); - if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } - - /* MEASUREMENTS response (everything before signature) */ - rc = wc_Sha384Update(&sha, rspBuf, sigOffset); - if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } - - rc = wc_Sha384Final(&sha, digest); - wc_Sha384Free(&sha); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + rc = wolfSPDM_Sha384Hash(digest, + ctx->transcript, ctx->vcaLen, + reqMsg, reqMsgSz, + rspBuf, sigOffset); + if (rc != WOLFSPDM_SUCCESS) return rc; /* Build M = prefix || zero_pad || context_str || L1L2_hash, then hash it */ rc = wolfSPDM_BuildSignedHash(ctx->spdmVersion, @@ -958,9 +906,7 @@ int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, #endif /* !NO_WOLFSPDM_MEAS_VERIFY */ #endif /* !NO_WOLFSPDM_MEAS */ -/* ========================================================================== - * Responder Public Key Extraction - * ========================================================================== +/* --- Responder Public Key Extraction --- * Extract responder's ECC P-384 public key from the leaf certificate in the * SPDM certificate chain. Used by both measurement signature verification * and CHALLENGE authentication, so it lives outside measurement guards. */ @@ -1086,13 +1032,10 @@ int wolfSPDM_ExtractResponderPubKey(WOLFSPDM_CTX* ctx) return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * Certificate Chain Validation - * ========================================================================== */ +/* --- Certificate Chain Validation --- */ int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx) { - wc_Sha384 sha; byte caHash[WOLFSPDM_HASH_SIZE]; const byte* chainRootHash; int rc; @@ -1110,16 +1053,10 @@ int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx) return WOLFSPDM_E_CERT_PARSE; } - /* Validate the root hash in the SPDM chain header against our trusted CA. - * The root hash at bytes 4-51 is SHA-384(root certificate). - * Compute hash of our trusted CA and compare. */ - rc = wc_InitSha384(&sha); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; - rc = wc_Sha384Update(&sha, ctx->trustedCAs, ctx->trustedCAsSz); - if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } - rc = wc_Sha384Final(&sha, caHash); - wc_Sha384Free(&sha); - if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + /* Validate the root hash against our trusted CA */ + rc = wolfSPDM_Sha384Hash(caHash, ctx->trustedCAs, ctx->trustedCAsSz, + NULL, 0, NULL, 0); + if (rc != WOLFSPDM_SUCCESS) return rc; chainRootHash = ctx->certChain + 4; /* Skip Length(2) + Reserved(2) */ if (XMEMCMP(caHash, chainRootHash, WOLFSPDM_HASH_SIZE) != 0) { @@ -1140,9 +1077,7 @@ int wolfSPDM_ValidateCertChain(WOLFSPDM_CTX* ctx) return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * Challenge Authentication (DSP0274 Section 10.8) - * ========================================================================== */ +/* --- Challenge Authentication (DSP0274 Section 10.8) --- */ #ifndef NO_WOLFSPDM_CHALLENGE @@ -1227,7 +1162,7 @@ int wolfSPDM_ParseChallengeAuth(WOLFSPDM_CTX* ctx, const byte* buf, if (offset + 2 > bufSz) { return WOLFSPDM_E_CHALLENGE; } - opaqueLen = (word16)(buf[offset] | (buf[offset + 1] << 8)); + opaqueLen = SPDM_Get16LE(&buf[offset]); offset += 2; /* Skip opaque data */ @@ -1300,21 +1235,11 @@ int wolfSPDM_VerifyChallengeAuthSig(WOLFSPDM_CTX* ctx, #endif /* !NO_WOLFSPDM_CHALLENGE */ -/* ========================================================================== - * Heartbeat (DSP0274 Section 10.10) - * ========================================================================== */ +/* --- Heartbeat (DSP0274 Section 10.10) --- */ int wolfSPDM_BuildHeartbeat(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz) { - SPDM_CHECK_BUILD_ARGS(ctx, buf, bufSz, 4); - - buf[0] = ctx->spdmVersion; - buf[1] = SPDM_HEARTBEAT; - buf[2] = 0x00; - buf[3] = 0x00; - *bufSz = 4; - - return WOLFSPDM_SUCCESS; + return wolfSPDM_BuildSimpleMsg(ctx, SPDM_HEARTBEAT, buf, bufSz); } int wolfSPDM_ParseHeartbeatAck(WOLFSPDM_CTX* ctx, const byte* buf, @@ -1327,9 +1252,7 @@ int wolfSPDM_ParseHeartbeatAck(WOLFSPDM_CTX* ctx, const byte* buf, return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * Key Update (DSP0274 Section 10.9) - * ========================================================================== */ +/* --- Key Update (DSP0274 Section 10.9) --- */ int wolfSPDM_BuildKeyUpdate(WOLFSPDM_CTX* ctx, byte* buf, word32* bufSz, byte operation, byte* tag) diff --git a/src/spdm_nuvoton.c b/src/spdm_nuvoton.c index c61029c..6738151 100644 --- a/src/spdm_nuvoton.c +++ b/src/spdm_nuvoton.c @@ -36,77 +36,15 @@ #include #include -/* ========================================================================== - * Internal Byte-Order Helpers - * ========================================================================== */ - -/* Store a 16-bit value in big-endian format */ -static void SPDM_Set16BE(byte* buf, word16 val) -{ - buf[0] = (byte)(val >> 8); - buf[1] = (byte)(val & 0xFF); -} - -/* Read a 16-bit value from big-endian format */ -static word16 SPDM_Get16BE(const byte* buf) -{ - return (word16)((buf[0] << 8) | buf[1]); -} - -/* Store a 16-bit value in little-endian format */ -static void SPDM_Set16LE(byte* buf, word16 val) -{ - buf[0] = (byte)(val & 0xFF); - buf[1] = (byte)(val >> 8); -} - -/* Read a 16-bit value from little-endian format */ -static word16 SPDM_Get16LE(const byte* buf) -{ - return (word16)(buf[0] | (buf[1] << 8)); -} - -/* Store a 32-bit value in big-endian format */ -static void SPDM_Set32BE(byte* buf, word32 val) -{ - buf[0] = (byte)(val >> 24); - buf[1] = (byte)(val >> 16); - buf[2] = (byte)(val >> 8); - buf[3] = (byte)(val & 0xFF); -} - -/* Read a 32-bit value from big-endian format */ -static word32 SPDM_Get32BE(const byte* buf) -{ - return ((word32)buf[0] << 24) | ((word32)buf[1] << 16) | - ((word32)buf[2] << 8) | (word32)buf[3]; -} - -/* Store a 64-bit value in little-endian format */ -static void SPDM_Set64LE(byte* buf, word64 val) -{ - buf[0] = (byte)(val & 0xFF); - buf[1] = (byte)((val >> 8) & 0xFF); - buf[2] = (byte)((val >> 16) & 0xFF); - buf[3] = (byte)((val >> 24) & 0xFF); - buf[4] = (byte)((val >> 32) & 0xFF); - buf[5] = (byte)((val >> 40) & 0xFF); - buf[6] = (byte)((val >> 48) & 0xFF); - buf[7] = (byte)((val >> 56) & 0xFF); -} - -/* Read a 64-bit value from little-endian format */ -static word64 SPDM_Get64LE(const byte* buf) -{ - return (word64)buf[0] | ((word64)buf[1] << 8) | - ((word64)buf[2] << 16) | ((word64)buf[3] << 24) | - ((word64)buf[4] << 32) | ((word64)buf[5] << 40) | - ((word64)buf[6] << 48) | ((word64)buf[7] << 56); -} +/* Check for SPDM ERROR in response payload */ +#define SPDM_CHECK_ERROR_RSP(ctx, buf, sz, label) \ + if ((sz) >= 4 && (buf)[1] == SPDM_ERROR) { \ + wolfSPDM_DebugPrint(ctx, label ": SPDM ERROR 0x%02x 0x%02x\n", \ + (buf)[2], (buf)[3]); \ + return WOLFSPDM_E_PEER_ERROR; \ + } -/* ========================================================================== - * TCG SPDM Binding Message Framing - * ========================================================================== */ +/* --- TCG SPDM Binding Message Framing --- */ int wolfSPDM_BuildTcgClearMessage( WOLFSPDM_CTX* ctx, @@ -341,9 +279,7 @@ int wolfSPDM_ParseTcgSecuredMessage( return (int)payloadSz; } -/* ========================================================================== - * SPDM Vendor Defined Message Helpers - * ========================================================================== */ +/* --- SPDM Vendor Defined Message Helpers --- */ /* SPDM message codes */ #define SPDM_VERSION_1_3 0x13 @@ -465,9 +401,7 @@ int wolfSPDM_ParseVendorDefined( return (int)dataLen; } -/* ========================================================================== - * Nuvoton-Specific SPDM Functions - * ========================================================================== */ +/* --- Nuvoton-Specific SPDM Functions --- */ /* Helper: Send TCG clear message and receive response */ static int wolfSPDM_Nuvoton_SendClear( @@ -558,12 +492,7 @@ int wolfSPDM_Nuvoton_GetPubKey( return rc; } - /* Check for SPDM ERROR response */ - if (spdmPayloadSz >= 4 && spdmPayload[1] == SPDM_ERROR) { - wolfSPDM_DebugPrint(ctx, "GET_PUBK: SPDM ERROR 0x%02x 0x%02x\n", - spdmPayload[2], spdmPayload[3]); - return WOLFSPDM_E_PEER_ERROR; - } + SPDM_CHECK_ERROR_RSP(ctx, spdmPayload, spdmPayloadSz, "GET_PUBK"); /* Parse vendor-defined response */ rspPayloadSz = sizeof(rspPayload); @@ -608,10 +537,6 @@ int wolfSPDM_Nuvoton_GivePubKey( int rc; byte spdmMsg[256]; int spdmMsgSz; - byte encBuf[WOLFSPDM_MAX_MSG_SIZE]; - word32 encSz; - byte rxBuf[512]; - word32 rxSz; byte decBuf[256]; word32 decSz; @@ -633,63 +558,18 @@ int wolfSPDM_Nuvoton_GivePubKey( } /* GIVE_PUB is sent as a SECURED (encrypted) message per Nuvoton spec Rev 1.11. - * Section 4.2.4 shows GIVE_PUB_KEY uses tag 0x8201 (secured), not 0x8101 (clear). - * - * Note: GIVE_PUB is an application-phase vendor command, NOT part of the - * SPDM handshake transcript. TH2 only includes handshake messages. */ - - encSz = sizeof(encBuf); - rc = wolfSPDM_EncryptMessage(ctx, spdmMsg, (word32)spdmMsgSz, - encBuf, &encSz); - if (rc != WOLFSPDM_SUCCESS) { - wolfSPDM_DebugPrint(ctx, "GIVE_PUB: Encrypt failed %d\n", rc); - return rc; - } - - /* Send encrypted message */ - if (ctx->ioCb == NULL) { - return WOLFSPDM_E_IO_FAIL; - } - - rxSz = sizeof(rxBuf); - rc = ctx->ioCb(ctx, encBuf, encSz, rxBuf, &rxSz, ctx->ioUserCtx); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "GIVE_PUB: I/O failed %d\n", rc); - return WOLFSPDM_E_IO_FAIL; - } - - /* Check if response is unencrypted SPDM message (likely an error). - * SPDM messages start with version byte (0x10-0x1F). - * Encrypted records start with session ID (first byte is low byte of reqSessionId). */ - if (rxSz >= 2 && rxBuf[0] >= 0x10 && rxBuf[0] <= 0x1F) { - /* Unencrypted SPDM message - check if it's an error */ - if (rxBuf[1] == SPDM_ERROR) { - wolfSPDM_DebugPrint(ctx, "GIVE_PUB: TPM returned unencrypted SPDM ERROR 0x%02x 0x%02x\n", - (rxSz >= 3) ? rxBuf[2] : 0, (rxSz >= 4) ? rxBuf[3] : 0); - return WOLFSPDM_E_PEER_ERROR; - } - wolfSPDM_DebugPrint(ctx, "GIVE_PUB: Unexpected unencrypted response code 0x%02x\n", - rxBuf[1]); - return WOLFSPDM_E_PEER_ERROR; - } - - /* Decrypt response (encrypted record format) */ + * Section 4.2.4 shows GIVE_PUB_KEY uses tag 0x8201 (secured), not 0x8101 (clear). */ decSz = sizeof(decBuf); - rc = wolfSPDM_DecryptMessage(ctx, rxBuf, rxSz, decBuf, &decSz); + rc = wolfSPDM_SecuredExchange(ctx, spdmMsg, (word32)spdmMsgSz, + decBuf, &decSz); if (rc != WOLFSPDM_SUCCESS) { - wolfSPDM_DebugPrint(ctx, "GIVE_PUB: Decrypt failed %d\n", rc); + wolfSPDM_DebugPrint(ctx, "GIVE_PUB: SecuredExchange failed %d\n", rc); return rc; } - /* Check for SPDM ERROR response in decrypted payload */ - if (decSz >= 4 && decBuf[1] == SPDM_ERROR) { - wolfSPDM_DebugPrint(ctx, "GIVE_PUB: SPDM ERROR 0x%02x 0x%02x\n", - decBuf[2], decBuf[3]); - return WOLFSPDM_E_PEER_ERROR; - } + SPDM_CHECK_ERROR_RSP(ctx, decBuf, decSz, "GIVE_PUB"); wolfSPDM_DebugPrint(ctx, "GIVE_PUB: Success\n"); - return WOLFSPDM_SUCCESS; } @@ -740,12 +620,7 @@ int wolfSPDM_Nuvoton_GetStatus( return rc; } - /* Check for SPDM ERROR response */ - if (spdmPayloadSz >= 4 && spdmPayload[1] == SPDM_ERROR) { - wolfSPDM_DebugPrint(ctx, "GET_STS_: SPDM ERROR 0x%02x 0x%02x\n", - spdmPayload[2], spdmPayload[3]); - return WOLFSPDM_E_PEER_ERROR; - } + SPDM_CHECK_ERROR_RSP(ctx, spdmPayload, spdmPayloadSz, "GET_STS_"); /* Parse vendor-defined response */ rspPayloadSz = sizeof(rspPayload); @@ -798,10 +673,6 @@ int wolfSPDM_Nuvoton_SetOnlyMode( int rc; byte spdmMsg[256]; int spdmMsgSz; - byte encBuf[WOLFSPDM_MAX_MSG_SIZE]; - word32 encSz; - byte rxBuf[512]; - word32 rxSz; byte decBuf[256]; word32 decSz; byte param[1]; @@ -826,47 +697,21 @@ int wolfSPDM_Nuvoton_SetOnlyMode( return spdmMsgSz; } - /* Encrypt the message */ - encSz = sizeof(encBuf); - rc = wolfSPDM_EncryptMessage(ctx, spdmMsg, (word32)spdmMsgSz, - encBuf, &encSz); - if (rc != WOLFSPDM_SUCCESS) { - return rc; - } - - /* Send encrypted message */ - if (ctx->ioCb == NULL) { - return WOLFSPDM_E_IO_FAIL; - } - - rxSz = sizeof(rxBuf); - rc = ctx->ioCb(ctx, encBuf, encSz, rxBuf, &rxSz, ctx->ioUserCtx); - if (rc != 0) { - return WOLFSPDM_E_IO_FAIL; - } - - /* Decrypt response */ + /* Send encrypted via SecuredExchange */ decSz = sizeof(decBuf); - rc = wolfSPDM_DecryptMessage(ctx, rxBuf, rxSz, decBuf, &decSz); + rc = wolfSPDM_SecuredExchange(ctx, spdmMsg, (word32)spdmMsgSz, + decBuf, &decSz); if (rc != WOLFSPDM_SUCCESS) { return rc; } - /* Check for SPDM ERROR response */ - if (decSz >= 4 && decBuf[1] == SPDM_ERROR) { - wolfSPDM_DebugPrint(ctx, "SPDMONLY: SPDM ERROR 0x%02x 0x%02x\n", - decBuf[2], decBuf[3]); - return WOLFSPDM_E_PEER_ERROR; - } + SPDM_CHECK_ERROR_RSP(ctx, decBuf, decSz, "SPDMONLY"); wolfSPDM_DebugPrint(ctx, "SPDMONLY: Success\n"); - return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * Nuvoton SPDM Connection Flow - * ========================================================================== */ +/* --- Nuvoton SPDM Connection Flow --- */ /* Nuvoton-specific connection flow: * GET_VERSION -> GET_PUB_KEY -> KEY_EXCHANGE -> GIVE_PUB_KEY -> FINISH @@ -901,16 +746,9 @@ int wolfSPDM_ConnectNuvoton(WOLFSPDM_CTX* ctx) ctx->state = WOLFSPDM_STATE_INIT; wolfSPDM_TranscriptReset(ctx); - /* Step 1: GET_VERSION / VERSION - * Note: For Nuvoton, GET_VERSION uses TCG binding header - * but the message parsing is the same as standard SPDM */ - wolfSPDM_DebugPrint(ctx, "Nuvoton Step 1: GET_VERSION\n"); - rc = wolfSPDM_GetVersion(ctx); - if (rc != WOLFSPDM_SUCCESS) { - wolfSPDM_DebugPrint(ctx, "GET_VERSION failed: %d\n", rc); - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } + /* Step 1: GET_VERSION / VERSION */ + SPDM_CONNECT_STEP(ctx, "Nuvoton Step 1: GET_VERSION\n", + wolfSPDM_GetVersion(ctx)); /* Step 2: GET_PUBK (Nuvoton vendor command) * Gets the TPM's SPDM-Identity public key (TPMT_PUBLIC format) */ @@ -928,38 +766,16 @@ int wolfSPDM_ConnectNuvoton(WOLFSPDM_CTX* ctx) * For Nuvoton, the cert_chain_buffer_hash is SHA-384(TPMT_PUBLIC) * instead of the standard certificate chain hash */ if (ctx->hasRspPubKey && ctx->rspPubKeyLen > 0) { - wc_Sha384 sha; - wolfSPDM_DebugPrint(ctx, "Nuvoton: Computing Ct = SHA-384(TPMT_PUBLIC[%u])\n", ctx->rspPubKeyLen); - - rc = wc_InitSha384(&sha); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "Nuvoton: SHA-384 init failed\n"); - ctx->state = WOLFSPDM_STATE_ERROR; - return WOLFSPDM_E_CRYPTO_FAIL; - } - - rc = wc_Sha384Update(&sha, ctx->rspPubKey, ctx->rspPubKeyLen); - if (rc != 0) { - wc_Sha384Free(&sha); - wolfSPDM_DebugPrint(ctx, "Nuvoton: SHA-384 update failed\n"); - ctx->state = WOLFSPDM_STATE_ERROR; - return WOLFSPDM_E_CRYPTO_FAIL; - } - - rc = wc_Sha384Final(&sha, ctx->certChainHash); - wc_Sha384Free(&sha); - if (rc != 0) { - wolfSPDM_DebugPrint(ctx, "Nuvoton: SHA-384 final failed\n"); + rc = wolfSPDM_Sha384Hash(ctx->certChainHash, + ctx->rspPubKey, ctx->rspPubKeyLen, NULL, 0, NULL, 0); + if (rc != WOLFSPDM_SUCCESS) { ctx->state = WOLFSPDM_STATE_ERROR; - return WOLFSPDM_E_CRYPTO_FAIL; + return rc; } - - /* Add Ct to transcript */ rc = wolfSPDM_TranscriptAdd(ctx, ctx->certChainHash, WOLFSPDM_HASH_SIZE); if (rc != WOLFSPDM_SUCCESS) { - wolfSPDM_DebugPrint(ctx, "Nuvoton: Failed to add Ct to transcript\n"); ctx->state = WOLFSPDM_STATE_ERROR; return rc; } @@ -968,14 +784,8 @@ int wolfSPDM_ConnectNuvoton(WOLFSPDM_CTX* ctx) wolfSPDM_DebugPrint(ctx, "Nuvoton: Warning - no responder public key for Ct\n"); } - /* Step 3: KEY_EXCHANGE */ - wolfSPDM_DebugPrint(ctx, "Nuvoton Step 3: KEY_EXCHANGE\n"); - rc = wolfSPDM_KeyExchange(ctx); - if (rc != WOLFSPDM_SUCCESS) { - wolfSPDM_DebugPrint(ctx, "KEY_EXCHANGE failed: %d\n", rc); - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } + SPDM_CONNECT_STEP(ctx, "Nuvoton Step 3: KEY_EXCHANGE\n", + wolfSPDM_KeyExchange(ctx)); /* Step 4: GIVE_PUB (Nuvoton vendor command) - sent as SECURED message * Gives the host's SPDM-Identity public key to the TPM. @@ -996,15 +806,9 @@ int wolfSPDM_ConnectNuvoton(WOLFSPDM_CTX* ctx) wolfSPDM_DebugPrint(ctx, "Nuvoton Step 4: GIVE_PUB (skipped, no host key)\n"); } - /* Step 5: FINISH (first encrypted message) - * Completes the handshake with RequesterVerifyData */ - wolfSPDM_DebugPrint(ctx, "Nuvoton Step 5: FINISH\n"); - rc = wolfSPDM_Finish(ctx); - if (rc != WOLFSPDM_SUCCESS) { - wolfSPDM_DebugPrint(ctx, "FINISH failed: %d\n", rc); - ctx->state = WOLFSPDM_STATE_ERROR; - return rc; - } + /* Step 5: FINISH */ + SPDM_CONNECT_STEP(ctx, "Nuvoton Step 5: FINISH\n", + wolfSPDM_Finish(ctx)); ctx->state = WOLFSPDM_STATE_CONNECTED; wolfSPDM_DebugPrint(ctx, "Nuvoton: SPDM Session Established! " diff --git a/src/spdm_secured.c b/src/spdm_secured.c index b734e13..48022d1 100644 --- a/src/spdm_secured.c +++ b/src/spdm_secured.c @@ -52,13 +52,9 @@ static int wolfSPDM_AesGcmSelfTest(WOLFSPDM_CTX* ctx) int rc; /* Build AAD matching what we'd use for SeqNum=0 */ - testAad[0] = (byte)(ctx->sessionId & 0xFF); - testAad[1] = (byte)((ctx->sessionId >> 8) & 0xFF); - testAad[2] = (byte)((ctx->sessionId >> 16) & 0xFF); - testAad[3] = (byte)((ctx->sessionId >> 24) & 0xFF); + SPDM_Set32LE(&testAad[0], ctx->sessionId); XMEMSET(&testAad[4], 0, 8); /* SeqNum = 0 */ - testAad[12] = (byte)((testPlainSz + 16) & 0xFF); - testAad[13] = (byte)(((testPlainSz + 16) >> 8) & 0xFF); + SPDM_Set16LE(&testAad[12], (word16)(testPlainSz + 16)); /* Encrypt */ rc = wc_AesGcmSetKey(&aesEnc, ctx->reqDataKey, WOLFSPDM_AEAD_KEY_SIZE); @@ -149,8 +145,7 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, } /* Build plaintext: AppDataLength(2 LE) || SPDM message || RandomData */ - plainBuf[0] = (byte)(appDataLen & 0xFF); - plainBuf[1] = (byte)((appDataLen >> 8) & 0xFF); + SPDM_Set16LE(plainBuf, appDataLen); XMEMCPY(&plainBuf[2], plain, plainSz); /* Fill RandomData with actual random bytes per Nuvoton spec */ if (padLen > 0) { @@ -165,24 +160,10 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, } /* Build header/AAD: SessionID(4 LE) + SeqNum(8 LE) + Length(2 LE) = 14 bytes */ - offset = 0; - /* SessionID (4 bytes LE): ReqSessionId || RspSessionId */ - enc[offset++] = (byte)(ctx->sessionId & 0xFF); - enc[offset++] = (byte)((ctx->sessionId >> 8) & 0xFF); - enc[offset++] = (byte)((ctx->sessionId >> 16) & 0xFF); - enc[offset++] = (byte)((ctx->sessionId >> 24) & 0xFF); - /* SequenceNumber (8 bytes LE) - per Nuvoton spec */ - enc[offset++] = (byte)(ctx->reqSeqNum & 0xFF); - enc[offset++] = (byte)((ctx->reqSeqNum >> 8) & 0xFF); - enc[offset++] = (byte)((ctx->reqSeqNum >> 16) & 0xFF); - enc[offset++] = (byte)((ctx->reqSeqNum >> 24) & 0xFF); - enc[offset++] = (byte)((ctx->reqSeqNum >> 32) & 0xFF); - enc[offset++] = (byte)((ctx->reqSeqNum >> 40) & 0xFF); - enc[offset++] = (byte)((ctx->reqSeqNum >> 48) & 0xFF); - enc[offset++] = (byte)((ctx->reqSeqNum >> 56) & 0xFF); - /* Length (2 bytes LE) = encrypted payload + MAC */ - enc[offset++] = (byte)(recordLen & 0xFF); - enc[offset++] = (byte)((recordLen >> 8) & 0xFF); + SPDM_Set32LE(&enc[0], ctx->sessionId); + SPDM_Set64LE(&enc[4], ctx->reqSeqNum); + SPDM_Set16LE(&enc[12], recordLen); + offset = 14; aadSz = 14; XMEMCPY(aad, enc, aadSz); @@ -207,52 +188,23 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, } /* Build plaintext: AppDataLen(2 LE) || MCTP header(0x05) || SPDM msg */ - plainBuf[0] = (byte)(appDataLen & 0xFF); - plainBuf[1] = (byte)((appDataLen >> 8) & 0xFF); + SPDM_Set16LE(plainBuf, appDataLen); plainBuf[2] = MCTP_MESSAGE_TYPE_SPDM; XMEMCPY(&plainBuf[3], plain, plainSz); /* Build header/AAD: SessionID(4 LE) + SeqNum(2 LE) + Length(2 LE) */ - offset = 0; - enc[offset++] = (byte)(ctx->sessionId & 0xFF); - enc[offset++] = (byte)((ctx->sessionId >> 8) & 0xFF); - enc[offset++] = (byte)((ctx->sessionId >> 16) & 0xFF); - enc[offset++] = (byte)((ctx->sessionId >> 24) & 0xFF); - enc[offset++] = (byte)(ctx->reqSeqNum & 0xFF); - enc[offset++] = (byte)((ctx->reqSeqNum >> 8) & 0xFF); - enc[offset++] = (byte)(recordLen & 0xFF); - enc[offset++] = (byte)((recordLen >> 8) & 0xFF); + SPDM_Set32LE(&enc[0], ctx->sessionId); + SPDM_Set16LE(&enc[4], (word16)ctx->reqSeqNum); + SPDM_Set16LE(&enc[6], recordLen); + offset = 8; aadSz = 8; XMEMCPY(aad, enc, aadSz); } - /* Build IV: BaseIV XOR sequence number */ - XMEMCPY(iv, ctx->reqDataIv, WOLFSPDM_AEAD_IV_SIZE); -#ifdef WOLFSPDM_NUVOTON - if (ctx->mode == WOLFSPDM_MODE_NUVOTON) { - /* DSP0277 1.2 IV construction: - * Zero-extend 8-byte LE sequence number to iv_length (12 bytes), - * then XOR with base IV. Seq occupies leftmost bytes (0-7), - * zero-padding at bytes 8-11. - */ - iv[0] ^= (byte)(ctx->reqSeqNum & 0xFF); - iv[1] ^= (byte)((ctx->reqSeqNum >> 8) & 0xFF); - iv[2] ^= (byte)((ctx->reqSeqNum >> 16) & 0xFF); - iv[3] ^= (byte)((ctx->reqSeqNum >> 24) & 0xFF); - iv[4] ^= (byte)((ctx->reqSeqNum >> 32) & 0xFF); - iv[5] ^= (byte)((ctx->reqSeqNum >> 40) & 0xFF); - iv[6] ^= (byte)((ctx->reqSeqNum >> 48) & 0xFF); - iv[7] ^= (byte)((ctx->reqSeqNum >> 56) & 0xFF); - } - else -#endif - { - /* MCTP format: Zero-extend 2-byte LE sequence number to iv_length, - * then XOR with base IV per DSP0277. LE integer starts at byte 0. */ - iv[0] ^= (byte)(ctx->reqSeqNum & 0xFF); - iv[1] ^= (byte)((ctx->reqSeqNum >> 8) & 0xFF); - } + /* Build IV: BaseIV XOR sequence number (DSP0277) */ + wolfSPDM_BuildIV(iv, ctx->reqDataIv, ctx->reqSeqNum, + ctx->mode == WOLFSPDM_MODE_NUVOTON); rc = wc_AesGcmSetKey(&aes, ctx->reqDataKey, WOLFSPDM_AEAD_KEY_SIZE); if (rc != 0) { @@ -316,13 +268,9 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, } /* Parse header: SessionID(4) + SeqNum(8) + Length(2) */ - rspSessionId = (word32)enc[0] | ((word32)enc[1] << 8) | - ((word32)enc[2] << 16) | ((word32)enc[3] << 24); - rspSeqNum64 = (word64)enc[4] | ((word64)enc[5] << 8) | - ((word64)enc[6] << 16) | ((word64)enc[7] << 24) | - ((word64)enc[8] << 32) | ((word64)enc[9] << 40) | - ((word64)enc[10] << 48) | ((word64)enc[11] << 56); - rspLen = (word16)(enc[12] | (enc[13] << 8)); + rspSessionId = SPDM_Get32LE(&enc[0]); + rspSeqNum64 = SPDM_Get64LE(&enc[4]); + rspLen = SPDM_Get16LE(&enc[12]); rspSeqNum = (word16)(rspSeqNum64 & 0xFFFF); /* For debug output */ if (rspSessionId != ctx->sessionId) { @@ -342,19 +290,8 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, XMEMCPY(aad, enc, aadSz); - /* DSP0277 1.2 IV construction: - * Zero-extend 8-byte LE sequence number to iv_length (12 bytes), - * then XOR with base IV. Seq occupies leftmost bytes (0-7). - * SeqNum bytes are at enc[4]-enc[11] in the record header. */ - XMEMCPY(iv, ctx->rspDataIv, WOLFSPDM_AEAD_IV_SIZE); - iv[0] ^= enc[4]; /* SeqNum byte 0 */ - iv[1] ^= enc[5]; /* SeqNum byte 1 */ - iv[2] ^= enc[6]; /* SeqNum byte 2 */ - iv[3] ^= enc[7]; /* SeqNum byte 3 */ - iv[4] ^= enc[8]; /* SeqNum byte 4 */ - iv[5] ^= enc[9]; /* SeqNum byte 5 */ - iv[6] ^= enc[10]; /* SeqNum byte 6 */ - iv[7] ^= enc[11]; /* SeqNum byte 7 */ + /* Build IV: BaseIV XOR sequence number (DSP0277 1.2) */ + wolfSPDM_BuildIV(iv, ctx->rspDataIv, rspSeqNum64, 1); rc = wc_AesGcmSetKey(&aes, ctx->rspDataKey, WOLFSPDM_AEAD_KEY_SIZE); if (rc != 0) { @@ -369,7 +306,7 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, } /* Parse decrypted: AppDataLen (2 LE) || SPDM message || RandomData */ - appDataLen = (word16)(decrypted[0] | (decrypted[1] << 8)); + appDataLen = SPDM_Get16LE(decrypted); if (cipherLen < (word32)(2 + appDataLen)) { return WOLFSPDM_E_BUFFER_SMALL; @@ -394,11 +331,10 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, return WOLFSPDM_E_BUFFER_SMALL; } - /* Parse header */ - rspSessionId = (word32)enc[0] | ((word32)enc[1] << 8) | - ((word32)enc[2] << 16) | ((word32)enc[3] << 24); - rspSeqNum = (word16)(enc[4] | (enc[5] << 8)); - rspLen = (word16)(enc[6] | (enc[7] << 8)); + /* Parse header: SessionID(4) + SeqNum(2) + Length(2) */ + rspSessionId = SPDM_Get32LE(&enc[0]); + rspSeqNum = SPDM_Get16LE(&enc[4]); + rspLen = SPDM_Get16LE(&enc[6]); if (rspSessionId != ctx->sessionId) { wolfSPDM_DebugPrint(ctx, "Session ID mismatch: 0x%08x != 0x%08x\n", @@ -416,11 +352,8 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, XMEMCPY(aad, enc, aadSz); - /* Build IV: Zero-extend 2-byte LE sequence number to iv_length, - * then XOR with base IV per DSP0277. LE integer starts at byte 0. */ - XMEMCPY(iv, ctx->rspDataIv, WOLFSPDM_AEAD_IV_SIZE); - iv[0] ^= (byte)(rspSeqNum & 0xFF); - iv[1] ^= (byte)((rspSeqNum >> 8) & 0xFF); + /* Build IV: BaseIV XOR sequence number (DSP0277) */ + wolfSPDM_BuildIV(iv, ctx->rspDataIv, (word64)rspSeqNum, 0); rc = wc_AesGcmSetKey(&aes, ctx->rspDataKey, WOLFSPDM_AEAD_KEY_SIZE); if (rc != 0) { @@ -435,7 +368,7 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, } /* Parse decrypted: AppDataLen (2) || MCTP (1) || SPDM msg */ - appDataLen = (word16)(decrypted[0] | (decrypted[1] << 8)); + appDataLen = SPDM_Get16LE(decrypted); if (appDataLen < 1 || cipherLen < (word32)(2 + appDataLen)) { return WOLFSPDM_E_BUFFER_SMALL; @@ -458,6 +391,7 @@ int wolfSPDM_DecryptInternal(WOLFSPDM_CTX* ctx, return WOLFSPDM_SUCCESS; } +#ifndef WOLFSPDM_LEAN int wolfSPDM_EncryptMessage(WOLFSPDM_CTX* ctx, const byte* plain, word32 plainSz, byte* enc, word32* encSz) @@ -491,6 +425,7 @@ int wolfSPDM_DecryptMessage(WOLFSPDM_CTX* ctx, return wolfSPDM_DecryptInternal(ctx, enc, encSz, plain, plainSz); } +#endif /* !WOLFSPDM_LEAN */ int wolfSPDM_SecuredExchange(WOLFSPDM_CTX* ctx, const byte* cmdPlain, word32 cmdSz, @@ -516,18 +451,12 @@ int wolfSPDM_SecuredExchange(WOLFSPDM_CTX* ctx, return rc; } - rc = wolfSPDM_DecryptInternal(ctx, rxBuf, rxSz, rspPlain, rspSz); - if (rc != WOLFSPDM_SUCCESS) { - return rc; - } - - return WOLFSPDM_SUCCESS; + return wolfSPDM_DecryptInternal(ctx, rxBuf, rxSz, rspPlain, rspSz); } -/* ========================================================================== - * Application Data Transfer - * ========================================================================== */ +/* --- Application Data Transfer --- */ +#ifndef WOLFSPDM_LEAN int wolfSPDM_SendData(WOLFSPDM_CTX* ctx, const byte* data, word32 dataSz) { byte encBuf[WOLFSPDM_MAX_MSG_SIZE + 48]; @@ -595,10 +524,6 @@ int wolfSPDM_ReceiveData(WOLFSPDM_CTX* ctx, byte* data, word32* dataSz) } /* Decrypt the received data */ - rc = wolfSPDM_DecryptInternal(ctx, rxBuf, rxSz, data, dataSz); - if (rc != WOLFSPDM_SUCCESS) { - return rc; - } - - return WOLFSPDM_SUCCESS; + return wolfSPDM_DecryptInternal(ctx, rxBuf, rxSz, data, dataSz); } +#endif /* !WOLFSPDM_LEAN */ diff --git a/src/spdm_session.c b/src/spdm_session.c index bc88914..ad4143a 100644 --- a/src/spdm_session.c +++ b/src/spdm_session.c @@ -293,13 +293,11 @@ int wolfSPDM_Finish(WOLFSPDM_CTX* ctx) rc = wolfSPDM_DecryptInternal(ctx, rxBuf, rxSz, decBuf, &decSz); if (rc != WOLFSPDM_SUCCESS) { wolfSPDM_DebugPrint(ctx, "FINISH decrypt failed: %d\n", rc); - return rc; } rc = wolfSPDM_ParseFinishRsp(ctx, decBuf, decSz); if (rc != WOLFSPDM_SUCCESS) { - return rc; } @@ -307,16 +305,12 @@ int wolfSPDM_Finish(WOLFSPDM_CTX* ctx) rc = wolfSPDM_DeriveAppDataKeys(ctx); if (rc != WOLFSPDM_SUCCESS) { wolfSPDM_DebugPrint(ctx, "App data key derivation failed: %d\n", rc); - return rc; } - return WOLFSPDM_SUCCESS; } -/* ========================================================================== - * Measurements (Device Attestation) - * ========================================================================== */ +/* --- Measurements (Device Attestation) --- */ #ifndef NO_WOLFSPDM_MEAS @@ -400,9 +394,7 @@ int wolfSPDM_GetMeasurements(WOLFSPDM_CTX* ctx, byte measOperation, #endif /* !NO_WOLFSPDM_MEAS */ -/* ========================================================================== - * Challenge Authentication (Sessionless Attestation) - * ========================================================================== */ +/* --- Challenge Authentication (Sessionless Attestation) --- */ #ifndef NO_WOLFSPDM_CHALLENGE @@ -465,9 +457,7 @@ int wolfSPDM_Challenge(WOLFSPDM_CTX* ctx, int slotId, byte measHashType) #endif /* !NO_WOLFSPDM_CHALLENGE */ -/* ========================================================================== - * Heartbeat (Session Keep-Alive) - * ========================================================================== */ +/* --- Heartbeat (Session Keep-Alive) --- */ int wolfSPDM_Heartbeat(WOLFSPDM_CTX* ctx) { @@ -504,9 +494,7 @@ int wolfSPDM_Heartbeat(WOLFSPDM_CTX* ctx) return wolfSPDM_ParseHeartbeatAck(ctx, rxBuf, rxSz); } -/* ========================================================================== - * Key Update (Session Key Rotation) - * ========================================================================== */ +/* --- Key Update (Session Key Rotation) --- */ int wolfSPDM_KeyUpdate(WOLFSPDM_CTX* ctx, int updateAll) { diff --git a/src/spdm_transcript.c b/src/spdm_transcript.c index 3f0804b..0bf422c 100644 --- a/src/spdm_transcript.c +++ b/src/spdm_transcript.c @@ -22,17 +22,11 @@ #include "spdm_internal.h" #include -/* ========================================================================== - * Transcript Management - * - * SPDM uses transcript hashing for key derivation: - * VCA = GET_VERSION || VERSION || GET_CAPS || CAPS || NEG_ALGO || ALGO - * Ct = Hash(certificate_chain) - * TH1 = Hash(VCA || Ct || KEY_EXCHANGE || KEY_EXCHANGE_RSP_partial || Signature) - * TH2 = Hash(VCA || Ct || message_k || FINISH_header) - * - * where message_k = KEY_EXCHANGE || KEY_EXCHANGE_RSP_partial || Signature || ResponderVerifyData - * ========================================================================== */ +/* --- Transcript Management --- + * VCA = GET_VERSION || VERSION || GET_CAPS || CAPS || NEG_ALGO || ALGO + * Ct = Hash(certificate_chain) + * TH1 = Hash(VCA || Ct || KEY_EXCHANGE || KEY_EXCHANGE_RSP_partial || Signature) + * TH2 = Hash(VCA || Ct || message_k || FINISH_header) */ void wolfSPDM_TranscriptReset(WOLFSPDM_CTX* ctx) { @@ -86,70 +80,53 @@ int wolfSPDM_CertChainAdd(WOLFSPDM_CTX* ctx, const byte* data, word32 len) return WOLFSPDM_SUCCESS; } -int wolfSPDM_TranscriptHash(WOLFSPDM_CTX* ctx, byte* hash) +int wolfSPDM_Sha384Hash(byte* out, + const byte* d1, word32 d1Sz, + const byte* d2, word32 d2Sz, + const byte* d3, word32 d3Sz) { wc_Sha384 sha; int rc; - if (ctx == NULL || hash == NULL) { - return WOLFSPDM_E_INVALID_ARG; - } - rc = wc_InitSha384(&sha); - if (rc != 0) { - return WOLFSPDM_E_CRYPTO_FAIL; + if (rc != 0) return WOLFSPDM_E_CRYPTO_FAIL; + if (d1 != NULL && d1Sz > 0) { + rc = wc_Sha384Update(&sha, d1, d1Sz); + if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } } - - rc = wc_Sha384Update(&sha, ctx->transcript, ctx->transcriptLen); - if (rc != 0) { - wc_Sha384Free(&sha); - return WOLFSPDM_E_CRYPTO_FAIL; + if (d2 != NULL && d2Sz > 0) { + rc = wc_Sha384Update(&sha, d2, d2Sz); + if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } } - - rc = wc_Sha384Final(&sha, hash); + if (d3 != NULL && d3Sz > 0) { + rc = wc_Sha384Update(&sha, d3, d3Sz); + if (rc != 0) { wc_Sha384Free(&sha); return WOLFSPDM_E_CRYPTO_FAIL; } + } + rc = wc_Sha384Final(&sha, out); wc_Sha384Free(&sha); + return (rc == 0) ? WOLFSPDM_SUCCESS : WOLFSPDM_E_CRYPTO_FAIL; +} - if (rc != 0) { - return WOLFSPDM_E_CRYPTO_FAIL; +int wolfSPDM_TranscriptHash(WOLFSPDM_CTX* ctx, byte* hash) +{ + if (ctx == NULL || hash == NULL) { + return WOLFSPDM_E_INVALID_ARG; } - - return WOLFSPDM_SUCCESS; + return wolfSPDM_Sha384Hash(hash, ctx->transcript, ctx->transcriptLen, + NULL, 0, NULL, 0); } int wolfSPDM_ComputeCertChainHash(WOLFSPDM_CTX* ctx) { - wc_Sha384 sha; - int rc; - if (ctx == NULL) { return WOLFSPDM_E_INVALID_ARG; } - if (ctx->certChainLen == 0) { - /* No certificate chain - Ct is zeros */ XMEMSET(ctx->certChainHash, 0, sizeof(ctx->certChainHash)); return WOLFSPDM_SUCCESS; } - rc = wc_InitSha384(&sha); - if (rc != 0) { - return WOLFSPDM_E_CRYPTO_FAIL; - } - - rc = wc_Sha384Update(&sha, ctx->certChain, ctx->certChainLen); - if (rc != 0) { - wc_Sha384Free(&sha); - return WOLFSPDM_E_CRYPTO_FAIL; - } - - rc = wc_Sha384Final(&sha, ctx->certChainHash); - wc_Sha384Free(&sha); - - if (rc != 0) { - return WOLFSPDM_E_CRYPTO_FAIL; - } - wolfSPDM_DebugPrint(ctx, "Ct = Hash(cert_chain[%u])\n", ctx->certChainLen); - - return WOLFSPDM_SUCCESS; + return wolfSPDM_Sha384Hash(ctx->certChainHash, + ctx->certChain, ctx->certChainLen, NULL, 0, NULL, 0); } diff --git a/wolfspdm/spdm.h b/wolfspdm/spdm.h index ffd7f94..3894b09 100644 --- a/wolfspdm/spdm.h +++ b/wolfspdm/spdm.h @@ -31,13 +31,22 @@ #include #include +/* Feature detection macros — external projects (e.g. wolfTPM) can check these + * to conditionally compile against optional wolfSPDM APIs. */ +#ifndef NO_WOLFSPDM_MEAS +#define WOLFSPDM_HAS_MEASUREMENTS +#endif +#ifndef NO_WOLFSPDM_CHALLENGE +#define WOLFSPDM_HAS_CHALLENGE +#endif +#define WOLFSPDM_HAS_HEARTBEAT +#define WOLFSPDM_HAS_KEY_UPDATE + #ifdef __cplusplus extern "C" { #endif -/* ========================================================================== - * Protocol Mode Selection - * ========================================================================== +/* --- Protocol Mode Selection --- * * wolfSPDM supports two protocol modes: * @@ -50,18 +59,14 @@ extern "C" { * WOLFSPDM_MODE_NUVOTON (requires --enable-nuvoton): * Nuvoton TPM-specific protocol with TCG binding headers. * Flow: GET_VERSION -> GET_PUB_KEY -> KEY_EXCHANGE -> GIVE_PUB_KEY -> FINISH - * Use with: Nuvoton NPCT75x TPMs (FW 7.2+) - * - * ========================================================================== */ + * Use with: Nuvoton NPCT75x TPMs (FW 7.2+) */ typedef enum { WOLFSPDM_MODE_STANDARD = 0, /* Standard SPDM 1.2 (default) */ WOLFSPDM_MODE_NUVOTON = 1, /* Nuvoton TCG binding + vendor commands */ } WOLFSPDM_MODE; -/* ========================================================================== - * wolfSPDM Overview - * ========================================================================== +/* --- wolfSPDM Overview --- * * wolfSPDM is a lightweight SPDM (Security Protocol and Data Model) * implementation using wolfCrypt for all cryptographic operations. @@ -93,9 +98,7 @@ typedef enum { * wolfSPDM_Free(ctx); // Frees the allocation * * Note: WOLFSPDM_CTX is approximately 22KB. On embedded systems with - * small stacks, declare it as a static global rather than a local variable. - * - * ========================================================================== */ + * 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 @@ -115,9 +118,7 @@ typedef struct WOLFSPDM_CTX WOLFSPDM_CTX; #include #endif -/* ========================================================================== - * I/O Callback - * ========================================================================== +/* --- I/O Callback --- * * The I/O callback is called by wolfSPDM to send and receive raw SPDM * messages. The transport layer (SPI, I2C, TCP, etc.) is handled externally. @@ -136,8 +137,7 @@ typedef struct WOLFSPDM_CTX WOLFSPDM_CTX; * Notes: * - For MCTP transport, the callback should handle MCTP encapsulation * - For secured messages (after KEY_EXCHANGE), the callback receives - * already-encrypted data including the session header - * ========================================================================== */ + * already-encrypted data including the session header */ typedef int (*WOLFSPDM_IO_CB)( WOLFSPDM_CTX* ctx, const byte* txBuf, word32 txSz, @@ -145,9 +145,7 @@ typedef int (*WOLFSPDM_IO_CB)( void* userCtx ); -/* ========================================================================== - * Context Management - * ========================================================================== */ +/* --- Context Management --- */ /** * Initialize a wolfSPDM context for use. @@ -200,9 +198,7 @@ int wolfSPDM_GetCtxSize(void); */ int wolfSPDM_InitStatic(WOLFSPDM_CTX* ctx, int size); -/* ========================================================================== - * Configuration - * ========================================================================== */ +/* --- Configuration --- */ /** * Set the I/O callback for sending/receiving SPDM messages. @@ -261,9 +257,7 @@ int wolfSPDM_SetRequesterKeyPair(WOLFSPDM_CTX* ctx, const byte* privKey, word32 privKeySz, const byte* pubKey, word32 pubKeySz); -/* ========================================================================== - * Session Establishment - * ========================================================================== */ +/* --- Session Establishment --- */ /** * Establish an SPDM session (full handshake). @@ -294,9 +288,7 @@ int wolfSPDM_IsConnected(WOLFSPDM_CTX* ctx); */ int wolfSPDM_Disconnect(WOLFSPDM_CTX* ctx); -/* ========================================================================== - * Individual Handshake Steps (for fine-grained control) - * ========================================================================== */ +/* --- Individual Handshake Steps (for fine-grained control) --- */ /** * Send GET_VERSION and receive VERSION response. @@ -361,10 +353,9 @@ int wolfSPDM_KeyExchange(WOLFSPDM_CTX* ctx); */ int wolfSPDM_Finish(WOLFSPDM_CTX* ctx); -/* ========================================================================== - * Secured Messaging - * ========================================================================== */ +/* --- Secured Messaging --- */ +#ifndef WOLFSPDM_LEAN /** * Encrypt a message for sending over the established session. * @@ -392,6 +383,7 @@ int wolfSPDM_EncryptMessage(WOLFSPDM_CTX* ctx, int wolfSPDM_DecryptMessage(WOLFSPDM_CTX* ctx, const byte* enc, word32 encSz, byte* plain, word32* plainSz); +#endif /* !WOLFSPDM_LEAN */ /** * Perform a secured message exchange (encrypt, send, receive, decrypt). @@ -409,9 +401,7 @@ int wolfSPDM_SecuredExchange(WOLFSPDM_CTX* ctx, byte* rspPlain, word32* rspSz); #ifndef NO_WOLFSPDM_MEAS -/* ========================================================================== - * Measurements (Device Attestation) - * ========================================================================== +/* --- Measurements (Device Attestation) --- * * When requestSignature=1 (and NO_WOLFSPDM_MEAS_VERIFY is NOT defined): * Retrieves measurements with a cryptographic signature from the responder, @@ -428,8 +418,7 @@ int wolfSPDM_SecuredExchange(WOLFSPDM_CTX* ctx, * disabled and returns WOLFSPDM_E_MEAS_NOT_VERIFIED regardless of * requestSignature (signature bytes are still captured in the context). * - * Contexts are NOT thread-safe; do not call from multiple threads. - * ========================================================================== */ + * Contexts are NOT thread-safe; do not call from multiple threads. */ /** * Retrieve measurements from the SPDM responder. @@ -466,15 +455,13 @@ int wolfSPDM_GetMeasurementBlock(WOLFSPDM_CTX* ctx, int blockIdx, byte* measIndex, byte* measType, byte* value, word32* valueSz); #endif /* !NO_WOLFSPDM_MEAS */ -/* ========================================================================== - * Application Data Transfer - * ========================================================================== +#ifndef WOLFSPDM_LEAN +/* --- Application Data Transfer --- * * Send/receive application data over an established SPDM session. * Max payload per call: WOLFSPDM_MAX_MSG_SIZE minus AEAD overhead (~4000 bytes). * These are message-oriented (no partial reads/writes). - * Contexts are NOT thread-safe; do not call from multiple threads. - * ========================================================================== */ + * Contexts are NOT thread-safe; do not call from multiple threads. */ /** * Send application data over an established SPDM session. @@ -495,10 +482,9 @@ int wolfSPDM_SendData(WOLFSPDM_CTX* ctx, const byte* data, word32 dataSz); * @return WOLFSPDM_SUCCESS or negative error code. */ int wolfSPDM_ReceiveData(WOLFSPDM_CTX* ctx, byte* data, word32* dataSz); +#endif /* !WOLFSPDM_LEAN */ -/* ========================================================================== - * Session Information - * ========================================================================== */ +/* --- Session Information --- */ /** * Get the current session ID. @@ -534,9 +520,7 @@ word32 wolfSPDM_GetConnectionHandle(WOLFSPDM_CTX* ctx); word16 wolfSPDM_GetFipsIndicator(WOLFSPDM_CTX* ctx); #endif -/* ========================================================================== - * Certificate Chain Validation - * ========================================================================== */ +/* --- Certificate Chain Validation --- */ /** * Load trusted root CA certificates for certificate chain validation. @@ -552,9 +536,7 @@ int wolfSPDM_SetTrustedCAs(WOLFSPDM_CTX* ctx, const byte* derCerts, word32 derCertsSz); #ifndef NO_WOLFSPDM_CHALLENGE -/* ========================================================================== - * Challenge Authentication (Sessionless Attestation) - * ========================================================================== */ +/* --- Challenge Authentication (Sessionless Attestation) --- */ /** * Perform CHALLENGE/CHALLENGE_AUTH exchange for sessionless attestation. @@ -573,9 +555,7 @@ int wolfSPDM_SetTrustedCAs(WOLFSPDM_CTX* ctx, const byte* derCerts, int wolfSPDM_Challenge(WOLFSPDM_CTX* ctx, int slotId, byte measHashType); #endif /* !NO_WOLFSPDM_CHALLENGE */ -/* ========================================================================== - * Session Keep-Alive - * ========================================================================== */ +/* --- Session Keep-Alive --- */ /** * Send HEARTBEAT and receive HEARTBEAT_ACK. @@ -587,9 +567,7 @@ int wolfSPDM_Challenge(WOLFSPDM_CTX* ctx, int slotId, byte measHashType); */ int wolfSPDM_Heartbeat(WOLFSPDM_CTX* ctx); -/* ========================================================================== - * Key Update (Session Key Rotation) - * ========================================================================== */ +/* --- Key Update (Session Key Rotation) --- */ /** * Perform KEY_UPDATE to rotate session encryption keys. @@ -603,9 +581,7 @@ int wolfSPDM_Heartbeat(WOLFSPDM_CTX* ctx); */ int wolfSPDM_KeyUpdate(WOLFSPDM_CTX* ctx, int updateAll); -/* ========================================================================== - * Debug/Utility - * ========================================================================== */ +/* --- Debug/Utility --- */ /** * Enable or disable debug output. diff --git a/wolfspdm/spdm_nuvoton.h b/wolfspdm/spdm_nuvoton.h index b908098..a1da94c 100644 --- a/wolfspdm/spdm_nuvoton.h +++ b/wolfspdm/spdm_nuvoton.h @@ -51,9 +51,7 @@ extern "C" { #endif -/* ========================================================================== - * TCG SPDM Binding Constants (per TCG SPDM Binding Spec v1.0) - * ========================================================================== */ +/* --- TCG SPDM Binding Constants (per TCG SPDM Binding Spec v1.0) --- */ /* Message Tags */ #define WOLFSPDM_TCG_TAG_CLEAR 0x8101 /* Clear (unencrypted) message */ @@ -67,9 +65,7 @@ extern "C" { #define WOLFSPDM_FIPS_NON_FIPS 0x00 #define WOLFSPDM_FIPS_APPROVED 0x01 -/* ========================================================================== - * Nuvoton Vendor-Defined Command Codes - * ========================================================================== */ +/* --- Nuvoton Vendor-Defined Command Codes --- */ /* 8-byte ASCII vendor codes for SPDM VENDOR_DEFINED messages */ #define WOLFSPDM_VDCODE_LEN 8 @@ -84,9 +80,7 @@ extern "C" { #define WOLFSPDM_SPDMONLY_LOCK 0x01 #define WOLFSPDM_SPDMONLY_UNLOCK 0x00 -/* ========================================================================== - * TCG Binding Header Structures - * ========================================================================== */ +/* --- TCG Binding Header Structures --- */ /* Clear message header (tag 0x8101) * Layout: tag(2/BE) + size(4/BE) + connectionHandle(4/BE) + @@ -112,9 +106,7 @@ typedef struct WOLFSPDM_TCG_SECURED_HDR { word32 reserved; /* Must be 0 */ } WOLFSPDM_TCG_SECURED_HDR; -/* ========================================================================== - * Nuvoton SPDM Status - * ========================================================================== */ +/* --- Nuvoton SPDM Status --- */ typedef struct WOLFSPDM_NUVOTON_STATUS { int spdmEnabled; /* SPDM is enabled on the TPM */ @@ -125,9 +117,7 @@ typedef struct WOLFSPDM_NUVOTON_STATUS { byte specVersionMinor; /* SPDM spec version minor (1=1.1, 3=1.3) */ } WOLFSPDM_NUVOTON_STATUS; -/* ========================================================================== - * TCG Binding Message Framing Functions - * ========================================================================== */ +/* --- TCG Binding Message Framing Functions --- */ /** * Build a TCG SPDM clear message (tag 0x8101). @@ -202,9 +192,7 @@ int wolfSPDM_ParseTcgSecuredMessage( byte* mac, word32* macSz, WOLFSPDM_TCG_SECURED_HDR* hdr); -/* ========================================================================== - * Vendor-Defined Message Helpers - * ========================================================================== */ +/* --- Vendor-Defined Message Helpers --- */ /** * Build an SPDM VENDOR_DEFINED_REQUEST message. @@ -236,9 +224,7 @@ int wolfSPDM_ParseVendorDefined( char* vdCode, byte* payload, word32* payloadSz); -/* ========================================================================== - * Nuvoton-Specific SPDM Functions - * ========================================================================== */ +/* --- Nuvoton-Specific SPDM Functions --- */ /** * Get the TPM's SPDM-Identity public key (GET_PUBK vendor command). @@ -314,9 +300,7 @@ int wolfSPDM_SetRequesterKeyTPMT(WOLFSPDM_CTX* ctx, */ int wolfSPDM_ConnectNuvoton(WOLFSPDM_CTX* ctx); -/* ========================================================================== - * Nuvoton Context Fields - * ========================================================================== */ +/* --- Nuvoton Context Fields --- */ /* These fields are added to WOLFSPDM_CTX when WOLFSPDM_NUVOTON is defined */ diff --git a/wolfspdm/spdm_types.h b/wolfspdm/spdm_types.h index 005d45f..043056b 100644 --- a/wolfspdm/spdm_types.h +++ b/wolfspdm/spdm_types.h @@ -37,9 +37,7 @@ extern "C" { #include #endif -/* ========================================================================== - * SPDM Protocol Constants (DMTF DSP0274 / DSP0277) - * ========================================================================== */ +/* --- SPDM Protocol Constants (DMTF DSP0274 / DSP0277) --- */ /* SPDM Version Numbers */ #define SPDM_VERSION_10 0x10 /* SPDM 1.0 (for GET_VERSION) */ @@ -109,10 +107,8 @@ extern "C" { #define SPDM_ERROR_RESPONSE_NOT_READY 0x42 #define SPDM_ERROR_REQUEST_RESYNCH 0x43 -/* ========================================================================== - * Algorithm Set B (FIPS 140-3 Level 3 compliant) - * This implementation ONLY supports Algorithm Set B for simplicity. - * ========================================================================== */ +/* --- Algorithm Set B (FIPS 140-3 Level 3 compliant) --- + * This implementation ONLY supports Algorithm Set B for simplicity. */ /* Hash Algorithms */ #define SPDM_HASH_ALGO_SHA_384 0x00000002 /* TPM_ALG_SHA384 */ @@ -139,9 +135,7 @@ extern "C" { #define WOLFSPDM_AEAD_TAG_SIZE 16 /* AES-GCM tag size */ #define WOLFSPDM_HMAC_SIZE 48 /* HMAC-SHA384 output size */ -/* ========================================================================== - * Capability Flags (per DSP0274) - * ========================================================================== */ +/* --- Capability Flags (per DSP0274) --- */ /* Requester Capabilities (GET_CAPABILITIES flags) */ #define SPDM_CAP_CERT_CAP 0x00000002 /* Certificate support */ @@ -167,18 +161,14 @@ extern "C" { SPDM_CAP_KEY_EX_CAP | SPDM_CAP_HBEAT_CAP | \ SPDM_CAP_KEY_UPD_CAP) -/* ========================================================================== - * Buffer/Message Size Limits - * ========================================================================== */ +/* --- Buffer/Message Size Limits --- */ #define WOLFSPDM_MAX_MSG_SIZE 4096 /* Maximum SPDM message size */ #define WOLFSPDM_MAX_CERT_CHAIN 4096 /* Maximum certificate chain size */ #define WOLFSPDM_MAX_TRANSCRIPT 4096 /* Maximum transcript buffer */ #define WOLFSPDM_RANDOM_SIZE 32 /* Random data in KEY_EXCHANGE */ -/* ========================================================================== - * MCTP Transport Constants (for TCP/socket transport) - * ========================================================================== */ +/* --- MCTP Transport Constants (for TCP/socket transport) --- */ #define MCTP_MESSAGE_TYPE_SPDM 0x05 /* SPDM over MCTP */ #define MCTP_MESSAGE_TYPE_SECURED 0x06 /* Secured SPDM over MCTP */ @@ -195,9 +185,7 @@ extern "C" { #endif #ifndef NO_WOLFSPDM_MEAS -/* ========================================================================== - * Measurement Constants (DSP0274 Section 10.11) - * ========================================================================== */ +/* --- Measurement Constants (DSP0274 Section 10.11) --- */ /* MeasurementSummaryHashType (Param1 of GET_MEASUREMENTS) */ #define SPDM_MEAS_SUMMARY_HASH_NONE 0x00 @@ -231,9 +219,7 @@ extern "C" { #define WOLFSPDM_MEAS_BLOCK_HDR_SIZE 4 /* Index(1) + MeasSpec(1) + Size(2 LE) */ #endif /* !NO_WOLFSPDM_MEAS */ -/* ========================================================================== - * Key Derivation Labels (SPDM 1.2 per DSP0277) - * ========================================================================== */ +/* --- Key Derivation Labels (SPDM 1.2 per DSP0277) --- */ #define SPDM_BIN_CONCAT_PREFIX_12 "spdm1.2 " #define SPDM_BIN_CONCAT_PREFIX_13 "spdm1.3 " From d0746009ecc2d23af016e2bc4b69237e572a3cb4 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 17 Feb 2026 18:02:00 +0000 Subject: [PATCH 4/8] Fix failing CI --- .github/workflows/spdm-emu-test.yml | 2 +- src/spdm_secured.c | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/spdm-emu-test.yml b/.github/workflows/spdm-emu-test.yml index df58037..25d2b88 100644 --- a/.github/workflows/spdm-emu-test.yml +++ b/.github/workflows/spdm-emu-test.yml @@ -60,7 +60,7 @@ jobs: if: steps.cache-spdm-emu.outputs.cache-hit != 'true' run: | cd ~ - git clone --depth 1 https://github.com/DMTF/spdm-emu.git + git clone --depth 1 --recursive https://github.com/DMTF/spdm-emu.git cd spdm-emu mkdir build && cd build cmake -DARCH=x64 -DTOOLCHAIN=GCC -DTARGET=Release -DCRYPTO=mbedtls .. diff --git a/src/spdm_secured.c b/src/spdm_secured.c index 48022d1..6db3300 100644 --- a/src/spdm_secured.c +++ b/src/spdm_secured.c @@ -108,7 +108,6 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, word16 recordLen; word32 hdrSz; word32 aadSz; - word32 offset; int rc; if (ctx == NULL || plain == NULL || enc == NULL || encSz == NULL) { @@ -163,7 +162,6 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, SPDM_Set32LE(&enc[0], ctx->sessionId); SPDM_Set64LE(&enc[4], ctx->reqSeqNum); SPDM_Set16LE(&enc[12], recordLen); - offset = 14; aadSz = 14; XMEMCPY(aad, enc, aadSz); @@ -196,7 +194,6 @@ int wolfSPDM_EncryptInternal(WOLFSPDM_CTX* ctx, SPDM_Set32LE(&enc[0], ctx->sessionId); SPDM_Set16LE(&enc[4], (word16)ctx->reqSeqNum); SPDM_Set16LE(&enc[6], recordLen); - offset = 8; aadSz = 8; XMEMCPY(aad, enc, aadSz); From f09ac0b1f6cfc1356499a26f5189d35048cc3367 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 17 Feb 2026 18:17:14 +0000 Subject: [PATCH 5/8] Cycle cahce period for faster builds --- .github/workflows/spdm-emu-test.yml | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/.github/workflows/spdm-emu-test.yml b/.github/workflows/spdm-emu-test.yml index 25d2b88..c8014e2 100644 --- a/.github/workflows/spdm-emu-test.yml +++ b/.github/workflows/spdm-emu-test.yml @@ -19,13 +19,18 @@ jobs: sudo apt-get update sudo apt-get install -y autoconf automake libtool cmake libmbedtls-dev + - name: Compute cache period + id: cache-period + run: | + echo "biweekly=$(( $(date +%s) / 1296000 ))" >> $GITHUB_OUTPUT + # --- Build wolfSSL --- - name: Cache wolfSSL id: cache-wolfssl uses: actions/cache@v4 with: path: ~/wolfssl-install - key: wolfssl-emu-test-v1 + key: wolfssl-${{ steps.cache-period.outputs.biweekly }} - name: Build wolfSSL if: steps.cache-wolfssl.outputs.cache-hit != 'true' @@ -54,7 +59,7 @@ jobs: uses: actions/cache@v4 with: path: ~/spdm-emu - key: spdm-emu-v1 + key: spdm-emu-${{ steps.cache-period.outputs.biweekly }} - name: Build spdm-emu if: steps.cache-spdm-emu.outputs.cache-hit != 'true' @@ -67,15 +72,29 @@ jobs: make copy_sample_key && make -j$(nproc) # --- Build wolfTPM --- - - name: Build wolfTPM + - name: Cache wolfTPM source + id: cache-wolftpm + uses: actions/cache@v4 + with: + path: ~/wolfTPM + key: wolftpm-${{ steps.cache-period.outputs.biweekly }} + + - name: Clone wolfTPM + if: steps.cache-wolftpm.outputs.cache-hit != 'true' run: | cd ~ - git clone --depth 1 https://github.com/wolfSSL/wolfTPM.git wolfTPM + # TODO: Switch to wolfSSL/wolfTPM once PR #453 is merged + git clone --depth 1 -b add-wolfspdm-backend \ + https://github.com/aidangarske/wolfTPM.git wolfTPM cd wolfTPM ./autogen.sh + + - name: Build wolfTPM + run: | + cd ~/wolfTPM ./configure --enable-spdm --enable-swtpm \ --with-wolfspdm=$HOME/wolfspdm-install \ - --with-wolfssl=$HOME/wolfssl-install + --with-wolfcrypt=$HOME/wolfssl-install make -j$(nproc) # --- Run integration tests --- From 185d5882177db78cc558109950eebcd7e521a71b Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 17 Feb 2026 18:28:14 +0000 Subject: [PATCH 6/8] Cach everything --- .github/workflows/spdm-emu-test.yml | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/.github/workflows/spdm-emu-test.yml b/.github/workflows/spdm-emu-test.yml index c8014e2..fce5dd9 100644 --- a/.github/workflows/spdm-emu-test.yml +++ b/.github/workflows/spdm-emu-test.yml @@ -19,12 +19,13 @@ jobs: sudo apt-get update sudo apt-get install -y autoconf automake libtool cmake libmbedtls-dev + # Cache period rotates every ~15 days so dependencies stay fresh - name: Compute cache period id: cache-period run: | echo "biweekly=$(( $(date +%s) / 1296000 ))" >> $GITHUB_OUTPUT - # --- Build wolfSSL --- + # --- Build wolfSSL (cached) --- - name: Cache wolfSSL id: cache-wolfssl uses: actions/cache@v4 @@ -44,7 +45,7 @@ jobs: make -j$(nproc) make install - # --- Build wolfSPDM --- + # --- Build wolfSPDM (always, this is what we're testing) --- - name: Build and install wolfSPDM run: | ./autogen.sh @@ -53,7 +54,7 @@ jobs: make -j$(nproc) make install - # --- Build spdm-emu --- + # --- Build spdm-emu (cached) --- - name: Cache spdm-emu id: cache-spdm-emu uses: actions/cache@v4 @@ -71,15 +72,15 @@ jobs: cmake -DARCH=x64 -DTOOLCHAIN=GCC -DTARGET=Release -DCRYPTO=mbedtls .. make copy_sample_key && make -j$(nproc) - # --- Build wolfTPM --- - - name: Cache wolfTPM source + # --- Build wolfTPM (cached, dynamically links wolfspdm at runtime) --- + - name: Cache wolfTPM id: cache-wolftpm uses: actions/cache@v4 with: path: ~/wolfTPM key: wolftpm-${{ steps.cache-period.outputs.biweekly }} - - name: Clone wolfTPM + - name: Build wolfTPM if: steps.cache-wolftpm.outputs.cache-hit != 'true' run: | cd ~ @@ -88,10 +89,6 @@ jobs: https://github.com/aidangarske/wolfTPM.git wolfTPM cd wolfTPM ./autogen.sh - - - name: Build wolfTPM - run: | - cd ~/wolfTPM ./configure --enable-spdm --enable-swtpm \ --with-wolfspdm=$HOME/wolfspdm-install \ --with-wolfcrypt=$HOME/wolfssl-install From 814853f29f7416e5f963b3b97e43fe60d5296edb Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 17 Feb 2026 18:49:50 +0000 Subject: [PATCH 7/8] =?UTF-8?q?.github/workflows/multi-compiler.yml=20?= =?UTF-8?q?=E2=80=94=20Matrix=20build=20across=206=20compilers=20(GCC=2011?= =?UTF-8?q?/12/13,=20Clang=2014/15/17)=20with=20-Wall=20-Wextra=20-Werror,?= =?UTF-8?q?=20runs=20unit=20tests.=20Uses=20concurrency=20to=20cancel=20?= =?UTF-8?q?=20=20=20stale=20runs.=20wolfSSL=20is=20cached=20(shared=20acro?= =?UTF-8?q?ss=20all=20matrix=20jobs).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .github/workflows/README.md — One-line summary table of all 8 workflows so you can see at a glance what each file does without opening them. --- .github/workflows/README.md | 12 +++++ .github/workflows/multi-compiler.yml | 67 ++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 .github/workflows/README.md create mode 100644 .github/workflows/multi-compiler.yml diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..c2e0234 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,12 @@ +# CI Workflows + +| Workflow | File | Description | +|----------|------|-------------| +| Build and Test | `build-test.yml` | Matrix build across OS (ubuntu-latest, 22.04) and configure options (debug, nuvoton, dynamic-mem). Runs unit tests for each combination. | +| Multiple Compilers | `multi-compiler.yml` | Builds and tests with GCC 11-13 and Clang 14-17 using `-Wall -Wextra -Werror`. | +| Compiler Warnings | `compiler-warnings.yml` | GCC strict warnings (`-Wpedantic -Wconversion -Wshadow -Werror`) and Clang `-Werror` build. | +| Static Analysis | `static-analysis.yml` | Runs cppcheck (style, performance, portability) and Clang Static Analyzer (scan-build). | +| Memory Check | `memory-check.yml` | Valgrind leak check with `--leak-check=full` for both static and dynamic memory modes. | +| CodeQL Security | `codeql.yml` | GitHub CodeQL security-and-quality analysis. Runs on PRs and weekly (Monday 6 AM UTC). | +| Codespell | `codespell.yml` | Spell-checks source files. | +| SPDM Emulator Test | `spdm-emu-test.yml` | End-to-end integration test against the DMTF libspdm emulator via wolfTPM. Runs 6 tests: session establishment, signed/unsigned measurements, challenge authentication, heartbeat, and key update. Dependencies (wolfSSL, spdm-emu, wolfTPM) are cached and refreshed every ~15 days. | diff --git a/.github/workflows/multi-compiler.yml b/.github/workflows/multi-compiler.yml new file mode 100644 index 0000000..d0afc6f --- /dev/null +++ b/.github/workflows/multi-compiler.yml @@ -0,0 +1,67 @@ +name: Multiple Compilers + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + compiler_test: + name: ${{ matrix.cc }} + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + include: + - cc: gcc-11 + - cc: gcc-12 + - cc: gcc-13 + - cc: clang-14 + - cc: clang-15 + - cc: clang-17 + + steps: + - uses: actions/checkout@v4 + + - name: Install compiler and tools + run: | + sudo apt-get update + sudo apt-get install -y ${{ matrix.cc }} autoconf automake libtool + + - name: Cache wolfSSL + id: cache-wolfssl + uses: actions/cache@v4 + with: + path: ~/wolfssl-install + key: wolfssl-ubuntu-latest-v1 + + - name: Build wolfSSL + if: steps.cache-wolfssl.outputs.cache-hit != 'true' + run: | + cd ~ + git clone --depth 1 https://github.com/wolfSSL/wolfssl.git + cd wolfssl + ./autogen.sh + ./configure --enable-ecc --enable-sha384 --enable-aesgcm --enable-hkdf \ + --enable-ecccustcurves --enable-keygen \ + --prefix=$HOME/wolfssl-install + make -j$(nproc) + make install + + - name: Build wolfSPDM with ${{ matrix.cc }} + run: | + ./autogen.sh + CC=${{ matrix.cc }} ./configure --with-wolfssl=$HOME/wolfssl-install \ + --enable-nuvoton + make -j$(nproc) CFLAGS="-Wall -Wextra -Werror" + + - name: Run unit tests + run: make check + env: + LD_LIBRARY_PATH: ${{ github.workspace }}/src/.libs:$HOME/wolfssl-install/lib From 436ef464433c093ddd2efed04c3dc007d9d9c372 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Tue, 17 Feb 2026 19:04:42 +0000 Subject: [PATCH 8/8] Fix Copilot concerns and retest --- src/spdm_msg.c | 6 +++--- src/spdm_secured.c | 12 ++++++++++-- wolfspdm/spdm.h | 4 ++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/spdm_msg.c b/src/spdm_msg.c index 8f5b2bd..1a73497 100644 --- a/src/spdm_msg.c +++ b/src/spdm_msg.c @@ -871,11 +871,11 @@ int wolfSPDM_VerifyMeasurementSig(WOLFSPDM_CTX* ctx, * * Per DSP0274: * - combined_spdm_prefix = "dmtf-spdm-v1.X.*" x4 = 64 bytes - * - zero_pad = 36 - strlen("responder-measurements signing") = 5 bytes - * - signing_context = "responder-measurements signing" (31 bytes) + * - zero_pad = 36 - strlen("responder-measurements signing") = 6 bytes + * - signing_context = "responder-measurements signing" (30 bytes) * - Hash(L1||L2) = SHA-384(reqMsg || rspBuf[0..sigOffset-1]) = 48 bytes * - * Total M = 64 + 5 + 31 + 48 = 148 bytes + * Total M = 64 + 6 + 30 + 48 = 148 bytes * Then sign Hash(M) */ /* Compute L1||L2 hash per DSP0274 Section 10.11.1: diff --git a/src/spdm_secured.c b/src/spdm_secured.c index 6db3300..e36034c 100644 --- a/src/spdm_secured.c +++ b/src/spdm_secured.c @@ -464,7 +464,11 @@ int wolfSPDM_SendData(WOLFSPDM_CTX* ctx, const byte* data, word32 dataSz) return WOLFSPDM_E_INVALID_ARG; } - if (ctx->state != WOLFSPDM_STATE_CONNECTED) { + if (ctx->state != WOLFSPDM_STATE_CONNECTED +#ifndef NO_WOLFSPDM_MEAS + && ctx->state != WOLFSPDM_STATE_MEASURED +#endif + ) { return WOLFSPDM_E_NOT_CONNECTED; } @@ -506,7 +510,11 @@ int wolfSPDM_ReceiveData(WOLFSPDM_CTX* ctx, byte* data, word32* dataSz) return WOLFSPDM_E_INVALID_ARG; } - if (ctx->state != WOLFSPDM_STATE_CONNECTED) { + if (ctx->state != WOLFSPDM_STATE_CONNECTED +#ifndef NO_WOLFSPDM_MEAS + && ctx->state != WOLFSPDM_STATE_MEASURED +#endif + ) { return WOLFSPDM_E_NOT_CONNECTED; } diff --git a/wolfspdm/spdm.h b/wolfspdm/spdm.h index 3894b09..1577afb 100644 --- a/wolfspdm/spdm.h +++ b/wolfspdm/spdm.h @@ -103,8 +103,8 @@ 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). - * Actual struct size: ~22.7 KB (base with measurements) / ~21.2 KB (NO_WOLFSPDM_MEAS). - * Rounded up to 24 KB for platform alignment and minor future growth. + * Actual struct size: ~31.3 KB (with measurements) / ~29.9 KB (NO_WOLFSPDM_MEAS). + * Rounded up to 32 KB for platform alignment. * wolfSPDM_InitStatic() verifies at runtime that the provided buffer * is large enough; returns WOLFSPDM_E_BUFFER_SMALL if not. */ #define WOLFSPDM_CTX_STATIC_SIZE 32768 /* 32KB - fits CTX with cert validation + challenge + key update fields */