mirror of
https://github.com/tianocore/edk2
synced 2026-08-27 00:23:19 -04:00
Previous changes introduced tests for unsigned images verified by hash entries in DB/DBX. This commit adds tests covering verification of signed images, including certificate chain validation as well as cases where image hashes are checked against entries in DB/DBX. Signed-off-by: Alexander Gryanko <xpahos@gmail.com>
1617 lines
50 KiB
C++
1617 lines
50 KiB
C++
/** @file
|
||
Unit tests for the implementation of DxeImageVerificationLib.
|
||
|
||
Copyright (c) 2025, Yandex. All rights reserved.
|
||
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||
**/
|
||
|
||
#include <GoogleTest/Library/MockDevicePathLib.h>
|
||
#include <GoogleTest/Library/MockUefiBootServicesTableLib.h>
|
||
#include <GoogleTest/Library/MockUefiLib.h>
|
||
#include <GoogleTest/Library/MockUefiRuntimeServicesTableLib.h>
|
||
#include <Library/GoogleTestLib.h>
|
||
|
||
extern "C" {
|
||
#include "Library/MemoryAllocationLib.h"
|
||
#include <Library/BaseLib.h>
|
||
#include <Library/DebugLib.h>
|
||
#include <Uefi.h>
|
||
|
||
#include "DxeImageVerificationLibGoogleTest.h"
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
void
|
||
PrepareCertList (
|
||
UINTN Size,
|
||
UINTN BufferSize,
|
||
CONST EFI_GUID *SignatureType,
|
||
CONST UINT8 *Src,
|
||
EFI_SIGNATURE_LIST *Dst
|
||
)
|
||
{
|
||
Dst->SignatureListSize = (UINT32)BufferSize;
|
||
Dst->SignatureSize = (UINT32)(sizeof (EFI_SIGNATURE_DATA) - 1 + Size);
|
||
Dst->SignatureHeaderSize = 0;
|
||
CopyGuid (&Dst->SignatureType, SignatureType);
|
||
|
||
EFI_SIGNATURE_DATA *Data = (EFI_SIGNATURE_DATA *)(Dst + 1);
|
||
|
||
CopyGuid (&Data->SignatureOwner, &gEfiGlobalVariableGuid);
|
||
CopyMem (&Data->SignatureData[0], Src, Size);
|
||
}
|
||
|
||
void
|
||
ExpectBeforeHashCheck (
|
||
MockUefiBootServicesTableLib &BsMock,
|
||
MockDevicePathLib &DevicePathMock,
|
||
MockUefiRuntimeServicesTableLib &RtServicesMock
|
||
)
|
||
{
|
||
// Skip all checks that are called before hash verification. This also applies
|
||
// to signed binary files, as no additional mocks are needed before the
|
||
// signature verification starts.
|
||
|
||
// Do not allocate on stack
|
||
static UINT8 SetupMode = SECURE_BOOT_MODE_ENABLE;
|
||
|
||
EXPECT_CALL (BsMock, gBS_LocateDevicePath)
|
||
.Times (3)
|
||
.WillRepeatedly (testing::Return (EFI_NOT_FOUND));
|
||
EXPECT_CALL (DevicePathMock, IsDevicePathEndType)
|
||
.WillOnce (testing::Return ((BOOLEAN)TRUE));
|
||
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_SECURE_BOOT_MODE_NAME),
|
||
BufferEq (&gEfiGlobalVariableGuid, sizeof (EFI_GUID)),
|
||
testing::NotNull (),
|
||
testing::Pointee (testing::Eq (sizeof (SetupMode))),
|
||
testing::NotNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (sizeof (SetupMode)),
|
||
SetArgBuffer<4> (&SetupMode, sizeof (SetupMode)),
|
||
testing::Return (EFI_SUCCESS)
|
||
)
|
||
);
|
||
}
|
||
|
||
void
|
||
ExpectHashCheckSkip (
|
||
MockUefiRuntimeServicesTableLib &RtServicesMock,
|
||
int n
|
||
)
|
||
{
|
||
// Here, the cycle and WillOnce are used because InSequence must be specified
|
||
// in the main function. When using InSequence, Times will retire.
|
||
for (int i = 0; i < n; i++) {
|
||
// Return ‘not found’ for DBx to exit from the first
|
||
// IsSignatureFoundInDatabase
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (
|
||
&gEfiImageSecurityDatabaseGuid,
|
||
sizeof (EFI_GUID)
|
||
),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND));
|
||
|
||
// IsFound is false, EFI_NOT_FOUND will proceed to the next algorithm
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (
|
||
&gEfiImageSecurityDatabaseGuid,
|
||
sizeof (EFI_GUID)
|
||
),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND));
|
||
}
|
||
}
|
||
|
||
template <size_t N>
|
||
void
|
||
ExpectHashDBValue (
|
||
MockUefiRuntimeServicesTableLib &RtServicesMock,
|
||
UINT8 (&CertListBuffer)[N],
|
||
UINTN BufferSize
|
||
)
|
||
{
|
||
// Return ‘not found’ for DBx to exit from the first
|
||
// IsSignatureFoundInDatabase
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND));
|
||
|
||
// Second call of IsSignatureFoundInDatabase for DB
|
||
// Get Size
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (BufferSize),
|
||
testing::Return (EFI_BUFFER_TOO_SMALL)
|
||
)
|
||
);
|
||
|
||
// Return hash
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)BufferSize)),
|
||
testing::NotNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (BufferSize),
|
||
SetArgBuffer<4> (&CertListBuffer[0], BufferSize),
|
||
testing::Return (EFI_SUCCESS)
|
||
)
|
||
);
|
||
|
||
// Hash verification does't exit the loop upon the first match
|
||
EXPECT_CALL (RtServicesMock, gRT_GetVariable)
|
||
.Times (testing::AnyNumber ())
|
||
.WillRepeatedly (testing::Return (EFI_NOT_FOUND));
|
||
}
|
||
|
||
EFI_STATUS
|
||
MockInstallConfigurationTable (
|
||
EFI_GUID *Guid,
|
||
VOID *Table
|
||
)
|
||
{
|
||
// LeakSanitizer workaround for AddImageExeInfo
|
||
if (Table != NULL) {
|
||
FreePool (Table);
|
||
}
|
||
|
||
return EFI_SUCCESS;
|
||
}
|
||
|
||
void
|
||
ExpectFailureJump (
|
||
MockDevicePathLib &DevicePathMock,
|
||
MockUefiLib &UefiMock,
|
||
MockUefiBootServicesTableLib &BsMock
|
||
)
|
||
{
|
||
EXPECT_CALL (DevicePathMock, ConvertDevicePathToText)
|
||
.WillRepeatedly (testing::Return ((CHAR16 *)NULL));
|
||
|
||
EXPECT_CALL (UefiMock, EfiGetSystemConfigurationTable)
|
||
.WillRepeatedly (testing::Return (EFI_SUCCESS));
|
||
|
||
EXPECT_CALL (DevicePathMock, GetDevicePathSize)
|
||
.WillRepeatedly (testing::Return ((UINTN)0));
|
||
|
||
EXPECT_CALL (BsMock, gBS_InstallConfigurationTable)
|
||
.WillRepeatedly (testing::Invoke (MockInstallConfigurationTable));
|
||
}
|
||
|
||
template <size_t N, size_t M>
|
||
void
|
||
ExpectForbiddenByDBxHash (
|
||
MockUefiRuntimeServicesTableLib &RtServicesMock,
|
||
UINT8 (&CertListBuffer)[N],
|
||
UINTN BufferSize,
|
||
UINT8 (&HashListBuffer)[M],
|
||
UINTN HashBufferSize
|
||
)
|
||
{
|
||
// Signature DBx check - EFI_NOT_FOUND
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND));
|
||
|
||
// IsAllowedByDb - DB size check
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (BufferSize),
|
||
testing::Return (EFI_BUFFER_TOO_SMALL)
|
||
)
|
||
);
|
||
|
||
// IsAllowedByDb - DB data (X509 certs entries)
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)BufferSize)),
|
||
testing::NotNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (BufferSize),
|
||
SetArgBuffer<4> (&CertListBuffer[0], BufferSize),
|
||
testing::Return (EFI_SUCCESS)
|
||
)
|
||
);
|
||
|
||
// IsAllowedByDb DBx check root cert revoked
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND));
|
||
|
||
// IsSignatureFoundInDatabase prepare buffer
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (HashBufferSize),
|
||
testing::Return (EFI_BUFFER_TOO_SMALL)
|
||
)
|
||
);
|
||
|
||
// IsSignatureFoundInDatabase prohibit hash
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)HashBufferSize)),
|
||
testing::NotNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (HashBufferSize),
|
||
SetArgBuffer<4> (&HashListBuffer[0], HashBufferSize),
|
||
testing::Return (EFI_SUCCESS)
|
||
)
|
||
);
|
||
|
||
EXPECT_CALL (RtServicesMock, gRT_GetVariable)
|
||
.Times (testing::AnyNumber ())
|
||
.WillRepeatedly (testing::Return (EFI_NOT_FOUND));
|
||
}
|
||
|
||
template <size_t N>
|
||
void
|
||
ExpectNoSignatureHashDBValue (
|
||
MockUefiRuntimeServicesTableLib &RtServicesMock,
|
||
UINT8 (&HashCertListBuffer)[N],
|
||
UINTN HashBufferSize
|
||
)
|
||
{
|
||
// Signature DBx check - EFI_NOT_FOUND
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND));
|
||
|
||
// IsAllowedByDb - DB size check
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (
|
||
&gEfiImageSecurityDatabaseGuid,
|
||
sizeof (EFI_GUID)
|
||
),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (HashBufferSize),
|
||
testing::Return (EFI_BUFFER_TOO_SMALL)
|
||
)
|
||
);
|
||
|
||
// IsAllowedByDb - DB data (hash entries, no X509 certs)
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (
|
||
&gEfiImageSecurityDatabaseGuid,
|
||
sizeof (EFI_GUID)
|
||
),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)HashBufferSize)),
|
||
testing::NotNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (HashBufferSize),
|
||
SetArgBuffer<4> (&HashCertListBuffer[0], HashBufferSize),
|
||
testing::Return (EFI_SUCCESS)
|
||
)
|
||
);
|
||
|
||
// IsAllowedByDb (DBX) - NOT FOUND
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND));
|
||
|
||
// IsSignatureFoundInDatabase (DBX) - NOT FOUND
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND));
|
||
|
||
// IsSignatureFoundInDatabase (DB) - size check
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (HashBufferSize),
|
||
testing::Return (EFI_BUFFER_TOO_SMALL)
|
||
)
|
||
);
|
||
|
||
// IsSignatureFoundInDatabase (DB) - data (hash found)
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)HashBufferSize)),
|
||
testing::NotNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (HashBufferSize),
|
||
SetArgBuffer<4> (&HashCertListBuffer[0], HashBufferSize),
|
||
testing::Return (EFI_SUCCESS)
|
||
)
|
||
);
|
||
|
||
EXPECT_CALL (RtServicesMock, gRT_GetVariable)
|
||
.Times (testing::AnyNumber ())
|
||
.WillRepeatedly (testing::Return (EFI_NOT_FOUND));
|
||
}
|
||
|
||
template <size_t N>
|
||
void
|
||
ExpectCertForbiddenByDbx (
|
||
MockUefiRuntimeServicesTableLib &RtServicesMock,
|
||
UINT8 (&CertListBuffer)[N],
|
||
UINTN BufferSize
|
||
)
|
||
{
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (BufferSize),
|
||
testing::Return (EFI_BUFFER_TOO_SMALL)
|
||
)
|
||
);
|
||
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)BufferSize)),
|
||
testing::NotNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (BufferSize),
|
||
SetArgBuffer<4> (&CertListBuffer[0], BufferSize),
|
||
testing::Return (EFI_SUCCESS)
|
||
)
|
||
);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
class CheckImageTypeResult : public ::testing::Test
|
||
{
|
||
public:
|
||
EFI_DEVICE_PATH_PROTOCOL File;
|
||
|
||
protected:
|
||
MockUefiRuntimeServicesTableLib RtServicesMock;
|
||
MockUefiBootServicesTableLib BsMock;
|
||
MockDevicePathLib DevicePathMock;
|
||
|
||
EFI_STATUS Status;
|
||
|
||
UINT32 AuthenticationStatus;
|
||
VOID *FileBuffer;
|
||
UINTN FileSize;
|
||
BOOLEAN BootPolicy;
|
||
|
||
virtual void
|
||
SetUp (
|
||
)
|
||
{
|
||
AuthenticationStatus = 0;
|
||
FileBuffer = NULL;
|
||
FileSize = 0;
|
||
BootPolicy = FALSE;
|
||
}
|
||
};
|
||
|
||
TEST_F (CheckImageTypeResult, ImageTypeVerifySanity) {
|
||
// Sanity check
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
NULL,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_INVALID_PARAMETER);
|
||
}
|
||
|
||
TEST_F (CheckImageTypeResult, ImageTypeVerifyImageFromFv) {
|
||
EXPECT_CALL (BsMock, gBS_LocateDevicePath)
|
||
.WillRepeatedly (testing::Return (EFI_SUCCESS));
|
||
EXPECT_CALL (BsMock, gBS_OpenProtocol)
|
||
.WillRepeatedly (testing::Return (EFI_SUCCESS));
|
||
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
TEST_F (CheckImageTypeResult, ImageTypeVerifyImageFromOptionRom) {
|
||
auto TestFunc = [&] (EFI_STATUS ExpectedStatus) {
|
||
EXPECT_CALL (BsMock, gBS_LocateDevicePath)
|
||
.Times (3)
|
||
.WillRepeatedly (testing::Return (EFI_NOT_FOUND));
|
||
EXPECT_CALL (BsMock, gBS_OpenProtocol)
|
||
.WillRepeatedly (testing::Return (EFI_NOT_FOUND));
|
||
EXPECT_CALL (DevicePathMock, IsDevicePathEndType)
|
||
.WillOnce (testing::Return ((BOOLEAN)FALSE));
|
||
EXPECT_CALL (DevicePathMock, DevicePathType)
|
||
.WillOnce (testing::Return ((UINT8)MEDIA_DEVICE_PATH));
|
||
EXPECT_CALL (DevicePathMock, DevicePathSubType)
|
||
.WillOnce (testing::Return ((UINT8)MEDIA_RELATIVE_OFFSET_RANGE_DP));
|
||
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, ExpectedStatus);
|
||
};
|
||
|
||
PatchPcdSet32 (PcdOptionRomImageVerificationPolicy, ALWAYS_EXECUTE);
|
||
TestFunc (EFI_SUCCESS);
|
||
PatchPcdSet32 (PcdOptionRomImageVerificationPolicy, NEVER_EXECUTE);
|
||
TestFunc (EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
TEST_F (CheckImageTypeResult, ImageTypeVerifyImageFromRemovableMedia) {
|
||
auto TestFunc = [&] (EFI_STATUS ExpectedStatus) {
|
||
EXPECT_CALL (BsMock, gBS_LocateDevicePath)
|
||
.Times (3)
|
||
.WillRepeatedly (testing::Return (EFI_NOT_FOUND));
|
||
EXPECT_CALL (DevicePathMock, IsDevicePathEndType)
|
||
.WillOnce (testing::Return ((BOOLEAN)FALSE));
|
||
EXPECT_CALL (DevicePathMock, DevicePathType)
|
||
.WillOnce (testing::Return ((UINT8)MESSAGING_DEVICE_PATH));
|
||
EXPECT_CALL (DevicePathMock, DevicePathSubType)
|
||
.WillOnce (testing::Return ((UINT8)MSG_MAC_ADDR_DP));
|
||
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, ExpectedStatus);
|
||
};
|
||
|
||
PatchPcdSet32 (PcdRemovableMediaImageVerificationPolicy, ALWAYS_EXECUTE);
|
||
TestFunc (EFI_SUCCESS);
|
||
PatchPcdSet32 (PcdRemovableMediaImageVerificationPolicy, NEVER_EXECUTE);
|
||
TestFunc (EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
TEST_F (CheckImageTypeResult, ImageTypeVerifyImageFromFixedMedia) {
|
||
auto TestFunc = [&] (EFI_STATUS ExpectedStatus) {
|
||
EXPECT_CALL (BsMock, gBS_LocateDevicePath)
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND))
|
||
.WillOnce (testing::Return (EFI_NOT_FOUND))
|
||
.WillOnce (testing::Return (EFI_SUCCESS));
|
||
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, ExpectedStatus);
|
||
};
|
||
|
||
PatchPcdSet32 (PcdFixedMediaImageVerificationPolicy, ALWAYS_EXECUTE);
|
||
TestFunc (EFI_SUCCESS);
|
||
PatchPcdSet32 (PcdFixedMediaImageVerificationPolicy, NEVER_EXECUTE);
|
||
TestFunc (EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
class CheckUnsignedImage : public ::testing::Test
|
||
{
|
||
public:
|
||
EFI_DEVICE_PATH_PROTOCOL File;
|
||
|
||
protected:
|
||
MockUefiRuntimeServicesTableLib RtServicesMock;
|
||
MockUefiBootServicesTableLib BsMock;
|
||
MockDevicePathLib DevicePathMock;
|
||
MockUefiLib UefiMock;
|
||
|
||
EFI_STATUS Status;
|
||
|
||
UINT32 AuthenticationStatus;
|
||
VOID *FileBuffer;
|
||
UINTN FileSize;
|
||
BOOLEAN BootPolicy;
|
||
|
||
virtual void
|
||
SetUp (
|
||
)
|
||
{
|
||
AuthenticationStatus = 0;
|
||
FileBuffer = NULL;
|
||
FileSize = 0;
|
||
BootPolicy = FALSE;
|
||
}
|
||
};
|
||
|
||
TEST_F (CheckUnsignedImage, HashNormalFlow) {
|
||
constexpr UINTN Hash512Size = sizeof (images::UnsignedCOFFSha512);
|
||
constexpr UINTN Hash384Size = sizeof (images::UnsignedCOFFSha384);
|
||
constexpr UINTN Hash256Size = sizeof (images::UnsignedCOFFSha256);
|
||
constexpr UINTN Hash1Size = sizeof (images::UnsignedCOFFSha1);
|
||
|
||
constexpr UINTN Hash512BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash512Size;
|
||
constexpr UINTN Hash384BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash384Size;
|
||
constexpr UINTN Hash256BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash256Size;
|
||
constexpr UINTN Hash1BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + Hash1Size;
|
||
|
||
UINT8 Hash512CertListBuffer[Hash512BufferSize] = { 0 };
|
||
UINT8 Hash384CertListBuffer[Hash384BufferSize] = { 0 };
|
||
UINT8 Hash256CertListBuffer[Hash256BufferSize] = { 0 };
|
||
UINT8 Hash1CertListBuffer[Hash1BufferSize] = { 0 };
|
||
|
||
PrepareCertList (
|
||
Hash512Size,
|
||
Hash512BufferSize,
|
||
&gEfiCertSha512Guid,
|
||
&images::UnsignedCOFFSha512[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash512CertListBuffer
|
||
);
|
||
|
||
{
|
||
// Do not delete this. Otherwise, GetVariable from ExpectHashDBValue will
|
||
// match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
ExpectHashDBValue<Hash512BufferSize> (
|
||
RtServicesMock,
|
||
Hash512CertListBuffer,
|
||
Hash512BufferSize
|
||
);
|
||
|
||
FileBuffer = (VOID *)&images::UnsignedCOFF;
|
||
FileSize = sizeof (images::UnsignedCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash384Size,
|
||
Hash384BufferSize,
|
||
&gEfiCertSha384Guid,
|
||
&images::UnsignedCOFFSha384[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash384CertListBuffer
|
||
);
|
||
|
||
{
|
||
// Do not delete this. Otherwise, GetVariable from ExpectHashDBValue will
|
||
// match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
// Skip SHA512 check
|
||
ExpectHashCheckSkip (RtServicesMock, 1);
|
||
|
||
// SHA384 check
|
||
ExpectHashDBValue<Hash384BufferSize> (
|
||
RtServicesMock,
|
||
Hash384CertListBuffer,
|
||
Hash384BufferSize
|
||
);
|
||
|
||
FileBuffer = (VOID *)&images::UnsignedCOFF;
|
||
FileSize = sizeof (images::UnsignedCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash256Size,
|
||
Hash256BufferSize,
|
||
&gEfiCertSha256Guid,
|
||
&images::UnsignedCOFFSha256[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash256CertListBuffer
|
||
);
|
||
|
||
{
|
||
// Do not delete this. Otherwise, GetVariable from ExpectHashDBValue will
|
||
// match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
// Skip SHA512, SHA384 check
|
||
ExpectHashCheckSkip (RtServicesMock, 2);
|
||
|
||
// SHA256 check
|
||
ExpectHashDBValue<Hash256BufferSize> (
|
||
RtServicesMock,
|
||
Hash256CertListBuffer,
|
||
Hash256BufferSize
|
||
);
|
||
|
||
FileBuffer = (VOID *)&images::UnsignedCOFF;
|
||
FileSize = sizeof (images::UnsignedCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash1Size,
|
||
Hash1BufferSize,
|
||
&gEfiCertSha1Guid,
|
||
&images::UnsignedCOFFSha1[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash1CertListBuffer
|
||
);
|
||
|
||
{
|
||
// Do not delete this. Otherwise, GetVariable from ExpectHashDBValue will
|
||
// match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
// Skip SHA512, SHA384, SHA256 check
|
||
ExpectHashCheckSkip (RtServicesMock, 3);
|
||
|
||
ExpectHashDBValue<Hash1BufferSize> (
|
||
RtServicesMock,
|
||
Hash1CertListBuffer,
|
||
Hash1BufferSize
|
||
);
|
||
|
||
// Last check. No need to loop.
|
||
FileBuffer = (VOID *)&images::UnsignedCOFF;
|
||
FileSize = sizeof (images::UnsignedCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
}
|
||
|
||
TEST_F (CheckUnsignedImage, HashNoDBRecods) {
|
||
// Do not delete this. Otherwise, GetVariable from ExpectHashDBValue will
|
||
// match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
// Skip SHA512, SHA384, SHA256, SHA1 check
|
||
ExpectHashCheckSkip (RtServicesMock, 4);
|
||
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
// Last check. No need to loop.
|
||
FileBuffer = (VOID *)&images::UnsignedCOFF;
|
||
FileSize = sizeof (images::UnsignedCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
TEST_F (CheckUnsignedImage, HashFoundDBx) {
|
||
constexpr UINTN Size = sizeof (images::UnsignedCOFFSha512);
|
||
constexpr UINTN BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + Size;
|
||
|
||
UINT8 CertListBuffer[BufferSize] = { 0 };
|
||
|
||
PrepareCertList (
|
||
Size,
|
||
BufferSize,
|
||
&gEfiCertSha512Guid,
|
||
&images::UnsignedCOFFSha512[0],
|
||
(EFI_SIGNATURE_LIST *)&CertListBuffer
|
||
);
|
||
|
||
// Do not delete this. Otherwise, GetVariable from ExpectHashDBValue will
|
||
// match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
// DBx routines
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)0)),
|
||
testing::IsNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (BufferSize),
|
||
testing::Return (EFI_BUFFER_TOO_SMALL)
|
||
)
|
||
);
|
||
|
||
// Return hash
|
||
EXPECT_CALL (
|
||
RtServicesMock,
|
||
gRT_GetVariable (
|
||
Char16StrEq (EFI_IMAGE_SECURITY_DATABASE1),
|
||
BufferEq (&gEfiImageSecurityDatabaseGuid, sizeof (EFI_GUID)),
|
||
testing::IsNull (),
|
||
testing::Pointee (testing::Eq ((UINTN)BufferSize)),
|
||
testing::NotNull ()
|
||
)
|
||
)
|
||
.WillOnce (
|
||
testing::DoAll (
|
||
testing::SetArgPointee<3> (BufferSize),
|
||
SetArgBuffer<4> (&CertListBuffer[0], BufferSize),
|
||
testing::Return (EFI_SUCCESS)
|
||
)
|
||
);
|
||
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
// Last check. No need to loop.
|
||
FileBuffer = (VOID *)&images::UnsignedCOFF;
|
||
FileSize = sizeof (images::UnsignedCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
class CheckSignedImage : public ::testing::Test
|
||
{
|
||
public:
|
||
EFI_DEVICE_PATH_PROTOCOL File;
|
||
|
||
protected:
|
||
MockUefiRuntimeServicesTableLib RtServicesMock;
|
||
MockUefiBootServicesTableLib BsMock;
|
||
MockDevicePathLib DevicePathMock;
|
||
MockUefiLib UefiMock;
|
||
|
||
EFI_STATUS Status;
|
||
|
||
UINT32 AuthenticationStatus;
|
||
VOID *FileBuffer;
|
||
UINTN FileSize;
|
||
BOOLEAN BootPolicy;
|
||
|
||
virtual void
|
||
SetUp (
|
||
)
|
||
{
|
||
AuthenticationStatus = 0;
|
||
FileBuffer = NULL;
|
||
FileSize = 0;
|
||
BootPolicy = FALSE;
|
||
}
|
||
};
|
||
|
||
TEST_F (CheckSignedImage, NormalFlowPkcs7) {
|
||
constexpr UINTN CertSize = sizeof (certs::TestCertDer);
|
||
constexpr UINTN BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + CertSize;
|
||
|
||
UINT8 CertListBuffer[BufferSize] = { 0 };
|
||
|
||
PrepareCertList (
|
||
CertSize,
|
||
BufferSize,
|
||
&gEfiCertX509Guid,
|
||
&certs::TestCertDer[0],
|
||
(EFI_SIGNATURE_LIST *)&CertListBuffer
|
||
);
|
||
|
||
// Do not delete this. Otherwise, GetVariable from ExpectSignatureFoundInDb
|
||
// will match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
ExpectHashDBValue<BufferSize> (RtServicesMock, CertListBuffer, BufferSize);
|
||
|
||
FileBuffer = (VOID *)&images::PkcsCOFF;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
TEST_F (CheckSignedImage, NormalFlowUefiGuid) {
|
||
constexpr UINTN CertSize = sizeof (certs::TestCertDer);
|
||
constexpr UINTN BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + CertSize;
|
||
|
||
UINT8 CertListBuffer[BufferSize] = { 0 };
|
||
|
||
PrepareCertList (
|
||
CertSize,
|
||
BufferSize,
|
||
&gEfiCertX509Guid,
|
||
&certs::TestCertDer[0],
|
||
(EFI_SIGNATURE_LIST *)&CertListBuffer
|
||
);
|
||
|
||
// Do not delete this. Otherwise, GetVariable from ExpectSignatureFoundInDb
|
||
// will match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
ExpectHashDBValue<BufferSize> (RtServicesMock, CertListBuffer, BufferSize);
|
||
|
||
FileBuffer = (VOID *)&images::CertGuidCOFF;
|
||
FileSize = sizeof (images::CertGuidCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
TEST_F (CheckSignedImage, NormalFlowTwoCerts) {
|
||
constexpr UINTN CertSize = sizeof (certs::TestCertDer);
|
||
constexpr UINTN SignatureDataSize = sizeof (EFI_GUID) + CertSize;
|
||
constexpr UINTN BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + (SignatureDataSize * 2);
|
||
|
||
UINT8 CertListBuffer[BufferSize] = { 0 };
|
||
|
||
UINT8 InvalidCert[CertSize] = { 0 };
|
||
|
||
CopyMem (InvalidCert, certs::TestCertDer, CertSize);
|
||
InvalidCert[0] = 0xAA;
|
||
|
||
EFI_SIGNATURE_LIST *CertList = (EFI_SIGNATURE_LIST *)&CertListBuffer;
|
||
|
||
CertList->SignatureListSize = (UINT32)BufferSize;
|
||
CertList->SignatureHeaderSize = 0;
|
||
CertList->SignatureSize = (UINT32)SignatureDataSize;
|
||
CopyGuid (&CertList->SignatureType, &gEfiCertX509Guid);
|
||
|
||
EFI_SIGNATURE_DATA *Cert1 = (EFI_SIGNATURE_DATA *)(CertList + 1);
|
||
|
||
CopyGuid (&Cert1->SignatureOwner, &gEfiGlobalVariableGuid);
|
||
CopyMem (Cert1->SignatureData, InvalidCert, CertSize);
|
||
|
||
EFI_SIGNATURE_DATA *Cert2 = (EFI_SIGNATURE_DATA *)((UINT8 *)Cert1 + SignatureDataSize);
|
||
|
||
CopyGuid (&Cert2->SignatureOwner, &gEfiGlobalVariableGuid);
|
||
CopyMem (Cert2->SignatureData, certs::TestCertDer, CertSize);
|
||
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
ExpectHashDBValue<BufferSize> (RtServicesMock, CertListBuffer, BufferSize);
|
||
|
||
FileBuffer = (VOID *)&images::PkcsCOFF;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
void
|
||
UpdatePeAuthCodeHash (
|
||
UINT8 *PkcsCopy,
|
||
UINT32 HashType,
|
||
CONST UINT8 *HashValue,
|
||
UINTN HashSize
|
||
)
|
||
{
|
||
constexpr UINTN HashOidOffset = 0x408; // AuthData(0x3e8) + 32
|
||
constexpr UINTN HashTagOffset = 0x470; // AuthData + 132
|
||
constexpr UINTN HashValueOffset = 0x472; // AuthData + 134
|
||
|
||
static const UINT8 OidSha1[] = { 0x2B, 0x0E, 0x03, 0x02, 0x1A };
|
||
static const UINT8 OidSha256[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 };
|
||
static const UINT8 OidSha384[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 };
|
||
static const UINT8 OidSha512[] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 };
|
||
|
||
const UINT8 *Oid;
|
||
UINTN OidLength;
|
||
|
||
switch (HashType) {
|
||
case HASHALG_SHA1:
|
||
Oid = OidSha1;
|
||
OidLength = sizeof (OidSha1);
|
||
break;
|
||
case HASHALG_SHA256:
|
||
Oid = OidSha256;
|
||
OidLength = sizeof (OidSha256);
|
||
break;
|
||
case HASHALG_SHA384:
|
||
Oid = OidSha384;
|
||
OidLength = sizeof (OidSha384);
|
||
break;
|
||
case HASHALG_SHA512:
|
||
Oid = OidSha512;
|
||
OidLength = sizeof (OidSha512);
|
||
break;
|
||
default:
|
||
return;
|
||
}
|
||
|
||
CopyMem (PkcsCopy + HashOidOffset, Oid, OidLength);
|
||
PkcsCopy[HashTagOffset] = 0x04; // ASN.1 Octet(value 0x04) string
|
||
PkcsCopy[HashTagOffset + 1] = (UINT8)HashSize;
|
||
CopyMem (PkcsCopy + HashValueOffset, HashValue, HashSize);
|
||
}
|
||
|
||
TEST_F (CheckSignedImage, NormalFlowPkcsNoCertHashAllowed) {
|
||
constexpr UINTN Hash512Size = sizeof (images::PkcsCOFFSha512);
|
||
constexpr UINTN Hash384Size = sizeof (images::PkcsCOFFSha384);
|
||
constexpr UINTN Hash256Size = sizeof (images::PkcsCOFFSha256);
|
||
constexpr UINTN Hash1Size = sizeof (images::PkcsCOFFSha1);
|
||
|
||
constexpr UINTN Hash512BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash512Size;
|
||
constexpr UINTN Hash384BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash384Size;
|
||
constexpr UINTN Hash256BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash256Size;
|
||
constexpr UINTN Hash1BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + Hash1Size;
|
||
|
||
UINT8 Hash512CertListBuffer[Hash512BufferSize] = { 0 };
|
||
UINT8 Hash384CertListBuffer[Hash384BufferSize] = { 0 };
|
||
UINT8 Hash256CertListBuffer[Hash256BufferSize] = { 0 };
|
||
UINT8 Hash1CertListBuffer[Hash1BufferSize] = { 0 };
|
||
|
||
UINT8 PkcsSha1Copy[sizeof (images::PkcsCOFF)];
|
||
UINT8 PkcsSha256Copy[sizeof (images::PkcsCOFF)];
|
||
UINT8 PkcsSha384Copy[sizeof (images::PkcsCOFF)];
|
||
UINT8 PkcsSha512Copy[sizeof (images::PkcsCOFF)];
|
||
|
||
CopyMem (PkcsSha1Copy, images::PkcsCOFF, sizeof (images::PkcsCOFF));
|
||
CopyMem (PkcsSha256Copy, images::PkcsCOFF, sizeof (images::PkcsCOFF));
|
||
CopyMem (PkcsSha384Copy, images::PkcsCOFF, sizeof (images::PkcsCOFF));
|
||
CopyMem (PkcsSha512Copy, images::PkcsCOFF, sizeof (images::PkcsCOFF));
|
||
|
||
UpdatePeAuthCodeHash (PkcsSha1Copy, HASHALG_SHA1, images::PkcsCOFFSha1, Hash1Size);
|
||
UpdatePeAuthCodeHash (PkcsSha256Copy, HASHALG_SHA256, images::PkcsCOFFSha256, Hash256Size);
|
||
UpdatePeAuthCodeHash (PkcsSha384Copy, HASHALG_SHA384, images::PkcsCOFFSha384, Hash384Size);
|
||
UpdatePeAuthCodeHash (PkcsSha512Copy, HASHALG_SHA512, images::PkcsCOFFSha512, Hash512Size);
|
||
|
||
PrepareCertList (
|
||
Hash512Size,
|
||
Hash512BufferSize,
|
||
&gEfiCertSha512Guid,
|
||
&images::PkcsCOFFSha512[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash512CertListBuffer
|
||
);
|
||
|
||
{
|
||
testing::InSequence s;
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
ExpectNoSignatureHashDBValue<Hash512BufferSize> (
|
||
RtServicesMock,
|
||
Hash512CertListBuffer,
|
||
Hash512BufferSize
|
||
);
|
||
|
||
FileBuffer = (VOID *)&PkcsSha512Copy;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash384Size,
|
||
Hash384BufferSize,
|
||
&gEfiCertSha384Guid,
|
||
&images::PkcsCOFFSha384[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash384CertListBuffer
|
||
);
|
||
|
||
{
|
||
testing::InSequence s;
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
ExpectNoSignatureHashDBValue<Hash384BufferSize> (
|
||
RtServicesMock,
|
||
Hash384CertListBuffer,
|
||
Hash384BufferSize
|
||
);
|
||
|
||
FileBuffer = (VOID *)&PkcsSha384Copy;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash256Size,
|
||
Hash256BufferSize,
|
||
&gEfiCertSha256Guid,
|
||
&images::PkcsCOFFSha256[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash256CertListBuffer
|
||
);
|
||
|
||
{
|
||
testing::InSequence s;
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
ExpectNoSignatureHashDBValue<Hash256BufferSize> (
|
||
RtServicesMock,
|
||
Hash256CertListBuffer,
|
||
Hash256BufferSize
|
||
);
|
||
|
||
FileBuffer = (VOID *)&PkcsSha256Copy;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash1Size,
|
||
Hash1BufferSize,
|
||
&gEfiCertSha1Guid,
|
||
&images::PkcsCOFFSha1[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash1CertListBuffer
|
||
);
|
||
|
||
{
|
||
testing::InSequence s;
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
ExpectNoSignatureHashDBValue<Hash1BufferSize> (
|
||
RtServicesMock,
|
||
Hash1CertListBuffer,
|
||
Hash1BufferSize
|
||
);
|
||
|
||
FileBuffer = (VOID *)&PkcsSha1Copy;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_SUCCESS);
|
||
}
|
||
}
|
||
|
||
TEST_F (CheckSignedImage, ForbiddenByDbxPkcs7Sign) {
|
||
constexpr UINTN CertSize = sizeof (certs::TestCertDer);
|
||
constexpr UINTN BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + CertSize;
|
||
|
||
UINT8 CertListBuffer[BufferSize] = { 0 };
|
||
|
||
PrepareCertList (
|
||
CertSize,
|
||
BufferSize,
|
||
&gEfiCertX509Guid,
|
||
&certs::TestCertDer[0],
|
||
(EFI_SIGNATURE_LIST *)&CertListBuffer
|
||
);
|
||
|
||
// Do not delete this. Otherwise, GetVariable from ExpectCertForbiddenByDbx
|
||
// will match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
ExpectCertForbiddenByDbx<BufferSize> (
|
||
RtServicesMock,
|
||
CertListBuffer,
|
||
BufferSize
|
||
);
|
||
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
FileBuffer = (VOID *)&images::PkcsCOFF;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
TEST_F (CheckSignedImage, ForbiddenByDbxUefiGuidSign) {
|
||
constexpr UINTN CertSize = sizeof (certs::TestCertDer);
|
||
constexpr UINTN BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + CertSize;
|
||
|
||
UINT8 CertListBuffer[BufferSize] = { 0 };
|
||
|
||
PrepareCertList (
|
||
CertSize,
|
||
BufferSize,
|
||
&gEfiCertX509Guid,
|
||
&certs::TestCertDer[0],
|
||
(EFI_SIGNATURE_LIST *)&CertListBuffer
|
||
);
|
||
|
||
// Do not delete this. Otherwise, GetVariable from ExpectCertForbiddenByDbx
|
||
// will match all calls.
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
ExpectCertForbiddenByDbx<BufferSize> (
|
||
RtServicesMock,
|
||
CertListBuffer,
|
||
BufferSize
|
||
);
|
||
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
FileBuffer = (VOID *)&images::CertGuidCOFF;
|
||
FileSize = sizeof (images::CertGuidCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
TEST_F (CheckSignedImage, NoCertPkcs7SignNoHash) {
|
||
// This test also covers the code path for when a perfectly valid signature is
|
||
// missing from the 'db' variable, resulting in the error: "Image is signed
|
||
// but signature is not allowed by DB and %s hash of image is not found in
|
||
// DB/DBX."
|
||
constexpr UINTN CertSize = sizeof (certs::TestCertDer);
|
||
constexpr UINTN BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + CertSize;
|
||
|
||
UINT8 InvalidCert[CertSize] = { 0 };
|
||
|
||
CopyMem (InvalidCert, images::PkcsCOFF, CertSize);
|
||
|
||
InvalidCert[0] = 0xAA;
|
||
|
||
UINT8 CertListBuffer[BufferSize] = { 0 };
|
||
|
||
PrepareCertList (
|
||
CertSize,
|
||
BufferSize,
|
||
&gEfiCertX509Guid,
|
||
InvalidCert,
|
||
(EFI_SIGNATURE_LIST *)&CertListBuffer
|
||
);
|
||
|
||
testing::InSequence s;
|
||
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
|
||
ExpectHashDBValue<BufferSize> (RtServicesMock, CertListBuffer, BufferSize);
|
||
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
FileBuffer = (VOID *)&images::PkcsCOFF;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
TEST_F (CheckSignedImage, ForbiddenByDBxHash) {
|
||
constexpr UINTN CertSize = sizeof (certs::TestCertDer);
|
||
constexpr UINTN Hash512Size = sizeof (images::PkcsCOFFSha512);
|
||
constexpr UINTN Hash384Size = sizeof (images::PkcsCOFFSha384);
|
||
constexpr UINTN Hash256Size = sizeof (images::PkcsCOFFSha256);
|
||
constexpr UINTN Hash1Size = sizeof (images::PkcsCOFFSha1);
|
||
|
||
constexpr UINTN BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + CertSize;
|
||
constexpr UINTN Hash512BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash512Size;
|
||
constexpr UINTN Hash384BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash384Size;
|
||
constexpr UINTN Hash256BufferSize = sizeof (EFI_SIGNATURE_LIST) +
|
||
sizeof (EFI_SIGNATURE_DATA) - 1 +
|
||
Hash256Size;
|
||
constexpr UINTN Hash1BufferSize =
|
||
sizeof (EFI_SIGNATURE_LIST) + sizeof (EFI_SIGNATURE_DATA) - 1 + Hash1Size;
|
||
|
||
UINT8 CertListBuffer[BufferSize] = { 0 };
|
||
UINT8 Hash512CertListBuffer[Hash512BufferSize] = { 0 };
|
||
UINT8 Hash384CertListBuffer[Hash384BufferSize] = { 0 };
|
||
UINT8 Hash256CertListBuffer[Hash256BufferSize] = { 0 };
|
||
UINT8 Hash1CertListBuffer[Hash1BufferSize] = { 0 };
|
||
|
||
UINT8 PkcsSha1Copy[sizeof (images::PkcsCOFF)];
|
||
UINT8 PkcsSha256Copy[sizeof (images::PkcsCOFF)];
|
||
UINT8 PkcsSha384Copy[sizeof (images::PkcsCOFF)];
|
||
UINT8 PkcsSha512Copy[sizeof (images::PkcsCOFF)];
|
||
|
||
CopyMem (PkcsSha1Copy, images::PkcsCOFF, sizeof (images::PkcsCOFF));
|
||
CopyMem (PkcsSha256Copy, images::PkcsCOFF, sizeof (images::PkcsCOFF));
|
||
CopyMem (PkcsSha384Copy, images::PkcsCOFF, sizeof (images::PkcsCOFF));
|
||
CopyMem (PkcsSha512Copy, images::PkcsCOFF, sizeof (images::PkcsCOFF));
|
||
|
||
UpdatePeAuthCodeHash (PkcsSha1Copy, HASHALG_SHA1, images::PkcsCOFFSha1, Hash1Size);
|
||
UpdatePeAuthCodeHash (PkcsSha256Copy, HASHALG_SHA256, images::PkcsCOFFSha256, Hash256Size);
|
||
UpdatePeAuthCodeHash (PkcsSha384Copy, HASHALG_SHA384, images::PkcsCOFFSha384, Hash384Size);
|
||
UpdatePeAuthCodeHash (PkcsSha512Copy, HASHALG_SHA512, images::PkcsCOFFSha512, Hash512Size);
|
||
|
||
PrepareCertList (
|
||
CertSize,
|
||
BufferSize,
|
||
&gEfiCertX509Guid,
|
||
certs::TestCertDer,
|
||
(EFI_SIGNATURE_LIST *)&CertListBuffer
|
||
);
|
||
|
||
PrepareCertList (
|
||
Hash512Size,
|
||
Hash512BufferSize,
|
||
&gEfiCertSha512Guid,
|
||
&images::PkcsCOFFSha512[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash512CertListBuffer
|
||
);
|
||
|
||
{
|
||
testing::InSequence s;
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
ExpectForbiddenByDBxHash<BufferSize, Hash512BufferSize> (
|
||
RtServicesMock,
|
||
CertListBuffer,
|
||
BufferSize,
|
||
Hash512CertListBuffer,
|
||
Hash512BufferSize
|
||
);
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
FileBuffer = (VOID *)&PkcsSha512Copy;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash384Size,
|
||
Hash384BufferSize,
|
||
&gEfiCertSha384Guid,
|
||
&images::PkcsCOFFSha384[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash384CertListBuffer
|
||
);
|
||
|
||
{
|
||
testing::InSequence s;
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
ExpectForbiddenByDBxHash<BufferSize, Hash384BufferSize> (
|
||
RtServicesMock,
|
||
CertListBuffer,
|
||
BufferSize,
|
||
Hash384CertListBuffer,
|
||
Hash384BufferSize
|
||
);
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
FileBuffer = (VOID *)&PkcsSha384Copy;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash256Size,
|
||
Hash256BufferSize,
|
||
&gEfiCertSha256Guid,
|
||
&images::PkcsCOFFSha256[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash256CertListBuffer
|
||
);
|
||
|
||
{
|
||
testing::InSequence s;
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
ExpectForbiddenByDBxHash<BufferSize, Hash256BufferSize> (
|
||
RtServicesMock,
|
||
CertListBuffer,
|
||
BufferSize,
|
||
Hash256CertListBuffer,
|
||
Hash256BufferSize
|
||
);
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
FileBuffer = (VOID *)&PkcsSha256Copy;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
|
||
PrepareCertList (
|
||
Hash1Size,
|
||
Hash1BufferSize,
|
||
&gEfiCertSha1Guid,
|
||
&images::PkcsCOFFSha1[0],
|
||
(EFI_SIGNATURE_LIST *)&Hash1CertListBuffer
|
||
);
|
||
|
||
{
|
||
testing::InSequence s;
|
||
ExpectBeforeHashCheck (BsMock, DevicePathMock, RtServicesMock);
|
||
ExpectForbiddenByDBxHash<BufferSize, Hash1BufferSize> (
|
||
RtServicesMock,
|
||
CertListBuffer,
|
||
BufferSize,
|
||
Hash1CertListBuffer,
|
||
Hash1BufferSize
|
||
);
|
||
ExpectFailureJump (DevicePathMock, UefiMock, BsMock);
|
||
|
||
FileBuffer = (VOID *)&PkcsSha1Copy;
|
||
FileSize = sizeof (images::PkcsCOFF);
|
||
Status = DxeImageVerificationHandler (
|
||
AuthenticationStatus,
|
||
&File,
|
||
FileBuffer,
|
||
FileSize,
|
||
BootPolicy
|
||
);
|
||
EXPECT_EQ (Status, EFI_ACCESS_DENIED);
|
||
}
|
||
}
|
||
|
||
int
|
||
main (
|
||
int argc,
|
||
char *argv[]
|
||
)
|
||
{
|
||
testing::InitGoogleTest (&argc, argv);
|
||
return RUN_ALL_TESTS ();
|
||
}
|