diff --git a/examples/attestation/activate_credential.c b/examples/attestation/activate_credential.c index 3c716c81..b453daba 100644 --- a/examples/attestation/activate_credential.c +++ b/examples/attestation/activate_credential.c @@ -139,6 +139,7 @@ int TPM2_ActivateCredential_Example(void* userCtx, int argc, char *argv[]) rc = readKeyBlob(keyblob, &akKey); if (rc != TPM_RC_SUCCESS) { printf("Failure to read keyblob.\n"); + goto exit; } rc = wolfTPM2_LoadKey(&dev, &akKey, &primary->handle); if (rc != TPM_RC_SUCCESS) { diff --git a/examples/keygen/external_import.c b/examples/keygen/external_import.c index 1d2b2505..88cd92f3 100644 --- a/examples/keygen/external_import.c +++ b/examples/keygen/external_import.c @@ -203,6 +203,7 @@ int TPM2_ExternalImport_Example(void* userCtx, int argc, char *argv[]) rc = readKeyBlob(keyblobFile, rsaKey3); if (rc != TPM_RC_SUCCESS) { printf("Error reading keyblob.bin: %d\n", rc); + goto exit; } } else { /* create key and save as keyblob.bin */ diff --git a/examples/native/native_test.c b/examples/native/native_test.c index 61d92133..11d7c0ad 100644 --- a/examples/native/native_test.c +++ b/examples/native/native_test.c @@ -1450,7 +1450,7 @@ int TPM2_Native_TestArgs(void* userCtx, int argc, char *argv[]) cmdIn.rsaDec.inScheme.scheme = TPM_ALG_OAEP; cmdIn.rsaDec.inScheme.details.oaep.hashAlg = TPM_ALG_SHA256; cmdIn.rsaDec.label.size = sizeof(label); /* Null term required */ - XMEMCPY(cmdIn.rsaDec.label.buffer, label, cmdIn.rsaEnc.label.size); + XMEMCPY(cmdIn.rsaDec.label.buffer, label, cmdIn.rsaDec.label.size); rc = TPM2_RSA_Decrypt(&cmdIn.rsaDec, &cmdOut.rsaDec); if (rc != TPM_RC_SUCCESS) { printf("TPM2_RSA_Decrypt failed 0x%x: %s\n", rc, diff --git a/examples/tls/tls_client.c b/examples/tls/tls_client.c index 7311a1ca..5b2f8e2d 100644 --- a/examples/tls/tls_client.c +++ b/examples/tls/tls_client.c @@ -301,12 +301,11 @@ int TPM2_TLS_ClientArgs(void* userCtx, int argc, char *argv[]) /* Attempt to use pre-provisioned identity key */ rc = wolfTPM2_ReadPublicKey(&dev, &eccKey, TPM2_IDEVID_KEY_HANDLE); if (rc == 0) { - /* Custom should supply their own custom master password used during - * device provisioning. If using a sample TPM supply NULL to use the - * default password. */ - wolfTPM2_SetIdentityAuth(&dev, &eccKey.handle, NULL, 0); + /* NULL uses the sample password on ST33 builds; else supply your + * own provisioning password (or this falls back to a key) */ + rc = wolfTPM2_SetIdentityAuth(&dev, &eccKey.handle, NULL, 0); } - else + if (rc != 0) #endif { /* Create/Load ECC key for TLS authentication */ diff --git a/examples/tls/tls_server.c b/examples/tls/tls_server.c index 4117aa93..52d5f1f1 100644 --- a/examples/tls/tls_server.c +++ b/examples/tls/tls_server.c @@ -343,12 +343,11 @@ int TPM2_TLS_ServerArgs(void* userCtx, int argc, char *argv[]) /* Attempt to use pre-provisioned identity key */ rc = wolfTPM2_ReadPublicKey(&dev, &eccKey, TPM2_IDEVID_KEY_HANDLE); if (rc == 0) { - /* Custom should supply their own custom master password used during - * device provisioning. If using a sample TPM supply NULL to use the - * default password. */ - wolfTPM2_SetIdentityAuth(&dev, &eccKey.handle, NULL, 0); + /* NULL uses the sample password on ST33 builds; else supply your + * own provisioning password (or this falls back to a key) */ + rc = wolfTPM2_SetIdentityAuth(&dev, &eccKey.handle, NULL, 0); } - else + if (rc != 0) #endif { /* Create/Load ECC key for TLS authentication */ diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 2d30f1a3..ebf9690b 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -1554,7 +1554,7 @@ static TPM_RC FwCmd_TestParms(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, case TPM_ALG_NULL: /* Supported - skip remaining type-specific params */ break; - #ifdef WOLFTPM_V185 + #ifdef WOLFTPM_MLDSA /* Part 2 Sec.12.2.3.6: TestParms for ML-DSA / Hash-ML-DSA / ML-KEM * MUST validate the parameterSet range, and for ML-DSA MUST * return TPM_RC_EXT_MU when allowExternalMu=YES on a TPM that @@ -1611,6 +1611,8 @@ static TPM_RC FwCmd_TestParms(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, } break; } + #endif /* WOLFTPM_MLDSA */ + #ifdef WOLFTPM_MLKEM case TPM_ALG_MLKEM: { /* TPMS_MLKEM_PARMS = symmetric (TPMT_SYM_DEF_OBJECT+) + * parameterSet. Use the existing TPMT symmetric parser so @@ -1640,7 +1642,7 @@ static TPM_RC FwCmd_TestParms(FWTPM_CTX* ctx, TPM2_Packet* cmd, int cmdSize, } break; } - #endif /* WOLFTPM_V185 */ + #endif /* WOLFTPM_MLKEM */ default: /* Unrecognized algorithm type. TPM_RC_PARMS only exists * under WOLFTPM_V185; fall back to TPM_RC_TYPE otherwise. */ diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 959bbb0d..a531514e 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -4615,11 +4615,12 @@ int wolfTPM2_RsaKey_TpmToWolf(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* tpmKey, exponent = tpmKey->pub.publicArea.parameters.rsaDetail.exponent; if (exponent == 0) exponent = RSA_DEFAULT_PUBLIC_EXPONENT; - e[3] = (exponent >> 24) & 0xFF; - e[2] = (exponent >> 16) & 0xFF; - e[1] = (exponent >> 8) & 0xFF; - e[0] = exponent & 0xFF; - eSz = e[3] ? 4 : e[2] ? 3 : e[1] ? 2 : e[0] ? 1 : 0; /* calc size */ + /* big-endian, matching wc_RsaPublicKeyDecodeRaw and RsaKey_Exponent */ + e[0] = (exponent >> 24) & 0xFF; + e[1] = (exponent >> 16) & 0xFF; + e[2] = (exponent >> 8) & 0xFF; + e[3] = exponent & 0xFF; + eSz = e[0] ? 4 : e[1] ? 3 : e[2] ? 2 : e[3] ? 1 : 0; /* significant bytes */ /* load public key */ nSz = tpmKey->pub.publicArea.unique.rsa.size; @@ -4628,8 +4629,8 @@ int wolfTPM2_RsaKey_TpmToWolf(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* tpmKey, } XMEMCPY(n, tpmKey->pub.publicArea.unique.rsa.buffer, nSz); - /* load public key portion into wolf RsaKey */ - rc = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, wolfKey); + /* load public key portion into wolf RsaKey (pass trailing significant e) */ + rc = wc_RsaPublicKeyDecodeRaw(n, nSz, e + (sizeof(e) - eSz), eSz, wolfKey); return rc; } @@ -4798,7 +4799,7 @@ int wolfTPM2_RsaKey_PubPemToTpm(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* tpmKey, int wolfTPM2_EccKey_TpmToWolf(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* tpmKey, ecc_key* wolfKey) { - int rc, curve_id; + int rc, curve_id, keySz; byte qx[WOLFTPM2_WRAP_ECC_KEY_BITS / 8]; byte qy[WOLFTPM2_WRAP_ECC_KEY_BITS / 8]; word32 qxSz; @@ -4812,24 +4813,32 @@ int wolfTPM2_EccKey_TpmToWolf(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* tpmKey, /* load curve type */ curve_id = tpmKey->pub.publicArea.parameters.eccDetail.curveID; + keySz = wolfTPM2_GetCurveSize(curve_id); /* field size for right-align */ rc = TPM2_GetWolfCurve(curve_id); if (rc < 0) return rc; curve_id = rc; + if (keySz <= 0 || keySz > (int)sizeof(qx)) { + return BUFFER_E; + } - /* load public key */ + /* load public key; right-align each coordinate into the field-size buffer + * so wc_ecc_import_unsigned reads the correct big-endian value even when + * the TPM stripped leading zero bytes */ qxSz = tpmKey->pub.publicArea.unique.ecc.x.size; - if (qxSz > sizeof(qx) || + if (qxSz > (word32)keySz || qxSz > sizeof(tpmKey->pub.publicArea.unique.ecc.x.buffer)) { return BUFFER_E; } - XMEMCPY(qx, tpmKey->pub.publicArea.unique.ecc.x.buffer, qxSz); + XMEMCPY(qx + (keySz - (int)qxSz), + tpmKey->pub.publicArea.unique.ecc.x.buffer, qxSz); qySz = tpmKey->pub.publicArea.unique.ecc.y.size; - if (qySz > sizeof(qy) || + if (qySz > (word32)keySz || qySz > sizeof(tpmKey->pub.publicArea.unique.ecc.y.buffer)) { return BUFFER_E; } - XMEMCPY(qy, tpmKey->pub.publicArea.unique.ecc.y.buffer, qySz); + XMEMCPY(qy + (keySz - (int)qySz), + tpmKey->pub.publicArea.unique.ecc.y.buffer, qySz); /* load public key portion into wolf ecc_key */ rc = wc_ecc_import_unsigned(wolfKey, qx, qy, NULL, curve_id); @@ -6320,6 +6329,20 @@ int wolfTPM2_ECDHGenKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* ecdhKey, int curve_id, return rc; } +/* Copy the ECDH shared secret to the caller buffer, reject if larger */ +int wolfTPM2_EccZToBuffer(byte* out, int* outSz, const TPM2B_ECC_PARAMETER* z) +{ + if (out == NULL || outSz == NULL || z == NULL) { + return BAD_FUNC_ARG; + } + if (z->size > (UINT16)sizeof(z->buffer) || (int)z->size > *outSz) { + return BUFFER_E; + } + *outSz = (int)z->size; + XMEMCPY(out, z->buffer, z->size); + return TPM_RC_SUCCESS; +} + /* Generate ephemeral key and compute Z (shared secret) */ /* One shot API using private key handle to generate key-pair and return pub-point and shared secret */ @@ -6360,16 +6383,14 @@ int wolfTPM2_ECDHGen(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* privKey, pubPoint->size = ecdhOut.pubPoint.size; wolfTPM2_CopyEccParam(&pubPoint->point.x, &ecdhOut.pubPoint.point.x); wolfTPM2_CopyEccParam(&pubPoint->point.y, &ecdhOut.pubPoint.point.y); - *outSz = ecdhOut.zPoint.point.x.size; - if (*outSz > (int)sizeof(ecdhOut.zPoint.point.x.buffer)) { - *outSz = (int)sizeof(ecdhOut.zPoint.point.x.buffer); /* truncate */ - } - XMEMCPY(out, ecdhOut.zPoint.point.x.buffer, *outSz); + rc = wolfTPM2_EccZToBuffer(out, outSz, &ecdhOut.zPoint.point.x); #ifdef DEBUG_WOLFTPM - printf("TPM2_ECDH_KeyGen: zPt %d, pubPt %d\n", - ecdhOut.zPoint.size, - ecdhOut.pubPoint.size); + if (rc == TPM_RC_SUCCESS) { + printf("TPM2_ECDH_KeyGen: zPt %d, pubPt %d\n", + ecdhOut.zPoint.size, + ecdhOut.pubPoint.size); + } #endif TPM2_ForceZero(&ecdhOut, sizeof(ecdhOut)); @@ -6412,14 +6433,12 @@ int wolfTPM2_ECDHGenZ(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* privKey, return rc; } - *outSz = ecdhZOut.outPoint.point.x.size; - if (*outSz > (int)sizeof(ecdhZOut.outPoint.point.x.buffer)) { - *outSz = (int)sizeof(ecdhZOut.outPoint.point.x.buffer); /* truncate */ - } - XMEMCPY(out, ecdhZOut.outPoint.point.x.buffer, *outSz); + rc = wolfTPM2_EccZToBuffer(out, outSz, &ecdhZOut.outPoint.point.x); #ifdef DEBUG_WOLFTPM - printf("TPM2_ECDH_ZGen: zPt %d\n", ecdhZOut.outPoint.size); + if (rc == TPM_RC_SUCCESS) { + printf("TPM2_ECDH_ZGen: zPt %d\n", ecdhZOut.outPoint.size); + } #endif TPM2_ForceZero(&ecdhZOut, sizeof(ecdhZOut)); @@ -6504,14 +6523,12 @@ int wolfTPM2_ECDHEGenZ(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* parentKey, return rc; } - *outSz = outZGen2Ph.outZ2.point.x.size; - if (*outSz > (int)sizeof(outZGen2Ph.outZ2.point.x.buffer)) { - *outSz = (int)sizeof(outZGen2Ph.outZ2.point.x.buffer); /* truncate */ - } - XMEMCPY(out, outZGen2Ph.outZ2.point.x.buffer, *outSz); + rc = wolfTPM2_EccZToBuffer(out, outSz, &outZGen2Ph.outZ2.point.x); #ifdef DEBUG_WOLFTPM - printf("TPM2_ZGen_2Phase: zPt %d\n", outZGen2Ph.outZ2.size); + if (rc == TPM_RC_SUCCESS) { + printf("TPM2_ZGen_2Phase: zPt %d\n", outZGen2Ph.outZ2.size); + } #endif TPM2_ForceZero(&outZGen2Ph, sizeof(outZGen2Ph)); @@ -10820,10 +10837,12 @@ int wolfTPM2_PolicyAuthorizeMake(TPM_ALG_ID pcrAlg, /* pre-provisioned IAK and IDevID key/cert from TPM vendor */ #ifdef WOLFTPM_MFG_IDENTITY +#if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) static const uint8_t TPM2_IAK_SAMPLE_MASTER_PASSWORD[] = { 0xFE, 0xEF, 0x8C, 0xDF, 0x1B, 0x77, 0xBD, 0x00, 0x30, 0x58, 0x5E, 0x47, 0xB8, 0x21, 0x46, 0x0B }; +#endif int wolfTPM2_SetIdentityAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle, uint8_t* masterPassword, uint16_t masterPasswordSz) @@ -10834,6 +10853,18 @@ int wolfTPM2_SetIdentityAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle, enum wc_HashType hashType = WC_HASH_TYPE_SHA256; uint8_t digest[TPM_SHA256_DIGEST_SIZE]; + if (handle == NULL) { + return BAD_FUNC_ARG; + } + +#if !defined(WOLFTPM_ST33) && !defined(WOLFTPM_AUTODETECT) + /* The sample master password only provisions ST33 sample parts; require an + * explicit secret on other targets rather than deriving from a public value */ + if (masterPassword == NULL || masterPasswordSz == 0) { + return BAD_FUNC_ARG; + } +#endif + /* Get TPM serial number */ rc = TPM2_GetProductInfo(serialNum, (uint16_t)sizeof(serialNum)); if (rc != 0) { @@ -10853,21 +10884,25 @@ int wolfTPM2_SetIdentityAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle, if (rc == 0) { rc = wc_HashUpdate(&hash_ctx, hashType, serialNum, sizeof(serialNum)); if (rc == 0) { - if (masterPassword == NULL || masterPasswordSz == 0) { + if (masterPassword != NULL && masterPasswordSz > 0) { rc = wc_HashUpdate(&hash_ctx, hashType, - TPM2_IAK_SAMPLE_MASTER_PASSWORD, - sizeof(TPM2_IAK_SAMPLE_MASTER_PASSWORD)); + masterPassword, masterPasswordSz); } + #if defined(WOLFTPM_ST33) || defined(WOLFTPM_AUTODETECT) else { rc = wc_HashUpdate(&hash_ctx, hashType, - masterPassword, masterPasswordSz); + TPM2_IAK_SAMPLE_MASTER_PASSWORD, + sizeof(TPM2_IAK_SAMPLE_MASTER_PASSWORD)); } + #endif } if (rc == 0) { rc = wc_HashFinal(&hash_ctx, hashType, digest); } wc_HashFree(&hash_ctx, hashType); + /* scrub the hash state: it retains trailing plaintext of the input */ + TPM2_ForceZero(&hash_ctx, sizeof(hash_ctx)); } /* Only copy digest to handle auth when hashing succeeded — otherwise diff --git a/tests/fwtpm_unit_tests.c b/tests/fwtpm_unit_tests.c index 40813cd4..a19c9541 100644 --- a/tests/fwtpm_unit_tests.c +++ b/tests/fwtpm_unit_tests.c @@ -6482,6 +6482,34 @@ static void test_fwtpm_mlkem_primary_determinism(void) } #endif /* WOLFTPM_V185 */ +#ifdef WOLFTPM_MLDSA +/* TestParms for a supported ML-DSA parameter set with allowExternalMu=NO must + * report TPM_RC_SUCCESS. Guarded on WOLFTPM_MLDSA so it also runs in the lean + * WOLFTPM_PQC build where the PQC arms were previously gated out. */ +static void test_fwtpm_testparms_mldsa_supported_returns_success(void) +{ + FWTPM_CTX ctx; + int rc, rspSize, pos; + + memset(&ctx, 0, sizeof(ctx)); + AssertIntEQ(fwtpm_test_startup(&ctx), 0); + + pos = BuildCmdHeader(gCmd, TPM_ST_NO_SESSIONS, 0, TPM_CC_TestParms); + PutU16BE(gCmd + pos, TPM_ALG_MLDSA); pos += 2; + PutU16BE(gCmd + pos, TPM_MLDSA_65); pos += 2; + gCmd[pos++] = NO; /* allowExternalMu */ + 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); + + FWTPM_Cleanup(&ctx); + fwtpm_pass("TestParms MLDSA supported ps (SUCCESS):", 1); +} +#endif /* WOLFTPM_MLDSA */ + /* ================================================================== */ /* 9. Hash Sequence */ /* ================================================================== */ @@ -10515,6 +10543,11 @@ int fwtpm_unit_tests(int argc, char *argv[]) test_fwtpm_create_primary_mldsa_extmu_returns_ext_mu(); test_fwtpm_testparms_mldsa_extmu_returns_ext_mu(); test_fwtpm_signdigest_wrong_digest_size_returns_size(); +#endif /* WOLFTPM_V185 */ +#ifdef WOLFTPM_MLDSA + test_fwtpm_testparms_mldsa_supported_returns_success(); +#endif +#ifdef WOLFTPM_V185 test_fwtpm_signseqcomplete_x509sign_returns_attributes(); test_fwtpm_sign_x509sign_returns_attributes(); test_fwtpm_signseqcomplete_restricted_generated_value_returns_value(); diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 75483603..5f4da10b 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1353,6 +1353,12 @@ static void test_TPM2_KDFe(void) byte key[TEST_KDFE_KEYSZ]; #ifndef WOLFTPM2_NO_WOLFCRYPT byte key2[TEST_KDFE_KEYSZ]; + /* KAT: SHA256(counter(1) || Z || "IDENTITY\0" || partyU || partyV) */ + const byte keyExp[TEST_KDFE_KEYSZ] = { + 0x36, 0xa3, 0xbc, 0x51, 0x5f, 0xe0, 0x12, 0xb8, + 0x1b, 0xac, 0x81, 0xd6, 0x21, 0x83, 0x74, 0x75, + 0xb9, 0x17, 0xf8, 0x9b, 0xcd, 0x94, 0xd4, 0xa3, + 0xa3, 0x7f, 0x31, 0x49, 0xaa, 0xe2, 0x9b, 0xa1}; #endif rc = TPM2_KDFe_ex(TPM_ALG_SHA256, Z, sizeof(Z), label, @@ -1362,6 +1368,8 @@ static void test_TPM2_KDFe(void) AssertIntEQ(NOT_COMPILED_IN, rc); #else AssertIntEQ((int)sizeof(key), rc); + /* Pin the exact output so counter, label and party order are verified */ + AssertIntEQ(0, XMEMCMP(key, keyExp, sizeof(keyExp))); /* Verify deterministic: same inputs produce same output */ rc = TPM2_KDFe_ex(TPM_ALG_SHA256, Z, sizeof(Z), label, partyU, sizeof(partyU), partyV, sizeof(partyV), @@ -1846,6 +1854,12 @@ static void test_TPM2_CalcHmac(void) TPM2B_NONCE nonceA, nonceB; TPMA_SESSION attr = TPMA_SESSION_continueSession; TPM2B_AUTH hmac1, hmac2; + /* KAT: HMAC-SHA256("test", 0xAB*32 || 0x11*32 || 0x22*32 || attr(0x01)) */ + const byte hmacExp[TPM_SHA256_DIGEST_SIZE] = { + 0x42, 0x7f, 0xbf, 0xe1, 0x1b, 0xc3, 0x4d, 0xff, + 0x89, 0x73, 0x43, 0x79, 0x8f, 0xb6, 0xaa, 0x88, + 0xcd, 0xb3, 0xde, 0xae, 0x88, 0x21, 0xe9, 0xe6, + 0x40, 0x9a, 0x51, 0x3c, 0x68, 0xd5, 0x90, 0xdf}; /* Known auth key */ auth.size = 4; @@ -1867,6 +1881,11 @@ static void test_TPM2_CalcHmac(void) attr, &hmac1); AssertIntEQ(0, rc); + /* Pin the exact HMAC so the cpHash and sessionAttributes contributions + * are verified, not just relative nonce ordering */ + AssertIntEQ(hmac1.size, (int)sizeof(hmacExp)); + AssertIntEQ(0, XMEMCMP(hmac1.buffer, hmacExp, sizeof(hmacExp))); + /* Compute HMAC with (nonceB, nonceA) — reversed order */ rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceB, &nonceA, attr, &hmac2); @@ -1875,6 +1894,20 @@ static void test_TPM2_CalcHmac(void) /* Reversed nonces MUST produce different HMAC */ AssertIntNE(0, XMEMCMP(hmac1.buffer, hmac2.buffer, hmac1.size)); + /* Changing only the cpHash MUST change the HMAC (binds command params) */ + XMEMSET(hash.buffer, 0xCD, hash.size); + rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceA, &nonceB, + attr, &hmac2); + AssertIntEQ(0, rc); + AssertIntNE(0, XMEMCMP(hmac1.buffer, hmac2.buffer, hmac1.size)); + + /* Changing only the sessionAttributes MUST change the HMAC */ + XMEMSET(hash.buffer, 0xAB, hash.size); + rc = TPM2_CalcHmac(TPM_ALG_SHA256, &auth, &hash, &nonceA, &nonceB, + (TPMA_SESSION)0, &hmac2); + AssertIntEQ(0, rc); + AssertIntNE(0, XMEMCMP(hmac1.buffer, hmac2.buffer, hmac1.size)); + printf("Test TPM Wrapper: %-40s Passed\n", "CalcHmac:"); #endif } @@ -2632,6 +2665,174 @@ static void test_TPM2_ParseAttest_NvDigest(void) printf("Test TPM Wrapper:\tParseAttest NV_DIGEST:\t\tPassed\n"); } +#if defined(WOLFTPM_MFG_IDENTITY) && \ + !defined(WOLFTPM_ST33) && !defined(WOLFTPM_AUTODETECT) +/* On non-ST33 targets, omitting the master password must fail closed rather + * than silently deriving auth from the public sample password. */ +static void test_wolfTPM2_SetIdentityAuth_RequiresPassword(void) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_HANDLE handle; + byte pw[16]; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&handle, 0, sizeof(handle)); + XMEMSET(pw, 0x11, sizeof(pw)); + + (void)wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + + /* NULL / zero-length master password is rejected */ + rc = wolfTPM2_SetIdentityAuth(&dev, &handle, NULL, 0); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* NULL handle is rejected */ + rc = wolfTPM2_SetIdentityAuth(&dev, NULL, pw, sizeof(pw)); + AssertIntEQ(rc, BAD_FUNC_ARG); + + wolfTPM2_Cleanup(&dev); + + printf("Test TPM Wrapper:\tSetIdentityAuth requires password:\tPassed\n"); +} +#endif /* WOLFTPM_MFG_IDENTITY && !WOLFTPM_ST33 && !WOLFTPM_AUTODETECT */ + +/* wolfTPM2_EccKey_TpmToWolf must right-align coordinates: a spec-valid TPM + * coordinate with a stripped leading-zero byte (size < field size) would be + * left-aligned and scaled up, corrupting the imported point. */ +static void test_wolfTPM2_EccKey_TpmToWolf_ShortCoord(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ + defined(HAVE_ECC_KEY_IMPORT) && defined(HAVE_ECC_KEY_EXPORT) && \ + !defined(NO_ECC256) + int rc; + ecc_key impKey; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY tpmKey; + byte xImp[32], yImp[32]; + word32 xImpSz, yImpSz; + /* Valid P-256 point whose x has a zero MSB, so the spec-valid stripped + * TPM form (size 31) is shorter than the 32-byte field */ + const byte xRaw[32] = { + 0x00, 0x4d, 0xb3, 0x2d, 0x25, 0x8e, 0x4d, 0xfd, + 0x3f, 0x47, 0xdc, 0x30, 0x9f, 0x36, 0x7a, 0x84, + 0x17, 0x7d, 0x47, 0x71, 0x47, 0x76, 0x5d, 0x04, + 0xd7, 0x11, 0xca, 0x8f, 0xba, 0x92, 0x2f, 0x2c}; + const byte yRaw[32] = { + 0xb3, 0x3d, 0x81, 0xc0, 0x73, 0x66, 0xfd, 0x51, + 0xd5, 0x6f, 0x53, 0xed, 0xac, 0x11, 0x36, 0x40, + 0xb0, 0xb5, 0x23, 0xee, 0x7e, 0x32, 0x99, 0x35, + 0x5e, 0x0d, 0x99, 0xfa, 0xb3, 0x75, 0xc7, 0x57}; + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&tpmKey, 0, sizeof(tpmKey)); + tpmKey.pub.publicArea.type = TPM_ALG_ECC; + tpmKey.pub.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256; + tpmKey.pub.publicArea.unique.ecc.x.size = 31; /* leading zero stripped */ + XMEMCPY(tpmKey.pub.publicArea.unique.ecc.x.buffer, xRaw + 1, 31); + tpmKey.pub.publicArea.unique.ecc.y.size = 32; + XMEMCPY(tpmKey.pub.publicArea.unique.ecc.y.buffer, yRaw, 32); + + AssertIntEQ(0, wc_ecc_init(&impKey)); + rc = wolfTPM2_EccKey_TpmToWolf(&dev, &tpmKey, &impKey); + AssertIntEQ(0, rc); + + /* Imported point must equal the original full-width coordinates */ + xImpSz = sizeof(xImp); + yImpSz = sizeof(yImp); + AssertIntEQ(0, wc_ecc_export_public_raw(&impKey, xImp, &xImpSz, + yImp, &yImpSz)); + AssertIntEQ(0, XMEMCMP(xImp, xRaw, 32)); + AssertIntEQ(0, XMEMCMP(yImp, yRaw, 32)); + + wc_ecc_free(&impKey); + printf("Test TPM Wrapper: %-40s Passed\n", "EccKey_TpmToWolf short coord:"); +#endif +} + +/* wolfTPM2_RsaKey_TpmToWolf must preserve the exponent for multi-byte + * non-palindromic values. The exponent bytes are big-endian on the wolfCrypt + * side, so a little-endian build would corrupt e.g. 0x010003. */ +static void test_wolfTPM2_RsaKey_TpmToWolf_Exponent(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_RSA) + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY tpmKey; + RsaKey wolfKey; + byte eOut[8]; + byte nOut[256]; + word32 eOutSz = (word32)sizeof(eOut); + word32 nOutSz = (word32)sizeof(nOut); + word32 exponent = 0x010003; /* non-palindromic multi-byte exponent */ + + XMEMSET(&dev, 0, sizeof(dev)); + XMEMSET(&tpmKey, 0, sizeof(tpmKey)); + + tpmKey.pub.publicArea.type = TPM_ALG_RSA; + tpmKey.pub.publicArea.parameters.rsaDetail.exponent = exponent; + tpmKey.pub.publicArea.unique.rsa.size = 128; + XMEMSET(tpmKey.pub.publicArea.unique.rsa.buffer, 0xC7, 128); + + rc = wc_InitRsaKey(&wolfKey, NULL); + AssertIntEQ(0, rc); + + rc = wolfTPM2_RsaKey_TpmToWolf(&dev, &tpmKey, &wolfKey); + AssertIntEQ(0, rc); + + rc = wc_RsaFlattenPublicKey(&wolfKey, eOut, &eOutSz, nOut, &nOutSz); + AssertIntEQ(0, rc); + + /* Round-trip: decoded exponent must equal the original TPM exponent */ + AssertIntEQ((int)exponent, (int)wolfTPM2_RsaKey_Exponent(eOut, eOutSz)); + + wc_FreeRsaKey(&wolfKey); + printf("Test TPM Wrapper: %-40s Passed\n", "RsaKey_TpmToWolf exponent:"); +#endif +} + +/* The ECDH shared-secret copy must reject a TPM response x-coordinate larger + * than the caller's output buffer. TPM2_Packet_ParseEccPoint clamps only to + * MAX_ECC_KEY_BYTES, so a MITM/crafted response can report a point.x.size + * bigger than a curve-sized caller buffer; copying it would overflow. */ +static void test_wolfTPM2_EccZToBuffer(void) +{ + int rc; + int outSz; + TPM2B_ECC_PARAMETER z; + byte out[32]; + + XMEMSET(&z, 0, sizeof(z)); + XMEMSET(out, 0, sizeof(out)); + + /* Response larger than caller capacity must be rejected, not copied */ + z.size = (UINT16)(sizeof(out) + 16); + outSz = (int)sizeof(out); + rc = wolfTPM2_EccZToBuffer(out, &outSz, &z); + AssertIntEQ(rc, BUFFER_E); + + /* Exact-fit response is accepted and reports its own size */ + z.size = (UINT16)sizeof(out); + outSz = (int)sizeof(out); + rc = wolfTPM2_EccZToBuffer(out, &outSz, &z); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(outSz, (int)sizeof(out)); + + /* Smaller response shrinks outSz to the response size */ + z.size = 20; + outSz = (int)sizeof(out); + rc = wolfTPM2_EccZToBuffer(out, &outSz, &z); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(outSz, 20); + + /* NULL arguments rejected */ + outSz = (int)sizeof(out); + AssertIntEQ(wolfTPM2_EccZToBuffer(NULL, &outSz, &z), BAD_FUNC_ARG); + AssertIntEQ(wolfTPM2_EccZToBuffer(out, NULL, &z), BAD_FUNC_ARG); + AssertIntEQ(wolfTPM2_EccZToBuffer(out, &outSz, NULL), BAD_FUNC_ARG); + + printf("Test TPM Wrapper:\tECDH shared secret bounds:\tPassed\n"); +} + /* wolfTPM2_LoadEccPublicKey_ex must honor caller-provided scheme, hashAlg * and objectAttributes (in particular, allow TPMA_OBJECT_decrypt for ECDH * peer keys). The legacy wolfTPM2_LoadEccPublicKey must continue to default @@ -4053,6 +4254,7 @@ static void test_wolfTPM2_EccSignVerifyDig(WOLFTPM2_DEV* dev, char nameBuf[48]; #ifdef WOLF_CRYPTO_CB TpmCryptoDevCtx tpmCtx; + byte badDigest[TPM_MAX_DIGEST_SIZE]; XMEMSET(&tpmCtx, 0, sizeof(tpmCtx)); tpmCtx.dev = dev; @@ -4128,6 +4330,20 @@ static void test_wolfTPM2_EccSignVerifyDig(WOLFTPM2_DEV* dev, AssertIntEQ(rc, 0); AssertIntEQ(verifyRes, 1); /* 1 indicates successful verification */ +#ifdef WOLF_CRYPTO_CB + /* Drive the invalid-signature branch of the crypto callback: a tampered + * digest must return verifyRes == 0 with rc == 0 */ + if (flags & FLAGS_USE_CRYPTO_CB) { + XMEMCPY(badDigest, digest, digestSz); + badDigest[0] ^= 0xFF; + verifyRes = 1; + rc = wc_ecc_verify_hash(sig, sigSz, badDigest, digestSz, &verifyRes, + &wolfKey); + AssertIntEQ(rc, 0); + AssertIntEQ(verifyRes, 0); + } +#endif + /* Cleanup first wolfCrypt key */ wc_ecc_free(&wolfKey); wolfTPM2_UnloadHandle(dev, &eccKey.handle); @@ -6145,6 +6361,13 @@ int unit_tests(int argc, char *argv[]) test_TPM2_ResponseProcess_ParamSizeOverflow(); test_wolfTPM2_NVCreateAuthPolicy_NameAlg(); test_wolfTPM2_GetKeyTemplate_KeyedHash_Scheme(); +#if defined(WOLFTPM_MFG_IDENTITY) && \ + !defined(WOLFTPM_ST33) && !defined(WOLFTPM_AUTODETECT) + test_wolfTPM2_SetIdentityAuth_RequiresPassword(); +#endif + test_wolfTPM2_EccKey_TpmToWolf_ShortCoord(); + test_wolfTPM2_RsaKey_TpmToWolf_Exponent(); + test_wolfTPM2_EccZToBuffer(); test_wolfTPM2_LoadEccPublicKey_Ex(); test_TPM2_KeyedHashScheme_XorSerialize(); test_TPM2_Signature_EcSchnorrSm2Serialize(); diff --git a/wolftpm/tpm2_wrap.h b/wolftpm/tpm2_wrap.h index 10c6c33f..47062d4e 100644 --- a/wolftpm/tpm2_wrap.h +++ b/wolftpm/tpm2_wrap.h @@ -4977,14 +4977,19 @@ WOLFTPM_API int wolfTPM2_PolicyCommandCode(WOLFTPM2_DEV* dev, \ingroup wolfTPM2_Wrappers \brief Set authentication for pre-provisioned identity keys \note Used with IAK and IDevID keys on ST33KTPM devices + \note On ST33 (or autodetect) builds a NULL masterPassword derives auth from + the public sample password; other targets require an explicit + masterPassword and return BAD_FUNC_ARG when it is NULL/empty \return TPM_RC_SUCCESS: successful \return TPM_RC_FAILURE: generic failure (check TPM IO and TPM return code) - \return BAD_FUNC_ARG: check the provided arguments + \return BAD_FUNC_ARG: NULL handle, or NULL/empty masterPassword on a + non-ST33 build \param dev pointer to a TPM2_DEV struct \param handle pointer to WOLFTPM2_HANDLE for the identity key - \param masterPassword pointer to master password data + \param masterPassword pointer to master password data (NULL uses the ST33 + sample password only on ST33-capable builds) \param masterPasswordSz size of master password in bytes \sa wolfTPM2_CreateAndLoadAIK @@ -5039,6 +5044,9 @@ WOLFTPM_LOCAL int GetKeyTemplateECC(TPMT_PUBLIC* publicTemplate, TPM_ALG_ID nameAlg, TPMA_OBJECT objectAttributes, TPM_ECC_CURVE curve, TPM_ALG_ID sigScheme, TPM_ALG_ID sigHash); +WOLFTPM_TEST_API int wolfTPM2_EccZToBuffer(byte* out, int* outSz, + const TPM2B_ECC_PARAMETER* z); + #ifdef WOLFTPM_FIRMWARE_UPGRADE typedef int (*wolfTPM2FwDataCb)(