diff --git a/CryptoPkg/Include/Library/BaseCryptLib.h b/CryptoPkg/Include/Library/BaseCryptLib.h
index a9ddcc0162f..dd527d78790 100644
--- a/CryptoPkg/Include/Library/BaseCryptLib.h
+++ b/CryptoPkg/Include/Library/BaseCryptLib.h
@@ -2048,6 +2048,48 @@ X509GetTBSCert (
OUT UINTN *TBSCertSize
);
+/**
+ Compute the digest of a DER-encoded X.509 certificate using the
+ hash algorithm identified by HashType.
+
+ The caller selects the digest algorithm by HashType (e.g.
+ gEfiCertX509Sha256Guid, gEfiCertX509Sha384Guid, gEfiCertX509Sha512Guid).
+
+ If the Digest buffer is too small to hold the contents of the digest,
+ the error EFI_BUFFER_TOO_SMALL is returned and DigestSize is set to
+ the required buffer size to obtain the data.
+
+ @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. 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 CertSize
+ is zero.
+ @retval EFI_BUFFER_TOO_SMALL DigestSize is too small for the
+ requested hash algorithm.
+ @retval EFI_UNSUPPORTED HashType is not a recognized X.509
+ certificate hash algorithm, or this
+ interface is not supported by the
+ underlying library instance.
+ @retval EFI_SECURITY_VIOLATION The hash computation failed.
+**/
+EFI_STATUS
+EFIAPI
+GetX509Hash (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ );
+
/**
Derives a key from a password using a salt and iteration count, based on PKCS#5 v2.0
password based encryption key derivation function PBKDF2, as specified in RFC 2898.
@@ -2588,6 +2630,200 @@ AuthenticodeVerify (
IN UINTN HashSize
);
+/**
+ Compute the PE/COFF Authenticode-style image hash of an image as described
+ in "Windows Authenticode Portable Executable Signature Format".
+
+ The caller selects the digest algorithm by HashType (e.g.
+ gEfiCertSha256Guid, gEfiCertSha384Guid).
+
+ If the Data buffer is too small to hold the contents of the digest,
+ the error EFI_BUFFER_TOO_SMALL is returned and DigestSize is set to
+ the required buffer size to obtain the data.
+
+ @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.
+ @retval EFI_BUFFER_TOO_SMALL DigestSize is too small for the
+ requested hash algorithm.
+ @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
+ );
+
+/**
+ Determine the image-hash algorithm declared by a PE/COFF Authenticode
+ signature.
+
+ The signature's PKCS#7 SignedData header carries the digest algorithm
+ used to hash the image. This function parses that header and returns
+ the matching signature-type GUID (for example gEfiCertSha256Guid).
+ The returned GUID can be passed directly to GetAuthenticodeHash to
+ compute the corresponding image hash.
+
+ @param[in] AuthData Pointer to the Authenticode signature retrieved
+ from a signed PE/COFF image.
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] HashType On success, receives the signature-type GUID
+ identifying the digest algorithm declared by
+ the signature.
+
+ @retval EFI_SUCCESS HashType has been populated.
+ @retval EFI_INVALID_PARAMETER AuthData or HashType is NULL, or
+ AuthDataSize is zero.
+ @retval EFI_BAD_BUFFER_SIZE AuthData is too small or not encoded in a
+ supported ASN.1 form.
+ @retval EFI_UNSUPPORTED The signature's digest algorithm is not a
+ recognized image hash algorithm, or this
+ interface is not supported by the
+ underlying library instance.
+**/
+EFI_STATUS
+EFIAPI
+GetAuthenticodeHashAlgorithm (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT EFI_GUID *HashType
+ );
+
+/**
+ 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, or this interface is
+ not supported by the underlying
+ library instance.
+**/
+EFI_STATUS
+EFIAPI
+X509GetTbsCertHash (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ );
+
+/**
+ 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.
+
+ The hash algorithm is selected by TbsCertHashSize:
+ 20 -> SHA-1, 32 -> SHA-256, 48 -> SHA-384, 64 -> SHA-512.
+
+ An optional cache may be passed via CacheHandle. CacheHandle is a
+ pointer to a caller-owned VOID *:
+ - If CacheHandle is NULL, no caching is performed.
+ - If *CacheHandle is NULL, this function allocates a new cache and
+ writes its handle to *CacheHandle. The caller releases it with
+ FreeTrustAnchorX509Cache().
+ - Otherwise, *CacheHandle is reused. The cache may be reused across
+ different AuthData inputs; entries are de-duplicated by the cert
+ DER bytes.
+
+ Caution: AuthData is untrusted. The PKCS#7 ASN.1 DER is parsed with
+ bounds-checked length decoding to avoid out-of-bounds reads.
+
+ @param[in,out] CacheHandle Optional cache handle pointer.
+ @param[in] TbsCertHash Pointer to the target TBSCertificate
+ digest bytes to match.
+ @param[in] TbsCertHashSize Length of TbsCertHash in bytes; this
+ selects the hash algorithm.
+ @param[in] AuthData Pointer to the PKCS#7 SignedData
+ blob (DER-encoded).
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] TrustAnchorX509 On success, *TrustAnchorX509 points
+ to a newly allocated buffer
+ containing the matching X.509
+ certificate in DER form. The caller
+ must release it with FreePool().
+ @param[out] TrustAnchorX509Size On success, receives the length
+ of the certificate in bytes.
+
+ @retval EFI_SUCCESS A matching certificate was found and
+ returned.
+ @retval EFI_NOT_FOUND No certificate in AuthData has a
+ TBSCertificate digest equal to the
+ supplied TbsCertHash.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL, a size
+ parameter is 0, the hash size is not a
+ supported value, or AuthData is not a
+ valid PKCS#7 SignedData blob.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_UNSUPPORTED This interface is not supported by the
+ current library instance.
+**/
+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
+ );
+
+/**
+ Release a trust-anchor cache previously allocated by
+ GetTrustAnchorX509FromAuthData().
+
+ @param[in] CacheHandle Cache handle returned by a previous call to
+ GetTrustAnchorX509FromAuthData(). May be
+ NULL, in which case the function is a no-op.
+**/
+VOID
+EFIAPI
+FreeTrustAnchorX509Cache (
+ IN VOID *CacheHandle OPTIONAL
+ );
+
/**
Verifies the validity of a RFC3161 Timestamp CounterSignature embedded in PE/COFF Authenticode
signature.
diff --git a/CryptoPkg/Include/Protocol/OneCrypto.h b/CryptoPkg/Include/Protocol/OneCrypto.h
index 8e71513c13f..cc229a89ec6 100644
--- a/CryptoPkg/Include/Protocol/OneCrypto.h
+++ b/CryptoPkg/Include/Protocol/OneCrypto.h
@@ -1986,6 +1986,99 @@ typedef BOOLEAN (EFIAPI *ONE_CRYPTO_AUTHENTICODE_VERIFY)(
IN UINTN HashSize
);
+/**
+ 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.
+
+ The hash algorithm is selected by TbsCertHashSize:
+ 20 -> SHA-1, 32 -> SHA-256, 48 -> SHA-384, 64 -> SHA-512.
+
+ CacheHandle is an optional caller-managed cache pointer:
+ - If CacheHandle is NULL, no caching is performed.
+ - If *CacheHandle is NULL, a new cache is allocated; the caller
+ releases it with ONE_CRYPTO_FREE_TRUST_ANCHOR_X509_CACHE.
+
+ @param[in,out] CacheHandle Optional cache handle pointer.
+ @param[in] TbsCertHash Pointer to the target TBSCertificate
+ digest bytes.
+ @param[in] TbsCertHashSize Length of TbsCertHash in bytes.
+ @param[in] AuthData Pointer to the PKCS#7 SignedData
+ blob (DER-encoded).
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] TrustAnchorX509 Receives a newly allocated buffer
+ holding the matching X.509 cert in
+ DER form. Caller frees with
+ FreePool().
+ @param[out] TrustAnchorX509Size Receives the certificate length.
+
+ @retval EFI_SUCCESS A matching certificate was found.
+ @retval EFI_NOT_FOUND No certificate matched.
+ @retval EFI_INVALID_PARAMETER Bad parameter or malformed AuthData.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_UNSUPPORTED Interface not supported.
+
+ @since 1.1
+ @ingroup PKCS
+**/
+typedef EFI_STATUS (EFIAPI *ONE_CRYPTO_GET_TRUST_ANCHOR_X509_FROM_AUTH_DATA)(
+ 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
+ );
+
+/**
+ Release a trust-anchor cache previously allocated by
+ ONE_CRYPTO_GET_TRUST_ANCHOR_X509_FROM_AUTH_DATA.
+
+ @param[in] CacheHandle Cache handle, or NULL.
+
+ @since 1.1
+ @ingroup PKCS
+**/
+typedef VOID (EFIAPI *ONE_CRYPTO_FREE_TRUST_ANCHOR_X509_CACHE)(
+ IN VOID *CacheHandle OPTIONAL
+ );
+
+/**
+ 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 recovered digest uniquely identifies the certificate
+ independent of the issuer signature and can be matched against the
+ TbsCertHash argument of GetTrustAnchorX509FromAuthData().
+
+ @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 Bad parameter or malformed certificate.
+ @retval EFI_UNSUPPORTED Unrecognized hash algorithm, or
+ interface not supported.
+
+ @since 1.1
+ @ingroup PKCS
+**/
+typedef EFI_STATUS (EFIAPI *ONE_CRYPTO_X509_GET_TBS_CERT_HASH)(
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ );
+
/**
Encrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the
encrypted message in a newly allocated buffer.
@@ -5241,236 +5334,239 @@ typedef struct _ONE_CRYPTO_PROTOCOL {
// Minor - Functions added to the end of this structure
//
// ---------------------------------------------------------------------------
- UINT16 Major;
- UINT16 Minor;
+ UINT16 Major;
+ UINT16 Minor;
/// v1.0 HMAC --------------------------------------------------------------
- ONE_CRYPTO_HMAC_SHA256_NEW HmacSha256New;
- ONE_CRYPTO_HMAC_SHA256_FREE HmacSha256Free;
- ONE_CRYPTO_HMAC_SHA256_SET_KEY HmacSha256SetKey;
- ONE_CRYPTO_HMAC_SHA256_DUPLICATE HmacSha256Duplicate;
- ONE_CRYPTO_HMAC_SHA256_UPDATE HmacSha256Update;
- ONE_CRYPTO_HMAC_SHA256_FINAL HmacSha256Final;
- ONE_CRYPTO_HMAC_SHA256_ALL HmacSha256All;
- ONE_CRYPTO_HMAC_SHA384_NEW HmacSha384New;
- ONE_CRYPTO_HMAC_SHA384_FREE HmacSha384Free;
- ONE_CRYPTO_HMAC_SHA384_SET_KEY HmacSha384SetKey;
- ONE_CRYPTO_HMAC_SHA384_DUPLICATE HmacSha384Duplicate;
- ONE_CRYPTO_HMAC_SHA384_UPDATE HmacSha384Update;
- ONE_CRYPTO_HMAC_SHA384_FINAL HmacSha384Final;
- ONE_CRYPTO_HMAC_SHA384_ALL HmacSha384All;
+ ONE_CRYPTO_HMAC_SHA256_NEW HmacSha256New;
+ ONE_CRYPTO_HMAC_SHA256_FREE HmacSha256Free;
+ ONE_CRYPTO_HMAC_SHA256_SET_KEY HmacSha256SetKey;
+ ONE_CRYPTO_HMAC_SHA256_DUPLICATE HmacSha256Duplicate;
+ ONE_CRYPTO_HMAC_SHA256_UPDATE HmacSha256Update;
+ ONE_CRYPTO_HMAC_SHA256_FINAL HmacSha256Final;
+ ONE_CRYPTO_HMAC_SHA256_ALL HmacSha256All;
+ ONE_CRYPTO_HMAC_SHA384_NEW HmacSha384New;
+ ONE_CRYPTO_HMAC_SHA384_FREE HmacSha384Free;
+ ONE_CRYPTO_HMAC_SHA384_SET_KEY HmacSha384SetKey;
+ ONE_CRYPTO_HMAC_SHA384_DUPLICATE HmacSha384Duplicate;
+ ONE_CRYPTO_HMAC_SHA384_UPDATE HmacSha384Update;
+ ONE_CRYPTO_HMAC_SHA384_FINAL HmacSha384Final;
+ ONE_CRYPTO_HMAC_SHA384_ALL HmacSha384All;
/// v1.0 Hash --------------------------------------------------------------
- ONE_CRYPTO_MD5_GET_CONTEXT_SIZE Md5GetContextSize;
- ONE_CRYPTO_MD5_INIT Md5Init;
- ONE_CRYPTO_MD5_UPDATE Md5Update;
- ONE_CRYPTO_MD5_FINAL Md5Final;
- ONE_CRYPTO_MD5_HASH_ALL Md5HashAll;
- ONE_CRYPTO_MD5_DUPLICATE Md5Duplicate;
- ONE_CRYPTO_SHA1_GET_CONTEXT_SIZE Sha1GetContextSize;
- ONE_CRYPTO_SHA1_INIT Sha1Init;
- ONE_CRYPTO_SHA1_UPDATE Sha1Update;
- ONE_CRYPTO_SHA1_FINAL Sha1Final;
- ONE_CRYPTO_SHA1_HASH_ALL Sha1HashAll;
- ONE_CRYPTO_SHA1_DUPLICATE Sha1Duplicate;
- ONE_CRYPTO_SHA256_GET_CONTEXT_SIZE Sha256GetContextSize;
- ONE_CRYPTO_SHA256_INIT Sha256Init;
- ONE_CRYPTO_SHA256_UPDATE Sha256Update;
- ONE_CRYPTO_SHA256_FINAL Sha256Final;
- ONE_CRYPTO_SHA256_HASH_ALL Sha256HashAll;
- ONE_CRYPTO_SHA256_DUPLICATE Sha256Duplicate;
- ONE_CRYPTO_SHA384_GET_CONTEXT_SIZE Sha384GetContextSize;
- ONE_CRYPTO_SHA384_INIT Sha384Init;
- ONE_CRYPTO_SHA384_DUPLICATE Sha384Duplicate;
- ONE_CRYPTO_SHA384_UPDATE Sha384Update;
- ONE_CRYPTO_SHA384_FINAL Sha384Final;
- ONE_CRYPTO_SHA384_HASH_ALL Sha384HashAll;
- ONE_CRYPTO_SHA512_GET_CONTEXT_SIZE Sha512GetContextSize;
- ONE_CRYPTO_SHA512_INIT Sha512Init;
- ONE_CRYPTO_SHA512_DUPLICATE Sha512Duplicate;
- ONE_CRYPTO_SHA512_UPDATE Sha512Update;
- ONE_CRYPTO_SHA512_FINAL Sha512Final;
- ONE_CRYPTO_SHA512_HASH_ALL Sha512HashAll;
- ONE_CRYPTO_SM3_GET_CONTEXT_SIZE Sm3GetContextSize;
- ONE_CRYPTO_SM3_INIT Sm3Init;
- ONE_CRYPTO_SM3_DUPLICATE Sm3Duplicate;
- ONE_CRYPTO_SM3_UPDATE Sm3Update;
- ONE_CRYPTO_SM3_FINAL Sm3Final;
- ONE_CRYPTO_SM3_HASH_ALL Sm3HashAll;
+ ONE_CRYPTO_MD5_GET_CONTEXT_SIZE Md5GetContextSize;
+ ONE_CRYPTO_MD5_INIT Md5Init;
+ ONE_CRYPTO_MD5_UPDATE Md5Update;
+ ONE_CRYPTO_MD5_FINAL Md5Final;
+ ONE_CRYPTO_MD5_HASH_ALL Md5HashAll;
+ ONE_CRYPTO_MD5_DUPLICATE Md5Duplicate;
+ ONE_CRYPTO_SHA1_GET_CONTEXT_SIZE Sha1GetContextSize;
+ ONE_CRYPTO_SHA1_INIT Sha1Init;
+ ONE_CRYPTO_SHA1_UPDATE Sha1Update;
+ ONE_CRYPTO_SHA1_FINAL Sha1Final;
+ ONE_CRYPTO_SHA1_HASH_ALL Sha1HashAll;
+ ONE_CRYPTO_SHA1_DUPLICATE Sha1Duplicate;
+ ONE_CRYPTO_SHA256_GET_CONTEXT_SIZE Sha256GetContextSize;
+ ONE_CRYPTO_SHA256_INIT Sha256Init;
+ ONE_CRYPTO_SHA256_UPDATE Sha256Update;
+ ONE_CRYPTO_SHA256_FINAL Sha256Final;
+ ONE_CRYPTO_SHA256_HASH_ALL Sha256HashAll;
+ ONE_CRYPTO_SHA256_DUPLICATE Sha256Duplicate;
+ ONE_CRYPTO_SHA384_GET_CONTEXT_SIZE Sha384GetContextSize;
+ ONE_CRYPTO_SHA384_INIT Sha384Init;
+ ONE_CRYPTO_SHA384_DUPLICATE Sha384Duplicate;
+ ONE_CRYPTO_SHA384_UPDATE Sha384Update;
+ ONE_CRYPTO_SHA384_FINAL Sha384Final;
+ ONE_CRYPTO_SHA384_HASH_ALL Sha384HashAll;
+ ONE_CRYPTO_SHA512_GET_CONTEXT_SIZE Sha512GetContextSize;
+ ONE_CRYPTO_SHA512_INIT Sha512Init;
+ ONE_CRYPTO_SHA512_DUPLICATE Sha512Duplicate;
+ ONE_CRYPTO_SHA512_UPDATE Sha512Update;
+ ONE_CRYPTO_SHA512_FINAL Sha512Final;
+ ONE_CRYPTO_SHA512_HASH_ALL Sha512HashAll;
+ ONE_CRYPTO_SM3_GET_CONTEXT_SIZE Sm3GetContextSize;
+ ONE_CRYPTO_SM3_INIT Sm3Init;
+ ONE_CRYPTO_SM3_DUPLICATE Sm3Duplicate;
+ ONE_CRYPTO_SM3_UPDATE Sm3Update;
+ ONE_CRYPTO_SM3_FINAL Sm3Final;
+ ONE_CRYPTO_SM3_HASH_ALL Sm3HashAll;
/// v1.0 AES ---------------------------------------------------------------
- ONE_CRYPTO_AES_GET_CONTEXT_SIZE AesGetContextSize;
- ONE_CRYPTO_AES_INIT AesInit;
- ONE_CRYPTO_AES_CBC_ENCRYPT AesCbcEncrypt;
- ONE_CRYPTO_AES_CBC_DECRYPT AesCbcDecrypt;
- ONE_CRYPTO_AEAD_AES_GCM_ENCRYPT AeadAesGcmEncrypt;
- ONE_CRYPTO_AEAD_AES_GCM_DECRYPT AeadAesGcmDecrypt;
+ ONE_CRYPTO_AES_GET_CONTEXT_SIZE AesGetContextSize;
+ ONE_CRYPTO_AES_INIT AesInit;
+ ONE_CRYPTO_AES_CBC_ENCRYPT AesCbcEncrypt;
+ ONE_CRYPTO_AES_CBC_DECRYPT AesCbcDecrypt;
+ ONE_CRYPTO_AEAD_AES_GCM_ENCRYPT AeadAesGcmEncrypt;
+ ONE_CRYPTO_AEAD_AES_GCM_DECRYPT AeadAesGcmDecrypt;
/// v1.0 BN ----------------------------------------------------------------
- ONE_CRYPTO_BIG_NUM_INIT BigNumInit;
- ONE_CRYPTO_BIG_NUM_FROM_BIN BigNumFromBin;
- ONE_CRYPTO_BIG_NUM_TO_BIN BigNumToBin;
- ONE_CRYPTO_BIG_NUM_FREE BigNumFree;
- ONE_CRYPTO_BIG_NUM_ADD BigNumAdd;
- ONE_CRYPTO_BIG_NUM_SUB BigNumSub;
- ONE_CRYPTO_BIG_NUM_MOD BigNumMod;
- ONE_CRYPTO_BIG_NUM_EXP_MOD BigNumExpMod;
- ONE_CRYPTO_BIG_NUM_INVERSE_MOD BigNumInverseMod;
- ONE_CRYPTO_BIG_NUM_DIV BigNumDiv;
- ONE_CRYPTO_BIG_NUM_MUL_MOD BigNumMulMod;
- ONE_CRYPTO_BIG_NUM_CMP BigNumCmp;
- ONE_CRYPTO_BIG_NUM_BITS BigNumBits;
- ONE_CRYPTO_BIG_NUM_BYTES BigNumBytes;
- ONE_CRYPTO_BIG_NUM_IS_WORD BigNumIsWord;
- ONE_CRYPTO_BIG_NUM_IS_ODD BigNumIsOdd;
- ONE_CRYPTO_BIG_NUM_COPY BigNumCopy;
- ONE_CRYPTO_BIG_NUM_VALUE_ONE BigNumValueOne;
- ONE_CRYPTO_BIG_NUM_R_SHIFT BigNumRShift;
- ONE_CRYPTO_BIG_NUM_CONST_TIME BigNumConstTime;
- ONE_CRYPTO_BIG_NUM_SQR_MOD BigNumSqrMod;
- ONE_CRYPTO_BIG_NUM_NEW_CONTEXT BigNumNewContext;
- ONE_CRYPTO_BIG_NUM_CONTEXT_FREE BigNumContextFree;
- ONE_CRYPTO_BIG_NUM_SET_UINT BigNumSetUint;
- ONE_CRYPTO_BIG_NUM_ADD_MOD BigNumAddMod;
+ ONE_CRYPTO_BIG_NUM_INIT BigNumInit;
+ ONE_CRYPTO_BIG_NUM_FROM_BIN BigNumFromBin;
+ ONE_CRYPTO_BIG_NUM_TO_BIN BigNumToBin;
+ ONE_CRYPTO_BIG_NUM_FREE BigNumFree;
+ ONE_CRYPTO_BIG_NUM_ADD BigNumAdd;
+ ONE_CRYPTO_BIG_NUM_SUB BigNumSub;
+ ONE_CRYPTO_BIG_NUM_MOD BigNumMod;
+ ONE_CRYPTO_BIG_NUM_EXP_MOD BigNumExpMod;
+ ONE_CRYPTO_BIG_NUM_INVERSE_MOD BigNumInverseMod;
+ ONE_CRYPTO_BIG_NUM_DIV BigNumDiv;
+ ONE_CRYPTO_BIG_NUM_MUL_MOD BigNumMulMod;
+ ONE_CRYPTO_BIG_NUM_CMP BigNumCmp;
+ ONE_CRYPTO_BIG_NUM_BITS BigNumBits;
+ ONE_CRYPTO_BIG_NUM_BYTES BigNumBytes;
+ ONE_CRYPTO_BIG_NUM_IS_WORD BigNumIsWord;
+ ONE_CRYPTO_BIG_NUM_IS_ODD BigNumIsOdd;
+ ONE_CRYPTO_BIG_NUM_COPY BigNumCopy;
+ ONE_CRYPTO_BIG_NUM_VALUE_ONE BigNumValueOne;
+ ONE_CRYPTO_BIG_NUM_R_SHIFT BigNumRShift;
+ ONE_CRYPTO_BIG_NUM_CONST_TIME BigNumConstTime;
+ ONE_CRYPTO_BIG_NUM_SQR_MOD BigNumSqrMod;
+ ONE_CRYPTO_BIG_NUM_NEW_CONTEXT BigNumNewContext;
+ ONE_CRYPTO_BIG_NUM_CONTEXT_FREE BigNumContextFree;
+ ONE_CRYPTO_BIG_NUM_SET_UINT BigNumSetUint;
+ ONE_CRYPTO_BIG_NUM_ADD_MOD BigNumAddMod;
/// v1.0 HKDF --------------------------------------------------------------
- ONE_CRYPTO_HKDF_SHA256_EXTRACT_AND_EXPAND HkdfSha256ExtractAndExpand;
- ONE_CRYPTO_HKDF_SHA256_EXTRACT HkdfSha256Extract;
- ONE_CRYPTO_HKDF_SHA256_EXPAND HkdfSha256Expand;
- ONE_CRYPTO_HKDF_SHA384_EXTRACT_AND_EXPAND HkdfSha384ExtractAndExpand;
- ONE_CRYPTO_HKDF_SHA384_EXTRACT HkdfSha384Extract;
- ONE_CRYPTO_HKDF_SHA384_EXPAND HkdfSha384Expand;
+ ONE_CRYPTO_HKDF_SHA256_EXTRACT_AND_EXPAND HkdfSha256ExtractAndExpand;
+ ONE_CRYPTO_HKDF_SHA256_EXTRACT HkdfSha256Extract;
+ ONE_CRYPTO_HKDF_SHA256_EXPAND HkdfSha256Expand;
+ ONE_CRYPTO_HKDF_SHA384_EXTRACT_AND_EXPAND HkdfSha384ExtractAndExpand;
+ ONE_CRYPTO_HKDF_SHA384_EXTRACT HkdfSha384Extract;
+ ONE_CRYPTO_HKDF_SHA384_EXPAND HkdfSha384Expand;
/// v1.0 PKCS --------------------------------------------------------------
- ONE_CRYPTO_AUTHENTICODE_VERIFY AuthenticodeVerify;
- ONE_CRYPTO_PKCS1V2_ENCRYPT Pkcs1v2Encrypt;
- ONE_CRYPTO_PKCS1V2_DECRYPT Pkcs1v2Decrypt;
- ONE_CRYPTO_RSA_OAEP_ENCRYPT RsaOaepEncrypt;
- ONE_CRYPTO_RSA_OAEP_DECRYPT RsaOaepDecrypt;
- ONE_CRYPTO_PKCS5_HASH_PASSWORD Pkcs5HashPassword;
- ONE_CRYPTO_PKCS7_GET_SIGNERS Pkcs7GetSigners;
- ONE_CRYPTO_PKCS7_FREE_SIGNERS Pkcs7FreeSigners;
- ONE_CRYPTO_PKCS7_GET_CERTIFICATES_LIST Pkcs7GetCertificatesList;
- ONE_CRYPTO_PKCS7_SIGN Pkcs7Sign;
- ONE_CRYPTO_PKCS7_VERIFY Pkcs7Verify;
- ONE_CRYPTO_PKCS7_ENCRYPT Pkcs7Encrypt;
- ONE_CRYPTO_VERIFY_EK_US_IN_PKCS7_SIGNATURE VerifyEKUsInPkcs7Signature;
- ONE_CRYPTO_PKCS7_GET_ATTACHED_CONTENT Pkcs7GetAttachedContent;
+ ONE_CRYPTO_AUTHENTICODE_VERIFY AuthenticodeVerify;
+ ONE_CRYPTO_PKCS1V2_ENCRYPT Pkcs1v2Encrypt;
+ ONE_CRYPTO_PKCS1V2_DECRYPT Pkcs1v2Decrypt;
+ ONE_CRYPTO_RSA_OAEP_ENCRYPT RsaOaepEncrypt;
+ ONE_CRYPTO_RSA_OAEP_DECRYPT RsaOaepDecrypt;
+ ONE_CRYPTO_PKCS5_HASH_PASSWORD Pkcs5HashPassword;
+ ONE_CRYPTO_PKCS7_GET_SIGNERS Pkcs7GetSigners;
+ ONE_CRYPTO_PKCS7_FREE_SIGNERS Pkcs7FreeSigners;
+ ONE_CRYPTO_PKCS7_GET_CERTIFICATES_LIST Pkcs7GetCertificatesList;
+ ONE_CRYPTO_PKCS7_SIGN Pkcs7Sign;
+ ONE_CRYPTO_PKCS7_VERIFY Pkcs7Verify;
+ ONE_CRYPTO_PKCS7_ENCRYPT Pkcs7Encrypt;
+ ONE_CRYPTO_VERIFY_EK_US_IN_PKCS7_SIGNATURE VerifyEKUsInPkcs7Signature;
+ ONE_CRYPTO_PKCS7_GET_ATTACHED_CONTENT Pkcs7GetAttachedContent;
/// v1.0 DH ----------------------------------------------------------------
- ONE_CRYPTO_DH_NEW DhNew;
- ONE_CRYPTO_DH_FREE DhFree;
- ONE_CRYPTO_DH_GENERATE_PARAMETER DhGenerateParameter;
- ONE_CRYPTO_DH_SET_PARAMETER DhSetParameter;
- ONE_CRYPTO_DH_GENERATE_KEY DhGenerateKey;
- ONE_CRYPTO_DH_COMPUTE_KEY DhComputeKey;
+ ONE_CRYPTO_DH_NEW DhNew;
+ ONE_CRYPTO_DH_FREE DhFree;
+ ONE_CRYPTO_DH_GENERATE_PARAMETER DhGenerateParameter;
+ ONE_CRYPTO_DH_SET_PARAMETER DhSetParameter;
+ ONE_CRYPTO_DH_GENERATE_KEY DhGenerateKey;
+ ONE_CRYPTO_DH_COMPUTE_KEY DhComputeKey;
/// v1.0 EC ----------------------------------------------------------------
- ONE_CRYPTO_EC_GROUP_INIT EcGroupInit;
- ONE_CRYPTO_EC_GROUP_GET_CURVE EcGroupGetCurve;
- ONE_CRYPTO_EC_GROUP_GET_ORDER EcGroupGetOrder;
- ONE_CRYPTO_EC_GROUP_FREE EcGroupFree;
- ONE_CRYPTO_EC_POINT_INIT EcPointInit;
- ONE_CRYPTO_EC_POINT_DE_INIT EcPointDeInit;
- ONE_CRYPTO_EC_POINT_GET_AFFINE_COORDINATES EcPointGetAffineCoordinates;
- ONE_CRYPTO_EC_POINT_SET_AFFINE_COORDINATES EcPointSetAffineCoordinates;
- ONE_CRYPTO_EC_POINT_ADD EcPointAdd;
- ONE_CRYPTO_EC_POINT_MUL EcPointMul;
- ONE_CRYPTO_EC_POINT_INVERT EcPointInvert;
- ONE_CRYPTO_EC_POINT_IS_ON_CURVE EcPointIsOnCurve;
- ONE_CRYPTO_EC_POINT_IS_AT_INFINITY EcPointIsAtInfinity;
- ONE_CRYPTO_EC_POINT_EQUAL EcPointEqual;
- ONE_CRYPTO_EC_POINT_SET_COMPRESSED_COORDINATES EcPointSetCompressedCoordinates;
- ONE_CRYPTO_EC_NEW_BY_NID EcNewByNid;
- ONE_CRYPTO_EC_FREE EcFree;
- ONE_CRYPTO_EC_GENERATE_KEY EcGenerateKey;
- ONE_CRYPTO_EC_GET_PUB_KEY EcGetPubKey;
- ONE_CRYPTO_EC_DH_COMPUTE_KEY EcDhComputeKey;
- ONE_CRYPTO_EC_GET_PRIVATE_KEY_FROM_PEM EcGetPrivateKeyFromPem;
- ONE_CRYPTO_EC_GET_PUBLIC_KEY_FROM_X509 EcGetPublicKeyFromX509;
- ONE_CRYPTO_EC_DSA_SIGN EcDsaSign;
- ONE_CRYPTO_EC_DSA_VERIFY EcDsaVerify;
+ ONE_CRYPTO_EC_GROUP_INIT EcGroupInit;
+ ONE_CRYPTO_EC_GROUP_GET_CURVE EcGroupGetCurve;
+ ONE_CRYPTO_EC_GROUP_GET_ORDER EcGroupGetOrder;
+ ONE_CRYPTO_EC_GROUP_FREE EcGroupFree;
+ ONE_CRYPTO_EC_POINT_INIT EcPointInit;
+ ONE_CRYPTO_EC_POINT_DE_INIT EcPointDeInit;
+ ONE_CRYPTO_EC_POINT_GET_AFFINE_COORDINATES EcPointGetAffineCoordinates;
+ ONE_CRYPTO_EC_POINT_SET_AFFINE_COORDINATES EcPointSetAffineCoordinates;
+ ONE_CRYPTO_EC_POINT_ADD EcPointAdd;
+ ONE_CRYPTO_EC_POINT_MUL EcPointMul;
+ ONE_CRYPTO_EC_POINT_INVERT EcPointInvert;
+ ONE_CRYPTO_EC_POINT_IS_ON_CURVE EcPointIsOnCurve;
+ ONE_CRYPTO_EC_POINT_IS_AT_INFINITY EcPointIsAtInfinity;
+ ONE_CRYPTO_EC_POINT_EQUAL EcPointEqual;
+ ONE_CRYPTO_EC_POINT_SET_COMPRESSED_COORDINATES EcPointSetCompressedCoordinates;
+ ONE_CRYPTO_EC_NEW_BY_NID EcNewByNid;
+ ONE_CRYPTO_EC_FREE EcFree;
+ ONE_CRYPTO_EC_GENERATE_KEY EcGenerateKey;
+ ONE_CRYPTO_EC_GET_PUB_KEY EcGetPubKey;
+ ONE_CRYPTO_EC_DH_COMPUTE_KEY EcDhComputeKey;
+ ONE_CRYPTO_EC_GET_PRIVATE_KEY_FROM_PEM EcGetPrivateKeyFromPem;
+ ONE_CRYPTO_EC_GET_PUBLIC_KEY_FROM_X509 EcGetPublicKeyFromX509;
+ ONE_CRYPTO_EC_DSA_SIGN EcDsaSign;
+ ONE_CRYPTO_EC_DSA_VERIFY EcDsaVerify;
/// v1.0 RSA ---------------------------------------------------------------
- ONE_CRYPTO_RSA_NEW RsaNew;
- ONE_CRYPTO_RSA_FREE RsaFree;
- ONE_CRYPTO_RSA_SET_KEY RsaSetKey;
- ONE_CRYPTO_RSA_GET_KEY RsaGetKey;
- ONE_CRYPTO_RSA_GENERATE_KEY RsaGenerateKey;
- ONE_CRYPTO_RSA_CHECK_KEY RsaCheckKey;
- ONE_CRYPTO_RSA_PKCS1_SIGN RsaPkcs1Sign;
- ONE_CRYPTO_RSA_PKCS1_VERIFY RsaPkcs1Verify;
- ONE_CRYPTO_RSA_PSS_SIGN RsaPssSign;
- ONE_CRYPTO_RSA_PSS_VERIFY RsaPssVerify;
- ONE_CRYPTO_RSA_GET_PRIVATE_KEY_FROM_PEM RsaGetPrivateKeyFromPem;
- ONE_CRYPTO_RSA_GET_PUBLIC_KEY_FROM_X509 RsaGetPublicKeyFromX509;
+ ONE_CRYPTO_RSA_NEW RsaNew;
+ ONE_CRYPTO_RSA_FREE RsaFree;
+ ONE_CRYPTO_RSA_SET_KEY RsaSetKey;
+ ONE_CRYPTO_RSA_GET_KEY RsaGetKey;
+ ONE_CRYPTO_RSA_GENERATE_KEY RsaGenerateKey;
+ ONE_CRYPTO_RSA_CHECK_KEY RsaCheckKey;
+ ONE_CRYPTO_RSA_PKCS1_SIGN RsaPkcs1Sign;
+ ONE_CRYPTO_RSA_PKCS1_VERIFY RsaPkcs1Verify;
+ ONE_CRYPTO_RSA_PSS_SIGN RsaPssSign;
+ ONE_CRYPTO_RSA_PSS_VERIFY RsaPssVerify;
+ ONE_CRYPTO_RSA_GET_PRIVATE_KEY_FROM_PEM RsaGetPrivateKeyFromPem;
+ ONE_CRYPTO_RSA_GET_PUBLIC_KEY_FROM_X509 RsaGetPublicKeyFromX509;
/// v1.0 X509 --------------------------------------------------------------
- ONE_CRYPTO_X509_GET_SUBJECT_NAME X509GetSubjectName;
- ONE_CRYPTO_X509_GET_COMMON_NAME X509GetCommonName;
- ONE_CRYPTO_X509_GET_ORGANIZATION_NAME X509GetOrganizationName;
- ONE_CRYPTO_X509_VERIFY_CERT X509VerifyCert;
- ONE_CRYPTO_X509_CONSTRUCT_CERTIFICATE X509ConstructCertificate;
- ONE_CRYPTO_X509_CONSTRUCT_CERTIFICATE_STACK_V X509ConstructCertificateStackV;
- ONE_CRYPTO_X509_CONSTRUCT_CERTIFICATE_STACK X509ConstructCertificateStack;
- ONE_CRYPTO_X509_FREE X509Free;
- ONE_CRYPTO_X509_STACK_FREE X509StackFree;
- ONE_CRYPTO_X509_GET_TBS_CERT X509GetTBSCert;
- ONE_CRYPTO_X509_GET_VERSION X509GetVersion;
- ONE_CRYPTO_X509_GET_SERIAL_NUMBER X509GetSerialNumber;
- ONE_CRYPTO_X509_GET_ISSUER_NAME X509GetIssuerName;
- ONE_CRYPTO_X509_GET_SIGNATURE_ALGORITHM X509GetSignatureAlgorithm;
- ONE_CRYPTO_X509_GET_EXTENDED_KEY_USAGE X509GetExtendedKeyUsage;
- ONE_CRYPTO_X509_GET_EXTENSION_DATA X509GetExtensionData;
- ONE_CRYPTO_X509_GET_VALIDITY X509GetValidity;
- ONE_CRYPTO_X509_FORMAT_DATE_TIME X509FormatDateTime;
- ONE_CRYPTO_X509_GET_KEY_USAGE X509GetKeyUsage;
- ONE_CRYPTO_X509_VERIFY_CERT_CHAIN X509VerifyCertChain;
- ONE_CRYPTO_X509_GET_CERT_FROM_CERT_CHAIN X509GetCertFromCertChain;
- ONE_CRYPTO_X509_GET_EXTENDED_BASIC_CONSTRAINTS X509GetExtendedBasicConstraints;
- ONE_CRYPTO_ASN1_GET_TAG Asn1GetTag;
- ONE_CRYPTO_X509_COMPARE_DATE_TIME X509CompareDateTime;
+ ONE_CRYPTO_X509_GET_SUBJECT_NAME X509GetSubjectName;
+ ONE_CRYPTO_X509_GET_COMMON_NAME X509GetCommonName;
+ ONE_CRYPTO_X509_GET_ORGANIZATION_NAME X509GetOrganizationName;
+ ONE_CRYPTO_X509_VERIFY_CERT X509VerifyCert;
+ ONE_CRYPTO_X509_CONSTRUCT_CERTIFICATE X509ConstructCertificate;
+ ONE_CRYPTO_X509_CONSTRUCT_CERTIFICATE_STACK_V X509ConstructCertificateStackV;
+ ONE_CRYPTO_X509_CONSTRUCT_CERTIFICATE_STACK X509ConstructCertificateStack;
+ ONE_CRYPTO_X509_FREE X509Free;
+ ONE_CRYPTO_X509_STACK_FREE X509StackFree;
+ ONE_CRYPTO_X509_GET_TBS_CERT X509GetTBSCert;
+ ONE_CRYPTO_X509_GET_VERSION X509GetVersion;
+ ONE_CRYPTO_X509_GET_SERIAL_NUMBER X509GetSerialNumber;
+ ONE_CRYPTO_X509_GET_ISSUER_NAME X509GetIssuerName;
+ ONE_CRYPTO_X509_GET_SIGNATURE_ALGORITHM X509GetSignatureAlgorithm;
+ ONE_CRYPTO_X509_GET_EXTENDED_KEY_USAGE X509GetExtendedKeyUsage;
+ ONE_CRYPTO_X509_GET_EXTENSION_DATA X509GetExtensionData;
+ ONE_CRYPTO_X509_GET_VALIDITY X509GetValidity;
+ ONE_CRYPTO_X509_FORMAT_DATE_TIME X509FormatDateTime;
+ ONE_CRYPTO_X509_GET_KEY_USAGE X509GetKeyUsage;
+ ONE_CRYPTO_X509_VERIFY_CERT_CHAIN X509VerifyCertChain;
+ ONE_CRYPTO_X509_GET_CERT_FROM_CERT_CHAIN X509GetCertFromCertChain;
+ ONE_CRYPTO_X509_GET_EXTENDED_BASIC_CONSTRAINTS X509GetExtendedBasicConstraints;
+ ONE_CRYPTO_ASN1_GET_TAG Asn1GetTag;
+ ONE_CRYPTO_X509_COMPARE_DATE_TIME X509CompareDateTime;
/// v1.0 Random ------------------------------------------------------------
- ONE_CRYPTO_RANDOM_SEED RandomSeed;
- ONE_CRYPTO_RANDOM_BYTES RandomBytes;
+ ONE_CRYPTO_RANDOM_SEED RandomSeed;
+ ONE_CRYPTO_RANDOM_BYTES RandomBytes;
/// v1.0 Tls ---------------------------------------------------------------
- ONE_CRYPTO_TLS_INITIALIZE TlsInitialize;
- ONE_CRYPTO_TLS_CTX_FREE TlsCtxFree;
- ONE_CRYPTO_TLS_CTX_NEW TlsCtxNew;
- ONE_CRYPTO_TLS_FREE TlsFree;
- ONE_CRYPTO_TLS_NEW TlsNew;
- ONE_CRYPTO_TLS_IN_HANDSHAKE TlsInHandshake;
- ONE_CRYPTO_TLS_DO_HANDSHAKE TlsDoHandshake;
- ONE_CRYPTO_TLS_HANDLE_ALERT TlsHandleAlert;
- ONE_CRYPTO_TLS_CLOSE_NOTIFY TlsCloseNotify;
- ONE_CRYPTO_TLS_CTRL_TRAFFIC_OUT TlsCtrlTrafficOut;
- ONE_CRYPTO_TLS_CTRL_TRAFFIC_IN TlsCtrlTrafficIn;
- ONE_CRYPTO_TLS_READ TlsRead;
- ONE_CRYPTO_TLS_WRITE TlsWrite;
- ONE_CRYPTO_TLS_SHUTDOWN TlsShutdown;
- ONE_CRYPTO_TLS_SET_VERSION TlsSetVersion;
- ONE_CRYPTO_TLS_SET_CONNECTION_END TlsSetConnectionEnd;
- ONE_CRYPTO_TLS_SET_CIPHER_LIST TlsSetCipherList;
- ONE_CRYPTO_TLS_SET_COMPRESSION_METHOD TlsSetCompressionMethod;
- ONE_CRYPTO_TLS_SET_VERIFY TlsSetVerify;
- ONE_CRYPTO_TLS_SET_VERIFY_HOST TlsSetVerifyHost;
- ONE_CRYPTO_TLS_SET_SESSION_ID TlsSetSessionId;
- ONE_CRYPTO_TLS_SET_CA_CERTIFICATE TlsSetCaCertificate;
- ONE_CRYPTO_TLS_SET_HOST_PUBLIC_CERT TlsSetHostPublicCert;
- ONE_CRYPTO_TLS_SET_HOST_PRIVATE_KEY_EX TlsSetHostPrivateKeyEx;
- ONE_CRYPTO_TLS_SET_HOST_PRIVATE_KEY TlsSetHostPrivateKey;
- ONE_CRYPTO_TLS_SET_CERT_REVOCATION_LIST TlsSetCertRevocationList;
- ONE_CRYPTO_TLS_SET_SIGNATURE_ALGO_LIST TlsSetSignatureAlgoList;
- ONE_CRYPTO_TLS_SET_EC_CURVE TlsSetEcCurve;
- ONE_CRYPTO_TLS_GET_VERSION TlsGetVersion;
- ONE_CRYPTO_TLS_GET_CONNECTION_END TlsGetConnectionEnd;
- ONE_CRYPTO_TLS_GET_CURRENT_CIPHER TlsGetCurrentCipher;
- ONE_CRYPTO_TLS_GET_CURRENT_COMPRESSION_ID TlsGetCurrentCompressionId;
- ONE_CRYPTO_TLS_GET_VERIFY TlsGetVerify;
- ONE_CRYPTO_TLS_GET_SESSION_ID TlsGetSessionId;
- ONE_CRYPTO_TLS_GET_CLIENT_RANDOM TlsGetClientRandom;
- ONE_CRYPTO_TLS_GET_SERVER_RANDOM TlsGetServerRandom;
- ONE_CRYPTO_TLS_GET_KEY_MATERIAL TlsGetKeyMaterial;
- ONE_CRYPTO_TLS_GET_CA_CERTIFICATE TlsGetCaCertificate;
- ONE_CRYPTO_TLS_GET_HOST_PUBLIC_CERT TlsGetHostPublicCert;
- ONE_CRYPTO_TLS_GET_HOST_PRIVATE_KEY TlsGetHostPrivateKey;
- ONE_CRYPTO_TLS_GET_CERT_REVOCATION_LIST TlsGetCertRevocationList;
- ONE_CRYPTO_TLS_GET_EXPORT_KEY TlsGetExportKey;
+ ONE_CRYPTO_TLS_INITIALIZE TlsInitialize;
+ ONE_CRYPTO_TLS_CTX_FREE TlsCtxFree;
+ ONE_CRYPTO_TLS_CTX_NEW TlsCtxNew;
+ ONE_CRYPTO_TLS_FREE TlsFree;
+ ONE_CRYPTO_TLS_NEW TlsNew;
+ ONE_CRYPTO_TLS_IN_HANDSHAKE TlsInHandshake;
+ ONE_CRYPTO_TLS_DO_HANDSHAKE TlsDoHandshake;
+ ONE_CRYPTO_TLS_HANDLE_ALERT TlsHandleAlert;
+ ONE_CRYPTO_TLS_CLOSE_NOTIFY TlsCloseNotify;
+ ONE_CRYPTO_TLS_CTRL_TRAFFIC_OUT TlsCtrlTrafficOut;
+ ONE_CRYPTO_TLS_CTRL_TRAFFIC_IN TlsCtrlTrafficIn;
+ ONE_CRYPTO_TLS_READ TlsRead;
+ ONE_CRYPTO_TLS_WRITE TlsWrite;
+ ONE_CRYPTO_TLS_SHUTDOWN TlsShutdown;
+ ONE_CRYPTO_TLS_SET_VERSION TlsSetVersion;
+ ONE_CRYPTO_TLS_SET_CONNECTION_END TlsSetConnectionEnd;
+ ONE_CRYPTO_TLS_SET_CIPHER_LIST TlsSetCipherList;
+ ONE_CRYPTO_TLS_SET_COMPRESSION_METHOD TlsSetCompressionMethod;
+ ONE_CRYPTO_TLS_SET_VERIFY TlsSetVerify;
+ ONE_CRYPTO_TLS_SET_VERIFY_HOST TlsSetVerifyHost;
+ ONE_CRYPTO_TLS_SET_SESSION_ID TlsSetSessionId;
+ ONE_CRYPTO_TLS_SET_CA_CERTIFICATE TlsSetCaCertificate;
+ ONE_CRYPTO_TLS_SET_HOST_PUBLIC_CERT TlsSetHostPublicCert;
+ ONE_CRYPTO_TLS_SET_HOST_PRIVATE_KEY_EX TlsSetHostPrivateKeyEx;
+ ONE_CRYPTO_TLS_SET_HOST_PRIVATE_KEY TlsSetHostPrivateKey;
+ ONE_CRYPTO_TLS_SET_CERT_REVOCATION_LIST TlsSetCertRevocationList;
+ ONE_CRYPTO_TLS_SET_SIGNATURE_ALGO_LIST TlsSetSignatureAlgoList;
+ ONE_CRYPTO_TLS_SET_EC_CURVE TlsSetEcCurve;
+ ONE_CRYPTO_TLS_GET_VERSION TlsGetVersion;
+ ONE_CRYPTO_TLS_GET_CONNECTION_END TlsGetConnectionEnd;
+ ONE_CRYPTO_TLS_GET_CURRENT_CIPHER TlsGetCurrentCipher;
+ ONE_CRYPTO_TLS_GET_CURRENT_COMPRESSION_ID TlsGetCurrentCompressionId;
+ ONE_CRYPTO_TLS_GET_VERIFY TlsGetVerify;
+ ONE_CRYPTO_TLS_GET_SESSION_ID TlsGetSessionId;
+ ONE_CRYPTO_TLS_GET_CLIENT_RANDOM TlsGetClientRandom;
+ ONE_CRYPTO_TLS_GET_SERVER_RANDOM TlsGetServerRandom;
+ ONE_CRYPTO_TLS_GET_KEY_MATERIAL TlsGetKeyMaterial;
+ ONE_CRYPTO_TLS_GET_CA_CERTIFICATE TlsGetCaCertificate;
+ ONE_CRYPTO_TLS_GET_HOST_PUBLIC_CERT TlsGetHostPublicCert;
+ ONE_CRYPTO_TLS_GET_HOST_PRIVATE_KEY TlsGetHostPrivateKey;
+ ONE_CRYPTO_TLS_GET_CERT_REVOCATION_LIST TlsGetCertRevocationList;
+ ONE_CRYPTO_TLS_GET_EXPORT_KEY TlsGetExportKey;
/// v1.0 Timestamp ---------------------------------------------------------
- ONE_CRYPTO_IMAGE_TIMESTAMP_VERIFY ImageTimestampVerify;
+ ONE_CRYPTO_IMAGE_TIMESTAMP_VERIFY ImageTimestampVerify;
/// v1.0 Info --------------------------------------------------------------
- ONE_CRYPTO_GET_CRYPTO_PROVIDER_VERSION_STRING GetCryptoProviderVersionString;
+ ONE_CRYPTO_GET_CRYPTO_PROVIDER_VERSION_STRING GetCryptoProviderVersionString;
+ ONE_CRYPTO_GET_TRUST_ANCHOR_X509_FROM_AUTH_DATA GetTrustAnchorX509FromAuthData;
+ ONE_CRYPTO_FREE_TRUST_ANCHOR_X509_CACHE FreeTrustAnchorX509Cache;
+ ONE_CRYPTO_X509_GET_TBS_CERT_HASH X509GetTbsCertHash;
} ONE_CRYPTO_PROTOCOL;
/** @} */
diff --git a/CryptoPkg/Library/BaseCryptLibNull/Pk/CryptAuthenticodeNull.c b/CryptoPkg/Library/BaseCryptLibNull/Pk/CryptAuthenticodeNull.c
index 62e8400c4cc..d0197ddb924 100644
--- a/CryptoPkg/Library/BaseCryptLibNull/Pk/CryptAuthenticodeNull.c
+++ b/CryptoPkg/Library/BaseCryptLibNull/Pk/CryptAuthenticodeNull.c
@@ -43,3 +43,146 @@ AuthenticodeVerify (
ASSERT (FALSE);
return FALSE;
}
+
+/**
+ Compute the PE/COFF Authenticode-style image hash of an image as described
+ in "Windows Authenticode Portable Executable Signature Format".
+
+ The caller selects the digest algorithm by HashType (e.g.
+ gEfiCertSha256Guid, gEfiCertSha384Guid).
+
+ If the Data buffer is too small to hold the contents of the digest,
+ the error EFI_BUFFER_TOO_SMALL is returned and DigestSize is set to
+ the required buffer size to obtain the data.
+
+ @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.
+ @retval EFI_BUFFER_TOO_SMALL DigestSize is too small for the
+ requested hash algorithm.
+ @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
+ )
+{
+ ASSERT (FALSE);
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Determine the image-hash algorithm declared by a PE/COFF Authenticode
+ signature.
+
+ The signature's PKCS#7 SignedData header carries the digest algorithm
+ used to hash the image. This function parses that header and returns
+ the matching signature-type GUID (for example gEfiCertSha256Guid).
+ The returned GUID can be passed directly to GetAuthenticodeHash to
+ compute the corresponding image hash.
+
+ @param[in] AuthData Pointer to the Authenticode signature retrieved
+ from a signed PE/COFF image.
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] HashType On success, receives the signature-type GUID
+ identifying the digest algorithm declared by
+ the signature.
+
+ @retval EFI_SUCCESS HashType has been populated.
+ @retval EFI_INVALID_PARAMETER AuthData or HashType is NULL, or
+ AuthDataSize is zero.
+ @retval EFI_BAD_BUFFER_SIZE AuthData is too small or not encoded in a
+ supported ASN.1 form.
+ @retval EFI_UNSUPPORTED The signature's digest algorithm is not a
+ recognized image hash algorithm, or this
+ interface is not supported by the
+ underlying library instance.
+**/
+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.
+
+ @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;
+}
+
+/**
+ Locate, in a PKCS#7 SignedData blob, the X.509 certificate whose
+ TBSCertificate digest matches a caller-supplied hash.
+
+ Return EFI_UNSUPPORTED to indicate this interface is not supported.
+
+ @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;
+}
+
+/**
+ Release a trust-anchor cache. No-op for the Null instance.
+
+**/
+VOID
+EFIAPI
+FreeTrustAnchorX509Cache (
+ IN VOID *CacheHandle OPTIONAL
+ )
+{
+ ASSERT (FALSE);
+}
diff --git a/CryptoPkg/Library/BaseCryptLibNull/Pk/CryptX509Null.c b/CryptoPkg/Library/BaseCryptLibNull/Pk/CryptX509Null.c
index 128fcf1241c..8e34df705f3 100644
--- a/CryptoPkg/Library/BaseCryptLibNull/Pk/CryptX509Null.c
+++ b/CryptoPkg/Library/BaseCryptLibNull/Pk/CryptX509Null.c
@@ -749,3 +749,34 @@ X509GetExtendedBasicConstraints (
ASSERT (FALSE);
return FALSE;
}
+
+/**
+ Compute the digest of a DER-encoded X.509 certificate using the
+ hash algorithm identified by HashType.
+
+ 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
+GetX509Hash (
+ 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/CryptoPkg/Library/BaseCryptLibOnOneCrypto/OneCryptoLib.c b/CryptoPkg/Library/BaseCryptLibOnOneCrypto/OneCryptoLib.c
index 28c5fec9860..99d47d22b6f 100644
--- a/CryptoPkg/Library/BaseCryptLibOnOneCrypto/OneCryptoLib.c
+++ b/CryptoPkg/Library/BaseCryptLibOnOneCrypto/OneCryptoLib.c
@@ -2499,6 +2499,98 @@ AuthenticodeVerify (
CALL_CRYPTO_SERVICE (AuthenticodeVerify, (AuthData, DataSize, TrustedCert, CertSize, ImageHash, HashSize), FALSE, 1, 0);
}
+/**
+ Compute the digest of the TBSCertificate of an X.509 certificate.
+
+ @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.
+ @param[out] DigestSize On success, receives the digest length in
+ bytes.
+
+ @retval EFI_SUCCESS Digest was computed successfully.
+ @retval EFI_INVALID_PARAMETER Bad parameter or malformed certificate.
+ @retval EFI_UNSUPPORTED Unrecognized hash algorithm, or
+ interface not supported.
+
+ @since 1.1
+ @ingroup PKCS
+**/
+EFI_STATUS
+EFIAPI
+X509GetTbsCertHash (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+{
+ CALL_CRYPTO_SERVICE (X509GetTbsCertHash, (Cert, CertSize, HashType, Digest, DigestSize), EFI_UNSUPPORTED, 1, 1);
+}
+
+/**
+ Locate, in a PKCS#7 SignedData blob, the X.509 certificate whose
+ TBSCertificate digest matches a caller-supplied hash.
+
+ @param[in,out] CacheHandle Optional cache handle pointer.
+ @param[in] TbsCertHash Pointer to the target TBSCertificate
+ digest bytes.
+ @param[in] TbsCertHashSize Length of TbsCertHash in bytes
+ (selects the algorithm).
+ @param[in] AuthData Pointer to the PKCS#7 SignedData
+ blob.
+ @param[in] AuthDataSize Size of AuthData in bytes.
+ @param[out] TrustAnchorX509 Receives a newly allocated buffer
+ holding the matching X.509 cert in
+ DER form.
+ @param[out] TrustAnchorX509Size Receives the certificate length.
+
+ @retval EFI_SUCCESS Match found.
+ @retval EFI_NOT_FOUND No certificate matched.
+ @retval EFI_INVALID_PARAMETER Bad parameter or malformed AuthData.
+ @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
+ @retval EFI_UNSUPPORTED Interface not supported.
+
+ @since 1.1
+ @ingroup PKCS
+**/
+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
+ )
+{
+ CALL_CRYPTO_SERVICE (GetTrustAnchorX509FromAuthData, (CacheHandle, TbsCertHash, TbsCertHashSize, AuthData, AuthDataSize, TrustAnchorX509, TrustAnchorX509Size), EFI_UNSUPPORTED, 1, 1);
+}
+
+/**
+ Release a trust-anchor cache previously allocated by
+ GetTrustAnchorX509FromAuthData().
+
+ @param[in] CacheHandle Cache handle, or NULL.
+
+ @since 1.1
+ @ingroup PKCS
+**/
+VOID
+EFIAPI
+FreeTrustAnchorX509Cache (
+ IN VOID *CacheHandle OPTIONAL
+ )
+{
+ CALL_VOID_CRYPTO_SERVICE (FreeTrustAnchorX509Cache, (CacheHandle), 1, 1);
+}
+
/**
Encrypts a blob using PKCS1v2 (RSAES-OAEP) schema. On success, will return the
encrypted message in a newly allocated buffer.
diff --git a/CryptoPkg/Test/Mock/Include/GoogleTest/Library/MockBaseCryptLib.h b/CryptoPkg/Test/Mock/Include/GoogleTest/Library/MockBaseCryptLib.h
index d8a47d458f0..bc399ec6e4b 100644
--- a/CryptoPkg/Test/Mock/Include/GoogleTest/Library/MockBaseCryptLib.h
+++ b/CryptoPkg/Test/Mock/Include/GoogleTest/Library/MockBaseCryptLib.h
@@ -869,9 +869,7 @@ struct MockBaseCryptLib {
IN CONST UINT8 *KeyPassword,
IN UINT8 *InData,
IN UINTN InDataSize,
- // MU_CHANGE [TCBZ3925] - Pkcs7Sign is broken
IN CONST UINT8 *SignCert,
- // MU_CHANGE [TCBZ3925] - Pkcs7Sign is broken
IN UINTN SignCertSize,
IN UINT8 *OtherCerts OPTIONAL,
OUT UINT8 **SignedData,
@@ -942,6 +940,74 @@ struct MockBaseCryptLib {
)
);
+ MOCK_FUNCTION_DECLARATION (
+ EFI_STATUS,
+ GetAuthenticodeHash,
+ (
+ IN VOID *FileBuffer,
+ IN UINTN FileSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+ );
+
+ MOCK_FUNCTION_DECLARATION (
+ EFI_STATUS,
+ GetAuthenticodeHashAlgorithm,
+ (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ OUT EFI_GUID *HashType
+ )
+ );
+
+ MOCK_FUNCTION_DECLARATION (
+ EFI_STATUS,
+ X509GetTbsCertHash,
+ (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+ );
+
+ MOCK_FUNCTION_DECLARATION (
+ EFI_STATUS,
+ 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
+ )
+ );
+
+ MOCK_FUNCTION_DECLARATION (
+ VOID,
+ FreeTrustAnchorX509Cache,
+ (
+ IN VOID *CacheHandle OPTIONAL
+ )
+ );
+
+ MOCK_FUNCTION_DECLARATION (
+ EFI_STATUS,
+ GetX509Hash,
+ (
+ IN VOID *Cert,
+ IN UINTN CertSize,
+ IN CONST EFI_GUID *HashType,
+ OUT UINT8 *Digest,
+ OUT UINTN *DigestSize
+ )
+ );
+
MOCK_FUNCTION_DECLARATION (
BOOLEAN,
ImageTimestampVerify,
diff --git a/CryptoPkg/Test/Mock/Library/GoogleTest/MockBaseCryptLib/MockBaseCryptLib.cpp b/CryptoPkg/Test/Mock/Library/GoogleTest/MockBaseCryptLib/MockBaseCryptLib.cpp
index f5144911963..0e97467795f 100644
--- a/CryptoPkg/Test/Mock/Library/GoogleTest/MockBaseCryptLib/MockBaseCryptLib.cpp
+++ b/CryptoPkg/Test/Mock/Library/GoogleTest/MockBaseCryptLib/MockBaseCryptLib.cpp
@@ -92,14 +92,18 @@ MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, RsaOaepDecrypt, 6, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, Pkcs7GetSigners, 6, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, Pkcs7FreeSigners, 1, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, Pkcs7GetCertificatesList, 6, EFIAPI);
-// MU_CHANGE [TCBZ3925] - Pkcs7Sign is broken
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, Pkcs7Sign, 10, EFIAPI);
-// MU_CHANGE [TCBZ3925] - Pkcs7Sign is broken
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, Pkcs7Verify, 6, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, Pkcs7Encrypt, 7, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, VerifyEKUsInPkcs7Signature, 5, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, Pkcs7GetAttachedContent, 4, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, AuthenticodeVerify, 6, EFIAPI);
+MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, GetAuthenticodeHash, 5, EFIAPI);
+MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, GetAuthenticodeHashAlgorithm, 3, EFIAPI);
+MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, X509GetTbsCertHash, 5, EFIAPI);
+MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, GetTrustAnchorX509FromAuthData, 7, EFIAPI);
+MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, FreeTrustAnchorX509Cache, 1, EFIAPI);
+MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, GetX509Hash, 5, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, ImageTimestampVerify, 5, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, X509GetVersion, 3, EFIAPI);
MOCK_FUNCTION_DEFINITION (MockBaseCryptLib, X509GetSerialNumber, 4, EFIAPI);
diff --git a/MdePkg/Include/Library/PeCoffLib.h b/MdePkg/Include/Library/PeCoffLib.h
index 7be587f2fca..bbe82e46647 100644
--- a/MdePkg/Include/Library/PeCoffLib.h
+++ b/MdePkg/Include/Library/PeCoffLib.h
@@ -204,6 +204,16 @@ typedef struct {
/// Private storage for implementation specific data.
///
UINT64 Context;
+ ///
+ /// Set by PeCoffLoaderGetImageInfo() to a copy of the image's
+ /// EFI_IMAGE_DIRECTORY_ENTRY_SECURITY data directory entry. The entry is
+ /// populated only after PeCoffLoaderGetImageInfo() has validated that the
+ /// security data directory is non-empty and lies within the image bounds.
+ /// If the image declares no security directory, this field is zeroed. This
+ /// field is always written by PeCoffLoaderGetImageInfo(), so callers do not
+ /// need to zero-initialize the image context to consume it.
+ ///
+ EFI_IMAGE_DATA_DIRECTORY SecurityDataDirectory;
} PE_COFF_LOADER_IMAGE_CONTEXT;
/**
diff --git a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
index 6dae14352f8..99ee6e2a71d 100644
--- a/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
+++ b/MdePkg/Library/BasePeCoffLib/BasePeCoff.c
@@ -75,6 +75,15 @@ PeCoffLoaderGetPeHeader (
UINTN NumberOfSections;
EFI_IMAGE_SECTION_HEADER SectionHeader;
+ //
+ // Zero the security data directory. It is populated below only if the image
+ // declares a non-empty EFI_IMAGE_DIRECTORY_ENTRY_SECURITY entry that passes
+ // bounds checking. Callers are not required to zero-initialize the image
+ // context, so this field must always be explicitly initialized here.
+ //
+ ImageContext->SecurityDataDirectory.VirtualAddress = 0;
+ ImageContext->SecurityDataDirectory.Size = 0;
+
//
// Read the DOS image header to check for its existence
//
@@ -303,6 +312,13 @@ PeCoffLoaderGetPeHeader (
return Status;
}
+
+ //
+ // The security data directory has been validated to lie within the
+ // image bounds. Record a copy of it in the image context so callers
+ // can locate the certificate data without re-parsing the headers.
+ //
+ ImageContext->SecurityDataDirectory = Hdr.Pe32->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
}
@@ -425,6 +441,13 @@ PeCoffLoaderGetPeHeader (
return Status;
}
+
+ //
+ // The security data directory has been validated to lie within the
+ // image bounds. Record a copy of it in the image context so callers
+ // can locate the certificate data without re-parsing the headers.
+ //
+ ImageContext->SecurityDataDirectory = Hdr.Pe32Plus->OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
}
}
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/Database.c b/SecurityPkg/Library/DxeImageVerificationLib2/Database.c
new file mode 100644
index 00000000000..6ead8556e93
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/Database.c
@@ -0,0 +1,956 @@
+/** @file
+ Secureboot DB/DBX/DBT (EFI_SIGNATURE_LIST) helpers, including signed-image
+ (certificate / Authenticode) validation, for the DXE Image Verification Library.
+
+ Caution: This file consumes external input (the PE/COFF image and the
+ Secure Boot signature databases). All inputs must be treated as
+ attacker-controlled.
+
+ Copyright (C) Microsoft Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include "Database.h"
+
+/**
+ Load a Secure Boot Signature Database into a pool-allocated buffer.
+
+ The returned buffer is allocated using AllocatePool(). The caller is responsible for freeing
+ this buffer with FreePool().
+
+ @param[in] DatabaseName Variable name (e.g. EFI_IMAGE_SECURITY_DATABASE,
+ EFI_IMAGE_SECURITY_DATABASE1).
+ @param[out] Buffer Pool-allocated copy of the variable contents,
+ or NULL if the variable does not exist.
+ Caller is responsible for freeing this buffer with
+ FreePool when non-NULL.
+ @param[out] BufferSize BufferSize of *Buffer in bytes, or 0 if the
+ variable does not exist.
+
+ @retval EFI_SUCCESS The variable was loaded successfully, or it was absent.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL.
+ @retval Other Status from gRT->GetVariable.
+**/
+EFI_STATUS
+LoadSignatureDatabase (
+ IN CONST CHAR16 *DatabaseName,
+ OUT VOID **Buffer,
+ OUT UINTN *BufferSize
+ )
+{
+ EFI_STATUS Status;
+
+ if ((DatabaseName == NULL) || (Buffer == NULL) || (BufferSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Buffer = NULL;
+ *BufferSize = 0;
+
+ Status = GetVariable2 (DatabaseName, &gEfiImageSecurityDatabaseGuid, Buffer, BufferSize);
+ if (Status == EFI_NOT_FOUND) {
+ return EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ Load the platform's db and dbx signature databases.
+
+ The Db / Dbx buffers in the returned structure are allocated using AllocatePool(). The caller is
+ responsible for freeing them with FreePool().
+
+ @param[out] Databases On success, receives pool-allocated copies of the `db` and `dbx`
+ variable contents. Either Db or Dbx may be NULL (with a 0 size) if the
+ corresponding variable is absent.
+
+ @retval EFI_SUCCESS Databases loaded. Db / Dbx may still be NULL if the
+ corresponding variable was absent.
+ @retval EFI_INVALID_PARAMETER Databases is NULL.
+ @retval Other Failure status from gRT->GetVariable.
+**/
+EFI_STATUS
+LoadSignatureDatabases (
+ OUT SIGNATURE_DATABASES *Databases
+ )
+{
+ EFI_STATUS Status;
+
+ if (Databases == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Databases->Db = NULL;
+ Databases->DbSize = 0;
+ Databases->Dbx = NULL;
+ Databases->DbxSize = 0;
+
+ Status = LoadSignatureDatabase (EFI_IMAGE_SECURITY_DATABASE, &Databases->Db, &Databases->DbSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: failed to load db - %r\n", Status));
+ goto Error;
+ }
+
+ Status = LoadSignatureDatabase (EFI_IMAGE_SECURITY_DATABASE1, &Databases->Dbx, &Databases->DbxSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: failed to load dbx - %r\n", Status));
+ goto Error;
+ }
+
+ return EFI_SUCCESS;
+
+Error:
+ if (Databases->Db != NULL) {
+ FreePool (Databases->Db);
+ Databases->Db = NULL;
+ Databases->DbSize = 0;
+ }
+
+ if (Databases->Dbx != NULL) {
+ FreePool (Databases->Dbx);
+ Databases->Dbx = NULL;
+ Databases->DbxSize = 0;
+ }
+
+ return Status;
+}
+
+/**
+ Search the database for a digest authority that matches the image.
+
+ A match is defined as a hash in the database that matches the hash of the image being searched.
+ The hash algorithm used is determined by the SignatureType of each EFI_SIGNATURE_LIST in the
+ database.
+
+ @param[in] Database The raw database contents.
+ @param[in] DatabaseSize The size of the Database in bytes.
+ @param[in, out] Cache DIGEST_CACHE pointer bound to the image being searched. The cache
+ may be updated during the search.
+ @param[out] Authority Only valid on EFI_SUCCESS; reflects if a matching entry was found.
+ NULL if no match; otherwise, it contains the matching entry.
+
+ @retval EFI_SUCCESS Search completed; Authority reflects if a matching entry was found.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL, or Cache is not bound to an image.
+ @retval EFI_VOLUME_CORRUPTED Database is structurally malformed.
+ @retval other Propagated from GetHash.
+**/
+EFI_STATUS
+GetImageDigestAuthority (
+ IN CONST VOID *Database,
+ IN UINTN DatabaseSize,
+ IN OUT DIGEST_CACHE *Cache,
+ OUT IMAGE_AUTHORITY *Authority
+ )
+{
+ EFI_STATUS Status;
+ SIG_DATABASE_ITER DbIter;
+ SIG_LIST_ITER ListIter;
+ CONST EFI_SIGNATURE_LIST *List;
+ CONST EFI_SIGNATURE_DATA *Entry;
+ CONST UINT8 *Digest;
+ UINTN DigestSize;
+
+ if ((Cache == NULL) || (Authority == NULL) ||
+ (Cache->Buffer == NULL) || (Cache->BufferSize == 0) ||
+ (Cache->Type != DigestCacheTypeImage))
+ {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Authority->Data = NULL;
+ Authority->Size = 0;
+ ZeroMem (&Authority->SignatureType, sizeof (EFI_GUID));
+
+ if ((DatabaseSize == 0) || (Database == NULL)) {
+ return EFI_SUCCESS;
+ }
+
+ Status = DatabaseIterInit (&DbIter, Database, DatabaseSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Iterate over each EFI_SIGNATURE_LIST in the database.
+ //
+ while ((List = DatabaseIterNext (&DbIter)) != NULL) {
+ Status = GetHash (
+ &List->SignatureType,
+ Cache,
+ &Digest,
+ &DigestSize
+ );
+
+ //
+ // Unsupported hash type; skip this list.
+ //
+ if (Status == EFI_UNSUPPORTED) {
+ continue;
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: failed to get image hash - %r\n", Status));
+ return Status;
+ }
+
+ if (EFI_ERROR (SigListIterInit (&ListIter, List))) {
+ continue;
+ }
+
+ //
+ // Iterate over each Entry in the current EFI_SIGNATURE_LIST.
+ //
+ while ((Entry = SigListIterNext (&ListIter)) != NULL) {
+ if (CompareMem (Entry->SignatureData, Digest, DigestSize) == 0) {
+ Authority->Data = Entry;
+ Authority->Size = List->SignatureSize;
+ CopyGuid (&Authority->SignatureType, &List->SignatureType);
+ return EFI_SUCCESS;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Determine whether a TBS Certificate hash is present in the `dbx`.
+
+ Iterates over the EFI_SIGNATURE_LISTs in the `dbx` database and checks if any of them contain
+ the hash of the TBS Certificate. Any failure to iterate will assume the certificate is in the DBX.
+
+ @param[in] Cert DER-encoded X.509 certificate.
+ @param[in] CertSize BufferSize of Cert in bytes.
+ @param[in] Dbx Raw dbx contents, or NULL.
+ @param[in] DbxSize BufferSize of Dbx in bytes; 0 when Dbx is NULL.
+
+ @retval TRUE The certificate hash was located in dbx, or an error
+ prevented a definitive answer.
+ @retval FALSE The certificate hash is not present in dbx.
+**/
+BOOLEAN
+IsTBSCertHashInDbx (
+ IN CONST UINT8 *Cert,
+ IN UINTN CertSize,
+ IN CONST VOID *Dbx,
+ IN UINTN DbxSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN DigestSize;
+ DIGEST_CACHE HashCache;
+ SIG_DATABASE_ITER Iter;
+ SIG_LIST_ITER ListIter;
+ CONST EFI_SIGNATURE_LIST *List;
+ CONST EFI_SIGNATURE_DATA *Entry;
+ CONST UINT8 *CertDigest;
+
+ //
+ // There is no DBX, so it's definitely not in the DBX.
+ //
+ if ((Dbx == NULL) || (DbxSize == 0)) {
+ return FALSE;
+ }
+
+ if (EFI_ERROR (DatabaseIterInit (&Iter, Dbx, DbxSize))) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: dbx is malformed; treating cert as revoked.\n"));
+ return TRUE;
+ }
+
+ ZeroMem (&HashCache, sizeof (HashCache));
+ HashCache.Type = DigestCacheTypeX509;
+ HashCache.Buffer = Cert;
+ HashCache.BufferSize = CertSize;
+
+ while ((List = DatabaseIterNext (&Iter)) != NULL) {
+ Status = GetHash (&List->SignatureType, &HashCache, &CertDigest, &DigestSize);
+
+ //
+ // This EFI_SIGNATURE_LIST in the DBX is not applicable to X509 certicicates.
+ //
+ if (Status == EFI_UNSUPPORTED) {
+ continue;
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: failed to compute X509 hash (%r).\n", Status));
+ return TRUE;
+ }
+
+ if (List->SignatureSize < sizeof (EFI_GUID) + DigestSize) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: malformed dbx signature list size.\n"));
+ return TRUE;
+ }
+
+ if (EFI_ERROR (SigListIterInit (&ListIter, List))) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: malformed dbx signature list.\n"));
+ return TRUE;
+ }
+
+ while ((Entry = SigListIterNext (&ListIter)) != NULL) {
+ if (CompareMem (Entry->SignatureData, CertDigest, DigestSize) == 0) {
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Extract the DER-encoded PKCS#7 SignedData payload from a single WIN_CERTIFICATE entry.
+
+ @param[in] Cert The certificate to inspect.
+ @param[out] AuthData On success, set to point at the PKCS#7 payload inside Cert.
+ @param[out] AuthDataSize On success, set to the PKCS#7 payload length in bytes.
+
+ @retval EFI_SUCCESS AuthData/AuthDataSize were populated.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL.
+ @retval EFI_UNSUPPORTED Unsupported WIN_CERTIFICATE type.
+ @retval EFI_VOLUME_CORRUPTED dwLength is too small to contain the required header for the
+ declared type.
+**/
+EFI_STATUS
+GetWinCertificatePkcs7AuthData (
+ IN CONST WIN_CERTIFICATE *Cert,
+ OUT CONST UINT8 **AuthData,
+ OUT UINTN *AuthDataSize
+ )
+{
+ CONST WIN_CERTIFICATE_UEFI_GUID *UefiGuidCert;
+
+ if ((Cert == NULL) || (AuthData == NULL) || (AuthDataSize == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (Cert->wCertificateType) {
+ case WIN_CERT_TYPE_PKCS_SIGNED_DATA:
+ //
+ // The certificate is a bare DER-encoded PKCS#7 SignedData prefixed
+ // by the WIN_CERTIFICATE header.
+ //
+ if (Cert->dwLength <= sizeof (WIN_CERTIFICATE)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ *AuthData = (CONST UINT8 *)Cert + sizeof (WIN_CERTIFICATE);
+ *AuthDataSize = Cert->dwLength - sizeof (WIN_CERTIFICATE);
+ return EFI_SUCCESS;
+
+ case WIN_CERT_TYPE_EFI_GUID:
+ //
+ // The certificate is a WIN_CERTIFICATE_UEFI_GUID; the embedded
+ // payload format is identified by CertType. Only the PKCS#7
+ // SignedData GUID is supported.
+ //
+ if (Cert->dwLength <= OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData)) {
+ return EFI_VOLUME_CORRUPTED;
+ }
+
+ UefiGuidCert = (CONST WIN_CERTIFICATE_UEFI_GUID *)Cert;
+ if (!CompareGuid (&UefiGuidCert->CertType, &gEfiCertPkcs7Guid)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ *AuthData = UefiGuidCert->CertData;
+ *AuthDataSize = Cert->dwLength - OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
+ return EFI_SUCCESS;
+
+ default:
+ return EFI_UNSUPPORTED;
+ }
+}
+
+/**
+ Determine whether a PKCS#7 signature is authorized by an `EFI_CERT_X509_GUID`
+ `EFI_SIGNATURE_LIST`.
+
+ Iterates over the X.509 trust anchors carried in `List`, asks `AuthenticodeVerify` whether any
+ one of them verifies the signature, and rejects an otherwise verifying anchor whose TBS hash is
+ enrolled in `dbx`.
+
+ The caller is responsible for confirming `List->SignatureType == gEfiCertX509Guid` before
+ invocation.
+
+ @param[in] List Candidate list of X.509 certificates (an EFI_SIGNATURE_LIST).
+ @param[in] AuthData DER-encoded PKCS#7 SignedData.
+ @param[in] AuthDataSize BufferSize of AuthData in bytes.
+ @param[in] ImageHash Authenticode digest of the image.
+ @param[in] ImageHashSize BufferSize of ImageHash in bytes.
+ @param[in] Dbx Raw dbx contents, or NULL.
+ @param[in] DbxSize BufferSize of Dbx in bytes; 0 when Dbx is NULL.
+ @param[out] Authority On a match, Authority->Data is the verifying EFI_SIGNATURE_DATA entry
+ and Authority->Size is List->SignatureSize.
+
+ @retval TRUE At least one entry verifies AuthData and is not revoked.
+ @retval FALSE No entry verifies AuthData (or all that do are revoked).
+**/
+STATIC
+BOOLEAN
+IsPkcs7AuthDataAuthorizedByX509List (
+ IN CONST EFI_SIGNATURE_LIST *List,
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN CONST UINT8 *ImageHash,
+ IN UINTN ImageHashSize,
+ IN CONST VOID *Dbx,
+ IN UINTN DbxSize,
+ OUT IMAGE_AUTHORITY *Authority
+ )
+{
+ SIG_LIST_ITER Iter;
+ CONST EFI_SIGNATURE_DATA *Entry;
+ CONST UINT8 *TrustedCert;
+ UINTN TrustedCertSize;
+ UINT8 *TBSCert;
+ UINTN TBSCertSize;
+
+ //
+ // If the signature size is less than or equal to an EFI_GUID there
+ // is no cert payload to inspect.
+ //
+ if (List->SignatureSize <= sizeof (EFI_GUID)) {
+ return FALSE;
+ }
+
+ if (EFI_ERROR (SigListIterInit (&Iter, List))) {
+ return FALSE;
+ }
+
+ TrustedCertSize = List->SignatureSize - sizeof (EFI_GUID);
+
+ while ((Entry = SigListIterNext (&Iter)) != NULL) {
+ TrustedCert = Entry->SignatureData;
+
+ if (!AuthenticodeVerify (
+ AuthData,
+ AuthDataSize,
+ TrustedCert,
+ TrustedCertSize,
+ ImageHash,
+ ImageHashSize
+ ))
+ {
+ continue;
+ }
+
+ if (!X509GetTBSCert (TrustedCert, TrustedCertSize, &TBSCert, &TBSCertSize)) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: X509GetTBSCert failed; treating cert as revoked.\n"));
+ return FALSE;
+ }
+
+ //
+ // The Authenticode signature is a valid trust anchor; make sure its not revoked
+ //
+ if (IsTBSCertHashInDbx (TrustedCert, TrustedCertSize, Dbx, DbxSize)) {
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: signing cert hash present in dbx; rejected.\n"));
+ continue;
+ }
+
+ Authority->Data = Entry;
+ Authority->Size = List->SignatureSize;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ Determine whether a PKCS#7 signature is authorized by an `EFI_CERT_X509__GUID`
+ `EFI_SIGNATURE_LIST`.
+
+ Each entry in `List` is a precomputed TBS-cert hash. For each entry, attempt to locate a signer
+ in `AuthData` whose TBS hash (under the algorithm identified by `List->SignatureType`) matches
+ the entry, then confirm the matched TBS hash is not enrolled in `dbx`.
+
+ The caller is responsible for confirming `List->SignatureType` is one of the
+ `EFI_CERT_X509_SHA{256,384,512}_GUID` values (see `IsX509CertHashGuid`) before invocation.
+
+ @param[in] List Candidate list of TBS-cert hashes (an EFI_SIGNATURE_LIST).
+ @param[in] AuthData DER-encoded PKCS#7 SignedData.
+ @param[in] AuthDataSize BufferSize of AuthData in bytes.
+ @param[in] ImageHash Authenticode digest of the image.
+ @param[in] ImageHashSize BufferSize of ImageHash in bytes.
+ @param[in] Dbx Raw dbx contents, or NULL.
+ @param[in] DbxSize BufferSize of Dbx in bytes; 0 when Dbx is NULL.
+ @param[out] Authority On a match, Authority->Data is the matching EFI_SIGNATURE_DATA entry
+ and Authority->Size is List->SignatureSize.
+ @param[in,out] TrustAnchorCacheHandle Caller-owned cache handle pointer passed through to
+ GetTrustAnchorX509FromAuthData for reuse across list entries.
+
+ @retval TRUE At least one entry matches a non-revoked signer TBS hash.
+ @retval FALSE No entry matches, all matches are revoked, or the list could not be parsed.
+**/
+STATIC
+BOOLEAN
+IsPkcs7AuthDataAuthorizedByX509HashList (
+ IN CONST EFI_SIGNATURE_LIST *List,
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN CONST UINT8 *ImageHash,
+ IN UINTN ImageHashSize,
+ IN CONST VOID *Dbx,
+ IN UINTN DbxSize,
+ OUT IMAGE_AUTHORITY *Authority,
+ IN OUT VOID **TrustAnchorCacheHandle
+ )
+{
+ EFI_STATUS Status;
+ SIG_LIST_ITER Iter;
+ CONST EFI_SIGNATURE_DATA *Entry;
+ UINTN TbsCertHashSize;
+ UINT8 *TrustAnchorX509;
+ UINTN TrustAnchorX509Size;
+ BOOLEAN Authorized;
+
+ (VOID)ImageHash;
+ (VOID)ImageHashSize;
+
+ if ((List == NULL) || (AuthData == NULL) || (AuthDataSize == 0) ||
+ (Authority == NULL) || (TrustAnchorCacheHandle == NULL))
+ {
+ return FALSE;
+ }
+
+ if (!IsX509CertHashGuid (&List->SignatureType)) {
+ return FALSE;
+ }
+
+ if (List->SignatureSize <= sizeof (EFI_GUID)) {
+ return FALSE;
+ }
+
+ if (EFI_ERROR (SigListIterInit (&Iter, List))) {
+ return FALSE;
+ }
+
+ TbsCertHashSize = List->SignatureSize - sizeof (EFI_GUID);
+ TrustAnchorX509 = NULL;
+ Authorized = FALSE;
+
+ while ((Entry = SigListIterNext (&Iter)) != NULL) {
+ TrustAnchorX509Size = 0;
+
+ Status = GetTrustAnchorX509FromAuthData (
+ TrustAnchorCacheHandle,
+ Entry->SignatureData,
+ TbsCertHashSize,
+ AuthData,
+ AuthDataSize,
+ &TrustAnchorX509,
+ &TrustAnchorX509Size
+ );
+
+ if (Status == EFI_NOT_FOUND) {
+ continue;
+ }
+
+ if (EFI_ERROR (Status)) {
+ goto Done;
+ }
+
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Trust Anchor found, checking TBS cert hash in dbx.\n"));
+ if (IsTBSCertHashInDbx (TrustAnchorX509, TrustAnchorX509Size, Dbx, DbxSize)) {
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: Trust Anchor TBS cert hash found in dbx, skipping.\n"));
+ FreePool (TrustAnchorX509);
+ TrustAnchorX509 = NULL;
+ continue;
+ }
+
+ Authority->Data = Entry;
+ Authority->Size = List->SignatureSize;
+ Authorized = TRUE;
+ goto Done;
+ }
+
+Done:
+ if (TrustAnchorX509 != NULL) {
+ FreePool (TrustAnchorX509);
+ }
+
+ return Authorized;
+}
+
+/**
+ Determine whether the auth data (PKCS#7 SignedData) is revoked by the `dbx`.
+
+ Runs two checks against the auth data (PKCS#7 SignedData):
+ 1. If a X.509 trust anchor enrolled in the `dbx` authenticates the signature, the certificate is
+ revoked.
+ 2. if a signer in the signing chain (as reported by `Pkcs7GetSigners`) has a TBS hash that is
+ enrolled in the `dbx`, the certificate is revoked.
+
+ @param[in] AuthData DER-encoded PKCS#7 SignedData payload.
+ @param[in] AuthDataSize Size of AuthData in bytes; must be non-zero.
+ @param[in] ImageHash Authenticode digest of the image under the algorithm
+ AuthData uses.
+ @param[in] ImageHashSize Size of ImageHash in bytes; must be non-zero.
+ @param[in] Dbx Raw `dbx` contents, or NULL.
+ @param[in] DbxSize Size of Dbx in bytes; 0 when Dbx is NULL.
+
+ @retval TRUE The signature is revoked by `dbx`, or a missing/empty `AuthData`
+ or `ImageHash` prevented a safe determination (fail closed).
+ @retval FALSE The signature is not revoked by `dbx`, including when `dbx` is
+ absent or empty.
+**/
+BOOLEAN
+IsCertRevoked (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN CONST UINT8 *ImageHash,
+ IN UINTN ImageHashSize,
+ IN CONST VOID *Dbx,
+ IN UINTN DbxSize
+ )
+{
+ SIG_DATABASE_ITER DbIter;
+ CONST EFI_SIGNATURE_LIST *List;
+ SIG_LIST_ITER ListIter;
+ CONST EFI_SIGNATURE_DATA *Entry;
+ UINTN TrustedCertSize;
+ UINT8 *CertStack;
+ UINTN CertStackSize;
+ UINT8 *TrustedCert;
+ UINTN TrustedCertOutSize;
+ UINT8 CertNumber;
+ CONST UINT8 *Walker;
+ CONST UINT8 *StackEnd;
+ UINTN Index;
+ UINT32 CertLen;
+ UINT8 *TBSCert;
+ UINTN TBSCertSize;
+ BOOLEAN Revoked;
+
+ if ((AuthData == NULL) || (AuthDataSize == 0) ||
+ (ImageHash == NULL) || (ImageHashSize == 0))
+ {
+ return TRUE;
+ }
+
+ if ((Dbx == NULL) || (DbxSize == 0)) {
+ return FALSE;
+ }
+
+ //
+ // Step 1: walk dbx and try AuthenticodeVerify against each X.509 trust anchor. Failure to walk
+ // the `dbx` is fail-closed since we cannot make a safe determination.
+ //
+ if (EFI_ERROR (DatabaseIterInit (&DbIter, Dbx, DbxSize))) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: `dbx` malformed.\n"));
+ return TRUE;
+ }
+
+ while ((List = DatabaseIterNext (&DbIter)) != NULL) {
+ if (!CompareGuid (&List->SignatureType, &gEfiCertX509Guid)) {
+ continue;
+ }
+
+ if (List->SignatureSize <= sizeof (EFI_GUID)) {
+ continue;
+ }
+
+ if (EFI_ERROR (SigListIterInit (&ListIter, List))) {
+ continue;
+ }
+
+ TrustedCertSize = List->SignatureSize - sizeof (EFI_GUID);
+
+ while ((Entry = SigListIterNext (&ListIter)) != NULL) {
+ if (AuthenticodeVerify (
+ AuthData,
+ AuthDataSize,
+ Entry->SignatureData,
+ TrustedCertSize,
+ ImageHash,
+ ImageHashSize
+ ))
+ {
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: image verifies against `dbx` X.509 entry.\n"));
+ return TRUE;
+ }
+ }
+ }
+
+ //
+ // Step 2: walk the PKCS#7 signer chain. If any signer's TBS hash is in the `dbx`, the certificate
+ // is revoked.
+ //
+ CertStack = NULL;
+ CertStackSize = 0;
+ TrustedCert = NULL;
+ TrustedCertOutSize = 0;
+
+ if (!Pkcs7GetSigners (
+ AuthData,
+ AuthDataSize,
+ &CertStack,
+ &CertStackSize,
+ &TrustedCert,
+ &TrustedCertOutSize
+ ))
+ {
+ return FALSE;
+ }
+
+ Revoked = FALSE;
+
+ if ((CertStack != NULL) && (CertStackSize > 0)) {
+ StackEnd = CertStack + CertStackSize;
+ CertNumber = *CertStack;
+ Walker = CertStack + 1;
+
+ //
+ // Fail-closed for malformed signer stacks.
+ //
+ for (Index = 0; Index < CertNumber; Index++) {
+ if ((UINTN)(StackEnd - Walker) < sizeof (UINT32)) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: IsCertRevoked: malformed signer stack length prefix.\n"));
+ Revoked = TRUE;
+ break;
+ }
+
+ CertLen = ReadUnaligned32 ((CONST UINT32 *)Walker);
+ Walker += sizeof (UINT32);
+
+ if ((CertLen == 0) || ((UINTN)(StackEnd - Walker) < CertLen)) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: IsCertRevoked: malformed signer cert payload.\n"));
+ Revoked = TRUE;
+ break;
+ }
+
+ if (!X509GetTBSCert (Walker, CertLen, &TBSCert, &TBSCertSize)) {
+ DEBUG ((DEBUG_WARN, "DxeImageVerificationLib: IsCertRevoked: Failed to get TBS certificate.\n"));
+ Revoked = TRUE;
+ break;
+ }
+
+ if (IsTBSCertHashInDbx (Walker, CertLen, Dbx, DbxSize)) {
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: IsCertRevoked: signer TBS hash present in dbx.\n"));
+ Revoked = TRUE;
+ break;
+ }
+
+ Walker += CertLen;
+ }
+ }
+
+ if (CertStack != NULL) {
+ Pkcs7FreeSigners (CertStack);
+ }
+
+ if (TrustedCert != NULL) {
+ Pkcs7FreeSigners (TrustedCert);
+ }
+
+ return Revoked;
+}
+
+/**
+ Determine whether the auth data (PKCS#7 SignedData) is allowed by the `db`.
+
+ Iterates over the the EFI_SIGNATURE_LISTs in the `db` and validates the auth data one of two ways
+ depending on the associated GUID of the list:
+
+ 1. `EFI_CERT_X509_GUID`: Checks each X.509 trust anchor in the list to see if it verifies the
+ auth data with `AuthenticodeVerify`. If it does verify, The TBS certificate is extracted, hashed
+ and checked against the `dbx`.
+ 2. EFI_CERT_X509__GUID: Uses each TBS cert hash in the list to attempt to extract
+ the TBS certificate from the auth data. If found, the TBS certificate is hashed and checked
+ against the `dbx`.
+
+ @param[in] AuthData DER-encoded PKCS#7 SignedData payload.
+ @param[in] AuthDataSize Size of AuthData in bytes; must be non-zero.
+ @param[in] ImageHash Authenticode digest of the image under the algorithm
+ AuthData uses.
+ @param[in] ImageHashSize Size of ImageHash in bytes; must be non-zero.
+ @param[in] Databases The `db` / `dbx` signature databases to evaluate against.
+ @param[out] Authority On authorization, Authority->Data is the EFI_SIGNATURE_DATA trust
+ anchor in `db` that authorized the image and Authority->Size is its
+ SignatureSize. On no authorization, Authority->Data is NULL and
+ Authority->Size is 0.
+
+ @retval TRUE The signature authorizes the image.
+ @retval FALSE The signature does not authorize the image, a required pointer was NULL, or an
+ error prevented a definitive answer.
+**/
+BOOLEAN
+IsCertAuthorized (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN CONST UINT8 *ImageHash,
+ IN UINTN ImageHashSize,
+ IN CONST SIGNATURE_DATABASES *Databases,
+ OUT IMAGE_AUTHORITY *Authority
+ )
+{
+ SIG_DATABASE_ITER Iter;
+ CONST EFI_SIGNATURE_LIST *List;
+ VOID *TrustAnchorCacheHandle;
+ BOOLEAN Authorized;
+
+ if ((AuthData == NULL) || (AuthDataSize == 0) ||
+ (ImageHash == NULL) || (ImageHashSize == 0) ||
+ (Databases == NULL) || (Authority == NULL))
+ {
+ return FALSE;
+ }
+
+ Authority->Data = NULL;
+ Authority->Size = 0;
+ TrustAnchorCacheHandle = NULL;
+ Authorized = FALSE;
+
+ if (EFI_ERROR (DatabaseIterInit (&Iter, Databases->Db, Databases->DbSize))) {
+ return FALSE;
+ }
+
+ //
+ // Iterate over each signature list in the database, dispatching to the helper
+ // that matches the list's signature-type GUID.
+ //
+ while ((List = DatabaseIterNext (&Iter)) != NULL) {
+ if (CompareGuid (&List->SignatureType, &gEfiCertX509Guid) &&
+ IsPkcs7AuthDataAuthorizedByX509List (
+ List,
+ AuthData,
+ AuthDataSize,
+ ImageHash,
+ ImageHashSize,
+ Databases->Dbx,
+ Databases->DbxSize,
+ Authority
+ ))
+ {
+ Authorized = TRUE;
+ break;
+ }
+
+ if (IsX509CertHashGuid (&List->SignatureType) &&
+ IsPkcs7AuthDataAuthorizedByX509HashList (
+ List,
+ AuthData,
+ AuthDataSize,
+ ImageHash,
+ ImageHashSize,
+ Databases->Dbx,
+ Databases->DbxSize,
+ Authority,
+ &TrustAnchorCacheHandle
+ ))
+ {
+ Authorized = TRUE;
+ break;
+ }
+ }
+
+ if (TrustAnchorCacheHandle != NULL) {
+ FreeTrustAnchorX509Cache (TrustAnchorCacheHandle);
+ }
+
+ return Authorized;
+}
+
+/**
+ Search the `db` for a trust anchor that authorizes the image via a single WIN_CERTIFICATE.
+
+ A match is defined as a X.509 certificate in the `db` which:
+ 1. Verifies the auth data found in the WIN_CERTIFICATE against the hashed image digest.
+ 2. Whose TBS (To Be Signed) hash is not present in the `dbx`.
+
+ @param[in] Cert The certificate to evaluate.
+ @param[in,out] Cache Image digest cache bound to the image buffer; the cache may
+ memoize one digest per algorithm across calls.
+ @param[in] Databases The `db` / `dbx` signature databases to evaluate against.
+ @param[out] Authority Authority->SignatureType is the authenticode hash algorithm. On
+ EFI_SUCCESS, Authority->Data references the EFI_SIGNATURE_DATA
+ trust anchor in the `db` that authorized the image.
+
+ @retval EFI_SUCCESS The certificate authorizes the image; inspect Authority.
+ @retval EFI_NOT_FOUND The certificate is valid but no `db` trust anchor authorizes it.
+ @retval EFI_ACCESS_DENIED The certificate is revoked by `dbx`, or a prelude failure
+ (PKCS#7 extraction, hash-algorithm lookup, or image-hash
+ computation) prevented evaluation.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL.
+**/
+EFI_STATUS
+GetImageCertAuthority (
+ IN CONST WIN_CERTIFICATE *Cert,
+ IN OUT DIGEST_CACHE *Cache,
+ IN CONST SIGNATURE_DATABASES *Databases,
+ OUT IMAGE_AUTHORITY *Authority
+ )
+{
+ EFI_STATUS Status;
+ CONST UINT8 *AuthData;
+ UINTN AuthDataSize;
+ EFI_GUID HashType;
+ CONST UINT8 *ImageHash;
+ UINTN ImageHashSize;
+
+ if ((Cert == NULL) || (Cache == NULL) || (Databases == NULL) || (Authority == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Authority->Data = NULL;
+ Authority->Size = 0;
+ ZeroMem (&Authority->SignatureType, sizeof (EFI_GUID));
+
+ Status = GetWinCertificatePkcs7AuthData (Cert, &AuthData, &AuthDataSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_WARN,
+ "DxeImageVerificationLib: Unable to extract PKCS#7 authentication data from WIN_CERTIFICATE (type=0x%04x, status=%r).\n",
+ Cert->wCertificateType,
+ Status
+ ));
+ return EFI_ACCESS_DENIED;
+ }
+
+ Status = GetAuthenticodeHashAlgorithm (AuthData, AuthDataSize, &HashType);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_WARN,
+ "DxeImageVerificationLib: Unable to determine the hash algorithm from the PKCS#7 authentication data (%r).\n",
+ Status
+ ));
+ return EFI_ACCESS_DENIED;
+ }
+
+ Status = GetHash (&HashType, Cache, &ImageHash, &ImageHashSize);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((
+ DEBUG_WARN,
+ "DxeImageVerificationLib: Unable to compute the image hash (type=%g, status=%r).\n",
+ &HashType,
+ Status
+ ));
+ return EFI_ACCESS_DENIED;
+ }
+
+ CopyGuid (&Authority->SignatureType, &HashType);
+
+ if (IsCertRevoked (AuthData, AuthDataSize, ImageHash, ImageHashSize, Databases->Dbx, Databases->DbxSize)) {
+ DEBUG ((DEBUG_INFO, "DxeImageVerificationLib: certificate revoked by dbx.\n"));
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // IsCertAuthorized populates Authority->Data only when it authorizes the image; on a
+ // non-authorizing result it leaves Data NULL while preserving SignatureType set above.
+ //
+ if (!IsCertAuthorized (
+ AuthData,
+ AuthDataSize,
+ ImageHash,
+ ImageHashSize,
+ Databases,
+ Authority
+ ))
+ {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/Database.h b/SecurityPkg/Library/DxeImageVerificationLib2/Database.h
new file mode 100644
index 00000000000..2895a7d95e6
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/Database.h
@@ -0,0 +1,130 @@
+/** @file
+ Secureboot DB/DBX/DBT (EFI_SIGNATURE_LIST) helpers, including signed-image
+ (certificate / Authenticode) validation, for the DXE Image Verification Library.
+
+ Copyright (C) Microsoft Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef DXE_IMAGE_VERIFICATION_LIB_DATABASE_H_
+#define DXE_IMAGE_VERIFICATION_LIB_DATABASE_H_
+
+#include "DxeImageVerificationLib.h"
+#include "Iterator.h"
+#include "Support.h"
+#include
+#include
+
+//
+// The platform's Secure Boot signature databases (`db` and `dbx`). Db / Dbx
+// are pool-allocated copies owned by the caller; either may be NULL when the
+// corresponding variable is absent, in which case its size is 0.
+//
+typedef struct {
+ VOID *Db;
+ UINTN DbSize;
+ VOID *Dbx;
+ UINTN DbxSize;
+} SIGNATURE_DATABASES;
+
+/**
+ Load the platform's db and dbx signature databases.
+
+ The Db / Dbx buffers in the returned structure are allocated using AllocatePool(). The caller is
+ responsible for freeing them with FreePool().
+
+ @param[out] Databases On success, receives pool-allocated copies of the `db` and `dbx`
+ variable contents. Either Db or Dbx may be NULL (with a 0 size) if the
+ corresponding variable is absent.
+
+ @retval EFI_SUCCESS Databases loaded. Db / Dbx may still be NULL if the
+ corresponding variable was absent.
+ @retval EFI_INVALID_PARAMETER Databases is NULL.
+ @retval Other Failure status from gRT->GetVariable.
+**/
+EFI_STATUS
+LoadSignatureDatabases (
+ OUT SIGNATURE_DATABASES *Databases
+ );
+
+/**
+ Search the database for a digest authority that matches the image.
+
+ A match is defined as a hash in the database that matches the hash of the image being searched.
+ The hash algorithm used is determined by the SignatureType of each EFI_SIGNATURE_LIST in the
+ database.
+
+ @param[in] Database The raw database contents.
+ @param[in] DatabaseSize The size of the Database in bytes.
+ @param[in, out] Cache DIGEST_CACHE pointer bound to the image being searched. The cache
+ may be updated during the search.
+ @param[out] Authority Only valid on EFI_SUCCESS; reflects if a matching entry was found.
+ NULL if no match; otherwise, it contains the matching entry.
+
+ @retval EFI_SUCCESS Search completed; Authority reflects if a matching entry was found.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL, or Cache is not bound to an image.
+ @retval EFI_VOLUME_CORRUPTED Database is structurally malformed.
+ @retval other Propagated from GetHash.
+**/
+EFI_STATUS
+GetImageDigestAuthority (
+ IN CONST VOID *Database,
+ IN UINTN DatabaseSize,
+ IN OUT DIGEST_CACHE *Cache,
+ OUT IMAGE_AUTHORITY *Authority
+ );
+
+/**
+ Determine whether a TBS Certificate hash is present in the `dbx`.
+
+ Iterates over the EFI_SIGNATURE_LISTs in the `dbx` database and checks if any of them contain
+ the hash of the TBS Certificate.
+
+ @param[in] Cert DER-encoded X.509 certificate.
+ @param[in] CertSize BufferSize of Cert in bytes.
+ @param[in] Dbx Raw dbx contents, or NULL.
+ @param[in] DbxSize BufferSize of Dbx in bytes; 0 when Dbx is NULL.
+
+ @retval TRUE The certificate hash was located in dbx, or an error
+ prevented a definitive answer.
+ @retval FALSE The certificate hash is not present in dbx.
+**/
+BOOLEAN
+IsTBSCertHashInDbx (
+ IN CONST UINT8 *Cert,
+ IN UINTN CertSize,
+ IN CONST VOID *Dbx,
+ IN UINTN DbxSize
+ );
+
+/**
+ Search the `db` for a trust anchor that authorizes the image via a single WIN_CERTIFICATE.
+
+ A match is defined as a X.509 certificate in the `db` which:
+ 1. Verifies the auth data found in the WIN_CERTIFICATE against the hashed image digest.
+ 2. Whose TBS (To Be Signed) hash is not present in the `dbx`.
+
+ @param[in] Cert The certificate to evaluate.
+ @param[in,out] Cache Image digest cache bound to the image buffer; the cache may
+ memoize one digest per algorithm across calls.
+ @param[in] Databases The `db` / `dbx` signature databases to evaluate against.
+ @param[out] Authority Authority->SignatureType is the authenticode hash algorithm. On
+ EFI_SUCCESS, Authority->Data references the EFI_SIGNATURE_DATA
+ trust anchor in the `db` that authorized the image.
+
+ @retval EFI_SUCCESS The certificate authorizes the image; inspect Authority.
+ @retval EFI_NOT_FOUND The certificate is valid but no `db` trust anchor authorizes it.
+ @retval EFI_ACCESS_DENIED The certificate is revoked by `dbx`, or a prelude failure
+ (PKCS#7 extraction, hash-algorithm lookup, or image-hash
+ computation) prevented evaluation.
+ @retval EFI_INVALID_PARAMETER A required pointer is NULL.
+**/
+EFI_STATUS
+GetImageCertAuthority (
+ IN CONST WIN_CERTIFICATE *Cert,
+ IN OUT DIGEST_CACHE *Cache,
+ IN CONST SIGNATURE_DATABASES *Databases,
+ OUT IMAGE_AUTHORITY *Authority
+ );
+
+#endif // DXE_IMAGE_VERIFICATION_LIB_DATABASE_H_
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.c b/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.c
new file mode 100644
index 00000000000..81229db4f25
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.c
@@ -0,0 +1,335 @@
+/** @file
+ Implement image verification services for secure boot service
+
+ Caution: This file requires additional review when modified.
+ This library will have external input - PE/COFF image.
+ This external input must be validated carefully to avoid security issue like
+ buffer overflow, integer overflow.
+
+ DxeImageVerificationLibImageRead() function will make sure the PE/COFF image content
+ read is within the image buffer.
+
+ DxeImageVerificationHandler(), HashPeImageByType(), HashPeImage() function will accept
+ untrusted PE/COFF image and validate its data structure within this image buffer before use.
+
+Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP
+Copyright (c) Microsoft Corporation.
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "DxeImageVerificationLib.h"
+#include "Database.h"
+#include "Iterator.h"
+#include "Support.h"
+
+/**
+ Validate a PE/COFF image against the platform signature databases.
+
+ 1. Reject immediately if the image's Authenticode hash is enrolled in the `dbx`.
+ 2. Walk each WIN_CERTIFICATE in the image's security data directory to determine if the
+ Auth Data from it is not revoked by the `dbx` and is authorized by the `db`. Only one
+ WIN_CERTIFICATE needs to authorize the image for it to be validated.
+ 3. Authorize the image if the image's Authenticode hash is enrolled in the `db`.
+
+ When the image is rejected for any reason, an entry describing the rejection is appended to the
+ Image Execution Information Table. The recorded EFI_IMAGE_EXECUTION_ACTION is:
+ - EFI_IMAGE_EXECUTION_AUTH_UNTESTED Unsigned image rejected (digest in `dbx`, or digest
+ not in `db`).
+ - EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND Signed image rejected because its digest is in `dbx`.
+ - EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED Signed image rejected and at least one certificate was
+ revoked by `dbx` (or could not be evaluated).
+ - EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND Signed image rejected because no certificate is in `db`
+ and the digest is not in `db`.
+
+ @param[in] File Device path of the image being verified. Used to record rejections.
+ @param[in] FileBuffer Pointer to the in-memory PE/COFF image.
+ @param[in] FileSize Size of FileBuffer in bytes.
+ @param[in] SecDataDir Security data directory describing the embedded WIN_CERTIFICATE table.
+ A Size of 0 indicates an unsigned image.
+ @param[in,out] Measured Authority measurement state used to record the `db` entry that
+ authorized the image into PCR 7 (de-duplicated across images).
+
+ @retval EFI_SUCCESS The image is authorized.
+ @retval EFI_ACCESS_DENIED The image is revoked, not authorized, or the
+ databases could not be loaded.
+**/
+EFI_STATUS
+ValidateImage (
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
+ IN VOID *FileBuffer,
+ IN UINTN FileSize,
+ IN CONST EFI_IMAGE_DATA_DIRECTORY *SecDataDir,
+ IN OUT MEASURED_AUTHORITIES *Measured
+ )
+{
+ EFI_STATUS Status;
+ DIGEST_CACHE Cache;
+ SIGNATURE_DATABASES Databases;
+ WIN_CERT_ITER CertIter;
+ CONST WIN_CERTIFICATE *Cert;
+ IMAGE_AUTHORITY Authority;
+ EFI_IMAGE_EXECUTION_ACTION Action;
+ EFI_GUID RejectHashType;
+ CONST UINT8 *RejectDigest;
+ UINTN RejectDigestSize;
+
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_NOT_FOUND;
+ ZeroMem (&RejectHashType, sizeof (EFI_GUID));
+
+ //
+ // Setup digest cache for the image. This prevents redundant authenticode hash computations
+ // across the image-hash revocation check, per-cert authorization, and the image-hash fallback.
+ //
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Type = DigestCacheTypeImage;
+ Cache.Buffer = FileBuffer;
+ Cache.BufferSize = FileSize;
+
+ Status = LoadSignatureDatabases (&Databases);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Failed to load signature databases (%r).\n", Status));
+ goto Reject;
+ }
+
+ //
+ // Step 1: Reject the image if its Authenticode hash is found in the `dbx`. A failure to search
+ // the `dbx` rejects the image.
+ //
+ Status = GetImageDigestAuthority (Databases.Dbx, Databases.DbxSize, &Cache, &Authority);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Failed to search DBX for image hash (%r).\n", Status));
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
+ goto Reject;
+ }
+
+ if (Authority.Data != NULL) {
+ DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Image hash is forbidden by DBX.\n"));
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND;
+ CopyGuid (&RejectHashType, &Authority.SignatureType);
+ goto Reject;
+ }
+
+ //
+ // Step 2: For each WIN_CERTIFICATE in the image's security data directory, extract the auth data
+ // and check if it authorizes the image per the `db` and `dbx`. Exit on the first authorization.
+ //
+ // Note: If the image is unsigned, the iterator is empty and this step is a no-op.
+ //
+ Status = WinCertIterInit (&CertIter, FileBuffer, FileSize, SecDataDir);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Failed to walk security data directory (%r).\n", Status));
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
+ goto Reject;
+ }
+
+ while ((Cert = WinCertIterNext (&CertIter)) != NULL) {
+ Status = GetImageCertAuthority (Cert, &Cache, &Databases, &Authority);
+ if ((Status == EFI_SUCCESS) && (Authority.Data != NULL)) {
+ //
+ // Measure the `db` trust anchor that authorized the image into PCR 7.
+ //
+ SecureBootHook (
+ Measured,
+ EFI_IMAGE_SECURITY_DATABASE,
+ &gEfiImageSecurityDatabaseGuid,
+ &Authority
+ );
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ //
+ // This auth data is rejected by the `dbx`; update the rejection information so that we can
+ // properly record a rejection record if we end up rejecting the image.
+ //
+ if (Status == EFI_ACCESS_DENIED) {
+ CopyGuid (&RejectHashType, &Authority.SignatureType);
+ Action = EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED;
+ }
+ }
+
+ //
+ // Step 3: Authorize the image if the image authenticode hash is in the `db`.
+ //
+ Status = GetImageDigestAuthority (Databases.Db, Databases.DbSize, &Cache, &Authority);
+ if (!EFI_ERROR (Status) && (Authority.Data != NULL)) {
+ //
+ // Measure the `db` image-hash entry that authorized the image into PCR 7.
+ //
+ SecureBootHook (
+ Measured,
+ EFI_IMAGE_SECURITY_DATABASE,
+ &gEfiImageSecurityDatabaseGuid,
+ &Authority
+ );
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ DEBUG ((DEBUG_ERROR, "DxeImageVerificationLib: Image is not authorized by DB.\n"));
+
+Reject:
+ //
+ // Above logic assumed signed images. If the image is unsigned, it's rejection reason is always
+ // EFI_IMAGE_EXECUTION_AUTH_UNTESTED and the table record does not contain any signature
+ // information.
+ //
+ if (SecDataDir->Size == 0) {
+ Action = EFI_IMAGE_EXECUTION_AUTH_UNTESTED;
+ ZeroMem (&RejectHashType, sizeof (EFI_GUID));
+ }
+
+ //
+ // Recover the memoized image digest for the rejection signature when a hash
+ // algorithm was established (SIG_FOUND / SIG_FAILED). A failure simply records
+ // no signature.
+ //
+ RejectDigest = NULL;
+ RejectDigestSize = 0;
+ if (!IsZeroGuid (&RejectHashType)) {
+ GetHash (&RejectHashType, &Cache, &RejectDigest, &RejectDigestSize);
+ }
+
+ RecordRejectedImage (File, Action, &RejectHashType, RejectDigest, RejectDigestSize);
+ Status = EFI_ACCESS_DENIED;
+
+Exit:
+ if (Databases.Db != NULL) {
+ FreePool (Databases.Db);
+ }
+
+ if (Databases.Dbx != NULL) {
+ FreePool (Databases.Dbx);
+ }
+
+ return Status;
+}
+
+/**
+ Provide verification service for signed images, which include both signature validation
+ and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and
+ MSFT Authenticode type signatures are supported.
+
+ In this implementation, only verify external executables when in USER MODE.
+ Executables from FV is bypass, so pass in AuthenticationStatus is ignored.
+
+ The image verification policy is:
+ If the image is signed,
+ At least one valid signature or at least one hash value of the image must match a record
+ in the security database "db", and no valid signature nor any hash value of the image may
+ be reflected in the security database "dbx".
+ Otherwise, the image is not signed,
+ The hash value of the image must match a record in the security database "db", and
+ not be reflected in the security data base "dbx".
+
+ Caution: This function may receive untrusted input.
+ PE/COFF image is external input, so this function will validate its data structure
+ within this image buffer before use.
+
+ @param[in] AuthenticationStatus
+ This is the authentication status returned from the security
+ measurement services for the input file.
+ @param[in] File This is a pointer to the device path of the file that is
+ being dispatched. This will optionally be used for logging.
+ @param[in] FileBuffer File buffer matches the input file device path.
+ @param[in] FileSize Size of File buffer matches the input file device path.
+ @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.
+
+ @retval EFI_SUCCESS The file specified by DevicePath and non-NULL
+ FileBuffer did authenticate, and the platform policy dictates
+ that the DXE Foundation may use the file.
+ @retval EFI_SUCCESS The device path specified by NULL device path DevicePath
+ and non-NULL FileBuffer did authenticate, and the platform
+ policy dictates that the DXE Foundation may execute the image in
+ FileBuffer.
+ @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not
+ authenticate, and the DXE Foundation may not use File. The
+ image has been added to the file execution table.
+
+**/
+EFI_STATUS
+EFIAPI
+DxeImageVerificationHandler (
+ IN UINT32 AuthenticationStatus,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *File OPTIONAL,
+ IN VOID *FileBuffer,
+ IN UINTN FileSize,
+ IN BOOLEAN BootPolicy
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Policy;
+ EFI_IMAGE_DATA_DIRECTORY SecDataDir;
+
+ //
+ // Sanity check.
+ //
+ if (File == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Resolve the platform authorization policy from the image's origin.
+ // This runs before the Secure Boot variable check because it is much
+ // cheaper, and the common case (FV-dispatched drivers) short-circuits
+ // if the policy is ALWAYS_EXECUTE.
+ //
+ Status = GetExecutionPolicy (File, &Policy);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Policy unconditionally permits execution; no further checks needed.
+ //
+ if (Policy == ALWAYS_EXECUTE) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Secure Boot is the gate for all remaining checks. When it is not
+ // enabled, the platform has opted out of image authorization.
+ //
+ if (!IsSecureBootEnabled ()) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Inspect the image to locate its security data directory. Any failure
+ // to parse the PE/COFF headers is treated as a verification failure.
+ //
+ Status = GetImageSecurityDataDirectory (FileBuffer, FileSize, &SecDataDir);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Run image verification. The unified path handles both signed and
+ // unsigned images; SecDataDir->Size == 0 simply produces an empty
+ // WIN_CERTIFICATE iteration inside ValidateImage.
+ //
+ return ValidateImage (File, FileBuffer, FileSize, &SecDataDir, GetMeasuredAuthorities ());
+}
+
+/**
+ Register security measurement handler.
+
+ @param ImageHandle ImageHandle of the loaded driver.
+ @param SystemTable Pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The handlers were registered successfully.
+**/
+EFI_STATUS
+EFIAPI
+DxeImageVerificationLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return RegisterSecurity2Handler (
+ DxeImageVerificationHandler,
+ EFI_AUTH_OPERATION_VERIFY_IMAGE | EFI_AUTH_OPERATION_IMAGE_REQUIRED
+ );
+}
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.h b/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.h
new file mode 100644
index 00000000000..f7c570d1609
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.h
@@ -0,0 +1,255 @@
+/** @file
+ Internal declarations for the DXE Image Verification Library.
+
+ This header is consumed by the library's source files (and its unit
+ tests) to share prototypes for the constructor and the Security2
+ verification handler.
+
+ Copyright (C) Microsoft Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef DXE_IMAGE_VERIFICATION_LIB_H_
+#define DXE_IMAGE_VERIFICATION_LIB_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+//
+// Authorization policy bit definition
+//
+#define ALWAYS_EXECUTE 0x00000000
+#define DENY_EXECUTE_ON_SECURITY_VIOLATION 0x00000001
+
+//
+// Image type definitions
+//
+#define IMAGE_UNKNOWN 0x00000000
+#define IMAGE_FROM_FV 0x00000001
+
+#define MAX_DIGEST_SIZE SHA512_DIGEST_SIZE
+
+//
+// Type definition for all information necessary to describe hash algorithm usage in this library.
+//
+typedef struct {
+ CONST CHAR8 *Name;
+ CONST EFI_GUID *ImageHashGuid;
+ CONST EFI_GUID *X509CertHashGuid;
+} HASH_ALGORITHM;
+
+//
+// All supported hash algorithms for secureboot validation. Adding a new algorithm to this list
+// will add support for that algorithm across the entire library.
+//
+STATIC CONST HASH_ALGORITHM mHashAlgorithms[] = {
+ { "SHA256", &gEfiCertSha256Guid, &gEfiCertX509Sha256Guid },
+ { "SHA384", &gEfiCertSha384Guid, &gEfiCertX509Sha384Guid },
+ { "SHA512", &gEfiCertSha512Guid, &gEfiCertX509Sha512Guid },
+};
+
+//
+// The result of evaluating an image against a signature database.
+//
+// Data / Size describe the `db` authority that authorized the image: Data
+// references the EFI_SIGNATURE_DATA entry inside the signature database that
+// authorized the image, and Size is that entry's SignatureSize. Data is NULL
+// and Size is 0 when no authority authorized the image.
+//
+// SignatureType is the image-hash algorithm GUID under which the image was
+// evaluated.
+//
+typedef struct {
+ CONST EFI_SIGNATURE_DATA *Data;
+ UINTN Size;
+ EFI_GUID SignatureType;
+} IMAGE_AUTHORITY;
+
+//
+// A single Secure Boot authority entry that has been measured into PCR 7.
+// VariableName / VendorGuid reference canonical storage owned by the library;
+// Data is a pool-allocated copy of the EFI_SIGNATURE_DATA that authorized an
+// image.
+//
+typedef struct {
+ CHAR16 *VariableName;
+ EFI_GUID *VendorGuid;
+ VOID *Data;
+ UINTN Size;
+} MEASURED_VARIABLE;
+
+//
+// Tracks the set of authority entries that have been measured into PCR 7
+// during this boot so that no single entry is measured more than once. The
+// only instance of this structure is the module global owned by Measurement.c
+// and obtained through GetMeasuredAuthorities ().
+//
+typedef struct {
+ MEASURED_VARIABLE *List;
+ UINTN Count;
+ UINTN Max;
+} MEASURED_AUTHORITIES;
+
+/**
+ Return the module-global authority measurement state.
+
+ The returned pointer references storage that persists for the lifetime of
+ the module so that measurement de-duplication is maintained across every
+ image the verification handler processes.
+
+ @return Pointer to the module-global MEASURED_AUTHORITIES instance.
+**/
+MEASURED_AUTHORITIES *
+GetMeasuredAuthorities (
+ VOID
+ );
+
+/**
+ Record a rejected image into the Image Execution Information Table.
+
+ Appends a single EFI_IMAGE_EXECUTION_INFO entry describing the rejection. The
+ image name is derived from File via ConvertDevicePathToText. When Digest is
+ non-NULL it is wrapped as an EFI_SIGNATURE_LIST typed by HashType and recorded
+ as the entry's signature (matching the legacy SIG_FOUND / SIG_FAILED
+ behavior); otherwise no signature is recorded. All transient allocations are
+ freed before return.
+
+ @param[in] File Device path of the rejected image. Must be non-NULL.
+ @param[in] Action The EFI_IMAGE_EXECUTION_ACTION describing why the image
+ was rejected.
+ @param[in] HashType Image-hash algorithm GUID describing Digest. Ignored
+ when Digest is NULL.
+ @param[in] Digest Optional image digest to record, or NULL to record no
+ signature.
+ @param[in] DigestSize Size of Digest in bytes; must be non-zero when Digest
+ is non-NULL.
+**/
+VOID
+RecordRejectedImage (
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
+ IN EFI_IMAGE_EXECUTION_ACTION Action,
+ IN CONST EFI_GUID *HashType,
+ IN CONST UINT8 *Digest OPTIONAL,
+ IN UINTN DigestSize
+ );
+
+/**
+ Resolve an image's authorization policy.
+
+ @param[in] File Device path describing the image origin.
+ @param[out] Policy On success, filled with the resolved policy value.
+
+ @retval EFI_SUCCESS Policy contains a valid policy value.
+ @retval EFI_INVALID_PARAMETER File or Policy is NULL.
+**/
+EFI_STATUS
+GetExecutionPolicy (
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
+ OUT UINT32 *Policy
+ );
+
+/**
+ Measure a Secure Boot authority entry into PCR 7, skipping entries that have
+ already been measured this boot.
+
+ If VariableName / VendorGuid do not identify a Secure Boot authority
+ variable, or the supplied data has already been measured, the call is a
+ no-op. Otherwise the data is measured via TpmMeasureAndLogData and recorded
+ in Measured so subsequent identical entries are not measured again.
+
+ @param[in,out] Measured Authority measurement state to consult and update.
+ @param[in] VariableName Name of the variable that authorized the image.
+ @param[in] VendorGuid Vendor GUID of the variable that authorized the image.
+ @param[in] Authority The `db` authority that authorized the image, whose Data / Size
+ identify the EFI_SIGNATURE_DATA entry to measure.
+**/
+VOID
+SecureBootHook (
+ IN OUT MEASURED_AUTHORITIES *Measured,
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN CONST IMAGE_AUTHORITY *Authority
+ );
+
+/**
+ Provide verification service for signed images, which include both signature validation
+ and platform policy control. For signature types, both UEFI WIN_CERTIFICATE_UEFI_GUID and
+ MSFT Authenticode type signatures are supported.
+
+ In this implementation, only verify external executables when in USER MODE.
+ Executables from FV is bypass, so pass in AuthenticationStatus is ignored.
+
+ The image verification policy is:
+ If the image is signed,
+ At least one valid signature or at least one hash value of the image must match a record
+ in the security database "db", and no valid signature nor any hash value of the image may
+ be reflected in the security database "dbx".
+ Otherwise, the image is not signed,
+ The hash value of the image must match a record in the security database "db", and
+ not be reflected in the security data base "dbx".
+
+ Caution: This function may receive untrusted input.
+ PE/COFF image is external input, so this function will validate its data structure
+ within this image buffer before use.
+
+ @param[in] AuthenticationStatus
+ This is the authentication status returned from the security
+ measurement services for the input file.
+ @param[in] File This is a pointer to the device path of the file that is
+ being dispatched. This will optionally be used for logging.
+ @param[in] FileBuffer File buffer matches the input file device path.
+ @param[in] FileSize Size of File buffer matches the input file device path.
+ @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.
+
+ @retval EFI_SUCCESS The file specified by DevicePath and non-NULL
+ FileBuffer did authenticate, and the platform policy dictates
+ that the DXE Foundation may use the file.
+ @retval EFI_SUCCESS The device path specified by NULL device path DevicePath
+ and non-NULL FileBuffer did authenticate, and the platform
+ policy dictates that the DXE Foundation may execute the image in
+ FileBuffer.
+ @retval EFI_ACCESS_DENIED The file specified by File and FileBuffer did not
+ authenticate, and the DXE Foundation may not use File. The
+ image has been added to the file execution table.
+
+**/
+EFI_STATUS
+EFIAPI
+DxeImageVerificationHandler (
+ IN UINT32 AuthenticationStatus,
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *File OPTIONAL,
+ IN VOID *FileBuffer,
+ IN UINTN FileSize,
+ IN BOOLEAN BootPolicy
+ );
+
+/**
+ Register security measurement handler.
+
+ @param ImageHandle ImageHandle of the loaded driver.
+ @param SystemTable Pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The handlers were registered successfully.
+**/
+EFI_STATUS
+EFIAPI
+DxeImageVerificationLibConstructor (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+#endif // DXE_IMAGE_VERIFICATION_LIB_H_
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.inf b/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.inf
new file mode 100644
index 00000000000..0d8ebfaa046
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/DxeImageVerificationLib.inf
@@ -0,0 +1,95 @@
+## @file
+# Provides security service of image verification
+#
+# This library hooks LoadImage() API to verify every image by the verification policy.
+#
+# Caution: This module requires additional review when modified.
+# This library will have external input - PE/COFF image.
+# This external input must be validated carefully to avoid security issues such as
+# buffer overflow or integer overflow.
+#
+# Copyright (C) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeImageVerificationLib
+ MODULE_UNI_FILE = DxeImageVerificationLib.uni
+ FILE_GUID = CFEF751D-B633-489D-8DB4-BE39262D1599
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = NULL|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
+ CONSTRUCTOR = DxeImageVerificationLibConstructor
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ Database.c
+ Database.h
+ DxeImageVerificationLib.c
+ DxeImageVerificationLib.h
+ ImageExecution.c
+ Iterator.c
+ Iterator.h
+ Measurement.c
+ Policy.c
+ Support.c
+ Support.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ CryptoPkg/CryptoPkg.dec
+ SecurityPkg/SecurityPkg.dec
+
+[LibraryClasses]
+ MemoryAllocationLib
+ BaseLib
+ UefiLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ BaseCryptLib
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ PeCoffLib
+ SecureBootVariableLib
+ SecurityManagementLib
+ TpmMeasurementLib
+
+[Protocols]
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## Variable:L"DB"
+ ## SOMETIMES_CONSUMES ## Variable:L"DBX"
+ ## PRODUCES ## SystemTable
+ ## CONSUMES ## SystemTable
+ gEfiImageSecurityDatabaseGuid
+
+ ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature.
+ gEfiCertSha1Guid
+
+ ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature.
+ gEfiCertSha256Guid
+
+ ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature.
+ gEfiCertSha384Guid
+
+ ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature.
+ gEfiCertSha512Guid
+
+ gEfiCertX509Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ gEfiCertX509Sha256Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ gEfiCertX509Sha384Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ gEfiCertX509Sha512Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ gEfiCertPkcs7Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the certificate.
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DatabaseGoogleTest.cpp b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DatabaseGoogleTest.cpp
new file mode 100644
index 00000000000..b2e27cbe865
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DatabaseGoogleTest.cpp
@@ -0,0 +1,2905 @@
+/** @file
+ Unit tests for the signature-database helpers in
+ DxeImageVerificationLib (Database.c): GetImageDigestAuthority,
+ LoadSignatureDatabase, LoadSignatureDatabases, IsCertRevoked,
+ IsCertAuthorized, GetWinCertificatePkcs7AuthData, and
+ GetImageCertAuthority. The database helpers are exercised against
+ synthetic in-memory EFI_SIGNATURE_LIST buffers built by helpers in
+ this file; the variable loaders against a mocked GetVariable2; and the
+ certificate helpers against a mocked BaseCryptLib.
+ Copyright (C) Microsoft Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include
+#include
+#include
+
+#include
+#include
+
+extern "C" {
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include "../Database.h"
+ #include "../Support.h"
+
+ EFI_STATUS
+ LoadSignatureDatabase (
+ IN CONST CHAR16 *DatabaseName,
+ OUT VOID **Buffer,
+ OUT UINTN *BufferSize
+ );
+
+ BOOLEAN
+ IsCertRevoked (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN CONST UINT8 *ImageHash,
+ IN UINTN ImageHashSize,
+ IN CONST VOID *Dbx,
+ IN UINTN DbxSize
+ );
+
+ BOOLEAN
+ IsCertAuthorized (
+ IN CONST UINT8 *AuthData,
+ IN UINTN AuthDataSize,
+ IN CONST UINT8 *ImageHash,
+ IN UINTN ImageHashSize,
+ IN CONST SIGNATURE_DATABASES *Databases,
+ OUT IMAGE_AUTHORITY *Authority
+ );
+
+ EFI_STATUS
+ GetWinCertificatePkcs7AuthData (
+ IN CONST WIN_CERTIFICATE *Cert,
+ OUT CONST UINT8 **AuthData,
+ OUT UINTN *AuthDataSize
+ );
+}
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::SetArgPointee;
+
+static bool
+GetImageHashIndexForTest (
+ const EFI_GUID *Guid,
+ UINTN *Index
+ )
+{
+ UINTN I;
+
+ if ((Guid == nullptr) || (Index == nullptr)) {
+ return false;
+ }
+
+ for (I = 0; I < ARRAY_SIZE (mHashAlgorithms); I++) {
+ if (CompareGuid (Guid, mHashAlgorithms[I].ImageHashGuid)) {
+ *Index = I;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// ---------------------------------------------------------------------------
+// Helpers for constructing signature-list buffers.
+// ---------------------------------------------------------------------------
+
+//
+// Append one EFI_SIGNATURE_LIST containing SignatureCount entries of
+// EntrySize bytes each (entry payloads are zero-initialized) to Buffer.
+// Returns the offset of the new list within Buffer.
+//
+static size_t
+AppendSignatureList (
+ std::vector &Buffer,
+ const EFI_GUID &SignatureType,
+ UINT32 SignatureHeaderSize,
+ UINT32 EntrySize,
+ UINT32 SignatureCount
+ )
+{
+ const size_t PayloadBytes = (size_t)EntrySize * (size_t)SignatureCount;
+ const size_t ListBytes = sizeof (EFI_SIGNATURE_LIST) + SignatureHeaderSize + PayloadBytes;
+ const size_t Offset = Buffer.size ();
+
+ Buffer.resize (Offset + ListBytes, 0);
+
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)(Buffer.data () + Offset);
+
+ CopyMem (&List->SignatureType, &SignatureType, sizeof (EFI_GUID));
+ List->SignatureListSize = (UINT32)ListBytes;
+ List->SignatureHeaderSize = SignatureHeaderSize;
+ List->SignatureSize = EntrySize;
+
+ return Offset;
+}
+
+//
+// Walk callback that simply counts invocations and records the
+// per-list SignatureType GUIDs in visit order.
+//
+// SHA-256 entry size: 16-byte owner GUID + 32-byte digest.
+static constexpr UINT32 kSha256EntrySize = sizeof (EFI_GUID) + 32;
+static constexpr UINT32 kSha384EntrySize = sizeof (EFI_GUID) + 48;
+
+// ---------------------------------------------------------------------------
+// GetImageDigestAuthority
+// ---------------------------------------------------------------------------
+
+//
+// Write Bytes into the entry payload (the part after the owner GUID) of
+// signature index EntryIndex inside the EFI_SIGNATURE_LIST that begins
+// at ListOffset within Buffer.
+//
+static void
+SetEntryPayload (
+ std::vector &Buffer,
+ size_t ListOffset,
+ UINTN EntryIndex,
+ const std::vector &Bytes
+ )
+{
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)(Buffer.data () + ListOffset);
+ const size_t FirstEntry = ListOffset + sizeof (EFI_SIGNATURE_LIST) + List->SignatureHeaderSize;
+ const size_t EntryStart = FirstEntry + (size_t)EntryIndex * (size_t)List->SignatureSize;
+ const size_t PayloadOff = EntryStart + sizeof (EFI_GUID);
+
+ ASSERT_LE (PayloadOff + Bytes.size (), Buffer.size ());
+ std::memcpy (Buffer.data () + PayloadOff, Bytes.data (), Bytes.size ());
+}
+
+// SHA-256 digest payload size (no owner GUID).
+static constexpr UINTN kSha256DigestSize = 32;
+static constexpr UINTN kSha384DigestSize = 48;
+
+static DIGEST_CACHE
+MakeBoundCache (
+ const EFI_GUID *HashType,
+ const std::vector &Digest
+ )
+{
+ DIGEST_CACHE Cache;
+ UINTN Index;
+
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Buffer = (const VOID *)(UINTN)1;
+ Cache.BufferSize = 1;
+
+ EXPECT_TRUE (GetImageHashIndexForTest (HashType, &Index));
+ EXPECT_LE (Digest.size (), (size_t)MAX_DIGEST_SIZE);
+
+ std::memcpy (Cache.Entries[Index].Bytes, Digest.data (), Digest.size ());
+ Cache.Entries[Index].BufferSize = Digest.size ();
+ return Cache;
+}
+
+TEST (GetImageDigestAuthorityTest, NullDatabaseWithNonZeroSize_ReturnsSuccess) {
+ DIGEST_CACHE Cache;
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Buffer = (const VOID *)(UINTN)1;
+ Cache.BufferSize = 1;
+
+ EXPECT_EQ (GetImageDigestAuthority (NULL, 1, &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_EQ (Authority.Data, nullptr);
+ // Validate that Cache remains consistent
+ EXPECT_EQ (Cache.Buffer, (const VOID *)(UINTN)1);
+ EXPECT_EQ (Cache.BufferSize, (UINTN)1);
+}
+
+TEST (GetImageDigestAuthorityTest, NullDatabaseWithZeroSize_EmptyDatabaseNotFound) {
+ DIGEST_CACHE Cache;
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Buffer = (const VOID *)(UINTN)1;
+ Cache.BufferSize = 1;
+
+ EXPECT_EQ (GetImageDigestAuthority (NULL, 0, &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_EQ (Authority.Data, nullptr);
+ // Validate that Cache remains consistent
+ EXPECT_EQ (Cache.Buffer, (const VOID *)(UINTN)1);
+ EXPECT_EQ (Cache.BufferSize, (UINTN)1);
+}
+
+TEST (GetImageDigestAuthorityTest, NullCache_ReturnsInvalidParameter) {
+ UINT8 Dummy = 0;
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (&Dummy, 1, NULL, &Authority), EFI_INVALID_PARAMETER);
+}
+
+TEST (GetImageDigestAuthorityTest, NullAuthority_ReturnsInvalidParameter) {
+ UINT8 Dummy = 0;
+ DIGEST_CACHE Cache;
+
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Buffer = (const VOID *)(UINTN)1;
+ Cache.BufferSize = 1;
+
+ EXPECT_EQ (GetImageDigestAuthority (&Dummy, 1, &Cache, NULL), EFI_INVALID_PARAMETER);
+}
+
+TEST (GetImageDigestAuthorityTest, CacheWithoutImageBinding_ReturnsInvalidParameter) {
+ UINT8 Dummy = 0;
+ DIGEST_CACHE Cache;
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ ZeroMem (&Cache, sizeof (Cache));
+ EXPECT_EQ (GetImageDigestAuthority (&Dummy, 1, &Cache, &Authority), EFI_INVALID_PARAMETER);
+}
+
+TEST (GetImageDigestAuthorityTest, CacheWithZeroFileSize_ReturnsInvalidParameter) {
+ UINT8 Dummy = 0;
+ DIGEST_CACHE Cache;
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Buffer = (const VOID *)(UINTN)1;
+ Cache.BufferSize = 0;
+
+ EXPECT_EQ (GetImageDigestAuthority (&Dummy, 1, &Cache, &Authority), EFI_INVALID_PARAMETER);
+}
+
+TEST (GetImageDigestAuthorityTest, HashComputationFailure_ReturnsSecurityViolation) {
+ MockBaseCryptLib BaseCryptLibMock;
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ DIGEST_CACHE Cache;
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Buffer = (const VOID *)(UINTN)1;
+ Cache.BufferSize = 1;
+
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHash (_, _, _, _, _))
+ .WillOnce (Return (EFI_DEVICE_ERROR));
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SECURITY_VIOLATION);
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+TEST (GetImageDigestAuthorityTest, ExactMatch_Found) {
+ std::vector Db;
+ size_t Off = AppendSignatureList (Db, gEfiCertSha256Guid, 0, kSha256EntrySize, 2);
+ std::vector Target (kSha256DigestSize, 0xAA);
+
+ SetEntryPayload (Db, Off, 1, Target);
+
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Target);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_NE (Authority.Data, nullptr);
+}
+
+TEST (GetImageDigestAuthorityTest, NoMatchingEntry_NotFound) {
+ std::vector Db;
+ size_t Off = AppendSignatureList (Db, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ std::vector Stored (kSha256DigestSize, 0xAA);
+
+ SetEntryPayload (Db, Off, 0, Stored);
+
+ std::vector Digest (kSha256DigestSize, 0xBB);
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Digest);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+TEST (GetImageDigestAuthorityTest, UnknownSignatureTypeList_Skipped) {
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertX509Guid, 0, sizeof (EFI_GUID) + 16, 1);
+
+ std::vector Digest (kSha256DigestSize, 0xAA);
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Digest);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+TEST (GetImageDigestAuthorityTest, MismatchedSignatureSize_Skipped) {
+ // List type matches but per-entry size doesn't, so the list describes
+ // a different algorithm and must be skipped.
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertSha256Guid, 0, kSha384EntrySize, 1);
+
+ std::vector Digest (kSha256DigestSize, 0xCC);
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Digest);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+TEST (GetImageDigestAuthorityTest, MatchInSecondList_Found) {
+ // First list is the wrong algorithm, second list contains the target.
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertX509Guid, 0, sizeof (EFI_GUID) + 16, 1);
+ size_t SecondOff = AppendSignatureList (Db, gEfiCertSha256Guid, 0, kSha256EntrySize, 2);
+
+ std::vector Target (kSha256DigestSize, 0x77);
+
+ SetEntryPayload (Db, SecondOff, 1, Target);
+
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Target);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_NE (Authority.Data, nullptr);
+}
+
+TEST (GetImageDigestAuthorityTest, NonZeroSignatureHeaderSize_EntryMathCorrect) {
+ // SignatureHeaderSize is non-zero: the per-list header occupies
+ // additional bytes between EFI_SIGNATURE_LIST and the first entry.
+ // A naive cursor that forgets to skip it would either miss the
+ // payload entirely or read the header bytes as a fake entry.
+ constexpr UINT32 kHeader = 8;
+ std::vector Db;
+ size_t Off = AppendSignatureList (Db, gEfiCertSha256Guid, kHeader, kSha256EntrySize, 2);
+
+ // Fill the per-list header with a recognizable pattern so a math
+ // bug that read it as an entry would compare against this, not the
+ // real digest. The search target intentionally differs from it.
+ std::memset (Db.data () + Off + sizeof (EFI_SIGNATURE_LIST), 0xEE, kHeader);
+
+ std::vector Target (kSha256DigestSize, 0x55);
+
+ SetEntryPayload (Db, Off, 1, Target);
+
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Target);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_NE (Authority.Data, nullptr);
+}
+
+TEST (GetImageDigestAuthorityTest, ZeroEntryList_NotFound) {
+ // A well-formed list with zero entries must be skipped without a
+ // false positive (EntryCount == 0 means the inner loop never runs).
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertSha256Guid, 0, kSha256EntrySize, 0);
+
+ std::vector Digest (kSha256DigestSize, 0x00);
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Digest);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+TEST (GetImageDigestAuthorityTest, MalformedDb_ReturnsCorrupted) {
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ ((EFI_SIGNATURE_LIST *)Db.data ())->SignatureListSize = (UINT32)(Db.size () + 1);
+
+ std::vector Digest (kSha256DigestSize, 0x00);
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Digest);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_VOLUME_CORRUPTED);
+}
+
+TEST (GetImageDigestAuthorityTest, ZeroSizeNonNullDatabase_EmptyDatabaseNotFound) {
+ UINT8 Dummy = 0;
+ DIGEST_CACHE Cache;
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Buffer = (const VOID *)(UINTN)1;
+ Cache.BufferSize = 1;
+
+ EXPECT_EQ (GetImageDigestAuthority (&Dummy, 0, &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+//
+// A db list whose SignatureType is a supported image-hash GUID (so GetHash
+// succeeds against the bound cache) but whose SignatureHeaderSize is
+// inflated so SigListIterInit rejects it. The list must be skipped and no
+// authority returned.
+//
+TEST (GetImageDigestAuthorityTest, MalformedListHeader_Skipped) {
+ std::vector Digest (kSha256DigestSize, 0xAB);
+ DIGEST_CACHE Cache = MakeBoundCache (&gEfiCertSha256Guid, Digest);
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ // One image-hash list sized for a single SHA-256 entry, but with an
+ // inflated SignatureHeaderSize that overflows the list payload area.
+ const UINT32 EntrySize = kSha256EntrySize;
+ const UINT32 ListSize = (UINT32)(sizeof (EFI_SIGNATURE_LIST) + EntrySize);
+ std::vector Db (ListSize, 0);
+
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Db.data ();
+
+ CopyMem (&List->SignatureType, &gEfiCertSha256Guid, sizeof (EFI_GUID));
+ List->SignatureListSize = ListSize;
+ List->SignatureHeaderSize = ListSize; // > ListSize - sizeof (EFI_SIGNATURE_LIST)
+ List->SignatureSize = EntrySize;
+
+ EXPECT_EQ (GetImageDigestAuthority (Db.data (), Db.size (), &Cache, &Authority), EFI_SUCCESS);
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// LoadSignatureDatabase (uses MockUefiLib::GetVariable2)
+// ---------------------------------------------------------------------------
+
+class LoadSignatureDatabaseTest : public ::testing::Test {
+protected:
+ MockUefiLib UefiLibMock;
+};
+
+TEST_F (LoadSignatureDatabaseTest, NullDatabaseName_ReturnsInvalidParameter) {
+ VOID *Buffer = NULL;
+ UINTN BufferSize = 0;
+
+ EXPECT_EQ (
+ LoadSignatureDatabase (NULL, &Buffer, &BufferSize),
+ EFI_INVALID_PARAMETER
+ );
+}
+
+TEST_F (LoadSignatureDatabaseTest, NullBuffer_ReturnsInvalidParameter) {
+ UINTN BufferSize = 0;
+
+ EXPECT_EQ (
+ LoadSignatureDatabase ((const CHAR16 *)u"db", NULL, &BufferSize),
+ EFI_INVALID_PARAMETER
+ );
+}
+
+TEST_F (LoadSignatureDatabaseTest, NullSize_ReturnsInvalidParameter) {
+ VOID *Buffer = NULL;
+
+ EXPECT_EQ (
+ LoadSignatureDatabase ((const CHAR16 *)u"db", &Buffer, NULL),
+ EFI_INVALID_PARAMETER
+ );
+}
+
+TEST_F (LoadSignatureDatabaseTest, VariableMissing_SuccessWithNullBuffer) {
+ // EFI_NOT_FOUND is normalized to EFI_SUCCESS with *Buffer == NULL.
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (Return (EFI_NOT_FOUND));
+
+ VOID *Buffer = (VOID *)(UINTN)0xDEADBEEF; // pre-set: must be cleared
+ UINTN BufferSize = 0xAA;
+
+ EXPECT_EQ (
+ LoadSignatureDatabase ((const CHAR16 *)u"db", &Buffer, &BufferSize),
+ EFI_SUCCESS
+ );
+ EXPECT_EQ (Buffer, (VOID *)NULL);
+ EXPECT_EQ (BufferSize, 0u);
+}
+
+TEST_F (LoadSignatureDatabaseTest, VariablePresent_BufferAndSizePopulated) {
+ static const UINT8 kPayload[] = { 0xAA, 0xBB, 0xCC, 0xDD };
+
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (
+ IN CONST CHAR16 *Name,
+ IN CONST EFI_GUID *Guid,
+ OUT VOID **Value,
+ OUT UINTN *BufferSize
+ ) -> EFI_STATUS {
+ (VOID)Name;
+ (VOID)Guid;
+ *Value = AllocateCopyPool (sizeof (kPayload), kPayload);
+ *BufferSize = sizeof (kPayload);
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ VOID *Buffer = NULL;
+ UINTN BufferSize = 0;
+
+ EXPECT_EQ (
+ LoadSignatureDatabase ((const CHAR16 *)u"db", &Buffer, &BufferSize),
+ EFI_SUCCESS
+ );
+ ASSERT_NE (Buffer, (VOID *)NULL);
+ EXPECT_EQ (BufferSize, sizeof (kPayload));
+ EXPECT_EQ (CompareMem (Buffer, kPayload, sizeof (kPayload)), 0);
+
+ FreePool (Buffer);
+}
+
+TEST_F (LoadSignatureDatabaseTest, GetVariableUnexpectedError_PropagatedVerbatim) {
+ // Errors other than EFI_NOT_FOUND must be reported unchanged.
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (Return (EFI_DEVICE_ERROR));
+
+ VOID *Buffer = NULL;
+ UINTN BufferSize = 0;
+
+ EXPECT_EQ (
+ LoadSignatureDatabase ((const CHAR16 *)u"db", &Buffer, &BufferSize),
+ EFI_DEVICE_ERROR
+ );
+}
+
+// ---------------------------------------------------------------------------
+// LoadSignatureDatabases (db + dbx)
+// ---------------------------------------------------------------------------
+
+class LoadSignatureDatabasesTest : public ::testing::Test {
+protected:
+ MockUefiLib UefiLibMock;
+};
+
+//
+// Lambda factory: a GetVariable2 action that allocates a copy of the
+// supplied payload and returns EFI_SUCCESS. Used to feed synthetic
+// db/dbx buffers into LoadSignatureDatabases through the mock.
+//
+static auto
+ReturnVariablePayload (
+ const UINT8 *Payload,
+ size_t PayloadSize
+ )
+{
+ return Invoke (
+ [Payload, PayloadSize] (
+ IN CONST CHAR16 *Name,
+ IN CONST EFI_GUID *Guid,
+ OUT VOID **Value,
+ OUT UINTN *BufferSize
+ ) -> EFI_STATUS {
+ (VOID)Name;
+ (VOID)Guid;
+ *Value = AllocateCopyPool (PayloadSize, Payload);
+ *BufferSize = PayloadSize;
+ return EFI_SUCCESS;
+ }
+ );
+}
+
+TEST_F (LoadSignatureDatabasesTest, NullDatabases_ReturnsInvalidParameter) {
+ EXPECT_EQ (
+ LoadSignatureDatabases (NULL),
+ EFI_INVALID_PARAMETER
+ );
+}
+
+TEST_F (LoadSignatureDatabasesTest, BothVariablesMissing_Success) {
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (Return (EFI_NOT_FOUND)) // db
+ .WillOnce (Return (EFI_NOT_FOUND)); // dbx
+
+ // Pre-set to bogus values: must be cleared.
+ SIGNATURE_DATABASES Databases = {
+ (VOID *)(UINTN)0xDEADBEEF, 0xAA,
+ (VOID *)(UINTN)0xCAFEF00D, 0xBB
+ };
+
+ EXPECT_EQ (
+ LoadSignatureDatabases (&Databases),
+ EFI_SUCCESS
+ );
+ EXPECT_EQ (Databases.Db, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbSize, 0u);
+ EXPECT_EQ (Databases.Dbx, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbxSize, 0u);
+}
+
+TEST_F (LoadSignatureDatabasesTest, OnlyDbPresent_DbAllocated) {
+ std::vector DbBuf;
+
+ AppendSignatureList (DbBuf, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (ReturnVariablePayload (DbBuf.data (), DbBuf.size ())) // db
+ .WillOnce (Return (EFI_NOT_FOUND)); // dbx
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+
+ EXPECT_EQ (
+ LoadSignatureDatabases (&Databases),
+ EFI_SUCCESS
+ );
+ ASSERT_NE (Databases.Db, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbSize, DbBuf.size ());
+ EXPECT_EQ (Databases.Dbx, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbxSize, 0u);
+ FreePool (Databases.Db);
+}
+
+TEST_F (LoadSignatureDatabasesTest, OnlyDbxPresent_DbxAllocated) {
+ std::vector DbxBuf;
+
+ AppendSignatureList (DbxBuf, gEfiCertSha384Guid, 0, kSha384EntrySize, 1);
+
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (Return (EFI_NOT_FOUND)) // db
+ .WillOnce (ReturnVariablePayload (DbxBuf.data (), DbxBuf.size ())); // dbx
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+
+ EXPECT_EQ (
+ LoadSignatureDatabases (&Databases),
+ EFI_SUCCESS
+ );
+ EXPECT_EQ (Databases.Db, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbSize, 0u);
+ ASSERT_NE (Databases.Dbx, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbxSize, DbxBuf.size ());
+ FreePool (Databases.Dbx);
+}
+
+TEST_F (LoadSignatureDatabasesTest, BothPresent_BuffersAllocated) {
+ std::vector DbBuf;
+ std::vector DbxBuf;
+
+ AppendSignatureList (DbBuf, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ AppendSignatureList (DbxBuf, gEfiCertSha384Guid, 0, kSha384EntrySize, 1);
+
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (ReturnVariablePayload (DbBuf.data (), DbBuf.size ())) // db
+ .WillOnce (ReturnVariablePayload (DbxBuf.data (), DbxBuf.size ())); // dbx
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+
+ EXPECT_EQ (
+ LoadSignatureDatabases (&Databases),
+ EFI_SUCCESS
+ );
+ ASSERT_NE (Databases.Db, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbSize, DbBuf.size ());
+ ASSERT_NE (Databases.Dbx, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbxSize, DbxBuf.size ());
+ FreePool (Databases.Db);
+ FreePool (Databases.Dbx);
+}
+
+TEST_F (LoadSignatureDatabasesTest, DbLoadFails_ErrorPropagatedNothingAllocated) {
+ // The db lookup fails with a non-NOT_FOUND status; dbx must not even
+ // be attempted, and both out-pointers must be NULL.
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (Return (EFI_DEVICE_ERROR));
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+
+ EXPECT_EQ (
+ LoadSignatureDatabases (&Databases),
+ EFI_DEVICE_ERROR
+ );
+ EXPECT_EQ (Databases.Db, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbSize, 0u);
+ EXPECT_EQ (Databases.Dbx, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbxSize, 0u);
+}
+
+TEST_F (LoadSignatureDatabasesTest, DbxLoadFails_DbFreedAndErrorPropagated) {
+ std::vector DbBuf;
+
+ AppendSignatureList (DbBuf, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ // db succeeds (allocation must be cleaned up internally); dbx fails.
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (ReturnVariablePayload (DbBuf.data (), DbBuf.size ())) // db
+ .WillOnce (Return (EFI_DEVICE_ERROR)); // dbx
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+
+ EXPECT_EQ (
+ LoadSignatureDatabases (&Databases),
+ EFI_DEVICE_ERROR
+ );
+ EXPECT_EQ (Databases.Db, (VOID *)NULL); // freed and nulled
+ EXPECT_EQ (Databases.DbSize, 0u);
+ EXPECT_EQ (Databases.Dbx, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbxSize, 0u);
+}
+
+TEST_F (LoadSignatureDatabasesTest, DbxLoadFailsWithAllocatedBuffer_DbxFreedAndNulled) {
+ std::vector DbBuf;
+
+ AppendSignatureList (DbBuf, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ EXPECT_CALL (UefiLibMock, GetVariable2 (_, _, _, _))
+ .WillOnce (ReturnVariablePayload (DbBuf.data (), DbBuf.size ()))
+ .WillOnce (
+ Invoke (
+ [] (
+ IN CONST CHAR16 *Name,
+ IN CONST EFI_GUID *Guid,
+ OUT VOID **Value,
+ OUT UINTN *BufferSize
+ ) -> EFI_STATUS {
+ (VOID)Name;
+ (VOID)Guid;
+ *Value = AllocatePool (8);
+ *BufferSize = 8;
+ return EFI_DEVICE_ERROR;
+ }
+ )
+ );
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+
+ EXPECT_EQ (
+ LoadSignatureDatabases (&Databases),
+ EFI_DEVICE_ERROR
+ );
+ EXPECT_EQ (Databases.Db, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbSize, 0u);
+ EXPECT_EQ (Databases.Dbx, (VOID *)NULL);
+ EXPECT_EQ (Databases.DbxSize, 0u);
+}
+
+// ---------------------------------------------------------------------------
+// IsTBSCertHashInDbx -- additional list-handling coverage
+// ---------------------------------------------------------------------------
+
+//
+// Dbx is too small to even contain one EFI_SIGNATURE_LIST header.
+// DatabaseIterInit must reject it and the helper must fail closed
+// (return TRUE).
+//
+TEST (IsTBSCertHashInDbxTest, MalformedDbx_ReturnsTrue) {
+ UINT8 TBSCert[] = { 0xDE, 0xAD };
+ // Less than sizeof(EFI_SIGNATURE_LIST) -> DatabaseIterInit returns corrupted.
+ std::vector Dbx (4, 0);
+
+ EXPECT_TRUE (IsTBSCertHashInDbx (TBSCert, sizeof (TBSCert), Dbx.data (), Dbx.size ()));
+}
+
+//
+// Dbx contains exactly one list whose SignatureType is not any of the
+// supported gEfiCertX509ShaXXXGuid values. The helper must skip it
+// and return FALSE.
+//
+TEST (IsTBSCertHashInDbxTest, UnsupportedShaList_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+ UINT8 TBSCert[] = { 0xDE };
+ std::vector Dbx;
+
+ // gEfiCertSha256Guid is an image-hash list type, not a cert-hash list type.
+ AppendSignatureList (Dbx, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ // X509GetTbsCertHash should not be invoked for an unsupported list type.
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _)).Times (0);
+
+ EXPECT_FALSE (IsTBSCertHashInDbx (TBSCert, sizeof (TBSCert), Dbx.data (), Dbx.size ()));
+}
+
+//
+// Dbx is an X509-SHA384 list with no matching entry. The helper must
+// take the SHA-384 branch and return FALSE.
+//
+TEST (IsTBSCertHashInDbxTest, Sha384List_NoMatch_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+ UINT8 TBSCert[] = { 0xDE };
+ const UINT32 EntrySize = (UINT32)(sizeof (EFI_GUID) + SHA384_DIGEST_SIZE);
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertX509Sha384Guid, 0, EntrySize, 1);
+
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0x11, SHA384_DIGEST_SIZE);
+ *DigestSize = SHA384_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ EXPECT_FALSE (IsTBSCertHashInDbx (TBSCert, sizeof (TBSCert), Dbx.data (), Dbx.size ()));
+}
+
+//
+// Dbx is an X509-SHA512 list that contains the matching cert hash.
+// The helper must take the SHA-512 branch and return TRUE.
+//
+TEST (IsTBSCertHashInDbxTest, Sha512List_Match_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+ UINT8 TBSCert[] = { 0xDE };
+ const UINT32 EntrySize = (UINT32)(sizeof (EFI_GUID) + SHA512_DIGEST_SIZE);
+ std::vector Dbx;
+
+ size_t Off = AppendSignatureList (Dbx, gEfiCertX509Sha512Guid, 0, EntrySize, 1);
+
+ SetEntryPayload (Dbx, Off, 0, std::vector(SHA512_DIGEST_SIZE, 0x99));
+
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0x99, SHA512_DIGEST_SIZE);
+ *DigestSize = SHA512_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ EXPECT_TRUE (IsTBSCertHashInDbx (TBSCert, sizeof (TBSCert), Dbx.data (), Dbx.size ()));
+}
+
+//
+// The hash routine itself fails (X509GetTbsCertHash returns an error).
+// The helper must fail closed and return TRUE.
+//
+TEST (IsTBSCertHashInDbxTest, HashFails_FailsClosed_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+ UINT8 TBSCert[] = { 0xDE };
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertX509Sha256Guid, 0, kSha256EntrySize, 1);
+
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (Return (EFI_DEVICE_ERROR));
+
+ EXPECT_TRUE (IsTBSCertHashInDbx (TBSCert, sizeof (TBSCert), Dbx.data (), Dbx.size ()));
+}
+
+//
+// Dbx X509-SHA256 list whose SignatureSize is too small to contain a
+// 32-byte digest. The helper must fail closed and return TRUE.
+//
+TEST (IsTBSCertHashInDbxTest, SignatureSizeTooSmall_FailsClosed_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+ UINT8 TBSCert[] = { 0xDE };
+ // EntrySize = GUID + 16 bytes -- smaller than required for a SHA-256 digest.
+ const UINT32 EntrySize = (UINT32)(sizeof (EFI_GUID) + 16);
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertX509Sha256Guid, 0, EntrySize, 1);
+
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0x00, SHA256_DIGEST_SIZE);
+ *DigestSize = SHA256_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ EXPECT_TRUE (IsTBSCertHashInDbx (TBSCert, sizeof (TBSCert), Dbx.data (), Dbx.size ()));
+}
+
+//
+// Dbx X509-SHA256 list that passes the size check but whose
+// SignatureHeaderSize is inconsistent with SignatureListSize, causing
+// SigListIterInit to fail. The helper must fail closed and return
+// TRUE.
+//
+TEST (IsTBSCertHashInDbxTest, SigListIterInitFails_FailsClosed_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+ UINT8 TBSCert[] = { 0xDE };
+ std::vector Dbx;
+
+ // Build a list with a SignatureHeaderSize larger than the list itself
+ // allows. We size the list to contain a single SHA-256 cert-hash entry
+ // (so the size check at the top of IsX509HashInList passes), but
+ // the inflated SignatureHeaderSize makes SigListIterInit reject it.
+ const UINT32 EntrySize = kSha256EntrySize;
+ const UINT32 ListSize = (UINT32)(sizeof (EFI_SIGNATURE_LIST) + EntrySize);
+
+ Dbx.resize (ListSize, 0);
+
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Dbx.data ();
+
+ CopyMem (&List->SignatureType, &gEfiCertX509Sha256Guid, sizeof (EFI_GUID));
+ List->SignatureListSize = ListSize;
+ // SignatureHeaderSize > SignatureListSize - sizeof (EFI_SIGNATURE_LIST).
+ List->SignatureHeaderSize = ListSize;
+ List->SignatureSize = EntrySize;
+
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0x00, SHA256_DIGEST_SIZE);
+ *DigestSize = SHA256_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ EXPECT_TRUE (IsTBSCertHashInDbx (TBSCert, sizeof (TBSCert), Dbx.data (), Dbx.size ()));
+}
+
+//
+// Dbx contains two X509-SHA256 lists for the same certificate. The
+// TBS digest should be computed once and reused for the second list.
+//
+TEST (IsTBSCertHashInDbxTest, RepeatedSha256Lists_UsesCachedDigest_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+ UINT8 TBSCert[] = { 0xDE };
+ std::vector Dbx;
+
+ size_t Off0 = AppendSignatureList (Dbx, gEfiCertX509Sha256Guid, 0, kSha256EntrySize, 1);
+ size_t Off1 = AppendSignatureList (Dbx, gEfiCertX509Sha256Guid, 0, kSha256EntrySize, 1);
+
+ SetEntryPayload (Dbx, Off0, 0, std::vector(SHA256_DIGEST_SIZE, 0x11));
+ SetEntryPayload (Dbx, Off1, 0, std::vector(SHA256_DIGEST_SIZE, 0x22));
+
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .Times (1)
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0xAA, SHA256_DIGEST_SIZE);
+ *DigestSize = SHA256_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ EXPECT_FALSE (IsTBSCertHashInDbx (TBSCert, sizeof (TBSCert), Dbx.data (), Dbx.size ()));
+}
+
+static const std::vector kAuthDataDefault = std::vector(16, 0xA1);
+static const std::vector kImageHashDefault = std::vector(SHA256_DIGEST_SIZE, 0x55);
+
+// ---------------------------------------------------------------------------
+// IsCertRevoked -- parameter validation
+// ---------------------------------------------------------------------------
+
+TEST (IsCertRevokedTest, NullAuthData_ReturnsTrue) {
+ std::vector Dbx (16, 0);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ NULL,
+ 0,
+ kImageHashDefault.data (),
+ kImageHashDefault.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+TEST (IsCertRevokedTest, ZeroAuthDataSize_ReturnsTrue) {
+ std::vector Dbx (16, 0);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ kAuthDataDefault.data (),
+ 0,
+ kImageHashDefault.data (),
+ kImageHashDefault.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+TEST (IsCertRevokedTest, NullImageHash_ReturnsTrue) {
+ std::vector Dbx (16, 0);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ kAuthDataDefault.data (),
+ kAuthDataDefault.size (),
+ NULL,
+ 0,
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+TEST (IsCertRevokedTest, ZeroImageHashSize_ReturnsTrue) {
+ std::vector Dbx (16, 0);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ kAuthDataDefault.data (),
+ kAuthDataDefault.size (),
+ kImageHashDefault.data (),
+ 0,
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+TEST (IsCertRevokedTest, NullDbx_ReturnsFalse) {
+ EXPECT_FALSE (
+ IsCertRevoked (
+ kAuthDataDefault.data (),
+ kAuthDataDefault.size (),
+ kImageHashDefault.data (),
+ kImageHashDefault.size (),
+ NULL,
+ 0
+ )
+ );
+}
+
+TEST (IsCertRevokedTest, EmptyDbx_ReturnsFalse) {
+ std::vector Dbx;
+
+ EXPECT_FALSE (
+ IsCertRevoked (
+ kAuthDataDefault.data (),
+ kAuthDataDefault.size (),
+ kImageHashDefault.data (),
+ kImageHashDefault.size (),
+ Dbx.data (),
+ 0
+ )
+ );
+}
+
+// ---------------------------------------------------------------------------
+// IsCertAuthorized -- parameter validation
+// ---------------------------------------------------------------------------
+
+TEST (IsCertAuthorizedTest, NullAuthData_ReturnsFalse) {
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ NULL,
+ 0,
+ kImageHashDefault.data (),
+ kImageHashDefault.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+TEST (IsCertAuthorizedTest, ZeroAuthDataSize_ReturnsFalse) {
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ kAuthDataDefault.data (),
+ 0,
+ kImageHashDefault.data (),
+ kImageHashDefault.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+TEST (IsCertAuthorizedTest, NullImageHash_ReturnsFalse) {
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ kAuthDataDefault.data (),
+ kAuthDataDefault.size (),
+ NULL,
+ 0,
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+TEST (IsCertAuthorizedTest, NullDatabases_ReturnsFalse) {
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ kAuthDataDefault.data (),
+ kAuthDataDefault.size (),
+ kImageHashDefault.data (),
+ kImageHashDefault.size (),
+ NULL,
+ &Authority
+ )
+ );
+}
+
+TEST (IsCertAuthorizedTest, NullAuthority_ReturnsFalse) {
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ kAuthDataDefault.data (),
+ kAuthDataDefault.size (),
+ kImageHashDefault.data (),
+ kImageHashDefault.size (),
+ &Databases,
+ NULL
+ )
+ );
+}
+
+TEST (IsCertAuthorizedTest, ZeroImageHashSize_ReturnsFalse) {
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ kAuthDataDefault.data (),
+ kAuthDataDefault.size (),
+ kImageHashDefault.data (),
+ 0,
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+// ---------------------------------------------------------------------------
+// GetWinCertificatePkcs7AuthData
+// ---------------------------------------------------------------------------
+
+TEST (GetWinCertificatePkcs7AuthDataTest, NullCert_ReturnsInvalidParameter) {
+ const UINT8 *AuthData = NULL;
+ UINTN AuthDataSize = 0;
+
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (NULL, &AuthData, &AuthDataSize),
+ EFI_INVALID_PARAMETER
+ );
+}
+
+TEST (GetWinCertificatePkcs7AuthDataTest, NullOutputs_ReturnsInvalidParameter) {
+ WIN_CERTIFICATE Cert = { sizeof (WIN_CERTIFICATE) + 1, 0x0200, WIN_CERT_TYPE_PKCS_SIGNED_DATA };
+ const UINT8 *AuthData = NULL;
+ UINTN AuthDataSize = 0;
+
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (&Cert, NULL, &AuthDataSize),
+ EFI_INVALID_PARAMETER
+ );
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (&Cert, &AuthData, NULL),
+ EFI_INVALID_PARAMETER
+ );
+}
+
+TEST (GetWinCertificatePkcs7AuthDataTest, PkcsSignedData_ExtractsPayload) {
+ // Build a WIN_CERTIFICATE followed by 4 bytes of payload.
+ const UINT8 Payload[] = { 0xAA, 0xBB, 0xCC, 0xDD };
+ std::vector Buffer (sizeof (WIN_CERTIFICATE) + sizeof (Payload), 0);
+ WIN_CERTIFICATE *Cert = (WIN_CERTIFICATE *)Buffer.data ();
+
+ Cert->dwLength = (UINT32)Buffer.size ();
+ Cert->wRevision = 0x0200;
+ Cert->wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
+ std::memcpy (Buffer.data () + sizeof (WIN_CERTIFICATE), Payload, sizeof (Payload));
+
+ const UINT8 *AuthData = NULL;
+ UINTN AuthDataSize = 0;
+
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (Cert, &AuthData, &AuthDataSize),
+ EFI_SUCCESS
+ );
+ ASSERT_EQ (AuthDataSize, sizeof (Payload));
+ EXPECT_EQ (0, std::memcmp (AuthData, Payload, sizeof (Payload)));
+}
+
+TEST (GetWinCertificatePkcs7AuthDataTest, PkcsSignedData_HeaderOnly_ReturnsCorrupted) {
+ WIN_CERTIFICATE Cert = { sizeof (WIN_CERTIFICATE), 0x0200, WIN_CERT_TYPE_PKCS_SIGNED_DATA };
+ const UINT8 *AuthData = NULL;
+ UINTN AuthDataSize = 0;
+
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (&Cert, &AuthData, &AuthDataSize),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (GetWinCertificatePkcs7AuthDataTest, EfiGuidPkcs7_ExtractsPayload) {
+ const UINT8 Payload[] = { 0x11, 0x22, 0x33 };
+ const size_t HeaderSize = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
+ std::vector Buffer (HeaderSize + sizeof (Payload), 0);
+ WIN_CERTIFICATE_UEFI_GUID *UefiCert = (WIN_CERTIFICATE_UEFI_GUID *)Buffer.data ();
+
+ UefiCert->Hdr.dwLength = (UINT32)Buffer.size ();
+ UefiCert->Hdr.wRevision = 0x0200;
+ UefiCert->Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
+ CopyMem (&UefiCert->CertType, &gEfiCertPkcs7Guid, sizeof (EFI_GUID));
+ std::memcpy (Buffer.data () + HeaderSize, Payload, sizeof (Payload));
+
+ const UINT8 *AuthData = NULL;
+ UINTN AuthDataSize = 0;
+
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (&UefiCert->Hdr, &AuthData, &AuthDataSize),
+ EFI_SUCCESS
+ );
+ ASSERT_EQ (AuthDataSize, sizeof (Payload));
+ EXPECT_EQ (0, std::memcmp (AuthData, Payload, sizeof (Payload)));
+}
+
+TEST (GetWinCertificatePkcs7AuthDataTest, EfiGuidNonPkcs7_ReturnsUnsupported) {
+ const size_t HeaderSize = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
+ std::vector Buffer (HeaderSize + 4, 0);
+ WIN_CERTIFICATE_UEFI_GUID *UefiCert = (WIN_CERTIFICATE_UEFI_GUID *)Buffer.data ();
+ const EFI_GUID OtherGuid = { 0x12345678, 0x1234, 0x1234, { 1, 2, 3, 4, 5, 6, 7, 8 }
+ };
+
+ UefiCert->Hdr.dwLength = (UINT32)Buffer.size ();
+ UefiCert->Hdr.wRevision = 0x0200;
+ UefiCert->Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
+ CopyMem (&UefiCert->CertType, &OtherGuid, sizeof (EFI_GUID));
+
+ const UINT8 *AuthData = NULL;
+ UINTN AuthDataSize = 0;
+
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (&UefiCert->Hdr, &AuthData, &AuthDataSize),
+ EFI_UNSUPPORTED
+ );
+}
+
+TEST (GetWinCertificatePkcs7AuthDataTest, EfiGuid_HeaderOnly_ReturnsCorrupted) {
+ WIN_CERTIFICATE_UEFI_GUID UefiCert;
+
+ ZeroMem (&UefiCert, sizeof (UefiCert));
+ UefiCert.Hdr.dwLength = (UINT32)OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
+ UefiCert.Hdr.wRevision = 0x0200;
+ UefiCert.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
+
+ const UINT8 *AuthData = NULL;
+ UINTN AuthDataSize = 0;
+
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (&UefiCert.Hdr, &AuthData, &AuthDataSize),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (GetWinCertificatePkcs7AuthDataTest, UnknownCertType_ReturnsUnsupported) {
+ WIN_CERTIFICATE Cert = { sizeof (WIN_CERTIFICATE) + 8, 0x0200, WIN_CERT_TYPE_EFI_PKCS115 };
+ const UINT8 *AuthData = NULL;
+ UINTN AuthDataSize = 0;
+
+ EXPECT_EQ (
+ GetWinCertificatePkcs7AuthData (&Cert, &AuthData, &AuthDataSize),
+ EFI_UNSUPPORTED
+ );
+}
+
+// ---------------------------------------------------------------------------
+// IsCertAuthorized -- end-to-end PKCS#7 + dbx scenarios
+// ---------------------------------------------------------------------------
+
+//
+// One PKCS#7 signature whose only valid trust anchor in db has its
+// hash listed in dbx. The cert must not be authorized.
+//
+TEST (IsCertAuthorizedTest, SignatureCertInDbAndDbx_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ // db: one X509 list with one cert (payload byte == 0x11).
+ std::vector Db;
+ size_t DbOff = AppendSignatureList (
+ Db,
+ gEfiCertX509Guid,
+ 0,
+ sizeof (EFI_GUID) + 16,
+ 1
+ );
+
+ SetEntryPayload (Db, DbOff, 0, std::vector(16, 0x11));
+
+ // dbx: one X509-SHA256 list with the digest the mocked X509GetTbsCertHash
+ // will produce for the cert's TBS bytes (0xE1 * 32).
+ std::vector Dbx;
+ size_t DbxOff = AppendSignatureList (
+ Dbx,
+ gEfiCertX509Sha256Guid,
+ 0,
+ kSha256EntrySize,
+ 1
+ );
+
+ SetEntryPayload (Dbx, DbxOff, 0, std::vector(kSha256DigestSize, 0xE1));
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .WillOnce (Return (TRUE));
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutTbs, UINTN *OutTbsSize) -> BOOLEAN {
+ static UINT8 TbsBytes[] = { 0x11 };
+ *OutTbs = TbsBytes;
+ *OutTbsSize = sizeof (TbsBytes);
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0xE1, SHA256_DIGEST_SIZE);
+ *DigestSize = SHA256_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), Dbx.data (), Dbx.size () };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+//
+// Happy path: a single PKCS#7 signature, db contains an X509 trust
+// anchor that AuthenticodeVerify accepts, and no dbx. IsCertAuthorized
+// must return TRUE. X509GetTBSCert is still called for the verifying
+// trust anchor, but dbx hashing is skipped because dbx is empty.
+//
+TEST (IsCertAuthorizedTest, SingleSignatureVerifies_NoDbx_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ std::vector Db;
+ size_t DbOff = AppendSignatureList (
+ Db,
+ gEfiCertX509Guid,
+ 0,
+ sizeof (EFI_GUID) + 16,
+ 1
+ );
+
+ SetEntryPayload (Db, DbOff, 0, std::vector(16, 0x11));
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .WillOnce (Return (TRUE));
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutTbs, UINTN *OutTbsSize) -> BOOLEAN {
+ static UINT8 TbsBytes[] = { 0x11 };
+ *OutTbs = TbsBytes;
+ *OutTbsSize = sizeof (TbsBytes);
+ return TRUE;
+ }
+ )
+ );
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_TRUE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+//
+// A PKCS#7 signature exists but no db cert verifies it. IsCertAuthorized
+// must return FALSE.
+//
+TEST (IsCertAuthorizedTest, SignatureDoesNotVerifyAnyDbCert_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ std::vector Db;
+ size_t DbOff = AppendSignatureList (
+ Db,
+ gEfiCertX509Guid,
+ 0,
+ sizeof (EFI_GUID) + 16,
+ 2
+ );
+
+ SetEntryPayload (Db, DbOff, 0, std::vector(16, 0x11));
+ SetEntryPayload (Db, DbOff, 1, std::vector(16, 0x22));
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .Times (2)
+ .WillRepeatedly (Return (FALSE));
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+//
+// db is absent (NULL/0) but the cert carries a valid-looking PKCS#7
+// signature. With no trust anchors at all IsCertAuthorized must return
+// FALSE, and no crypto must be invoked.
+//
+TEST (IsCertAuthorizedTest, NoDbButValidSignature_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ // No db => the db walk visits nothing => AuthenticodeVerify is never called.
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+// ---------------------------------------------------------------------------
+// IsCertAuthorized -- additional signature-list handling coverage
+// ---------------------------------------------------------------------------
+
+//
+// db contains only a non-X.509 list (image-hash list). The db walk
+// inspects it but skips it; with no trust anchors available the cert
+// must not be authorized.
+//
+TEST (IsCertAuthorizedTest, DbHasOnlyNonX509List_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ // db: one image-hash list (gEfiCertSha256Guid) with a non-matching digest.
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ // No X.509 list => AuthenticodeVerify is never called.
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+//
+// db contains an X.509 list whose SignatureSize equals sizeof(EFI_GUID),
+// i.e. no cert payload at all. The list must be skipped and the cert
+// must not be authorized.
+//
+TEST (IsCertAuthorizedTest, DbX509ListNoCertPayload_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ // X.509 list with SignatureSize = sizeof(EFI_GUID) (no cert payload).
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertX509Guid, 0, (UINT32)sizeof (EFI_GUID), 1);
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+//
+// db contains an X.509 list whose SignatureHeaderSize is inflated past
+// SignatureListSize - sizeof(EFI_SIGNATURE_LIST). DatabaseIterInit
+// accepts the list (it only validates SignatureListSize), but
+// SigListIterInit inside IsPkcs7AuthDataVerifiedByX509 rejects it.
+// The list must be skipped and the cert must not be authorized.
+//
+TEST (IsCertAuthorizedTest, DbX509ListMalformedHeader_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ // Build a single X.509 list with SignatureSize > sizeof(EFI_GUID) (so
+ // the early-return check in IsPkcs7AuthDataVerifiedByX509 is passed)
+ // but a SignatureHeaderSize that overflows the list payload area.
+ const UINT32 EntrySize = (UINT32)(sizeof (EFI_GUID) + 16);
+ const UINT32 ListSize = (UINT32)(sizeof (EFI_SIGNATURE_LIST) + EntrySize);
+ std::vector Db (ListSize, 0);
+
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Db.data ();
+
+ CopyMem (&List->SignatureType, &gEfiCertX509Guid, sizeof (EFI_GUID));
+ List->SignatureListSize = ListSize;
+ List->SignatureHeaderSize = ListSize; // > ListSize - sizeof(EFI_SIGNATURE_LIST)
+ List->SignatureSize = EntrySize;
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+//
+// db is malformed (less than one EFI_SIGNATURE_LIST header). The
+// per-signature DatabaseIterInit call fails and IsCertAuthorized must
+// return FALSE.
+//
+TEST (IsCertAuthorizedTest, MalformedDb_RejectsImage) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ // Less than sizeof(EFI_SIGNATURE_LIST) -> DatabaseIterInit fails.
+ std::vector Db (4, 0);
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+//
+// db contains an X.509 (full-cert) list whose entry AuthenticodeVerify
+// accepts, but X509GetTBSCert fails to extract the TBSCertificate. The
+// helper must fail closed and the image must not be authorized.
+//
+TEST (IsCertAuthorizedTest, DbX509AuthenticodeVerifiesButTbsCertFails_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ std::vector Db;
+ size_t DbOff = AppendSignatureList (Db, gEfiCertX509Guid, 0, (UINT32)(sizeof (EFI_GUID) + 16), 1);
+
+ SetEntryPayload (Db, DbOff, 0, std::vector(16, 0x11));
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .WillOnce (Return (TRUE));
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _))
+ .WillOnce (Return (FALSE));
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// IsCertAuthorized -- X.509 TBS-cert-hash (trust anchor) list handling
+// ---------------------------------------------------------------------------
+
+//
+// db contains an EFI_CERT_X509_SHA256 (TBS-cert-hash) list. A trust
+// anchor is recovered from the PKCS#7 auth data via
+// GetTrustAnchorX509FromAuthData, and its TBS hash is not present in an
+// (empty) dbx. The image must be authorized and Authority must point at
+// the matching db entry. The non-NULL cache handle produced by the
+// lookup must be released via FreeTrustAnchorX509Cache.
+//
+TEST (IsCertAuthorizedTest, X509HashListTrustAnchorNotRevoked_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertX509Sha256Guid, 0, kSha256EntrySize, 1);
+
+ // The returned trust anchor is freed by the library, so it must be
+ // pool-allocated. A non-NULL cache handle is also produced.
+ EXPECT_CALL (BaseCryptLibMock, GetTrustAnchorX509FromAuthData (_, _, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID **CacheHandle, CONST UINT8 *, UINTN, CONST UINT8 *, UINTN, UINT8 **TrustAnchor, UINTN *TrustAnchorSize) -> EFI_STATUS {
+ static const UINT8 CertBytes[] = { 0x30, 0x82, 0x01, 0x02 };
+ *TrustAnchor = (UINT8 *)AllocateCopyPool (sizeof (CertBytes), CertBytes);
+ *TrustAnchorSize = sizeof (CertBytes);
+ if (CacheHandle != NULL) {
+ *CacheHandle = (VOID *)(UINTN)1;
+ }
+
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ EXPECT_CALL (BaseCryptLibMock, FreeTrustAnchorX509Cache (_)).Times (1);
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_TRUE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+ EXPECT_NE (Authority.Data, nullptr);
+}
+
+//
+// db contains an EFI_CERT_X509_SHA256 list with two entries, but neither
+// entry recovers a trust anchor from the auth data (EFI_NOT_FOUND). The
+// helper must walk both entries and the image must not be authorized.
+//
+TEST (IsCertAuthorizedTest, X509HashListNoTrustAnchor_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertX509Sha256Guid, 0, kSha256EntrySize, 2);
+
+ EXPECT_CALL (BaseCryptLibMock, GetTrustAnchorX509FromAuthData (_, _, _, _, _, _, _))
+ .Times (2)
+ .WillRepeatedly (Return (EFI_NOT_FOUND));
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+//
+// db contains an EFI_CERT_X509_SHA256 list; the trust-anchor lookup
+// fails with a hard error (not EFI_NOT_FOUND). The helper must fail
+// closed and the image must not be authorized.
+//
+TEST (IsCertAuthorizedTest, X509HashListTrustAnchorLookupError_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertX509Sha256Guid, 0, kSha256EntrySize, 1);
+
+ EXPECT_CALL (BaseCryptLibMock, GetTrustAnchorX509FromAuthData (_, _, _, _, _, _, _))
+ .WillOnce (Return (EFI_DEVICE_ERROR));
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+//
+// db contains an EFI_CERT_X509_SHA256 list; a trust anchor is recovered
+// from the auth data, but its TBS hash is enrolled in dbx. The anchor
+// must be skipped and, with no other entries, the image is not
+// authorized.
+//
+TEST (IsCertAuthorizedTest, X509HashListTrustAnchorRevoked_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertX509Sha256Guid, 0, kSha256EntrySize, 1);
+
+ // dbx: one X509-SHA256 list holding the digest the mocked
+ // X509GetTbsCertHash produces for the recovered anchor (0xC3 * 32).
+ std::vector Dbx;
+ size_t DbxOff = AppendSignatureList (Dbx, gEfiCertX509Sha256Guid, 0, kSha256EntrySize, 1);
+
+ SetEntryPayload (Dbx, DbxOff, 0, std::vector(kSha256DigestSize, 0xC3));
+
+ EXPECT_CALL (BaseCryptLibMock, GetTrustAnchorX509FromAuthData (_, _, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID **, CONST UINT8 *, UINTN, CONST UINT8 *, UINTN, UINT8 **TrustAnchor, UINTN *TrustAnchorSize) -> EFI_STATUS {
+ static const UINT8 CertBytes[] = { 0x30, 0x82, 0x01, 0x02 };
+ *TrustAnchor = (UINT8 *)AllocateCopyPool (sizeof (CertBytes), CertBytes);
+ *TrustAnchorSize = sizeof (CertBytes);
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0xC3, SHA256_DIGEST_SIZE);
+ *DigestSize = SHA256_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), Dbx.data (), Dbx.size () };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+//
+// db contains an EFI_CERT_X509_SHA256 list whose SignatureSize is only
+// sizeof(EFI_GUID) -- no TBS-hash payload. The list must be skipped (no
+// trust-anchor lookup attempted) and the image must not be authorized.
+//
+TEST (IsCertAuthorizedTest, X509HashListNoCertPayload_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ std::vector Db;
+
+ AppendSignatureList (Db, gEfiCertX509Sha256Guid, 0, (UINT32)sizeof (EFI_GUID), 1);
+
+ EXPECT_CALL (BaseCryptLibMock, GetTrustAnchorX509FromAuthData (_, _, _, _, _, _, _)).Times (0);
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+//
+// db contains an EFI_CERT_X509_SHA256 list whose SignatureHeaderSize is
+// inflated past the list payload, so SigListIterInit rejects it. The list
+// must be skipped and the image must not be authorized.
+//
+TEST (IsCertAuthorizedTest, X509HashListMalformedHeader_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ const UINT32 EntrySize = kSha256EntrySize;
+ const UINT32 ListSize = (UINT32)(sizeof (EFI_SIGNATURE_LIST) + EntrySize);
+ std::vector Db (ListSize, 0);
+
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Db.data ();
+
+ CopyMem (&List->SignatureType, &gEfiCertX509Sha256Guid, sizeof (EFI_GUID));
+ List->SignatureListSize = ListSize;
+ List->SignatureHeaderSize = ListSize; // > ListSize - sizeof (EFI_SIGNATURE_LIST)
+ List->SignatureSize = EntrySize;
+
+ EXPECT_CALL (BaseCryptLibMock, GetTrustAnchorX509FromAuthData (_, _, _, _, _, _, _)).Times (0);
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_FALSE (
+ IsCertAuthorized (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ &Databases,
+ &Authority
+ )
+ );
+}
+
+// ---------------------------------------------------------------------------
+// IsCertRevoked -- end-to-end PKCS#7 + dbx scenarios
+// ---------------------------------------------------------------------------
+
+//
+// Build a buffer in EFI_CERT_STACK format:
+// UINT8 CertNumber; { UINT32 CertLen (LE); UINT8 CertData[CertLen]; } * N
+//
+static std::vector
+MakeCertStack (
+ const std::vector > &Certs
+ )
+{
+ std::vector Buf;
+
+ Buf.push_back ((UINT8)Certs.size ());
+ for (const auto &Cert : Certs) {
+ UINT32 Len = (UINT32)Cert.size ();
+ Buf.push_back ((UINT8)(Len & 0xFF));
+ Buf.push_back ((UINT8)((Len >> 8) & 0xFF));
+ Buf.push_back ((UINT8)((Len >> 16) & 0xFF));
+ Buf.push_back ((UINT8)((Len >> 24) & 0xFF));
+ Buf.insert (Buf.end (), Cert.begin (), Cert.end ());
+ }
+
+ return Buf;
+}
+
+//
+// dbx contains an X.509 list whose entry AuthenticodeVerify accepts as
+// a trust anchor for the cert's signature. Step 1 short-circuits and
+// the cert is revoked. Pkcs7GetSigners must not be reached.
+//
+TEST (IsCertRevokedTest, DbxX509VerifiesSignature_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+ size_t DbxOff = AppendSignatureList (
+ Dbx,
+ gEfiCertX509Guid,
+ 0,
+ sizeof (EFI_GUID) + 16,
+ 1
+ );
+
+ SetEntryPayload (Dbx, DbxOff, 0, std::vector(16, 0x11));
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .WillOnce (Return (TRUE));
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _)).Times (0);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// dbx X.509 list does not verify the signature, and Pkcs7GetSigners
+// reports no recoverable signer chain. The cert is not revoked.
+//
+TEST (IsCertRevokedTest, DbxX509DoesNotVerify_PkcsSignersFails_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+ size_t DbxOff = AppendSignatureList (
+ Dbx,
+ gEfiCertX509Guid,
+ 0,
+ sizeof (EFI_GUID) + 16,
+ 1
+ );
+
+ SetEntryPayload (Dbx, DbxOff, 0, std::vector(16, 0x22));
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .WillOnce (Return (FALSE));
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (Return (FALSE));
+
+ EXPECT_FALSE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// dbx has only hash-typed lists (no X.509 entries). Step 1 calls
+// AuthenticodeVerify zero times; step 2 attempts the signer chain.
+//
+TEST (IsCertRevokedTest, DbxOnlyHashLists_PkcsSignersFails_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (Return (FALSE));
+
+ EXPECT_FALSE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// dbx X.509 list whose SignatureSize equals sizeof(EFI_GUID) carries
+// no cert payload; step 1 must skip the list rather than treating
+// it as a verifying anchor.
+//
+TEST (IsCertRevokedTest, DbxX509ListNoCertPayload_StepOneSkipped_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertX509Guid, 0, sizeof (EFI_GUID), 1);
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (Return (FALSE));
+
+ EXPECT_FALSE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// dbx contains an X509-SHA256 list whose digest matches the TBS hash
+// of the signer reported by Pkcs7GetSigners. Step 1 finds no verifying
+// anchor; step 2 hashes the signer's TBS and IsTBSCertHashInDbx fires.
+//
+TEST (IsCertRevokedTest, SignerTBSHashInDbx_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+ size_t DbxOff = AppendSignatureList (
+ Dbx,
+ gEfiCertX509Sha256Guid,
+ 0,
+ kSha256EntrySize,
+ 1
+ );
+
+ SetEntryPayload (Dbx, DbxOff, 0, std::vector(kSha256DigestSize, 0xE1));
+
+ static std::vector Stack =
+ MakeCertStack (
+ { std::vector{ 0x11, 0x22, 0x33 }
+ }
+ );
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutStack, UINTN *OutStackLen, UINT8 **OutTrusted, UINTN *OutTrustedLen) -> BOOLEAN {
+ *OutStack = Stack.data ();
+ *OutStackLen = Stack.size ();
+ *OutTrusted = NULL;
+ *OutTrustedLen = 0;
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutTbs, UINTN *OutTbsSize) -> BOOLEAN {
+ static UINT8 Tbs[] = { 0x11 };
+ *OutTbs = Tbs;
+ *OutTbsSize = sizeof (Tbs);
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0xE1, SHA256_DIGEST_SIZE);
+ *DigestSize = SHA256_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7FreeSigners (_)).Times (1);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// Pkcs7GetSigners reports a single signer whose TBS hash is not in
+// dbx. The cert is not revoked.
+//
+TEST (IsCertRevokedTest, SignerTBSHashNotInDbx_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+ size_t DbxOff = AppendSignatureList (
+ Dbx,
+ gEfiCertX509Sha256Guid,
+ 0,
+ kSha256EntrySize,
+ 1
+ );
+
+ // Plant a digest that the mocked X509GetTbsCertHash will never produce.
+ SetEntryPayload (Dbx, DbxOff, 0, std::vector(kSha256DigestSize, 0xAA));
+
+ static std::vector Stack =
+ MakeCertStack (
+ { std::vector{ 0x11, 0x22, 0x33 }
+ }
+ );
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutStack, UINTN *OutStackLen, UINT8 **OutTrusted, UINTN *OutTrustedLen) -> BOOLEAN {
+ *OutStack = Stack.data ();
+ *OutStackLen = Stack.size ();
+ *OutTrusted = NULL;
+ *OutTrustedLen = 0;
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutTbs, UINTN *OutTbsSize) -> BOOLEAN {
+ static UINT8 Tbs[] = { 0x11 };
+ *OutTbs = Tbs;
+ *OutTbsSize = sizeof (Tbs);
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTbsCertHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Digest, UINTN *DigestSize) -> EFI_STATUS {
+ std::memset (Digest, 0x77, SHA256_DIGEST_SIZE);
+ *DigestSize = SHA256_DIGEST_SIZE;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7FreeSigners (_)).Times (1);
+
+ EXPECT_FALSE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// Pkcs7GetSigners reports a non-NULL stack with CertNumber == 0.
+// Step 2 has nothing to walk and the cert is not revoked.
+//
+TEST (IsCertRevokedTest, PkcsSignersEmptyStack_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ static std::vector EmptyStack = MakeCertStack ({ });
+
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutStack, UINTN *OutStackLen, UINT8 **OutTrusted, UINTN *OutTrustedLen) -> BOOLEAN {
+ *OutStack = EmptyStack.data ();
+ *OutStackLen = EmptyStack.size ();
+ *OutTrusted = NULL;
+ *OutTrustedLen = 0;
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7FreeSigners (_)).Times (1);
+
+ EXPECT_FALSE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// X509GetTBSCert fails on the signer; treat the chain as poisoned.
+//
+TEST (IsCertRevokedTest, X509GetTBSCertFails_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ static std::vector Stack =
+ MakeCertStack (
+ { std::vector{ 0x11, 0x22, 0x33 }
+ }
+ );
+
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutStack, UINTN *OutStackLen, UINT8 **OutTrusted, UINTN *OutTrustedLen) -> BOOLEAN {
+ *OutStack = Stack.data ();
+ *OutStackLen = Stack.size ();
+ *OutTrusted = NULL;
+ *OutTrustedLen = 0;
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _))
+ .WillOnce (Return (FALSE));
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7FreeSigners (_)).Times (1);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// Pkcs7GetSigners returns a stack whose CertNumber claims one cert
+// but whose length field would run past the end of the buffer. Step 2
+// detects the malformed payload and treats the chain as poisoned.
+//
+TEST (IsCertRevokedTest, MalformedSignerStack_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ // CertNumber=1, length=0x10, but only 4 payload bytes follow.
+ static std::vector BadStack = {
+ 0x01, 0x10, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC, 0xDD
+ };
+
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutStack, UINTN *OutStackLen, UINT8 **OutTrusted, UINTN *OutTrustedLen) -> BOOLEAN {
+ *OutStack = BadStack.data ();
+ *OutStackLen = BadStack.size ();
+ *OutTrusted = NULL;
+ *OutTrustedLen = 0;
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7FreeSigners (_)).Times (1);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// Pkcs7GetSigners returns a stack that claims a signer but truncates the
+// per-cert length prefix (fewer than 4 bytes remain). IsCertRevoked must
+// fail closed and report the cert as revoked.
+//
+TEST (IsCertRevokedTest, MalformedSignerStackLengthPrefix_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ // CertNumber=1, but only 2 bytes follow -- fewer than the 4-byte length
+ // prefix the parser expects.
+ static std::vector BadStack = { 0x01, 0xAA, 0xBB };
+
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutStack, UINTN *OutStackLen, UINT8 **OutTrusted, UINTN *OutTrustedLen) -> BOOLEAN {
+ *OutStack = BadStack.data ();
+ *OutStackLen = BadStack.size ();
+ *OutTrusted = NULL;
+ *OutTrustedLen = 0;
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7FreeSigners (_)).Times (1);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// Both CertStack and TrustedCert are returned by Pkcs7GetSigners.
+// Both must be freed exactly once via Pkcs7FreeSigners.
+//
+TEST (IsCertRevokedTest, BothPkcsBuffersFreed_ReturnsFalse) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ std::vector Dbx;
+
+ AppendSignatureList (Dbx, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ static std::vector Stack = MakeCertStack ({ });
+ static UINT8 TrustedCertBuf[] = { 0xDE, 0xAD, 0xBE, 0xEF };
+
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutStack, UINTN *OutStackLen, UINT8 **OutTrusted, UINTN *OutTrustedLen) -> BOOLEAN {
+ *OutStack = Stack.data ();
+ *OutStackLen = Stack.size ();
+ *OutTrusted = TrustedCertBuf;
+ *OutTrustedLen = sizeof (TrustedCertBuf);
+ return TRUE;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7FreeSigners (_)).Times (2);
+
+ EXPECT_FALSE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+//
+// dbx is too small to contain even one EFI_SIGNATURE_LIST header,
+// so DatabaseIterInit fails. Step 1 fails closed.
+//
+TEST (IsCertRevokedTest, MalformedDbx_ReturnsTrue) {
+ MockBaseCryptLib BaseCryptLibMock;
+
+ std::vector AuthData (16, 0xA1);
+ std::vector ImageHash (SHA256_DIGEST_SIZE, 0x55);
+
+ // Less than sizeof(EFI_SIGNATURE_LIST) -> DatabaseIterInit fails.
+ std::vector Dbx (4, 0);
+
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _)).Times (0);
+
+ EXPECT_TRUE (
+ IsCertRevoked (
+ AuthData.data (),
+ AuthData.size (),
+ ImageHash.data (),
+ ImageHash.size (),
+ Dbx.data (),
+ Dbx.size ()
+ )
+ );
+}
+
+// ---------------------------------------------------------------------------
+// GetImageCertAuthority -- prelude (GetWinCertificatePkcs7AuthData,
+// GetAuthenticodeHashAlgorithm, GetHash) plus IsCertRevoked / IsCertAuthorized
+// dispatch.
+// ---------------------------------------------------------------------------
+
+// Build a PKCS_SIGNED_DATA WIN_CERTIFICATE wrapping `Payload`.
+static std::vector
+MakePkcsSignedDataCert (
+ const std::vector &Payload
+ )
+{
+ std::vector Buffer (sizeof (WIN_CERTIFICATE) + Payload.size (), 0);
+ WIN_CERTIFICATE *Cert = (WIN_CERTIFICATE *)Buffer.data ();
+
+ Cert->dwLength = (UINT32)Buffer.size ();
+ Cert->wRevision = 0x0200;
+ Cert->wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
+ std::memcpy (Buffer.data () + sizeof (WIN_CERTIFICATE), Payload.data (), Payload.size ());
+ return Buffer;
+}
+
+// Tiny throwaway "image" buffer for the digest cache; mocks of
+// GetAuthenticodeHash never dereference it.
+static UINT8 kFakeImage[16] = { 0 };
+
+static void
+InitImageCache (
+ DIGEST_CACHE &Cache
+ )
+{
+ ZeroMem (&Cache, sizeof (Cache));
+ Cache.Type = DigestCacheTypeImage;
+ Cache.Buffer = kFakeImage;
+ Cache.BufferSize = sizeof (kFakeImage);
+}
+
+TEST (GetImageCertAuthorityTest, NullCert_ReturnsInvalidParameter) {
+ DIGEST_CACHE Cache;
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ InitImageCache (Cache);
+ EXPECT_EQ (GetImageCertAuthority (NULL, &Cache, &Databases, &Authority), EFI_INVALID_PARAMETER);
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+TEST (GetImageCertAuthorityTest, NullCache_ReturnsInvalidParameter) {
+ std::vector CertBuf = MakePkcsSignedDataCert (std::vector(16, 0xA1));
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ EXPECT_EQ (
+ GetImageCertAuthority (
+ (CONST WIN_CERTIFICATE *)CertBuf.data (),
+ NULL,
+ &Databases,
+ &Authority
+ ),
+ EFI_INVALID_PARAMETER
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+TEST (GetImageCertAuthorityTest, NullDatabases_ReturnsInvalidParameter) {
+ std::vector CertBuf = MakePkcsSignedDataCert (std::vector(16, 0xA1));
+ DIGEST_CACHE Cache;
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ InitImageCache (Cache);
+ EXPECT_EQ (
+ GetImageCertAuthority (
+ (CONST WIN_CERTIFICATE *)CertBuf.data (),
+ &Cache,
+ NULL,
+ &Authority
+ ),
+ EFI_INVALID_PARAMETER
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+}
+
+//
+// GetWinCertificatePkcs7AuthData fails (unsupported WIN_CERTIFICATE type) so
+// the prelude bails before touching crypto.
+//
+TEST (GetImageCertAuthorityTest, UnsupportedCertType_ReturnsError) {
+ MockBaseCryptLib BaseCryptLibMock;
+ DIGEST_CACHE Cache;
+
+ WIN_CERTIFICATE Cert = { sizeof (WIN_CERTIFICATE) + 8, 0x0200, WIN_CERT_TYPE_EFI_PKCS115 };
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ InitImageCache (Cache);
+
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHashAlgorithm (_, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHash (_, _, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+
+ // A prelude failure is folded into EFI_ACCESS_DENIED; no hash algorithm was
+ // established so SignatureType is left as the zero GUID.
+ EXPECT_EQ (GetImageCertAuthority (&Cert, &Cache, &Databases, &Authority), EFI_ACCESS_DENIED);
+ EXPECT_EQ (Authority.Data, nullptr);
+ EXPECT_TRUE (IsZeroBuffer (&Authority.SignatureType, sizeof (EFI_GUID)));
+}
+
+//
+// GetAuthenticodeHashAlgorithm fails -> prelude bails, hash and verify are
+// never invoked.
+//
+TEST (GetImageCertAuthorityTest, HashAlgorithmFails_ReturnsError) {
+ MockBaseCryptLib BaseCryptLibMock;
+ DIGEST_CACHE Cache;
+ std::vector CertBuf = MakePkcsSignedDataCert (std::vector(16, 0xA1));
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ InitImageCache (Cache);
+
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHashAlgorithm (_, _, _))
+ .WillOnce (Return (EFI_UNSUPPORTED));
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHash (_, _, _, _, _)).Times (0);
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+
+ // A prelude failure is folded into EFI_ACCESS_DENIED with a zero SignatureType.
+ EXPECT_EQ (
+ GetImageCertAuthority (
+ (CONST WIN_CERTIFICATE *)CertBuf.data (),
+ &Cache,
+ &Databases,
+ &Authority
+ ),
+ EFI_ACCESS_DENIED
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+ EXPECT_TRUE (IsZeroBuffer (&Authority.SignatureType, sizeof (EFI_GUID)));
+}
+
+//
+// GetAuthenticodeHash fails -> GetHash returns SECURITY_VIOLATION and the
+// prelude bails before any revocation / authorization decision.
+//
+TEST (GetImageCertAuthorityTest, GetHashFails_ReturnsError) {
+ MockBaseCryptLib BaseCryptLibMock;
+ DIGEST_CACHE Cache;
+ std::vector CertBuf = MakePkcsSignedDataCert (std::vector(16, 0xA1));
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, NULL, 0 };
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ InitImageCache (Cache);
+
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHashAlgorithm (_, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, EFI_GUID *Out) -> EFI_STATUS {
+ CopyMem (Out, &gEfiCertSha256Guid, sizeof (EFI_GUID));
+ return EFI_SUCCESS;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHash (_, _, _, _, _))
+ .WillOnce (Return (EFI_DEVICE_ERROR));
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _)).Times (0);
+
+ // GetHash failure is a prelude failure -> EFI_ACCESS_DENIED, zero SignatureType.
+ EXPECT_EQ (
+ GetImageCertAuthority (
+ (CONST WIN_CERTIFICATE *)CertBuf.data (),
+ &Cache,
+ &Databases,
+ &Authority
+ ),
+ EFI_ACCESS_DENIED
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+ EXPECT_TRUE (IsZeroBuffer (&Authority.SignatureType, sizeof (EFI_GUID)));
+}
+
+//
+// Happy path: prelude succeeds, dbx is empty so IsCertRevoked returns FALSE,
+// the first db trust anchor verifies via AuthenticodeVerify, and X509GetTBSCert
+// succeeds. GetImageCertAuthority returns EFI_SUCCESS with a non-NULL authority.
+//
+TEST (GetImageCertAuthorityTest, AuthorizedByDb_ReturnsAuthority) {
+ MockBaseCryptLib BaseCryptLibMock;
+ DIGEST_CACHE Cache;
+ std::vector CertBuf = MakePkcsSignedDataCert (std::vector(16, 0xA1));
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ InitImageCache (Cache);
+
+ std::vector Db;
+ size_t DbOff = AppendSignatureList (
+ Db,
+ gEfiCertX509Guid,
+ 0,
+ sizeof (EFI_GUID) + 16,
+ 1
+ );
+
+ SetEntryPayload (Db, DbOff, 0, std::vector(16, 0x11));
+
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHashAlgorithm (_, _, _))
+ .WillRepeatedly (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, EFI_GUID *Out) -> EFI_STATUS {
+ CopyMem (Out, &gEfiCertSha256Guid, sizeof (EFI_GUID));
+ return EFI_SUCCESS;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Out, UINTN *OutSize) -> EFI_STATUS {
+ SetMem (Out, kSha256DigestSize, 0x55);
+ *OutSize = kSha256DigestSize;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+
+ // dbx is empty so phase 1 of IsCertRevoked never calls AuthenticodeVerify;
+ // Pkcs7GetSigners returning FALSE skips phase 2.
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillRepeatedly (Return (FALSE));
+
+ // The single db anchor verifies the signature.
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .WillOnce (Return (TRUE));
+ EXPECT_CALL (BaseCryptLibMock, X509GetTBSCert (_, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, UINT8 **OutTbs, UINTN *OutTbsSize) -> BOOLEAN {
+ static UINT8 TbsBytes[] = { 0x11 };
+ *OutTbs = TbsBytes;
+ *OutTbsSize = sizeof (TbsBytes);
+ return TRUE;
+ }
+ )
+ );
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ EXPECT_EQ (
+ GetImageCertAuthority (
+ (CONST WIN_CERTIFICATE *)CertBuf.data (),
+ &Cache,
+ &Databases,
+ &Authority
+ ),
+ EFI_SUCCESS
+ );
+ EXPECT_NE (Authority.Data, nullptr);
+}
+
+//
+// A dbx X.509 anchor verifies the signature: IsCertRevoked returns TRUE so
+// the cert is skipped before IsCertAuthorized is consulted.
+//
+TEST (GetImageCertAuthorityTest, RevokedByDbx_ReturnsNoAuthority) {
+ MockBaseCryptLib BaseCryptLibMock;
+ DIGEST_CACHE Cache;
+ std::vector CertBuf = MakePkcsSignedDataCert (std::vector(16, 0xA1));
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ InitImageCache (Cache);
+
+ // dbx with a single X.509 anchor that AuthenticodeVerify accepts.
+ std::vector Dbx;
+ size_t DbxOff = AppendSignatureList (
+ Dbx,
+ gEfiCertX509Guid,
+ 0,
+ sizeof (EFI_GUID) + 16,
+ 1
+ );
+
+ SetEntryPayload (Dbx, DbxOff, 0, std::vector(16, 0xCC));
+
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHashAlgorithm (_, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, EFI_GUID *Out) -> EFI_STATUS {
+ CopyMem (Out, &gEfiCertSha256Guid, sizeof (EFI_GUID));
+ return EFI_SUCCESS;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Out, UINTN *OutSize) -> EFI_STATUS {
+ SetMem (Out, kSha256DigestSize, 0x55);
+ *OutSize = kSha256DigestSize;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .WillOnce (Return (TRUE));
+
+ SIGNATURE_DATABASES Databases = { NULL, 0, Dbx.data (), Dbx.size () };
+
+ // Revocation by dbx -> EFI_ACCESS_DENIED. The hash algorithm was established
+ // before revocation, so SignatureType carries it for the rejection record.
+ EXPECT_EQ (
+ GetImageCertAuthority (
+ (CONST WIN_CERTIFICATE *)CertBuf.data (),
+ &Cache,
+ &Databases,
+ &Authority
+ ),
+ EFI_ACCESS_DENIED
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+ EXPECT_TRUE (CompareGuid (&Authority.SignatureType, &gEfiCertSha256Guid));
+}
+
+//
+// Prelude succeeds, dbx is empty (not revoked), but db has no trust anchor
+// that verifies the signature, so IsCertAuthorized returns FALSE.
+//
+TEST (GetImageCertAuthorityTest, NotRevokedNotAuthorized_ReturnsNoAuthority) {
+ MockBaseCryptLib BaseCryptLibMock;
+ DIGEST_CACHE Cache;
+ std::vector CertBuf = MakePkcsSignedDataCert (std::vector(16, 0xA1));
+
+ IMAGE_AUTHORITY Authority = { NULL, 0 };
+
+ InitImageCache (Cache);
+
+ std::vector Db;
+ size_t DbOff = AppendSignatureList (
+ Db,
+ gEfiCertX509Guid,
+ 0,
+ sizeof (EFI_GUID) + 16,
+ 2
+ );
+
+ SetEntryPayload (Db, DbOff, 0, std::vector(16, 0x11));
+ SetEntryPayload (Db, DbOff, 1, std::vector(16, 0x22));
+
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHashAlgorithm (_, _, _))
+ .WillOnce (
+ Invoke (
+ [] (CONST UINT8 *, UINTN, EFI_GUID *Out) -> EFI_STATUS {
+ CopyMem (Out, &gEfiCertSha256Guid, sizeof (EFI_GUID));
+ return EFI_SUCCESS;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, GetAuthenticodeHash (_, _, _, _, _))
+ .WillOnce (
+ Invoke (
+ [] (VOID *, UINTN, CONST EFI_GUID *, UINT8 *Out, UINTN *OutSize) -> EFI_STATUS {
+ SetMem (Out, kSha256DigestSize, 0x55);
+ *OutSize = kSha256DigestSize;
+ return EFI_SUCCESS;
+ }
+ )
+ );
+ EXPECT_CALL (BaseCryptLibMock, Pkcs7GetSigners (_, _, _, _, _, _))
+ .WillRepeatedly (Return (FALSE));
+ EXPECT_CALL (BaseCryptLibMock, AuthenticodeVerify (_, _, _, _, _, _))
+ .Times (2)
+ .WillRepeatedly (Return (FALSE));
+
+ SIGNATURE_DATABASES Databases = { Db.data (), Db.size (), NULL, 0 };
+
+ // Not revoked, but no db anchor authorizes -> EFI_NOT_FOUND.
+ EXPECT_EQ (
+ GetImageCertAuthority (
+ (CONST WIN_CERTIFICATE *)CertBuf.data (),
+ &Cache,
+ &Databases,
+ &Authority
+ ),
+ EFI_NOT_FOUND
+ );
+ EXPECT_EQ (Authority.Data, nullptr);
+}
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DxeImageVerificationLibGoogleTest.cpp b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DxeImageVerificationLibGoogleTest.cpp
new file mode 100644
index 00000000000..b5e41cd9915
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DxeImageVerificationLibGoogleTest.cpp
@@ -0,0 +1,24 @@
+/** @file
+ Integration tests for DxeImageVerificationHandler.
+
+ Test cases in this file dispatch through RunVerificationScenario
+ declared in ScenarioHarness.h. The harness implementation, including
+ every mock and input builder, lives in ScenarioHarness.cpp so this
+ file stays limited to test bodies and the gtest entry point.
+
+ Copyright (c) 2025, Yandex. All rights reserved.
+ Copyright (C) Microsoft Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include
+
+int
+main (
+ int argc,
+ char *argv[]
+ )
+{
+ testing::InitGoogleTest (&argc, argv);
+ return RUN_ALL_TESTS ();
+}
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DxeImageVerificationLibGoogleTest.inf b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DxeImageVerificationLibGoogleTest.inf
new file mode 100644
index 00000000000..1357c7662f7
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/DxeImageVerificationLibGoogleTest.inf
@@ -0,0 +1,78 @@
+## @file
+# Unit test suite for the DxeImageVerificationLib using Google Test
+#
+# Copyright (c) Microsoft Corporation.
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = DxeImageVerificationLibGoogleTest
+ FILE_GUID = 30170C3A-E353-4C8E-B20A-7D0E5E821FD8
+ MODULE_TYPE = HOST_APPLICATION
+ VERSION_STRING = 1.0
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ DatabaseGoogleTest.cpp
+ DxeImageVerificationLibGoogleTest.cpp
+ IteratorGoogleTest.cpp
+ MeasurementGoogleTest.cpp
+ PolicyGoogleTest.cpp
+ SupportGoogleTest.cpp
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ SecurityPkg/SecurityPkg.dec
+ UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
+ CryptoPkg/CryptoPkg.dec
+
+[LibraryClasses]
+ DxeImageVerificationLib
+ GoogleTestLib
+ BaseCryptLib
+ DebugLib
+ SecureBootVariableLib
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## Variable:L"SecureBoot"
+ gEfiGlobalVariableGuid
+
+ ## SOMETIMES_CONSUMES ## Variable:L"DB"
+ ## SOMETIMES_CONSUMES ## Variable:L"DBX"
+ ## PRODUCES ## SystemTable
+ ## CONSUMES ## SystemTable
+ gEfiImageSecurityDatabaseGuid
+
+ ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature.
+ gEfiCertSha1Guid
+
+ ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature.
+ gEfiCertSha256Guid
+
+ ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature.
+ gEfiCertSha384Guid
+
+ ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ ## SOMETIMES_PRODUCES ## GUID # Unique ID for the type of the signature.
+ gEfiCertSha512Guid
+
+ gEfiCertX509Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ gEfiCertX509Sha256Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ gEfiCertX509Sha384Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ gEfiCertX509Sha512Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the signature.
+ gEfiCertPkcs7Guid ## SOMETIMES_CONSUMES ## GUID # Unique ID for the type of the certificate.
+
+[Protocols]
+ gEfiFirmwareVolume2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/IteratorGoogleTest.cpp b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/IteratorGoogleTest.cpp
new file mode 100644
index 00000000000..23765fb5399
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/IteratorGoogleTest.cpp
@@ -0,0 +1,590 @@
+/** @file
+ Unit tests for the iterators in Iterator.c:
+ DatabaseIterInit/Next, SigListIterInit/Next, and WinCertIterInit/Next.
+
+ All three iterators follow the same contract: Init performs full
+ structural validation of the container, and Next is infallible after
+ a successful Init.
+
+ Copyright (C) Microsoft Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include
+
+#include
+#include
+
+extern "C" {
+ #include
+ #include
+ #include
+ #include
+ #include "../Iterator.h"
+}
+
+// ---------------------------------------------------------------------------
+// Helpers for constructing synthetic signature-list buffers.
+// ---------------------------------------------------------------------------
+
+// SHA-256 entry size: 16-byte owner GUID + 32-byte digest.
+static constexpr UINT32 kSha256EntrySize = sizeof (EFI_GUID) + 32;
+
+//
+// Append one EFI_SIGNATURE_LIST containing SignatureCount entries of
+// EntrySize bytes each (entry payloads are zero-initialized) to Buffer.
+// Returns the offset of the new list within Buffer.
+//
+static size_t
+AppendSignatureList (
+ std::vector &Buffer,
+ const EFI_GUID &SignatureType,
+ UINT32 SignatureHeaderSize,
+ UINT32 EntrySize,
+ UINT32 SignatureCount
+ )
+{
+ const size_t PayloadBytes = (size_t)EntrySize * (size_t)SignatureCount;
+ const size_t ListBytes = sizeof (EFI_SIGNATURE_LIST) + SignatureHeaderSize + PayloadBytes;
+ const size_t Offset = Buffer.size ();
+
+ Buffer.resize (Offset + ListBytes, 0);
+
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)(Buffer.data () + Offset);
+
+ CopyMem (&List->SignatureType, &SignatureType, sizeof (EFI_GUID));
+ List->SignatureListSize = (UINT32)ListBytes;
+ List->SignatureHeaderSize = SignatureHeaderSize;
+ List->SignatureSize = EntrySize;
+
+ return Offset;
+}
+
+// ---------------------------------------------------------------------------
+// DatabaseIterInit
+// ---------------------------------------------------------------------------
+
+TEST (DatabaseIterInitTest, NullIter_ReturnsInvalidParameter) {
+ std::vector Buffer (16, 0);
+
+ EXPECT_EQ (DatabaseIterInit (NULL, Buffer.data (), Buffer.size ()), EFI_INVALID_PARAMETER);
+}
+
+TEST (DatabaseIterInitTest, NullBufferWithNonZeroSize_ReturnsInvalidParameter) {
+ SIG_DATABASE_ITER Iter;
+
+ EXPECT_EQ (DatabaseIterInit (&Iter, NULL, 16), EFI_INVALID_PARAMETER);
+}
+
+TEST (DatabaseIterInitTest, NullBufferWithZeroSize_Succeeds) {
+ SIG_DATABASE_ITER Iter;
+
+ EXPECT_EQ (DatabaseIterInit (&Iter, NULL, 0), EFI_SUCCESS);
+ EXPECT_EQ (Iter.Remaining, (UINTN)0);
+ EXPECT_EQ (DatabaseIterNext (&Iter), nullptr);
+}
+
+TEST (DatabaseIterInitTest, EmptyBuffer_Succeeds) {
+ std::vector Buffer;
+ SIG_DATABASE_ITER Iter;
+
+ EXPECT_EQ (DatabaseIterInit (&Iter, Buffer.data (), 0), EFI_SUCCESS);
+ EXPECT_EQ (DatabaseIterNext (&Iter), nullptr);
+}
+
+TEST (DatabaseIterInitTest, SingleWellFormedList_Succeeds) {
+ std::vector Buffer;
+ SIG_DATABASE_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 2);
+
+ EXPECT_EQ (DatabaseIterInit (&Iter, Buffer.data (), Buffer.size ()), EFI_SUCCESS);
+}
+
+TEST (DatabaseIterInitTest, TrailingBytesBelowHeader_ReturnsCorrupted) {
+ std::vector Buffer;
+ SIG_DATABASE_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ // Append a few stray bytes too small to be even a list header.
+ Buffer.resize (Buffer.size () + sizeof (EFI_SIGNATURE_LIST) - 1, 0);
+
+ EXPECT_EQ (DatabaseIterInit (&Iter, Buffer.data (), Buffer.size ()), EFI_VOLUME_CORRUPTED);
+}
+
+TEST (DatabaseIterInitTest, ListSizeBelowHeaderSize_ReturnsCorrupted) {
+ std::vector Buffer;
+ SIG_DATABASE_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ ((EFI_SIGNATURE_LIST *)Buffer.data ())->SignatureListSize = sizeof (EFI_SIGNATURE_LIST) - 1;
+
+ EXPECT_EQ (DatabaseIterInit (&Iter, Buffer.data (), Buffer.size ()), EFI_VOLUME_CORRUPTED);
+}
+
+TEST (DatabaseIterInitTest, ListSizeOverrunsBuffer_ReturnsCorrupted) {
+ std::vector Buffer;
+ SIG_DATABASE_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ ((EFI_SIGNATURE_LIST *)Buffer.data ())->SignatureListSize = (UINT32)(Buffer.size () + 1);
+
+ EXPECT_EQ (DatabaseIterInit (&Iter, Buffer.data (), Buffer.size ()), EFI_VOLUME_CORRUPTED);
+}
+
+// Init only checks the outer tiling; bad internals belong to SigListIterInit.
+TEST (DatabaseIterInitTest, MalformedInternalsButValidTiling_Succeeds) {
+ std::vector Buffer;
+ SIG_DATABASE_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ // SignatureSize < sizeof(EFI_GUID) is internal corruption that
+ // DatabaseIterInit deliberately ignores.
+ ((EFI_SIGNATURE_LIST *)Buffer.data ())->SignatureSize = 1;
+
+ EXPECT_EQ (DatabaseIterInit (&Iter, Buffer.data (), Buffer.size ()), EFI_SUCCESS);
+}
+
+// ---------------------------------------------------------------------------
+// DatabaseIterNext
+// ---------------------------------------------------------------------------
+
+TEST (DatabaseIterNextTest, NullIter_ReturnsNull) {
+ EXPECT_EQ (DatabaseIterNext (NULL), nullptr);
+}
+
+TEST (DatabaseIterNextTest, EmptyDatabase_ReturnsNull) {
+ SIG_DATABASE_ITER Iter;
+
+ ASSERT_EQ (DatabaseIterInit (&Iter, NULL, 0), EFI_SUCCESS);
+ EXPECT_EQ (DatabaseIterNext (&Iter), nullptr);
+}
+
+TEST (DatabaseIterNextTest, IteratesAllListsInOrder) {
+ std::vector Buffer;
+ SIG_DATABASE_ITER Iter;
+ size_t Off1, Off2, Off3;
+
+ Off1 = AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ Off2 = AppendSignatureList (Buffer, gEfiCertX509Guid, 0, sizeof (EFI_GUID) + 8, 2);
+ Off3 = AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 3);
+
+ ASSERT_EQ (DatabaseIterInit (&Iter, Buffer.data (), Buffer.size ()), EFI_SUCCESS);
+
+ CONST EFI_SIGNATURE_LIST *L1 = DatabaseIterNext (&Iter);
+ CONST EFI_SIGNATURE_LIST *L2 = DatabaseIterNext (&Iter);
+ CONST EFI_SIGNATURE_LIST *L3 = DatabaseIterNext (&Iter);
+ CONST EFI_SIGNATURE_LIST *L4 = DatabaseIterNext (&Iter);
+
+ EXPECT_EQ ((CONST UINT8 *)L1, Buffer.data () + Off1);
+ EXPECT_EQ ((CONST UINT8 *)L2, Buffer.data () + Off2);
+ EXPECT_EQ ((CONST UINT8 *)L3, Buffer.data () + Off3);
+ EXPECT_EQ (L4, nullptr);
+
+ // Drained iterator stays drained.
+ EXPECT_EQ (DatabaseIterNext (&Iter), nullptr);
+}
+
+// ---------------------------------------------------------------------------
+// SigListIterInit
+// ---------------------------------------------------------------------------
+
+TEST (SigListIterInitTest, NullIter_ReturnsInvalidParameter) {
+ std::vector Buffer;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+
+ EXPECT_EQ (
+ SigListIterInit (NULL, (EFI_SIGNATURE_LIST *)Buffer.data ()),
+ EFI_INVALID_PARAMETER
+ );
+}
+
+TEST (SigListIterInitTest, NullList_ReturnsInvalidParameter) {
+ SIG_LIST_ITER Iter;
+
+ EXPECT_EQ (SigListIterInit (&Iter, NULL), EFI_INVALID_PARAMETER);
+}
+
+TEST (SigListIterInitTest, ListSizeBelowHeader_ReturnsCorrupted) {
+ std::vector Buffer;
+ SIG_LIST_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ ((EFI_SIGNATURE_LIST *)Buffer.data ())->SignatureListSize = sizeof (EFI_SIGNATURE_LIST) - 1;
+
+ EXPECT_EQ (
+ SigListIterInit (&Iter, (EFI_SIGNATURE_LIST *)Buffer.data ()),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (SigListIterInitTest, SignatureSizeBelowGuid_ReturnsCorrupted) {
+ std::vector Buffer;
+ SIG_LIST_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ ((EFI_SIGNATURE_LIST *)Buffer.data ())->SignatureSize = sizeof (EFI_GUID) - 1;
+
+ EXPECT_EQ (
+ SigListIterInit (&Iter, (EFI_SIGNATURE_LIST *)Buffer.data ()),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (SigListIterInitTest, HeaderSizeExceedsList_ReturnsCorrupted) {
+ std::vector Buffer;
+ SIG_LIST_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 1);
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Buffer.data ();
+
+ List->SignatureHeaderSize = List->SignatureListSize; // leaves no room
+
+ EXPECT_EQ (SigListIterInit (&Iter, List), EFI_VOLUME_CORRUPTED);
+}
+
+TEST (SigListIterInitTest, PayloadNotMultipleOfSignatureSize_ReturnsCorrupted) {
+ std::vector Buffer;
+ SIG_LIST_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 2);
+ // Trim one byte off SignatureListSize so payload no longer divides
+ // evenly into SignatureSize chunks.
+ ((EFI_SIGNATURE_LIST *)Buffer.data ())->SignatureListSize -= 1;
+
+ EXPECT_EQ (
+ SigListIterInit (&Iter, (EFI_SIGNATURE_LIST *)Buffer.data ()),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (SigListIterInitTest, WellFormed_Succeeds) {
+ std::vector Buffer;
+ SIG_LIST_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 3);
+
+ EXPECT_EQ (
+ SigListIterInit (&Iter, (EFI_SIGNATURE_LIST *)Buffer.data ()),
+ EFI_SUCCESS
+ );
+ EXPECT_EQ (Iter.Stride, (UINTN)kSha256EntrySize);
+ EXPECT_EQ (Iter.Remaining, (UINTN)3);
+}
+
+// ---------------------------------------------------------------------------
+// SigListIterNext
+// ---------------------------------------------------------------------------
+
+TEST (SigListIterNextTest, NullIter_ReturnsNull) {
+ EXPECT_EQ (SigListIterNext (NULL), nullptr);
+}
+
+TEST (SigListIterNextTest, EmptyList_ReturnsNull) {
+ std::vector Buffer;
+ SIG_LIST_ITER Iter;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, 0);
+ ASSERT_EQ (
+ SigListIterInit (&Iter, (EFI_SIGNATURE_LIST *)Buffer.data ()),
+ EFI_SUCCESS
+ );
+
+ EXPECT_EQ (SigListIterNext (&Iter), nullptr);
+}
+
+TEST (SigListIterNextTest, IteratesEntriesWithCorrectStride) {
+ std::vector Buffer;
+ SIG_LIST_ITER Iter;
+ const UINT32 EntryCount = 4;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, 0, kSha256EntrySize, EntryCount);
+
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Buffer.data ();
+ UINT8 *FirstEntry =
+ (UINT8 *)List + sizeof (EFI_SIGNATURE_LIST) + List->SignatureHeaderSize;
+
+ ASSERT_EQ (SigListIterInit (&Iter, List), EFI_SUCCESS);
+
+ for (UINT32 i = 0; i < EntryCount; i++) {
+ CONST EFI_SIGNATURE_DATA *Entry = SigListIterNext (&Iter);
+ ASSERT_NE (Entry, nullptr) << "entry " << i;
+ EXPECT_EQ ((CONST UINT8 *)Entry, FirstEntry + (size_t)i * kSha256EntrySize);
+ }
+
+ EXPECT_EQ (SigListIterNext (&Iter), nullptr);
+ EXPECT_EQ (SigListIterNext (&Iter), nullptr);
+}
+
+TEST (SigListIterNextTest, RespectsSignatureHeaderSize) {
+ std::vector Buffer;
+ SIG_LIST_ITER Iter;
+ const UINT32 HeaderSize = 12;
+
+ AppendSignatureList (Buffer, gEfiCertSha256Guid, HeaderSize, kSha256EntrySize, 2);
+
+ EFI_SIGNATURE_LIST *List = (EFI_SIGNATURE_LIST *)Buffer.data ();
+
+ ASSERT_EQ (SigListIterInit (&Iter, List), EFI_SUCCESS);
+
+ CONST EFI_SIGNATURE_DATA *Entry = SigListIterNext (&Iter);
+
+ ASSERT_NE (Entry, nullptr);
+ EXPECT_EQ (
+ (CONST UINT8 *)Entry,
+ (CONST UINT8 *)List + sizeof (EFI_SIGNATURE_LIST) + HeaderSize
+ );
+}
+
+// ---------------------------------------------------------------------------
+// WinCertIter helpers
+// ---------------------------------------------------------------------------
+
+//
+// Build a synthetic PE/COFF "file" whose security directory immediately
+// follows a header area. The returned dir VA/Size point into FileBuffer.
+//
+struct SyntheticImage {
+ std::vector FileBuffer;
+ EFI_IMAGE_DATA_DIRECTORY Dir;
+};
+
+//
+// Append a single WIN_CERTIFICATE entry of total length dwLength
+// (including the header) to Dir. dwLength is written as-is, so callers
+// may inject malformed values for negative tests.
+//
+static void
+AppendWinCert (
+ std::vector &Dir,
+ UINT32 dwLength,
+ UINT16 wRevision,
+ UINT16 wCertificateType
+ )
+{
+ const UINT32 Padded = (UINT32)ALIGN_VALUE (dwLength, 8);
+ const size_t Offset = Dir.size ();
+
+ // Reserve the padded size so the next entry starts on an 8-byte boundary.
+ Dir.resize (Offset + Padded, 0);
+
+ WIN_CERTIFICATE *Cert = (WIN_CERTIFICATE *)(Dir.data () + Offset);
+
+ Cert->dwLength = dwLength;
+ Cert->wRevision = wRevision;
+ Cert->wCertificateType = wCertificateType;
+}
+
+static SyntheticImage
+BuildImageWithDir (
+ const std::vector &DirContents
+ )
+{
+ SyntheticImage Img;
+
+ // 64 bytes of leading "header" so the dir VA is non-zero.
+ Img.FileBuffer.assign (64, 0);
+ Img.Dir.VirtualAddress = (UINT32)Img.FileBuffer.size ();
+ Img.Dir.Size = (UINT32)DirContents.size ();
+ Img.FileBuffer.insert (
+ Img.FileBuffer.end (),
+ DirContents.begin (),
+ DirContents.end ()
+ );
+
+ return Img;
+}
+
+// ---------------------------------------------------------------------------
+// WinCertIterInit
+// ---------------------------------------------------------------------------
+
+TEST (WinCertIterInitTest, NullParams_ReturnInvalidParameter) {
+ WIN_CERT_ITER Iter;
+ std::vector File (64, 0);
+ EFI_IMAGE_DATA_DIRECTORY Dir = { 0, 0 };
+
+ EXPECT_EQ (WinCertIterInit (NULL, File.data (), File.size (), &Dir), EFI_INVALID_PARAMETER);
+ EXPECT_EQ (WinCertIterInit (&Iter, NULL, File.size (), &Dir), EFI_INVALID_PARAMETER);
+ EXPECT_EQ (WinCertIterInit (&Iter, File.data (), File.size (), NULL), EFI_INVALID_PARAMETER);
+}
+
+TEST (WinCertIterInitTest, EmptyDirectory_Succeeds) {
+ WIN_CERT_ITER Iter;
+ std::vector File (64, 0);
+ EFI_IMAGE_DATA_DIRECTORY Dir = { 0, 0 };
+
+ EXPECT_EQ (WinCertIterInit (&Iter, File.data (), File.size (), &Dir), EFI_SUCCESS);
+ EXPECT_EQ (WinCertIterNext (&Iter), nullptr);
+}
+
+TEST (WinCertIterInitTest, DirVirtualAddressPastEnd_ReturnsCorrupted) {
+ WIN_CERT_ITER Iter;
+ std::vector File (64, 0);
+ EFI_IMAGE_DATA_DIRECTORY Dir;
+
+ Dir.VirtualAddress = (UINT32)(File.size () + 1);
+ Dir.Size = 0;
+
+ EXPECT_EQ (
+ WinCertIterInit (&Iter, File.data (), File.size (), &Dir),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (WinCertIterInitTest, DirSizeOverrunsFile_ReturnsCorrupted) {
+ WIN_CERT_ITER Iter;
+ std::vector File (128, 0);
+ EFI_IMAGE_DATA_DIRECTORY Dir;
+
+ Dir.VirtualAddress = 64;
+ Dir.Size = (UINT32)(File.size () - 64 + 1);
+
+ EXPECT_EQ (
+ WinCertIterInit (&Iter, File.data (), File.size (), &Dir),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (WinCertIterInitTest, EntryDwLengthBelowHeader_ReturnsCorrupted) {
+ std::vector Dir;
+
+ AppendWinCert (Dir, sizeof (WIN_CERTIFICATE), 0x0200, WIN_CERT_TYPE_PKCS_SIGNED_DATA);
+ // Corrupt dwLength after the fact.
+ ((WIN_CERTIFICATE *)Dir.data ())->dwLength = sizeof (WIN_CERTIFICATE) - 1;
+
+ SyntheticImage Img = BuildImageWithDir (Dir);
+ WIN_CERT_ITER Iter;
+
+ EXPECT_EQ (
+ WinCertIterInit (&Iter, Img.FileBuffer.data (), Img.FileBuffer.size (), &Img.Dir),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (WinCertIterInitTest, EntryDwLengthOverrunsRemaining_ReturnsCorrupted) {
+ std::vector Dir;
+
+ AppendWinCert (Dir, sizeof (WIN_CERTIFICATE) + 8, 0x0200, WIN_CERT_TYPE_PKCS_SIGNED_DATA);
+ ((WIN_CERTIFICATE *)Dir.data ())->dwLength = (UINT32)Dir.size () + 1;
+
+ SyntheticImage Img = BuildImageWithDir (Dir);
+ WIN_CERT_ITER Iter;
+
+ EXPECT_EQ (
+ WinCertIterInit (&Iter, Img.FileBuffer.data (), Img.FileBuffer.size (), &Img.Dir),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+TEST (WinCertIterInitTest, WellFormedEntries_Succeeds) {
+ std::vector Dir;
+
+ AppendWinCert (Dir, sizeof (WIN_CERTIFICATE) + 16, 0x0200, WIN_CERT_TYPE_PKCS_SIGNED_DATA);
+ AppendWinCert (Dir, sizeof (WIN_CERTIFICATE) + 32, 0x0200, WIN_CERT_TYPE_EFI_GUID);
+
+ SyntheticImage Img = BuildImageWithDir (Dir);
+ WIN_CERT_ITER Iter;
+
+ EXPECT_EQ (
+ WinCertIterInit (&Iter, Img.FileBuffer.data (), Img.FileBuffer.size (), &Img.Dir),
+ EFI_SUCCESS
+ );
+}
+
+//
+// A directory smaller than a WIN_CERTIFICATE header: the walk sees a
+// non-zero Remaining that cannot hold another header and fails closed.
+//
+TEST (WinCertIterInitTest, TrailingBytesBelowHeader_ReturnsCorrupted) {
+ std::vector Dir (sizeof (WIN_CERTIFICATE) - 1, 0);
+
+ SyntheticImage Img = BuildImageWithDir (Dir);
+ WIN_CERT_ITER Iter;
+
+ EXPECT_EQ (
+ WinCertIterInit (&Iter, Img.FileBuffer.data (), Img.FileBuffer.size (), &Img.Dir),
+ EFI_VOLUME_CORRUPTED
+ );
+}
+
+// ---------------------------------------------------------------------------
+// WinCertIterNext
+// ---------------------------------------------------------------------------
+
+TEST (WinCertIterNextTest, NullIter_ReturnsNull) {
+ EXPECT_EQ (WinCertIterNext (NULL), nullptr);
+}
+
+TEST (WinCertIterNextTest, IteratesAllEntriesWithAlignment) {
+ std::vector Dir;
+
+ // Three entries with different dwLength values (some not multiples of 8)
+ // to exercise the ALIGN_VALUE advance.
+ const UINT32 Len1 = sizeof (WIN_CERTIFICATE) + 5; // 13 -> aligned to 16
+ const UINT32 Len2 = sizeof (WIN_CERTIFICATE) + 16; // 24
+ const UINT32 Len3 = sizeof (WIN_CERTIFICATE) + 1; // 9 -> aligned to 16
+
+ AppendWinCert (Dir, Len1, 0x0200, WIN_CERT_TYPE_PKCS_SIGNED_DATA);
+ AppendWinCert (Dir, Len2, 0x0200, WIN_CERT_TYPE_PKCS_SIGNED_DATA);
+ AppendWinCert (Dir, Len3, 0x0200, WIN_CERT_TYPE_EFI_GUID);
+
+ SyntheticImage Img = BuildImageWithDir (Dir);
+ WIN_CERT_ITER Iter;
+
+ ASSERT_EQ (
+ WinCertIterInit (&Iter, Img.FileBuffer.data (), Img.FileBuffer.size (), &Img.Dir),
+ EFI_SUCCESS
+ );
+
+ CONST UINT8 *Base = Img.FileBuffer.data () + Img.Dir.VirtualAddress;
+ CONST WIN_CERTIFICATE *C1 = WinCertIterNext (&Iter);
+ CONST WIN_CERTIFICATE *C2 = WinCertIterNext (&Iter);
+ CONST WIN_CERTIFICATE *C3 = WinCertIterNext (&Iter);
+ CONST WIN_CERTIFICATE *C4 = WinCertIterNext (&Iter);
+
+ ASSERT_NE (C1, nullptr);
+ ASSERT_NE (C2, nullptr);
+ ASSERT_NE (C3, nullptr);
+ EXPECT_EQ ((CONST UINT8 *)C1, Base);
+ EXPECT_EQ ((CONST UINT8 *)C2, Base + ALIGN_VALUE (Len1, 8));
+ EXPECT_EQ ((CONST UINT8 *)C3, Base + ALIGN_VALUE (Len1, 8) + ALIGN_VALUE (Len2, 8));
+ EXPECT_EQ (C1->dwLength, Len1);
+ EXPECT_EQ (C2->dwLength, Len2);
+ EXPECT_EQ (C3->dwLength, Len3);
+ EXPECT_EQ (C4, nullptr);
+
+ // Drained iterator stays drained.
+ EXPECT_EQ (WinCertIterNext (&Iter), nullptr);
+}
+
+//
+// A single entry whose dwLength consumes the entire (unpadded) directory:
+// ALIGN_VALUE(dwLength, 8) exceeds the remaining bytes, exercising the
+// clamp in both WinCertIterInit and WinCertIterNext.
+//
+TEST (WinCertIterNextTest, LastEntryUnpaddedClampsToRemaining) {
+ const UINT32 Len = sizeof (WIN_CERTIFICATE) + 4; // 12; ALIGN(12,8)=16 > 12
+ std::vector Dir (Len, 0);
+
+ WIN_CERTIFICATE *Cert = (WIN_CERTIFICATE *)Dir.data ();
+
+ Cert->dwLength = Len;
+ Cert->wRevision = 0x0200;
+ Cert->wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
+
+ SyntheticImage Img = BuildImageWithDir (Dir);
+ WIN_CERT_ITER Iter;
+
+ ASSERT_EQ (
+ WinCertIterInit (&Iter, Img.FileBuffer.data (), Img.FileBuffer.size (), &Img.Dir),
+ EFI_SUCCESS
+ );
+
+ CONST WIN_CERTIFICATE *C = WinCertIterNext (&Iter);
+
+ ASSERT_NE (C, nullptr);
+ EXPECT_EQ (C->dwLength, Len);
+ EXPECT_EQ (WinCertIterNext (&Iter), nullptr);
+}
diff --git a/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/MeasurementGoogleTest.cpp b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/MeasurementGoogleTest.cpp
new file mode 100644
index 00000000000..207f2c1a538
--- /dev/null
+++ b/SecurityPkg/Library/DxeImageVerificationLib2/GoogleTest/MeasurementGoogleTest.cpp
@@ -0,0 +1,608 @@
+/** @file
+ Unit tests for the Secure Boot authority measurement helpers in
+ DxeImageVerificationLib (Measurement.c): GetMeasuredAuthorities,
+ AssignVariableName, AssignVendorGuid, IsSecureAuthorityVariable,
+ IsDataMeasured, AddDataMeasured, MeasureVariable, and SecureBootHook.
+
+ The de-duplication helpers are exercised against a caller-supplied
+ MEASURED_AUTHORITIES instance so no module-global state is relied upon,
+ and the PCR 7 extend path is exercised against a mocked
+ TpmMeasureAndLogData.
+
+ Copyright (C) Microsoft Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include
+#include
+
+#include