/** @file Authenticode Portable Executable Signature Verification over OpenSSL. Caution: This module requires additional review when modified. This library will have external input - signature (e.g. PE/COFF Authenticode). This external input must be validated carefully to avoid security issue like buffer overflow, integer overflow. AuthenticodeVerify() will get PE/COFF Authenticode and will do basic check for data structure. Copyright (c) 2011 - 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "InternalCryptLib.h" #include #include #include // // OID ASN.1 Value for SPC_INDIRECT_DATA_OBJID // 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". If AuthData is NULL, then return FALSE. If ImageHash is NULL, then return FALSE. Caution: This function may receive untrusted input. PE/COFF Authenticode is external input, so this function will do basic check for Authenticode data structure. @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 TRUE The specified Authenticode Signature is valid. @retval FALSE Invalid Authenticode Signature. **/ 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; PKCS7 *Pkcs7; CONST UINT8 *Temp; CONST UINT8 *OrigAuthData; UINT8 *SpcIndirectDataContent; UINT8 Asn1Byte; UINTN ContentSize; CONST UINT8 *SpcIndirectDataOid; // // Check input parameters. // if ((AuthData == NULL) || (TrustedCert == NULL) || (ImageHash == NULL)) { return FALSE; } if ((DataSize > INT_MAX) || (CertSize > INT_MAX) || (HashSize > INT_MAX)) { return FALSE; } Status = FALSE; Pkcs7 = NULL; OrigAuthData = AuthData; // // Retrieve & Parse PKCS#7 Data (DER encoding) from Authenticode Signature // Temp = AuthData; Pkcs7 = d2i_PKCS7 (NULL, &Temp, (int)DataSize); if (Pkcs7 == NULL) { goto _Exit; } // // Check if it's PKCS#7 Signed Data (for Authenticode Scenario) // if (!PKCS7_type_is_signed (Pkcs7) || PKCS7_get_detached (Pkcs7)) { goto _Exit; } // // NOTE: OpenSSL PKCS7 Decoder didn't work for Authenticode-format signed data due to // some authenticode-specific structure. Use opaque ASN.1 string to retrieve // PKCS#7 ContentInfo here. // SpcIndirectDataOid = OBJ_get0_data (Pkcs7->d.sign->contents->type); if ((OBJ_length (Pkcs7->d.sign->contents->type) != sizeof (mSpcIndirectOidValue)) || (CompareMem ( SpcIndirectDataOid, mSpcIndirectOidValue, sizeof (mSpcIndirectOidValue) ) != 0)) { // // Un-matched SPC_INDIRECT_DATA_OBJID. // goto _Exit; } SpcIndirectDataContent = (UINT8 *)(Pkcs7->d.sign->contents->d.other->value.asn1_string->data); // // 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 (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: // // Release Resources // PKCS7_free (Pkcs7); return Status; }