/** @file Unit tests of Base64 conversion APIs in BaseLib. Copyright (C) Microsoft Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #define UNIT_TEST_APP_NAME "BaseLib Unit Test Application" #define UNIT_TEST_APP_VERSION "1.0" /** RFC 4648 https://tools.ietf.org/html/rfc4648 test vectors BASE64("") = "" BASE64("f") = "Zg==" BASE64("fo") = "Zm8=" BASE64("foo") = "Zm9v" BASE64("foob") = "Zm9vYg==" BASE64("fooba") = "Zm9vYmE=" BASE64("foobar") = "Zm9vYmFy" The test vectors are using ascii strings for the binary data */ typedef struct { CHAR8 *TestInput; CHAR8 *TestOutput; EFI_STATUS ExpectedStatus; VOID *BufferToFree; UINTN ExpectedSize; } BASIC_TEST_CONTEXT; #define B64_TEST_1 "" #define BIN_TEST_1 "" #define B64_TEST_2 "Zg==" #define BIN_TEST_2 "f" #define B64_TEST_3 "Zm8=" #define BIN_TEST_3 "fo" #define B64_TEST_4 "Zm9v" #define BIN_TEST_4 "foo" #define B64_TEST_5 "Zm9vYg==" #define BIN_TEST_5 "foob" #define B64_TEST_6 "Zm9vYmE=" #define BIN_TEST_6 "fooba" #define B64_TEST_7 "Zm9vYmFy" #define BIN_TEST_7 "foobar" // Adds all white space - also ends the last quantum with only spaces afterwards #define B64_TEST_8_IN " \t\v Zm9\r\nvYmFy \f " #define BIN_TEST_8 "foobar" // Not a quantum multiple of 4 #define B64_ERROR_1 "Zm9vymFy=" // Invalid characters in the string #define B64_ERROR_2 "Zm$vymFy" // Too many '=' characters #define B64_ERROR_3 "Z===" // Poorly placed '=' #define B64_ERROR_4 "Zm=vYmFy" #define MAX_TEST_STRING_SIZE (200) // ------------------------------------------------ Input----------Output-----------Result-------Free--Expected Output Size static BASIC_TEST_CONTEXT mBasicEncodeTest1 = { BIN_TEST_1, B64_TEST_1, EFI_SUCCESS, NULL, sizeof (B64_TEST_1) }; static BASIC_TEST_CONTEXT mBasicEncodeTest2 = { BIN_TEST_2, B64_TEST_2, EFI_SUCCESS, NULL, sizeof (B64_TEST_2) }; static BASIC_TEST_CONTEXT mBasicEncodeTest3 = { BIN_TEST_3, B64_TEST_3, EFI_SUCCESS, NULL, sizeof (B64_TEST_3) }; static BASIC_TEST_CONTEXT mBasicEncodeTest4 = { BIN_TEST_4, B64_TEST_4, EFI_SUCCESS, NULL, sizeof (B64_TEST_4) }; static BASIC_TEST_CONTEXT mBasicEncodeTest5 = { BIN_TEST_5, B64_TEST_5, EFI_SUCCESS, NULL, sizeof (B64_TEST_5) }; static BASIC_TEST_CONTEXT mBasicEncodeTest6 = { BIN_TEST_6, B64_TEST_6, EFI_SUCCESS, NULL, sizeof (B64_TEST_6) }; static BASIC_TEST_CONTEXT mBasicEncodeTest7 = { BIN_TEST_7, B64_TEST_7, EFI_SUCCESS, NULL, sizeof (B64_TEST_7) }; static BASIC_TEST_CONTEXT mBasicEncodeError1 = { BIN_TEST_7, B64_TEST_1, EFI_BUFFER_TOO_SMALL, NULL, sizeof (B64_TEST_7) }; static BASIC_TEST_CONTEXT mBasicDecodeTest1 = { B64_TEST_1, BIN_TEST_1, EFI_SUCCESS, NULL, sizeof (BIN_TEST_1)-1 }; static BASIC_TEST_CONTEXT mBasicDecodeTest2 = { B64_TEST_2, BIN_TEST_2, EFI_SUCCESS, NULL, sizeof (BIN_TEST_2)-1 }; static BASIC_TEST_CONTEXT mBasicDecodeTest3 = { B64_TEST_3, BIN_TEST_3, EFI_SUCCESS, NULL, sizeof (BIN_TEST_3)-1 }; static BASIC_TEST_CONTEXT mBasicDecodeTest4 = { B64_TEST_4, BIN_TEST_4, EFI_SUCCESS, NULL, sizeof (BIN_TEST_4)-1 }; static BASIC_TEST_CONTEXT mBasicDecodeTest5 = { B64_TEST_5, BIN_TEST_5, EFI_SUCCESS, NULL, sizeof (BIN_TEST_5)-1 }; static BASIC_TEST_CONTEXT mBasicDecodeTest6 = { B64_TEST_6, BIN_TEST_6, EFI_SUCCESS, NULL, sizeof (BIN_TEST_6)-1 }; static BASIC_TEST_CONTEXT mBasicDecodeTest7 = { B64_TEST_7, BIN_TEST_7, EFI_SUCCESS, NULL, sizeof (BIN_TEST_7)-1 }; static BASIC_TEST_CONTEXT mBasicDecodeTest8 = { B64_TEST_8_IN, BIN_TEST_8, EFI_SUCCESS, NULL, sizeof (BIN_TEST_8)-1 }; static BASIC_TEST_CONTEXT mBasicDecodeError1 = { B64_ERROR_1, B64_ERROR_1, EFI_INVALID_PARAMETER, NULL, 0 }; static BASIC_TEST_CONTEXT mBasicDecodeError2 = { B64_ERROR_2, B64_ERROR_2, EFI_INVALID_PARAMETER, NULL, 0 }; static BASIC_TEST_CONTEXT mBasicDecodeError3 = { B64_ERROR_3, B64_ERROR_3, EFI_INVALID_PARAMETER, NULL, 0 }; static BASIC_TEST_CONTEXT mBasicDecodeError4 = { B64_ERROR_4, B64_ERROR_4, EFI_INVALID_PARAMETER, NULL, 0 }; static BASIC_TEST_CONTEXT mBasicDecodeError5 = { B64_TEST_7, BIN_TEST_1, EFI_BUFFER_TOO_SMALL, NULL, sizeof (BIN_TEST_7)-1 }; /** Simple clean up method to make sure tests clean up even if interrupted and fail in the middle. **/ STATIC VOID EFIAPI CleanUpB64TestContext ( IN UNIT_TEST_CONTEXT Context ) { BASIC_TEST_CONTEXT *Btc; Btc = (BASIC_TEST_CONTEXT *)Context; if (Btc != NULL) { // free string if set if (Btc->BufferToFree != NULL) { FreePool (Btc->BufferToFree); Btc->BufferToFree = NULL; } } } /** Unit test for Base64 encode APIs of BaseLib. @param[in] Context [Optional] An optional parameter that enables: 1) test-case reuse with varied parameters and 2) test-case re-entry for Target tests that need a reboot. This parameter is a VOID* and it is the responsibility of the test author to ensure that the contents are well understood by all test cases that may consume it. @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ STATIC UNIT_TEST_STATUS EFIAPI RfcEncodeTest ( IN UNIT_TEST_CONTEXT Context ) { BASIC_TEST_CONTEXT *Btc; CHAR8 *b64String; CHAR8 *binString; UINTN b64StringSize; EFI_STATUS Status; UINT8 *BinData; UINTN BinSize; CHAR8 *b64WorkString; UINTN ReturnSize; INTN CompareStatus; UINTN indx; Btc = (BASIC_TEST_CONTEXT *)Context; binString = Btc->TestInput; b64String = Btc->TestOutput; // // Only testing the the translate functionality, so preallocate the proper // string buffer. // b64StringSize = AsciiStrnSizeS (b64String, MAX_TEST_STRING_SIZE); BinSize = AsciiStrnLenS (binString, MAX_TEST_STRING_SIZE); BinData = (UINT8 *)binString; b64WorkString = (CHAR8 *)AllocatePool (b64StringSize); UT_ASSERT_NOT_NULL (b64WorkString); Btc->BufferToFree = b64WorkString; ReturnSize = b64StringSize; Status = Base64Encode (BinData, BinSize, b64WorkString, &ReturnSize); UT_ASSERT_STATUS_EQUAL (Status, Btc->ExpectedStatus); UT_ASSERT_EQUAL (ReturnSize, Btc->ExpectedSize); if (!EFI_ERROR (Btc->ExpectedStatus)) { if (ReturnSize != 0) { CompareStatus = AsciiStrnCmp (b64String, b64WorkString, ReturnSize); if (CompareStatus != 0) { UT_LOG_ERROR ("b64 string compare error - size=%d\n", ReturnSize); for (indx = 0; indx < ReturnSize; indx++) { UT_LOG_ERROR (" %2.2x", 0xff & b64String[indx]); } UT_LOG_ERROR ("\n b64 work string:\n"); for (indx = 0; indx < ReturnSize; indx++) { UT_LOG_ERROR (" %2.2x", 0xff & b64WorkString[indx]); } UT_LOG_ERROR ("\n"); } UT_ASSERT_EQUAL (CompareStatus, 0); } } Btc->BufferToFree = NULL; FreePool (b64WorkString); return UNIT_TEST_PASSED; } /** Unit test for Base64 decode APIs of BaseLib. @param[in] Context [Optional] An optional parameter that enables: 1) test-case reuse with varied parameters and 2) test-case re-entry for Target tests that need a reboot. This parameter is a VOID* and it is the responsibility of the test author to ensure that the contents are well understood by all test cases that may consume it. @retval UNIT_TEST_PASSED The Unit test has completed and the test case was successful. @retval UNIT_TEST_ERROR_TEST_FAILED A test case assertion has failed. **/ STATIC UNIT_TEST_STATUS EFIAPI RfcDecodeTest ( IN UNIT_TEST_CONTEXT Context ) { BASIC_TEST_CONTEXT *Btc; CHAR8 *b64String; CHAR8 *binString; EFI_STATUS Status; UINTN b64StringLen; UINTN ReturnSize; UINT8 *BinData; UINTN BinSize; INTN CompareStatus; UINTN indx; Btc = (BASIC_TEST_CONTEXT *)Context; b64String = Btc->TestInput; binString = Btc->TestOutput; // // Only testing the the translate functionality // b64StringLen = AsciiStrnLenS (b64String, MAX_TEST_STRING_SIZE); BinSize = AsciiStrnLenS (binString, MAX_TEST_STRING_SIZE); BinData = AllocatePool (BinSize); UT_ASSERT_NOT_NULL (BinData); Btc->BufferToFree = BinData; ReturnSize = BinSize; Status = Base64Decode (b64String, b64StringLen, BinData, &ReturnSize); UT_ASSERT_STATUS_EQUAL (Status, Btc->ExpectedStatus); // If an error is not expected, check the results if (EFI_ERROR (Btc->ExpectedStatus)) { if (Btc->ExpectedStatus == EFI_BUFFER_TOO_SMALL) { UT_ASSERT_EQUAL (ReturnSize, Btc->ExpectedSize); } } else { UT_ASSERT_EQUAL (ReturnSize, Btc->ExpectedSize); if (ReturnSize != 0) { CompareStatus = CompareMem (binString, BinData, ReturnSize); if (CompareStatus != 0) { UT_LOG_ERROR ("bin string compare error - size=%d\n", ReturnSize); for (indx = 0; indx < ReturnSize; indx++) { UT_LOG_ERROR (" %2.2x", 0xff & binString[indx]); } UT_LOG_ERROR ("\nBinData:\n"); for (indx = 0; indx < ReturnSize; indx++) { UT_LOG_ERROR (" %2.2x", 0xff & BinData[indx]); } UT_LOG_ERROR ("\n"); } UT_ASSERT_EQUAL (CompareStatus, 0); } } Btc->BufferToFree = NULL; FreePool (BinData); return UNIT_TEST_PASSED; } #define SOURCE_STRING L"Hello" STATIC UNIT_TEST_STATUS EFIAPI SafeStringContraintCheckTest ( IN UNIT_TEST_CONTEXT Context ) { RETURN_STATUS Status; CHAR16 Destination[20]; CHAR16 AllZero[20]; // // Zero buffer used to verify Destination is not modified // ZeroMem (AllZero, sizeof (AllZero)); // // Positive test case copy source unicode string to destination // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (Destination, sizeof (Destination) / sizeof (CHAR16), SOURCE_STRING); UT_ASSERT_NOT_EFI_ERROR (Status); UT_ASSERT_MEM_EQUAL (Destination, SOURCE_STRING, sizeof (SOURCE_STRING)); // // Positive test case with DestMax the same as Source size // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (Destination, sizeof (SOURCE_STRING) / sizeof (CHAR16), SOURCE_STRING); UT_ASSERT_NOT_EFI_ERROR (Status); UT_ASSERT_MEM_EQUAL (Destination, SOURCE_STRING, sizeof (SOURCE_STRING)); // // Negative test case with Destination NULL // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (NULL, sizeof (Destination) / sizeof (CHAR16), SOURCE_STRING); UT_ASSERT_STATUS_EQUAL (Status, RETURN_INVALID_PARAMETER); UT_ASSERT_MEM_EQUAL (Destination, AllZero, sizeof (AllZero)); // // Negative test case with Source NULL // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (Destination, sizeof (Destination) / sizeof (CHAR16), NULL); UT_ASSERT_STATUS_EQUAL (Status, RETURN_INVALID_PARAMETER); UT_ASSERT_MEM_EQUAL (Destination, AllZero, sizeof (AllZero)); // // Negative test case with DestMax too big // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (Destination, MAX_UINTN, SOURCE_STRING); UT_ASSERT_STATUS_EQUAL (Status, RETURN_INVALID_PARAMETER); UT_ASSERT_MEM_EQUAL (Destination, AllZero, sizeof (AllZero)); // // Negative test case with DestMax 0 // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (Destination, 0, SOURCE_STRING); UT_ASSERT_STATUS_EQUAL (Status, RETURN_INVALID_PARAMETER); UT_ASSERT_MEM_EQUAL (Destination, AllZero, sizeof (AllZero)); // // Negative test case with DestMax smaller than Source size // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (Destination, 1, SOURCE_STRING); UT_ASSERT_STATUS_EQUAL (Status, RETURN_BUFFER_TOO_SMALL); UT_ASSERT_MEM_EQUAL (Destination, AllZero, sizeof (AllZero)); // // Negative test case with DestMax smaller than Source size by one character // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (Destination, sizeof (SOURCE_STRING) / sizeof (CHAR16) - 1, SOURCE_STRING); UT_ASSERT_STATUS_EQUAL (Status, RETURN_BUFFER_TOO_SMALL); UT_ASSERT_MEM_EQUAL (Destination, AllZero, sizeof (AllZero)); // // Negative test case with overlapping Destination and Source // ZeroMem (Destination, sizeof (Destination)); Status = StrCpyS (Destination, sizeof (Destination) / sizeof (CHAR16), Destination); UT_ASSERT_STATUS_EQUAL (Status, RETURN_ACCESS_DENIED); UT_ASSERT_MEM_EQUAL (Destination, AllZero, sizeof (AllZero)); return UNIT_TEST_PASSED; } /** Initialze the unit test framework, suite, and unit tests for the Base64 conversion APIs of BaseLib and run the unit tests. @retval EFI_SUCCESS All test cases were dispatched. @retval EFI_OUT_OF_RESOURCES There are not enough resources available to initialize the unit tests. **/ STATIC EFI_STATUS EFIAPI UnitTestingEntry ( VOID ) { EFI_STATUS Status; UNIT_TEST_FRAMEWORK_HANDLE Fw; UNIT_TEST_SUITE_HANDLE b64EncodeTests; UNIT_TEST_SUITE_HANDLE b64DecodeTests; UNIT_TEST_SUITE_HANDLE SafeStringTests; Fw = NULL; DEBUG ((DEBUG_INFO, "%a v%a\n", UNIT_TEST_APP_NAME, UNIT_TEST_APP_VERSION)); // // Start setting up the test framework for running the tests. // Status = InitUnitTestFramework (&Fw, UNIT_TEST_APP_NAME, gEfiCallerBaseName, UNIT_TEST_APP_VERSION); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed in InitUnitTestFramework. Status = %r\n", Status)); goto EXIT; } // // Populate the B64 Encode Unit Test Suite. // Status = CreateUnitTestSuite (&b64EncodeTests, Fw, "b64 Encode binary to Ascii string", "BaseLib.b64Encode", NULL, NULL); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for b64EncodeTests\n")); Status = EFI_OUT_OF_RESOURCES; goto EXIT; } // --------------Suite-----------Description--------------Class Name----------Function--------Pre---Post-------------------Context----------- AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - Empty", "Test1", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest1); AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - f", "Test2", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest2); AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - fo", "Test3", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest3); AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - foo", "Test4", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest4); AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - foob", "Test5", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest5); AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - fooba", "Test6", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest6); AddTestCase (b64EncodeTests, "RFC 4686 Test Vector - foobar", "Test7", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeTest7); AddTestCase (b64EncodeTests, "Too small of output buffer", "Error1", RfcEncodeTest, NULL, CleanUpB64TestContext, &mBasicEncodeError1); // // Populate the B64 Decode Unit Test Suite. // Status = CreateUnitTestSuite (&b64DecodeTests, Fw, "b64 Decode Ascii string to binary", "BaseLib.b64Decode", NULL, NULL); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for b64Decode Tests\n")); Status = EFI_OUT_OF_RESOURCES; goto EXIT; } AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - Empty", "Test1", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest1); AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - f", "Test2", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest2); AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - fo", "Test3", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest3); AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - foo", "Test4", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest4); AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - foob", "Test5", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest5); AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - fooba", "Test6", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest6); AddTestCase (b64DecodeTests, "RFC 4686 Test Vector - foobar", "Test7", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest7); AddTestCase (b64DecodeTests, "Ignore Whitespace test", "Test8", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeTest8); AddTestCase (b64DecodeTests, "Not a quantum multiple of 4", "Error1", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError1); AddTestCase (b64DecodeTests, "Invalid characters in the string", "Error2", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError2); AddTestCase (b64DecodeTests, "Too many padding characters", "Error3", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError3); AddTestCase (b64DecodeTests, "Incorrectly placed padding character", "Error4", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError4); AddTestCase (b64DecodeTests, "Too small of output buffer", "Error5", RfcDecodeTest, NULL, CleanUpB64TestContext, &mBasicDecodeError5); // // Populate the safe string Unit Test Suite. // Status = CreateUnitTestSuite (&SafeStringTests, Fw, "Safe String", "BaseLib.SafeString", NULL, NULL); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Failed in CreateUnitTestSuite for SafeStringTests\n")); Status = EFI_OUT_OF_RESOURCES; goto EXIT; } // --------------Suite-----------Description--------------Class Name----------Function--------Pre---Post-------------------Context----------- AddTestCase (SafeStringTests, "SAFE_STRING_CONSTRAINT_CHECK", "SafeStringContraintCheckTest", SafeStringContraintCheckTest, NULL, NULL, NULL); // // Execute the tests. // Status = RunAllTestSuites (Fw); EXIT: if (Fw) { FreeUnitTestFramework (Fw); } return Status; } /** Standard UEFI entry point for target based unit test execution from UEFI Shell. **/ EFI_STATUS EFIAPI BaseLibUnitTestAppEntry ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { return UnitTestingEntry (); } /** Standard POSIX C entry point for host based unit test execution. **/ int main ( int argc, char *argv[] ) { return UnitTestingEntry (); }