/** * 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 */ #include #include #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 ); }