SecurityPkg/Pkcs7Verify: Complete the Pkcs7VerifyDxe protocol

VerifySignature can be implemented using a mirror of the
AuthenticodeVerify function that's already in use in the
ImageVerificationDXE environment, so this patch simply wires up
VerifySignature using that code.
<NOTE: Only Authenticode-signature verification was supported by
       this VerifySignature() implementation now.)

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Reviewed-by: Long Qin <qin.long@intel.com>
This commit is contained in:
Long Qin 2017-09-05 15:46:21 +08:00
parent 0233871442
commit a2481f81b3
1 changed files with 376 additions and 3 deletions

View File

@ -113,6 +113,82 @@ _Exit:
return Status;
}
/**
Check whether the hash of data content is revoked by the revocation database.
@param[in] Hash Pointer to the hash that is searched for.
@param[in] HashSize The size of the hash 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.
@return TRUE The matched content hash is found in the revocation database.
@return FALSE The matched content hash is not found in the revocation database.
**/
BOOLEAN
IsContentHashRevokedByHash (
IN UINT8 *Hash,
IN UINTN HashSize,
IN EFI_SIGNATURE_LIST **RevokedDb
)
{
EFI_SIGNATURE_LIST *SigList;
EFI_SIGNATURE_DATA *SigData;
UINTN Index;
UINTN EntryIndex;
UINTN EntryCount;
BOOLEAN Status;
if (RevokedDb == NULL) {
return FALSE;
}
Status = FALSE;
//
// Check if any hash matching content hash can be found in RevokedDB
//
for (Index = 0; ; Index++) {
SigList = (EFI_SIGNATURE_LIST *)(RevokedDb[Index]);
//
// The list is terminated by a NULL pointer.
//
if (SigList == NULL) {
break;
}
//
// Search the signature database to search the revoked content hash
//
SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigList + sizeof (EFI_SIGNATURE_LIST) +
SigList->SignatureHeaderSize);
EntryCount = (SigList->SignatureListSize - SigList->SignatureHeaderSize -
sizeof (EFI_SIGNATURE_LIST)) / SigList->SignatureSize;
for (EntryIndex = 0; EntryIndex < EntryCount; EntryIndex++) {
//
// The problem case. There's a revocation hash but the sizes
// don't match, meaning it's a different hash algorithm and we
// can't tell if it's revoking our binary or not. Assume not.
//
if (SigList->SignatureSize - sizeof(EFI_GUID) == HashSize) {
//
// Compare Data Hash with Signature Data
//
if (CompareMem (SigData->SignatureData, Hash, HashSize) == 0) {
Status = TRUE;
goto _Exit;
}
}
SigData = (EFI_SIGNATURE_DATA *) ((UINT8 *) SigData + SigList->SignatureSize);
}
}
_Exit:
return Status;
}
/**
Check whether the hash of data content is revoked by the revocation database.
@ -441,6 +517,171 @@ IsValidTimestamp (
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] InHash Pointer to the buffer containing the hash of the mesage data
previously signed and to be verified.
@param[in] InHashSize The size of InHash 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
P7CheckRevocationByHash (
IN UINT8 *SignedData,
IN UINTN SignedDataSize,
IN UINT8 *InHash,
IN UINTN InHashSize,
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_SECURITY_VIOLATION;
SigData = NULL;
RevokedCert = NULL;
RevokedCertSize = 0;
CertBuffer = NULL;
TrustedCert = NULL;
//
// The signedData is revoked if the hash of content existed in RevokedDb
//
if (IsContentHashRevokedByHash (InHash, InHashSize, 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 (AuthenticodeVerify (SignedData, SignedDataSize, RevokedCert, RevokedCertSize, InHash, InHashSize)) {
//
// 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 is revoked by verifying with the revoked
certificates database, and if the signedData is timestamped, the embedded timestamp
@ -606,6 +847,100 @@ _Exit:
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] InHash Pointer to the buffer containing the hash of the message data
previously signed and to be verified.
@param[in] InHashSize The size of InHash 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
P7CheckTrustByHash (
IN UINT8 *SignedData,
IN UINTN SignedDataSize,
IN UINT8 *InHash,
IN UINTN InHashSize,
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 (AuthenticodeVerify (SignedData, SignedDataSize, TrustCert, TrustCertSize, InHash, InHashSize)) {
//
// The SignedData was verified successfully by one entry in Trusted Database
//
Status = EFI_SUCCESS;
break;
}
}
return Status;
}
/**
Check whether the PKCS7 signedData can be verified by the trusted certificates
database, and return the content of the signedData if requested.
@ -1051,11 +1386,49 @@ VerifySignature (
IN EFI_SIGNATURE_LIST **TimeStampDb OPTIONAL
)
{
EFI_STATUS Status;
//
// NOTE: Current EDKII-OpenSSL interface cannot support VerifySignature
// directly. EFI_UNSUPPORTED is returned in this version.
// Parameters Checking
//
return EFI_UNSUPPORTED;
if ((Signature == NULL) || (SignatureSize == 0) || (AllowedDb == NULL)
|| (InHash == NULL) || (InHashSize == 0)) {
return EFI_INVALID_PARAMETER;
}
//
// Verify PKCS7 SignedData with Revoked database
//
if (RevokedDb != NULL) {
Status = P7CheckRevocationByHash (
Signature,
SignatureSize,
InHash,
InHashSize,
RevokedDb,
TimeStampDb
);
if (!EFI_ERROR (Status)) {
//
// The PKCS7 SignedData is reovked
//
return EFI_SECURITY_VIOLATION;
}
}
//
// Verify PKCS7 SignedData with AllowedDB
//
Status = P7CheckTrustByHash (
Signature,
SignatureSize,
InHash,
InHashSize,
AllowedDb
);
return Status;
}
//