/** The handler is used to do the authentication for FMP capsule based upon EFI_FIRMWARE_IMAGE_AUTHENTICATION. Caution: This function may receive untrusted input. This function assumes the caller AuthenticateFmpImage() already did basic validation for EFI_FIRMWARE_IMAGE_AUTHENTICATION. @param[in] Image Points to an FMP authentication image, started from EFI_FIRMWARE_IMAGE_AUTHENTICATION. @param[in] ImageSize Size of the authentication image in bytes. @param[in] PublicKeyData The public key data used to validate the signature. @param[in] PublicKeyDataLength The length of the public key data. @retval RETURN_SUCCESS Authentication pass. The LastAttemptStatus should be LAST_ATTEMPT_STATUS_SUCCESS. @retval RETURN_SECURITY_VIOLATION Authentication fail. The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR. @retval RETURN_INVALID_PARAMETER The image is in an invalid format. The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT. @retval RETURN_OUT_OF_RESOURCES No Authentication handler associated with CertType. The LastAttemptStatus should be LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES. **/ RETURN_STATUS FmpAuthenticatedHandlerPkcs7 ( IN EFI_FIRMWARE_IMAGE_AUTHENTICATION *Image, IN UINTN ImageSize, IN CONST UINT8 *PublicKeyData, IN UINTN PublicKeyDataLength ) { RETURN_STATUS Status; BOOLEAN CryptoStatus; VOID *P7Data; UINTN P7Length; VOID *TempBuffer; DEBUG((DEBUG_INFO, "FmpAuthenticatedHandlerPkcs7 - Image: 0x%08x - 0x%08x\n", (UINTN)Image, (UINTN)ImageSize)); P7Length = Image->AuthInfo.Hdr.dwLength - (OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData)); P7Data = Image->AuthInfo.CertData; // It is a signature across the variable data and the Monotonic Count value. TempBuffer = AllocatePool(ImageSize - Image->AuthInfo.Hdr.dwLength); if (TempBuffer == NULL) { DEBUG((DEBUG_ERROR, "FmpAuthenticatedHandlerPkcs7: TempBuffer == NULL\n")); Status = RETURN_OUT_OF_RESOURCES; goto Done; } CopyMem( TempBuffer, (UINT8 *)Image + sizeof(Image->MonotonicCount) + Image->AuthInfo.Hdr.dwLength, ImageSize - sizeof(Image->MonotonicCount) - Image->AuthInfo.Hdr.dwLength ); CopyMem( (UINT8 *)TempBuffer + ImageSize - sizeof(Image->MonotonicCount) - Image->AuthInfo.Hdr.dwLength, &Image->MonotonicCount, sizeof(Image->MonotonicCount) ); CryptoStatus = Pkcs7Verify( P7Data, P7Length, PublicKeyData, PublicKeyDataLength, (UINT8 *)TempBuffer, ImageSize - Image->AuthInfo.Hdr.dwLength ); FreePool(TempBuffer); if (!CryptoStatus) { // // If PKCS7 signature verification fails, AUTH tested failed bit is set. // DEBUG((DEBUG_ERROR, "FmpAuthenticatedHandlerPkcs7: Pkcs7Verify() failed\n")); Status = RETURN_SECURITY_VIOLATION; goto Done; } DEBUG((DEBUG_INFO, "FmpAuthenticatedHandlerPkcs7: PASS verification\n")); Status = RETURN_SUCCESS; Done: return Status; }
/** Verifies the validility 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 procudure 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 *OrigAuthData; UINT8 *SpcIndirectDataContent; UINT8 Asn1Byte; UINTN ContentSize; 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 // Pkcs7 = d2i_PKCS7 (NULL, &AuthData, (int)DataSize); if (Pkcs7 == NULL) { goto _Exit; } // // Check if it's PKCS#7 Signed Data (for Authenticode Scenario) // if (!PKCS7_type_is_signed (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 = (UINT8 *)(Pkcs7->d.sign->contents->type->data); if (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 // ContentSize = (UINTN) (Asn1Byte & 0x7F); // // Skip the SEQUENCE Tag; // SpcIndirectDataContent += 2; } else if ((Asn1Byte & 0x82) == 0x82) { // // Long Form of Length Encoding, only support two bytes. // 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; }
/** Check whether the PKCS7 signedData is revoked by verifying with the revoked certificates database, and if the signedData is timestamped, the embedded timestamp couterSignature will be checked with the supplied timestamp database. @param[in] SignedData Pointer to buffer containing ASN.1 DER-encoded PKCS7 signature. @param[in] SignedDataSize The size of SignedData buffer in bytes. @param[in] InData Pointer to the buffer containing the raw message data previously signed and to be verified. @param[in] InDataSize The size of InData buffer in bytes. @param[in] RevokedDb Pointer to a list of pointers to EFI_SIGNATURE_LIST structure which contains list of X.509 certificates of revoked signers and revoked content hashes. @param[in] TimeStampDb Pointer to a list of pointers to EFI_SIGNATURE_LIST structures which is used to pass a list of X.509 certificates of trusted timestamp signers. @retval EFI_SUCCESS The PKCS7 signedData is revoked. @retval EFI_SECURITY_VIOLATION Fail to verify the signature in PKCS7 signedData. @retval EFI_INVALID_PARAMETER SignedData is NULL or SignedDataSize is zero. AllowedDb is NULL. Content is not NULL and ContentSize is NULL. @retval EFI_NOT_FOUND Content not found because InData is NULL and no content embedded in PKCS7 signedData. @retval EFI_UNSUPPORTED The PKCS7 signedData was not correctly formatted. **/ EFI_STATUS P7CheckRevocation ( IN UINT8 *SignedData, IN UINTN SignedDataSize, IN UINT8 *InData, IN UINTN InDataSize, IN EFI_SIGNATURE_LIST **RevokedDb, IN EFI_SIGNATURE_LIST **TimeStampDb ) { EFI_STATUS Status; EFI_SIGNATURE_LIST *SigList; EFI_SIGNATURE_DATA *SigData; UINT8 *RevokedCert; UINTN RevokedCertSize; UINTN Index; UINT8 *CertBuffer; UINTN BufferLength; UINT8 *TrustedCert; UINTN TrustedCertLength; UINT8 CertNumber; UINT8 *CertPtr; UINT8 *Cert; UINTN CertSize; EFI_TIME RevocationTime; Status = EFI_UNSUPPORTED; SigData = NULL; RevokedCert = NULL; RevokedCertSize = 0; CertBuffer = NULL; TrustedCert = NULL; // // The signedData is revoked if the hash of content existed in RevokedDb // if (IsContentHashRevoked (InData, InDataSize, RevokedDb)) { Status = EFI_SUCCESS; goto _Exit; } // // Check if the signer's certificate can be found in Revoked database // for (Index = 0; ; Index++) { SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]); // // The list is terminated by a NULL pointer. // if (SigList == NULL) { break; } // // Ignore any non-X509-format entry in the list. // if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) { continue; } SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) + SigList->SignatureHeaderSize); RevokedCert = SigData->SignatureData; RevokedCertSize = SigList->SignatureSize - sizeof (EFI_GUID); // // Verifying the PKCS#7 SignedData with the revoked certificate in RevokedDb // if (Pkcs7Verify (SignedData, SignedDataSize, RevokedCert, RevokedCertSize, InData, InDataSize)) { // // The signedData was verified by one entry in Revoked Database // Status = EFI_SUCCESS; break; } } if (!EFI_ERROR (Status)) { // // The signedData was revoked, since it was hit by RevokedDb // goto _Exit; } // // Now we will continue to check the X.509 Certificate Hash & Possible Timestamp // if ((TimeStampDb == NULL) || (*TimeStampDb == NULL)) { goto _Exit; } Pkcs7GetSigners (SignedData, SignedDataSize, &CertBuffer, &BufferLength, &TrustedCert, &TrustedCertLength); if ((BufferLength == 0) || (CertBuffer == NULL)) { Status = EFI_SUCCESS; goto _Exit; } // // Check if any hash of certificates embedded in P7 data is in the revoked database. // CertNumber = (UINT8) (*CertBuffer); CertPtr = CertBuffer + 1; for (Index = 0; Index < CertNumber; Index++) { // // Retrieve the Certificate data // CertSize = (UINTN) ReadUnaligned32 ((UINT32 *) CertPtr); Cert = (UINT8 *)CertPtr + sizeof (UINT32); if (IsCertHashRevoked (Cert, CertSize, RevokedDb, &RevocationTime)) { // // Check the timestamp signature and signing time to determine if p7 data can be trusted. // Status = EFI_SUCCESS; if (IsValidTimestamp (SignedData, SignedDataSize, TimeStampDb, &RevocationTime)) { // // Use EFI_NOT_READY to identify the P7Data is not reovked, because the timestamping // occured prior to the time of certificate revocation. // Status = EFI_NOT_READY; } goto _Exit; } CertPtr = CertPtr + sizeof (UINT32) + CertSize; } _Exit: Pkcs7FreeSigners (CertBuffer); Pkcs7FreeSigners (TrustedCert); return Status; }
/** Check whether the PKCS7 signedData can be verified by the trusted certificates database, and return the content of the signedData if requested. @param[in] SignedData Pointer to buffer containing ASN.1 DER-encoded PKCS7 signature. @param[in] SignedDataSize The size of SignedData buffer in bytes. @param[in] InData Pointer to the buffer containing the raw message data previously signed and to be verified. @param[in] InDataSize The size of InData buffer in bytes. @param[in] AllowedDb Pointer to a list of pointers to EFI_SIGNATURE_LIST structures which contains lists of X.509 certificates of approved signers. @retval EFI_SUCCESS The PKCS7 signedData is trusted. @retval EFI_SECURITY_VIOLATION Fail to verify the signature in PKCS7 signedData. @retval EFI_INVALID_PARAMETER SignedData is NULL or SignedDataSize is zero. AllowedDb is NULL. Content is not NULL and ContentSize is NULL. @retval EFI_NOT_FOUND Content not found because InData is NULL and no content embedded in PKCS7 signedData. @retval EFI_UNSUPPORTED The PKCS7 signedData was not correctly formatted. @retval EFI_BUFFER_TOO_SMALL The size of buffer indicated by ContentSize is too small to hold the content. ContentSize updated to the required size. **/ EFI_STATUS P7CheckTrust ( IN UINT8 *SignedData, IN UINTN SignedDataSize, IN UINT8 *InData, IN UINTN InDataSize, IN EFI_SIGNATURE_LIST **AllowedDb ) { EFI_STATUS Status; EFI_SIGNATURE_LIST *SigList; EFI_SIGNATURE_DATA *SigData; UINT8 *TrustCert; UINTN TrustCertSize; UINTN Index; Status = EFI_SECURITY_VIOLATION; SigData = NULL; TrustCert = NULL; TrustCertSize = 0; if (AllowedDb == NULL) { return EFI_INVALID_PARAMETER; } // // Build Certificate Stack with all valid X509 certificates in the supplied // Signature List for PKCS7 Verification. // for (Index = 0; ; Index++) { SigList = (EFI_SIGNATURE_LIST *)(AllowedDb[Index]); // // The list is terminated by a NULL pointer. // if (SigList == NULL) { break; } // // Ignore any non-X509-format entry in the list. // if (!CompareGuid (&SigList->SignatureType, &gEfiCertX509Guid)) { continue; } SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) + SigList->SignatureHeaderSize); TrustCert = SigData->SignatureData; TrustCertSize = SigList->SignatureSize - sizeof (EFI_GUID); // // Verifying the PKCS#7 SignedData with the trusted certificate from AllowedDb // if (Pkcs7Verify (SignedData, SignedDataSize, TrustCert, TrustCertSize, InData, InDataSize)) { // // The SignedData was verified successfully by one entry in Trusted Database // Status = EFI_SUCCESS; break; } } return Status; }
/** Validate UEFI-OpenSSL PKCS#7 Signing & Verification Interfaces. @retval EFI_SUCCESS Validation succeeded. @retval EFI_ABORTED Validation failed. **/ EFI_STATUS ValidateCryptPkcs7 ( VOID ) { BOOLEAN Status; UINT8 *P7SignedData; UINTN P7SignedDataSize; UINT8 *SignCert; P7SignedData = NULL; SignCert = NULL; Print (L"\nUEFI-OpenSSL PKCS#7 Signing & Verification Testing: "); Print (L"\n- Create PKCS#7 signedData ..."); // // Construct Signer Certificate from RAW data. // Status = X509ConstructCertificate (TestCert, sizeof (TestCert), (UINT8 **) &SignCert); if (!Status || SignCert == NULL) { Print (L"[Fail]"); goto _Exit; } else { Print (L"[Pass]"); } // // Create PKCS#7 signedData on Payload. // Note: Caller should release P7SignedData manually. // Status = Pkcs7Sign ( TestKeyPem, sizeof (TestKeyPem), (CONST UINT8 *) PemPass, (UINT8 *) Payload, AsciiStrLen (Payload), SignCert, NULL, &P7SignedData, &P7SignedDataSize ); if (!Status || P7SignedDataSize == 0) { Print (L"[Fail]"); goto _Exit; } else { Print (L"[Pass]"); } Print (L"\n- Verify PKCS#7 signedData ..."); Status = Pkcs7Verify ( P7SignedData, P7SignedDataSize, TestCACert, sizeof (TestCACert), (UINT8 *) Payload, AsciiStrLen (Payload) ); if (!Status) { Print (L"[Fail]"); } else { Print (L"[Pass]"); } _Exit: if (P7SignedData != NULL) { FreePool (P7SignedData); } if (SignCert != NULL) { X509Free (SignCert); } Print (L"\n"); return EFI_SUCCESS; }