mirror of
				https://git.proxmox.com/git/efi-boot-shim
				synced 2025-10-31 11:22:05 +00:00 
			
		
		
		
	 e571428e21
			
		
	
	
		e571428e21
		
	
	
	
	
		
			
			Also update Cryptlib to edk2 r19218 - Undefine NO_BUILTIN_VA_FUNCS in Cryptlib/OpenSSL/ for x86_64 to use the gcc builtins and remove all EFIAPI from the functions - Move the most of defines into the headers instead of Makefile - Remove the global variable 'timeval' - Remove the unused code: crypto/pqueue/* and crypto/ts/* - Include bn.h in MokManager.c due to the changes in openssl Signed-off-by: Gary Lin <glin@suse.com>
		
			
				
	
	
		
			581 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			581 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   X.509 Certificate Handler Wrapper Implementation over OpenSSL.
 | |
| 
 | |
| Copyright (c) 2010 - 2015, Intel Corporation. All rights reserved.<BR>
 | |
| This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions of the BSD License
 | |
| which accompanies this distribution.  The full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "InternalCryptLib.h"
 | |
| #include <openssl/x509.h>
 | |
| #include <openssl/rsa.h>
 | |
| 
 | |
| /**
 | |
|   Construct a X509 object from DER-encoded certificate data.
 | |
| 
 | |
|   If Cert is NULL, then return FALSE.
 | |
|   If SingleX509Cert is NULL, then return FALSE.
 | |
| 
 | |
|   @param[in]  Cert            Pointer to the DER-encoded certificate data.
 | |
|   @param[in]  CertSize        The size of certificate data in bytes.
 | |
|   @param[out] SingleX509Cert  The generated X509 object.
 | |
| 
 | |
|   @retval     TRUE            The X509 object generation succeeded.
 | |
|   @retval     FALSE           The operation failed.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| X509ConstructCertificate (
 | |
|   IN   CONST UINT8  *Cert,
 | |
|   IN   UINTN        CertSize,
 | |
|   OUT  UINT8        **SingleX509Cert
 | |
|   )
 | |
| {
 | |
|   X509         *X509Cert;
 | |
|   CONST UINT8  *Temp;
 | |
| 
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if (Cert == NULL || SingleX509Cert == NULL || CertSize > INT_MAX) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Read DER-encoded X509 Certificate and Construct X509 object.
 | |
|   //
 | |
|   Temp     = Cert;
 | |
|   X509Cert = d2i_X509 (NULL, &Temp, (long) CertSize);
 | |
|   if (X509Cert == NULL) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   *SingleX509Cert = (UINT8 *) X509Cert;
 | |
| 
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Construct a X509 stack object from a list of DER-encoded certificate data.
 | |
| 
 | |
|   If X509Stack is NULL, then return FALSE.
 | |
| 
 | |
|   @param[in, out]  X509Stack  On input, pointer to an existing or NULL X509 stack object.
 | |
|                               On output, pointer to the X509 stack object with new
 | |
|                               inserted X509 certificate.
 | |
|   @param           ...        A list of DER-encoded single certificate data followed
 | |
|                               by certificate size. A NULL terminates the list. The
 | |
|                               pairs are the arguments to X509ConstructCertificate().
 | |
|                                  
 | |
|   @retval     TRUE            The X509 stack construction succeeded.
 | |
|   @retval     FALSE           The construction operation failed.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| X509ConstructCertificateStack (
 | |
|   IN OUT  UINT8  **X509Stack,
 | |
|   ...  
 | |
|   )
 | |
| {
 | |
|   UINT8           *Cert;
 | |
|   UINTN           CertSize;
 | |
|   X509            *X509Cert;
 | |
|   STACK_OF(X509)  *CertStack;
 | |
|   BOOLEAN         Status;
 | |
|   VA_LIST         Args;
 | |
|   UINTN           Index;
 | |
| 
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if (X509Stack == NULL) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   Status = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Initialize X509 stack object.
 | |
|   //
 | |
|   CertStack = (STACK_OF(X509) *) (*X509Stack);
 | |
|   if (CertStack == NULL) {
 | |
|     CertStack = sk_X509_new_null ();
 | |
|     if (CertStack == NULL) {
 | |
|       return Status;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   VA_START (Args, X509Stack);
 | |
| 
 | |
|   for (Index = 0; ; Index++) {
 | |
|     //
 | |
|     // If Cert is NULL, then it is the end of the list.
 | |
|     //
 | |
|     Cert = VA_ARG (Args, UINT8 *);
 | |
|     if (Cert == NULL) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     CertSize = VA_ARG (Args, UINTN);
 | |
|     if (CertSize == 0) {
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Construct X509 Object from the given DER-encoded certificate data.
 | |
|     //
 | |
|     X509Cert = NULL;
 | |
|     Status = X509ConstructCertificate (
 | |
|                (CONST UINT8 *) Cert,
 | |
|                CertSize,
 | |
|                (UINT8 **) &X509Cert
 | |
|                );
 | |
|     if (!Status) {
 | |
|       if (X509Cert != NULL) {
 | |
|         X509_free (X509Cert);
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Insert the new X509 object into X509 stack object.
 | |
|     //
 | |
|     sk_X509_push (CertStack, X509Cert);
 | |
|   }
 | |
| 
 | |
|   VA_END (Args);
 | |
| 
 | |
|   if (!Status) {
 | |
|     sk_X509_pop_free (CertStack, X509_free);
 | |
|   } else {
 | |
|     *X509Stack = (UINT8 *) CertStack;
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Release the specified X509 object.
 | |
| 
 | |
|   If X509Cert is NULL, then return FALSE.
 | |
| 
 | |
|   @param[in]  X509Cert  Pointer to the X509 object to be released.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| X509Free (
 | |
|   IN  VOID  *X509Cert
 | |
|   )
 | |
| { 
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if (X509Cert == NULL) {
 | |
|     return;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Free OpenSSL X509 object.
 | |
|   //
 | |
|   X509_free ((X509 *) X509Cert);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Release the specified X509 stack object.
 | |
| 
 | |
|   If X509Stack is NULL, then return FALSE.
 | |
| 
 | |
|   @param[in]  X509Stack  Pointer to the X509 stack object to be released.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| X509StackFree (
 | |
|   IN  VOID  *X509Stack
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if (X509Stack == NULL) {
 | |
|     return;
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Free OpenSSL X509 stack object.
 | |
|   //
 | |
|   sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Retrieve the subject bytes from one X.509 certificate.
 | |
| 
 | |
|   @param[in]      Cert         Pointer to the DER-encoded X509 certificate.
 | |
|   @param[in]      CertSize     Size of the X509 certificate in bytes.
 | |
|   @param[out]     CertSubject  Pointer to the retrieved certificate subject bytes.
 | |
|   @param[in, out] SubjectSize  The size in bytes of the CertSubject buffer on input,
 | |
|                                and the size of buffer returned CertSubject on output.
 | |
| 
 | |
|   If Cert is NULL, then return FALSE.
 | |
|   If SubjectSize is NULL, then return FALSE.
 | |
| 
 | |
|   @retval  TRUE   The certificate subject retrieved successfully.
 | |
|   @retval  FALSE  Invalid certificate, or the SubjectSize is too small for the result.
 | |
|                   The SubjectSize will be updated with the required size.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| X509GetSubjectName (
 | |
|   IN      CONST UINT8  *Cert,
 | |
|   IN      UINTN        CertSize,
 | |
|   OUT     UINT8        *CertSubject,
 | |
|   IN OUT  UINTN        *SubjectSize
 | |
|   )
 | |
| {
 | |
|   BOOLEAN    Status;
 | |
|   X509       *X509Cert;
 | |
|   X509_NAME  *X509Name;
 | |
|   UINTN      X509NameSize;
 | |
| 
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if (Cert == NULL || SubjectSize == NULL) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   X509Cert = NULL;
 | |
| 
 | |
|   //
 | |
|   // Read DER-encoded X509 Certificate and Construct X509 object.
 | |
|   //
 | |
|   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
 | |
|   if ((X509Cert == NULL) || (!Status)) {
 | |
|     Status = FALSE;
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   Status = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Retrieve subject name from certificate object.
 | |
|   //
 | |
|   X509Name = X509_get_subject_name (X509Cert);
 | |
|   if (X509Name == NULL) {
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   X509NameSize = i2d_X509_NAME(X509Name, NULL);
 | |
|   if (*SubjectSize < X509NameSize) {
 | |
|     *SubjectSize = X509NameSize;
 | |
|     goto _Exit;
 | |
|   }
 | |
|   *SubjectSize = X509NameSize;
 | |
|   if (CertSubject != NULL) {
 | |
|     i2d_X509_NAME(X509Name, &CertSubject);
 | |
|     Status = TRUE;
 | |
|   }
 | |
| 
 | |
| _Exit:
 | |
|   //
 | |
|   // Release Resources.
 | |
|   //
 | |
|   if (X509Cert != NULL) {
 | |
|     X509_free (X509Cert);
 | |
|   }
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Retrieve the RSA Public Key from one DER-encoded X509 certificate.
 | |
| 
 | |
|   @param[in]  Cert         Pointer to the DER-encoded X509 certificate.
 | |
|   @param[in]  CertSize     Size of the X509 certificate in bytes.
 | |
|   @param[out] RsaContext   Pointer to new-generated RSA context which contain the retrieved
 | |
|                            RSA public key component. Use RsaFree() function to free the
 | |
|                            resource.
 | |
| 
 | |
|   If Cert is NULL, then return FALSE.
 | |
|   If RsaContext is NULL, then return FALSE.
 | |
| 
 | |
|   @retval  TRUE   RSA Public Key was retrieved successfully.
 | |
|   @retval  FALSE  Fail to retrieve RSA public key from X509 certificate.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| RsaGetPublicKeyFromX509 (
 | |
|   IN   CONST UINT8  *Cert,
 | |
|   IN   UINTN        CertSize,
 | |
|   OUT  VOID         **RsaContext
 | |
|   )
 | |
| {
 | |
|   BOOLEAN   Status;
 | |
|   EVP_PKEY  *Pkey;
 | |
|   X509      *X509Cert;
 | |
|   
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if (Cert == NULL || RsaContext == NULL) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   Pkey     = NULL;
 | |
|   X509Cert = NULL;
 | |
| 
 | |
|   //
 | |
|   // Read DER-encoded X509 Certificate and Construct X509 object.
 | |
|   //
 | |
|   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
 | |
|   if ((X509Cert == NULL) || (!Status)) {
 | |
|     Status = FALSE;
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   Status = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Retrieve and check EVP_PKEY data from X509 Certificate.
 | |
|   //
 | |
|   Pkey = X509_get_pubkey (X509Cert);
 | |
|   if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Duplicate RSA Context from the retrieved EVP_PKEY.
 | |
|   //
 | |
|   if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {
 | |
|     Status = TRUE;
 | |
|   }
 | |
| 
 | |
| _Exit:
 | |
|   //
 | |
|   // Release Resources.
 | |
|   //
 | |
|   if (X509Cert != NULL) {
 | |
|     X509_free (X509Cert);
 | |
|   }
 | |
| 
 | |
|   if (Pkey != NULL) {
 | |
|     EVP_PKEY_free (Pkey);
 | |
|   }  
 | |
| 
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Verify one X509 certificate was issued by the trusted CA.
 | |
| 
 | |
|   @param[in]      Cert         Pointer to the DER-encoded X509 certificate to be verified.
 | |
|   @param[in]      CertSize     Size of the X509 certificate in bytes.
 | |
|   @param[in]      CACert       Pointer to the DER-encoded trusted CA certificate.
 | |
|   @param[in]      CACertSize   Size of the CA Certificate in bytes.
 | |
| 
 | |
|   If Cert is NULL, then return FALSE.
 | |
|   If CACert is NULL, then return FALSE.
 | |
| 
 | |
|   @retval  TRUE   The certificate was issued by the trusted CA.
 | |
|   @retval  FALSE  Invalid certificate or the certificate was not issued by the given
 | |
|                   trusted CA.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| X509VerifyCert (
 | |
|   IN  CONST UINT8  *Cert,
 | |
|   IN  UINTN        CertSize,
 | |
|   IN  CONST UINT8  *CACert,
 | |
|   IN  UINTN        CACertSize
 | |
|   )
 | |
| {
 | |
|   BOOLEAN         Status;
 | |
|   X509            *X509Cert;
 | |
|   X509            *X509CACert;
 | |
|   X509_STORE      *CertStore;
 | |
|   X509_STORE_CTX  CertCtx;
 | |
|   
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if (Cert == NULL || CACert == NULL) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   Status     = FALSE;
 | |
|   X509Cert   = NULL;
 | |
|   X509CACert = NULL;
 | |
|   CertStore  = NULL;
 | |
| 
 | |
|   //
 | |
|   // Register & Initialize necessary digest algorithms for certificate verification.
 | |
|   //
 | |
|   if (EVP_add_digest (EVP_md5 ()) == 0) {
 | |
|     goto _Exit;
 | |
|   }
 | |
|   if (EVP_add_digest (EVP_sha1 ()) == 0) {
 | |
|     goto _Exit;
 | |
|   }
 | |
|   if (EVP_add_digest (EVP_sha256 ()) == 0) {
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Read DER-encoded certificate to be verified and Construct X509 object.
 | |
|   //
 | |
|   Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);
 | |
|   if ((X509Cert == NULL) || (!Status)) {
 | |
|     Status = FALSE;
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Read DER-encoded root certificate and Construct X509 object.
 | |
|   //
 | |
|   Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);
 | |
|   if ((X509CACert == NULL) || (!Status)) {
 | |
|     Status = FALSE;
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   Status = FALSE;
 | |
| 
 | |
|   //
 | |
|   // Set up X509 Store for trusted certificate.
 | |
|   //
 | |
|   CertStore = X509_STORE_new ();
 | |
|   if (CertStore == NULL) {
 | |
|     goto _Exit;
 | |
|   }
 | |
|   if (!(X509_STORE_add_cert (CertStore, X509CACert))) {
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Allow partial certificate chains, terminated by a non-self-signed but
 | |
|   // still trusted intermediate certificate. Also disable time checks.
 | |
|   //
 | |
|   X509_STORE_set_flags (CertStore,
 | |
|                         X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME);
 | |
| 
 | |
|   //
 | |
|   // Set up X509_STORE_CTX for the subsequent verification operation.
 | |
|   //
 | |
|   if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {
 | |
|     goto _Exit;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // X509 Certificate Verification.
 | |
|   //
 | |
|   Status = (BOOLEAN) X509_verify_cert (&CertCtx);
 | |
|   X509_STORE_CTX_cleanup (&CertCtx);
 | |
| 
 | |
| _Exit:
 | |
|   //
 | |
|   // Release Resources.
 | |
|   //
 | |
|   if (X509Cert != NULL) {
 | |
|     X509_free (X509Cert);
 | |
|   }
 | |
| 
 | |
|   if (X509CACert != NULL) {
 | |
|     X509_free (X509CACert);
 | |
|   }
 | |
| 
 | |
|   if (CertStore != NULL) {
 | |
|     X509_STORE_free (CertStore);
 | |
|   }
 | |
|   
 | |
|   return Status;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Retrieve the TBSCertificate from one given X.509 certificate.
 | |
| 
 | |
|   @param[in]      Cert         Pointer to the given DER-encoded X509 certificate.
 | |
|   @param[in]      CertSize     Size of the X509 certificate in bytes.
 | |
|   @param[out]     TBSCert      DER-Encoded To-Be-Signed certificate.
 | |
|   @param[out]     TBSCertSize  Size of the TBS certificate in bytes.
 | |
| 
 | |
|   If Cert is NULL, then return FALSE.
 | |
|   If TBSCert is NULL, then return FALSE.
 | |
|   If TBSCertSize is NULL, then return FALSE.
 | |
| 
 | |
|   @retval  TRUE   The TBSCertificate was retrieved successfully.
 | |
|   @retval  FALSE  Invalid X.509 certificate.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| EFIAPI
 | |
| X509GetTBSCert (
 | |
|   IN  CONST UINT8  *Cert,
 | |
|   IN  UINTN        CertSize,
 | |
|   OUT UINT8        **TBSCert,
 | |
|   OUT UINTN        *TBSCertSize
 | |
|   )
 | |
| {
 | |
|   CONST UINT8  *Temp;
 | |
|   INTN         Asn1Tag;
 | |
|   INTN         ObjClass;
 | |
|   UINTN        Length;
 | |
| 
 | |
|   //
 | |
|   // Check input parameters.
 | |
|   //
 | |
|   if ((Cert == NULL) || (TBSCert == NULL) ||
 | |
|       (TBSCertSize == NULL) || (CertSize > INT_MAX)) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // An X.509 Certificate is: (defined in RFC3280)
 | |
|   //   Certificate  ::=  SEQUENCE  {
 | |
|   //     tbsCertificate       TBSCertificate,
 | |
|   //     signatureAlgorithm   AlgorithmIdentifier,
 | |
|   //     signature            BIT STRING }
 | |
|   //
 | |
|   // and
 | |
|   //
 | |
|   //  TBSCertificate  ::=  SEQUENCE  {
 | |
|   //    version         [0]  Version DEFAULT v1,
 | |
|   //    ...
 | |
|   //    }
 | |
|   //
 | |
|   // So we can just ASN1-parse the x.509 DER-encoded data. If we strip
 | |
|   // the first SEQUENCE, the second SEQUENCE is the TBSCertificate.
 | |
|   //
 | |
|   Temp = Cert;
 | |
|   ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)CertSize);
 | |
| 
 | |
|   if (Asn1Tag != V_ASN1_SEQUENCE) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   *TBSCert = (UINT8 *)Temp;
 | |
| 
 | |
|   ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)Length);
 | |
|   //
 | |
|   // Verify the parsed TBSCertificate is one correct SEQUENCE data.
 | |
|   //
 | |
|   if (Asn1Tag != V_ASN1_SEQUENCE) {
 | |
|     return FALSE;
 | |
|   }
 | |
| 
 | |
|   *TBSCertSize = Length + (Temp - *TBSCert);
 | |
|   
 | |
|   return TRUE;
 | |
| }
 |