| /** @file | |
| Authenticode Portable Executable Signature Verification which does not provide | |
| real capabilities. | |
| Copyright (c) 2024, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "InternalCryptLib.h" | |
| #include <mbedtls/pkcs7.h> | |
| // | |
| // OID ASN.1 Value for SPC_INDIRECT_DATA_OBJID | |
| // | |
| GLOBAL_REMOVE_IF_UNREFERENCED const UINT8 mSpcIndirectOidValue[] = { | |
| 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04 | |
| }; | |
| /** | |
| Verifies the validity of a PE/COFF Authenticode Signature as described in "Windows | |
| Authenticode Portable Executable Signature Format". | |
| Return FALSE to indicate this interface is not supported. | |
| @param[in] AuthData Pointer to the Authenticode Signature retrieved from signed | |
| PE/COFF image to be verified. | |
| @param[in] DataSize Size of the Authenticode Signature in bytes. | |
| @param[in] TrustedCert Pointer to a trusted/root certificate encoded in DER, which | |
| is used for certificate chain verification. | |
| @param[in] CertSize Size of the trusted certificate in bytes. | |
| @param[in] ImageHash Pointer to the original image file hash value. The procedure | |
| for calculating the image hash value is described in Authenticode | |
| specification. | |
| @param[in] HashSize Size of Image hash value in bytes. | |
| @retval FALSE This interface is not supported. | |
| **/ | |
| BOOLEAN | |
| EFIAPI | |
| AuthenticodeVerify ( | |
| IN CONST UINT8 *AuthData, | |
| IN UINTN DataSize, | |
| IN CONST UINT8 *TrustedCert, | |
| IN UINTN CertSize, | |
| IN CONST UINT8 *ImageHash, | |
| IN UINTN HashSize | |
| ) | |
| { | |
| BOOLEAN Status; | |
| CONST UINT8 *OrigAuthData; | |
| UINT8 *SpcIndirectDataContent; | |
| UINT8 Asn1Byte; | |
| UINTN ContentSize; | |
| CONST UINT8 *SpcIndirectDataOid; | |
| UINT8 *Ptr; | |
| UINT8 *End; | |
| INT32 Len; | |
| UINTN ObjLen; | |
| OrigAuthData = AuthData; | |
| // | |
| // Check input parameters. | |
| // | |
| if ((AuthData == NULL) || (TrustedCert == NULL) || (ImageHash == NULL)) { | |
| return FALSE; | |
| } | |
| if ((DataSize > INT_MAX) || (CertSize > INT_MAX) || (HashSize > INT_MAX)) { | |
| return FALSE; | |
| } | |
| if (DataSize <= HashSize) { | |
| return FALSE; | |
| } | |
| Ptr = (UINT8 *)(UINTN)AuthData; | |
| Len = (UINT32)DataSize; | |
| End = Ptr + Len; | |
| // ContentInfo | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { | |
| return FALSE; | |
| } | |
| // ContentType | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OID) != 0) { | |
| return FALSE; | |
| } | |
| Ptr += ObjLen; | |
| // content | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) { | |
| return FALSE; | |
| } | |
| End = Ptr + ObjLen; | |
| // signedData | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { | |
| return FALSE; | |
| } | |
| // version | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_INTEGER) != 0) { | |
| return FALSE; | |
| } | |
| Ptr += ObjLen; | |
| // digestAlgo | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET) != 0) { | |
| return FALSE; | |
| } | |
| Ptr += ObjLen; | |
| // encapContentInfo | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE) != 0) { | |
| return FALSE; | |
| } | |
| End = Ptr + ObjLen; | |
| // eContentType | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OID) != 0) { | |
| return FALSE; | |
| } | |
| Status = FALSE; | |
| SpcIndirectDataOid = Ptr; | |
| if ((ObjLen != sizeof (mSpcIndirectOidValue)) || | |
| (CompareMem ( | |
| SpcIndirectDataOid, | |
| mSpcIndirectOidValue, | |
| sizeof (mSpcIndirectOidValue) | |
| ) != 0)) | |
| { | |
| // | |
| // Un-matched SPC_INDIRECT_DATA_OBJID. | |
| // | |
| goto _Exit; | |
| } | |
| Ptr += ObjLen; | |
| // eContent | |
| if (mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_CONTEXT_SPECIFIC) != 0) { | |
| return FALSE; | |
| } | |
| SpcIndirectDataContent = Ptr; | |
| // | |
| // Retrieve the SEQUENCE data size from ASN.1-encoded SpcIndirectDataContent. | |
| // | |
| Asn1Byte = *(SpcIndirectDataContent + 1); | |
| if ((Asn1Byte & 0x80) == 0) { | |
| // | |
| // Short Form of Length Encoding (Length < 128) | |
| // | |
| ContentSize = (UINTN)(Asn1Byte & 0x7F); | |
| // | |
| // Skip the SEQUENCE Tag; | |
| // | |
| SpcIndirectDataContent += 2; | |
| } else if ((Asn1Byte & 0x81) == 0x81) { | |
| // | |
| // Long Form of Length Encoding (128 <= Length < 255, Single Octet) | |
| // | |
| ContentSize = (UINTN)(*(UINT8 *)(SpcIndirectDataContent + 2)); | |
| // | |
| // Skip the SEQUENCE Tag; | |
| // | |
| SpcIndirectDataContent += 3; | |
| } else if ((Asn1Byte & 0x82) == 0x82) { | |
| // | |
| // Long Form of Length Encoding (Length > 255, Two Octet) | |
| // | |
| ContentSize = (UINTN)(*(UINT8 *)(SpcIndirectDataContent + 2)); | |
| ContentSize = (ContentSize << 8) + (UINTN)(*(UINT8 *)(SpcIndirectDataContent + 3)); | |
| // | |
| // Skip the SEQUENCE Tag; | |
| // | |
| SpcIndirectDataContent += 4; | |
| } else { | |
| goto _Exit; | |
| } | |
| // | |
| // Compare the original file hash value to the digest retrieve from SpcIndirectDataContent | |
| // defined in Authenticode | |
| // NOTE: Need to double-check HashLength here! | |
| // | |
| if (ContentSize < HashSize) { | |
| return FALSE; | |
| } | |
| if (CompareMem (SpcIndirectDataContent + ContentSize - HashSize, ImageHash, HashSize) != 0) { | |
| // | |
| // Un-matched PE/COFF Hash Value | |
| // | |
| goto _Exit; | |
| } | |
| // | |
| // Verifies the PKCS#7 Signed Data in PE/COFF Authenticode Signature | |
| // | |
| Status = (BOOLEAN)Pkcs7Verify (OrigAuthData, DataSize, TrustedCert, CertSize, SpcIndirectDataContent, ContentSize); | |
| _Exit: | |
| return Status; | |
| } |