From c5a655311e172afc0a29ffe4be90cf9d3acaf632 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Tue, 27 Oct 2026 10:54:53 -0500 Subject: [PATCH 1/2] fwtpm: size RSASSA DigestInfo buffers for SHA-512, check Sign digest size FwSignDigestAndAppend and FwVerifySignatureCore sized their PKCS#1 v1.5 DigestInfo buffers as WC_MAX_DIGEST_SIZE + MAX_ENCODED_DIG_ASN_SZ, which leaves out the outer SEQUENCE and the AlgorithmIdentifier. A SHA-512 DigestInfo is 83 bytes. Size both buffers with MAX_DER_DIGEST_SZ, the constant wolfCrypt uses for this encoding. - Reject TPM2_Sign with TPM_RC_SIZE when the digest size does not match the resolved scheme hashAlg (Part 3 Sec.20.2.1), matching FwCmd_SignDigest. - examples/pcr/policy_sign: size the DigestInfo buffer with WOLFTPM_MAX_DER_DIGEST_SZ, since older wolfSSL does not export MAX_DER_DIGEST_SZ. - tests: add RSASSA-SHA512 Sign/VerifySignature and Sign digest-size mismatch tests, run the SignDigest RSA roundtrip with SHA-512 too, and move BuildRsa2048SignPrimary out of the WOLFTPM_V185 block so builds without v1.85 can use it. --- examples/pcr/policy_sign.c | 9 +- src/fwtpm/fwtpm_command.c | 6 + src/fwtpm/fwtpm_crypto.c | 11 +- tests/fwtpm_unit_tests.c | 291 ++++++++++++++++++++++++++++++------- 4 files changed, 254 insertions(+), 63 deletions(-) diff --git a/examples/pcr/policy_sign.c b/examples/pcr/policy_sign.c index ab8a9081a..6add635b3 100644 --- a/examples/pcr/policy_sign.c +++ b/examples/pcr/policy_sign.c @@ -67,8 +67,11 @@ static void usage(void) printf("./examples/pcr/policy_sign -pcr=16 -pcr=15 -pcrdigest=ba8ac02be16d9d33080d98611d70bb869aa8ac3fc684ab732b91f75f164b36bc\n"); } -#ifndef WC_MAX_ENCODED_DIG_ASN_SZ -#define WC_MAX_ENCODED_DIG_ASN_SZ 9 /* enum(bit or octet) + length(4) */ +/* Max DER DigestInfo size: SEQUENCE + AlgorithmIdentifier + OCTET STRING + * header + digest. Mirrors wolfSSL's MAX_DER_DIGEST_SZ, which is not public + * in older wolfSSL releases. SHA-512 DigestInfo is 83 bytes. */ +#ifndef WOLFTPM_MAX_DER_DIGEST_SZ +#define WOLFTPM_MAX_DER_DIGEST_SZ (WC_MAX_DIGEST_SIZE + 36) #endif /* Function to sign policy with external key */ @@ -142,7 +145,7 @@ static int PolicySign(TPM_ALG_ID alg, const char* keyFile, const char* password, #if !defined(NO_RSA) && !defined(NO_ASN) rc = wc_InitRsaKey(&key.rsa, NULL); if (rc == 0) { - byte encHash[WC_MAX_DIGEST_SIZE + WC_MAX_ENCODED_DIG_ASN_SZ]; + byte encHash[WOLFTPM_MAX_DER_DIGEST_SZ]; word32 idx = 0; #ifdef HAVE_PKCS8 /* skip PKCS8 header */ diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index c4091f360..b3dac5268 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -9518,6 +9518,12 @@ static TPM_RC FwCmd_Sign(FWTPM_CTX* ctx, TPM2_Packet* cmd, /* Use key's scheme if command scheme is NULL */ FwResolveSignScheme(obj, &sigScheme, &sigHashAlg); } + /* Part 3 Sec.20.2.1: digest size MUST match the scheme hashAlg digest + * size (matches FwCmd_SignDigest). */ + if (rc == 0 && sigHashAlg != TPM_ALG_NULL && + digest.size != (UINT16)TPM2_GetHashDigestSize(sigHashAlg)) { + rc = TPM_RC_SIZE; + } /* Parse validation ticket (TPMT_TK_HASHCHECK) */ if (rc == 0) { diff --git a/src/fwtpm/fwtpm_crypto.c b/src/fwtpm/fwtpm_crypto.c index c0de62ee4..0c2a5514f 100644 --- a/src/fwtpm/fwtpm_crypto.c +++ b/src/fwtpm/fwtpm_crypto.c @@ -3904,9 +3904,11 @@ TPM_RC FwSignDigestAndAppend(FWTPM_CTX* ctx, FWTPM_Object* obj, RSA_PSS_SALT_LEN_DEFAULT, rsaKey, &ctx->rng); } else { - /* RSASSA PKCS#1 v1.5: wrap in ASN.1 DigestInfo */ - byte encHash[WC_MAX_DIGEST_SIZE + - MAX_ENCODED_DIG_ASN_SZ]; + /* RSASSA PKCS#1 v1.5: wrap in ASN.1 DigestInfo. + * Size for the full DigestInfo (SEQUENCE + + * AlgorithmIdentifier + OCTET STRING); SHA-512 needs + * 83 bytes. */ + byte encHash[MAX_DER_DIGEST_SZ]; int encSz; int oid = wc_HashGetOID( (enum wc_HashType)wcHashType); @@ -4064,8 +4066,7 @@ TPM_RC FwVerifySignatureCore(FWTPM_Object* obj, } else { FWTPM_DECLARE_BUF(decSig, FWTPM_MAX_PUB_BUF); - byte expDI[WC_MAX_DIGEST_SIZE + - MAX_ENCODED_DIG_ASN_SZ]; + byte expDI[MAX_DER_DIGEST_SZ]; /* full DigestInfo */ int wcHash = FwGetRsaHashOid( sig->signature.rsassa.hash); int oid = wc_HashGetOID( diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index d532a34aa..a21191543 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -2918,6 +2918,214 @@ static void test_fwtpm_object_auth_policy_size_rejected(void) FWTPM_Cleanup(&ctx); fwtpm_pass("Object authPolicy size mismatch rejected:", 0); } + +#endif /* !NO_RSA && WOLFSSL_KEY_GEN */ + +#if defined(WOLFTPM_V185) || (!defined(NO_RSA) && defined(WOLFSSL_KEY_GEN)) +/* Build a CreatePrimary for an unrestricted RSA-2048 SIGN key with the + * supplied scheme (TPM_ALG_RSASSA / TPM_ALG_RSAPSS) and hashAlg. Returns + * command size. */ +static int BuildRsa2048SignPrimary(byte* buf, UINT16 sigScheme, + UINT16 sigHashAlg) +{ + int pos = 0, pubAreaStart, pubAreaLen; + + PutU16BE(buf + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(buf + pos, 0); pos += 4; + PutU32BE(buf + pos, TPM_CC_CreatePrimary); pos += 4; + PutU32BE(buf + pos, TPM_RH_OWNER); pos += 4; + PutU32BE(buf + pos, 9); pos += 4; + PutU32BE(buf + pos, TPM_RS_PW); pos += 4; + PutU16BE(buf + pos, 0); pos += 2; + buf[pos++] = 0; + PutU16BE(buf + pos, 0); pos += 2; + PutU16BE(buf + pos, 4); pos += 2; /* sensitive */ + PutU16BE(buf + pos, 0); pos += 2; + PutU16BE(buf + pos, 0); pos += 2; + pubAreaStart = pos; + PutU16BE(buf + pos, 0); pos += 2; + PutU16BE(buf + pos, TPM_ALG_RSA); pos += 2; + PutU16BE(buf + pos, TPM_ALG_SHA256); pos += 2; + /* fixedTPM|fixedParent|sensitiveDataOrigin|userWithAuth|sign */ + PutU32BE(buf + pos, 0x00040072); pos += 4; + PutU16BE(buf + pos, 0); pos += 2; /* authPolicy */ + PutU16BE(buf + pos, TPM_ALG_NULL); pos += 2; /* sym */ + PutU16BE(buf + pos, sigScheme); pos += 2; + if (sigScheme != TPM_ALG_NULL) { + PutU16BE(buf + pos, sigHashAlg); pos += 2; + } + PutU16BE(buf + pos, 2048); pos += 2; /* keyBits */ + PutU32BE(buf + pos, 0); pos += 4; /* exponent (default) */ + PutU16BE(buf + pos, 0); pos += 2; /* unique */ + pubAreaLen = pos - pubAreaStart - 2; + PutU16BE(buf + pubAreaStart, (UINT16)pubAreaLen); + PutU16BE(buf + pos, 0); pos += 2; + PutU32BE(buf + pos, 0); pos += 4; + PutU32BE(buf + 2, (UINT32)pos); + return pos; +} +#endif /* WOLFTPM_V185 || (!NO_RSA && WOLFSSL_KEY_GEN) */ + +#if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) +/* Build a TPM2_Sign command (empty password auth, NULL ticket) for keyHandle + * with the supplied inScheme. Returns command size. */ +static int BuildSignCmd(byte* buf, UINT32 keyHandle, const byte* digest, + UINT16 digestSz, UINT16 sigScheme, UINT16 sigHashAlg) +{ + int pos = 0; + + PutU16BE(buf + pos, TPM_ST_SESSIONS); pos += 2; + PutU32BE(buf + pos, 0); pos += 4; + PutU32BE(buf + pos, TPM_CC_Sign); pos += 4; + PutU32BE(buf + pos, keyHandle); pos += 4; + PutU32BE(buf + pos, 9); pos += 4; + PutU32BE(buf + pos, TPM_RS_PW); pos += 4; + PutU16BE(buf + pos, 0); pos += 2; + buf[pos++] = 0; PutU16BE(buf + pos, 0); pos += 2; + PutU16BE(buf + pos, digestSz); pos += 2; + memcpy(buf + pos, digest, digestSz); pos += digestSz; + PutU16BE(buf + pos, sigScheme); pos += 2; + if (sigScheme != TPM_ALG_NULL) { + PutU16BE(buf + pos, sigHashAlg); pos += 2; + } + PutU16BE(buf + pos, TPM_ST_HASHCHECK); pos += 2; + PutU32BE(buf + pos, TPM_RH_NULL); pos += 4; + PutU16BE(buf + pos, 0); pos += 2; + PutU32BE(buf + 2, (UINT32)pos); + return pos; +} + +/* TPM2_Sign digest size must match the scheme hashAlg (Part 3 Sec.20.2.1), + * whether hashAlg comes from inScheme or from the key's scheme. */ +static void test_fwtpm_sign_digest_size_mismatch(void) +{ + FWTPM_CTX ctx; + int rc, rspSize, cmdSz; + UINT32 keyHandle; + byte digest[TPM_MAX_DIGEST_SIZE]; + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + memset(digest, 0xAB, sizeof(digest)); + + /* Key scheme NULL: hashAlg comes from inScheme. */ + cmdSz = BuildRsa2048SignPrimary(gCmd, TPM_ALG_NULL, 0); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + keyHandle = GetU32BE(gRsp + TPM2_HEADER_SIZE); + + cmdSz = BuildSignCmd(gCmd, keyHandle, digest, TPM_MAX_DIGEST_SIZE, + TPM_ALG_RSASSA, TPM_ALG_SHA256); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SIZE); + + cmdSz = BuildSignCmd(gCmd, keyHandle, digest, TPM_SHA256_DIGEST_SIZE, + TPM_ALG_RSASSA, TPM_ALG_SHA256); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + FWTPM_Cleanup(&ctx); + + /* Key scheme RSASSA-SHA256, inScheme NULL: hashAlg comes from the key. */ + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + cmdSz = BuildRsa2048SignPrimary(gCmd, TPM_ALG_RSASSA, TPM_ALG_SHA256); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + keyHandle = GetU32BE(gRsp + TPM2_HEADER_SIZE); + + cmdSz = BuildSignCmd(gCmd, keyHandle, digest, TPM_MAX_DIGEST_SIZE, + TPM_ALG_NULL, 0); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SIZE); + + FWTPM_Cleanup(&ctx); + fwtpm_pass("Sign digest-size mismatch rejected:", 0); +} + +#ifdef WOLFSSL_SHA512 +/* TPM2_Sign + TPM2_VerifySignature with RSASSA-SHA512, the largest PKCS#1 + * v1.5 DigestInfo (83 bytes). */ +static void test_fwtpm_rsassa_sha512_sign_verify(void) +{ + FWTPM_CTX ctx; + int rc, rspSize, pos; + UINT32 keyHandle; + UINT16 sigAlg, sigHash, sigSz; + FWTPM_DECLARE_BUF(sig, 512); + byte digest[TPM_SHA512_DIGEST_SIZE]; + + FWTPM_ALLOC_BUF(sig, 512); + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + + pos = BuildRsa2048SignPrimary(gCmd, TPM_ALG_NULL, 0); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + keyHandle = GetU32BE(gRsp + TPM2_HEADER_SIZE); + + memset(digest, 0xAB, sizeof(digest)); + + /* Sign with inScheme RSASSA-SHA512. */ + pos = BuildSignCmd(gCmd, keyHandle, digest, sizeof(digest), + TPM_ALG_RSASSA, TPM_ALG_SHA512); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + /* Response: header | paramSize | sigAlg | hashAlg | sigSz | sig */ + pos = TPM2_HEADER_SIZE + 4; + sigAlg = GetU16BE(gRsp + pos); pos += 2; + AssertIntEQ(sigAlg, TPM_ALG_RSASSA); + sigHash = GetU16BE(gRsp + pos); pos += 2; + AssertIntEQ(sigHash, TPM_ALG_SHA512); + sigSz = GetU16BE(gRsp + pos); pos += 2; + AssertIntEQ((int)sigSz, 256); /* RSA-2048 */ + memcpy(sig, gRsp + pos, sigSz); + + /* VerifySignature must accept the signature. */ + pos = 0; + PutU16BE(gCmd + pos, TPM_ST_NO_SESSIONS); pos += 2; + PutU32BE(gCmd + pos, 0); pos += 4; + PutU32BE(gCmd + pos, TPM_CC_VerifySignature); pos += 4; + PutU32BE(gCmd + pos, keyHandle); pos += 4; + PutU16BE(gCmd + pos, sizeof(digest)); pos += 2; + memcpy(gCmd + pos, digest, sizeof(digest)); pos += sizeof(digest); + PutU16BE(gCmd + pos, TPM_ALG_RSASSA); pos += 2; + PutU16BE(gCmd + pos, TPM_ALG_SHA512); pos += 2; + PutU16BE(gCmd + pos, sigSz); pos += 2; + memcpy(gCmd + pos, sig, sigSz); pos += sigSz; + PutU32BE(gCmd + 2, (UINT32)pos); + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, pos, gRsp, &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); + + /* Changing the last digest byte must make verification fail. */ + pos = TPM2_HEADER_SIZE + 4 + 2 + sizeof(digest) - 1; + gCmd[pos] ^= 0x01; + rspSize = 0; + rc = FWTPM_ProcessCommand(&ctx, gCmd, (int)GetU32BE(gCmd + 2), gRsp, + &rspSize, 0); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntNE(GetRspRC(gRsp), TPM_RC_SUCCESS); + + FWTPM_Cleanup(&ctx); + FWTPM_FREE_BUF(sig); + fwtpm_pass("Sign/VerifySignature RSASSA-SHA512:", 0); +} +#endif /* WOLFSSL_SHA512 */ #endif /* !NO_RSA && WOLFSSL_KEY_GEN */ #if defined(WOLFTPM_V185) && !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) @@ -4665,49 +4873,6 @@ static void test_fwtpm_signsequence_classical_ecdsa_roundtrip(void) fwtpm_pass("SignSequence/VerifySequence ECDSA Roundtrip:", 1); } -/* Build a CreatePrimary for an unrestricted RSA-2048 SIGN key with the - * supplied scheme (TPM_ALG_RSASSA / TPM_ALG_RSAPSS) and hashAlg. Returns - * command size. */ -static int BuildRsa2048SignPrimary(byte* buf, UINT16 sigScheme, - UINT16 sigHashAlg) -{ - int pos = 0, pubAreaStart, pubAreaLen; - - PutU16BE(buf + pos, TPM_ST_SESSIONS); pos += 2; - PutU32BE(buf + pos, 0); pos += 4; - PutU32BE(buf + pos, TPM_CC_CreatePrimary); pos += 4; - PutU32BE(buf + pos, TPM_RH_OWNER); pos += 4; - PutU32BE(buf + pos, 9); pos += 4; - PutU32BE(buf + pos, TPM_RS_PW); pos += 4; - PutU16BE(buf + pos, 0); pos += 2; - buf[pos++] = 0; - PutU16BE(buf + pos, 0); pos += 2; - PutU16BE(buf + pos, 4); pos += 2; /* sensitive */ - PutU16BE(buf + pos, 0); pos += 2; - PutU16BE(buf + pos, 0); pos += 2; - pubAreaStart = pos; - PutU16BE(buf + pos, 0); pos += 2; - PutU16BE(buf + pos, TPM_ALG_RSA); pos += 2; - PutU16BE(buf + pos, TPM_ALG_SHA256); pos += 2; - /* fixedTPM|fixedParent|sensitiveDataOrigin|userWithAuth|sign */ - PutU32BE(buf + pos, 0x00040072); pos += 4; - PutU16BE(buf + pos, 0); pos += 2; /* authPolicy */ - PutU16BE(buf + pos, TPM_ALG_NULL); pos += 2; /* sym */ - PutU16BE(buf + pos, sigScheme); pos += 2; - if (sigScheme != TPM_ALG_NULL) { - PutU16BE(buf + pos, sigHashAlg); pos += 2; - } - PutU16BE(buf + pos, 2048); pos += 2; /* keyBits */ - PutU32BE(buf + pos, 0); pos += 4; /* exponent (default) */ - PutU16BE(buf + pos, 0); pos += 2; /* unique */ - pubAreaLen = pos - pubAreaStart - 2; - PutU16BE(buf + pubAreaStart, (UINT16)pubAreaLen); - PutU16BE(buf + pos, 0); pos += 2; - PutU32BE(buf + pos, 0); pos += 4; - PutU32BE(buf + 2, (UINT32)pos); - return pos; -} - /* Build a CreatePrimary command for an ECC P-256 key with the supplied * scheme (TPM_ALG_NULL for unspecified). Returns command size. Used by the * v1.85 negative-scheme tests below. */ @@ -4971,28 +5136,30 @@ static void test_fwtpm_signdigest_digest_size_mismatch(void) } /* MEDIUM-34: RSA SignDigest + VerifyDigestSignature roundtrip - * (RSASSA-SHA256). Mirror of the ECDSA test for the RSA classical arm. */ -static void test_fwtpm_signdigest_classical_rsa_roundtrip(void) + * (RSASSA). Mirror of the ECDSA test for the RSA classical arm. Run with + * SHA-512 to cover the largest PKCS#1 v1.5 DigestInfo (83 bytes). */ +static void RunSignDigestRsaRoundtrip(UINT16 hashAlg, int digestSz, + const char* name) { FWTPM_CTX ctx; int rc, rspSize, cmdSz, pos; UINT32 keyHandle; UINT16 sigAlg, sigHash, sigSz; FWTPM_DECLARE_BUF(sig, 512); - byte digest[32]; + byte digest[TPM_MAX_DIGEST_SIZE]; FWTPM_ALLOC_BUF(sig, 512); memset(&ctx, 0, sizeof(ctx)); AssertIntEQ(fwtpm_test_startup(&ctx), 0); - cmdSz = BuildRsa2048SignPrimary(gCmd, TPM_ALG_RSASSA, TPM_ALG_SHA256); + cmdSz = BuildRsa2048SignPrimary(gCmd, TPM_ALG_RSASSA, hashAlg); rspSize = 0; rc = FWTPM_ProcessCommand(&ctx, gCmd, cmdSz, gRsp, &rspSize, 0); AssertIntEQ(rc, TPM_RC_SUCCESS); AssertIntEQ(GetRspRC(gRsp), TPM_RC_SUCCESS); keyHandle = GetU32BE(gRsp + TPM2_HEADER_SIZE); - memset(digest, 0xAB, sizeof(digest)); + memset(digest, 0xAB, digestSz); /* SignDigest. */ pos = 0; @@ -5005,8 +5172,8 @@ static void test_fwtpm_signdigest_classical_rsa_roundtrip(void) PutU16BE(gCmd + pos, 0); pos += 2; gCmd[pos++] = 0; PutU16BE(gCmd + pos, 0); pos += 2; PutU16BE(gCmd + pos, 0); pos += 2; /* context empty */ - PutU16BE(gCmd + pos, sizeof(digest)); pos += 2; - memcpy(gCmd + pos, digest, sizeof(digest)); pos += sizeof(digest); + PutU16BE(gCmd + pos, (UINT16)digestSz); pos += 2; + memcpy(gCmd + pos, digest, digestSz); pos += digestSz; PutU16BE(gCmd + pos, TPM_ST_HASHCHECK); pos += 2; PutU32BE(gCmd + pos, TPM_RH_NULL); pos += 4; PutU16BE(gCmd + pos, 0); pos += 2; @@ -5020,7 +5187,7 @@ static void test_fwtpm_signdigest_classical_rsa_roundtrip(void) sigAlg = GetU16BE(gRsp + pos); pos += 2; AssertIntEQ(sigAlg, TPM_ALG_RSASSA); sigHash = GetU16BE(gRsp + pos); pos += 2; - AssertIntEQ(sigHash, TPM_ALG_SHA256); + AssertIntEQ(sigHash, hashAlg); sigSz = GetU16BE(gRsp + pos); pos += 2; AssertIntEQ((int)sigSz, 256); /* RSA-2048 */ memcpy(sig, gRsp + pos, sigSz); @@ -5032,10 +5199,10 @@ static void test_fwtpm_signdigest_classical_rsa_roundtrip(void) PutU32BE(gCmd + pos, TPM_CC_VerifyDigestSignature); pos += 4; PutU32BE(gCmd + pos, keyHandle); pos += 4; PutU16BE(gCmd + pos, 0); pos += 2; - PutU16BE(gCmd + pos, sizeof(digest)); pos += 2; - memcpy(gCmd + pos, digest, sizeof(digest)); pos += sizeof(digest); + PutU16BE(gCmd + pos, (UINT16)digestSz); pos += 2; + memcpy(gCmd + pos, digest, digestSz); pos += digestSz; PutU16BE(gCmd + pos, TPM_ALG_RSASSA); pos += 2; - PutU16BE(gCmd + pos, TPM_ALG_SHA256); pos += 2; + PutU16BE(gCmd + pos, hashAlg); pos += 2; PutU16BE(gCmd + pos, sigSz); pos += 2; memcpy(gCmd + pos, sig, sigSz); pos += sigSz; PutU32BE(gCmd + 2, (UINT32)pos); @@ -5046,7 +5213,17 @@ static void test_fwtpm_signdigest_classical_rsa_roundtrip(void) FWTPM_Cleanup(&ctx); FWTPM_FREE_BUF(sig); - fwtpm_pass("SignDigest/VerifyDigestSignature RSA Roundtrip:", 1); + fwtpm_pass(name, 1); +} + +static void test_fwtpm_signdigest_classical_rsa_roundtrip(void) +{ + RunSignDigestRsaRoundtrip(TPM_ALG_SHA256, TPM_SHA256_DIGEST_SIZE, + "SignDigest/VerifyDigestSignature RSA Roundtrip:"); +#ifdef WOLFSSL_SHA512 + RunSignDigestRsaRoundtrip(TPM_ALG_SHA512, TPM_SHA512_DIGEST_SIZE, + "SignDigest/VerifyDigestSignature RSA SHA-512 Roundtrip:"); +#endif } /* MEDIUM-33: RSA SignSequence + VerifySequence roundtrip (RSASSA-SHA256). */ @@ -19276,6 +19453,10 @@ int fwtpm_unit_tests(int argc, char *argv[]) #if !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) test_fwtpm_create_primary_rsa(); test_fwtpm_object_auth_policy_size_rejected(); + test_fwtpm_sign_digest_size_mismatch(); +#ifdef WOLFSSL_SHA512 + test_fwtpm_rsassa_sha512_sign_verify(); +#endif #endif #if defined(WOLFTPM_V185) && !defined(NO_RSA) && defined(WOLFSSL_KEY_GEN) test_fwtpm_create_primary_limited_attrs(); From 2ec9d7972e98aaf7e79c0c77e381f86913296ad2 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Tue, 27 Oct 2026 13:29:30 -0500 Subject: [PATCH 2/2] Fix from review --- examples/pcr/policy_sign.c | 7 ------- src/fwtpm/fwtpm_crypto.c | 9 +++------ wolftpm/tpm2_types.h | 13 +++++++++++++ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/examples/pcr/policy_sign.c b/examples/pcr/policy_sign.c index 6add635b3..01951ca99 100644 --- a/examples/pcr/policy_sign.c +++ b/examples/pcr/policy_sign.c @@ -67,13 +67,6 @@ static void usage(void) printf("./examples/pcr/policy_sign -pcr=16 -pcr=15 -pcrdigest=ba8ac02be16d9d33080d98611d70bb869aa8ac3fc684ab732b91f75f164b36bc\n"); } -/* Max DER DigestInfo size: SEQUENCE + AlgorithmIdentifier + OCTET STRING - * header + digest. Mirrors wolfSSL's MAX_DER_DIGEST_SZ, which is not public - * in older wolfSSL releases. SHA-512 DigestInfo is 83 bytes. */ -#ifndef WOLFTPM_MAX_DER_DIGEST_SZ -#define WOLFTPM_MAX_DER_DIGEST_SZ (WC_MAX_DIGEST_SIZE + 36) -#endif - /* Function to sign policy with external key */ static int PolicySign(TPM_ALG_ID alg, const char* keyFile, const char* password, TPM_ALG_ID hashAlg, byte* hash, word32 hashSz, byte* sig, word32* sigSz, diff --git a/src/fwtpm/fwtpm_crypto.c b/src/fwtpm/fwtpm_crypto.c index 0c2a5514f..f4e08adea 100644 --- a/src/fwtpm/fwtpm_crypto.c +++ b/src/fwtpm/fwtpm_crypto.c @@ -3904,11 +3904,8 @@ TPM_RC FwSignDigestAndAppend(FWTPM_CTX* ctx, FWTPM_Object* obj, RSA_PSS_SALT_LEN_DEFAULT, rsaKey, &ctx->rng); } else { - /* RSASSA PKCS#1 v1.5: wrap in ASN.1 DigestInfo. - * Size for the full DigestInfo (SEQUENCE + - * AlgorithmIdentifier + OCTET STRING); SHA-512 needs - * 83 bytes. */ - byte encHash[MAX_DER_DIGEST_SZ]; + /* RSASSA PKCS#1 v1.5: wrap in ASN.1 DigestInfo */ + byte encHash[WOLFTPM_MAX_DER_DIGEST_SZ]; int encSz; int oid = wc_HashGetOID( (enum wc_HashType)wcHashType); @@ -4066,7 +4063,7 @@ TPM_RC FwVerifySignatureCore(FWTPM_Object* obj, } else { FWTPM_DECLARE_BUF(decSig, FWTPM_MAX_PUB_BUF); - byte expDI[MAX_DER_DIGEST_SZ]; /* full DigestInfo */ + byte expDI[WOLFTPM_MAX_DER_DIGEST_SZ]; int wcHash = FwGetRsaHashOid( sig->signature.rsassa.hash); int oid = wc_HashGetOID( diff --git a/wolftpm/tpm2_types.h b/wolftpm/tpm2_types.h index d5d97557c..67464969f 100644 --- a/wolftpm/tpm2_types.h +++ b/wolftpm/tpm2_types.h @@ -602,6 +602,19 @@ typedef int64_t INT64; #define TPM_MAX_DIGEST_SIZE TPM_SHA512_DIGEST_SIZE #endif +/* Max DER-encoded PKCS#1 v1.5 DigestInfo (RFC 8017 Sec.9.2): + * DigestInfo ::= SEQUENCE { AlgorithmIdentifier, OCTET STRING digest } + * Header sizes are worst case and match wolfSSL MAX_SEQ_SZ, MAX_ALGO_SZ and + * MAX_ENCODED_DIG_ASN_SZ, so with a 64-byte max digest this equals wolfSSL + * MAX_DER_DIGEST_SZ (100). wolfSSL only exports that constant since v5.8.2. */ +#define WOLFTPM_DER_SEQ_HDR_SZ 6 /* tag + up to 5-byte length */ +#define WOLFTPM_DER_ALGO_ID_SZ 20 /* AlgorithmIdentifier: OID + NULL */ +#define WOLFTPM_DER_OCTET_HDR_SZ 10 /* OCTET STRING tag + length */ +#ifndef WOLFTPM_MAX_DER_DIGEST_SZ +#define WOLFTPM_MAX_DER_DIGEST_SZ (WOLFTPM_DER_SEQ_HDR_SZ + \ + WOLFTPM_DER_ALGO_ID_SZ + WOLFTPM_DER_OCTET_HDR_SZ + TPM_MAX_DIGEST_SIZE) +#endif + /* ---------------------------------------------------------------------------*/ /* CONFIGURABLE LIMITS */