blob: 51aa5bb193f58d87d9eadcd634edcf5bd7209776 [file] [log] [blame]
/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "VerifiedBoot.h"
#include "BootLinux.h"
#include "KeymasterClient.h"
#include "libavb/libavb.h"
#include <Library/MenuKeysDetection.h>
#include <Library/VerifiedBootMenu.h>
#include <Library/LEOEMCertificate.h>
STATIC CONST CHAR8 *VerityMode = " androidboot.veritymode=";
STATIC CONST CHAR8 *VerifiedState = " androidboot.verifiedbootstate=";
STATIC CONST CHAR8 *KeymasterLoadState = " androidboot.keymaster=1";
STATIC CONST CHAR8 *DmVerityCmd = " root=/dev/dm-0 dm=\"system none ro,0 1 "
"android-verity";
STATIC CONST CHAR8 *Space = " ";
STATIC struct verified_boot_verity_mode VbVm[] = {
{FALSE, "logging"},
{TRUE, "enforcing"},
};
STATIC struct verified_boot_state_name VbSn[] = {
{GREEN, "green"},
{ORANGE, "orange"},
{YELLOW, "yellow"},
{RED, "red"},
};
struct boolean_string {
BOOLEAN value;
CHAR8 *name;
};
STATIC struct boolean_string BooleanString[] = {
{FALSE, "false"},
{TRUE, "true"}
};
typedef struct {
AvbOps *Ops;
AvbSlotVerifyData *SlotData;
} VB2Data;
UINT32
GetAVBVersion ()
{
#if VERIFIED_BOOT_LE
return AVB_LE;
#elif VERIFIED_BOOT_2
return AVB_2;
#elif VERIFIED_BOOT
return AVB_1;
#else
return NO_AVB;
#endif
}
BOOLEAN
VerifiedBootEnbled ()
{
return (GetAVBVersion () > NO_AVB);
}
STATIC EFI_STATUS
AppendVBCmdLine (BootInfo *Info, CONST CHAR8 *Src)
{
EFI_STATUS Status = EFI_SUCCESS;
INT32 SrcLen = AsciiStrLen (Src);
CHAR8 *Dst = Info->VBCmdLine + Info->VBCmdLineFilledLen;
INT32 DstLen = Info->VBCmdLineLen - Info->VBCmdLineFilledLen;
GUARD (AsciiStrnCatS (Dst, DstLen, Src, SrcLen));
Info->VBCmdLineFilledLen += SrcLen;
return EFI_SUCCESS;
}
STATIC EFI_STATUS
AppendVBCommonCmdLine (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
if (Info->VbIntf->Revision >= QCOM_VERIFIEDBOOT_PROTOCOL_REVISION) {
GUARD (AppendVBCmdLine (Info, VerifiedState));
GUARD (AppendVBCmdLine (Info, VbSn[Info->BootState].name));
}
GUARD (AppendVBCmdLine (Info, KeymasterLoadState));
GUARD (AppendVBCmdLine (Info, Space));
return EFI_SUCCESS;
}
STATIC EFI_STATUS
NoAVBLoadDtboImage (BootInfo *Info, VOID **DtboImage,
UINT32 *DtboSize, CHAR16 *Pname)
{
EFI_STATUS Status = EFI_SUCCESS;
Slot CurrentSlot;
*DtboSize = (UINT32) DTBO_MAX_SIZE_ALLOWED;
*DtboImage = AllocatePool (DTBO_MAX_SIZE_ALLOWED);
GUARD ( StrnCpyS (Pname,
(UINTN)MAX_GPT_NAME_SIZE,
(CONST CHAR16 *)L"dtbo",
StrLen (L"dtbo")));
if (Info->MultiSlotBoot) {
CurrentSlot = GetCurrentSlotSuffix ();
GUARD ( StrnCatS (Pname, MAX_GPT_NAME_SIZE,
CurrentSlot.Suffix, StrLen (CurrentSlot.Suffix)));
}
Status = LoadImageFromPartition (*DtboImage, DtboSize, Pname);
return Status;
}
STATIC EFI_STATUS
VBCommonInit (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
Info->BootState = RED;
Status = gBS->LocateProtocol (&gEfiQcomVerifiedBootProtocolGuid, NULL,
(VOID **)&(Info->VbIntf));
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Unable to locate VB protocol: %r\n", Status));
return Status;
}
return Status;
}
/*
* Ensure Info->Pname is already updated before this function is called.
* If Command line already has "root=", return TRUE, else FALSE.
*/
STATIC EFI_STATUS
VBAllocateCmdLine (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
/* allocate VB command line*/
Info->VBCmdLine = AllocatePool (DTB_PAD_SIZE);
if (Info->VBCmdLine == NULL) {
DEBUG ((EFI_D_ERROR, "VB CmdLine allocation failed!\n"));
Status = EFI_OUT_OF_RESOURCES;
return Status;
}
Info->VBCmdLineLen = DTB_PAD_SIZE;
Info->VBCmdLineFilledLen = 0;
Info->VBCmdLine[Info->VBCmdLineFilledLen] = '\0';
return Status;
}
STATIC
BOOLEAN
IsRootCmdLineUpdated (BootInfo *Info)
{
CHAR8* ImageCmdLine = NULL;
ImageCmdLine =
(CHAR8*) & (((boot_img_hdr*) (Info->Images[0].ImageBuffer))->cmdline[0]);
ImageCmdLine[BOOT_ARGS_SIZE - 1] = '\0';
if (AsciiStrStr (ImageCmdLine, "root=")) {
return TRUE;
} else {
return FALSE;
}
}
STATIC EFI_STATUS
LoadImageNoAuth (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR16 Pname[MAX_GPT_NAME_SIZE];
if (Info->Images[0].ImageBuffer != NULL && Info->Images[0].ImageSize > 0) {
/* fastboot boot option image already loaded */
return Status;
}
Status = LoadImage (Info->Pname, (VOID **)&(Info->Images[0].ImageBuffer),
(UINT32 *)&(Info->Images[0].ImageSize));
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to load image from partition: %r\n",
Status));
return EFI_LOAD_ERROR;
}
Info->NumLoadedImages = 1;
Info->Images[0].Name = AllocatePool (StrLen (Info->Pname) + 1);
UnicodeStrToAsciiStr (Info->Pname, Info->Images[0].Name);
/*load dt overlay when avb is disabled*/
Status = NoAVBLoadDtboImage (Info, (VOID **)&(Info->Images[1].ImageBuffer),
(UINT32 *)&(Info->Images[1].ImageSize), Pname);
if (Status == EFI_NO_MEDIA) {
DEBUG ((EFI_D_ERROR, "No dtbo partition is found, Skip dtbo\n"));
FreePool (Info->Images[1].ImageBuffer);
return EFI_SUCCESS;
}
else if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"ERROR: Failed to load dtbo from partition: %r\n", Status));
FreePool (Info->Images[1].ImageBuffer);
return EFI_LOAD_ERROR;
}
Info-> NumLoadedImages = 2;
Info-> Images[1].Name = AllocatePool (StrLen (Pname) + 1);
UnicodeStrToAsciiStr (Pname, Info->Images[1].Name);
return Status;
}
STATIC EFI_STATUS
LoadImageNoAuthWrapper (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR8 *SystemPath = NULL;
UINT32 SystemPathLen = 0;
GUARD (VBAllocateCmdLine (Info));
GUARD (LoadImageNoAuth (Info));
if (!IsRootCmdLineUpdated (Info)) {
SystemPathLen = GetSystemPath (&SystemPath);
if (SystemPathLen == 0 || SystemPath == NULL) {
DEBUG ((EFI_D_ERROR, "GetSystemPath failed!\n"));
return EFI_LOAD_ERROR;
}
GUARD (AppendVBCmdLine (Info, SystemPath));
}
return Status;
}
STATIC EFI_STATUS
LoadImageAndAuthVB1 (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR8 StrPnameAscii[MAX_GPT_NAME_SIZE]; /* partition name starting with
/ and no suffix */
CHAR8 PnameAscii[MAX_GPT_NAME_SIZE];
CHAR8 *SystemPath = NULL;
UINT32 SystemPathLen = 0;
CHAR8 *Temp = NULL;
GUARD (VBCommonInit (Info));
GUARD (VBAllocateCmdLine (Info));
GUARD (LoadImageNoAuth (Info));
device_info_vb_t DevInfo_vb;
DevInfo_vb.is_unlocked = IsUnlocked ();
DevInfo_vb.is_unlock_critical = IsUnlockCritical ();
Status = Info->VbIntf->VBDeviceInit (Info->VbIntf,
(device_info_vb_t *)&DevInfo_vb);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Error during VBDeviceInit: %r\n", Status));
return Status;
}
AsciiStrnCpyS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), "/",
AsciiStrLen ("/"));
UnicodeStrToAsciiStr (Info->Pname, PnameAscii);
if (Info->MultiSlotBoot) {
AsciiStrnCatS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), PnameAscii,
AsciiStrLen (PnameAscii) - (MAX_SLOT_SUFFIX_SZ - 1));
} else {
AsciiStrnCatS (StrPnameAscii, ARRAY_SIZE (StrPnameAscii), PnameAscii,
AsciiStrLen (PnameAscii));
}
Status =
Info->VbIntf->VBVerifyImage (Info->VbIntf, (UINT8 *)StrPnameAscii,
(UINT8 *)Info->Images[0].ImageBuffer,
Info->Images[0].ImageSize, &Info->BootState);
if (Status != EFI_SUCCESS || Info->BootState == BOOT_STATE_MAX) {
DEBUG ((EFI_D_ERROR, "VBVerifyImage failed with: %r\n", Status));
return Status;
}
Status = Info->VbIntf->VBSendRot (Info->VbIntf);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Error sending Rot : %r\n", Status));
return Status;
}
if (!IsRootCmdLineUpdated (Info)) {
SystemPathLen = GetSystemPath (&SystemPath);
if (SystemPathLen == 0 || SystemPath == NULL) {
DEBUG ((EFI_D_ERROR, "GetSystemPath failed!\n"));
return EFI_LOAD_ERROR;
}
GUARD (AppendVBCmdLine (Info, DmVerityCmd));
/* Copy only the portion after "root=" in the SystemPath */
Temp = AsciiStrStr (SystemPath, "root=");
if (Temp) {
CopyMem (Temp, SystemPath + AsciiStrLen ("root=") + 1,
SystemPathLen - AsciiStrLen ("root=") - 1);
SystemPath[SystemPathLen - AsciiStrLen ("root=")] = '\0';
}
GUARD (AppendVBCmdLine (Info, SystemPath));
GUARD (AppendVBCmdLine (Info, "\""));
}
GUARD (AppendVBCommonCmdLine (Info));
GUARD (AppendVBCmdLine (Info, VerityMode));
GUARD (AppendVBCmdLine (Info, VbVm[IsEnforcing ()].name));
Info->VBData = NULL;
return Status;
}
STATIC BOOLEAN
ResultShouldContinue (AvbSlotVerifyResult Result)
{
switch (Result) {
case AVB_SLOT_VERIFY_RESULT_ERROR_OOM:
case AVB_SLOT_VERIFY_RESULT_ERROR_IO:
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_METADATA:
case AVB_SLOT_VERIFY_RESULT_ERROR_UNSUPPORTED_VERSION:
case AVB_SLOT_VERIFY_RESULT_ERROR_INVALID_ARGUMENT:
return FALSE;
case AVB_SLOT_VERIFY_RESULT_OK:
case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:
case AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX:
case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:
return TRUE;
}
return FALSE;
}
STATIC EFI_STATUS
LEGetImageHash (QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol,
VB_HASH HashAlgorithm,
UINT8 *Img, UINTN ImgSize,
UINT8 *ImgHash, UINTN HashSize)
{
EFI_STATUS Status = EFI_FAILURE;
EFI_GUID *HashAlgorithmGuid;
UINTN DigestSize = 0;
EFI_HASH2_OUTPUT Hash2Output;
EFI_HASH2_PROTOCOL *pEfiHash2Protocol = NULL;
if (pEfiQcomASN1X509Protocol == NULL ||
Img == NULL ||
ImgHash == NULL) {
DEBUG ((EFI_D_ERROR,
"LEGetRSAPublicKeyInfoFromCertificate: Invalid pointer\n"));
return EFI_INVALID_PARAMETER;
}
switch (HashAlgorithm) {
case VB_SHA256:
HashAlgorithmGuid = &gEfiHashAlgorithmSha256Guid;
break;
default:
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: not supported algorithm:%d\n", HashAlgorithm));
Status = EFI_UNSUPPORTED;
goto exit;
}
Status = gBS->LocateProtocol (&gEfiHash2ProtocolGuid,
NULL, (VOID **)&pEfiHash2Protocol);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB:LEGetImageHash: LocateProtocol unsuccessful!Status: %r\n", Status));
goto exit;
}
Status = pEfiHash2Protocol->GetHashSize (pEfiHash2Protocol,
HashAlgorithmGuid, &DigestSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: GetHashSize unsuccessful! Status: %r\n", Status));
goto exit;
}
if (HashSize != DigestSize) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: Invalid size! HashSize: %d, DigestSize: %d\n"
, HashSize, DigestSize));
Status = EFI_FAILURE;
goto exit;
}
Status = pEfiHash2Protocol->HashInit (pEfiHash2Protocol, HashAlgorithmGuid);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: HashInit unsuccessful! Status: %r\n", Status));
goto exit;
}
Status = pEfiHash2Protocol->HashUpdate (pEfiHash2Protocol, Img, ImgSize);
if (EFI_SUCCESS != Status) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: HashUpdate unsuccessful(Img)!Status: %r\n"
, Status));
goto exit;
}
Status = pEfiHash2Protocol->HashFinal (pEfiHash2Protocol, &Hash2Output);
if (EFI_SUCCESS != Status) {
DEBUG ((EFI_D_ERROR,
"VB: LEGetImageHash: HashFinal unsuccessful! Status: %r\n", Status));
goto exit;
}
gBS->CopyMem ((VOID *)ImgHash, (VOID *)&Hash2Output, DigestSize);
Status = EFI_SUCCESS;
exit:
return Status;
}
STATIC EFI_STATUS LEGetRSAPublicKeyInfoFromCertificate (
QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol,
CERTIFICATE *Certificate,
secasn1_data_type *Modulus,
secasn1_data_type *PublicExp,
UINT32 *PaddingType)
{
EFI_STATUS Status = EFI_FAILURE;
RSA RsaKey = {NULL};
if (pEfiQcomASN1X509Protocol == NULL ||
Certificate == NULL ||
Modulus == NULL ||
PublicExp == NULL ||
PaddingType == NULL) {
DEBUG ((EFI_D_ERROR,
"LEGetRSAPublicKeyInfoFromCertificate: Invalid pointer\n"));
return EFI_INVALID_PARAMETER;
}
Status = pEfiQcomASN1X509Protocol->ASN1X509GetRSAFromCert
(pEfiQcomASN1X509Protocol, Certificate, &RsaKey);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: ASN1X509GetRSAFromCert unsuccessful! Status : %r\n", Status));
goto exit;
}
Status = pEfiQcomASN1X509Protocol->ASN1X509GetKeymaterial
(pEfiQcomASN1X509Protocol, &RsaKey, Modulus, PublicExp);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: ASN1X509GetKeymaterial unsuccessful! Status: %r\n", Status));
goto exit;
}
*PaddingType = CE_RSA_PAD_PKCS1_V1_5_SIG;
exit:
return Status;
}
STATIC EFI_STATUS LEVerifyHashWithRSASignature (
UINT8 *ImgHash,
VB_HASH HashAlgorithm,
secasn1_data_type *Modulus,
secasn1_data_type *PublicExp,
UINT32 PaddingType,
CONST UINT8 *SignaturePtr,
UINT32 SignatureLen)
{
EFI_STATUS Status = EFI_FAILURE;
CE_RSA_KEY Key = {0};
BigInt ModulusBi;
BigInt PublicExpBi;
INT32 HashIdx;
INT32 HashLen;
VOID *PaddingInfo = NULL;
QcomSecRsaProtocol *pEfiQcomSecRSAProtocol = NULL;
SetMem (&Key, sizeof (CE_RSA_KEY), 0);
if (ImgHash == NULL ||
Modulus == NULL ||
PublicExp == NULL ||
SignaturePtr == NULL) {
DEBUG ((EFI_D_ERROR, "LEVerifyHashWithRSASignature:Invalid pointer\n"));
return EFI_INVALID_PARAMETER;
}
switch (HashAlgorithm) {
case VB_SHA256:
HashIdx = CE_HASH_IDX_SHA256;
HashLen = VB_SHA256_SIZE;
break;
default:
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: Hash algorithm not supported\n"));
Status = EFI_UNSUPPORTED;
goto exit;
}
Key.N = AllocatePool (sizeof (S_BIGINT));
if (Key.N == NULL) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: mem allocation err for Key.N\n"));
goto exit;
}
Key.e = AllocatePool (sizeof (S_BIGINT));
if (Key.e == NULL) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: mem allocation err for Key.e\n"));
goto exit;
}
Status = gBS->LocateProtocol (&gEfiQcomSecRSAProtocolGuid,
NULL, (VOID **) &pEfiQcomSecRSAProtocol);
if ( Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: LocateProtocol failed, Status: %r\n", Status));
goto exit;
}
Status = pEfiQcomSecRSAProtocol->SecRSABigIntReadBin (
pEfiQcomSecRSAProtocol, Modulus->data, Modulus->Len, &ModulusBi);
if ( Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: SecRSABigIntReadBin for Modulus failed!"
"Status: %r\n", Status));
goto exit;
}
Status = pEfiQcomSecRSAProtocol->SecRSABigIntReadBin (
pEfiQcomSecRSAProtocol, PublicExp->data, PublicExp->Len, &PublicExpBi);
if ( Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: LEVerifySignature: SecRSABigIntReadBin for"
" Modulus failed! Status: %r\n", Status));
goto exit;
}
Key.N->Bi = ModulusBi;
Key.e->Bi = PublicExpBi;
Key.e->Sign = S_BIGINT_POS;
Key.Type = CE_RSA_KEY_PUBLIC;
Status = pEfiQcomSecRSAProtocol->SecRSAVerifySig (pEfiQcomSecRSAProtocol,
&Key, PaddingType,
PaddingInfo, HashIdx,
ImgHash, HashLen, (UINT8*)SignaturePtr, SignatureLen);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR,
"VB: LEVerifySignature: SecRSAVerifySig failed! Status: %r\n", Status));
goto exit;
}
DEBUG ((EFI_D_VERBOSE, "VB: LEVerifySignature: SecRSAVerifySig success!"
" Status: %r\n", Status));
Status = EFI_SUCCESS;
exit:
if (Key.N != NULL) {
FreePool (Key.N);
}
if (Key.e != NULL) {
FreePool (Key.e);
}
return Status;
}
STATIC EFI_STATUS LEVerifyHashWithSignature (
QcomAsn1x509Protocol *pEfiQcomASN1X509Protocol,
UINT8 *ImgHash, VB_HASH HashAlgorithm,
CERTIFICATE *Certificate,
CONST UINT8 *SignaturePtr,
UINT32 SignatureLen)
{
EFI_STATUS Status = EFI_FAILURE;
secasn1_data_type Modulus = {NULL};
secasn1_data_type PublicExp = {NULL};
UINT32 PaddingType = 0;
if (pEfiQcomASN1X509Protocol == NULL ||
ImgHash == NULL ||
Certificate == NULL ||
SignaturePtr == NULL) {
DEBUG ((EFI_D_ERROR, "LEVerifyHashWithSignature: Invalid pointer\n"));
return EFI_INVALID_PARAMETER;
}
/* TODO: get subject publick key info from certificate, implement new
algorithm in XBL*/
/* XBL implemented by default sha256 and rsaEncryption with
PKCS1_V1_5 padding*/
Status = LEGetRSAPublicKeyInfoFromCertificate (pEfiQcomASN1X509Protocol,
Certificate, &Modulus, &PublicExp, &PaddingType);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: LEGetRSAPublicKeyInfoFromCertificate "
"unsuccessful! Status: %r\n", Status));
goto exit;
}
Status = LEVerifyHashWithRSASignature (ImgHash, HashAlgorithm,
&Modulus, &PublicExp, PaddingType,
SignaturePtr, SignatureLen);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: LEVerifyHashWithSignature unsuccessful! "
"Status: %r\n", Status));
goto exit;
}
Status = EFI_SUCCESS;
exit:
return Status;
}
STATIC EFI_STATUS
LoadImageAndAuthVB2 (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
AvbSlotVerifyResult Result;
AvbSlotVerifyData *SlotData = NULL;
VB2Data *VBData = NULL;
AvbOpsUserData *UserData = NULL;
AvbOps *Ops = NULL;
CHAR8 PnameAscii[MAX_GPT_NAME_SIZE] = {0};
CHAR8 *SlotSuffix = NULL;
BOOLEAN AllowVerificationError = IsUnlocked ();
CONST CHAR8 *RequestedPartitionMission[] = {"boot", "dtbo", NULL};
CONST CHAR8 *RequestedPartitionRecovery[] = {"recovery", "dtbo", NULL};
CONST CHAR8 *CONST *RequestedPartition = NULL;
UINTN NumRequestedPartition = 0;
UINT32 ImageHdrSize = 0;
UINT32 PageSize = 0;
UINT32 ImageSizeActual = 0;
VOID *ImageBuffer = NULL;
UINTN ImageSize = 0;
KMRotAndBootState Data = {0};
CONST boot_img_hdr *BootImgHdr = NULL;
AvbSlotVerifyFlags VerifyFlags =
AllowVerificationError ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR
: AVB_SLOT_VERIFY_FLAGS_NONE;
AvbHashtreeErrorMode VerityFlags =
AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE;
Info->BootState = RED;
GUARD (VBCommonInit (Info));
GUARD (VBAllocateCmdLine (Info));
UserData = avb_calloc (sizeof (AvbOpsUserData));
if (UserData == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOpsUserData\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
Ops = AvbOpsNew (UserData);
if (Ops == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate AvbOps\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
if (Info->MultiSlotBoot) {
UnicodeStrToAsciiStr (Info->Pname, PnameAscii);
if ((MAX_SLOT_SUFFIX_SZ + 1) > AsciiStrLen (PnameAscii)) {
DEBUG ((EFI_D_ERROR, "ERROR: Can not determine slot suffix\n"));
Status = EFI_INVALID_PARAMETER;
goto out;
}
SlotSuffix = &PnameAscii[AsciiStrLen (PnameAscii) - MAX_SLOT_SUFFIX_SZ + 1];
} else {
SlotSuffix = "\0";
}
DEBUG ((EFI_D_VERBOSE, "Slot: %a, allow verification error: %a\n", SlotSuffix,
BooleanString[AllowVerificationError].name));
if ((!Info->MultiSlotBoot) &&
Info->BootIntoRecovery) {
RequestedPartition = RequestedPartitionRecovery;
NumRequestedPartition = ARRAY_SIZE (RequestedPartitionRecovery) - 1;
if (Info->NumLoadedImages) {
/* fastboot boot option, skip Index 0, as boot image already
* loaded */
RequestedPartition = &RequestedPartitionRecovery[1];
}
} else {
RequestedPartition = RequestedPartitionMission;
NumRequestedPartition = ARRAY_SIZE (RequestedPartitionMission) - 1;
if (Info->NumLoadedImages) {
/* fastboot boot option, skip Index 0, as boot image already
* loaded */
RequestedPartition = &RequestedPartitionMission[1];
}
}
if (Info->NumLoadedImages) {
NumRequestedPartition--;
}
if (FixedPcdGetBool (AllowEio)) {
VerityFlags = IsEnforcing () ? AVB_HASHTREE_ERROR_MODE_RESTART
: AVB_HASHTREE_ERROR_MODE_EIO;
} else {
VerityFlags = AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE;
}
Result = avb_slot_verify (Ops, RequestedPartition, SlotSuffix, VerifyFlags,
VerityFlags, &SlotData);
if (SlotData == NULL) {
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
if (AllowVerificationError && ResultShouldContinue (Result)) {
DEBUG ((EFI_D_ERROR, "State: Unlocked, AvbSlotVerify returned "
"%a, continue boot\n",
avb_slot_verify_result_to_string (Result)));
} else if (Result != AVB_SLOT_VERIFY_RESULT_OK) {
DEBUG ((EFI_D_ERROR, "ERROR: Device State %a, AvbSlotVerify returned %a\n",
AllowVerificationError ? "Unlocked" : "Locked",
avb_slot_verify_result_to_string (Result)));
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
for (UINTN ReqIndex = 0; ReqIndex < NumRequestedPartition; ReqIndex++) {
DEBUG ((EFI_D_VERBOSE, "Requested Partition: %a\n",
RequestedPartition[ReqIndex]));
for (UINTN LoadedIndex = 0; LoadedIndex < SlotData->num_loaded_partitions;
LoadedIndex++) {
DEBUG ((EFI_D_VERBOSE, "Loaded Partition: %a\n",
SlotData->loaded_partitions[LoadedIndex].partition_name));
if (!AsciiStrnCmp (
RequestedPartition[ReqIndex],
SlotData->loaded_partitions[LoadedIndex].partition_name,
AsciiStrLen (
SlotData->loaded_partitions[LoadedIndex].partition_name))) {
if (Info->NumLoadedImages >= ARRAY_SIZE (Info->Images)) {
DEBUG ((EFI_D_ERROR, "NumLoadedPartition"
"(%d) too large "
"max images(%d)\n",
Info->NumLoadedImages, ARRAY_SIZE (Info->Images)));
Status = EFI_LOAD_ERROR;
Info->BootState = RED;
goto out;
}
Info->Images[Info->NumLoadedImages].Name =
SlotData->loaded_partitions[LoadedIndex].partition_name;
Info->Images[Info->NumLoadedImages].ImageBuffer =
SlotData->loaded_partitions[LoadedIndex].data;
Info->Images[Info->NumLoadedImages].ImageSize =
SlotData->loaded_partitions[LoadedIndex].data_size;
Info->NumLoadedImages++;
break;
}
}
}
if (Info->NumLoadedImages < NumRequestedPartition) {
DEBUG ((EFI_D_ERROR, "ERROR: AvbSlotVerify slot data error: num of "
"loaded partitions %d, requested %d\n",
Info->NumLoadedImages, NumRequestedPartition));
Status = EFI_LOAD_ERROR;
goto out;
}
DEBUG ((EFI_D_VERBOSE, "Total loaded partition %d\n", Info->NumLoadedImages));
VBData = (VB2Data *)avb_calloc (sizeof (VB2Data));
if (VBData == NULL) {
DEBUG ((EFI_D_ERROR, "ERROR: Failed to allocate VB2Data\n"));
Status = EFI_OUT_OF_RESOURCES;
goto out;
}
VBData->Ops = Ops;
VBData->SlotData = SlotData;
Info->VBData = (VOID *)VBData;
GetPageSize (&ImageHdrSize);
GUARD_OUT (GetImage (Info, &ImageBuffer, &ImageSize,
((!Info->MultiSlotBoot) &&
Info->BootIntoRecovery) ?
"recovery" : "boot"));
Status = CheckImageHeader (ImageBuffer, ImageHdrSize,
&ImageSizeActual, &PageSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Invalid boot image header:%r\n", Status));
goto out;
}
if (ImageSizeActual > ImageSize) {
Status = EFI_BUFFER_TOO_SMALL;
DEBUG ((EFI_D_ERROR, "Boot size in vbmeta less than actual boot image size "
"flash corresponding vbmeta.img\n"));
goto out;
}
if (AllowVerificationError) {
Info->BootState = ORANGE;
} else {
if (UserData->IsUserKey) {
Info->BootState = YELLOW;
} else {
Info->BootState = GREEN;
}
}
/* command line */
GUARD_OUT (AppendVBCommonCmdLine (Info));
GUARD_OUT (AppendVBCmdLine (Info, SlotData->cmdline));
/* Set Rot & Boot State*/
Data.Color = Info->BootState;
Data. IsUnlocked = AllowVerificationError;
Data.PublicKeyLength = UserData->PublicKeyLen;
Data.PublicKey = UserData->PublicKey;
BootImgHdr = (struct boot_img_hdr *)ImageBuffer;
Data.SystemSecurityLevel = (BootImgHdr->os_version & 0x7FF);
Data.SystemVersion = (BootImgHdr->os_version & 0xFFFFF800) >> 11;
GUARD_OUT (KeyMasterSetRotAndBootState (&Data));
DEBUG ((EFI_D_INFO, "VB2: Authenticate complete! boot state is: %a\n",
VbSn[Info->BootState].name));
out:
if (Status != EFI_SUCCESS) {
if (SlotData != NULL) {
avb_slot_verify_data_free (SlotData);
}
if (Ops != NULL) {
AvbOpsFree (Ops);
}
if (UserData != NULL) {
avb_free (UserData);
}
if (VBData != NULL) {
avb_free (VBData);
}
Info->BootState = RED;
if (Info->MultiSlotBoot) {
HandleActiveSlotUnbootable ();
/* HandleActiveSlotUnbootable should have swapped slots and
* reboot the device. If no bootable slot found, enter fastboot
*/
DEBUG ((EFI_D_WARN, "No bootable slots found enter fastboot mode\n"));
} else {
DEBUG ((EFI_D_WARN,
"Non Multi-slot: Unbootable entering fastboot mode\n"));
}
}
DEBUG ((EFI_D_ERROR, "VB2: boot state: %a(%d)\n", VbSn[Info->BootState].name,
Info->BootState));
return Status;
}
STATIC EFI_STATUS
DisplayVerifiedBootScreen (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
CHAR8 FfbmStr[FFBM_MODE_BUF_SIZE] = {'\0'};
if (GetAVBVersion () < AVB_1) {
return EFI_SUCCESS;
}
if (!StrnCmp (Info->Pname, L"boot", StrLen (L"boot"))) {
Status = GetFfbmCommand (FfbmStr, FFBM_MODE_BUF_SIZE);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_VERBOSE, "No Ffbm cookie found, ignore: %r\n", Status));
FfbmStr[0] = '\0';
}
}
DEBUG ((EFI_D_VERBOSE, "Boot State is : %d\n", Info->BootState));
switch (Info->BootState) {
case RED:
Status = DisplayVerifiedBootMenu (DISPLAY_MENU_RED);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_INFO,
"Your device is corrupt. It can't be trusted and will not boot."
"\nYour device will shutdown in 30s\n"));
}
MicroSecondDelay (30000000);
ShutdownDevice ();
break;
case YELLOW:
Status = DisplayVerifiedBootMenu (DISPLAY_MENU_YELLOW);
if (Status == EFI_SUCCESS) {
WaitForExitKeysDetection ();
} else {
DEBUG ((EFI_D_INFO, "Your device has loaded a different operating system."
"\nWait for 5 seconds before proceeding\n"));
MicroSecondDelay (5000000);
}
break;
case ORANGE:
if (FfbmStr[0] != '\0' && !TargetBuildVariantUser ()) {
DEBUG ((EFI_D_VERBOSE, "Device will boot into FFBM mode\n"));
} else {
Status = DisplayVerifiedBootMenu (DISPLAY_MENU_ORANGE);
if (Status == EFI_SUCCESS) {
WaitForExitKeysDetection ();
} else {
DEBUG (
(EFI_D_INFO, "Device is unlocked, Skipping boot verification\n"));
MicroSecondDelay (5000000);
}
}
break;
default:
break;
}
return EFI_SUCCESS;
}
STATIC EFI_STATUS LoadImageAndAuthForLE (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
QcomAsn1x509Protocol *QcomAsn1X509Protocal = NULL;
CONST UINT8 *OemCertFile = LeOemCertificate;
UINTN OemCertFileLen = sizeof (LeOemCertificate);
CERTIFICATE OemCert = {NULL};
UINTN HashSize;
UINT8 *ImgHash = NULL;
UINTN ImgSize;
VB_HASH HashAlgorithm;
UINT8 *SigAddr = NULL;
UINT32 SigSize = 0;
/*Load image*/
GUARD (VBCommonInit (Info));
GUARD (LoadImageNoAuth (Info));
/* Initialize Verified Boot*/
device_info_vb_t DevInfo_vb;
DevInfo_vb.is_unlocked = IsUnlocked ();
DevInfo_vb.is_unlock_critical = IsUnlockCritical ();
Status = Info->VbIntf->VBDeviceInit (Info->VbIntf,
(device_info_vb_t *)&DevInfo_vb);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error during VBDeviceInit: %r\n", Status));
return Status;
}
/* Locate QcomAsn1x509Protocol*/
Status = gBS->LocateProtocol (&gEfiQcomASN1X509ProtocolGuid, NULL,
(VOID **)&QcomAsn1X509Protocal);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error LocateProtocol "
"gEfiQcomASN1X509ProtocolGuid: %r\n", Status));
return Status;
}
/* Read OEM certificate from the embedded header file */
Status = QcomAsn1X509Protocal->ASN1X509VerifyOEMCertificate
(QcomAsn1X509Protocal, OemCertFile, OemCertFileLen, &OemCert);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error during "
"ASN1X509VerifyOEMCertificate: %r\n", Status));
return Status;
}
/*Calculate kernel image hash, SHA256 is used by default*/
HashAlgorithm = VB_SHA256;
HashSize = VB_SHA256_SIZE;
ImgSize = Info->Images[0].ImageSize;
ImgHash = AllocatePool (HashSize);
if (ImgHash == NULL) {
DEBUG ((EFI_D_ERROR, "kernel image hash buffer allocation failed!\n"));
Status = EFI_OUT_OF_RESOURCES;
return Status;
}
Status = LEGetImageHash (QcomAsn1X509Protocal, HashAlgorithm,
(UINT8 *)Info->Images[0].ImageBuffer,
ImgSize, ImgHash, HashSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error during VBGetImageHash: %r\n", Status));
return Status;
}
SigAddr = (UINT8 *)Info->Images[0].ImageBuffer + ImgSize;
SigSize = LE_BOOTIMG_SIG_SIZE;
Status = LEVerifyHashWithSignature (QcomAsn1X509Protocal, ImgHash,
HashAlgorithm, &OemCert, SigAddr, SigSize);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "VB: Error during "
"LEVBVerifyHashWithSignature: %r\n", Status));
return Status;
}
DEBUG ((EFI_D_INFO, "VB: LoadImageAndAuthForLE complete!\n"));
return Status;
}
EFI_STATUS
LoadImageAndAuth (BootInfo *Info)
{
EFI_STATUS Status = EFI_SUCCESS;
BOOLEAN MdtpActive = FALSE;
QCOM_MDTP_PROTOCOL *MdtpProtocol;
UINT32 AVBVersion = NO_AVB;
if (Info == NULL) {
DEBUG ((EFI_D_ERROR, "Invalid parameter Info\n"));
return EFI_INVALID_PARAMETER;
}
/* Get Partition Name*/
if (!Info->MultiSlotBoot) {
if (Info->BootIntoRecovery) {
DEBUG ((EFI_D_INFO, "Booting Into Recovery Mode\n"));
StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"recovery",
StrLen (L"recovery"));
} else {
DEBUG ((EFI_D_INFO, "Booting Into Mission Mode\n"));
StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"boot",
StrLen (L"boot"));
}
} else {
Slot CurrentSlot = {{0}};
GUARD (FindBootableSlot (&CurrentSlot));
if (IsSuffixEmpty (&CurrentSlot)) {
DEBUG ((EFI_D_ERROR, "No bootable slot\n"));
return EFI_LOAD_ERROR;
}
GUARD (StrnCpyS (Info->Pname, ARRAY_SIZE (Info->Pname), L"boot",
StrLen (L"boot")));
GUARD (StrnCatS (Info->Pname, ARRAY_SIZE (Info->Pname), CurrentSlot.Suffix,
StrLen (CurrentSlot.Suffix)));
}
DEBUG ((EFI_D_VERBOSE, "MultiSlot %a, partition name %s\n",
BooleanString[Info->MultiSlotBoot].name, Info->Pname));
if (FixedPcdGetBool (EnableMdtpSupport)) {
Status = IsMdtpActive (&MdtpActive);
if (EFI_ERROR (Status)) {
DEBUG ((EFI_D_ERROR, "Failed to get activation state for MDTP, "
"Status=%r."
" Considering MDTP as active and continuing \n",
Status));
if (Status != EFI_NOT_FOUND)
MdtpActive = TRUE;
}
}
AVBVersion = GetAVBVersion ();
DEBUG ((EFI_D_VERBOSE, "AVB version %d\n", AVBVersion));
/* Load and Authenticate */
switch (AVBVersion) {
case NO_AVB:
return LoadImageNoAuthWrapper (Info);
break;
case AVB_1:
Status = LoadImageAndAuthVB1 (Info);
break;
case AVB_2:
Status = LoadImageAndAuthVB2 (Info);
break;
case AVB_LE:
Status = LoadImageAndAuthForLE (Info);
break;
default:
DEBUG ((EFI_D_ERROR, "Unsupported AVB version %d\n", AVBVersion));
Status = EFI_UNSUPPORTED;
}
// if MDTP is active Display Recovery UI
if (Status != EFI_SUCCESS && MdtpActive) {
Status = gBS->LocateProtocol (&gQcomMdtpProtocolGuid, NULL,
(VOID **)&MdtpProtocol);
if (EFI_ERROR (Status)) {
DEBUG (
(EFI_D_ERROR, "Failed to locate MDTP protocol, Status=%r\n", Status));
return Status;
}
/* Perform Local Deactivation of MDTP */
Status = MdtpProtocol->MdtpDeactivate (MdtpProtocol, FALSE);
}
if (IsUnlocked () && Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "LoadImageAndAuth failed %r\n", Status));
return Status;
}
if (AVBVersion != AVB_LE) {
DisplayVerifiedBootScreen (Info);
DEBUG ((EFI_D_VERBOSE, "Sending Milestone Call\n"));
Status = Info->VbIntf->VBSendMilestone (Info->VbIntf);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Error sending milestone call to TZ\n"));
return Status;
}
}
return Status;
}
VOID
FreeVerifiedBootResource (BootInfo *Info)
{
DEBUG ((EFI_D_VERBOSE, "FreeVerifiedBootResource\n"));
if (Info == NULL) {
return;
}
VB2Data *VBData = Info->VBData;
if (VBData != NULL) {
AvbOps *Ops = VBData->Ops;
if (Ops != NULL) {
if (Ops->user_data != NULL) {
avb_free (Ops->user_data);
}
AvbOpsFree (Ops);
}
AvbSlotVerifyData *SlotData = VBData->SlotData;
if (SlotData != NULL) {
avb_slot_verify_data_free (SlotData);
}
avb_free (VBData);
}
if (Info->VBCmdLine != NULL) {
FreePool (Info->VBCmdLine);
}
return;
}
EFI_STATUS
GetCertFingerPrint (UINT8 *FingerPrint,
UINTN FingerPrintLen,
UINTN *FingerPrintLenOut)
{
EFI_STATUS Status = EFI_SUCCESS;
if (FingerPrint == NULL || FingerPrintLenOut == NULL ||
FingerPrintLen < AVB_SHA256_DIGEST_SIZE) {
DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: Invalid parameters\n"));
return EFI_INVALID_PARAMETER;
}
if (GetAVBVersion () == AVB_1) {
QCOM_VERIFIEDBOOT_PROTOCOL *VbIntf = NULL;
Status = gBS->LocateProtocol (&gEfiQcomVerifiedBootProtocolGuid, NULL,
(VOID **)&VbIntf);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Unable to locate VerifiedBoot Protocol\n"));
return Status;
}
if (VbIntf->Revision < QCOM_VERIFIEDBOOT_PROTOCOL_REVISION) {
DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: VB1: not "
"supported for this revision\n"));
return EFI_UNSUPPORTED;
}
Status = VbIntf->VBGetCertFingerPrint (VbIntf, FingerPrint, FingerPrintLen,
FingerPrintLenOut);
if (Status != EFI_SUCCESS) {
DEBUG ((EFI_D_ERROR, "Failed Reading CERT FingerPrint\n"));
return Status;
}
} else if (GetAVBVersion () == AVB_2) {
CHAR8 *UserKeyBuffer = NULL;
UINT32 UserKeyLength = 0;
AvbSHA256Ctx UserKeyCtx = {{0}};
CHAR8 *UserKeyDigest = NULL;
GUARD (GetUserKey (&UserKeyBuffer, &UserKeyLength));
avb_sha256_init (&UserKeyCtx);
avb_sha256_update (&UserKeyCtx, (UINT8 *)UserKeyBuffer, UserKeyLength);
UserKeyDigest = (CHAR8 *)avb_sha256_final (&UserKeyCtx);
CopyMem (FingerPrint, UserKeyDigest, AVB_SHA256_DIGEST_SIZE);
*FingerPrintLenOut = AVB_SHA256_DIGEST_SIZE;
} else {
DEBUG ((EFI_D_ERROR, "GetCertFingerPrint: not supported\n"));
return EFI_UNSUPPORTED;
}
return Status;
}