CloverBootloader/Protocols/HashServiceFix/HashServiceFix.c
2019-09-03 12:58:42 +03:00

325 lines
7.1 KiB
C

/**
* Hash service fix for AMI EFIs with broken SHA implementations.
*
* If an existing SHA1 implementation is found and produces wrong hashes,
* is is first unregistered and then replaced with a working one. This
* replacement protocol only implements SHA1.
*
* Author: Joel Höner <athre0z@zyantific.com>
*/
#include <Protocol/ServiceBinding.h>
#include <Protocol/Hash.h>
#include "sha1.h"
/* ===================================================================== */
/* [Hash Protocol] */
/* ===================================================================== */
typedef struct _HS_PRIVATE_DATA
{
SHA1_CTX Sha1Ctx;
EFI_HASH_PROTOCOL Proto;
} HS_PRIVATE_DATA;
#define HS_PRIVATE_FROM_PROTO(x) \
(HS_PRIVATE_DATA*) \
((UINTN)x - OFFSET_OF(HS_PRIVATE_DATA, Proto))
EFI_STATUS
EFIAPI
HSGetHashSize(
IN CONST EFI_HASH_PROTOCOL *This,
IN CONST EFI_GUID *HashAlgorithm,
OUT UINTN *HashSize
)
{
if (!HashAlgorithm || !HashSize) {
return EFI_INVALID_PARAMETER;
}
if (CompareGuid(&gEfiHashAlgorithmSha1Guid, HashAlgorithm)) {
*HashSize = sizeof(EFI_SHA1_HASH);
return EFI_SUCCESS;
}
return EFI_UNSUPPORTED;
}
EFI_STATUS
EFIAPI
HSHash(
IN CONST EFI_HASH_PROTOCOL *This,
IN CONST EFI_GUID *HashAlgorithm,
IN BOOLEAN Extend,
IN CONST UINT8 *Message,
IN UINT64 MessageSize,
IN OUT EFI_HASH_OUTPUT *Hash
)
{
HS_PRIVATE_DATA *PrivateData;
SHA1_CTX CtxCopy;
if (!This || !HashAlgorithm || !Message || !Hash || !MessageSize) {
return EFI_INVALID_PARAMETER;
}
if (!CompareGuid(&gEfiHashAlgorithmSha1Guid, HashAlgorithm)) {
return EFI_UNSUPPORTED;
}
PrivateData = HS_PRIVATE_FROM_PROTO(This);
if (!Extend) {
SHA1Init(&PrivateData->Sha1Ctx);
}
SHA1Update(&PrivateData->Sha1Ctx, Message, MessageSize);
// SHA1Final clears the context, so we need to create a copy
// in order to be able to support later updates.
CopyMem(&CtxCopy, &PrivateData->Sha1Ctx, sizeof CtxCopy);
SHA1Final(*Hash->Sha1Hash, &CtxCopy);
return EFI_SUCCESS;
}
EFI_HASH_PROTOCOL mHashProto = {
&HSGetHashSize,
&HSHash
};
/* ========================================================================== */
/* [Hash Binding Protocol] */
/* ========================================================================== */
EFI_STATUS
EFIAPI
HSCreateChild(
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN OUT EFI_HANDLE *ChildHandle
)
{
HS_PRIVATE_DATA *PrivateData;
EFI_STATUS Status;
PrivateData = AllocateZeroPool(sizeof *PrivateData);
if (!PrivateData) {
return EFI_OUT_OF_RESOURCES;
}
CopyMem(&PrivateData->Proto, &mHashProto, sizeof mHashProto);
Status = gBS->InstallProtocolInterface(
ChildHandle,
&gEfiHashProtocolGuid,
EFI_NATIVE_INTERFACE,
&PrivateData->Proto
);
if (EFI_ERROR(Status)) {
FreePool(PrivateData);
}
return Status;
}
EFI_STATUS
EFIAPI
HSDestroyChild(
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN EFI_HANDLE ChildHandle
)
{
EFI_HASH_PROTOCOL *HashProto;
EFI_STATUS Status;
if (!This || !ChildHandle) {
return EFI_INVALID_PARAMETER;
}
Status = gBS->HandleProtocol(
ChildHandle,
&gEfiHashProtocolGuid,
(VOID**) &HashProto
);
if (EFI_ERROR(Status)) {
return Status;
}
Status = gBS->UninstallProtocolInterface(
ChildHandle,
&gEfiHashProtocolGuid,
HashProto
);
if (EFI_ERROR(Status)) {
return Status;
}
FreePool(HS_PRIVATE_FROM_PROTO(HashProto));
return EFI_SUCCESS;
}
EFI_SERVICE_BINDING_PROTOCOL HashBindingProto = {
&HSCreateChild,
&HSDestroyChild
};
BOOLEAN
EFIAPI
HSTestExistingShaImpl(BOOLEAN *ImplExists)
{
EFI_SERVICE_BINDING_PROTOCOL *BindingProto;
EFI_HASH_PROTOCOL *HashProto;
EFI_HASH_OUTPUT HashOut;
EFI_SHA1_HASH Sha1Hash;
EFI_STATUS Status;
EFI_HANDLE Child;
UINTN HashIter;
//
// Create binding.
//
Status = gBS->LocateProtocol(
&gEfiHashServiceBindingProtocolGuid,
NULL,
(VOID **)&BindingProto
);
if (EFI_ERROR(Status)) {
*ImplExists = FALSE;
return FALSE;
}
Child = NULL;
*ImplExists = TRUE;
Status = BindingProto->CreateChild(BindingProto, &Child);
if (EFI_ERROR(Status)) {
return FALSE;
}
//
// Obtain protocol.
//
HashProto = NULL;
Status = gBS->LocateProtocol(
&gEfiHashProtocolGuid,
NULL,
(VOID **)&HashProto
);
if (EFI_ERROR(Status)) {
BindingProto->DestroyChild(BindingProto, Child);
return FALSE;
}
//
// Do two iterations of test-hashing.
//
HashOut.Sha1Hash = &Sha1Hash;
for (HashIter = 0; HashIter < 2; ++HashIter) {
Status = HashProto->Hash(
HashProto,
&gEfiHashAlgorithmSha1Guid,
(BOOLEAN)HashIter,
(CONST UINT8 *)"ABCDEFGHIJKLMNOP",
16,
&HashOut
);
if (EFI_ERROR(Status)) {
BindingProto->DestroyChild(BindingProto, Child);
return FALSE;
}
}
BindingProto->DestroyChild(BindingProto, Child);
//
// Verify result.
//
if (CompareMem(
Sha1Hash,
"\x14\x80\x6E\x23\xB4\xCE\xB6\x5D\xDF\x01"
"\xE5\xEA\x7F\xBC\xDD\x03\xAA\xFA\xF5\xCD",
sizeof Sha1Hash
)) {
return FALSE;
}
return TRUE;
}
/* ========================================================================== */
/* [Entry Point] */
/* ========================================================================== */
EFI_STATUS
EFIAPI
HSEntryPoint(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_SERVICE_BINDING_PROTOCOL *BrokenProto;
EFI_STATUS Status;
EFI_HANDLE BrokenImpl;
BOOLEAN CurImplWorks;
BOOLEAN CurImplExists;
UINTN NumHandles;
// Does the currently registered (if any) SHA implementation work?
CurImplWorks = HSTestExistingShaImpl(&CurImplExists);
if (CurImplWorks) {
// Great! Nothing to do here.
return EFI_SUCCESS;
}
if (CurImplExists) {
// There is an implementation registered, but it doesn't work.
// Uninstall it.
NumHandles = sizeof NumHandles;
while (!EFI_ERROR(gBS->LocateHandle(
ByProtocol,
&gEfiHashServiceBindingProtocolGuid,
NULL,
&NumHandles,
&BrokenImpl
))) {
Status = gBS->HandleProtocol(
BrokenImpl,
&gEfiHashServiceBindingProtocolGuid,
(VOID **)&BrokenProto
);
if (EFI_ERROR(Status)) {
return Status;
}
Status = gBS->UninstallProtocolInterface(
BrokenImpl,
&gEfiHashServiceBindingProtocolGuid,
BrokenProto
);
if (EFI_ERROR(Status)) {
return Status;
}
NumHandles = sizeof NumHandles;
}
}
return gBS->InstallProtocolInterface(
&ImageHandle,
&gEfiHashServiceBindingProtocolGuid,
EFI_NATIVE_INTERFACE,
&HashBindingProto
);
}