Merge pull request #418 from dgarske/tpmsign

Fix logic for signing with input digest smaller than key size
pull/368/merge
Eric Blankenhorn 2025-06-02 15:56:48 -05:00 committed by GitHub
commit bd682553ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 258 additions and 38 deletions

View File

@ -36,6 +36,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\src\tpm2.c" />
<ClCompile Include="..\..\src\tpm2_asn.c" />
<ClCompile Include="..\..\src\tpm2_cryptocb.c" />
<ClCompile Include="..\..\src\tpm2_packet.c" />
<ClCompile Include="..\..\src\tpm2_param_enc.c" />

View File

@ -27,6 +27,18 @@
#ifndef WOLFTPM2_NO_ASN
/* Helper to trim leading zeros when not required */
byte* TPM2_ASN_TrimZeros(byte* in, word32* len)
{
word32 idx = 0;
while (idx+1 < *len && in[idx] == 0 && (in[idx+1] & 0x80) == 0) {
idx++;
in++;
}
*len -= idx;
return in;
}
int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx, int* len,
word32 maxIdx, int check)
{

View File

@ -24,24 +24,10 @@
#endif
#include <wolftpm/tpm2_wrap.h>
#include <wolftpm/tpm2_asn.h>
#if !defined(WOLFTPM2_NO_WRAPPER)
#if defined(HAVE_ECC) && (defined(WOLFTPM_CRYPTOCB) || \
(defined(HAVE_PK_CALLBACKS) && !defined(WOLFCRYPT_ONLY)))
/* Helper to trim leading zeros when not required */
static byte* wolfTPM2_ASNTrimZeros(byte* in, word32* len)
{
word32 idx = 0;
while (idx+1 < *len && in[idx] == 0 && (in[idx+1] & 0x80) == 0) {
idx++;
in++;
}
*len -= idx;
return in;
}
#endif
#ifdef WOLFTPM_CRYPTOCB
/* Internal structure for tracking hash state */
@ -272,8 +258,8 @@ int wolfTPM2_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx)
rLen = sLen = rsLen / 2;
r = &sigRS[0];
s = &sigRS[rLen];
r = wolfTPM2_ASNTrimZeros(r, &rLen);
s = wolfTPM2_ASNTrimZeros(s, &sLen);
r = TPM2_ASN_TrimZeros(r, &rLen);
s = TPM2_ASN_TrimZeros(s, &sLen);
/* Encode ECDSA Header */
rc = wc_ecc_rs_raw_to_sig(r, rLen, s, sLen,
@ -1134,8 +1120,8 @@ int wolfTPM2_PK_EccSign(WOLFSSL* ssl,
rLen = sLen = rsLen / 2;
r = &sigRS[0];
s = &sigRS[rLen];
r = wolfTPM2_ASNTrimZeros(r, &rLen);
s = wolfTPM2_ASNTrimZeros(s, &sLen);
r = TPM2_ASN_TrimZeros(r, &rLen);
s = TPM2_ASN_TrimZeros(s, &sLen);
/* Encode ECDSA Header */
ret = wc_ecc_rs_raw_to_sig(r, rLen, s, sLen, out, outSz);

View File

@ -3756,7 +3756,7 @@ int wolfTPM2_SignHashScheme(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key,
int sigOutSz = 0;
if (dev == NULL || key == NULL || digest == NULL || sig == NULL ||
sigSz == NULL) {
sigSz == NULL) {
return BAD_FUNC_ARG;
}
@ -3772,10 +3772,24 @@ 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 = digestSz;
XMEMCPY(signIn.digest.buffer, digest, signIn.digest.size);
signIn.digest.size = TPM2_GetHashDigestSize(hashAlg);
if (signIn.digest.size <= 0) {
return BAD_FUNC_ARG;
}
/* 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);
}
else {
XMEMCPY(signIn.digest.buffer, digest, digestSz);
}
signIn.inScheme.scheme = sigAlg;
signIn.inScheme.details.any.hashAlg = hashAlg;
signIn.validation.tag = TPM_ST_HASHCHECK;
@ -3918,8 +3932,18 @@ int wolfTPM2_VerifyHashTicket(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key,
XMEMSET(&verifySigIn, 0, sizeof(verifySigIn));
verifySigIn.keyHandle = key->handle.hndl;
verifySigIn.digest.size = digestSz;
XMEMCPY(verifySigIn.digest.buffer, digest, digestSz);
verifySigIn.digest.size = TPM2_GetHashDigestSize(hashAlg);
if (verifySigIn.digest.size <= 0) {
return BAD_FUNC_ARG;
}
/* 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);
}
else {
XMEMCPY(verifySigIn.digest.buffer, digest, digestSz);
}
verifySigIn.signature.sigAlg = sigAlg;
verifySigIn.signature.signature.any.hashAlg = hashAlg;
if (key->pub.publicArea.type == TPM_ALG_ECC) {

View File

@ -28,6 +28,7 @@
#include <wolftpm/tpm2.h>
#include <wolftpm/tpm2_wrap.h>
#include <wolftpm/tpm2_param_enc.h>
#include <wolftpm/tpm2_asn.h>
#include <hal/tpm_io.h>
#include <examples/tpm_test.h>
@ -100,7 +101,8 @@ static void test_wolfTPM2_Init(void)
AssertIntNE(rc, 0);
/* Test second argument, TPM2 IO Callbacks */
rc = wolfTPM2_Init(&dev, NULL, NULL);
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || defined(WOLFTPM_WINAPI)
#if defined(WOLFTPM_LINUX_DEV) || defined(WOLFTPM_SWTPM) || \
defined(WOLFTPM_WINAPI)
/* Custom IO Callbacks are not needed for Linux TIS driver */
AssertIntEQ(rc, 0);
#else
@ -275,11 +277,12 @@ static void test_TPM2_PCRSel(void)
/* Test bad case - invalid PCR */
XMEMSET(&pcr, 0, sizeof(pcr));
pcrArray[0] = PCR_SELECT_MAX+1;
pcrArray[0] = PCR_LAST+1;
TPM2_SetupPCRSelArray(&pcr, TPM_ALG_SHA256, pcrArray, 1);
if (pcr.count != 0) {
rc = BAD_FUNC_ARG;
}
AssertIntEQ(rc, 0);
/* Test bad case - too many hash algorithms */
XMEMSET(&pcr, 0, sizeof(pcr));
@ -294,6 +297,7 @@ static void test_TPM2_PCRSel(void)
if (pcr.count != HASH_COUNT) {
rc = BAD_FUNC_ARG;
}
AssertIntEQ(rc, 0);
printf("Test TPM Wrapper:\tPCR Select Array:\t%s\n",
rc == 0 ? "Passed" : "Failed");
@ -345,7 +349,8 @@ static void test_TPM2_KDFa(void)
0xd7, 0x04, 0xb6, 0x9a, 0x90, 0x2e, 0x9a, 0xde, 0x84, 0xc4};
#endif
rc = TPM2_KDFa(TPM_ALG_SHA256, &keyIn, label, &contextU, &contextV, key, keyIn.size);
rc = TPM2_KDFa(TPM_ALG_SHA256, &keyIn, label, &contextU, &contextV, key,
keyIn.size);
#ifdef WOLFTPM2_NO_WOLFCRYPT
AssertIntEQ(NOT_COMPILED_IN, rc);
#else
@ -396,6 +401,172 @@ static void test_wolfTPM2_CSR(void)
#endif
}
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \
!defined(WOLFTPM2_NO_ASN)
static void test_wolfTPM2_EccSignVerifyDig(WOLFTPM2_DEV* dev,
WOLFTPM2_KEY* storageKey, const byte* digest, int digestSz,
TPM_ECC_CURVE curve, TPMI_ALG_HASH hashAlg)
{
int rc;
int verifyRes = 0;
WOLFTPM2_KEY eccKey;
TPMT_PUBLIC publicTemplate;
byte sigRs[MAX_ECC_BYTES*2];
word32 sigRsSz = (word32)sizeof(sigRs);
byte sig[ECC_MAX_SIG_SIZE];
word32 sigSz;
byte *r, *s;
word32 rLen, sLen;
ecc_key wolfKey;
int curveSize = TPM2_GetCurveSize(curve);
/* -- Use TPM key to sign and verify with wolfCrypt -- */
/* Create ECC key for signing */
rc = wolfTPM2_GetKeyTemplate_ECC_ex(&publicTemplate, hashAlg,
(TPMA_OBJECT_sensitiveDataOrigin | TPMA_OBJECT_userWithAuth |
TPMA_OBJECT_sign | TPMA_OBJECT_noDA),
curve, TPM_ALG_ECDSA, hashAlg);
AssertIntEQ(rc, 0);
rc = wolfTPM2_CreateAndLoadKey(dev, &eccKey, &storageKey->handle,
&publicTemplate, (byte*)gKeyAuth, sizeof(gKeyAuth)-1);
if ((rc & TPM_RC_HASH) == TPM_RC_HASH) {
printf("Hash type not supported... Skipping\n");
return;
}
AssertIntEQ(rc, 0);
/* Sign with TPM */
rc = wolfTPM2_SignHashScheme(dev, &eccKey, digest, digestSz,
sigRs, (int*)&sigRsSz, TPM_ALG_ECDSA, hashAlg);
AssertIntEQ(rc, 0);
/* Make sure leading zero's not required are trimmed */
rLen = sLen = sigRsSz / 2;
r = &sigRs[0];
s = &sigRs[rLen];
r = TPM2_ASN_TrimZeros(r, &rLen);
s = TPM2_ASN_TrimZeros(s, &sLen);
/* Encode ECDSA Header */
sigSz = (word32)sizeof(sig);
rc = wc_ecc_rs_raw_to_sig(r, rLen, s, sLen, sig, &sigSz);
AssertIntEQ(rc, 0);
/* Initialize wolfCrypt ECC key */
rc = wc_ecc_init(&wolfKey);
AssertIntEQ(rc, 0);
/* Convert TPM key to wolfCrypt key for verification */
rc = wolfTPM2_EccKey_TpmToWolf(dev, &eccKey, &wolfKey);
AssertIntEQ(rc, 0);
/* Verify TPM signature with wolfCrypt */
rc = wc_ecc_verify_hash(sig, sigSz, digest, digestSz, &verifyRes, &wolfKey);
AssertIntEQ(rc, 0);
AssertIntEQ(verifyRes, 1); /* 1 indicates successful verification */
/* Cleanup first wolfCrypt key */
wc_ecc_free(&wolfKey);
wolfTPM2_UnloadHandle(dev, &eccKey.handle);
/* -- Use wolfCrypt key to sign and verify with TPM -- */
/* Initialize new wolfCrypt ECC key */
rc = wc_ecc_init(&wolfKey);
AssertIntEQ(rc, 0);
/* Generate new ECC key with wolfCrypt */
rc = wc_ecc_make_key(wolfTPM2_GetRng(dev), curveSize, &wolfKey);
AssertIntEQ(rc, 0);
/* Sign with wolfCrypt */
sigSz = (word32)sizeof(sig);
rc = wc_ecc_sign_hash(digest, digestSz, sig, &sigSz, wolfTPM2_GetRng(dev),
&wolfKey);
AssertIntEQ(rc, 0);
/* Decode ECDSA Header */
r = sigRs;
s = &sigRs[MAX_ECC_BYTES];
rLen = sLen = MAX_ECC_BYTES;
rc = wc_ecc_sig_to_rs(sig,
sigSz, r, &rLen, s, &sLen);
AssertIntEQ(rc, 0);
/* Convert wolfCrypt key to TPM key for verification */
rc = wolfTPM2_EccKey_WolfToTpm(dev, &wolfKey, &eccKey);
AssertIntEQ(rc, 0);
/* combine R and S at key size (zero pad leading) */
XMEMCPY(&sigRs[curveSize-rLen], r, rLen);
XMEMSET(&sigRs[0], 0, curveSize-rLen);
XMEMCPY(&sigRs[curveSize + (curveSize-sLen)], s, sLen);
XMEMSET(&sigRs[curveSize], 0, curveSize-sLen);
/* Verify wolfCrypt signature with TPM */
rc = wolfTPM2_VerifyHashScheme(dev, &eccKey, sigRs, curveSize*2,
digest, digestSz, TPM_ALG_ECDSA, hashAlg);
AssertIntEQ(rc, 0);
/* Cleanup */
wc_ecc_free(&wolfKey);
wolfTPM2_UnloadHandle(dev, &eccKey.handle);
printf("Test TPM Wrapper:\t"
"Sign/Verify (DigSz=%d, CurveSz=%d, Hash=%s):"
"\t%s\n",
digestSz, TPM2_GetCurveSize(curve), TPM2_GetAlgName(hashAlg),
rc == 0 ? "Passed" : "Failed");
}
/* Test with smaller, same and larger digest sizes using different ECC curves.
* Interop sign and verify with wolfCrypt and TPM */
static void test_wolfTPM2_EccSignVerify(void)
{
int rc, i;
byte digest[TPM_MAX_DIGEST_SIZE];
WOLFTPM2_DEV dev;
WOLFTPM2_KEY storageKey;
/* Initialize TPM */
rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL);
AssertIntEQ(rc, 0);
/* Create storage key */
rc = wolfTPM2_CreateSRK(&dev, &storageKey, TPM_ALG_ECC,
(byte*)gStorageKeyAuth, sizeof(gStorageKeyAuth)-1);
AssertIntEQ(rc, 0);
for (i = 0; i < (int)sizeof(digest); i++) {
digest[i] = (byte)i;
}
test_wolfTPM2_EccSignVerifyDig(&dev, &storageKey, digest, 20,
TPM_ECC_NIST_P256, TPM_ALG_SHA256);
test_wolfTPM2_EccSignVerifyDig(&dev, &storageKey, digest, 32,
TPM_ECC_NIST_P256, TPM_ALG_SHA256);
test_wolfTPM2_EccSignVerifyDig(&dev, &storageKey, digest, 48,
TPM_ECC_NIST_P256, TPM_ALG_SHA256);
test_wolfTPM2_EccSignVerifyDig(&dev, &storageKey, digest, 64,
TPM_ECC_NIST_P256, TPM_ALG_SHA256);
#if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384
test_wolfTPM2_EccSignVerifyDig(&dev, &storageKey, digest, 20,
TPM_ECC_NIST_P384, TPM_ALG_SHA384);
test_wolfTPM2_EccSignVerifyDig(&dev, &storageKey, digest, 32,
TPM_ECC_NIST_P384, TPM_ALG_SHA384);
test_wolfTPM2_EccSignVerifyDig(&dev, &storageKey, digest, 48,
TPM_ECC_NIST_P384, TPM_ALG_SHA384);
test_wolfTPM2_EccSignVerifyDig(&dev, &storageKey, digest, 64,
TPM_ECC_NIST_P384, TPM_ALG_SHA384);
#endif
wolfTPM2_UnloadHandle(&dev, &storageKey.handle);
wolfTPM2_Cleanup(&dev);
}
#endif
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM2_PEM_DECODE) && \
!defined(NO_RSA)
static WOLFTPM2_KEY authKey; /* also used for test_wolfTPM2_PCRPolicy */
@ -479,7 +650,8 @@ static void test_wolfTPM2_PCRPolicy(void)
digest, &digestSz, NULL, 0);
AssertIntEQ(rc, 0);
AssertIntEQ(XMEMCMP(digest, expectedPolicyAuth, sizeof(expectedPolicyAuth)), 0);
AssertIntEQ(XMEMCMP(digest, expectedPolicyAuth, sizeof(expectedPolicyAuth)),
0);
rc = wolfTPM2_ResetPCR(&dev, pcrIndex);
AssertIntEQ(rc, 0);
@ -679,6 +851,10 @@ int unit_tests(int argc, char *argv[])
#endif
test_wolfTPM2_KeyBlob(TPM_ALG_RSA);
test_wolfTPM2_KeyBlob(TPM_ALG_ECC);
#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \
!defined(WOLFTPM2_NO_ASN)
test_wolfTPM2_EccSignVerify();
#endif
test_wolfTPM2_Cleanup();
test_wolfTPM2_thread_local_storage();
#endif /* !WOLFTPM2_NO_WRAPPER */

