From 4a32b538cdcdf5ce2bf8a96d3300c6c3f31d7cd2 Mon Sep 17 00:00:00 2001 From: Doug Flick Date: Sun, 22 Mar 2026 20:28:09 -0700 Subject: [PATCH 1/5] CryptoPkg/BaseCryptLib: Add host unit test coverage for RSA/EC/X509/PSS EcTests.c: add tampered-message and cross-key ECDSA rejection tests to confirm that EcDsaVerify rejects a modified hash and a signature verified against a different key pair. RsaPssTests.c: add SHA-384 and SHA-512 PSS round-trip tests and a tampered-message rejection test to verify that RsaPssSign and RsaPssVerify operate correctly across hash algorithms and reject corrupted message digests. RsaTests.c: add a cross-key PKCS#1 verify rejection test to confirm that RsaPkcs1Verify fails when the public key does not match the signing key. X509Tests.c: add tests for X509GetSubjectName, GetCommonName, GetOrganizationName (NOT_FOUND case), GetSignatureAlgorithm, and GetTBSCert to improve X.509 parsing coverage. --- .../UnitTest/Library/BaseCryptLib/EcTests.c | 50 ++++++ .../Library/BaseCryptLib/RsaPssTests.c | 153 +++++++++++++++++- .../UnitTest/Library/BaseCryptLib/RsaTests.c | 72 +++++++++ .../UnitTest/Library/BaseCryptLib/X509Tests.c | 89 +++++++++- 4 files changed, 362 insertions(+), 2 deletions(-) diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/EcTests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/EcTests.c index ee1e6e870b4..4c280cd381b 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/EcTests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/EcTests.c @@ -371,10 +371,13 @@ TestVerifyEcKey ( BOOLEAN Status; VOID *EcPrivKey; VOID *EcPubKey; + VOID *EcAltKey; // MU_CHANGE UINT8 HashValue[SHA256_DIGEST_SIZE]; UINTN HashSize; UINT8 Signature[66 * 2]; UINTN SigSize; + UINT8 AltPublic[64]; // MU_CHANGE + UINTN AltPublicLen; // MU_CHANGE // // Retrieve EC private key from PEM data. @@ -428,6 +431,53 @@ TestVerifyEcKey ( ); UT_ASSERT_TRUE (Status); + // MU_CHANGE [BEGIN] + // + // Tampered message test: flip a byte in the hash and verify that the signature + // is rejected, then restore the original hash value. + // + HashValue[0] ^= 0xFF; + Status = EcDsaVerify ( + EcPubKey, + CRYPTO_NID_SHA256, + HashValue, + HashSize, + Signature, + SigSize + ); + UT_ASSERT_FALSE (Status); + HashValue[0] ^= 0xFF; + // MU_CHANGE [END] + + // MU_CHANGE [BEGIN] + // + // Cross-key rejection: generate a fresh key pair and confirm it cannot verify + // a signature produced by a different private key. + // + EcAltKey = EcNewByNid (CRYPTO_NID_SECP256R1); + if (EcAltKey == NULL) { + EcFree (EcPrivKey); + EcFree (EcPubKey); + UT_LOG_ERROR ("Failed to allocate EcAltKey"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + AltPublicLen = sizeof (AltPublic); + Status = EcGenerateKey (EcAltKey, AltPublic, &AltPublicLen); + UT_ASSERT_TRUE (Status); + + Status = EcDsaVerify ( + EcAltKey, + CRYPTO_NID_SHA256, + HashValue, + HashSize, + Signature, + SigSize + ); + UT_ASSERT_FALSE (Status); + EcFree (EcAltKey); + // MU_CHANGE [END] + EcFree (EcPrivKey); EcFree (EcPubKey); diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPssTests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPssTests.c index 48c1b7a4508..04e6d5d57f2 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPssTests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPssTests.c @@ -183,11 +183,162 @@ TestVerifyRsaPssSignVerify ( return UNIT_TEST_PASSED; } +// MU_CHANGE [BEGIN] +UNIT_TEST_STATUS +EFIAPI +TestVerifyRsaPssSignVerifySha384 ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 *Signature; + UINTN SigSize; + BOOLEAN Status; + + Status = RsaSetKey (mRsa, RsaKeyN, RsaPssN, sizeof (RsaPssN)); + UT_ASSERT_TRUE (Status); + + Status = RsaSetKey (mRsa, RsaKeyE, RsaPssE, sizeof (RsaPssE)); + UT_ASSERT_TRUE (Status); + + Status = RsaSetKey (mRsa, RsaKeyD, RsaPssD, sizeof (RsaPssD)); + UT_ASSERT_TRUE (Status); + + SigSize = 0; + Status = RsaPssSign (mRsa, PssMessage, sizeof (PssMessage), SHA384_DIGEST_SIZE, SHA384_DIGEST_SIZE, NULL, &SigSize); + UT_ASSERT_FALSE (Status); + UT_ASSERT_NOT_EQUAL (SigSize, 0); + + Signature = AllocatePool (SigSize); + if (Signature == NULL) { + UT_LOG_ERROR ("Failed to allocate memory for Signature"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status = RsaPssSign (mRsa, PssMessage, sizeof (PssMessage), SHA384_DIGEST_SIZE, SHA384_DIGEST_SIZE, Signature, &SigSize); + UT_ASSERT_TRUE (Status); + + Status = RsaPssVerify (mRsa, PssMessage, sizeof (PssMessage), Signature, SigSize, SHA384_DIGEST_SIZE, SHA384_DIGEST_SIZE); + UT_ASSERT_TRUE (Status); + + // + // Corrupt the signature and verify rejection. + // + Signature[0] ^= 0xFF; + Status = RsaPssVerify (mRsa, PssMessage, sizeof (PssMessage), Signature, SigSize, SHA384_DIGEST_SIZE, SHA384_DIGEST_SIZE); + UT_ASSERT_FALSE (Status); + + FreePool (Signature); + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + +// MU_CHANGE [BEGIN] +UNIT_TEST_STATUS +EFIAPI +TestVerifyRsaPssSignVerifySha512 ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 *Signature; + UINTN SigSize; + BOOLEAN Status; + + Status = RsaSetKey (mRsa, RsaKeyN, RsaPssN, sizeof (RsaPssN)); + UT_ASSERT_TRUE (Status); + + Status = RsaSetKey (mRsa, RsaKeyE, RsaPssE, sizeof (RsaPssE)); + UT_ASSERT_TRUE (Status); + + Status = RsaSetKey (mRsa, RsaKeyD, RsaPssD, sizeof (RsaPssD)); + UT_ASSERT_TRUE (Status); + + SigSize = 0; + Status = RsaPssSign (mRsa, PssMessage, sizeof (PssMessage), SHA512_DIGEST_SIZE, SHA512_DIGEST_SIZE, NULL, &SigSize); + UT_ASSERT_FALSE (Status); + UT_ASSERT_NOT_EQUAL (SigSize, 0); + + Signature = AllocatePool (SigSize); + if (Signature == NULL) { + UT_LOG_ERROR ("Failed to allocate memory for Signature"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status = RsaPssSign (mRsa, PssMessage, sizeof (PssMessage), SHA512_DIGEST_SIZE, SHA512_DIGEST_SIZE, Signature, &SigSize); + UT_ASSERT_TRUE (Status); + + Status = RsaPssVerify (mRsa, PssMessage, sizeof (PssMessage), Signature, SigSize, SHA512_DIGEST_SIZE, SHA512_DIGEST_SIZE); + UT_ASSERT_TRUE (Status); + + // + // Corrupt the signature and verify rejection. + // + Signature[0] ^= 0xFF; + Status = RsaPssVerify (mRsa, PssMessage, sizeof (PssMessage), Signature, SigSize, SHA512_DIGEST_SIZE, SHA512_DIGEST_SIZE); + UT_ASSERT_FALSE (Status); + + FreePool (Signature); + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + +// MU_CHANGE [BEGIN] +UNIT_TEST_STATUS +EFIAPI +TestVerifyRsaPssTamperedMessage ( + IN UNIT_TEST_CONTEXT Context + ) +{ + UINT8 *Signature; + UINTN SigSize; + BOOLEAN Status; + + Status = RsaSetKey (mRsa, RsaKeyN, RsaPssN, sizeof (RsaPssN)); + UT_ASSERT_TRUE (Status); + + Status = RsaSetKey (mRsa, RsaKeyE, RsaPssE, sizeof (RsaPssE)); + UT_ASSERT_TRUE (Status); + + Status = RsaSetKey (mRsa, RsaKeyD, RsaPssD, sizeof (RsaPssD)); + UT_ASSERT_TRUE (Status); + + SigSize = 0; + Status = RsaPssSign (mRsa, PssMessage, sizeof (PssMessage), SHA256_DIGEST_SIZE, SHA256_DIGEST_SIZE, NULL, &SigSize); + UT_ASSERT_FALSE (Status); + UT_ASSERT_NOT_EQUAL (SigSize, 0); + + Signature = AllocatePool (SigSize); + if (Signature == NULL) { + UT_LOG_ERROR ("Failed to allocate memory for Signature"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status = RsaPssSign (mRsa, PssMessage, sizeof (PssMessage), SHA256_DIGEST_SIZE, SHA256_DIGEST_SIZE, Signature, &SigSize); + UT_ASSERT_TRUE (Status); + + // + // Tamper with the message and verify that the signature is rejected. + // + PssMessage[0] ^= 0xFF; + Status = RsaPssVerify (mRsa, PssMessage, sizeof (PssMessage), Signature, SigSize, SHA256_DIGEST_SIZE, SHA256_DIGEST_SIZE); + UT_ASSERT_FALSE (Status); + PssMessage[0] ^= 0xFF; + + FreePool (Signature); + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + TEST_DESC mRsaPssTest[] = { // // -----Description--------------------------------------Class----------------------Function---------------------------------Pre---------------------Post---------Context // - { "TestVerifyRsaPssSignVerify()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaPssSignVerify, TestVerifyRsaPssPreReq, TestVerifyRsaPssCleanUp, NULL }, + { "TestVerifyRsaPssSignVerify()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaPssSignVerify, TestVerifyRsaPssPreReq, TestVerifyRsaPssCleanUp, NULL }, + { "TestVerifyRsaPssSignVerifySha384()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaPssSignVerifySha384, TestVerifyRsaPssPreReq, TestVerifyRsaPssCleanUp, NULL }, // MU_CHANGE + { "TestVerifyRsaPssSignVerifySha512()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaPssSignVerifySha512, TestVerifyRsaPssPreReq, TestVerifyRsaPssCleanUp, NULL }, // MU_CHANGE + { "TestVerifyRsaPssTamperedMessage()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaPssTamperedMessage, TestVerifyRsaPssPreReq, TestVerifyRsaPssCleanUp, NULL }, // MU_CHANGE }; UINTN mRsaPssTestNum = ARRAY_SIZE (mRsaPssTest); diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaTests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaTests.c index f80a9b07df8..81d67ecbab1 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaTests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaTests.c @@ -330,6 +330,77 @@ TestVerifyRsaPkcs1SignVerify ( return UNIT_TEST_PASSED; } +// MU_CHANGE [BEGIN] +UNIT_TEST_STATUS +EFIAPI +TestVerifyRsaPkcs1CrossKeyReject ( + IN UNIT_TEST_CONTEXT Context + ) +{ + VOID *Rsa1; + VOID *Rsa2; + UINT8 HashValue[SHA256_DIGEST_SIZE]; + UINT8 *Signature; + UINTN SigSize; + BOOLEAN Status; + + Rsa1 = RsaNew (); + if (Rsa1 == NULL) { + UT_LOG_ERROR ("Failed to allocate Rsa1"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Rsa2 = RsaNew (); + if (Rsa2 == NULL) { + RsaFree (Rsa1); + UT_LOG_ERROR ("Failed to allocate Rsa2"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status = RsaGenerateKey (Rsa1, 2048, NULL, 0); + UT_ASSERT_TRUE (Status); + + Status = RsaGenerateKey (Rsa2, 2048, NULL, 0); + UT_ASSERT_TRUE (Status); + + ZeroMem (HashValue, sizeof (HashValue)); + + SigSize = 0; + Status = RsaPkcs1Sign (Rsa1, HashValue, SHA256_DIGEST_SIZE, NULL, &SigSize); + UT_ASSERT_FALSE (Status); + UT_ASSERT_NOT_EQUAL (SigSize, 0); + + Signature = AllocatePool (SigSize); + if (Signature == NULL) { + RsaFree (Rsa1); + RsaFree (Rsa2); + UT_LOG_ERROR ("Failed to allocate memory for Signature"); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status = RsaPkcs1Sign (Rsa1, HashValue, SHA256_DIGEST_SIZE, Signature, &SigSize); + UT_ASSERT_TRUE (Status); + + // + // Cross-key rejection: verify with Rsa2 (wrong key) must fail. + // + Status = RsaPkcs1Verify (Rsa2, HashValue, SHA256_DIGEST_SIZE, Signature, SigSize); + UT_ASSERT_FALSE (Status); + + // + // Coherence check: verify with Rsa1 (correct key) must succeed. + // + Status = RsaPkcs1Verify (Rsa1, HashValue, SHA256_DIGEST_SIZE, Signature, SigSize); + UT_ASSERT_TRUE (Status); + + FreePool (Signature); + RsaFree (Rsa1); + RsaFree (Rsa2); + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + TEST_DESC mRsaTest[] = { // // -----Description--------------------------------------Class----------------------Function---------------------------------Pre---------------------Post---------Context @@ -337,6 +408,7 @@ TEST_DESC mRsaTest[] = { { "TestVerifyRsaSetGetKeyComponents()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaSetGetKeyComponents, TestVerifyRsaPreReq, TestVerifyRsaCleanUp, NULL }, { "TestVerifyRsaGenerateKeyComponents()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaGenerateKeyComponents, TestVerifyRsaPreReq, TestVerifyRsaCleanUp, NULL }, { "TestVerifyRsaPkcs1SignVerify()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaPkcs1SignVerify, TestVerifyRsaPreReq, TestVerifyRsaCleanUp, NULL }, + { "TestVerifyRsaPkcs1CrossKeyReject()", "CryptoPkg.BaseCryptLib.Rsa", TestVerifyRsaPkcs1CrossKeyReject, NULL, NULL, NULL }, // MU_CHANGE }; UINTN mRsaTestNum = ARRAY_SIZE (mRsaTest); diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c index 1141821daa2..86864f4e834 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c @@ -626,11 +626,98 @@ TestVerifyX509 ( return UNIT_TEST_PASSED; } +// MU_CHANGE [BEGIN] +UNIT_TEST_STATUS +EFIAPI +TestVerifyX509Fields ( + IN UNIT_TEST_CONTEXT Context + ) +{ + BOOLEAN Status; + RETURN_STATUS RetStatus; + UINT8 *SubjectBuf; + UINTN SubjectSize; + CHAR8 CommonName[128]; + UINTN CommonNameSize; + CHAR8 OrgName[128]; + UINTN OrgNameSize; + UINT8 OidBuf[64]; + UINTN OidSize; + UINT8 *TbsCert; + UINTN TbsCertSize; + + // + // X509GetSubjectName: size-query (NULL buffer) returns FALSE and sets SubjectSize. + // + SubjectSize = 0; + Status = X509GetSubjectName (mTestCert, sizeof (mTestCert), NULL, &SubjectSize); + UT_ASSERT_FALSE (Status); + UT_ASSERT_NOT_EQUAL (SubjectSize, 0); + + SubjectBuf = AllocatePool (SubjectSize); + if (SubjectBuf == NULL) { + ASSERT (SubjectBuf != NULL); + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status = X509GetSubjectName (mTestCert, sizeof (mTestCert), SubjectBuf, &SubjectSize); + UT_ASSERT_TRUE (Status); + FreePool (SubjectBuf); + + // + // X509GetCommonName: size-query returns RETURN_BUFFER_TOO_SMALL, then full retrieval. + // + CommonNameSize = 0; + RetStatus = X509GetCommonName (mTestCert, sizeof (mTestCert), NULL, &CommonNameSize); + UT_ASSERT_EQUAL (RetStatus, RETURN_BUFFER_TOO_SMALL); + UT_ASSERT_NOT_EQUAL (CommonNameSize, 0); + + CommonNameSize = sizeof (CommonName); + RetStatus = X509GetCommonName (mTestCert, sizeof (mTestCert), CommonName, &CommonNameSize); + UT_ASSERT_EQUAL (RetStatus, RETURN_SUCCESS); + UT_ASSERT_EQUAL (AsciiStrCmp (CommonName, "intel test RSA intermediate cert"), 0); + + // + // X509GetOrganizationName: certs have CN only, no O= field — expect RETURN_NOT_FOUND. + // + OrgNameSize = sizeof (OrgName); + RetStatus = X509GetOrganizationName (mTestCert, sizeof (mTestCert), OrgName, &OrgNameSize); + UT_ASSERT_EQUAL (RetStatus, RETURN_NOT_FOUND); + + // + // X509GetSignatureAlgorithm: size-query (NULL buffer) returns FALSE and sets OidSize, + // then full retrieval returns TRUE. + // + OidSize = 0; + Status = X509GetSignatureAlgorithm (mTestCert, sizeof (mTestCert), NULL, &OidSize); + UT_ASSERT_FALSE (Status); + UT_ASSERT_NOT_EQUAL (OidSize, 0); + + OidSize = sizeof (OidBuf); + Status = X509GetSignatureAlgorithm (mTestCert, sizeof (mTestCert), OidBuf, &OidSize); + UT_ASSERT_TRUE (Status); + + // + // X509GetTBSCert: returns a pointer into the cert buffer — no allocation needed. + // + TbsCert = NULL; + TbsCertSize = 0; + Status = X509GetTBSCert (mTestCert, sizeof (mTestCert), &TbsCert, &TbsCertSize); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_EQUAL (TbsCertSize, 0); + UT_ASSERT_TRUE (TbsCert != NULL); + + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + TEST_DESC mX509Test[] = { // // -----Description--------------------------------------Class----------------------Function---------------------------------Pre---------------------Post---------Context // - { "TestVerifyX509()", "CryptoPkg.BaseCryptLib.Hkdf", TestVerifyX509, NULL, NULL, NULL }, + { "TestVerifyX509()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509, NULL, NULL, NULL }, + { "TestVerifyX509Fields()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509Fields, NULL, NULL, NULL }, // MU_CHANGE }; UINTN mX509TestNum = ARRAY_SIZE (mX509Test); From 2b89007e2a830062732da3981851a3015e8e40c8 Mon Sep 17 00:00:00 2001 From: Doug Flick Date: Sun, 22 Mar 2026 20:28:09 -0700 Subject: [PATCH 2/5] CryptoPkg/BaseCryptLib: Add Pkcs7GetSigners and Pkcs7GetCertificatesList tests Add TestVerifyPkcs7GetSigners to exercise the two previously untested signer-extraction APIs: - Pkcs7GetSigners: verify that signing certificates can be extracted from a freshly-signed PKCS#7 blob, and that Pkcs7FreeSigners correctly releases both CertStack and TrustedCert. - Pkcs7GetCertificatesList: verify chained and unchained certificate list extraction from the same blob. This test also served as the regression trigger that exposed a bug in Pkcs7GetCertificatesList (passing &NewP7Data directly to d2i_PKCS7, which advances the pointer past parsed bytes, causing free() to be called on a non-base address). --- .../Library/BaseCryptLib/RsaPkcs7Tests.c | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPkcs7Tests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPkcs7Tests.c index 987c0397b72..533df3aaaf2 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPkcs7Tests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPkcs7Tests.c @@ -663,6 +663,93 @@ TestVerifyPkcs7SignVerifyNonSelfIssued ( return UNIT_TEST_PASSED; } +// MU_CHANGE [BEGIN] +UNIT_TEST_STATUS +EFIAPI +TestVerifyPkcs7GetSigners ( + IN UNIT_TEST_CONTEXT Context + ) +{ + BOOLEAN Status; + UINT8 *P7SignedData; + UINTN P7SignedDataSize; + UINT8 *CertStack; + UINTN StackLength; + UINT8 *TrustedCert; + UINTN CertLength; + + P7SignedData = NULL; + CertStack = NULL; + TrustedCert = NULL; + + // + // Build a PKCS#7 signed blob to operate on. + // + Status = Pkcs7Sign ( + TestKeyPem, + sizeof (TestKeyPem), + (CONST UINT8 *)PemPass, + (UINT8 *)Payload, + AsciiStrLen (Payload), + TestCert, + sizeof (TestCert), + NULL, + &P7SignedData, + &P7SignedDataSize + ); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_NULL (P7SignedData); + + // + // Extract signers — CertStack and TrustedCert must be freed with Pkcs7FreeSigners(). + // + Status = Pkcs7GetSigners ( + P7SignedData, + P7SignedDataSize, + &CertStack, + &StackLength, + &TrustedCert, + &CertLength + ); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_NULL (CertStack); + UT_ASSERT_NOT_EQUAL (StackLength, 0); + UT_ASSERT_NOT_NULL (TrustedCert); + UT_ASSERT_NOT_EQUAL (CertLength, 0); + + Pkcs7FreeSigners (CertStack); + Pkcs7FreeSigners (TrustedCert); + CertStack = NULL; + TrustedCert = NULL; + + // + // GetCertificatesList — outputs chained and unchained cert lists. + // + Status = Pkcs7GetCertificatesList ( + P7SignedData, + P7SignedDataSize, + &CertStack, + &StackLength, + &TrustedCert, + &CertLength + ); + UT_ASSERT_TRUE (Status); + + if (CertStack != NULL) { + Pkcs7FreeSigners (CertStack); + } + + if (TrustedCert != NULL) { + Pkcs7FreeSigners (TrustedCert); + } + + FreePool (P7SignedData); + + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + TEST_DESC mRsaCertTest[] = { // // -----Description--------------------------------------Class----------------------Function-----------------Pre---Post--Context @@ -678,6 +765,7 @@ TEST_DESC mPkcs7Test[] = { // { "TestVerifyPkcs7SignVerify()", "CryptoPkg.BaseCryptLib.Pkcs7", TestVerifyPkcs7SignVerify, NULL, NULL, NULL }, { "TestVerifyPkcs7SignVerifyNonSelfIssued()", "CryptoPkg.BaseCryptLib.Pkcs7", TestVerifyPkcs7SignVerifyNonSelfIssued, NULL, NULL, NULL }, + { "TestVerifyPkcs7GetSigners()", "CryptoPkg.BaseCryptLib.Pkcs7", TestVerifyPkcs7GetSigners, NULL, NULL, NULL }, // MU_CHANGE }; UINTN mPkcs7TestNum = ARRAY_SIZE (mPkcs7Test); From fcc8fc18e165af0ef4eb979ac63bfda991a83504 Mon Sep 17 00:00:00 2001 From: Doug Flick Date: Sun, 22 Mar 2026 20:28:09 -0700 Subject: [PATCH 3/5] CryptoPkg/BaseCryptLib: Add X509 construct/free and key usage tests Add two new test functions to X509Tests.c covering previously untested APIs: TestVerifyX509ConstructFree: - X509ConstructCertificate: wrap DER cert bytes into an opaque X509 object - X509ConstructCertificateStack: build a multi-cert stack with variadic args - X509Free / X509StackFree: release both object types without leaking TestVerifyX509KeyUsage (uses mTestEndCert which has v3_end extensions): - X509GetKeyUsage: verify Digital Signature, Non Repudiation, and Key Encipherment bits are set in the returned OpenSSL KU bitmask - X509GetExtendedKeyUsage: size-query then full retrieval of EKU OID bytes - X509GetExtendedBasicConstraints: size-query then full retrieval of the Basic Constraints extension (CA:FALSE on the end-entity cert) --- .../UnitTest/Library/BaseCryptLib/X509Tests.c | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c index 86864f4e834..1b170eada52 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c @@ -712,12 +712,139 @@ TestVerifyX509Fields ( // MU_CHANGE [END] +// MU_CHANGE [BEGIN] +/** + Test X509ConstructCertificate, X509ConstructCertificateStack, + X509ConstructCertificateStackV, X509Free, X509StackFree. + + Note: X509ConstructCertificateStack calls X509ConstructCertificateStackV + internally, so both are exercised by the stack test below. +**/ +UNIT_TEST_STATUS +EFIAPI +TestVerifyX509ConstructFree ( + IN UNIT_TEST_CONTEXT Context + ) +{ + BOOLEAN Status; + UINT8 *SingleCert; + UINT8 *X509Stack; + + SingleCert = NULL; + X509Stack = NULL; + + // + // X509ConstructCertificate: wrap DER bytes into an opaque X509 object. + // + Status = X509ConstructCertificate (mTestCert, sizeof (mTestCert), &SingleCert); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_NULL (SingleCert); + + // + // X509ConstructCertificateStack: build a stack from one or more certs; NULL + // sentinel terminates the variadic argument list. + // + Status = X509ConstructCertificateStack ( + &X509Stack, + mTestCert, + sizeof (mTestCert), + mTestCaCert, + sizeof (mTestCaCert), + NULL + ); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_NULL (X509Stack); + + X509Free (SingleCert); + X509StackFree (X509Stack); + + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + +// MU_CHANGE [BEGIN] +/** + Test X509GetKeyUsage, X509GetExtendedKeyUsage, and X509GetExtendedBasicConstraints + against mTestEndCert (created with v3_end extensions). + + mTestEndCert has: + Key Usage: Digital Signature, Non Repudiation, Key Encipherment + Extended Key Usage: TLS Web Server Auth, TLS Web Client Auth, OCSP Signing + Basic Constraints: CA:FALSE (critical) +**/ +UNIT_TEST_STATUS +EFIAPI +TestVerifyX509KeyUsage ( + IN UNIT_TEST_CONTEXT Context + ) +{ + BOOLEAN Status; + UINTN KeyUsage; + UINT8 *ExtUsageBuf; + UINTN ExtUsageSize; + UINT8 *BasicBuf; + UINTN BasicSize; + + ExtUsageBuf = NULL; + BasicBuf = NULL; + + // + // X509GetKeyUsage: returns OpenSSL KU bitmask. + // End cert has Digital Signature (0x0080) | Non Repudiation (0x0040) | Key Encipherment (0x0020). + // + KeyUsage = 0; + Status = X509GetKeyUsage (mTestEndCert, sizeof (mTestEndCert), &KeyUsage); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_EQUAL (KeyUsage & 0x0080, 0); // Digital Signature + UT_ASSERT_NOT_EQUAL (KeyUsage & 0x0040, 0); // Non Repudiation + UT_ASSERT_NOT_EQUAL (KeyUsage & 0x0020, 0); // Key Encipherment + + // + // X509GetExtendedKeyUsage: size-query then retrieval. + // + ExtUsageSize = 0; + Status = X509GetExtendedKeyUsage (mTestEndCert, sizeof (mTestEndCert), NULL, &ExtUsageSize); + UT_ASSERT_FALSE (Status); + UT_ASSERT_NOT_EQUAL (ExtUsageSize, 0); + + ExtUsageBuf = AllocatePool (ExtUsageSize); + UT_ASSERT_NOT_NULL (ExtUsageBuf); + + Status = X509GetExtendedKeyUsage (mTestEndCert, sizeof (mTestEndCert), ExtUsageBuf, &ExtUsageSize); + UT_ASSERT_TRUE (Status); + + FreePool (ExtUsageBuf); + + // + // X509GetExtendedBasicConstraints: end cert has CA:FALSE. + // + BasicSize = 0; + Status = X509GetExtendedBasicConstraints (mTestEndCert, sizeof (mTestEndCert), NULL, &BasicSize); + UT_ASSERT_FALSE (Status); + UT_ASSERT_NOT_EQUAL (BasicSize, 0); + + BasicBuf = AllocatePool (BasicSize); + UT_ASSERT_NOT_NULL (BasicBuf); + + Status = X509GetExtendedBasicConstraints (mTestEndCert, sizeof (mTestEndCert), BasicBuf, &BasicSize); + UT_ASSERT_TRUE (Status); + + FreePool (BasicBuf); + + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + TEST_DESC mX509Test[] = { // // -----Description--------------------------------------Class----------------------Function---------------------------------Pre---------------------Post---------Context // { "TestVerifyX509()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509, NULL, NULL, NULL }, { "TestVerifyX509Fields()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509Fields, NULL, NULL, NULL }, // MU_CHANGE + { "TestVerifyX509ConstructFree()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509ConstructFree, NULL, NULL, NULL }, // MU_CHANGE + { "TestVerifyX509KeyUsage()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509KeyUsage, NULL, NULL, NULL }, // MU_CHANGE }; UINTN mX509TestNum = ARRAY_SIZE (mX509Test); From 5ede67345e3e79dedc744346e62c97189c813651 Mon Sep 17 00:00:00 2001 From: Doug Flick Date: Sun, 22 Mar 2026 20:28:09 -0700 Subject: [PATCH 4/5] CryptoPkg/BaseCryptLib: Add Pkcs7Encrypt and Pkcs7GetAttachedContent tests Add TestVerifyPkcs7Encrypt to cover the two remaining untested PKCS#7 APIs: Pkcs7Encrypt: - Build a recipient X509 stack from TestCert using X509ConstructCertificateStack - Encrypt a payload with AES-256-CBC and verify non-empty ContentInfo is produced - Test NULL X509Stack argument rejection Pkcs7GetAttachedContent: - Call on a Pkcs7Sign-produced blob (which uses PKCS7_DETACHED) - Verify the API returns TRUE with Content=NULL / ContentSize=0, confirming correct detached-content signalling without crashing --- .../Library/BaseCryptLib/RsaPkcs7Tests.c | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPkcs7Tests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPkcs7Tests.c index 533df3aaaf2..294b85992f6 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPkcs7Tests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/RsaPkcs7Tests.c @@ -750,6 +750,125 @@ TestVerifyPkcs7GetSigners ( // MU_CHANGE [END] +// MU_CHANGE [BEGIN] +/** + Test Pkcs7Encrypt: encrypt a payload for a recipient identified by TestCert + (RSA public key cert). Verifies the function produces non-empty output and + tests invalid-argument rejection. + + Test Pkcs7GetAttachedContent: the blob produced by Pkcs7Sign uses + PKCS7_DETACHED so this call returns FALSE / empty — but the API must + not crash and must behave correctly. +**/ +UNIT_TEST_STATUS +EFIAPI +TestVerifyPkcs7Encrypt ( + IN UNIT_TEST_CONTEXT Context + ) +{ + BOOLEAN Status; + UINT8 *X509Stack; + UINT8 *ContentInfo; + UINTN ContentInfoSize; + UINT8 *P7SignedData; + UINTN P7SignedDataSize; + VOID *AttachedContent; + UINTN AttachedContentSize; + + X509Stack = NULL; + ContentInfo = NULL; + P7SignedData = NULL; + AttachedContent = NULL; + AttachedContentSize = 0; + + // + // Build an X509 stack containing the test certificate as the recipient. + // + Status = X509ConstructCertificateStack ( + &X509Stack, + TestCert, + sizeof (TestCert), + NULL + ); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_NULL (X509Stack); + + // + // Encrypt the payload for the recipient. + // + ContentInfoSize = 0; + Status = Pkcs7Encrypt ( + X509Stack, + (UINT8 *)Payload, + AsciiStrLen (Payload), + CRYPTO_NID_AES256CBC, + CRYPTO_PKCS7_DEFAULT, + &ContentInfo, + &ContentInfoSize + ); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_NULL (ContentInfo); + UT_ASSERT_NOT_EQUAL (ContentInfoSize, 0); + + FreePool (ContentInfo); + ContentInfo = NULL; + + // + // Invalid-argument rejection: NULL X509Stack must return FALSE. + // + ContentInfoSize = 0; + Status = Pkcs7Encrypt ( + NULL, + (UINT8 *)Payload, + AsciiStrLen (Payload), + CRYPTO_NID_AES256CBC, + CRYPTO_PKCS7_DEFAULT, + &ContentInfo, + &ContentInfoSize + ); + UT_ASSERT_FALSE (Status); + + X509StackFree (X509Stack); + + // + // Pkcs7GetAttachedContent: Pkcs7Sign produces detached content, so the + // result is FALSE with Content=NULL / ContentSize=0. + // + Status = Pkcs7Sign ( + TestKeyPem, + sizeof (TestKeyPem), + (CONST UINT8 *)PemPass, + (UINT8 *)Payload, + AsciiStrLen (Payload), + TestCert, + sizeof (TestCert), + NULL, + &P7SignedData, + &P7SignedDataSize + ); + UT_ASSERT_TRUE (Status); + UT_ASSERT_NOT_NULL (P7SignedData); + + Status = Pkcs7GetAttachedContent ( + P7SignedData, + P7SignedDataSize, + &AttachedContent, + &AttachedContentSize + ); + // + // Detached signature: no content embedded — TRUE but Content=NULL / ContentSize=0. + // + UT_ASSERT_TRUE (Status); + UT_ASSERT_EQUAL (AttachedContentSize, 0); + UT_ASSERT_TRUE (AttachedContent == NULL); + + FreePool (P7SignedData); + + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + TEST_DESC mRsaCertTest[] = { // // -----Description--------------------------------------Class----------------------Function-----------------Pre---Post--Context @@ -766,6 +885,7 @@ TEST_DESC mPkcs7Test[] = { { "TestVerifyPkcs7SignVerify()", "CryptoPkg.BaseCryptLib.Pkcs7", TestVerifyPkcs7SignVerify, NULL, NULL, NULL }, { "TestVerifyPkcs7SignVerifyNonSelfIssued()", "CryptoPkg.BaseCryptLib.Pkcs7", TestVerifyPkcs7SignVerifyNonSelfIssued, NULL, NULL, NULL }, { "TestVerifyPkcs7GetSigners()", "CryptoPkg.BaseCryptLib.Pkcs7", TestVerifyPkcs7GetSigners, NULL, NULL, NULL }, // MU_CHANGE + { "TestVerifyPkcs7Encrypt()", "CryptoPkg.BaseCryptLib.Pkcs7", TestVerifyPkcs7Encrypt, NULL, NULL, NULL }, // MU_CHANGE }; UINTN mPkcs7TestNum = ARRAY_SIZE (mPkcs7Test); From d242437cd3763cfe1d1c1cd106d792a19256a304 Mon Sep 17 00:00:00 2001 From: Doug Flick Date: Sun, 22 Mar 2026 20:28:09 -0700 Subject: [PATCH 5/5] CryptoPkg/BaseCryptLib: Add EcPointGetAffineCoordinates and Asn1GetTag tests Cover two previously untested BaseCryptLib APIs: - EcPointGetAffineCoordinates: round-trip test generating a key pair and verifying the extracted affine coordinates match the original BIGNUMs - Asn1GetTag: verify correct tag matching advances Ptr and returns length; verify tag mismatch restores Ptr to original position Note: X509ConstructCertificateStack exercises X509ConstructCertificateStackV internally (covered by commit 3). --- .../UnitTest/Library/BaseCryptLib/EcTests.c | 20 +++++++++ .../UnitTest/Library/BaseCryptLib/X509Tests.c | 43 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/EcTests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/EcTests.c index 4c280cd381b..5c18562eda9 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/EcTests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/EcTests.c @@ -203,6 +203,8 @@ TestVerifyEcBasic ( { UINTN CurveCount; BOOLEAN Status; + VOID *BnXOut; // MU_CHANGE + VOID *BnYOut; // MU_CHANGE // // Initialize BigNumbers @@ -244,6 +246,24 @@ TestVerifyEcBasic ( Status = EcPointIsOnCurve (Group, Point1, NULL); UT_ASSERT_TRUE (Status); + // MU_CHANGE [BEGIN] + // Round-trip: get coordinates back and verify they match + BnXOut = BigNumInit (); + BnYOut = BigNumInit (); + if ((BnXOut == NULL) || (BnYOut == NULL)) { + return UNIT_TEST_ERROR_TEST_FAILED; + } + + Status = EcPointGetAffineCoordinates (Group, Point1, BnXOut, BnYOut, NULL); + UT_ASSERT_TRUE (Status); + UT_ASSERT_TRUE (BigNumCmp (BnXOut, BnX) == 0); + UT_ASSERT_TRUE (BigNumCmp (BnYOut, BnY) == 0); + BigNumFree (BnXOut, TRUE); + BigNumFree (BnYOut, TRUE); + BnXOut = NULL; + BnYOut = NULL; + // MU_CHANGE [END] + Status = EcPointIsAtInfinity (Group, Point1); UT_ASSERT_FALSE (Status); diff --git a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c index 1b170eada52..88177dce5cd 100644 --- a/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c +++ b/CryptoPkg/Test/UnitTest/Library/BaseCryptLib/X509Tests.c @@ -837,6 +837,48 @@ TestVerifyX509KeyUsage ( // MU_CHANGE [END] +// MU_CHANGE [BEGIN] +/** + Test Asn1GetTag against a hand-crafted minimal DER buffer. + Verifies correct tag matching, Length/Ptr advancement, and Ptr restoration + on tag mismatch — all important regression signals for provider changes. +**/ +UNIT_TEST_STATUS +EFIAPI +TestVerifyMiscCryptoAPIs ( + IN UNIT_TEST_CONTEXT Context + ) +{ + // DER INTEGER 42: tag=0x02, length=0x01, value=0x2A + UINT8 DerInteger[] = { 0x02, 0x01, 0x2A }; + UINT8 *Ptr; + UINTN Length; + BOOLEAN Status; + + // + // Matching tag (0x02 = UNIVERSAL INTEGER): must succeed, advance Ptr to value, + // and return the correct content length. + // + Ptr = DerInteger; + Length = 0; + Status = Asn1GetTag (&Ptr, DerInteger + sizeof (DerInteger), &Length, 0x02); + UT_ASSERT_TRUE (Status); + UT_ASSERT_EQUAL (Length, 1); + UT_ASSERT_EQUAL (*Ptr, 0x2A); + + // + // Wrong tag (0x04 = OCTET STRING): must fail and restore Ptr to original. + // + Ptr = DerInteger; + Status = Asn1GetTag (&Ptr, DerInteger + sizeof (DerInteger), &Length, 0x04); + UT_ASSERT_FALSE (Status); + UT_ASSERT_TRUE (Ptr == DerInteger); + + return UNIT_TEST_PASSED; +} + +// MU_CHANGE [END] + TEST_DESC mX509Test[] = { // // -----Description--------------------------------------Class----------------------Function---------------------------------Pre---------------------Post---------Context @@ -845,6 +887,7 @@ TEST_DESC mX509Test[] = { { "TestVerifyX509Fields()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509Fields, NULL, NULL, NULL }, // MU_CHANGE { "TestVerifyX509ConstructFree()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509ConstructFree, NULL, NULL, NULL }, // MU_CHANGE { "TestVerifyX509KeyUsage()", "CryptoPkg.BaseCryptLib.X509", TestVerifyX509KeyUsage, NULL, NULL, NULL }, // MU_CHANGE + { "TestVerifyMiscCryptoAPIs()", "CryptoPkg.BaseCryptLib.Misc", TestVerifyMiscCryptoAPIs, NULL, NULL, NULL }, // MU_CHANGE }; UINTN mX509TestNum = ARRAY_SIZE (mX509Test);