mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-27 12:15:19 +01:00
7c0aa811ec
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
325 lines
7.1 KiB
C
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
|
|
);
|
|
}
|
|
|