F-3499 - https://fenrir.wolfssl.com/finding/3499 - wolfTPM2_SignHashScheme/VerifyHashTicket: reject mismatched digest size for RSA, gate ECDSA

pad/truncate
pull/496/head
Aidan 2026-04-28 10:24:06 -07:00
parent ae4ebe764d
commit 68bf667c91
2 changed files with 80 additions and 18 deletions

View File

@ -4955,10 +4955,6 @@ int wolfTPM2_SignHashScheme(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key,
/* set session auth for key */
wolfTPM2_SetAuthHandle(dev, 0, &key->handle);
/* verify input cannot exceed buffer */
if (digestSz > (int)sizeof(signIn.digest.buffer))
digestSz = (int)sizeof(signIn.digest.buffer);
XMEMSET(&signIn, 0, sizeof(signIn));
signIn.keyHandle = key->handle.hndl;
signIn.digest.size = (UINT16)TPM2_GetHashDigestSize(hashAlg);
@ -4966,13 +4962,30 @@ int wolfTPM2_SignHashScheme(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key,
signIn.digest.size > sizeof(signIn.digest.buffer)) {
return BUFFER_E;
}
/* if digest provided is smaller than key size then zero pad leading */
if (digestSz < signIn.digest.size) {
XMEMCPY(&signIn.digest.buffer[signIn.digest.size - digestSz], digest,
digestSz);
/* Hard upper bound: digest must fit the message-buffer. */
if (digestSz < 0 || digestSz > (int)sizeof(signIn.digest.buffer)) {
return BUFFER_E;
}
if (key->pub.publicArea.type != TPM_ALG_ECC) {
/* RSA: digest size must match the declared hash algorithm.
* Silently zero-padding produces a signature over crafted
* but incorrect content, so this is a caller error. */
if (digestSz != (int)signIn.digest.size) {
return BUFFER_E;
}
XMEMCPY(signIn.digest.buffer, digest, digestSz);
}
else {
XMEMCPY(signIn.digest.buffer, digest, digestSz);
/* ECDSA: digests shorter than hashAlg's size are left-padded with
* zeros; longer digests are silently truncated to hashAlg's size
* (TCG Part 1 - ECDSA admits short or long inputs). */
if (digestSz < (int)signIn.digest.size) {
XMEMCPY(&signIn.digest.buffer[signIn.digest.size - digestSz],
digest, digestSz);
}
else {
XMEMCPY(signIn.digest.buffer, digest, signIn.digest.size);
}
}
signIn.inScheme.scheme = sigAlg;
signIn.inScheme.details.any.hashAlg = hashAlg;
@ -5119,10 +5132,6 @@ int wolfTPM2_VerifyHashTicket(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key,
return BAD_FUNC_ARG;
}
/* verify input cannot exceed buffer */
if (digestSz > (int)sizeof(verifySigIn.digest.buffer))
digestSz = (int)sizeof(verifySigIn.digest.buffer);
/* set session auth for key */
wolfTPM2_SetAuthHandle(dev, 0, &key->handle);
@ -5133,13 +5142,27 @@ int wolfTPM2_VerifyHashTicket(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key,
verifySigIn.digest.size > sizeof(verifySigIn.digest.buffer)) {
return BUFFER_E;
}
/* if digest provided is smaller than key size then zero pad leading */
if (digestSz < verifySigIn.digest.size) {
XMEMCPY(&verifySigIn.digest.buffer[verifySigIn.digest.size - digestSz],
digest, digestSz);
/* Hard upper bound: digest must fit the message-buffer. */
if (digestSz < 0 || digestSz > (int)sizeof(verifySigIn.digest.buffer)) {
return BUFFER_E;
}
if (key->pub.publicArea.type != TPM_ALG_ECC) {
/* RSA: digest size must match the declared hash algorithm. */
if (digestSz != (int)verifySigIn.digest.size) {
return BUFFER_E;
}
XMEMCPY(verifySigIn.digest.buffer, digest, digestSz);
}
else {
XMEMCPY(verifySigIn.digest.buffer, digest, digestSz);
/* ECDSA: short digests are left-padded with zeros, longer digests
* are silently truncated to hashAlg's size. */
if (digestSz < (int)verifySigIn.digest.size) {
XMEMCPY(&verifySigIn.digest.buffer[verifySigIn.digest.size -
digestSz], digest, digestSz);
}
else {
XMEMCPY(verifySigIn.digest.buffer, digest, verifySigIn.digest.size);
}
}
verifySigIn.signature.sigAlg = sigAlg;
signature->any.hashAlg = hashAlg;

View File

@ -1982,6 +1982,9 @@ static void test_wolfTPM2_LoadEccPublicKey_Ex(void)
printf("Test TPM Wrapper:\tLoadEccPublicKey_ex:\tSkipped\n");
return;
}
/* Flush any transient objects left by previous tests so CreatePrimary
* does not get TPM_RC_OBJECT_MEMORY on a busy simulator. */
(void)wolfTPM2_UnloadHandles_AllTransient(&dev);
/* Create an ECC SRK to harvest valid P-256 X/Y coordinates from. */
XMEMSET(&pub, 0, sizeof(pub));
@ -2031,6 +2034,41 @@ static void test_wolfTPM2_LoadEccPublicKey_Ex(void)
#endif
}
/* wolfTPM2_SignHashScheme must reject digest sizes that don't match the
* declared hashAlg for RSA keys, instead of silently zero-padding. The
* pad-to-hash-size convention is preserved for ECDSA per spec. */
static void test_wolfTPM2_SignHashScheme_DigestSize(void)
{
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA)
int rc;
WOLFTPM2_DEV dev;
WOLFTPM2_KEY key;
byte digest[TPM_MAX_DIGEST_SIZE];
byte sig[MAX_RSA_KEY_BYTES];
int sigSz = (int)sizeof(sig);
XMEMSET(&dev, 0, sizeof(dev));
XMEMSET(&key, 0, sizeof(key));
XMEMSET(digest, 0xCC, sizeof(digest));
key.handle.hndl = 0x80000000;
key.pub.publicArea.type = TPM_ALG_RSA;
/* SHA-256 digest (32) but caller declared SHA-512 (64): for RSA this
* was previously silently zero-padded; now must return BUFFER_E. */
rc = wolfTPM2_SignHashScheme(&dev, &key, digest, 32, sig, &sigSz,
TPM_ALG_RSASSA, TPM_ALG_SHA512);
AssertIntEQ(rc, BUFFER_E);
/* Oversized digest (larger than declared hashAlg) is also BUFFER_E. */
sigSz = (int)sizeof(sig);
rc = wolfTPM2_SignHashScheme(&dev, &key, digest, 64, sig, &sigSz,
TPM_ALG_RSASSA, TPM_ALG_SHA256);
AssertIntEQ(rc, BUFFER_E);
printf("Test TPM Wrapper:\tSignHashScheme size:\t\tPassed\n");
#endif
}
/* wolfTPM2_RsaEncrypt and wolfTPM2_RsaDecrypt must reject oversized inputs
* with BUFFER_E rather than silently truncating to the message buffer
* length. The bounds check fires before the TPM is contacted, so this
@ -3482,6 +3520,7 @@ int unit_tests(int argc, char *argv[])
test_TPM2_ParseAttest_NvDigest();
test_TPM2_BrainpoolCurveMapping();
test_wolfTPM2_RsaEncryptDecrypt_OversizedBufferE();
test_wolfTPM2_SignHashScheme_DigestSize();
test_wolfTPM2_LoadEccPublicKey_Ex();
test_TPM2_KeyedHashScheme_XorSerialize();
test_TPM2_Signature_EcSchnorrSm2Serialize();