/** @file\r
X.509 Certificate Handler Wrapper Implementation over OpenSSL.\r
\r
-Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution. The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
#include "InternalCryptLib.h"\r
#include <openssl/x509.h>\r
-\r
+#include <openssl/rsa.h>\r
\r
/**\r
Construct a X509 object from DER-encoded certificate data.\r
OUT UINT8 **SingleX509Cert\r
)\r
{\r
- X509 *X509Cert;\r
+ X509 *X509Cert;\r
+ CONST UINT8 *Temp;\r
\r
//\r
// Check input parameters.\r
//\r
- if (Cert == NULL || SingleX509Cert == NULL || CertSize > INT_MAX) {\r
+ if ((Cert == NULL) || (SingleX509Cert == NULL) || (CertSize > INT_MAX)) {\r
return FALSE;\r
}\r
\r
//\r
// Read DER-encoded X509 Certificate and Construct X509 object.\r
//\r
- X509Cert = d2i_X509 (NULL, &Cert, (long) CertSize);\r
+ Temp = Cert;\r
+ X509Cert = d2i_X509 (NULL, &Temp, (long)CertSize);\r
if (X509Cert == NULL) {\r
return FALSE;\r
}\r
\r
- *SingleX509Cert = (UINT8 *) X509Cert;\r
+ *SingleX509Cert = (UINT8 *)X509Cert;\r
\r
return TRUE;\r
}\r
Construct a X509 stack object from a list of DER-encoded certificate data.\r
\r
If X509Stack is NULL, then return FALSE.\r
+ If this interface is not supported, then return FALSE.\r
\r
- @param[in, out] X509Stack On input, pointer to an existing X509 stack object.\r
+ @param[in, out] X509Stack On input, pointer to an existing or NULL X509 stack object.\r
On output, pointer to the X509 stack object with new\r
inserted X509 certificate.\r
- @param ... A list of DER-encoded single certificate data followed\r
+ @param[in] Args VA_LIST marker for the variable argument list.\r
+ A list of DER-encoded single certificate data followed\r
by certificate size. A NULL terminates the list. The\r
pairs are the arguments to X509ConstructCertificate().\r
- \r
+\r
@retval TRUE The X509 stack construction succeeded.\r
@retval FALSE The construction operation failed.\r
+ @retval FALSE This interface is not supported.\r
\r
**/\r
BOOLEAN\r
EFIAPI\r
-X509ConstructCertificateStack (\r
- IN OUT UINT8 **X509Stack,\r
- ... \r
+X509ConstructCertificateStackV (\r
+ IN OUT UINT8 **X509Stack,\r
+ IN VA_LIST Args\r
)\r
{\r
- UINT8 *Cert;\r
- UINTN CertSize;\r
- X509 *X509Cert;\r
- STACK_OF(X509) *CertStack;\r
- BOOLEAN Status;\r
- VA_LIST Args;\r
- UINTN Index;\r
+ UINT8 *Cert;\r
+ UINTN CertSize;\r
+ X509 *X509Cert;\r
+\r
+ STACK_OF (X509) *CertStack;\r
+ BOOLEAN Status;\r
+ UINTN Index;\r
\r
//\r
// Check input parameters.\r
//\r
// Initialize X509 stack object.\r
//\r
- CertStack = (STACK_OF(X509) *) (*X509Stack);\r
+ CertStack = (STACK_OF (X509) *)(*X509Stack);\r
if (CertStack == NULL) {\r
CertStack = sk_X509_new_null ();\r
if (CertStack == NULL) {\r
}\r
}\r
\r
- VA_START (Args, X509Stack);\r
-\r
for (Index = 0; ; Index++) {\r
//\r
// If Cert is NULL, then it is the end of the list.\r
}\r
\r
CertSize = VA_ARG (Args, UINTN);\r
+ if (CertSize == 0) {\r
+ break;\r
+ }\r
\r
//\r
// Construct X509 Object from the given DER-encoded certificate data.\r
//\r
- Status = X509ConstructCertificate (\r
- (CONST UINT8 *) Cert,\r
- CertSize,\r
- (UINT8 **) &X509Cert\r
- );\r
+ X509Cert = NULL;\r
+ Status = X509ConstructCertificate (\r
+ (CONST UINT8 *)Cert,\r
+ CertSize,\r
+ (UINT8 **)&X509Cert\r
+ );\r
if (!Status) {\r
- X509_free (X509Cert);\r
+ if (X509Cert != NULL) {\r
+ X509_free (X509Cert);\r
+ }\r
+\r
break;\r
}\r
\r
sk_X509_push (CertStack, X509Cert);\r
}\r
\r
- VA_END (Args);\r
-\r
if (!Status) {\r
sk_X509_pop_free (CertStack, X509_free);\r
} else {\r
- *X509Stack = (UINT8 *) CertStack;\r
+ *X509Stack = (UINT8 *)CertStack;\r
}\r
\r
return Status;\r
}\r
\r
+/**\r
+ Construct a X509 stack object from a list of DER-encoded certificate data.\r
+\r
+ If X509Stack is NULL, then return FALSE.\r
+\r
+ @param[in, out] X509Stack On input, pointer to an existing or NULL X509 stack object.\r
+ On output, pointer to the X509 stack object with new\r
+ inserted X509 certificate.\r
+ @param ... A list of DER-encoded single certificate data followed\r
+ by certificate size. A NULL terminates the list. The\r
+ pairs are the arguments to X509ConstructCertificate().\r
+\r
+ @retval TRUE The X509 stack construction succeeded.\r
+ @retval FALSE The construction operation failed.\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+X509ConstructCertificateStack (\r
+ IN OUT UINT8 **X509Stack,\r
+ ...\r
+ )\r
+{\r
+ VA_LIST Args;\r
+ BOOLEAN Result;\r
+\r
+ VA_START (Args, X509Stack);\r
+ Result = X509ConstructCertificateStackV (X509Stack, Args);\r
+ VA_END (Args);\r
+ return Result;\r
+}\r
+\r
/**\r
Release the specified X509 object.\r
\r
X509Free (\r
IN VOID *X509Cert\r
)\r
-{ \r
+{\r
//\r
// Check input parameters.\r
//\r
if (X509Cert == NULL) {\r
return;\r
}\r
- \r
+\r
//\r
// Free OpenSSL X509 object.\r
//\r
- X509_free ((X509 *) X509Cert);\r
+ X509_free ((X509 *)X509Cert);\r
}\r
\r
/**\r
if (X509Stack == NULL) {\r
return;\r
}\r
- \r
+\r
//\r
// Free OpenSSL X509 stack object.\r
//\r
- sk_X509_pop_free ((STACK_OF(X509) *) X509Stack, X509_free);\r
+ sk_X509_pop_free ((STACK_OF (X509) *) X509Stack, X509_free);\r
}\r
\r
/**\r
BOOLEAN Status;\r
X509 *X509Cert;\r
X509_NAME *X509Name;\r
+ UINTN X509NameSize;\r
\r
//\r
// Check input parameters.\r
//\r
- if (Cert == NULL || SubjectSize == NULL) {\r
+ if ((Cert == NULL) || (SubjectSize == NULL)) {\r
return FALSE;\r
}\r
\r
//\r
// Read DER-encoded X509 Certificate and Construct X509 object.\r
//\r
- Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
+ Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert);\r
if ((X509Cert == NULL) || (!Status)) {\r
Status = FALSE;\r
goto _Exit;\r
goto _Exit;\r
}\r
\r
- if (*SubjectSize < (UINTN) X509Name->bytes->length) {\r
- *SubjectSize = (UINTN) X509Name->bytes->length;\r
+ X509NameSize = i2d_X509_NAME (X509Name, NULL);\r
+ if (*SubjectSize < X509NameSize) {\r
+ *SubjectSize = X509NameSize;\r
goto _Exit;\r
}\r
- *SubjectSize = (UINTN) X509Name->bytes->length;\r
+\r
+ *SubjectSize = X509NameSize;\r
if (CertSubject != NULL) {\r
- CopyMem (CertSubject, (UINT8 *) X509Name->bytes->data, *SubjectSize);\r
+ i2d_X509_NAME (X509Name, &CertSubject);\r
Status = TRUE;\r
}\r
\r
return Status;\r
}\r
\r
+/**\r
+ Retrieve a string from one X.509 certificate base on the Request_NID.\r
+\r
+ @param[in] Cert Pointer to the DER-encoded X509 certificate.\r
+ @param[in] CertSize Size of the X509 certificate in bytes.\r
+ @param[in] Request_NID NID of string to obtain\r
+ @param[out] CommonName Buffer to contain the retrieved certificate common\r
+ name string (UTF8). At most CommonNameSize bytes will be\r
+ written and the string will be null terminated. May be\r
+ NULL in order to determine the size buffer needed.\r
+ @param[in,out] CommonNameSize The size in bytes of the CommonName buffer on input,\r
+ and the size of buffer returned CommonName on output.\r
+ If CommonName is NULL then the amount of space needed\r
+ in buffer (including the final null) is returned.\r
+\r
+ @retval RETURN_SUCCESS The certificate CommonName retrieved successfully.\r
+ @retval RETURN_INVALID_PARAMETER If Cert is NULL.\r
+ If CommonNameSize is NULL.\r
+ If CommonName is not NULL and *CommonNameSize is 0.\r
+ If Certificate is invalid.\r
+ @retval RETURN_NOT_FOUND If no NID Name entry exists.\r
+ @retval RETURN_BUFFER_TOO_SMALL If the CommonName is NULL. The required buffer size\r
+ (including the final null) is returned in the\r
+ CommonNameSize parameter.\r
+ @retval RETURN_UNSUPPORTED The operation is not supported.\r
+\r
+**/\r
+STATIC\r
+RETURN_STATUS\r
+InternalX509GetNIDName (\r
+ IN CONST UINT8 *Cert,\r
+ IN UINTN CertSize,\r
+ IN INT32 Request_NID,\r
+ OUT CHAR8 *CommonName OPTIONAL,\r
+ IN OUT UINTN *CommonNameSize\r
+ )\r
+{\r
+ RETURN_STATUS ReturnStatus;\r
+ BOOLEAN Status;\r
+ X509 *X509Cert;\r
+ X509_NAME *X509Name;\r
+ INT32 Index;\r
+ INTN Length;\r
+ X509_NAME_ENTRY *Entry;\r
+ ASN1_STRING *EntryData;\r
+ UINT8 *UTF8Name;\r
+\r
+ ReturnStatus = RETURN_INVALID_PARAMETER;\r
+ UTF8Name = NULL;\r
+\r
+ //\r
+ // Check input parameters.\r
+ //\r
+ if ((Cert == NULL) || (CertSize > INT_MAX) || (CommonNameSize == NULL)) {\r
+ return ReturnStatus;\r
+ }\r
+\r
+ if ((CommonName != NULL) && (*CommonNameSize == 0)) {\r
+ return ReturnStatus;\r
+ }\r
+\r
+ X509Cert = NULL;\r
+ //\r
+ // Read DER-encoded X509 Certificate and Construct X509 object.\r
+ //\r
+ Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert);\r
+ if ((X509Cert == NULL) || (!Status)) {\r
+ //\r
+ // Invalid X.509 Certificate\r
+ //\r
+ goto _Exit;\r
+ }\r
+\r
+ Status = FALSE;\r
+\r
+ //\r
+ // Retrieve subject name from certificate object.\r
+ //\r
+ X509Name = X509_get_subject_name (X509Cert);\r
+ if (X509Name == NULL) {\r
+ //\r
+ // Fail to retrieve subject name content\r
+ //\r
+ goto _Exit;\r
+ }\r
+\r
+ //\r
+ // Retrive the string from X.509 Subject base on the Request_NID\r
+ //\r
+ Index = X509_NAME_get_index_by_NID (X509Name, Request_NID, -1);\r
+ if (Index < 0) {\r
+ //\r
+ // No Request_NID name entry exists in X509_NAME object\r
+ //\r
+ *CommonNameSize = 0;\r
+ ReturnStatus = RETURN_NOT_FOUND;\r
+ goto _Exit;\r
+ }\r
+\r
+ Entry = X509_NAME_get_entry (X509Name, Index);\r
+ if (Entry == NULL) {\r
+ //\r
+ // Fail to retrieve name entry data\r
+ //\r
+ *CommonNameSize = 0;\r
+ ReturnStatus = RETURN_NOT_FOUND;\r
+ goto _Exit;\r
+ }\r
+\r
+ EntryData = X509_NAME_ENTRY_get_data (Entry);\r
+\r
+ Length = ASN1_STRING_to_UTF8 (&UTF8Name, EntryData);\r
+ if (Length < 0) {\r
+ //\r
+ // Fail to convert the Name string\r
+ //\r
+ *CommonNameSize = 0;\r
+ ReturnStatus = RETURN_INVALID_PARAMETER;\r
+ goto _Exit;\r
+ }\r
+\r
+ if (CommonName == NULL) {\r
+ *CommonNameSize = Length + 1;\r
+ ReturnStatus = RETURN_BUFFER_TOO_SMALL;\r
+ } else {\r
+ *CommonNameSize = MIN ((UINTN)Length, *CommonNameSize - 1) + 1;\r
+ CopyMem (CommonName, UTF8Name, *CommonNameSize - 1);\r
+ CommonName[*CommonNameSize - 1] = '\0';\r
+ ReturnStatus = RETURN_SUCCESS;\r
+ }\r
+\r
+_Exit:\r
+ //\r
+ // Release Resources.\r
+ //\r
+ if (X509Cert != NULL) {\r
+ X509_free (X509Cert);\r
+ }\r
+\r
+ if (UTF8Name != NULL) {\r
+ OPENSSL_free (UTF8Name);\r
+ }\r
+\r
+ return ReturnStatus;\r
+}\r
+\r
+/**\r
+ Retrieve the common name (CN) string from one X.509 certificate.\r
+\r
+ @param[in] Cert Pointer to the DER-encoded X509 certificate.\r
+ @param[in] CertSize Size of the X509 certificate in bytes.\r
+ @param[out] CommonName Buffer to contain the retrieved certificate common\r
+ name string. At most CommonNameSize bytes will be\r
+ written and the string will be null terminated. May be\r
+ NULL in order to determine the size buffer needed.\r
+ @param[in,out] CommonNameSize The size in bytes of the CommonName buffer on input,\r
+ and the size of buffer returned CommonName on output.\r
+ If CommonName is NULL then the amount of space needed\r
+ in buffer (including the final null) is returned.\r
+\r
+ @retval RETURN_SUCCESS The certificate CommonName retrieved successfully.\r
+ @retval RETURN_INVALID_PARAMETER If Cert is NULL.\r
+ If CommonNameSize is NULL.\r
+ If CommonName is not NULL and *CommonNameSize is 0.\r
+ If Certificate is invalid.\r
+ @retval RETURN_NOT_FOUND If no CommonName entry exists.\r
+ @retval RETURN_BUFFER_TOO_SMALL If the CommonName is NULL. The required buffer size\r
+ (including the final null) is returned in the\r
+ CommonNameSize parameter.\r
+ @retval RETURN_UNSUPPORTED The operation is not supported.\r
+\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+X509GetCommonName (\r
+ IN CONST UINT8 *Cert,\r
+ IN UINTN CertSize,\r
+ OUT CHAR8 *CommonName OPTIONAL,\r
+ IN OUT UINTN *CommonNameSize\r
+ )\r
+{\r
+ return InternalX509GetNIDName (Cert, CertSize, NID_commonName, CommonName, CommonNameSize);\r
+}\r
+\r
+/**\r
+ Retrieve the organization name (O) string from one X.509 certificate.\r
+\r
+ @param[in] Cert Pointer to the DER-encoded X509 certificate.\r
+ @param[in] CertSize Size of the X509 certificate in bytes.\r
+ @param[out] NameBuffer Buffer to contain the retrieved certificate organization\r
+ name string. At most NameBufferSize bytes will be\r
+ written and the string will be null terminated. May be\r
+ NULL in order to determine the size buffer needed.\r
+ @param[in,out] NameBufferSize The size in bytes of the Name buffer on input,\r
+ and the size of buffer returned Name on output.\r
+ If NameBuffer is NULL then the amount of space needed\r
+ in buffer (including the final null) is returned.\r
+\r
+ @retval RETURN_SUCCESS The certificate Organization Name retrieved successfully.\r
+ @retval RETURN_INVALID_PARAMETER If Cert is NULL.\r
+ If NameBufferSize is NULL.\r
+ If NameBuffer is not NULL and *CommonNameSize is 0.\r
+ If Certificate is invalid.\r
+ @retval RETURN_NOT_FOUND If no Organization Name entry exists.\r
+ @retval RETURN_BUFFER_TOO_SMALL If the NameBuffer is NULL. The required buffer size\r
+ (including the final null) is returned in the\r
+ CommonNameSize parameter.\r
+ @retval RETURN_UNSUPPORTED The operation is not supported.\r
+\r
+**/\r
+RETURN_STATUS\r
+EFIAPI\r
+X509GetOrganizationName (\r
+ IN CONST UINT8 *Cert,\r
+ IN UINTN CertSize,\r
+ OUT CHAR8 *NameBuffer OPTIONAL,\r
+ IN OUT UINTN *NameBufferSize\r
+ )\r
+{\r
+ return InternalX509GetNIDName (Cert, CertSize, NID_organizationName, NameBuffer, NameBufferSize);\r
+}\r
+\r
/**\r
Retrieve the RSA Public Key from one DER-encoded X509 certificate.\r
\r
BOOLEAN Status;\r
EVP_PKEY *Pkey;\r
X509 *X509Cert;\r
- \r
+\r
//\r
// Check input parameters.\r
//\r
- if (Cert == NULL || RsaContext == NULL) {\r
+ if ((Cert == NULL) || (RsaContext == NULL)) {\r
return FALSE;\r
}\r
\r
//\r
// Read DER-encoded X509 Certificate and Construct X509 object.\r
//\r
- Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
+ Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert);\r
if ((X509Cert == NULL) || (!Status)) {\r
Status = FALSE;\r
goto _Exit;\r
// Retrieve and check EVP_PKEY data from X509 Certificate.\r
//\r
Pkey = X509_get_pubkey (X509Cert);\r
- if ((Pkey == NULL) || (Pkey->type != EVP_PKEY_RSA)) {\r
+ if ((Pkey == NULL) || (EVP_PKEY_id (Pkey) != EVP_PKEY_RSA)) {\r
goto _Exit;\r
}\r
\r
//\r
// Duplicate RSA Context from the retrieved EVP_PKEY.\r
//\r
- if ((*RsaContext = RSAPublicKey_dup (Pkey->pkey.rsa)) != NULL) {\r
+ if ((*RsaContext = RSAPublicKey_dup (EVP_PKEY_get0_RSA (Pkey))) != NULL) {\r
Status = TRUE;\r
}\r
\r
\r
if (Pkey != NULL) {\r
EVP_PKEY_free (Pkey);\r
- } \r
+ }\r
\r
return Status;\r
}\r
X509 *X509Cert;\r
X509 *X509CACert;\r
X509_STORE *CertStore;\r
- X509_STORE_CTX CertCtx;\r
- \r
+ X509_STORE_CTX *CertCtx;\r
+\r
//\r
// Check input parameters.\r
//\r
- if (Cert == NULL || CACert == NULL) {\r
+ if ((Cert == NULL) || (CACert == NULL)) {\r
return FALSE;\r
}\r
\r
X509Cert = NULL;\r
X509CACert = NULL;\r
CertStore = NULL;\r
+ CertCtx = NULL;\r
\r
//\r
// Register & Initialize necessary digest algorithms for certificate verification.\r
if (EVP_add_digest (EVP_md5 ()) == 0) {\r
goto _Exit;\r
}\r
+\r
if (EVP_add_digest (EVP_sha1 ()) == 0) {\r
goto _Exit;\r
}\r
+\r
if (EVP_add_digest (EVP_sha256 ()) == 0) {\r
goto _Exit;\r
}\r
//\r
// Read DER-encoded certificate to be verified and Construct X509 object.\r
//\r
- Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **) &X509Cert);\r
+ Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert);\r
if ((X509Cert == NULL) || (!Status)) {\r
Status = FALSE;\r
goto _Exit;\r
//\r
// Read DER-encoded root certificate and Construct X509 object.\r
//\r
- Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **) &X509CACert);\r
+ Status = X509ConstructCertificate (CACert, CACertSize, (UINT8 **)&X509CACert);\r
if ((X509CACert == NULL) || (!Status)) {\r
Status = FALSE;\r
goto _Exit;\r
if (CertStore == NULL) {\r
goto _Exit;\r
}\r
+\r
if (!(X509_STORE_add_cert (CertStore, X509CACert))) {\r
goto _Exit;\r
}\r
\r
+ //\r
+ // Allow partial certificate chains, terminated by a non-self-signed but\r
+ // still trusted intermediate certificate. Also disable time checks.\r
+ //\r
+ X509_STORE_set_flags (\r
+ CertStore,\r
+ X509_V_FLAG_PARTIAL_CHAIN | X509_V_FLAG_NO_CHECK_TIME\r
+ );\r
+\r
//\r
// Set up X509_STORE_CTX for the subsequent verification operation.\r
//\r
- if (!X509_STORE_CTX_init (&CertCtx, CertStore, X509Cert, NULL)) {\r
+ CertCtx = X509_STORE_CTX_new ();\r
+ if (CertCtx == NULL) {\r
+ goto _Exit;\r
+ }\r
+\r
+ if (!X509_STORE_CTX_init (CertCtx, CertStore, X509Cert, NULL)) {\r
goto _Exit;\r
}\r
\r
//\r
// X509 Certificate Verification.\r
//\r
- Status = (BOOLEAN) X509_verify_cert (&CertCtx);\r
- X509_STORE_CTX_cleanup (&CertCtx);\r
+ Status = (BOOLEAN)X509_verify_cert (CertCtx);\r
+ X509_STORE_CTX_cleanup (CertCtx);\r
\r
_Exit:\r
//\r
if (CertStore != NULL) {\r
X509_STORE_free (CertStore);\r
}\r
- \r
+\r
+ X509_STORE_CTX_free (CertCtx);\r
+\r
return Status;\r
}\r
\r
)\r
{\r
CONST UINT8 *Temp;\r
- INTN Asn1Tag;\r
- INTN ObjClass;\r
+ UINT32 Asn1Tag;\r
+ UINT32 ObjClass;\r
UINTN Length;\r
\r
//\r
// Check input parameters.\r
//\r
- if ((Cert == NULL) || (TBSCert == NULL) || (TBSCertSize == NULL)) {\r
+ if ((Cert == NULL) || (TBSCert == NULL) ||\r
+ (TBSCertSize == NULL) || (CertSize > INT_MAX))\r
+ {\r
return FALSE;\r
}\r
\r
// So we can just ASN1-parse the x.509 DER-encoded data. If we strip\r
// the first SEQUENCE, the second SEQUENCE is the TBSCertificate.\r
//\r
- Temp = Cert;\r
+ Temp = Cert;\r
+ Length = 0;\r
ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)CertSize);\r
\r
if (Asn1Tag != V_ASN1_SEQUENCE) {\r
}\r
\r
*TBSCertSize = Length + (Temp - *TBSCert);\r
- \r
+\r
return TRUE;\r
}\r