diff --git a/.pytool/CISettings.py b/.pytool/CISettings.py
index 269998ecd..003ce3083 100644
--- a/.pytool/CISettings.py
+++ b/.pytool/CISettings.py
@@ -167,8 +167,13 @@ def GetDependencies(self):
return [
{
"Path": "MU_BASECORE",
- "Url": "https://github.com/microsoft/mu_basecore.git",
- "Commit": "922377cb580e03c45628ddb4e0a0871ccf2f6f2d"
+ # MU_CHANGE [TEMP] - pin to fork commit carrying the new
+ # BaseCryptLib/OneCrypto APIs (GetAuthenticodeHash,
+ # GetTrustAnchorX509FromAuthData, FreeTrustAnchorX509Cache,
+ # GetAuthenticodeHashAlgorithm, X509GetTbsCertHash). Revert to
+ # https://github.com/microsoft/mu_basecore.git once merged.
+ "Url": "https://github.com/flickdm/mu_basecore.git",
+ "Commit": "709ab9b016ecf0555142ba5bc0da55227f34a7cd"
},
{
"Path": "Features/MM_SUPV",
diff --git a/MbedTlsPkg/Library/BaseCryptLib/BaseCryptLib.inf b/MbedTlsPkg/Library/BaseCryptLib/BaseCryptLib.inf
index 6161df737..ee6b7ffdc 100644
--- a/MbedTlsPkg/Library/BaseCryptLib/BaseCryptLib.inf
+++ b/MbedTlsPkg/Library/BaseCryptLib/BaseCryptLib.inf
@@ -43,6 +43,8 @@
Pk/CryptDhNull.c
Pk/CryptX509.c
Pk/CryptAuthenticode.c
+ Pk/CryptAuthenticodeHash.c # MU_CHANGE
+ Pk/CryptTrustAnchor.c # MU_CHANGE
Pk/CryptTs.c
Pk/CryptRsaPss.c
Pk/CryptRsaPssSign.c
@@ -78,6 +80,13 @@
SynchronizationLib
[Protocols]
gEfiMpServiceProtocolGuid
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # Image hash type used by GetAuthenticodeHash
+ gEfiCertSha1Guid # MU_CHANGE
+ gEfiCertSha256Guid # MU_CHANGE
+ gEfiCertSha384Guid # MU_CHANGE
+ gEfiCertSha512Guid # MU_CHANGE
#
# Remove these [BuildOptions] after this library is cleaned up
#
diff --git a/MbedTlsPkg/Library/BaseCryptLib/PeiCryptLib.inf b/MbedTlsPkg/Library/BaseCryptLib/PeiCryptLib.inf
index 92ec84a51..c0076ec49 100644
--- a/MbedTlsPkg/Library/BaseCryptLib/PeiCryptLib.inf
+++ b/MbedTlsPkg/Library/BaseCryptLib/PeiCryptLib.inf
@@ -56,6 +56,8 @@
Pk/CryptDhNull.c
Pk/CryptX509Null.c
Pk/CryptAuthenticodeNull.c
+ Pk/CryptAuthenticodeHashNull.c # MU_CHANGE
+ Pk/CryptTrustAnchorNull.c # MU_CHANGE
Pk/CryptTsNull.c
Pk/CryptRsaPss.c
Pk/CryptRsaPssSignNull.c
diff --git a/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHash.c b/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHash.c
new file mode 100644
index 000000000..b45e0d765
--- /dev/null
+++ b/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHash.c
@@ -0,0 +1,934 @@
+/** @file
+ PE/COFF Authenticode Image Hash computation.
+
+ Implements GetAuthenticodeHash() per the "Windows Authenticode Portable
+ Executable Signature Format" specification. The hash covers the entire
+ PE/COFF image except for the image checksum, the Certificate Table
+ data-directory entry, and the certificate table content itself.
+
+ Caution: This module operates on untrusted input (the PE/COFF image),
+ so each header field is validated against FileSize before use.
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "InternalCryptLib.h"
+
+#include
+#include
+
+//
+// Function pointer table type for a single hash algorithm.
+// We use the BaseCryptLib hash primitives so this implementation is
+// independent of the underlying crypto provider (OpenSSL / Mbed TLS).
+//
+typedef
+UINTN
+(EFIAPI *AUTH_HASH_GET_CONTEXT_SIZE)(
+ VOID
+ );
+
+typedef
+BOOLEAN
+(EFIAPI *AUTH_HASH_INIT)(
+ OUT VOID *HashContext
+ );
+
+typedef
+BOOLEAN
+(EFIAPI *AUTH_HASH_UPDATE)(
+ IN OUT VOID *HashContext,
+ IN CONST VOID *Data,
+ IN UINTN DataSize
+ );
+
+typedef
+BOOLEAN
+(EFIAPI *AUTH_HASH_FINAL)(
+ IN OUT VOID *HashContext,
+ OUT UINT8 *HashValue
+ );
+
+typedef struct {
+ CONST EFI_GUID *HashGuid;
+ UINTN DigestSize;
+ AUTH_HASH_GET_CONTEXT_SIZE GetContextSize;
+ AUTH_HASH_INIT Init;
+ AUTH_HASH_UPDATE Update;
+ AUTH_HASH_FINAL Final;
+} AUTH_HASH_INFO;
+
+//
+// Forward references to BaseCryptLib hash primitives. These are provided
+// by the BaseCryptLib hash sources in this same library instance.
+//
+STATIC CONST AUTH_HASH_INFO mAuthHashInfo[] = {
+ { &gEfiCertSha1Guid, SHA1_DIGEST_SIZE, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final },
+ { &gEfiCertSha256Guid, SHA256_DIGEST_SIZE, Sha256GetContextSize, Sha256Init, Sha256Update, Sha256Final },
+ { &gEfiCertSha384Guid, SHA384_DIGEST_SIZE, Sha384GetContextSize, Sha384Init, Sha384Update, Sha384Final },
+ { &gEfiCertSha512Guid, SHA512_DIGEST_SIZE, Sha512GetContextSize, Sha512Init, Sha512Update, Sha512Final },
+};
+
+#define AUTH_HASH_INFO_COUNT (sizeof (mAuthHashInfo) / sizeof (mAuthHashInfo[0]))
+
+/**
+ Look up an entry in mAuthHashInfo by HashType GUID.
+
+ @param[in] HashType Signature-type GUID identifying the hash algorithm.
+
+ @retval Pointer to the AUTH_HASH_INFO on match.
+ @retval NULL if HashType does not match a supported algorithm.
+**/
+STATIC
+CONST AUTH_HASH_INFO *
+LookupAuthHashInfo (
+ IN CONST EFI_GUID *HashType
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < AUTH_HASH_INFO_COUNT; Index++) {
+ if (CompareGuid (HashType, mAuthHashInfo[Index].HashGuid)) {
+ return &mAuthHashInfo[Index];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Compute the PE/COFF Authenticode-style image hash of a loaded image,
+ as described in the "Windows Authenticode Portable Executable
+ Signature Format" specification.
+
+ The caller selects the digest algorithm by HashType (e.g.
+ gEfiCertSha256Guid, gEfiCertSha384Guid). The digest is written to
+ Digest, which must be large enough to hold the largest supported
+ digest (at least SHA512_DIGEST_SIZE bytes).
+
+ Caution: This function may receive untrusted input. The PE/COFF image
+ is external input, so this function validates the image's data
+ structure before hashing.
+
+ @param[in] FileBuffer Pointer to the in-memory PE/COFF image.
+ @param[in] FileSize Size of FileBuffer in bytes.
+ @param[in] HashType Signature-type GUID identifying the hash
+ algorithm to use.
+ @param[out] Digest Caller-provided buffer that receives the
+ computed digest. Must be at least
+ SHA512_DIGEST_SIZE bytes.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_SUCCESS Digest was computed successfully.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL or
+ FileSize is 0.
+ @retval EFI_UNSUPPORTED HashType is not a recognized image
+ hash algorithm, or this interface is
+ not supported by the underlying
+ library instance.
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHash (
+ IN VOID *FileBuffer,
+ IN UINTN FileSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ EFI_STATUS Status;
+ CONST AUTH_HASH_INFO *HashInfo;
+ UINT8 *ImageBase;
+ UINTN PeCoffHeaderOffset;
+ EFI_IMAGE_DOS_HEADER *DosHdr;
+ EFI_IMAGE_OPTIONAL_HEADER_UNION *NtHdr;
+ UINT16 Magic;
+ UINT32 NumberOfRvaAndSizes;
+ UINT32 SizeOfHeaders;
+ UINT16 NumberOfSections;
+ UINT16 SizeOfOptionalHeader;
+ UINT8 *CheckSumPtr;
+ UINT8 *SecDirPtr;
+ EFI_IMAGE_DATA_DIRECTORY *SecDir;
+ UINT32 CertSize;
+ VOID *HashCtx;
+ UINTN CtxSize;
+ UINT8 *HashBase;
+ UINTN HashSize;
+ UINTN SumOfBytesHashed;
+ EFI_IMAGE_SECTION_HEADER *SectionHeaders;
+ EFI_IMAGE_SECTION_HEADER *Section;
+ UINTN SectionHeadersSize;
+ UINTN FirstSectionOffset;
+ UINTN Index;
+ UINTN Pos;
+
+ HashCtx = NULL;
+ SectionHeaders = NULL;
+ Status = EFI_INVALID_PARAMETER;
+
+ if ((FileBuffer == NULL) || (FileSize == 0) || (HashType == NULL) ||
+ (Digest == NULL) || (DigestSize == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HashInfo = LookupAuthHashInfo (HashType);
+ if (HashInfo == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ ImageBase = (UINT8 *)FileBuffer;
+
+ //
+ // Locate the PE header.
+ //
+ if (FileSize < sizeof (EFI_IMAGE_DOS_HEADER)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DosHdr = (EFI_IMAGE_DOS_HEADER *)ImageBase;
+ if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
+ PeCoffHeaderOffset = DosHdr->e_lfanew;
+ } else {
+ PeCoffHeaderOffset = 0;
+ }
+
+ if ((PeCoffHeaderOffset > FileSize) ||
+ ((FileSize - PeCoffHeaderOffset) < sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + sizeof (UINT16)))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NtHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(ImageBase + PeCoffHeaderOffset);
+ if (NtHdr->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // FileHeader is at the same offset for PE32 and PE32+.
+ //
+ SizeOfOptionalHeader = NtHdr->Pe32.FileHeader.SizeOfOptionalHeader;
+ NumberOfSections = NtHdr->Pe32.FileHeader.NumberOfSections;
+
+ //
+ // Make sure the optional header fits.
+ //
+ if ((FileSize - PeCoffHeaderOffset) <
+ sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + (UINTN)SizeOfOptionalHeader)
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Magic = NtHdr->Pe32.OptionalHeader.Magic;
+
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (SizeOfOptionalHeader < sizeof (EFI_IMAGE_OPTIONAL_HEADER32)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CheckSumPtr = (UINT8 *)&NtHdr->Pe32.OptionalHeader.CheckSum;
+ NumberOfRvaAndSizes = NtHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
+ SizeOfHeaders = NtHdr->Pe32.OptionalHeader.SizeOfHeaders;
+ } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ if (SizeOfOptionalHeader < sizeof (EFI_IMAGE_OPTIONAL_HEADER64)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CheckSumPtr = (UINT8 *)&NtHdr->Pe32Plus.OptionalHeader.CheckSum;
+ NumberOfRvaAndSizes = NtHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
+ SizeOfHeaders = NtHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // SizeOfHeaders must be within FileSize.
+ //
+ if ((SizeOfHeaders > FileSize) ||
+ (SizeOfHeaders < (PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + (UINTN)SizeOfOptionalHeader)))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The section headers must lie within the headers region.
+ //
+ FirstSectionOffset = PeCoffHeaderOffset + sizeof (UINT32) +
+ sizeof (EFI_IMAGE_FILE_HEADER) + (UINTN)SizeOfOptionalHeader;
+ SectionHeadersSize = (UINTN)NumberOfSections * sizeof (EFI_IMAGE_SECTION_HEADER);
+ if ((SectionHeadersSize / sizeof (EFI_IMAGE_SECTION_HEADER)) != (UINTN)NumberOfSections) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((FirstSectionOffset > FileSize) ||
+ ((FileSize - FirstSectionOffset) < SectionHeadersSize) ||
+ ((FirstSectionOffset + SectionHeadersSize) > SizeOfHeaders))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate and initialize the hash context.
+ //
+ CtxSize = HashInfo->GetContextSize ();
+ HashCtx = AllocatePool (CtxSize);
+ if (HashCtx == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!HashInfo->Init (HashCtx)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ //
+ // Step 1-4: Hash the image header from its base to the start of the
+ // CheckSum field.
+ //
+ HashBase = ImageBase;
+ HashSize = (UINTN)(CheckSumPtr - ImageBase);
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ //
+ // Step 5: Skip over the image checksum (4 bytes).
+ // Step 6/7: Hash everything from the end of the checksum to either the
+ // end of the optional header or the start of the Cert Directory entry.
+ //
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
+ HashBase = CheckSumPtr + sizeof (UINT32);
+ HashSize = SizeOfHeaders - (UINTN)(HashBase - ImageBase);
+ SecDir = NULL;
+ } else {
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ SecDirPtr = (UINT8 *)&NtHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ SecDir = &NtHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ } else {
+ SecDirPtr = (UINT8 *)&NtHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ SecDir = &NtHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ }
+
+ HashBase = CheckSumPtr + sizeof (UINT32);
+ HashSize = (UINTN)(SecDirPtr - HashBase);
+ }
+
+ if (HashSize != 0) {
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+ }
+
+ //
+ // Step 8/9: Skip the Cert Directory entry. Hash from end of cert dir
+ // entry to end of image header.
+ //
+ if (SecDir != NULL) {
+ HashBase = SecDirPtr + sizeof (EFI_IMAGE_DATA_DIRECTORY);
+ HashSize = SizeOfHeaders - (UINTN)(HashBase - ImageBase);
+ if (HashSize != 0) {
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+ }
+ }
+
+ //
+ // Step 10: SUM_OF_BYTES_HASHED = SizeOfHeaders.
+ //
+ SumOfBytesHashed = SizeOfHeaders;
+
+ //
+ // Step 11-12: Build a sorted table of section headers.
+ //
+ if (NumberOfSections != 0) {
+ SectionHeaders = AllocateZeroPool (SectionHeadersSize);
+ if (SectionHeaders == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ Section = (EFI_IMAGE_SECTION_HEADER *)(ImageBase + FirstSectionOffset);
+
+ //
+ // Insertion sort by PointerToRawData.
+ //
+ for (Index = 0; Index < (UINTN)NumberOfSections; Index++) {
+ Pos = Index;
+ while ((Pos > 0) && (Section->PointerToRawData < SectionHeaders[Pos - 1].PointerToRawData)) {
+ CopyMem (&SectionHeaders[Pos], &SectionHeaders[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));
+ Pos--;
+ }
+
+ CopyMem (&SectionHeaders[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));
+ Section++;
+ }
+
+ //
+ // Step 13-15: Hash each section in order.
+ //
+ for (Index = 0; Index < (UINTN)NumberOfSections; Index++) {
+ Section = &SectionHeaders[Index];
+ if (Section->SizeOfRawData == 0) {
+ continue;
+ }
+
+ //
+ // Validate the section bounds against the file size.
+ //
+ if ((Section->PointerToRawData > FileSize) ||
+ ((FileSize - Section->PointerToRawData) < (UINTN)Section->SizeOfRawData))
+ {
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+
+ HashBase = ImageBase + Section->PointerToRawData;
+ HashSize = (UINTN)Section->SizeOfRawData;
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ SumOfBytesHashed += HashSize;
+ }
+ }
+
+ //
+ // Step 16: Hash any trailing bytes between SUM_OF_BYTES_HASHED and the
+ // start of the certificate table (or end of file if no cert table).
+ //
+ if (FileSize > SumOfBytesHashed) {
+ HashBase = ImageBase + SumOfBytesHashed;
+
+ if ((SecDir == NULL) || (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY)) {
+ CertSize = 0;
+ } else {
+ CertSize = SecDir->Size;
+ }
+
+ if (FileSize > (UINTN)CertSize + SumOfBytesHashed) {
+ HashSize = FileSize - (UINTN)CertSize - SumOfBytesHashed;
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+ } else if (FileSize < (UINTN)CertSize + SumOfBytesHashed) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+ }
+
+ if (!HashInfo->Final (HashCtx, Digest)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ *DigestSize = HashInfo->DigestSize;
+ Status = EFI_SUCCESS;
+
+Done:
+ if (HashCtx != NULL) {
+ FreePool (HashCtx);
+ }
+
+ if (SectionHeaders != NULL) {
+ FreePool (SectionHeaders);
+ }
+
+ return Status;
+}
+
+//
+// ===========================================================================
+// Authenticode hash-algorithm discovery (SpcIndirectDataContent parsing)
+// ===========================================================================
+//
+// The functions below walk the PKCS#7 SignedData ASN.1 structure to recover
+// the digest algorithm recorded by the signer, without depending on any
+// particular crypto provider. AuthData is untrusted, so every length field
+// is decoded with bounds checking.
+//
+
+//
+// ASN.1 DER tag bytes used while walking the PKCS#7 SignedData structure.
+//
+#define AUTH_ASN1_TAG_INTEGER 0x02
+#define AUTH_ASN1_TAG_OID 0x06
+#define AUTH_ASN1_TAG_SEQUENCE 0x30
+#define AUTH_ASN1_TAG_SET 0x31
+#define AUTH_ASN1_TAG_CTX_CONS_0 0xA0 // [0] EXPLICIT, constructed
+
+//
+// OID 1.2.840.113549.1.7.2 (PKCS#7 signedData) - DER value bytes.
+//
+STATIC CONST UINT8 mAuthPkcs7SignedDataOid[] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02
+};
+
+//
+// OID 1.3.6.1.4.1.311.2.1.4 (SPC_INDIRECT_DATA_OBJID) - DER value bytes.
+//
+STATIC CONST UINT8 mAuthSpcIndirectDataOid[] = {
+ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04
+};
+
+//
+// Digest-algorithm OIDs (DER value bytes) recognized by Authenticode.
+//
+STATIC CONST UINT8 mAuthOidSha1[] = {
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A // 1.3.14.3.2.26
+};
+STATIC CONST UINT8 mAuthOidSha256[] = {
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 // 2.16.840.1.101.3.4.2.1
+};
+STATIC CONST UINT8 mAuthOidSha384[] = {
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 // 2.16.840.1.101.3.4.2.2
+};
+STATIC CONST UINT8 mAuthOidSha512[] = {
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 // 2.16.840.1.101.3.4.2.3
+};
+
+//
+// Mapping of digest-algorithm OID to the signature-type GUID consumed by
+// GetAuthenticodeHash().
+//
+typedef struct {
+ CONST UINT8 *Oid;
+ UINTN OidSize;
+ CONST EFI_GUID *HashGuid;
+} AUTH_DIGEST_OID_INFO;
+
+STATIC CONST AUTH_DIGEST_OID_INFO mAuthDigestOidInfo[] = {
+ { mAuthOidSha1, sizeof (mAuthOidSha1), &gEfiCertSha1Guid },
+ { mAuthOidSha256, sizeof (mAuthOidSha256), &gEfiCertSha256Guid },
+ { mAuthOidSha384, sizeof (mAuthOidSha384), &gEfiCertSha384Guid },
+ { mAuthOidSha512, sizeof (mAuthOidSha512), &gEfiCertSha512Guid },
+};
+
+#define AUTH_DIGEST_OID_INFO_COUNT (sizeof (mAuthDigestOidInfo) / sizeof (mAuthDigestOidInfo[0]))
+
+/**
+ Decode an ASN.1 DER length field starting at *Cursor. On success, the
+ decoded length is written to *Length and *Cursor is advanced past the
+ length octets.
+
+ Only the definite form is accepted. Lengths > the remaining input are
+ rejected.
+
+ @param[in,out] Cursor Pointer to the cursor pointer; advanced on
+ success.
+ @param[in] End One past the last valid input byte.
+ @param[out] Length Receives the decoded content length.
+
+ @retval EFI_SUCCESS Length parsed.
+ @retval EFI_INVALID_PARAMETER Malformed encoding or out of bounds.
+**/
+STATIC
+EFI_STATUS
+Asn1DecodeLength (
+ IN OUT CONST UINT8 **Cursor,
+ IN CONST UINT8 *End,
+ OUT UINTN *Length
+ )
+{
+ CONST UINT8 *P;
+ UINTN Result;
+ UINTN NumOctets;
+ UINTN Index;
+
+ P = *Cursor;
+ if (P >= End) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*P & 0x80) == 0) {
+ Result = (UINTN)*P;
+ P++;
+ } else {
+ NumOctets = (UINTN)(*P & 0x7F);
+ P++;
+ //
+ // Reject indefinite (0x80) and lengths longer than UINTN.
+ //
+ if ((NumOctets == 0) || (NumOctets > sizeof (UINTN))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((UINTN)(End - P) < NumOctets) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Result = 0;
+ for (Index = 0; Index < NumOctets; Index++) {
+ Result = (Result << 8) | P[Index];
+ }
+
+ P += NumOctets;
+ }
+
+ if ((UINTN)(End - P) < Result) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Cursor = P;
+ *Length = Result;
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse an ASN.1 DER TLV at *Cursor and require Tag. On success, *Body
+ points to the value bytes, *BodyLen is the value length, and *Cursor
+ is advanced past the entire TLV.
+
+ @param[in,out] Cursor Cursor pointer.
+ @param[in] End One past the last valid input byte.
+ @param[in] Tag Required tag byte.
+ @param[out] Body Receives a pointer to the value bytes.
+ @param[out] BodyLen Receives the value length.
+
+ @retval EFI_SUCCESS TLV parsed.
+ @retval EFI_INVALID_PARAMETER Wrong tag or malformed encoding.
+**/
+STATIC
+EFI_STATUS
+Asn1ExpectTagged (
+ IN OUT CONST UINT8 **Cursor,
+ IN CONST UINT8 *End,
+ IN UINT8 Tag,
+ OUT CONST UINT8 **Body,
+ OUT UINTN *BodyLen
+ )
+{
+ CONST UINT8 *P;
+ EFI_STATUS Status;
+ UINTN Length;
+
+ P = *Cursor;
+ if ((P >= End) || (*P != Tag)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ P++;
+ Status = Asn1DecodeLength (&P, End, &Length);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *Body = P;
+ *BodyLen = Length;
+ *Cursor = P + Length;
+ return EFI_SUCCESS;
+}
+
+/**
+ Determine the image-hash algorithm used by an Authenticode signature.
+
+ Parses the PKCS#7 SignedData blob's SpcIndirectDataContent
+ (OID 1.3.6.1.4.1.311.2.1.4) and reads the digestAlgorithm of its
+ embedded messageDigest DigestInfo, mapping it to the corresponding
+ signature-type GUID. The recovered GUID can be passed directly to
+ GetAuthenticodeHash() as its HashType.
+
+ Caution: AuthData is untrusted. The ASN.1 DER is parsed with
+ bounds-checked length decoding to avoid out-of-bounds reads.
+
+ @param[in] AuthData Pointer to the PKCS#7 SignedData blob
+ (DER-encoded Authenticode signature).
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] HashType On success, receives the signature-type
+ GUID identifying the digest algorithm.
+
+ @retval EFI_SUCCESS The hash algorithm was identified.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL,
+ AuthDataSize is 0, or AuthData is not a
+ well-formed Authenticode SignedData
+ blob.
+ @retval EFI_UNSUPPORTED The digest algorithm is not a
+ recognized image hash algorithm.
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHashAlgorithm (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT EFI_GUID *HashType
+ )
+{
+ EFI_STATUS Status;
+ CONST UINT8 *Cursor;
+ CONST UINT8 *End;
+ CONST UINT8 *Body;
+ UINTN BodyLen;
+ CONST UINT8 *OidBody;
+ UINTN OidLen;
+ UINTN Index;
+
+ if ((AuthData == NULL) || (AuthDataSize == 0) || (HashType == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = AuthData;
+ End = AuthData + AuthDataSize;
+
+ //
+ // ContentInfo ::= SEQUENCE { contentType OID, content [0] EXPLICIT }
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // contentType OID == pkcs7-signedData (1.2.840.113549.1.7.2).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_OID, &OidBody, &OidLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((OidLen != sizeof (mAuthPkcs7SignedDataOid)) ||
+ (CompareMem (OidBody, mAuthPkcs7SignedDataOid, OidLen) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // content [0] EXPLICIT -> SignedData.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_CTX_CONS_0, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // SignedData ::= SEQUENCE { version, digestAlgorithms, encapContentInfo, ... }
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // version INTEGER (skip).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_INTEGER, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // digestAlgorithms SET (skip).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SET, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // encapContentInfo ::= SEQUENCE { contentType OID, content [0] EXPLICIT }.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // contentType OID == SPC_INDIRECT_DATA (1.3.6.1.4.1.311.2.1.4).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_OID, &OidBody, &OidLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((OidLen != sizeof (mAuthSpcIndirectDataOid)) ||
+ (CompareMem (OidBody, mAuthSpcIndirectDataOid, OidLen) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // content [0] EXPLICIT -> SpcIndirectDataContent.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_CTX_CONS_0, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // SpcIndirectDataContent ::= SEQUENCE { data, messageDigest }.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // data SpcAttributeTypeAndOptionalValue SEQUENCE (skip).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // messageDigest DigestInfo ::= SEQUENCE { digestAlgorithm, digest }.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // digestAlgorithm AlgorithmIdentifier ::= SEQUENCE { algorithm OID, ... }.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // algorithm OID.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_OID, &OidBody, &OidLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < AUTH_DIGEST_OID_INFO_COUNT; Index++) {
+ if ((OidLen == mAuthDigestOidInfo[Index].OidSize) &&
+ (CompareMem (OidBody, mAuthDigestOidInfo[Index].Oid, OidLen) == 0))
+ {
+ CopyGuid (HashType, mAuthDigestOidInfo[Index].HashGuid);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Compute the digest of the TBSCertificate of an X.509 certificate.
+
+ Extracts the TBSCertificate (the to-be-signed portion) of the given
+ DER-encoded certificate and hashes it with the algorithm selected by
+ HashType. The TBSCertificate is the exact byte range a certificate
+ authority signs, so its digest uniquely identifies the certificate
+ independent of the issuer signature.
+
+ The caller selects the digest algorithm by HashType (e.g.
+ gEfiCertSha256Guid, gEfiCertSha384Guid). The digest is written to
+ Digest, which must be large enough to hold the largest supported
+ digest (at least SHA512_DIGEST_SIZE bytes).
+
+ @param[in] Cert Pointer to the DER-encoded X.509 certificate.
+ @param[in] CertSize Size of Cert in bytes.
+ @param[in] HashType Signature-type GUID identifying the hash
+ algorithm to use.
+ @param[out] Digest Caller-provided buffer that receives the
+ computed TBSCertificate digest. Must be at
+ least SHA512_DIGEST_SIZE bytes.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_SUCCESS Digest was computed successfully.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL, CertSize is
+ 0, or Cert is not a well-formed X.509
+ certificate.
+ @retval EFI_UNSUPPORTED HashType is not a recognized image
+ hash algorithm.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate the hash context.
+ @retval EFI_DEVICE_ERROR A hash primitive failed.
+**/
+EFI_STATUS
+EFIAPI
+X509GetTbsCertHash (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ EFI_STATUS Status;
+ CONST AUTH_HASH_INFO *HashInfo;
+ UINT8 *TbsCert;
+ UINTN TbsCertSize;
+ VOID *HashCtx;
+ UINTN CtxSize;
+
+ if ((Cert == NULL) || (CertSize == 0) || (HashType == NULL) ||
+ (Digest == NULL) || (DigestSize == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HashInfo = LookupAuthHashInfo (HashType);
+ if (HashInfo == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Extract the TBSCertificate byte range. X509GetTBSCert() returns a
+ // pointer into Cert (it does not allocate), so TbsCert must not be
+ // freed here.
+ //
+ TbsCert = NULL;
+ TbsCertSize = 0;
+ if (!X509GetTBSCert ((CONST UINT8 *)Cert, CertSize, &TbsCert, &TbsCertSize)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate and initialize the hash context, then hash the
+ // TBSCertificate bytes.
+ //
+ CtxSize = HashInfo->GetContextSize ();
+ HashCtx = AllocatePool (CtxSize);
+ if (HashCtx == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_DEVICE_ERROR;
+ if (HashInfo->Init (HashCtx) &&
+ HashInfo->Update (HashCtx, TbsCert, TbsCertSize) &&
+ HashInfo->Final (HashCtx, Digest))
+ {
+ *DigestSize = HashInfo->DigestSize;
+ Status = EFI_SUCCESS;
+ }
+
+ FreePool (HashCtx);
+ return Status;
+}
diff --git a/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHashNull.c b/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHashNull.c
new file mode 100644
index 000000000..f51b95e78
--- /dev/null
+++ b/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHashNull.c
@@ -0,0 +1,98 @@
+/** @file
+ GetAuthenticodeHash() Null implementation.
+
+ Returns EFI_UNSUPPORTED to indicate this interface is not provided by
+ the current library instance (e.g., PEI / Runtime / SEC / SMM phases
+ that do not include the Pk/CryptAuthenticodeHash.c implementation).
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "InternalCryptLib.h"
+
+/**
+ Compute the PE/COFF Authenticode-style image hash of a loaded image.
+
+ Return EFI_UNSUPPORTED to indicate this interface is not supported.
+
+ @param[in] FileBuffer Pointer to the in-memory PE/COFF image.
+ @param[in] FileSize Size of FileBuffer in bytes.
+ @param[in] HashType Signature-type GUID identifying the hash
+ algorithm to use.
+ @param[out] Digest Caller-provided buffer that receives the
+ computed digest.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_UNSUPPORTED This interface is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHash (
+ IN VOID *FileBuffer,
+ IN UINTN FileSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Determine the image-hash algorithm used by an Authenticode signature.
+
+ Return EFI_UNSUPPORTED to indicate this interface is not supported.
+
+ @param[in] AuthData Pointer to the PKCS#7 SignedData blob.
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] HashType Receives the signature-type GUID.
+
+ @retval EFI_UNSUPPORTED This interface is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHashAlgorithm (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT EFI_GUID *HashType
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Compute the digest of the TBSCertificate of an X.509 certificate.
+
+ Return EFI_UNSUPPORTED to indicate this interface is not supported.
+
+ @param[in] Cert Pointer to the DER-encoded X.509 certificate.
+ @param[in] CertSize Size of Cert in bytes.
+ @param[in] HashType Signature-type GUID identifying the hash
+ algorithm to use.
+ @param[out] Digest Caller-provided buffer that receives the
+ computed digest.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_UNSUPPORTED This interface is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+X509GetTbsCertHash (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
diff --git a/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptTrustAnchor.c b/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptTrustAnchor.c
new file mode 100644
index 000000000..f743e98d3
--- /dev/null
+++ b/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptTrustAnchor.c
@@ -0,0 +1,752 @@
+/** @file
+ GetTrustAnchorX509FromAuthData() and the matching cache lifecycle.
+
+ Walks a PKCS#7 SignedData blob, hashes each embedded X.509 certificate's
+ TBSCertificate, and returns the certificate whose digest matches a
+ caller-supplied hash. Optional cache memoizes (cert -> TBS-hash[s])
+ across calls so callers that ask repeatedly do not re-hash the same
+ certificates.
+
+ Caution: AuthData is treated as untrusted input. Each ASN.1 length is
+ bounds-checked against the remaining input before the parser advances.
+
+ Reference:
+ RFC 2315 / RFC 5652 (PKCS#7 / CMS SignedData)
+ RFC 5280 (X.509 Certificate)
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "InternalCryptLib.h"
+
+//
+// ASN.1 DER constants used during the walk.
+//
+#define ASN1_TAG_SEQUENCE 0x30
+#define ASN1_TAG_SET 0x31
+#define ASN1_TAG_OID 0x06
+#define ASN1_TAG_CTX_CONS_0 0xA0 // [0] EXPLICIT / IMPLICIT constructed
+
+//
+// signedData OID 1.2.840.113549.1.7.2 = 06 09 2A 86 48 86 F7 0D 01 07 02
+//
+STATIC CONST UINT8 mPkcs7SignedDataOid[] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02
+};
+
+//
+// Hash algorithm dispatch table; selected by digest length. The one-shot
+// ShaXxxHashAll() entry points handle context allocation, init, update,
+// and finalization internally, so the table only needs the digest size
+// and the function pointer.
+//
+typedef
+BOOLEAN
+(EFIAPI *AUTH_HASH_ALL)(
+ IN CONST VOID *Data,
+ IN UINTN DataSize,
+ OUT UINT8 *HashValue
+ );
+
+typedef struct {
+ UINTN DigestSize;
+ AUTH_HASH_ALL HashAll;
+} TRUST_ANCHOR_HASH_INFO;
+
+STATIC CONST TRUST_ANCHOR_HASH_INFO mHashTable[] = {
+ { SHA1_DIGEST_SIZE, Sha1HashAll },
+ { SHA256_DIGEST_SIZE, Sha256HashAll },
+ { SHA384_DIGEST_SIZE, Sha384HashAll },
+ { SHA512_DIGEST_SIZE, Sha512HashAll },
+};
+
+#define HASH_TABLE_COUNT (sizeof (mHashTable) / sizeof (mHashTable[0]))
+
+//
+// Cache structures. The cache stores up to TRUST_ANCHOR_CACHE_MAX entries;
+// each entry is a copy of the certificate DER bytes plus a per-algorithm
+// TBSCertificate digest computed lazily on first use. The digests are
+// inlined (not heap-allocated) — at four 64-byte slots per entry the
+// cost is bounded and the cache lifecycle has no inner allocations to
+// fail or leak.
+//
+#define TRUST_ANCHOR_CACHE_SIGNATURE SIGNATURE_32 ('T', 'A', 'X', 'C')
+#define TRUST_ANCHOR_CACHE_MAX 64
+
+typedef struct {
+ UINT8 *CertDer; // Allocated cert DER copy.
+ UINTN CertDerSize;
+ BOOLEAN TbsHashValid[HASH_TABLE_COUNT];
+ UINT8 TbsHash[HASH_TABLE_COUNT][SHA512_DIGEST_SIZE];
+} TRUST_ANCHOR_CACHE_ENTRY;
+
+typedef struct {
+ UINT32 Signature;
+ UINTN Count;
+ TRUST_ANCHOR_CACHE_ENTRY Entries[TRUST_ANCHOR_CACHE_MAX];
+} TRUST_ANCHOR_CACHE;
+
+/**
+ Map a digest size to an index in mHashTable.
+
+ @param[in] HashSize Digest length in bytes.
+
+ @retval Index into mHashTable on match.
+ @retval (UINTN)-1 if the size is not supported.
+**/
+STATIC
+UINTN
+HashSizeToIndex (
+ IN UINTN HashSize
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < HASH_TABLE_COUNT; Index++) {
+ if (mHashTable[Index].DigestSize == HashSize) {
+ return Index;
+ }
+ }
+
+ return (UINTN)-1;
+}
+
+/**
+ Decode an ASN.1 DER length field starting at *Cursor. On success, the
+ decoded length is written to *Length and *Cursor is advanced past the
+ length octets.
+
+ Only the definite form is accepted. Lengths > the remaining input are
+ rejected.
+
+ @param[in,out] Cursor Pointer to the cursor pointer; advanced on
+ success.
+ @param[in] End One past the last valid input byte.
+ @param[out] Length Receives the decoded content length.
+
+ @retval EFI_SUCCESS Length parsed.
+ @retval EFI_INVALID_PARAMETER Malformed encoding or out of bounds.
+**/
+STATIC
+EFI_STATUS
+Asn1DecodeLength (
+ IN OUT CONST UINT8 **Cursor,
+ IN CONST UINT8 *End,
+ OUT UINTN *Length
+ )
+{
+ CONST UINT8 *P;
+ UINTN Result;
+ UINTN NumOctets;
+ UINTN Index;
+
+ P = *Cursor;
+ if (P >= End) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*P & 0x80) == 0) {
+ Result = (UINTN)*P;
+ P++;
+ } else {
+ NumOctets = (UINTN)(*P & 0x7F);
+ P++;
+ //
+ // Reject indefinite (0x80) and lengths longer than UINTN.
+ //
+ if ((NumOctets == 0) || (NumOctets > sizeof (UINTN))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((UINTN)(End - P) < NumOctets) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Result = 0;
+ for (Index = 0; Index < NumOctets; Index++) {
+ Result = (Result << 8) | P[Index];
+ }
+
+ P += NumOctets;
+ }
+
+ if ((UINTN)(End - P) < Result) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Cursor = P;
+ *Length = Result;
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse an ASN.1 DER TLV at *Cursor and require Tag. On success, *Body
+ points to the value bytes, *BodyLen is the value length, and *Cursor
+ is advanced past the entire TLV.
+
+ @param[in,out] Cursor Cursor pointer.
+ @param[in] End One past the last valid input byte.
+ @param[in] Tag Required tag byte.
+ @param[out] Body Receives a pointer to the value bytes.
+ @param[out] BodyLen Receives the value length.
+
+ @retval EFI_SUCCESS TLV parsed.
+ @retval EFI_INVALID_PARAMETER Wrong tag or malformed encoding.
+**/
+STATIC
+EFI_STATUS
+Asn1ExpectTagged (
+ IN OUT CONST UINT8 **Cursor,
+ IN CONST UINT8 *End,
+ IN UINT8 Tag,
+ OUT CONST UINT8 **Body,
+ OUT UINTN *BodyLen
+ )
+{
+ CONST UINT8 *P;
+ EFI_STATUS Status;
+ UINTN Length;
+
+ P = *Cursor;
+ if ((P >= End) || (*P != Tag)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ P++;
+ Status = Asn1DecodeLength (&P, End, &Length);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *Body = P;
+ *BodyLen = Length;
+ *Cursor = P + Length;
+ return EFI_SUCCESS;
+}
+
+/**
+ Compute the TBSCertificate digest of an X.509 certificate.
+
+ @param[in] CertDer Pointer to the DER-encoded Certificate.
+ @param[in] CertDerSize Size of the certificate in bytes.
+ @param[in] HashIndex Algorithm index in mHashTable.
+ @param[out] Digest Caller-allocated buffer for the digest. Must
+ be at least mHashTable[HashIndex].DigestSize
+ bytes.
+
+ @retval EFI_SUCCESS Digest computed.
+ @retval EFI_INVALID_PARAMETER CertDer is not a well-formed Certificate.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate hash context.
+ @retval EFI_DEVICE_ERROR Hash primitive failed.
+**/
+STATIC
+EFI_STATUS
+HashTbsCertificate (
+ IN CONST UINT8 *CertDer,
+ IN UINTN CertDerSize,
+ IN UINTN HashIndex,
+ OUT UINT8 *Digest
+ )
+{
+ CONST UINT8 *Cursor;
+ CONST UINT8 *End;
+ CONST UINT8 *CertBody;
+ UINTN CertBodyLen;
+ CONST UINT8 *TbsStart;
+ CONST UINT8 *TbsBody;
+ UINTN TbsBodyLen;
+ EFI_STATUS Status;
+
+ Cursor = CertDer;
+ End = CertDer + CertDerSize;
+
+ //
+ // Certificate ::= SEQUENCE
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &CertBody, &CertBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // First element of Certificate is TBSCertificate ::= SEQUENCE.
+ // Hash that whole TLV (tag + length + value) per RFC 5280 4.1.2.
+ //
+ TbsStart = CertBody;
+ Cursor = CertBody;
+ Status = Asn1ExpectTagged (&Cursor, CertBody + CertBodyLen, ASN1_TAG_SEQUENCE, &TbsBody, &TbsBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!mHashTable[HashIndex].HashAll (TbsStart, (UINTN)(Cursor - TbsStart), Digest)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Look up a certificate in the cache by DER bytes. If found, return the
+ entry; otherwise append a new entry with a copy of the cert bytes if
+ the cache has room.
+
+ @param[in] Cache Cache handle.
+ @param[in] CertDer Pointer to certificate DER bytes.
+ @param[in] CertDerSize Size of certificate in bytes.
+
+ @retval Pointer to the matching or newly inserted entry on success.
+ @retval NULL when the cache is full or memory allocation failed.
+**/
+STATIC
+TRUST_ANCHOR_CACHE_ENTRY *
+CacheLookupOrInsert (
+ IN TRUST_ANCHOR_CACHE *Cache,
+ IN CONST UINT8 *CertDer,
+ IN UINTN CertDerSize
+ )
+{
+ UINTN Index;
+ TRUST_ANCHOR_CACHE_ENTRY *Entry;
+
+ for (Index = 0; Index < Cache->Count; Index++) {
+ Entry = &Cache->Entries[Index];
+ if ((Entry->CertDerSize == CertDerSize) &&
+ (CompareMem (Entry->CertDer, CertDer, CertDerSize) == 0))
+ {
+ return Entry;
+ }
+ }
+
+ if (Cache->Count >= TRUST_ANCHOR_CACHE_MAX) {
+ return NULL;
+ }
+
+ Entry = &Cache->Entries[Cache->Count];
+ Entry->CertDer = AllocateCopyPool (CertDerSize, CertDer);
+ if (Entry->CertDer == NULL) {
+ return NULL;
+ }
+
+ Entry->CertDerSize = CertDerSize;
+ ZeroMem (Entry->TbsHashValid, sizeof (Entry->TbsHashValid));
+ Cache->Count++;
+ return Entry;
+}
+
+/**
+ Compare a certificate's TBS digest at HashIndex against the supplied
+ TbsCertHash, using the cache when available to avoid re-hashing.
+
+ @param[in] Cache Cache or NULL.
+ @param[in] CertDer Pointer to certificate DER bytes.
+ @param[in] CertDerSize Size of certificate in bytes.
+ @param[in] HashIndex Algorithm index in mHashTable.
+ @param[in] TbsCertHash Target digest bytes.
+ @param[out] IsMatch TRUE on match; FALSE otherwise.
+
+ @retval EFI_SUCCESS IsMatch was set.
+ @retval other A hashing or allocation error occurred.
+**/
+STATIC
+EFI_STATUS
+CertMatchesTbsHash (
+ IN TRUST_ANCHOR_CACHE *Cache OPTIONAL,
+ IN CONST UINT8 *CertDer,
+ IN UINTN CertDerSize,
+ IN UINTN HashIndex,
+ IN CONST UINT8 *TbsCertHash,
+ OUT BOOLEAN *IsMatch
+ )
+{
+ EFI_STATUS Status;
+ UINTN DigestSize;
+ TRUST_ANCHOR_CACHE_ENTRY *Entry;
+ UINT8 LocalDigest[SHA512_DIGEST_SIZE];
+ UINT8 *DigestBuf;
+
+ *IsMatch = FALSE;
+ DigestSize = mHashTable[HashIndex].DigestSize;
+ Entry = (Cache != NULL) ? CacheLookupOrInsert (Cache, CertDer, CertDerSize) : NULL;
+
+ //
+ // Hash directly into the cache slot when available; fall back to a
+ // stack buffer when the cache is full or absent.
+ //
+ if ((Entry != NULL) && Entry->TbsHashValid[HashIndex]) {
+ DigestBuf = Entry->TbsHash[HashIndex];
+ } else {
+ DigestBuf = (Entry != NULL) ? Entry->TbsHash[HashIndex] : LocalDigest;
+ Status = HashTbsCertificate (CertDer, CertDerSize, HashIndex, DigestBuf);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Entry != NULL) {
+ Entry->TbsHashValid[HashIndex] = TRUE;
+ }
+ }
+
+ if (CompareMem (DigestBuf, TbsCertHash, DigestSize) == 0) {
+ *IsMatch = TRUE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Walk a SignedData certificates field and search for a certificate
+ whose TBSCertificate digest equals TbsCertHash.
+
+ @param[in] CertSetBody Pointer to the contents of the [0]
+ IMPLICIT certificates field.
+ @param[in] CertSetBodyLen Length of CertSetBody.
+ @param[in] HashIndex Algorithm index.
+ @param[in] TbsCertHash Target digest bytes.
+ @param[in] Cache Optional cache.
+ @param[out] MatchCert On success, points within CertSetBody to
+ the matching certificate's first byte.
+ @param[out] MatchCertSize On success, size of the matching cert.
+
+ @retval EFI_SUCCESS Match found.
+ @retval EFI_NOT_FOUND No certificate matched.
+ @retval other Parse or hashing error.
+**/
+STATIC
+EFI_STATUS
+SearchCertificateSet (
+ IN CONST UINT8 *CertSetBody,
+ IN UINTN CertSetBodyLen,
+ IN UINTN HashIndex,
+ IN CONST UINT8 *TbsCertHash,
+ IN TRUST_ANCHOR_CACHE *Cache OPTIONAL,
+ OUT CONST UINT8 **MatchCert,
+ OUT UINTN *MatchCertSize
+ )
+{
+ CONST UINT8 *Cursor;
+ CONST UINT8 *End;
+ CONST UINT8 *CertStart;
+ CONST UINT8 *CertBody;
+ UINTN CertBodyLen;
+ EFI_STATUS Status;
+ BOOLEAN Match;
+
+ Cursor = CertSetBody;
+ End = CertSetBody + CertSetBodyLen;
+
+ while (Cursor < End) {
+ if (*Cursor != ASN1_TAG_SEQUENCE) {
+ //
+ // The certificates field may also contain extendedCertificate or
+ // other choices in older PKCS#7. Skip non-SEQUENCE TLVs.
+ //
+ CertStart = Cursor + 1;
+ Status = Asn1DecodeLength (&CertStart, End, &CertBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cursor = CertStart + CertBodyLen;
+ continue;
+ }
+
+ CertStart = Cursor;
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &CertBody, &CertBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = CertMatchesTbsHash (
+ Cache,
+ CertStart,
+ (UINTN)(Cursor - CertStart),
+ HashIndex,
+ TbsCertHash,
+ &Match
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Match) {
+ *MatchCert = CertStart;
+ *MatchCertSize = (UINTN)(Cursor - CertStart);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Parse a PKCS#7 SignedData blob and return the contents of the
+ certificates [0] IMPLICIT field.
+
+ Accepts both the bare SignedData encoding and a ContentInfo wrapper.
+
+ @param[in] AuthData Pointer to PKCS#7 / SignedData bytes.
+ @param[in] AuthDataSize Size in bytes.
+ @param[out] CertSetBody Receives a pointer into AuthData where the
+ certificates contents start.
+ @param[out] CertSetBodyLen Receives the length of the certificates
+ contents.
+
+ @retval EFI_SUCCESS Parsed and certificates field located.
+ @retval EFI_INVALID_PARAMETER Malformed input.
+ @retval EFI_NOT_FOUND The optional certificates field is
+ absent.
+**/
+STATIC
+EFI_STATUS
+LocateCertificatesField (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT CONST UINT8 **CertSetBody,
+ OUT UINTN *CertSetBodyLen
+ )
+{
+ CONST UINT8 *Cursor;
+ CONST UINT8 *End;
+ CONST UINT8 *Body;
+ UINTN BodyLen;
+ CONST UINT8 *OidBody;
+ UINTN OidLen;
+ EFI_STATUS Status;
+
+ Cursor = AuthData;
+ End = AuthData + AuthDataSize;
+
+ //
+ // Outer SEQUENCE.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // Detect ContentInfo: optional contentType OID followed by [0] EXPLICIT
+ // SignedData. If we see an OID first, peel the wrapper.
+ //
+ if ((Cursor < End) && (*Cursor == ASN1_TAG_OID)) {
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_OID, &OidBody, &OidLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((OidLen != sizeof (mPkcs7SignedDataOid)) ||
+ (CompareMem (OidBody, mPkcs7SignedDataOid, OidLen) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // [0] EXPLICIT SignedData wrapper.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_CTX_CONS_0, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // Inner SignedData SEQUENCE.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+ }
+
+ //
+ // Now Cursor..End contains the SignedData contents.
+ // version INTEGER
+ // digestAlgorithms SET
+ // encapContentInfo SEQUENCE
+ // certificates [0] IMPLICIT (optional) <-- the field we want
+ // crls [1] IMPLICIT (optional)
+ // signerInfos SET
+ //
+ // Skip version + digestAlgorithms + encapContentInfo by parsing each
+ // TLV and discarding its body.
+ //
+ // version (INTEGER, tag 0x02)
+ //
+ if (Cursor >= End) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Asn1ExpectTagged (&Cursor, End, *Cursor, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // digestAlgorithms (SET)
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SET, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // encapContentInfo (SEQUENCE)
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Optional certificates [0] IMPLICIT.
+ //
+ if ((Cursor < End) && (*Cursor == ASN1_TAG_CTX_CONS_0)) {
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_CTX_CONS_0, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *CertSetBody = Body;
+ *CertSetBodyLen = BodyLen;
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Locate, in a PKCS#7 SignedData blob, the X.509 certificate whose
+ TBSCertificate digest matches a caller-supplied hash, and return
+ that certificate as a newly allocated DER-encoded buffer.
+
+ See BaseCryptLib.h for the full contract.
+**/
+EFI_STATUS
+EFIAPI
+GetTrustAnchorX509FromAuthData (
+ IN OUT VOID **CacheHandle OPTIONAL,
+ IN CONST UINT8 *TbsCertHash,
+ IN UINTN TbsCertHashSize,
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT UINT8 **TrustAnchorX509,
+ OUT UINTN *TrustAnchorX509Size
+ )
+{
+ EFI_STATUS Status;
+ UINTN HashIndex;
+ CONST UINT8 *CertSetBody;
+ UINTN CertSetBodyLen;
+ CONST UINT8 *MatchCert;
+ UINTN MatchCertSize;
+ TRUST_ANCHOR_CACHE *Cache;
+ UINT8 *Output;
+
+ if ((TbsCertHash == NULL) || (TbsCertHashSize == 0) ||
+ (AuthData == NULL) || (AuthDataSize == 0) ||
+ (TrustAnchorX509 == NULL) || (TrustAnchorX509Size == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HashIndex = HashSizeToIndex (TbsCertHashSize);
+ if (HashIndex == (UINTN)-1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Optional cache. *CacheHandle == NULL means "allocate me one"; the
+ // caller releases it later via FreeTrustAnchorX509Cache().
+ //
+ Cache = NULL;
+ if (CacheHandle != NULL) {
+ if (*CacheHandle == NULL) {
+ Cache = AllocateZeroPool (sizeof (TRUST_ANCHOR_CACHE));
+ if (Cache == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Cache->Signature = TRUST_ANCHOR_CACHE_SIGNATURE;
+ *CacheHandle = Cache;
+ } else {
+ Cache = (TRUST_ANCHOR_CACHE *)*CacheHandle;
+ if (Cache->Signature != TRUST_ANCHOR_CACHE_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ Status = LocateCertificatesField (AuthData, AuthDataSize, &CertSetBody, &CertSetBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = SearchCertificateSet (
+ CertSetBody,
+ CertSetBodyLen,
+ HashIndex,
+ TbsCertHash,
+ Cache,
+ &MatchCert,
+ &MatchCertSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Output = AllocateCopyPool (MatchCertSize, MatchCert);
+ if (Output == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *TrustAnchorX509 = Output;
+ *TrustAnchorX509Size = MatchCertSize;
+ return EFI_SUCCESS;
+}
+
+/**
+ Release a trust-anchor cache previously allocated by
+ GetTrustAnchorX509FromAuthData().
+
+ @param[in] CacheHandle Cache handle, or NULL.
+**/
+VOID
+EFIAPI
+FreeTrustAnchorX509Cache (
+ IN VOID *CacheHandle OPTIONAL
+ )
+{
+ TRUST_ANCHOR_CACHE *Cache;
+ TRUST_ANCHOR_CACHE_ENTRY *Entry;
+ UINTN Index;
+
+ if (CacheHandle == NULL) {
+ return;
+ }
+
+ Cache = (TRUST_ANCHOR_CACHE *)CacheHandle;
+ if (Cache->Signature != TRUST_ANCHOR_CACHE_SIGNATURE) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ for (Index = 0; Index < Cache->Count; Index++) {
+ Entry = &Cache->Entries[Index];
+ if (Entry->CertDer != NULL) {
+ FreePool (Entry->CertDer);
+ }
+ }
+
+ ZeroMem (Cache, sizeof (TRUST_ANCHOR_CACHE));
+ FreePool (Cache);
+}
diff --git a/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptTrustAnchorNull.c b/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptTrustAnchorNull.c
new file mode 100644
index 000000000..2b121479b
--- /dev/null
+++ b/MbedTlsPkg/Library/BaseCryptLib/Pk/CryptTrustAnchorNull.c
@@ -0,0 +1,45 @@
+/** @file
+ GetTrustAnchorX509FromAuthData() / FreeTrustAnchorX509Cache() Null
+ implementation.
+
+ Returns EFI_UNSUPPORTED to indicate this interface is not provided by
+ the current library instance (e.g., PEI / Runtime / SEC / SMM phases).
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "InternalCryptLib.h"
+
+/**
+ Null stub for GetTrustAnchorX509FromAuthData().
+
+ @retval EFI_UNSUPPORTED This interface is not supported.
+**/
+EFI_STATUS
+EFIAPI
+GetTrustAnchorX509FromAuthData (
+ IN OUT VOID **CacheHandle OPTIONAL,
+ IN CONST UINT8 *TbsCertHash,
+ IN UINTN TbsCertHashSize,
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT UINT8 **TrustAnchorX509,
+ OUT UINTN *TrustAnchorX509Size
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Null stub for FreeTrustAnchorX509Cache(). No-op.
+**/
+VOID
+EFIAPI
+FreeTrustAnchorX509Cache (
+ IN VOID *CacheHandle OPTIONAL
+ )
+{
+ ASSERT (FALSE);
+}
diff --git a/MbedTlsPkg/Library/BaseCryptLib/RuntimeCryptLib.inf b/MbedTlsPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
index f3bbefb6a..8f94318c2 100644
--- a/MbedTlsPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
+++ b/MbedTlsPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
@@ -49,6 +49,8 @@
Pk/CryptDhNull.c
Pk/CryptX509.c
Pk/CryptAuthenticodeNull.c
+ Pk/CryptAuthenticodeHashNull.c # MU_CHANGE
+ Pk/CryptTrustAnchorNull.c # MU_CHANGE
Pk/CryptTsNull.c
Pk/CryptRsaPssNull.c
Pk/CryptRsaPssSignNull.c
diff --git a/MbedTlsPkg/Library/BaseCryptLib/SecCryptLib.inf b/MbedTlsPkg/Library/BaseCryptLib/SecCryptLib.inf
index 40b31e03b..c11de30bb 100644
--- a/MbedTlsPkg/Library/BaseCryptLib/SecCryptLib.inf
+++ b/MbedTlsPkg/Library/BaseCryptLib/SecCryptLib.inf
@@ -51,6 +51,8 @@
Pk/CryptPkcs7VerifyEkuNull.c
Pk/CryptX509Null.c
Pk/CryptAuthenticodeNull.c
+ Pk/CryptAuthenticodeHashNull.c # MU_CHANGE
+ Pk/CryptTrustAnchorNull.c # MU_CHANGE
Pk/CryptTsNull.c
Rand/CryptRandNull.c
SysCall/CrtWrapper.c
diff --git a/MbedTlsPkg/Library/BaseCryptLib/SmmCryptLib.inf b/MbedTlsPkg/Library/BaseCryptLib/SmmCryptLib.inf
index a1948cec6..3637dbba2 100644
--- a/MbedTlsPkg/Library/BaseCryptLib/SmmCryptLib.inf
+++ b/MbedTlsPkg/Library/BaseCryptLib/SmmCryptLib.inf
@@ -48,6 +48,8 @@
Pk/CryptDhNull.c
Pk/CryptX509.c
Pk/CryptAuthenticodeNull.c
+ Pk/CryptAuthenticodeHashNull.c # MU_CHANGE
+ Pk/CryptTrustAnchorNull.c # MU_CHANGE
Pk/CryptTsNull.c
Pk/CryptRsaPss.c
Pk/CryptRsaPssSignNull.c
diff --git a/MbedTlsPkg/Library/BaseCryptLib/TestBaseCryptLib.inf b/MbedTlsPkg/Library/BaseCryptLib/TestBaseCryptLib.inf
index b96088ec3..54323547d 100644
--- a/MbedTlsPkg/Library/BaseCryptLib/TestBaseCryptLib.inf
+++ b/MbedTlsPkg/Library/BaseCryptLib/TestBaseCryptLib.inf
@@ -42,6 +42,8 @@
Pk/CryptDhNull.c
Pk/CryptX509.c
Pk/CryptAuthenticode.c
+ Pk/CryptAuthenticodeHash.c # MU_CHANGE
+ Pk/CryptTrustAnchor.c # MU_CHANGE
Pk/CryptTs.c
Pem/CryptPem.c
Pk/CryptRsaPss.c
@@ -68,6 +70,13 @@
PrintLib
RngLib
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # Image hash type used by GetAuthenticodeHash
+ gEfiCertSha1Guid # MU_CHANGE
+ gEfiCertSha256Guid # MU_CHANGE
+ gEfiCertSha384Guid # MU_CHANGE
+ gEfiCertSha512Guid # MU_CHANGE
+
#
# Remove these [BuildOptions] after this library is cleaned up
#
diff --git a/MbedTlsPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf b/MbedTlsPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
index 7accd5c24..30402b807 100644
--- a/MbedTlsPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
+++ b/MbedTlsPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
@@ -48,6 +48,8 @@
Pk/CryptDhNull.c
Pk/CryptX509.c
Pk/CryptAuthenticode.c
+ Pk/CryptAuthenticodeHash.c # MU_CHANGE
+ Pk/CryptTrustAnchor.c # MU_CHANGE
Pk/CryptTs.c
Pk/CryptRsaPss.c
Pk/CryptRsaPssSign.c
@@ -74,6 +76,13 @@
PrintLib
RngLib
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # Image hash type used by GetAuthenticodeHash
+ gEfiCertSha1Guid # MU_CHANGE
+ gEfiCertSha256Guid # MU_CHANGE
+ gEfiCertSha384Guid # MU_CHANGE
+ gEfiCertSha512Guid # MU_CHANGE
+
#
# Remove these [BuildOptions] after this library is cleaned up
#
diff --git a/OneCryptoPkg/DriverBuild.py b/OneCryptoPkg/DriverBuild.py
index 90a60102a..b2aec1592 100644
--- a/OneCryptoPkg/DriverBuild.py
+++ b/OneCryptoPkg/DriverBuild.py
@@ -65,8 +65,13 @@ def GetDependencies():
return [
{
"Path": "MU_BASECORE",
- "Url": "https://github.com/microsoft/mu_basecore.git",
- "Commit": "922377cb580e03c45628ddb4e0a0871ccf2f6f2d"
+ # MU_CHANGE [TEMP] - pin to fork commit carrying the new
+ # BaseCryptLib/OneCrypto APIs (GetAuthenticodeHash,
+ # GetTrustAnchorX509FromAuthData, FreeTrustAnchorX509Cache,
+ # GetAuthenticodeHashAlgorithm, X509GetTbsCertHash). Revert to
+ # https://github.com/microsoft/mu_basecore.git once merged.
+ "Url": "https://github.com/flickdm/mu_basecore.git",
+ "Commit": "709ab9b016ecf0555142ba5bc0da55227f34a7cd"
},
{
"Path": "Features/MM_SUPV",
diff --git a/OneCryptoPkg/OneCryptoBin/OneCryptoBin.c b/OneCryptoPkg/OneCryptoBin/OneCryptoBin.c
index 051672e4d..6549a64a3 100644
--- a/OneCryptoPkg/OneCryptoBin/OneCryptoBin.c
+++ b/OneCryptoPkg/OneCryptoBin/OneCryptoBin.c
@@ -333,6 +333,16 @@ CryptoInit (
// ========================================================================================================
CryptoProtocol->GetCryptoProviderVersionString = GetCryptoProviderVersionString;
+
+ // ========================================================================================================
+ // v1.1 functions
+ // ========================================================================================================
+
+ CryptoProtocol->GetAuthenticodeHash = GetAuthenticodeHash;
+ CryptoProtocol->GetTrustAnchorX509FromAuthData = GetTrustAnchorX509FromAuthData;
+ CryptoProtocol->FreeTrustAnchorX509Cache = FreeTrustAnchorX509Cache;
+ CryptoProtocol->GetAuthenticodeHashAlgorithm = GetAuthenticodeHashAlgorithm;
+ CryptoProtocol->X509GetTbsCertHash = X509GetTbsCertHash;
}
/**
diff --git a/OpensslPkg/Library/BaseCryptLib/BaseCryptLib.inf b/OpensslPkg/Library/BaseCryptLib/BaseCryptLib.inf
index 2f4e10383..6ffd829a4 100644
--- a/OpensslPkg/Library/BaseCryptLib/BaseCryptLib.inf
+++ b/OpensslPkg/Library/BaseCryptLib/BaseCryptLib.inf
@@ -58,6 +58,8 @@
Pk/CryptDh.c
Pk/CryptX509.c
Pk/CryptAuthenticode.c
+ Pk/CryptAuthenticodeHash.c # MU_CHANGE
+ Pk/CryptTrustAnchor.c # MU_CHANGE
Pk/CryptTs.c
Pk/CryptRsaPss.c
Pk/CryptRsaPssSign.c
@@ -108,6 +110,13 @@
[Protocols]
# gEfiMpServiceProtocolGuid # MU_CHANGE
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # Image hash type used by GetAuthenticodeHash
+ gEfiCertSha1Guid # MU_CHANGE
+ gEfiCertSha256Guid # MU_CHANGE
+ gEfiCertSha384Guid # MU_CHANGE
+ gEfiCertSha512Guid # MU_CHANGE
+
#
# Remove these [BuildOptions] after this library is cleaned up
#
diff --git a/OpensslPkg/Library/BaseCryptLib/PeiCryptLib.inf b/OpensslPkg/Library/BaseCryptLib/PeiCryptLib.inf
index d4eb940ac..6a8acdc08 100644
--- a/OpensslPkg/Library/BaseCryptLib/PeiCryptLib.inf
+++ b/OpensslPkg/Library/BaseCryptLib/PeiCryptLib.inf
@@ -54,6 +54,8 @@
Pk/CryptDhNull.c
Pk/CryptX509Null.c
Pk/CryptAuthenticodeNull.c
+ Pk/CryptAuthenticodeHashNull.c # MU_CHANGE
+ Pk/CryptTrustAnchorNull.c # MU_CHANGE
Pk/CryptTsNull.c
Pk/CryptRsaPss.c
Pk/CryptRsaPssSignNull.c
diff --git a/OpensslPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHash.c b/OpensslPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHash.c
new file mode 100644
index 000000000..b45e0d765
--- /dev/null
+++ b/OpensslPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHash.c
@@ -0,0 +1,934 @@
+/** @file
+ PE/COFF Authenticode Image Hash computation.
+
+ Implements GetAuthenticodeHash() per the "Windows Authenticode Portable
+ Executable Signature Format" specification. The hash covers the entire
+ PE/COFF image except for the image checksum, the Certificate Table
+ data-directory entry, and the certificate table content itself.
+
+ Caution: This module operates on untrusted input (the PE/COFF image),
+ so each header field is validated against FileSize before use.
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "InternalCryptLib.h"
+
+#include
+#include
+
+//
+// Function pointer table type for a single hash algorithm.
+// We use the BaseCryptLib hash primitives so this implementation is
+// independent of the underlying crypto provider (OpenSSL / Mbed TLS).
+//
+typedef
+UINTN
+(EFIAPI *AUTH_HASH_GET_CONTEXT_SIZE)(
+ VOID
+ );
+
+typedef
+BOOLEAN
+(EFIAPI *AUTH_HASH_INIT)(
+ OUT VOID *HashContext
+ );
+
+typedef
+BOOLEAN
+(EFIAPI *AUTH_HASH_UPDATE)(
+ IN OUT VOID *HashContext,
+ IN CONST VOID *Data,
+ IN UINTN DataSize
+ );
+
+typedef
+BOOLEAN
+(EFIAPI *AUTH_HASH_FINAL)(
+ IN OUT VOID *HashContext,
+ OUT UINT8 *HashValue
+ );
+
+typedef struct {
+ CONST EFI_GUID *HashGuid;
+ UINTN DigestSize;
+ AUTH_HASH_GET_CONTEXT_SIZE GetContextSize;
+ AUTH_HASH_INIT Init;
+ AUTH_HASH_UPDATE Update;
+ AUTH_HASH_FINAL Final;
+} AUTH_HASH_INFO;
+
+//
+// Forward references to BaseCryptLib hash primitives. These are provided
+// by the BaseCryptLib hash sources in this same library instance.
+//
+STATIC CONST AUTH_HASH_INFO mAuthHashInfo[] = {
+ { &gEfiCertSha1Guid, SHA1_DIGEST_SIZE, Sha1GetContextSize, Sha1Init, Sha1Update, Sha1Final },
+ { &gEfiCertSha256Guid, SHA256_DIGEST_SIZE, Sha256GetContextSize, Sha256Init, Sha256Update, Sha256Final },
+ { &gEfiCertSha384Guid, SHA384_DIGEST_SIZE, Sha384GetContextSize, Sha384Init, Sha384Update, Sha384Final },
+ { &gEfiCertSha512Guid, SHA512_DIGEST_SIZE, Sha512GetContextSize, Sha512Init, Sha512Update, Sha512Final },
+};
+
+#define AUTH_HASH_INFO_COUNT (sizeof (mAuthHashInfo) / sizeof (mAuthHashInfo[0]))
+
+/**
+ Look up an entry in mAuthHashInfo by HashType GUID.
+
+ @param[in] HashType Signature-type GUID identifying the hash algorithm.
+
+ @retval Pointer to the AUTH_HASH_INFO on match.
+ @retval NULL if HashType does not match a supported algorithm.
+**/
+STATIC
+CONST AUTH_HASH_INFO *
+LookupAuthHashInfo (
+ IN CONST EFI_GUID *HashType
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < AUTH_HASH_INFO_COUNT; Index++) {
+ if (CompareGuid (HashType, mAuthHashInfo[Index].HashGuid)) {
+ return &mAuthHashInfo[Index];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ Compute the PE/COFF Authenticode-style image hash of a loaded image,
+ as described in the "Windows Authenticode Portable Executable
+ Signature Format" specification.
+
+ The caller selects the digest algorithm by HashType (e.g.
+ gEfiCertSha256Guid, gEfiCertSha384Guid). The digest is written to
+ Digest, which must be large enough to hold the largest supported
+ digest (at least SHA512_DIGEST_SIZE bytes).
+
+ Caution: This function may receive untrusted input. The PE/COFF image
+ is external input, so this function validates the image's data
+ structure before hashing.
+
+ @param[in] FileBuffer Pointer to the in-memory PE/COFF image.
+ @param[in] FileSize Size of FileBuffer in bytes.
+ @param[in] HashType Signature-type GUID identifying the hash
+ algorithm to use.
+ @param[out] Digest Caller-provided buffer that receives the
+ computed digest. Must be at least
+ SHA512_DIGEST_SIZE bytes.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_SUCCESS Digest was computed successfully.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL or
+ FileSize is 0.
+ @retval EFI_UNSUPPORTED HashType is not a recognized image
+ hash algorithm, or this interface is
+ not supported by the underlying
+ library instance.
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHash (
+ IN VOID *FileBuffer,
+ IN UINTN FileSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ EFI_STATUS Status;
+ CONST AUTH_HASH_INFO *HashInfo;
+ UINT8 *ImageBase;
+ UINTN PeCoffHeaderOffset;
+ EFI_IMAGE_DOS_HEADER *DosHdr;
+ EFI_IMAGE_OPTIONAL_HEADER_UNION *NtHdr;
+ UINT16 Magic;
+ UINT32 NumberOfRvaAndSizes;
+ UINT32 SizeOfHeaders;
+ UINT16 NumberOfSections;
+ UINT16 SizeOfOptionalHeader;
+ UINT8 *CheckSumPtr;
+ UINT8 *SecDirPtr;
+ EFI_IMAGE_DATA_DIRECTORY *SecDir;
+ UINT32 CertSize;
+ VOID *HashCtx;
+ UINTN CtxSize;
+ UINT8 *HashBase;
+ UINTN HashSize;
+ UINTN SumOfBytesHashed;
+ EFI_IMAGE_SECTION_HEADER *SectionHeaders;
+ EFI_IMAGE_SECTION_HEADER *Section;
+ UINTN SectionHeadersSize;
+ UINTN FirstSectionOffset;
+ UINTN Index;
+ UINTN Pos;
+
+ HashCtx = NULL;
+ SectionHeaders = NULL;
+ Status = EFI_INVALID_PARAMETER;
+
+ if ((FileBuffer == NULL) || (FileSize == 0) || (HashType == NULL) ||
+ (Digest == NULL) || (DigestSize == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HashInfo = LookupAuthHashInfo (HashType);
+ if (HashInfo == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ ImageBase = (UINT8 *)FileBuffer;
+
+ //
+ // Locate the PE header.
+ //
+ if (FileSize < sizeof (EFI_IMAGE_DOS_HEADER)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DosHdr = (EFI_IMAGE_DOS_HEADER *)ImageBase;
+ if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
+ PeCoffHeaderOffset = DosHdr->e_lfanew;
+ } else {
+ PeCoffHeaderOffset = 0;
+ }
+
+ if ((PeCoffHeaderOffset > FileSize) ||
+ ((FileSize - PeCoffHeaderOffset) < sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + sizeof (UINT16)))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NtHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)(ImageBase + PeCoffHeaderOffset);
+ if (NtHdr->Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // FileHeader is at the same offset for PE32 and PE32+.
+ //
+ SizeOfOptionalHeader = NtHdr->Pe32.FileHeader.SizeOfOptionalHeader;
+ NumberOfSections = NtHdr->Pe32.FileHeader.NumberOfSections;
+
+ //
+ // Make sure the optional header fits.
+ //
+ if ((FileSize - PeCoffHeaderOffset) <
+ sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + (UINTN)SizeOfOptionalHeader)
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Magic = NtHdr->Pe32.OptionalHeader.Magic;
+
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ if (SizeOfOptionalHeader < sizeof (EFI_IMAGE_OPTIONAL_HEADER32)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CheckSumPtr = (UINT8 *)&NtHdr->Pe32.OptionalHeader.CheckSum;
+ NumberOfRvaAndSizes = NtHdr->Pe32.OptionalHeader.NumberOfRvaAndSizes;
+ SizeOfHeaders = NtHdr->Pe32.OptionalHeader.SizeOfHeaders;
+ } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ if (SizeOfOptionalHeader < sizeof (EFI_IMAGE_OPTIONAL_HEADER64)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CheckSumPtr = (UINT8 *)&NtHdr->Pe32Plus.OptionalHeader.CheckSum;
+ NumberOfRvaAndSizes = NtHdr->Pe32Plus.OptionalHeader.NumberOfRvaAndSizes;
+ SizeOfHeaders = NtHdr->Pe32Plus.OptionalHeader.SizeOfHeaders;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // SizeOfHeaders must be within FileSize.
+ //
+ if ((SizeOfHeaders > FileSize) ||
+ (SizeOfHeaders < (PeCoffHeaderOffset + sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER) + (UINTN)SizeOfOptionalHeader)))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The section headers must lie within the headers region.
+ //
+ FirstSectionOffset = PeCoffHeaderOffset + sizeof (UINT32) +
+ sizeof (EFI_IMAGE_FILE_HEADER) + (UINTN)SizeOfOptionalHeader;
+ SectionHeadersSize = (UINTN)NumberOfSections * sizeof (EFI_IMAGE_SECTION_HEADER);
+ if ((SectionHeadersSize / sizeof (EFI_IMAGE_SECTION_HEADER)) != (UINTN)NumberOfSections) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((FirstSectionOffset > FileSize) ||
+ ((FileSize - FirstSectionOffset) < SectionHeadersSize) ||
+ ((FirstSectionOffset + SectionHeadersSize) > SizeOfHeaders))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate and initialize the hash context.
+ //
+ CtxSize = HashInfo->GetContextSize ();
+ HashCtx = AllocatePool (CtxSize);
+ if (HashCtx == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!HashInfo->Init (HashCtx)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ //
+ // Step 1-4: Hash the image header from its base to the start of the
+ // CheckSum field.
+ //
+ HashBase = ImageBase;
+ HashSize = (UINTN)(CheckSumPtr - ImageBase);
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ //
+ // Step 5: Skip over the image checksum (4 bytes).
+ // Step 6/7: Hash everything from the end of the checksum to either the
+ // end of the optional header or the start of the Cert Directory entry.
+ //
+ if (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY) {
+ HashBase = CheckSumPtr + sizeof (UINT32);
+ HashSize = SizeOfHeaders - (UINTN)(HashBase - ImageBase);
+ SecDir = NULL;
+ } else {
+ if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
+ SecDirPtr = (UINT8 *)&NtHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ SecDir = &NtHdr->Pe32.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ } else {
+ SecDirPtr = (UINT8 *)&NtHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ SecDir = &NtHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
+ }
+
+ HashBase = CheckSumPtr + sizeof (UINT32);
+ HashSize = (UINTN)(SecDirPtr - HashBase);
+ }
+
+ if (HashSize != 0) {
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+ }
+
+ //
+ // Step 8/9: Skip the Cert Directory entry. Hash from end of cert dir
+ // entry to end of image header.
+ //
+ if (SecDir != NULL) {
+ HashBase = SecDirPtr + sizeof (EFI_IMAGE_DATA_DIRECTORY);
+ HashSize = SizeOfHeaders - (UINTN)(HashBase - ImageBase);
+ if (HashSize != 0) {
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+ }
+ }
+
+ //
+ // Step 10: SUM_OF_BYTES_HASHED = SizeOfHeaders.
+ //
+ SumOfBytesHashed = SizeOfHeaders;
+
+ //
+ // Step 11-12: Build a sorted table of section headers.
+ //
+ if (NumberOfSections != 0) {
+ SectionHeaders = AllocateZeroPool (SectionHeadersSize);
+ if (SectionHeaders == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Done;
+ }
+
+ Section = (EFI_IMAGE_SECTION_HEADER *)(ImageBase + FirstSectionOffset);
+
+ //
+ // Insertion sort by PointerToRawData.
+ //
+ for (Index = 0; Index < (UINTN)NumberOfSections; Index++) {
+ Pos = Index;
+ while ((Pos > 0) && (Section->PointerToRawData < SectionHeaders[Pos - 1].PointerToRawData)) {
+ CopyMem (&SectionHeaders[Pos], &SectionHeaders[Pos - 1], sizeof (EFI_IMAGE_SECTION_HEADER));
+ Pos--;
+ }
+
+ CopyMem (&SectionHeaders[Pos], Section, sizeof (EFI_IMAGE_SECTION_HEADER));
+ Section++;
+ }
+
+ //
+ // Step 13-15: Hash each section in order.
+ //
+ for (Index = 0; Index < (UINTN)NumberOfSections; Index++) {
+ Section = &SectionHeaders[Index];
+ if (Section->SizeOfRawData == 0) {
+ continue;
+ }
+
+ //
+ // Validate the section bounds against the file size.
+ //
+ if ((Section->PointerToRawData > FileSize) ||
+ ((FileSize - Section->PointerToRawData) < (UINTN)Section->SizeOfRawData))
+ {
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+
+ HashBase = ImageBase + Section->PointerToRawData;
+ HashSize = (UINTN)Section->SizeOfRawData;
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ SumOfBytesHashed += HashSize;
+ }
+ }
+
+ //
+ // Step 16: Hash any trailing bytes between SUM_OF_BYTES_HASHED and the
+ // start of the certificate table (or end of file if no cert table).
+ //
+ if (FileSize > SumOfBytesHashed) {
+ HashBase = ImageBase + SumOfBytesHashed;
+
+ if ((SecDir == NULL) || (NumberOfRvaAndSizes <= EFI_IMAGE_DIRECTORY_ENTRY_SECURITY)) {
+ CertSize = 0;
+ } else {
+ CertSize = SecDir->Size;
+ }
+
+ if (FileSize > (UINTN)CertSize + SumOfBytesHashed) {
+ HashSize = FileSize - (UINTN)CertSize - SumOfBytesHashed;
+ if (!HashInfo->Update (HashCtx, HashBase, HashSize)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+ } else if (FileSize < (UINTN)CertSize + SumOfBytesHashed) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+ }
+
+ if (!HashInfo->Final (HashCtx, Digest)) {
+ Status = EFI_DEVICE_ERROR;
+ goto Done;
+ }
+
+ *DigestSize = HashInfo->DigestSize;
+ Status = EFI_SUCCESS;
+
+Done:
+ if (HashCtx != NULL) {
+ FreePool (HashCtx);
+ }
+
+ if (SectionHeaders != NULL) {
+ FreePool (SectionHeaders);
+ }
+
+ return Status;
+}
+
+//
+// ===========================================================================
+// Authenticode hash-algorithm discovery (SpcIndirectDataContent parsing)
+// ===========================================================================
+//
+// The functions below walk the PKCS#7 SignedData ASN.1 structure to recover
+// the digest algorithm recorded by the signer, without depending on any
+// particular crypto provider. AuthData is untrusted, so every length field
+// is decoded with bounds checking.
+//
+
+//
+// ASN.1 DER tag bytes used while walking the PKCS#7 SignedData structure.
+//
+#define AUTH_ASN1_TAG_INTEGER 0x02
+#define AUTH_ASN1_TAG_OID 0x06
+#define AUTH_ASN1_TAG_SEQUENCE 0x30
+#define AUTH_ASN1_TAG_SET 0x31
+#define AUTH_ASN1_TAG_CTX_CONS_0 0xA0 // [0] EXPLICIT, constructed
+
+//
+// OID 1.2.840.113549.1.7.2 (PKCS#7 signedData) - DER value bytes.
+//
+STATIC CONST UINT8 mAuthPkcs7SignedDataOid[] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02
+};
+
+//
+// OID 1.3.6.1.4.1.311.2.1.4 (SPC_INDIRECT_DATA_OBJID) - DER value bytes.
+//
+STATIC CONST UINT8 mAuthSpcIndirectDataOid[] = {
+ 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04
+};
+
+//
+// Digest-algorithm OIDs (DER value bytes) recognized by Authenticode.
+//
+STATIC CONST UINT8 mAuthOidSha1[] = {
+ 0x2B, 0x0E, 0x03, 0x02, 0x1A // 1.3.14.3.2.26
+};
+STATIC CONST UINT8 mAuthOidSha256[] = {
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 // 2.16.840.1.101.3.4.2.1
+};
+STATIC CONST UINT8 mAuthOidSha384[] = {
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 // 2.16.840.1.101.3.4.2.2
+};
+STATIC CONST UINT8 mAuthOidSha512[] = {
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 // 2.16.840.1.101.3.4.2.3
+};
+
+//
+// Mapping of digest-algorithm OID to the signature-type GUID consumed by
+// GetAuthenticodeHash().
+//
+typedef struct {
+ CONST UINT8 *Oid;
+ UINTN OidSize;
+ CONST EFI_GUID *HashGuid;
+} AUTH_DIGEST_OID_INFO;
+
+STATIC CONST AUTH_DIGEST_OID_INFO mAuthDigestOidInfo[] = {
+ { mAuthOidSha1, sizeof (mAuthOidSha1), &gEfiCertSha1Guid },
+ { mAuthOidSha256, sizeof (mAuthOidSha256), &gEfiCertSha256Guid },
+ { mAuthOidSha384, sizeof (mAuthOidSha384), &gEfiCertSha384Guid },
+ { mAuthOidSha512, sizeof (mAuthOidSha512), &gEfiCertSha512Guid },
+};
+
+#define AUTH_DIGEST_OID_INFO_COUNT (sizeof (mAuthDigestOidInfo) / sizeof (mAuthDigestOidInfo[0]))
+
+/**
+ Decode an ASN.1 DER length field starting at *Cursor. On success, the
+ decoded length is written to *Length and *Cursor is advanced past the
+ length octets.
+
+ Only the definite form is accepted. Lengths > the remaining input are
+ rejected.
+
+ @param[in,out] Cursor Pointer to the cursor pointer; advanced on
+ success.
+ @param[in] End One past the last valid input byte.
+ @param[out] Length Receives the decoded content length.
+
+ @retval EFI_SUCCESS Length parsed.
+ @retval EFI_INVALID_PARAMETER Malformed encoding or out of bounds.
+**/
+STATIC
+EFI_STATUS
+Asn1DecodeLength (
+ IN OUT CONST UINT8 **Cursor,
+ IN CONST UINT8 *End,
+ OUT UINTN *Length
+ )
+{
+ CONST UINT8 *P;
+ UINTN Result;
+ UINTN NumOctets;
+ UINTN Index;
+
+ P = *Cursor;
+ if (P >= End) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*P & 0x80) == 0) {
+ Result = (UINTN)*P;
+ P++;
+ } else {
+ NumOctets = (UINTN)(*P & 0x7F);
+ P++;
+ //
+ // Reject indefinite (0x80) and lengths longer than UINTN.
+ //
+ if ((NumOctets == 0) || (NumOctets > sizeof (UINTN))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((UINTN)(End - P) < NumOctets) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Result = 0;
+ for (Index = 0; Index < NumOctets; Index++) {
+ Result = (Result << 8) | P[Index];
+ }
+
+ P += NumOctets;
+ }
+
+ if ((UINTN)(End - P) < Result) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Cursor = P;
+ *Length = Result;
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse an ASN.1 DER TLV at *Cursor and require Tag. On success, *Body
+ points to the value bytes, *BodyLen is the value length, and *Cursor
+ is advanced past the entire TLV.
+
+ @param[in,out] Cursor Cursor pointer.
+ @param[in] End One past the last valid input byte.
+ @param[in] Tag Required tag byte.
+ @param[out] Body Receives a pointer to the value bytes.
+ @param[out] BodyLen Receives the value length.
+
+ @retval EFI_SUCCESS TLV parsed.
+ @retval EFI_INVALID_PARAMETER Wrong tag or malformed encoding.
+**/
+STATIC
+EFI_STATUS
+Asn1ExpectTagged (
+ IN OUT CONST UINT8 **Cursor,
+ IN CONST UINT8 *End,
+ IN UINT8 Tag,
+ OUT CONST UINT8 **Body,
+ OUT UINTN *BodyLen
+ )
+{
+ CONST UINT8 *P;
+ EFI_STATUS Status;
+ UINTN Length;
+
+ P = *Cursor;
+ if ((P >= End) || (*P != Tag)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ P++;
+ Status = Asn1DecodeLength (&P, End, &Length);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *Body = P;
+ *BodyLen = Length;
+ *Cursor = P + Length;
+ return EFI_SUCCESS;
+}
+
+/**
+ Determine the image-hash algorithm used by an Authenticode signature.
+
+ Parses the PKCS#7 SignedData blob's SpcIndirectDataContent
+ (OID 1.3.6.1.4.1.311.2.1.4) and reads the digestAlgorithm of its
+ embedded messageDigest DigestInfo, mapping it to the corresponding
+ signature-type GUID. The recovered GUID can be passed directly to
+ GetAuthenticodeHash() as its HashType.
+
+ Caution: AuthData is untrusted. The ASN.1 DER is parsed with
+ bounds-checked length decoding to avoid out-of-bounds reads.
+
+ @param[in] AuthData Pointer to the PKCS#7 SignedData blob
+ (DER-encoded Authenticode signature).
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] HashType On success, receives the signature-type
+ GUID identifying the digest algorithm.
+
+ @retval EFI_SUCCESS The hash algorithm was identified.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL,
+ AuthDataSize is 0, or AuthData is not a
+ well-formed Authenticode SignedData
+ blob.
+ @retval EFI_UNSUPPORTED The digest algorithm is not a
+ recognized image hash algorithm.
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHashAlgorithm (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT EFI_GUID *HashType
+ )
+{
+ EFI_STATUS Status;
+ CONST UINT8 *Cursor;
+ CONST UINT8 *End;
+ CONST UINT8 *Body;
+ UINTN BodyLen;
+ CONST UINT8 *OidBody;
+ UINTN OidLen;
+ UINTN Index;
+
+ if ((AuthData == NULL) || (AuthDataSize == 0) || (HashType == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = AuthData;
+ End = AuthData + AuthDataSize;
+
+ //
+ // ContentInfo ::= SEQUENCE { contentType OID, content [0] EXPLICIT }
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // contentType OID == pkcs7-signedData (1.2.840.113549.1.7.2).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_OID, &OidBody, &OidLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((OidLen != sizeof (mAuthPkcs7SignedDataOid)) ||
+ (CompareMem (OidBody, mAuthPkcs7SignedDataOid, OidLen) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // content [0] EXPLICIT -> SignedData.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_CTX_CONS_0, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // SignedData ::= SEQUENCE { version, digestAlgorithms, encapContentInfo, ... }
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // version INTEGER (skip).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_INTEGER, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // digestAlgorithms SET (skip).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SET, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // encapContentInfo ::= SEQUENCE { contentType OID, content [0] EXPLICIT }.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // contentType OID == SPC_INDIRECT_DATA (1.3.6.1.4.1.311.2.1.4).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_OID, &OidBody, &OidLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((OidLen != sizeof (mAuthSpcIndirectDataOid)) ||
+ (CompareMem (OidBody, mAuthSpcIndirectDataOid, OidLen) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // content [0] EXPLICIT -> SpcIndirectDataContent.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_CTX_CONS_0, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // SpcIndirectDataContent ::= SEQUENCE { data, messageDigest }.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // data SpcAttributeTypeAndOptionalValue SEQUENCE (skip).
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // messageDigest DigestInfo ::= SEQUENCE { digestAlgorithm, digest }.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // digestAlgorithm AlgorithmIdentifier ::= SEQUENCE { algorithm OID, ... }.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // algorithm OID.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, AUTH_ASN1_TAG_OID, &OidBody, &OidLen);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < AUTH_DIGEST_OID_INFO_COUNT; Index++) {
+ if ((OidLen == mAuthDigestOidInfo[Index].OidSize) &&
+ (CompareMem (OidBody, mAuthDigestOidInfo[Index].Oid, OidLen) == 0))
+ {
+ CopyGuid (HashType, mAuthDigestOidInfo[Index].HashGuid);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Compute the digest of the TBSCertificate of an X.509 certificate.
+
+ Extracts the TBSCertificate (the to-be-signed portion) of the given
+ DER-encoded certificate and hashes it with the algorithm selected by
+ HashType. The TBSCertificate is the exact byte range a certificate
+ authority signs, so its digest uniquely identifies the certificate
+ independent of the issuer signature.
+
+ The caller selects the digest algorithm by HashType (e.g.
+ gEfiCertSha256Guid, gEfiCertSha384Guid). The digest is written to
+ Digest, which must be large enough to hold the largest supported
+ digest (at least SHA512_DIGEST_SIZE bytes).
+
+ @param[in] Cert Pointer to the DER-encoded X.509 certificate.
+ @param[in] CertSize Size of Cert in bytes.
+ @param[in] HashType Signature-type GUID identifying the hash
+ algorithm to use.
+ @param[out] Digest Caller-provided buffer that receives the
+ computed TBSCertificate digest. Must be at
+ least SHA512_DIGEST_SIZE bytes.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_SUCCESS Digest was computed successfully.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL, CertSize is
+ 0, or Cert is not a well-formed X.509
+ certificate.
+ @retval EFI_UNSUPPORTED HashType is not a recognized image
+ hash algorithm.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate the hash context.
+ @retval EFI_DEVICE_ERROR A hash primitive failed.
+**/
+EFI_STATUS
+EFIAPI
+X509GetTbsCertHash (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ EFI_STATUS Status;
+ CONST AUTH_HASH_INFO *HashInfo;
+ UINT8 *TbsCert;
+ UINTN TbsCertSize;
+ VOID *HashCtx;
+ UINTN CtxSize;
+
+ if ((Cert == NULL) || (CertSize == 0) || (HashType == NULL) ||
+ (Digest == NULL) || (DigestSize == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HashInfo = LookupAuthHashInfo (HashType);
+ if (HashInfo == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Extract the TBSCertificate byte range. X509GetTBSCert() returns a
+ // pointer into Cert (it does not allocate), so TbsCert must not be
+ // freed here.
+ //
+ TbsCert = NULL;
+ TbsCertSize = 0;
+ if (!X509GetTBSCert ((CONST UINT8 *)Cert, CertSize, &TbsCert, &TbsCertSize)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate and initialize the hash context, then hash the
+ // TBSCertificate bytes.
+ //
+ CtxSize = HashInfo->GetContextSize ();
+ HashCtx = AllocatePool (CtxSize);
+ if (HashCtx == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_DEVICE_ERROR;
+ if (HashInfo->Init (HashCtx) &&
+ HashInfo->Update (HashCtx, TbsCert, TbsCertSize) &&
+ HashInfo->Final (HashCtx, Digest))
+ {
+ *DigestSize = HashInfo->DigestSize;
+ Status = EFI_SUCCESS;
+ }
+
+ FreePool (HashCtx);
+ return Status;
+}
diff --git a/OpensslPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHashNull.c b/OpensslPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHashNull.c
new file mode 100644
index 000000000..f51b95e78
--- /dev/null
+++ b/OpensslPkg/Library/BaseCryptLib/Pk/CryptAuthenticodeHashNull.c
@@ -0,0 +1,98 @@
+/** @file
+ GetAuthenticodeHash() Null implementation.
+
+ Returns EFI_UNSUPPORTED to indicate this interface is not provided by
+ the current library instance (e.g., PEI / Runtime / SEC / SMM phases
+ that do not include the Pk/CryptAuthenticodeHash.c implementation).
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "InternalCryptLib.h"
+
+/**
+ Compute the PE/COFF Authenticode-style image hash of a loaded image.
+
+ Return EFI_UNSUPPORTED to indicate this interface is not supported.
+
+ @param[in] FileBuffer Pointer to the in-memory PE/COFF image.
+ @param[in] FileSize Size of FileBuffer in bytes.
+ @param[in] HashType Signature-type GUID identifying the hash
+ algorithm to use.
+ @param[out] Digest Caller-provided buffer that receives the
+ computed digest.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_UNSUPPORTED This interface is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHash (
+ IN VOID *FileBuffer,
+ IN UINTN FileSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Determine the image-hash algorithm used by an Authenticode signature.
+
+ Return EFI_UNSUPPORTED to indicate this interface is not supported.
+
+ @param[in] AuthData Pointer to the PKCS#7 SignedData blob.
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] HashType Receives the signature-type GUID.
+
+ @retval EFI_UNSUPPORTED This interface is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHashAlgorithm (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT EFI_GUID *HashType
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Compute the digest of the TBSCertificate of an X.509 certificate.
+
+ Return EFI_UNSUPPORTED to indicate this interface is not supported.
+
+ @param[in] Cert Pointer to the DER-encoded X.509 certificate.
+ @param[in] CertSize Size of Cert in bytes.
+ @param[in] HashType Signature-type GUID identifying the hash
+ algorithm to use.
+ @param[out] Digest Caller-provided buffer that receives the
+ computed digest.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_UNSUPPORTED This interface is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+X509GetTbsCertHash (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
diff --git a/OpensslPkg/Library/BaseCryptLib/Pk/CryptTrustAnchor.c b/OpensslPkg/Library/BaseCryptLib/Pk/CryptTrustAnchor.c
new file mode 100644
index 000000000..f743e98d3
--- /dev/null
+++ b/OpensslPkg/Library/BaseCryptLib/Pk/CryptTrustAnchor.c
@@ -0,0 +1,752 @@
+/** @file
+ GetTrustAnchorX509FromAuthData() and the matching cache lifecycle.
+
+ Walks a PKCS#7 SignedData blob, hashes each embedded X.509 certificate's
+ TBSCertificate, and returns the certificate whose digest matches a
+ caller-supplied hash. Optional cache memoizes (cert -> TBS-hash[s])
+ across calls so callers that ask repeatedly do not re-hash the same
+ certificates.
+
+ Caution: AuthData is treated as untrusted input. Each ASN.1 length is
+ bounds-checked against the remaining input before the parser advances.
+
+ Reference:
+ RFC 2315 / RFC 5652 (PKCS#7 / CMS SignedData)
+ RFC 5280 (X.509 Certificate)
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "InternalCryptLib.h"
+
+//
+// ASN.1 DER constants used during the walk.
+//
+#define ASN1_TAG_SEQUENCE 0x30
+#define ASN1_TAG_SET 0x31
+#define ASN1_TAG_OID 0x06
+#define ASN1_TAG_CTX_CONS_0 0xA0 // [0] EXPLICIT / IMPLICIT constructed
+
+//
+// signedData OID 1.2.840.113549.1.7.2 = 06 09 2A 86 48 86 F7 0D 01 07 02
+//
+STATIC CONST UINT8 mPkcs7SignedDataOid[] = {
+ 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02
+};
+
+//
+// Hash algorithm dispatch table; selected by digest length. The one-shot
+// ShaXxxHashAll() entry points handle context allocation, init, update,
+// and finalization internally, so the table only needs the digest size
+// and the function pointer.
+//
+typedef
+BOOLEAN
+(EFIAPI *AUTH_HASH_ALL)(
+ IN CONST VOID *Data,
+ IN UINTN DataSize,
+ OUT UINT8 *HashValue
+ );
+
+typedef struct {
+ UINTN DigestSize;
+ AUTH_HASH_ALL HashAll;
+} TRUST_ANCHOR_HASH_INFO;
+
+STATIC CONST TRUST_ANCHOR_HASH_INFO mHashTable[] = {
+ { SHA1_DIGEST_SIZE, Sha1HashAll },
+ { SHA256_DIGEST_SIZE, Sha256HashAll },
+ { SHA384_DIGEST_SIZE, Sha384HashAll },
+ { SHA512_DIGEST_SIZE, Sha512HashAll },
+};
+
+#define HASH_TABLE_COUNT (sizeof (mHashTable) / sizeof (mHashTable[0]))
+
+//
+// Cache structures. The cache stores up to TRUST_ANCHOR_CACHE_MAX entries;
+// each entry is a copy of the certificate DER bytes plus a per-algorithm
+// TBSCertificate digest computed lazily on first use. The digests are
+// inlined (not heap-allocated) — at four 64-byte slots per entry the
+// cost is bounded and the cache lifecycle has no inner allocations to
+// fail or leak.
+//
+#define TRUST_ANCHOR_CACHE_SIGNATURE SIGNATURE_32 ('T', 'A', 'X', 'C')
+#define TRUST_ANCHOR_CACHE_MAX 64
+
+typedef struct {
+ UINT8 *CertDer; // Allocated cert DER copy.
+ UINTN CertDerSize;
+ BOOLEAN TbsHashValid[HASH_TABLE_COUNT];
+ UINT8 TbsHash[HASH_TABLE_COUNT][SHA512_DIGEST_SIZE];
+} TRUST_ANCHOR_CACHE_ENTRY;
+
+typedef struct {
+ UINT32 Signature;
+ UINTN Count;
+ TRUST_ANCHOR_CACHE_ENTRY Entries[TRUST_ANCHOR_CACHE_MAX];
+} TRUST_ANCHOR_CACHE;
+
+/**
+ Map a digest size to an index in mHashTable.
+
+ @param[in] HashSize Digest length in bytes.
+
+ @retval Index into mHashTable on match.
+ @retval (UINTN)-1 if the size is not supported.
+**/
+STATIC
+UINTN
+HashSizeToIndex (
+ IN UINTN HashSize
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < HASH_TABLE_COUNT; Index++) {
+ if (mHashTable[Index].DigestSize == HashSize) {
+ return Index;
+ }
+ }
+
+ return (UINTN)-1;
+}
+
+/**
+ Decode an ASN.1 DER length field starting at *Cursor. On success, the
+ decoded length is written to *Length and *Cursor is advanced past the
+ length octets.
+
+ Only the definite form is accepted. Lengths > the remaining input are
+ rejected.
+
+ @param[in,out] Cursor Pointer to the cursor pointer; advanced on
+ success.
+ @param[in] End One past the last valid input byte.
+ @param[out] Length Receives the decoded content length.
+
+ @retval EFI_SUCCESS Length parsed.
+ @retval EFI_INVALID_PARAMETER Malformed encoding or out of bounds.
+**/
+STATIC
+EFI_STATUS
+Asn1DecodeLength (
+ IN OUT CONST UINT8 **Cursor,
+ IN CONST UINT8 *End,
+ OUT UINTN *Length
+ )
+{
+ CONST UINT8 *P;
+ UINTN Result;
+ UINTN NumOctets;
+ UINTN Index;
+
+ P = *Cursor;
+ if (P >= End) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*P & 0x80) == 0) {
+ Result = (UINTN)*P;
+ P++;
+ } else {
+ NumOctets = (UINTN)(*P & 0x7F);
+ P++;
+ //
+ // Reject indefinite (0x80) and lengths longer than UINTN.
+ //
+ if ((NumOctets == 0) || (NumOctets > sizeof (UINTN))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((UINTN)(End - P) < NumOctets) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Result = 0;
+ for (Index = 0; Index < NumOctets; Index++) {
+ Result = (Result << 8) | P[Index];
+ }
+
+ P += NumOctets;
+ }
+
+ if ((UINTN)(End - P) < Result) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Cursor = P;
+ *Length = Result;
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse an ASN.1 DER TLV at *Cursor and require Tag. On success, *Body
+ points to the value bytes, *BodyLen is the value length, and *Cursor
+ is advanced past the entire TLV.
+
+ @param[in,out] Cursor Cursor pointer.
+ @param[in] End One past the last valid input byte.
+ @param[in] Tag Required tag byte.
+ @param[out] Body Receives a pointer to the value bytes.
+ @param[out] BodyLen Receives the value length.
+
+ @retval EFI_SUCCESS TLV parsed.
+ @retval EFI_INVALID_PARAMETER Wrong tag or malformed encoding.
+**/
+STATIC
+EFI_STATUS
+Asn1ExpectTagged (
+ IN OUT CONST UINT8 **Cursor,
+ IN CONST UINT8 *End,
+ IN UINT8 Tag,
+ OUT CONST UINT8 **Body,
+ OUT UINTN *BodyLen
+ )
+{
+ CONST UINT8 *P;
+ EFI_STATUS Status;
+ UINTN Length;
+
+ P = *Cursor;
+ if ((P >= End) || (*P != Tag)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ P++;
+ Status = Asn1DecodeLength (&P, End, &Length);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *Body = P;
+ *BodyLen = Length;
+ *Cursor = P + Length;
+ return EFI_SUCCESS;
+}
+
+/**
+ Compute the TBSCertificate digest of an X.509 certificate.
+
+ @param[in] CertDer Pointer to the DER-encoded Certificate.
+ @param[in] CertDerSize Size of the certificate in bytes.
+ @param[in] HashIndex Algorithm index in mHashTable.
+ @param[out] Digest Caller-allocated buffer for the digest. Must
+ be at least mHashTable[HashIndex].DigestSize
+ bytes.
+
+ @retval EFI_SUCCESS Digest computed.
+ @retval EFI_INVALID_PARAMETER CertDer is not a well-formed Certificate.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate hash context.
+ @retval EFI_DEVICE_ERROR Hash primitive failed.
+**/
+STATIC
+EFI_STATUS
+HashTbsCertificate (
+ IN CONST UINT8 *CertDer,
+ IN UINTN CertDerSize,
+ IN UINTN HashIndex,
+ OUT UINT8 *Digest
+ )
+{
+ CONST UINT8 *Cursor;
+ CONST UINT8 *End;
+ CONST UINT8 *CertBody;
+ UINTN CertBodyLen;
+ CONST UINT8 *TbsStart;
+ CONST UINT8 *TbsBody;
+ UINTN TbsBodyLen;
+ EFI_STATUS Status;
+
+ Cursor = CertDer;
+ End = CertDer + CertDerSize;
+
+ //
+ // Certificate ::= SEQUENCE
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &CertBody, &CertBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // First element of Certificate is TBSCertificate ::= SEQUENCE.
+ // Hash that whole TLV (tag + length + value) per RFC 5280 4.1.2.
+ //
+ TbsStart = CertBody;
+ Cursor = CertBody;
+ Status = Asn1ExpectTagged (&Cursor, CertBody + CertBodyLen, ASN1_TAG_SEQUENCE, &TbsBody, &TbsBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!mHashTable[HashIndex].HashAll (TbsStart, (UINTN)(Cursor - TbsStart), Digest)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Look up a certificate in the cache by DER bytes. If found, return the
+ entry; otherwise append a new entry with a copy of the cert bytes if
+ the cache has room.
+
+ @param[in] Cache Cache handle.
+ @param[in] CertDer Pointer to certificate DER bytes.
+ @param[in] CertDerSize Size of certificate in bytes.
+
+ @retval Pointer to the matching or newly inserted entry on success.
+ @retval NULL when the cache is full or memory allocation failed.
+**/
+STATIC
+TRUST_ANCHOR_CACHE_ENTRY *
+CacheLookupOrInsert (
+ IN TRUST_ANCHOR_CACHE *Cache,
+ IN CONST UINT8 *CertDer,
+ IN UINTN CertDerSize
+ )
+{
+ UINTN Index;
+ TRUST_ANCHOR_CACHE_ENTRY *Entry;
+
+ for (Index = 0; Index < Cache->Count; Index++) {
+ Entry = &Cache->Entries[Index];
+ if ((Entry->CertDerSize == CertDerSize) &&
+ (CompareMem (Entry->CertDer, CertDer, CertDerSize) == 0))
+ {
+ return Entry;
+ }
+ }
+
+ if (Cache->Count >= TRUST_ANCHOR_CACHE_MAX) {
+ return NULL;
+ }
+
+ Entry = &Cache->Entries[Cache->Count];
+ Entry->CertDer = AllocateCopyPool (CertDerSize, CertDer);
+ if (Entry->CertDer == NULL) {
+ return NULL;
+ }
+
+ Entry->CertDerSize = CertDerSize;
+ ZeroMem (Entry->TbsHashValid, sizeof (Entry->TbsHashValid));
+ Cache->Count++;
+ return Entry;
+}
+
+/**
+ Compare a certificate's TBS digest at HashIndex against the supplied
+ TbsCertHash, using the cache when available to avoid re-hashing.
+
+ @param[in] Cache Cache or NULL.
+ @param[in] CertDer Pointer to certificate DER bytes.
+ @param[in] CertDerSize Size of certificate in bytes.
+ @param[in] HashIndex Algorithm index in mHashTable.
+ @param[in] TbsCertHash Target digest bytes.
+ @param[out] IsMatch TRUE on match; FALSE otherwise.
+
+ @retval EFI_SUCCESS IsMatch was set.
+ @retval other A hashing or allocation error occurred.
+**/
+STATIC
+EFI_STATUS
+CertMatchesTbsHash (
+ IN TRUST_ANCHOR_CACHE *Cache OPTIONAL,
+ IN CONST UINT8 *CertDer,
+ IN UINTN CertDerSize,
+ IN UINTN HashIndex,
+ IN CONST UINT8 *TbsCertHash,
+ OUT BOOLEAN *IsMatch
+ )
+{
+ EFI_STATUS Status;
+ UINTN DigestSize;
+ TRUST_ANCHOR_CACHE_ENTRY *Entry;
+ UINT8 LocalDigest[SHA512_DIGEST_SIZE];
+ UINT8 *DigestBuf;
+
+ *IsMatch = FALSE;
+ DigestSize = mHashTable[HashIndex].DigestSize;
+ Entry = (Cache != NULL) ? CacheLookupOrInsert (Cache, CertDer, CertDerSize) : NULL;
+
+ //
+ // Hash directly into the cache slot when available; fall back to a
+ // stack buffer when the cache is full or absent.
+ //
+ if ((Entry != NULL) && Entry->TbsHashValid[HashIndex]) {
+ DigestBuf = Entry->TbsHash[HashIndex];
+ } else {
+ DigestBuf = (Entry != NULL) ? Entry->TbsHash[HashIndex] : LocalDigest;
+ Status = HashTbsCertificate (CertDer, CertDerSize, HashIndex, DigestBuf);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Entry != NULL) {
+ Entry->TbsHashValid[HashIndex] = TRUE;
+ }
+ }
+
+ if (CompareMem (DigestBuf, TbsCertHash, DigestSize) == 0) {
+ *IsMatch = TRUE;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Walk a SignedData certificates field and search for a certificate
+ whose TBSCertificate digest equals TbsCertHash.
+
+ @param[in] CertSetBody Pointer to the contents of the [0]
+ IMPLICIT certificates field.
+ @param[in] CertSetBodyLen Length of CertSetBody.
+ @param[in] HashIndex Algorithm index.
+ @param[in] TbsCertHash Target digest bytes.
+ @param[in] Cache Optional cache.
+ @param[out] MatchCert On success, points within CertSetBody to
+ the matching certificate's first byte.
+ @param[out] MatchCertSize On success, size of the matching cert.
+
+ @retval EFI_SUCCESS Match found.
+ @retval EFI_NOT_FOUND No certificate matched.
+ @retval other Parse or hashing error.
+**/
+STATIC
+EFI_STATUS
+SearchCertificateSet (
+ IN CONST UINT8 *CertSetBody,
+ IN UINTN CertSetBodyLen,
+ IN UINTN HashIndex,
+ IN CONST UINT8 *TbsCertHash,
+ IN TRUST_ANCHOR_CACHE *Cache OPTIONAL,
+ OUT CONST UINT8 **MatchCert,
+ OUT UINTN *MatchCertSize
+ )
+{
+ CONST UINT8 *Cursor;
+ CONST UINT8 *End;
+ CONST UINT8 *CertStart;
+ CONST UINT8 *CertBody;
+ UINTN CertBodyLen;
+ EFI_STATUS Status;
+ BOOLEAN Match;
+
+ Cursor = CertSetBody;
+ End = CertSetBody + CertSetBodyLen;
+
+ while (Cursor < End) {
+ if (*Cursor != ASN1_TAG_SEQUENCE) {
+ //
+ // The certificates field may also contain extendedCertificate or
+ // other choices in older PKCS#7. Skip non-SEQUENCE TLVs.
+ //
+ CertStart = Cursor + 1;
+ Status = Asn1DecodeLength (&CertStart, End, &CertBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cursor = CertStart + CertBodyLen;
+ continue;
+ }
+
+ CertStart = Cursor;
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &CertBody, &CertBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = CertMatchesTbsHash (
+ Cache,
+ CertStart,
+ (UINTN)(Cursor - CertStart),
+ HashIndex,
+ TbsCertHash,
+ &Match
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Match) {
+ *MatchCert = CertStart;
+ *MatchCertSize = (UINTN)(Cursor - CertStart);
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Parse a PKCS#7 SignedData blob and return the contents of the
+ certificates [0] IMPLICIT field.
+
+ Accepts both the bare SignedData encoding and a ContentInfo wrapper.
+
+ @param[in] AuthData Pointer to PKCS#7 / SignedData bytes.
+ @param[in] AuthDataSize Size in bytes.
+ @param[out] CertSetBody Receives a pointer into AuthData where the
+ certificates contents start.
+ @param[out] CertSetBodyLen Receives the length of the certificates
+ contents.
+
+ @retval EFI_SUCCESS Parsed and certificates field located.
+ @retval EFI_INVALID_PARAMETER Malformed input.
+ @retval EFI_NOT_FOUND The optional certificates field is
+ absent.
+**/
+STATIC
+EFI_STATUS
+LocateCertificatesField (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT CONST UINT8 **CertSetBody,
+ OUT UINTN *CertSetBodyLen
+ )
+{
+ CONST UINT8 *Cursor;
+ CONST UINT8 *End;
+ CONST UINT8 *Body;
+ UINTN BodyLen;
+ CONST UINT8 *OidBody;
+ UINTN OidLen;
+ EFI_STATUS Status;
+
+ Cursor = AuthData;
+ End = AuthData + AuthDataSize;
+
+ //
+ // Outer SEQUENCE.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // Detect ContentInfo: optional contentType OID followed by [0] EXPLICIT
+ // SignedData. If we see an OID first, peel the wrapper.
+ //
+ if ((Cursor < End) && (*Cursor == ASN1_TAG_OID)) {
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_OID, &OidBody, &OidLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((OidLen != sizeof (mPkcs7SignedDataOid)) ||
+ (CompareMem (OidBody, mPkcs7SignedDataOid, OidLen) != 0))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // [0] EXPLICIT SignedData wrapper.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_CTX_CONS_0, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+
+ //
+ // Inner SignedData SEQUENCE.
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Cursor = Body;
+ End = Body + BodyLen;
+ }
+
+ //
+ // Now Cursor..End contains the SignedData contents.
+ // version INTEGER
+ // digestAlgorithms SET
+ // encapContentInfo SEQUENCE
+ // certificates [0] IMPLICIT (optional) <-- the field we want
+ // crls [1] IMPLICIT (optional)
+ // signerInfos SET
+ //
+ // Skip version + digestAlgorithms + encapContentInfo by parsing each
+ // TLV and discarding its body.
+ //
+ // version (INTEGER, tag 0x02)
+ //
+ if (Cursor >= End) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Asn1ExpectTagged (&Cursor, End, *Cursor, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // digestAlgorithms (SET)
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SET, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // encapContentInfo (SEQUENCE)
+ //
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_SEQUENCE, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Optional certificates [0] IMPLICIT.
+ //
+ if ((Cursor < End) && (*Cursor == ASN1_TAG_CTX_CONS_0)) {
+ Status = Asn1ExpectTagged (&Cursor, End, ASN1_TAG_CTX_CONS_0, &Body, &BodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ *CertSetBody = Body;
+ *CertSetBodyLen = BodyLen;
+ return EFI_SUCCESS;
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ Locate, in a PKCS#7 SignedData blob, the X.509 certificate whose
+ TBSCertificate digest matches a caller-supplied hash, and return
+ that certificate as a newly allocated DER-encoded buffer.
+
+ See BaseCryptLib.h for the full contract.
+**/
+EFI_STATUS
+EFIAPI
+GetTrustAnchorX509FromAuthData (
+ IN OUT VOID **CacheHandle OPTIONAL,
+ IN CONST UINT8 *TbsCertHash,
+ IN UINTN TbsCertHashSize,
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT UINT8 **TrustAnchorX509,
+ OUT UINTN *TrustAnchorX509Size
+ )
+{
+ EFI_STATUS Status;
+ UINTN HashIndex;
+ CONST UINT8 *CertSetBody;
+ UINTN CertSetBodyLen;
+ CONST UINT8 *MatchCert;
+ UINTN MatchCertSize;
+ TRUST_ANCHOR_CACHE *Cache;
+ UINT8 *Output;
+
+ if ((TbsCertHash == NULL) || (TbsCertHashSize == 0) ||
+ (AuthData == NULL) || (AuthDataSize == 0) ||
+ (TrustAnchorX509 == NULL) || (TrustAnchorX509Size == NULL))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ HashIndex = HashSizeToIndex (TbsCertHashSize);
+ if (HashIndex == (UINTN)-1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Optional cache. *CacheHandle == NULL means "allocate me one"; the
+ // caller releases it later via FreeTrustAnchorX509Cache().
+ //
+ Cache = NULL;
+ if (CacheHandle != NULL) {
+ if (*CacheHandle == NULL) {
+ Cache = AllocateZeroPool (sizeof (TRUST_ANCHOR_CACHE));
+ if (Cache == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Cache->Signature = TRUST_ANCHOR_CACHE_SIGNATURE;
+ *CacheHandle = Cache;
+ } else {
+ Cache = (TRUST_ANCHOR_CACHE *)*CacheHandle;
+ if (Cache->Signature != TRUST_ANCHOR_CACHE_SIGNATURE) {
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+
+ Status = LocateCertificatesField (AuthData, AuthDataSize, &CertSetBody, &CertSetBodyLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = SearchCertificateSet (
+ CertSetBody,
+ CertSetBodyLen,
+ HashIndex,
+ TbsCertHash,
+ Cache,
+ &MatchCert,
+ &MatchCertSize
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Output = AllocateCopyPool (MatchCertSize, MatchCert);
+ if (Output == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *TrustAnchorX509 = Output;
+ *TrustAnchorX509Size = MatchCertSize;
+ return EFI_SUCCESS;
+}
+
+/**
+ Release a trust-anchor cache previously allocated by
+ GetTrustAnchorX509FromAuthData().
+
+ @param[in] CacheHandle Cache handle, or NULL.
+**/
+VOID
+EFIAPI
+FreeTrustAnchorX509Cache (
+ IN VOID *CacheHandle OPTIONAL
+ )
+{
+ TRUST_ANCHOR_CACHE *Cache;
+ TRUST_ANCHOR_CACHE_ENTRY *Entry;
+ UINTN Index;
+
+ if (CacheHandle == NULL) {
+ return;
+ }
+
+ Cache = (TRUST_ANCHOR_CACHE *)CacheHandle;
+ if (Cache->Signature != TRUST_ANCHOR_CACHE_SIGNATURE) {
+ ASSERT (FALSE);
+ return;
+ }
+
+ for (Index = 0; Index < Cache->Count; Index++) {
+ Entry = &Cache->Entries[Index];
+ if (Entry->CertDer != NULL) {
+ FreePool (Entry->CertDer);
+ }
+ }
+
+ ZeroMem (Cache, sizeof (TRUST_ANCHOR_CACHE));
+ FreePool (Cache);
+}
diff --git a/OpensslPkg/Library/BaseCryptLib/Pk/CryptTrustAnchorNull.c b/OpensslPkg/Library/BaseCryptLib/Pk/CryptTrustAnchorNull.c
new file mode 100644
index 000000000..2b121479b
--- /dev/null
+++ b/OpensslPkg/Library/BaseCryptLib/Pk/CryptTrustAnchorNull.c
@@ -0,0 +1,45 @@
+/** @file
+ GetTrustAnchorX509FromAuthData() / FreeTrustAnchorX509Cache() Null
+ implementation.
+
+ Returns EFI_UNSUPPORTED to indicate this interface is not provided by
+ the current library instance (e.g., PEI / Runtime / SEC / SMM phases).
+
+Copyright (C) Microsoft Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "InternalCryptLib.h"
+
+/**
+ Null stub for GetTrustAnchorX509FromAuthData().
+
+ @retval EFI_UNSUPPORTED This interface is not supported.
+**/
+EFI_STATUS
+EFIAPI
+GetTrustAnchorX509FromAuthData (
+ IN OUT VOID **CacheHandle OPTIONAL,
+ IN CONST UINT8 *TbsCertHash,
+ IN UINTN TbsCertHashSize,
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT UINT8 **TrustAnchorX509,
+ OUT UINTN *TrustAnchorX509Size
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Null stub for FreeTrustAnchorX509Cache(). No-op.
+**/
+VOID
+EFIAPI
+FreeTrustAnchorX509Cache (
+ IN VOID *CacheHandle OPTIONAL
+ )
+{
+ ASSERT (FALSE);
+}
diff --git a/OpensslPkg/Library/BaseCryptLib/RuntimeCryptLib.inf b/OpensslPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
index 1bee26764..2a3fe1c9f 100644
--- a/OpensslPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
+++ b/OpensslPkg/Library/BaseCryptLib/RuntimeCryptLib.inf
@@ -57,6 +57,8 @@
Pk/CryptDhNull.c
Pk/CryptX509.c
Pk/CryptAuthenticodeNull.c
+ Pk/CryptAuthenticodeHashNull.c # MU_CHANGE
+ Pk/CryptTrustAnchorNull.c # MU_CHANGE
Pk/CryptTsNull.c
Pk/CryptRsaPssNull.c
Pk/CryptRsaPssSignNull.c
diff --git a/OpensslPkg/Library/BaseCryptLib/SecCryptLib.inf b/OpensslPkg/Library/BaseCryptLib/SecCryptLib.inf
index 100686d6c..224d84b33 100644
--- a/OpensslPkg/Library/BaseCryptLib/SecCryptLib.inf
+++ b/OpensslPkg/Library/BaseCryptLib/SecCryptLib.inf
@@ -49,6 +49,8 @@
Pk/CryptDhNull.c
Pk/CryptX509Null.c
Pk/CryptAuthenticodeNull.c
+ Pk/CryptAuthenticodeHashNull.c # MU_CHANGE
+ Pk/CryptTrustAnchorNull.c # MU_CHANGE
Pk/CryptTsNull.c
Pem/CryptPemNull.c
Rand/CryptRandNull.c
diff --git a/OpensslPkg/Library/BaseCryptLib/SmmCryptLib.inf b/OpensslPkg/Library/BaseCryptLib/SmmCryptLib.inf
index ae075b49d..08a17d24f 100644
--- a/OpensslPkg/Library/BaseCryptLib/SmmCryptLib.inf
+++ b/OpensslPkg/Library/BaseCryptLib/SmmCryptLib.inf
@@ -58,6 +58,8 @@
Pk/CryptDhNull.c
Pk/CryptX509.c
Pk/CryptAuthenticodeNull.c
+ Pk/CryptAuthenticodeHashNull.c # MU_CHANGE
+ Pk/CryptTrustAnchorNull.c # MU_CHANGE
Pk/CryptTsNull.c
Pk/CryptRsaPss.c
Pk/CryptRsaPssSignNull.c
diff --git a/OpensslPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf b/OpensslPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
index 23bc87e92..7ccc75a0d 100644
--- a/OpensslPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
+++ b/OpensslPkg/Library/BaseCryptLib/UnitTestHostBaseCryptLib.inf
@@ -45,6 +45,8 @@
Pk/CryptDh.c
Pk/CryptX509.c
Pk/CryptAuthenticode.c
+ Pk/CryptAuthenticodeHash.c # MU_CHANGE
+ Pk/CryptTrustAnchor.c # MU_CHANGE
Pk/CryptTs.c
Pem/CryptPem.c
Pk/CryptRsaPss.c
@@ -73,6 +75,13 @@
OpensslLib
PrintLib
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # Image hash type used by GetAuthenticodeHash
+ gEfiCertSha1Guid # MU_CHANGE
+ gEfiCertSha256Guid # MU_CHANGE
+ gEfiCertSha384Guid # MU_CHANGE
+ gEfiCertSha512Guid # MU_CHANGE
+
#
# Remove these [BuildOptions] after this library is cleaned up
#