/** @file This library is the PeilessSec version of the HashLib. It will initiate a hash on each supported hash algorithm via the TPM or TransferList. Copyright (c) 2025, Arm Limited. All rights reserved.
Copyright (c), Microsoft Corporation SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct { TPM_ALG_ID AlgoId; UINT32 Mask; } TPM2_HASH_MASK; STATIC TPM2_HASH_MASK mTpm2HashMask[] = { { TPM_ALG_SHA1, HASH_ALG_SHA1 }, { TPM_ALG_SHA256, HASH_ALG_SHA256 }, { TPM_ALG_SHA384, HASH_ALG_SHA384 }, { TPM_ALG_SHA512, HASH_ALG_SHA512 }, }; /** Get hash algorithm from mask. @param[in] HashMask Hash mask @return Hash algorithm **/ TPMI_ALG_HASH EFIAPI GetHashAlgoFromMask ( IN UINT32 HashMask ) { UINTN Index; for (Index = 0; Index < ARRAY_SIZE (mTpm2HashMask); Index++) { if (mTpm2HashMask[Index].Mask == HashMask) { return mTpm2HashMask[Index].AlgoId; } } return TPM_ALG_ERROR; } /** Get internal hash info size. @return Hash info size **/ UINTN EFIAPI GetHashInfoSize ( VOID ) { return ARRAY_SIZE (mTpm2HashMask); } /** Get hash mask at specified index. @param[in] Index Index requested @return Hash mask at the specified index **/ UINT32 EFIAPI GetHashMaskAtIndex ( IN UINT32 Index ) { return mTpm2HashMask[Index].Mask; } /** Get transfer list header. @param[out] TransferList Transfer list header @retval EFI_SUCCESS Transfer list is found. @retval EFI_NOT_FOUND Transfer list is not found. **/ STATIC EFI_STATUS EFIAPI GetTransferList ( OUT TRANSFER_LIST_HEADER **TransferList ) { VOID *HobList; EFI_HOB_GUID_TYPE *GuidHob; UINTN *GuidHobData; *TransferList = NULL; HobList = GetHobList (); if (HobList == NULL) { return EFI_NOT_FOUND; } GuidHob = GetNextGuidHob (&gArmTransferListHobGuid, HobList); if (GuidHob == NULL) { return EFI_NOT_FOUND; } GuidHobData = GET_GUID_HOB_DATA (GuidHob); *TransferList = (TRANSFER_LIST_HEADER *)(*GuidHobData); return EFI_SUCCESS; } /** Get supported hash bitmap from the Transfer List and by querying the TPM for supported hashing algorithms. @param[out] SupportedHashBitmap @retval EFI_SUCCESS Bitmap populated @retval EFI_INVALID_PARAMETER Invalid pointer @retval EFI_NOT_FOUND Error accessing data @retval EFI_DEVICE_ERROR TPM device error **/ STATIC EFI_STATUS EFIAPI GetSupportedHashBitmap ( OUT UINT32 *SupportedHashBitmap ) { EFI_STATUS Status; TRANSFER_LIST_HEADER *TransferList; VOID *EventLog; UINTN EventLogSize; TCG_EfiSpecIDEventStruct *TcgEfiSpecIdEventStruct; TCG_EfiSpecIdEventAlgorithmSize *DigestSize; UINTN Idx; UINT32 NumberOfAlgorithms; UINT32 TpmHashBitmap; UINT32 PcrHashBitmap; BOOLEAN UseTlHashBitmap; if (SupportedHashBitmap == NULL) { return EFI_INVALID_PARAMETER; } Status = Tpm2RequestUseTpm (); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: TPM2 not detected!\n", __func__)); return Status; } Status = Tpm2GetCapabilitySupportedAndActivePcrs (&TpmHashBitmap, &PcrHashBitmap); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Failed to get Tpm capability... Status: %r\n", __func__, Status)); return Status; } // NOTE: TpmHashBitmap is what the TPM supports, PcrHashBitmap is what is currently active DEBUG ((DEBUG_INFO, "TpmHashBitmap: %x, PcrHashBitmap: %x\n", TpmHashBitmap, PcrHashBitmap)); UseTlHashBitmap = FALSE; Status = GetTransferList (&TransferList); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Unable to acquire Transfer list...\n", __func__)); goto Exit; } if (TransferListCheckHeader (TransferList) == TRANSFER_LIST_OPS_INVALID) { DEBUG ((DEBUG_ERROR, "%a: Invalid Transfer list...\n", __func__)); goto Exit; } Status = TransferListGetEventLog (TransferList, &EventLog, &EventLogSize, NULL); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: No data for TPM event log...\n", __func__)); goto Exit; } UseTlHashBitmap = TRUE; TcgEfiSpecIdEventStruct = (TCG_EfiSpecIDEventStruct *) (EventLog + OFFSET_OF (TCG_PCR_EVENT, Event)); CopyMem (&NumberOfAlgorithms, TcgEfiSpecIdEventStruct + 1, sizeof (NumberOfAlgorithms)); DigestSize = (TCG_EfiSpecIdEventAlgorithmSize *)((UINT8 *)TcgEfiSpecIdEventStruct + sizeof (*TcgEfiSpecIdEventStruct) + sizeof (NumberOfAlgorithms)); DEBUG ((DEBUG_INFO, "%a: Transfer list TPM event log available\n", __func__)); // Update the supported hash bitmap based on the info from the TCG event log for (Idx = 0; Idx < NumberOfAlgorithms; Idx++) { *SupportedHashBitmap |= Tpm2GetHashMaskFromAlgo (DigestSize[Idx].algorithmId); } // The active PCR banks should match what is reported in the TCG event log if (PcrHashBitmap != *SupportedHashBitmap) { DEBUG ((DEBUG_ERROR, "Active PCRs & Transfer List mismatch!\n")); UseTlHashBitmap = FALSE; } Exit: if (!UseTlHashBitmap) { // Use the information from the TPM to update the supported hash bitmap *SupportedHashBitmap = TpmHashBitmap; DEBUG ((DEBUG_INFO, "%a: No Transfer List or TPM event log available\n", __func__)); } *SupportedHashBitmap &= PcrHashBitmap; if (*SupportedHashBitmap == 0x00) { DEBUG ((DEBUG_ERROR, "%a: No supported Hash algorithm with event log Spec...!\n", __func__)); } return EFI_SUCCESS; } /** Start hash sequence. @param HashHandle Hash handle. @retval EFI_SUCCESS Hash sequence start and HandleHandle returned. @retval EFI_OUT_OF_RESOURCES No enough resource to start hash. **/ EFI_STATUS EFIAPI HashStart ( OUT HASH_HANDLE *HashHandle ) { EFI_STATUS Status; TPM_ALG_ID AlgoId; UINT32 Idx; UINT32 SupportedHashBitmap; HASH_HANDLE *HashCtx; UINTN HashInfoSize; SupportedHashBitmap = 0; Status = GetSupportedHashBitmap (&SupportedHashBitmap); if (EFI_ERROR (Status)) { return Status; } if (SupportedHashBitmap == 0) { return EFI_DEVICE_ERROR; } HashInfoSize = GetHashInfoSize (); HashCtx = AllocatePool (HashInfoSize * sizeof (HASH_HANDLE)); if (HashCtx == NULL) { return EFI_OUT_OF_RESOURCES; } for (Idx = 0; Idx < HashInfoSize; Idx++) { if ((GetHashMaskAtIndex (Idx) & SupportedHashBitmap) == 0) { continue; } AlgoId = GetHashAlgoFromMask (GetHashMaskAtIndex (Idx)); if (AlgoId == TPM_ALG_ERROR) { return EFI_UNSUPPORTED; } Status = Tpm2HashSequenceStart (AlgoId, (TPMI_DH_OBJECT *)&HashCtx[Idx]); if (EFI_ERROR (Status)) { return Status; } } *HashHandle = (HASH_HANDLE)HashCtx; return Status; } /** Update hash sequence data. @param HashHandle Hash handle. @param DataToHash Data to be hashed. @param DataToHashLen Data size. @retval EFI_SUCCESS Hash sequence updated. **/ EFI_STATUS EFIAPI HashUpdate ( IN HASH_HANDLE HashHandle, IN VOID *DataToHash, IN UINTN DataToHashLen ) { EFI_STATUS Status; UINT32 Idx; UINT8 *Buffer; UINT64 HashLen; TPM2B_MAX_BUFFER HashBuffer; UINT32 SupportedHashBitmap; HASH_HANDLE *HashCtx; UINTN HashInfoSize; SupportedHashBitmap = 0; Status = GetSupportedHashBitmap (&SupportedHashBitmap); if (EFI_ERROR (Status)) { return Status; } if (SupportedHashBitmap == 0) { return EFI_DEVICE_ERROR; } HashCtx = (HASH_HANDLE *)HashHandle; HashInfoSize = GetHashInfoSize (); for (Idx = 0; Idx < HashInfoSize; Idx++) { if ((GetHashMaskAtIndex (Idx) & SupportedHashBitmap) == 0) { continue; } Buffer = (UINT8 *)(UINTN)DataToHash; for (HashLen = DataToHashLen; HashLen > sizeof (HashBuffer.buffer); HashLen -= sizeof (HashBuffer.buffer)) { HashBuffer.size = sizeof (HashBuffer.buffer); CopyMem (HashBuffer.buffer, Buffer, sizeof (HashBuffer.buffer)); Buffer += sizeof (HashBuffer.buffer); Status = Tpm2SequenceUpdate ((TPMI_DH_OBJECT)HashCtx[Idx], &HashBuffer); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } } // Last one HashBuffer.size = (UINT16)HashLen; CopyMem (HashBuffer.buffer, Buffer, (UINTN)HashLen); Status = Tpm2SequenceUpdate ((TPMI_DH_OBJECT)HashCtx[Idx], &HashBuffer); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } } return Status; } /** Hash sequence complete and extend to PCR. @param HashHandle Hash handle. @param PcrIndex PCR to be extended. @param DataToHash Data to be hashed. @param DataToHashLen Data size. @param DigestList Digest list. @retval EFI_SUCCESS Hash sequence complete and DigestList is returned. **/ EFI_STATUS EFIAPI HashCompleteAndExtend ( IN HASH_HANDLE HashHandle, IN TPMI_DH_PCR PcrIndex, IN VOID *DataToHash, IN UINTN DataToHashLen, OUT TPML_DIGEST_VALUES *DigestList ) { EFI_STATUS Status; UINT32 Idx; UINT32 DigestIdx; UINT8 *Buffer; UINT64 HashLen; TPM2B_MAX_BUFFER HashBuffer; TPM_ALG_ID AlgoId; TPM2B_DIGEST Result; UINT32 SupportedHashBitmap; HASH_HANDLE *HashCtx; UINTN HashInfoSize; SupportedHashBitmap = 0; Status = GetSupportedHashBitmap (&SupportedHashBitmap); if (EFI_ERROR (Status)) { return Status; } if (SupportedHashBitmap == 0) { return EFI_DEVICE_ERROR; } ZeroMem (DigestList, sizeof (*DigestList)); DigestList->count = HASH_COUNT; DigestIdx = 0; HashCtx = (HASH_HANDLE *)HashHandle; HashInfoSize = GetHashInfoSize (); for (Idx = 0; Idx < HashInfoSize; Idx++) { if ((GetHashMaskAtIndex (Idx) & SupportedHashBitmap) == 0) { continue; } Buffer = (UINT8 *)(UINTN)DataToHash; for (HashLen = DataToHashLen; HashLen > sizeof (HashBuffer.buffer); HashLen -= sizeof (HashBuffer.buffer)) { HashBuffer.size = sizeof (HashBuffer.buffer); CopyMem (HashBuffer.buffer, Buffer, sizeof (HashBuffer.buffer)); Buffer += sizeof (HashBuffer.buffer); Status = Tpm2SequenceUpdate ((TPMI_DH_OBJECT)HashCtx[Idx], &HashBuffer); if (EFI_ERROR (Status)) { goto Error; } } // Last one HashBuffer.size = (UINT16)HashLen; CopyMem (HashBuffer.buffer, Buffer, (UINTN)HashLen); Status = Tpm2SequenceComplete ((TPMI_DH_OBJECT)HashCtx[Idx], &HashBuffer, &Result); if (EFI_ERROR (Status)) { goto Error; } AlgoId = GetHashAlgoFromMask (GetHashMaskAtIndex (Idx)); if (AlgoId == TPM_ALG_ERROR) { Status = EFI_UNSUPPORTED; goto Error; } // Copy the result of hash. CopyMem (&DigestList->digests[DigestIdx].digest, Result.buffer, Result.size); DigestList->digests[DigestIdx].hashAlg = AlgoId; DigestIdx++; } DigestList->count = DigestIdx; Status = Tpm2PcrExtend (PcrIndex, DigestList); Error: FreePool (HashCtx); return Status; } /** Hash data and extend to PCR. @param PcrIndex PCR to be extended. @param DataToHash Data to be hashed. @param DataToHashLen Data size. @param DigestList Digest list. @retval EFI_SUCCESS Hash data and DigestList is returned. **/ EFI_STATUS EFIAPI HashAndExtend ( IN TPMI_DH_PCR PcrIndex, IN VOID *DataToHash, IN UINTN DataToHashLen, OUT TPML_DIGEST_VALUES *DigestList ) { EFI_STATUS Status; UINT8 *Buffer; UINT64 HashLen; TPMI_DH_OBJECT SequenceHandle; TPM2B_MAX_BUFFER HashBuffer; TPM2B_DIGEST Result; TPM_ALG_ID AlgoId; UINT32 Idx; UINT32 DigestIdx; UINT32 SupportedHashBitmap; UINT32 HashInfoSize; DEBUG ((DEBUG_VERBOSE, "\n HashAndExtend Entry \n")); SupportedHashBitmap = 0; Status = GetSupportedHashBitmap (&SupportedHashBitmap); if (EFI_ERROR (Status)) { return Status; } if (SupportedHashBitmap == 0x00) { return EFI_DEVICE_ERROR; } ZeroMem (DigestList, sizeof (*DigestList)); DigestList->count = HASH_COUNT; DigestIdx = 0; HashInfoSize = GetHashInfoSize (); for (Idx = 0; Idx < HashInfoSize; Idx++) { if ((GetHashMaskAtIndex (Idx) & SupportedHashBitmap) == 0) { continue; } DEBUG ((DEBUG_INFO, "Hashing with Mask: %x\n", GetHashMaskAtIndex (Idx))); AlgoId = GetHashAlgoFromMask (GetHashMaskAtIndex (Idx)); if (AlgoId == TPM_ALG_ERROR) { return EFI_UNSUPPORTED; } Status = Tpm2HashSequenceStart (AlgoId, &SequenceHandle); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } DEBUG ((DEBUG_VERBOSE, "\n Tpm2HashSequenceStart Success \n")); DEBUG ((DEBUG_INFO, "Hashing %d bytes of data\n", DataToHashLen)); Buffer = (UINT8 *)(UINTN)DataToHash; for (HashLen = DataToHashLen; HashLen > sizeof (HashBuffer.buffer); HashLen -= sizeof (HashBuffer.buffer)) { HashBuffer.size = sizeof (HashBuffer.buffer); CopyMem (HashBuffer.buffer, Buffer, sizeof (HashBuffer.buffer)); Buffer += sizeof (HashBuffer.buffer); Status = Tpm2SequenceUpdate (SequenceHandle, &HashBuffer); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } } DEBUG ((DEBUG_VERBOSE, "\n Tpm2SequenceUpdate Success \n")); HashBuffer.size = (UINT16)HashLen; CopyMem (HashBuffer.buffer, Buffer, (UINTN)HashLen); Status = Tpm2SequenceComplete (SequenceHandle, &HashBuffer, &Result); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } DEBUG ((DEBUG_VERBOSE, "\n Tpm2SequenceComplete Success \n")); CopyMem (&DigestList->digests[DigestIdx].digest, Result.buffer, Result.size); DigestList->digests[DigestIdx].hashAlg = AlgoId; DigestIdx++; } DigestList->count = DigestIdx; DEBUG ((DEBUG_INFO, "Extending to PCR%d\n", PcrIndex)); Status = Tpm2PcrExtend (PcrIndex, DigestList); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } DEBUG ((DEBUG_VERBOSE, "\n Tpm2PcrExtend Success \n")); return EFI_SUCCESS; } /** This service register Hash. @param HashInterface Hash interface @retval EFI_SUCCESS This hash interface is registered successfully. @retval EFI_UNSUPPORTED System does not support register this interface. @retval EFI_ALREADY_STARTED System already register this interface. **/ EFI_STATUS EFIAPI RegisterHashInterfaceLib ( IN HASH_INTERFACE *HashInterface ) { return EFI_UNSUPPORTED; }