View File

@ -80,8 +80,9 @@ typedef struct DecodedX509 {
\param maxIdx Maximum allowed index in buffer
\return Length on success, TPM_RC_INSUFFICIENT on buffer error
*/
WOLFTPM_API int TPM2_ASN_GetLength(const uint8_t* input, word32* inOutIdx, int* len,
word32 maxIdx);
WOLFTPM_API int TPM2_ASN_GetLength(const uint8_t* input, word32* inOutIdx,
int* len, word32 maxIdx);
/*!
\ingroup ASN
\brief Decodes ASN.1 length with optional length checking
@ -92,8 +93,9 @@ WOLFTPM_API int TPM2_ASN_GetLength(const uint8_t* input, word32* inOutIdx, int*
\param check Flag to enable length validation
\return Length on success, TPM_RC_INSUFFICIENT on buffer error
*/
WOLFTPM_API int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx, int* len,
word32 maxIdx, int check);
WOLFTPM_API int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx,
int* len, word32 maxIdx, int check);
/*!
\ingroup ASN
\brief Decodes ASN.1 tag and validates length
@ -104,15 +106,19 @@ WOLFTPM_API int TPM2_ASN_GetLength_ex(const uint8_t* input, word32* inOutIdx, in
\param tag Expected ASN.1 tag value
\return 0 on success, TPM_RC_INSUFFICIENT on buffer error, TPM_RC_VALUE on tag mismatch
*/
WOLFTPM_API int TPM2_ASN_DecodeTag(const uint8_t* input, int inputSz, int* inOutIdx, int* tag_len, uint8_t tag);
WOLFTPM_API int TPM2_ASN_DecodeTag(const uint8_t* input, int inputSz,
int* inOutIdx, int* tag_len, uint8_t tag);
/*!
\ingroup ASN
\brief Decodes RSA signature from ASN.1 format
\param pInput Pointer to buffer containing ASN.1 encoded RSA signature
\param inputSz Size of input buffer
\return Size of decoded signature on success, TPM_RC_VALUE on invalid input, TPM_RC_INSUFFICIENT on buffer error
\return Size of decoded signature on success, TPM_RC_VALUE on invalid input,
TPM_RC_INSUFFICIENT on buffer error
*/
WOLFTPM_API int TPM2_ASN_RsaDecodeSignature(uint8_t** pInput, int inputSz);
/*!
\brief Decodes an X.509 certificate
\param input Buffer containing ASN.1 encoded X.509 certificate
@ -120,25 +126,40 @@ WOLFTPM_API int TPM2_ASN_RsaDecodeSignature(uint8_t** pInput, int inputSz);
\param x509 Structure to store decoded certificate data
\return 0 on success, TPM_RC_VALUE on invalid input, TPM_RC_INSUFFICIENT on buffer error
*/
WOLFTPM_API int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz, DecodedX509* x509);
WOLFTPM_API int TPM2_ASN_DecodeX509Cert(uint8_t* input, int inputSz,
DecodedX509* x509);
/*!
\ingroup ASN
\brief Decodes RSA public key from ASN.1 format into TPM2B_PUBLIC structure
\param input Buffer containing ASN.1 encoded RSA public key
\param inputSz Size of input buffer
\param pub TPM2B_PUBLIC structure to store decoded key
\return 0 on success, TPM_RC_VALUE on invalid input, TPM_RC_INSUFFICIENT on buffer error
\return 0 on success, TPM_RC_VALUE on invalid input,
TPM_RC_INSUFFICIENT on buffer error
*/
WOLFTPM_API int TPM2_ASN_DecodeRsaPubKey(uint8_t* input, int inputSz, TPM2B_PUBLIC* pub);
WOLFTPM_API int TPM2_ASN_DecodeRsaPubKey(uint8_t* input, int inputSz,
TPM2B_PUBLIC* pub);
/*!
\ingroup ASN
\brief Removes PKCS#1 v1.5 padding from RSA signature
\param pSig Pointer to buffer containing padded signature, updated to point to unpadded data
\param pSig Pointer to buffer containing padded signature, updated to point
to unpadded data
\param sigSz Size of signature buffer, updated with unpadded size
\return 0 on success, TPM_RC_VALUE on invalid padding
*/
WOLFTPM_API int TPM2_ASN_RsaUnpadPkcsv15(uint8_t** pSig, int* sigSz);
/*!
\ingroup ASN
\brief Removes leading zero bytes from a buffer
\param in Pointer to input buffer containing data to trim
\param len Pointer to length of input buffer, updated with new length after trimming
\return Pointer to the trimmed buffer (may be same as input if no trimming needed)
*/
WOLFTPM_API byte* TPM2_ASN_TrimZeros(byte* in, word32* len);
#ifdef __cplusplus
} /* extern "C" */
#endif