From 3e5756512efd00beca8e76ea70d5924056aa4714 Mon Sep 17 00:00:00 2001 From: Steve Langasek Date: Wed, 29 Aug 2012 16:51:10 -0700 Subject: Import upstream version 0~20120728.3df9e294 --- Cryptlib/Pk/CryptX509.c | 554 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 Cryptlib/Pk/CryptX509.c (limited to 'Cryptlib/Pk/CryptX509.c') diff --git a/Cryptlib/Pk/CryptX509.c b/Cryptlib/Pk/CryptX509.c new file mode 100644 index 00000000..a0c5a2a7 --- /dev/null +++ b/Cryptlib/Pk/CryptX509.c @@ -0,0 +1,554 @@ +/** @file + X.509 Certificate Handler Wrapper Implementation over OpenSSL. + +Copyright (c) 2010 - 2012, Intel Corporation. All rights reserved.
+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 + + +/** + 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 + ) +{ + BIO *CertBio; + X509 *X509Cert; + BOOLEAN Status; + + // + // Check input parameters. + // + if (Cert == NULL || SingleX509Cert == NULL || CertSize > INT_MAX) { + return FALSE; + } + + Status = FALSE; + + // + // Read DER-encoded X509 Certificate and Construct X509 object. + // + CertBio = BIO_new (BIO_s_mem ()); + BIO_write (CertBio, Cert, (int) CertSize); + if (CertBio == NULL) { + goto _Exit; + } + X509Cert = d2i_X509_bio (CertBio, NULL); + if (X509Cert == NULL) { + goto _Exit; + } + + *SingleX509Cert = (UINT8 *) X509Cert; + Status = TRUE; + +_Exit: + // + // Release Resources. + // + BIO_free (CertBio); + + return Status; +} + +/** + 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 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); + + // + // Construct X509 Object from the given DER-encoded certificate data. + // + Status = X509ConstructCertificate ( + (CONST UINT8 *) Cert, + CertSize, + (UINT8 **) &X509Cert + ); + if (!Status) { + 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); +} + +/** + Pop single certificate from STACK_OF(X509). + + If X509Stack, Cert, or CertSize is NULL, then return FALSE. + + @param[in] X509Stack Pointer to a X509 stack object. + @param[out] Cert Pointer to a X509 certificate. + @param[out] CertSize Length of output X509 certificate in bytes. + + @retval TRUE The X509 stack pop succeeded. + @retval FALSE The pop operation failed. + +**/ +BOOLEAN +X509PopCertificate ( + IN VOID *X509Stack, + OUT UINT8 **Cert, + OUT UINTN *CertSize + ) +{ + BIO *CertBio; + X509 *X509Cert; + STACK_OF(X509) *CertStack; + BOOLEAN Status; + int Result; + int Length; + VOID *Buffer; + + Status = FALSE; + + if ((X509Stack == NULL) || (Cert == NULL) || (CertSize == NULL)) { + return Status; + } + + CertStack = (STACK_OF(X509) *) X509Stack; + + X509Cert = sk_X509_pop (CertStack); + + if (X509Cert == NULL) { + return Status; + } + + Buffer = NULL; + + CertBio = BIO_new (BIO_s_mem ()); + if (CertBio == NULL) { + return Status; + } + + Result = i2d_X509_bio (CertBio, X509Cert); + if (Result == 0) { + goto _Exit; + } + + Length = ((BUF_MEM *) CertBio->ptr)->length; + if (Length <= 0) { + goto _Exit; + } + + Buffer = malloc (Length); + if (Buffer == NULL) { + goto _Exit; + } + + Result = BIO_read (CertBio, Buffer, Length); + if (Result != Length) { + goto _Exit; + } + + *Cert = Buffer; + *CertSize = Length; + + Status = TRUE; + +_Exit: + + BIO_free (CertBio); + + if (!Status && (Buffer != NULL)) { + free (Buffer); + } + + return Status; +} + +/** + 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; + + // + // Check input parameters. + // + if (Cert == NULL || SubjectSize == NULL) { + return FALSE; + } + + Status = FALSE; + X509Cert = NULL; + + // + // Read DER-encoded X509 Certificate and Construct X509 object. + // + Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); + if ((X509Cert == NULL) || (!Status)) { + goto _Exit; + } + + // + // Retrieve subject name from certificate object. + // + X509Name = X509_get_subject_name (X509Cert); + if (*SubjectSize < (UINTN) X509Name->bytes->length) { + *SubjectSize = (UINTN) X509Name->bytes->length; + goto _Exit; + } + *SubjectSize = (UINTN) X509Name->bytes->length; + if (CertSubject != NULL) { + CopyMem (CertSubject, (UINT8 *)X509Name->bytes->data, *SubjectSize); + Status = TRUE; + } + +_Exit: + // + // Release Resources. + // + 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; + } + + Status = FALSE; + Pkey = NULL; + X509Cert = NULL; + + // + // Read DER-encoded X509 Certificate and Construct X509 object. + // + Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); + if ((X509Cert == NULL) || (!Status)) { + goto _Exit; + } + + // + // 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. + // + X509_free (X509Cert); + 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. + // + EVP_add_digest (EVP_md5()); + EVP_add_digest (EVP_sha1()); + EVP_add_digest (EVP_sha256()); + + // + // Read DER-encoded certificate to be verified and Construct X509 object. + // + Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert); + if ((X509Cert == NULL) || (!Status)) { + goto _Exit; + } + + // + // Read DER-encoded root certificate and Construct X509 object. + // + Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert); + if ((X509CACert == NULL) || (!Status)) { + goto _Exit; + } + + // + // 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; + } + + // + // 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. + // + X509_free (X509Cert); + X509_free (X509CACert); + X509_STORE_free (CertStore); + + return Status; +} -- cgit v1.2.3