/** @file Application for ML-DSA Primitives Validation. This file contains unit tests for the ML-DSA (Module-Lattice-Based Digital Signature Algorithm) cryptographic functions defined in CryptMlDsa.c. ML-DSA is a post-quantum digital signature scheme based on the CRYSTALS-Dilithium algorithm and standardized in FIPS 204. The test vectors are provided in MlDsaTestVectors.h which contains: - mMlDsa87TestCert[] - X.509 DER certificate with ML-DSA-87 public key - mMlDsa87TestPemKey[] - PEM-encoded ML-DSA-87 private key The test structure mirrors EdDsaTests.c and validates: - Context creation and destruction (MlDsaNewByNid, MlDsaFree) - Key setting and retrieval error cases (MlDsaSetPrivKey, MlDsaSetPubKey, MlDsaGetPubKey) - Signature generation and verification via PEM/X509 (TestVerifyMlDsaPemX509) - Error handling for invalid inputs NOTE: TestVerifyMlDsaSignVerify() and TestVerifyMlDsaSignVerifyWithContext() are currently skipped as they require raw key arrays. The main signing/verification test is TestVerifyMlDsaPemX509() which uses real PEM and X509 test vectors. Copyright (c) 2026, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "TestBaseCryptLib.h" #define ML_DSA_87_PRIVATE_KEY_SIZE 4896 #define ML_DSA_87_PUBLIC_KEY_SIZE 2592 #define ML_DSA_87_SIGNATURE_SIZE 4627 #define ML_DSA_MAX_CONTEXT_SIZE 255 // // ML-DSA-87 test vectors - include generated certificate and PEM key // #include "MlDsaTestVectors.h" // // Test message for signing and verification // CONST CHAR8 *mMlDsaTestMessage = "Test message for ML-DSA signing and verification"; // // Optional context string for domain separation // CONST CHAR8 *mMlDsaTestContext = "ML-DSA test context"; VOID *MlDsaContext1; VOID *MlDsaContext2; /** Prerequisite function for ML-DSA tests. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaPreReq ( UNIT_TEST_CONTEXT Context ) { MlDsaContext1 = NULL; MlDsaContext2 = NULL; return UNIT_TEST_PASSED; } /** Cleanup function for ML-DSA tests. @param[in] Context Unit test context. **/ VOID EFIAPI TestVerifyMlDsaCleanUp ( UNIT_TEST_CONTEXT Context ) { if (MlDsaContext1 != NULL) { MlDsaFree (MlDsaContext1); MlDsaContext1 = NULL; } if (MlDsaContext2 != NULL) { MlDsaFree (MlDsaContext2); MlDsaContext2 = NULL; } } /** Validate UEFI-OpenSSL ML-DSA Context Creation. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaNew ( UNIT_TEST_CONTEXT Context ) { // // Test MlDsaNewByNid with ML-DSA-87 // MlDsaContext1 = MlDsaNewByNid (CRYPTO_NID_ML_DSA_87); UT_ASSERT_NOT_NULL (MlDsaContext1); // // Test MlDsaNewByNid with invalid NID // MlDsaContext2 = MlDsaNewByNid (CRYPTO_NID_NULL); UT_ASSERT_EQUAL ((UINTN)MlDsaContext2, (UINTN)NULL); // // Test MlDsaFree // MlDsaFree (MlDsaContext1); MlDsaContext1 = NULL; // // Test MlDsaFree with NULL (should not crash) // MlDsaFree (NULL); return UNIT_TEST_PASSED; } /** Validate UEFI-OpenSSL ML-DSA Key Setting and Getting. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaKeySetGet ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; UINT8 PublicKey[ML_DSA_87_PUBLIC_KEY_SIZE]; UINTN PublicKeySize; UINT8 TooSmallBuffer[10]; UINTN TooSmallSize; // // Create ML-DSA context // MlDsaContext1 = MlDsaNewByNid (CRYPTO_NID_ML_DSA_87); UT_ASSERT_NOT_NULL (MlDsaContext1); // // Test MlDsaGetPubKey with too small buffer (before key is set) // TooSmallSize = sizeof (TooSmallBuffer); Status = MlDsaGetPubKey (MlDsaContext1, TooSmallBuffer, &TooSmallSize); UT_ASSERT_FALSE (Status); UT_ASSERT_NOT_EQUAL (TooSmallSize, ML_DSA_87_PUBLIC_KEY_SIZE); // // Test MlDsaSetPrivKey with NULL context // Status = MlDsaSetPrivKey (NULL, (UINT8 *)mMlDsa87TestPemKey, 100); UT_ASSERT_FALSE (Status); // // Test MlDsaSetPrivKey with NULL key // Status = MlDsaSetPrivKey (MlDsaContext1, NULL, ML_DSA_87_PRIVATE_KEY_SIZE); UT_ASSERT_FALSE (Status); // // Test MlDsaSetPrivKey with wrong size // Status = MlDsaSetPrivKey (MlDsaContext1, (UINT8 *)mMlDsa87TestPemKey, 32); UT_ASSERT_FALSE (Status); // // Test MlDsaSetPubKey with NULL context // Status = MlDsaSetPubKey (NULL, (UINT8 *)mMlDsa87TestCert, 100); UT_ASSERT_FALSE (Status); // // Test MlDsaSetPubKey with NULL key // Status = MlDsaSetPubKey (MlDsaContext1, NULL, ML_DSA_87_PUBLIC_KEY_SIZE); UT_ASSERT_FALSE (Status); // // Test MlDsaSetPubKey with wrong size // Status = MlDsaSetPubKey (MlDsaContext1, (UINT8 *)mMlDsa87TestCert, 32); UT_ASSERT_FALSE (Status); // // Test MlDsaGetPubKey with NULL context // PublicKeySize = sizeof (PublicKey); Status = MlDsaGetPubKey (NULL, PublicKey, &PublicKeySize); UT_ASSERT_FALSE (Status); // // Test MlDsaGetPubKey with NULL size // Status = MlDsaGetPubKey (MlDsaContext1, PublicKey, NULL); UT_ASSERT_FALSE (Status); // // Clean up context // MlDsaFree (MlDsaContext1); MlDsaContext1 = NULL; return UNIT_TEST_PASSED; } /** Validate ML-DSA error cases. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaErrorCases ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINTN SigSize; UINT8 TooSmallBuffer[10]; UINTN TooSmallSize; // // Create ML-DSA context // MlDsaContext1 = MlDsaNewByNid (CRYPTO_NID_ML_DSA_87); UT_ASSERT_NOT_NULL (MlDsaContext1); // // Test MlDsaSign with NULL context // SigSize = sizeof (Signature); Status = MlDsaSign ( NULL, NULL, 0, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), Signature, &SigSize ); UT_ASSERT_FALSE (Status); // // Test MlDsaSign with NULL message // SigSize = sizeof (Signature); Status = MlDsaSign ( MlDsaContext1, NULL, 0, NULL, 0, Signature, &SigSize ); UT_ASSERT_FALSE (Status); // // Test MlDsaSign with too small buffer // TooSmallSize = sizeof (TooSmallBuffer); Status = MlDsaSign ( MlDsaContext1, NULL, 0, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), TooSmallBuffer, &TooSmallSize ); UT_ASSERT_FALSE (Status); UT_ASSERT_NOT_EQUAL (TooSmallSize, sizeof (Signature)); // // Test MlDsaVerify with NULL context // Status = MlDsaVerify ( NULL, NULL, 0, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), Signature, sizeof (Signature) ); UT_ASSERT_FALSE (Status); // // Test MlDsaVerify with NULL message // Status = MlDsaVerify ( MlDsaContext1, NULL, 0, NULL, 0, Signature, sizeof (Signature) ); UT_ASSERT_FALSE (Status); // // Test MlDsaVerify with NULL signature // Status = MlDsaVerify ( MlDsaContext1, NULL, 0, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), NULL, sizeof (Signature) ); UT_ASSERT_FALSE (Status); // // Test MlDsaVerify with zero signature size // Status = MlDsaVerify ( MlDsaContext1, NULL, 0, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), Signature, 0 ); UT_ASSERT_FALSE (Status); return UNIT_TEST_PASSED; } /** Validate ML-DSA key retrieval from PEM and X509. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaPemX509 ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *MlDsaPrivKey; VOID *MlDsaPubKey; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINTN SigSize; UINTN MessageSize; MlDsaPrivKey = NULL; MlDsaPubKey = NULL; MessageSize = AsciiStrLen (mMlDsaTestMessage); // // Retrieve ML-DSA private key from PEM data. // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &MlDsaPrivKey ); UT_ASSERT_TRUE (Status); UT_ASSERT_NOT_NULL (MlDsaPrivKey); // // Retrieve ML-DSA public key from X509 certificate. // Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), &MlDsaPubKey ); UT_ASSERT_TRUE (Status); UT_ASSERT_NOT_NULL (MlDsaPubKey); // // ML-DSA signing with key from PEM (no context string) // SigSize = sizeof (Signature); Status = MlDsaSign ( MlDsaPrivKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, &SigSize ); UT_ASSERT_TRUE (Status); UT_ASSERT_EQUAL (SigSize, ML_DSA_87_SIGNATURE_SIZE); // // ML-DSA verification with key from X509 // Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, SigSize ); UT_ASSERT_TRUE (Status); MlDsaFree (MlDsaPrivKey); MlDsaFree (MlDsaPubKey); return UNIT_TEST_PASSED; } /** Validate ML-DSA signing and verification with context string. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaSignVerifyWithContext ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *MlDsaPrivKey; VOID *MlDsaPubKey; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINTN SigSize; UINTN MessageSize; UINTN ContextSize; MlDsaPrivKey = NULL; MlDsaPubKey = NULL; MessageSize = AsciiStrLen (mMlDsaTestMessage); ContextSize = AsciiStrLen (mMlDsaTestContext); // // Retrieve ML-DSA private key from PEM data. // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &MlDsaPrivKey ); UT_ASSERT_TRUE (Status); UT_ASSERT_NOT_NULL (MlDsaPrivKey); // // Retrieve ML-DSA public key from X509 certificate. // Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), &MlDsaPubKey ); UT_ASSERT_TRUE (Status); UT_ASSERT_NOT_NULL (MlDsaPubKey); // // ML-DSA signing with context string // SigSize = sizeof (Signature); Status = MlDsaSign ( MlDsaPrivKey, (UINT8 *)mMlDsaTestContext, ContextSize, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, &SigSize ); UT_ASSERT_TRUE (Status); UT_ASSERT_EQUAL (SigSize, ML_DSA_87_SIGNATURE_SIZE); // // ML-DSA verification with matching context string // Status = MlDsaVerify ( MlDsaPubKey, (UINT8 *)mMlDsaTestContext, ContextSize, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, SigSize ); UT_ASSERT_TRUE (Status); // // ML-DSA verification should fail with mismatched context string // Status = MlDsaVerify ( MlDsaPubKey, (UINT8 *)"Different context", AsciiStrLen ("Different context"), (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, SigSize ); UT_ASSERT_FALSE (Status); // // ML-DSA verification should fail with no context when signature used context // Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, SigSize ); UT_ASSERT_FALSE (Status); MlDsaFree (MlDsaPrivKey); MlDsaFree (MlDsaPubKey); return UNIT_TEST_PASSED; } /** Validate ML-DSA public key extraction and generation. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaGetPubKey ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *MlDsaPrivKey; UINT8 PublicKey[ML_DSA_87_PUBLIC_KEY_SIZE]; UINTN PublicKeySize; MlDsaPrivKey = NULL; // // Retrieve ML-DSA private key from PEM data. // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &MlDsaPrivKey ); UT_ASSERT_TRUE (Status); UT_ASSERT_NOT_NULL (MlDsaPrivKey); // // Get public key from private key context // PublicKeySize = sizeof (PublicKey); Status = MlDsaGetPubKey ( MlDsaPrivKey, PublicKey, &PublicKeySize ); UT_ASSERT_TRUE (Status); UT_ASSERT_EQUAL (PublicKeySize, ML_DSA_87_PUBLIC_KEY_SIZE); // // Verify the public key buffer is not all zeros // BOOLEAN AllZeros; UINTN Index; AllZeros = TRUE; for (Index = 0; Index < PublicKeySize; Index++) { if (PublicKey[Index] != 0) { AllZeros = FALSE; break; } } UT_ASSERT_FALSE (AllZeros); MlDsaFree (MlDsaPrivKey); return UNIT_TEST_PASSED; } /** Validate ML-DSA signature verification fails with tampered data. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaTamperedData ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *MlDsaPrivKey; VOID *MlDsaPubKey; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINT8 TamperedSignature[ML_DSA_87_SIGNATURE_SIZE]; CHAR8 TamperedMessage[100]; UINTN SigSize; UINTN MessageSize; MlDsaPrivKey = NULL; MlDsaPubKey = NULL; MessageSize = AsciiStrLen (mMlDsaTestMessage); // // Retrieve ML-DSA private key from PEM data. // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &MlDsaPrivKey ); UT_ASSERT_TRUE (Status); UT_ASSERT_NOT_NULL (MlDsaPrivKey); // // Retrieve ML-DSA public key from X509 certificate. // Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), &MlDsaPubKey ); UT_ASSERT_TRUE (Status); UT_ASSERT_NOT_NULL (MlDsaPubKey); // // Generate valid signature // SigSize = sizeof (Signature); Status = MlDsaSign ( MlDsaPrivKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, &SigSize ); UT_ASSERT_TRUE (Status); // // Verify original signature works // Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, SigSize ); UT_ASSERT_TRUE (Status); // // Test with tampered message (should fail) // AsciiStrCpyS (TamperedMessage, sizeof (TamperedMessage), mMlDsaTestMessage); TamperedMessage[0] = 'X'; Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)TamperedMessage, MessageSize, Signature, SigSize ); UT_ASSERT_FALSE (Status); // // Test with tampered signature (should fail) // CopyMem (TamperedSignature, Signature, sizeof (Signature)); TamperedSignature[0] ^= 0x01; Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, TamperedSignature, SigSize ); UT_ASSERT_FALSE (Status); // // Test with wrong signature size (should fail) // Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, SigSize - 1 ); UT_ASSERT_FALSE (Status); MlDsaFree (MlDsaPrivKey); MlDsaFree (MlDsaPubKey); return UNIT_TEST_PASSED; } /** Validate ML-DSA PEM loading error cases. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaPemErrors ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *MlDsaKey; MlDsaKey = NULL; // // Test MlDsaGetPrivateKeyFromPem with NULL PEM data // Status = MlDsaGetPrivateKeyFromPem ( NULL, 100, NULL, &MlDsaKey ); UT_ASSERT_FALSE (Status); // // Test MlDsaGetPrivateKeyFromPem with NULL context pointer // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, NULL ); UT_ASSERT_FALSE (Status); // // Test MlDsaGetPrivateKeyFromPem with invalid PEM data // Status = MlDsaGetPrivateKeyFromPem ( (UINT8 *)"Invalid PEM data", 16, NULL, &MlDsaKey ); UT_ASSERT_FALSE (Status); // // Test MlDsaGetPublicKeyFromX509 with NULL certificate // Status = MlDsaGetPublicKeyFromX509 ( NULL, 100, &MlDsaKey ); UT_ASSERT_FALSE (Status); // // Test MlDsaGetPublicKeyFromX509 with NULL context pointer // Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), NULL ); UT_ASSERT_FALSE (Status); // // Test MlDsaGetPublicKeyFromX509 with invalid certificate data // Status = MlDsaGetPublicKeyFromX509 ( (UINT8 *)"Invalid cert data", 17, &MlDsaKey ); UT_ASSERT_FALSE (Status); return UNIT_TEST_PASSED; } /** Validate ML-DSA operations on context without keys. This test validates that operations fail properly when no key is set. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaNoKeyOperations ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINT8 PublicKey[ML_DSA_87_PUBLIC_KEY_SIZE]; UINTN SigSize; UINTN PublicKeySize; // // Create context without setting any key // MlDsaContext1 = MlDsaNewByNid (CRYPTO_NID_ML_DSA_87); UT_ASSERT_NOT_NULL (MlDsaContext1); // // MlDsaSign should fail when EvpPkey is NULL // SigSize = sizeof (Signature); Status = MlDsaSign ( MlDsaContext1, NULL, 0, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), Signature, &SigSize ); UT_ASSERT_FALSE (Status); // // MlDsaVerify should fail when EvpPkey is NULL // Status = MlDsaVerify ( MlDsaContext1, NULL, 0, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), Signature, sizeof (Signature) ); UT_ASSERT_FALSE (Status); // // MlDsaGetPubKey should fail when EvpPkey is NULL // PublicKeySize = sizeof (PublicKey); Status = MlDsaGetPubKey ( MlDsaContext1, PublicKey, &PublicKeySize ); UT_ASSERT_FALSE (Status); MlDsaFree (MlDsaContext1); MlDsaContext1 = NULL; return UNIT_TEST_PASSED; } /** Validate ML-DSA context string parameter validation. This test validates the check: (ContextSize > 0) && (Context == NULL). @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaInvalidContextParams ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *MlDsaPrivKey; VOID *MlDsaPubKey; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINTN SigSize; UINTN MessageSize; MlDsaPrivKey = NULL; MlDsaPubKey = NULL; MessageSize = AsciiStrLen (mMlDsaTestMessage); // // Get valid keys // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &MlDsaPrivKey ); UT_ASSERT_TRUE (Status); Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), &MlDsaPubKey ); UT_ASSERT_TRUE (Status); // // Test MlDsaSign with NULL Context but ContextSize > 0 (invalid combination) // SigSize = sizeof (Signature); Status = MlDsaSign ( MlDsaPrivKey, NULL, 10, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, &SigSize ); UT_ASSERT_FALSE (Status); // // Generate valid signature for verify test // SigSize = sizeof (Signature); Status = MlDsaSign ( MlDsaPrivKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, &SigSize ); UT_ASSERT_TRUE (Status); // // Test MlDsaVerify with NULL Context but ContextSize > 0 (invalid combination) // Status = MlDsaVerify ( MlDsaPubKey, NULL, 5, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, SigSize ); UT_ASSERT_FALSE (Status); MlDsaFree (MlDsaPrivKey); MlDsaFree (MlDsaPubKey); return UNIT_TEST_PASSED; } /** Validate MlDsaGetPubKey with NULL buffer parameter. This test validates that NULL PublicKey buffer returns FALSE and sets size to 0. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaGetPubKeyNullBuffer ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *MlDsaPrivKey; UINTN PublicKeySize; MlDsaPrivKey = NULL; // // Get valid private key // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &MlDsaPrivKey ); UT_ASSERT_TRUE (Status); // // Test MlDsaGetPubKey with NULL buffer // Should return FALSE and set PublicKeySize to 0 // PublicKeySize = 9999; Status = MlDsaGetPubKey ( MlDsaPrivKey, NULL, &PublicKeySize ); UT_ASSERT_FALSE (Status); UT_ASSERT_EQUAL (PublicKeySize, 0); MlDsaFree (MlDsaPrivKey); return UNIT_TEST_PASSED; } /** Validate ML-DSA signature size exact match validation. This test validates that MlDsaVerify properly validates signature size. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaSignatureSizeExact ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *MlDsaPrivKey; VOID *MlDsaPubKey; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINTN SigSize; UINTN MessageSize; MlDsaPrivKey = NULL; MlDsaPubKey = NULL; MessageSize = AsciiStrLen (mMlDsaTestMessage); // // Get valid keys // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &MlDsaPrivKey ); UT_ASSERT_TRUE (Status); Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), &MlDsaPubKey ); UT_ASSERT_TRUE (Status); // // Generate valid signature // SigSize = sizeof (Signature); Status = MlDsaSign ( MlDsaPrivKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, &SigSize ); UT_ASSERT_TRUE (Status); UT_ASSERT_EQUAL (SigSize, ML_DSA_87_SIGNATURE_SIZE); // // Verify with exact size - should succeed // Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, ML_DSA_87_SIGNATURE_SIZE ); UT_ASSERT_TRUE (Status); // // Verify with size + 1 - should fail // Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, ML_DSA_87_SIGNATURE_SIZE + 1 ); UT_ASSERT_FALSE (Status); // // Verify with size - 1 - should fail // Status = MlDsaVerify ( MlDsaPubKey, NULL, 0, (UINT8 *)mMlDsaTestMessage, MessageSize, Signature, ML_DSA_87_SIGNATURE_SIZE - 1 ); UT_ASSERT_FALSE (Status); MlDsaFree (MlDsaPrivKey); MlDsaFree (MlDsaPubKey); return UNIT_TEST_PASSED; } /** Test ML-DSA context lifecycle with multiple allocations. Tests proper resource management including multiple successive allocations, cleanup verification, and null pointer safety. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaContextLifecycle ( UNIT_TEST_CONTEXT Context ) { VOID *TempCtx1; VOID *TempCtx2; VOID *TempCtx3; // // Test creating multiple contexts // TempCtx1 = MlDsaNewByNid (CRYPTO_NID_ML_DSA_87); UT_ASSERT_NOT_NULL (TempCtx1); TempCtx2 = MlDsaNewByNid (CRYPTO_NID_ML_DSA_87); UT_ASSERT_NOT_NULL (TempCtx2); TempCtx3 = MlDsaNewByNid (CRYPTO_NID_ML_DSA_87); UT_ASSERT_NOT_NULL (TempCtx3); // // Free in different order // MlDsaFree (TempCtx2); MlDsaFree (TempCtx1); MlDsaFree (TempCtx3); // // Double-free safety (should not crash) // MlDsaFree (NULL); MlDsaFree (NULL); return UNIT_TEST_PASSED; } /** Test ML-DSA empty message signing and verification. Tests edge case of zero-length messages. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaEmptyMessage ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *PrivKey; VOID *PubKey; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINTN SigSize; UINT8 EmptyMsg[1]; PrivKey = NULL; PubKey = NULL; // // Load keys // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &PrivKey ); UT_ASSERT_TRUE (Status); Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), &PubKey ); UT_ASSERT_TRUE (Status); // // Sign empty message - should fail with NULL message // SigSize = sizeof (Signature); Status = MlDsaSign ( PrivKey, NULL, 0, NULL, 0, Signature, &SigSize ); UT_ASSERT_FALSE (Status); // // Sign with valid pointer but zero size - should succeed // SigSize = sizeof (Signature); Status = MlDsaSign ( PrivKey, NULL, 0, EmptyMsg, 0, Signature, &SigSize ); UT_ASSERT_TRUE (Status); // // Verify the zero-length message signature // Status = MlDsaVerify ( PubKey, NULL, 0, EmptyMsg, 0, Signature, SigSize ); UT_ASSERT_TRUE (Status); MlDsaFree (PrivKey); MlDsaFree (PubKey); return UNIT_TEST_PASSED; } /** Test ML-DSA maximum length context string. Tests the maximum allowed context string size per FIPS 204. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaMaxContextString ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *PrivKey; VOID *PubKey; UINT8 Signature[ML_DSA_87_SIGNATURE_SIZE]; UINTN SigSize; UINT8 MaxContext[ML_DSA_MAX_CONTEXT_SIZE]; UINTN Index; PrivKey = NULL; PubKey = NULL; // // Fill context with pattern // for (Index = 0; Index < ML_DSA_MAX_CONTEXT_SIZE; Index++) { MaxContext[Index] = (UINT8)(Index & 0xFF); } // // Load keys // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &PrivKey ); UT_ASSERT_TRUE (Status); Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), &PubKey ); UT_ASSERT_TRUE (Status); // // Sign with maximum context // SigSize = sizeof (Signature); Status = MlDsaSign ( PrivKey, MaxContext, ML_DSA_MAX_CONTEXT_SIZE, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), Signature, &SigSize ); UT_ASSERT_TRUE (Status); // // Verify with matching context // Status = MlDsaVerify ( PubKey, MaxContext, ML_DSA_MAX_CONTEXT_SIZE, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), Signature, SigSize ); UT_ASSERT_TRUE (Status); // // Verify should fail with one byte different // MaxContext[0] ^= 1; Status = MlDsaVerify ( PubKey, MaxContext, ML_DSA_MAX_CONTEXT_SIZE, (UINT8 *)mMlDsaTestMessage, AsciiStrLen (mMlDsaTestMessage), Signature, SigSize ); UT_ASSERT_FALSE (Status); MlDsaFree (PrivKey); MlDsaFree (PubKey); return UNIT_TEST_PASSED; } /** Test ML-DSA key replacement in context. Tests that replacing keys properly frees old key and sets new key. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaKeyReplacement ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *PrivKey; UINT8 PublicKey1[ML_DSA_87_PUBLIC_KEY_SIZE]; UINT8 PublicKey2[ML_DSA_87_PUBLIC_KEY_SIZE]; UINTN PubKeySize; PrivKey = NULL; // // Load first key // Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &PrivKey ); UT_ASSERT_TRUE (Status); // // Get public key // PubKeySize = sizeof (PublicKey1); Status = MlDsaGetPubKey (PrivKey, PublicKey1, &PubKeySize); UT_ASSERT_TRUE (Status); // // Now replace with a public-only key // MlDsaContext1 = MlDsaNewByNid (CRYPTO_NID_ML_DSA_87); UT_ASSERT_NOT_NULL (MlDsaContext1); Status = MlDsaSetPubKey (MlDsaContext1, PublicKey1, ML_DSA_87_PUBLIC_KEY_SIZE); UT_ASSERT_TRUE (Status); // // Replace with same public key again (tests EVP_PKEY_free path) // Status = MlDsaSetPubKey (MlDsaContext1, PublicKey1, ML_DSA_87_PUBLIC_KEY_SIZE); UT_ASSERT_TRUE (Status); // // Verify we can still get the key // PubKeySize = sizeof (PublicKey2); Status = MlDsaGetPubKey (MlDsaContext1, PublicKey2, &PubKeySize); UT_ASSERT_TRUE (Status); UT_ASSERT_EQUAL (PubKeySize, ML_DSA_87_PUBLIC_KEY_SIZE); MlDsaFree (PrivKey); MlDsaFree (MlDsaContext1); MlDsaContext1 = NULL; return UNIT_TEST_PASSED; } /** Test ML-DSA multiple signatures with same key. Tests that the same key can generate multiple signatures. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaMultipleSignatures ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *PrivKey; VOID *PubKey; UINT8 Sig1[ML_DSA_87_SIGNATURE_SIZE]; UINT8 Sig2[ML_DSA_87_SIGNATURE_SIZE]; UINT8 Sig3[ML_DSA_87_SIGNATURE_SIZE]; UINTN SigSize; CHAR8 *Msg1 = "First message"; CHAR8 *Msg2 = "Second message"; CHAR8 *Msg3 = "Third message"; PrivKey = NULL; PubKey = NULL; Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &PrivKey ); UT_ASSERT_TRUE (Status); Status = MlDsaGetPublicKeyFromX509 ( mMlDsa87TestCert, sizeof (mMlDsa87TestCert), &PubKey ); UT_ASSERT_TRUE (Status); // // Generate three different signatures // SigSize = sizeof (Sig1); Status = MlDsaSign (PrivKey, NULL, 0, (UINT8 *)Msg1, AsciiStrLen (Msg1), Sig1, &SigSize); UT_ASSERT_TRUE (Status); SigSize = sizeof (Sig2); Status = MlDsaSign (PrivKey, NULL, 0, (UINT8 *)Msg2, AsciiStrLen (Msg2), Sig2, &SigSize); UT_ASSERT_TRUE (Status); SigSize = sizeof (Sig3); Status = MlDsaSign (PrivKey, NULL, 0, (UINT8 *)Msg3, AsciiStrLen (Msg3), Sig3, &SigSize); UT_ASSERT_TRUE (Status); // // Verify all three with correct messages // Status = MlDsaVerify (PubKey, NULL, 0, (UINT8 *)Msg1, AsciiStrLen (Msg1), Sig1, sizeof (Sig1)); UT_ASSERT_TRUE (Status); Status = MlDsaVerify (PubKey, NULL, 0, (UINT8 *)Msg2, AsciiStrLen (Msg2), Sig2, sizeof (Sig2)); UT_ASSERT_TRUE (Status); Status = MlDsaVerify (PubKey, NULL, 0, (UINT8 *)Msg3, AsciiStrLen (Msg3), Sig3, sizeof (Sig3)); UT_ASSERT_TRUE (Status); // // Cross-verify should fail (wrong message/signature pairs) // Status = MlDsaVerify (PubKey, NULL, 0, (UINT8 *)Msg1, AsciiStrLen (Msg1), Sig2, sizeof (Sig2)); UT_ASSERT_FALSE (Status); Status = MlDsaVerify (PubKey, NULL, 0, (UINT8 *)Msg2, AsciiStrLen (Msg2), Sig3, sizeof (Sig3)); UT_ASSERT_FALSE (Status); MlDsaFree (PrivKey); MlDsaFree (PubKey); return UNIT_TEST_PASSED; } /** Test ML-DSA GetPubKey called multiple times. Tests that repeated calls to GetPubKey return consistent results. @param[in] Context Unit test context. @retval UNIT_TEST_PASSED The unit test has completed successfully. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ UNIT_TEST_STATUS EFIAPI TestVerifyMlDsaGetPubKeyRepeated ( UNIT_TEST_CONTEXT Context ) { BOOLEAN Status; VOID *PrivKey; UINT8 PubKey1[ML_DSA_87_PUBLIC_KEY_SIZE]; UINT8 PubKey2[ML_DSA_87_PUBLIC_KEY_SIZE]; UINT8 PubKey3[ML_DSA_87_PUBLIC_KEY_SIZE]; UINTN Size1, Size2, Size3; PrivKey = NULL; Status = MlDsaGetPrivateKeyFromPem ( mMlDsa87TestPemKey, sizeof (mMlDsa87TestPemKey), NULL, &PrivKey ); UT_ASSERT_TRUE (Status); // // Get public key three times // Size1 = sizeof (PubKey1); Status = MlDsaGetPubKey (PrivKey, PubKey1, &Size1); UT_ASSERT_TRUE (Status); Size2 = sizeof (PubKey2); Status = MlDsaGetPubKey (PrivKey, PubKey2, &Size2); UT_ASSERT_TRUE (Status); Size3 = sizeof (PubKey3); Status = MlDsaGetPubKey (PrivKey, PubKey3, &Size3); UT_ASSERT_TRUE (Status); // // All should be identical // UT_ASSERT_EQUAL (Size1, Size2); UT_ASSERT_EQUAL (Size2, Size3); UT_ASSERT_MEM_EQUAL (PubKey1, PubKey2, Size1); UT_ASSERT_MEM_EQUAL (PubKey2, PubKey3, Size2); MlDsaFree (PrivKey); return UNIT_TEST_PASSED; } TEST_DESC mMlDsaTest[] = { // // -----Description------------------------------------Class-------------------------Function----------------------------Pre-------------------Post--------------------Context // { "TestVerifyMlDsaNew()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaNew, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaKeySetGet()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaKeySetGet, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaErrorCases()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaErrorCases, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaPemX509()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaPemX509, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaSignVerifyWithContext()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaSignVerifyWithContext, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaGetPubKey()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaGetPubKey, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaTamperedData()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaTamperedData, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaPemErrors()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaPemErrors, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaNoKeyOperations()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaNoKeyOperations, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaInvalidContextParams()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaInvalidContextParams, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaGetPubKeyNullBuffer()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaGetPubKeyNullBuffer, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaSignatureSizeExact()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaSignatureSizeExact, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaContextLifecycle()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaContextLifecycle, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaEmptyMessage()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaEmptyMessage, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaMaxContextString()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaMaxContextString, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaKeyReplacement()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaKeyReplacement, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaMultipleSignatures()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaMultipleSignatures, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, { "TestVerifyMlDsaGetPubKeyRepeated()", "CryptoPkg.BaseCryptLib.MlDsa", TestVerifyMlDsaGetPubKeyRepeated, TestVerifyMlDsaPreReq, TestVerifyMlDsaCleanUp, NULL }, }; UINTN mMlDsaTestNum = ARRAY_SIZE (mMlDsaTest);