/** @file | |
X.509 Certificate Handler Wrapper Implementation over OpenSSL. | |
Copyright (c) 2010 - 2020, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "InternalCryptLib.h" | |
#include <openssl/x509.h> | |
#include <openssl/x509v3.h> | |
#include <crypto/asn1.h> | |
#include <openssl/asn1.h> | |
#include <openssl/rsa.h> | |
/* OID*/ | |
#define OID_EXT_KEY_USAGE { 0x55, 0x1D, 0x25 } | |
#define OID_BASIC_CONSTRAINTS { 0x55, 0x1D, 0x13 } | |
static CONST UINT8 mOidExtKeyUsage[] = OID_EXT_KEY_USAGE; | |
static CONST UINT8 mOidBasicConstraints[] = OID_BASIC_CONSTRAINTS; | |
#define CRYPTO_ASN1_TAG_CLASS_MASK 0xC0 | |
#define CRYPTO_ASN1_TAG_PC_MASK 0x20 | |
#define CRYPTO_ASN1_TAG_VALUE_MASK 0x1F | |
/** | |
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. | |
If this interface is not supported, 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[in] Args VA_LIST marker for the variable argument list. | |
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. | |
@retval FALSE This interface is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509ConstructCertificateStackV ( | |
IN OUT UINT8 **X509Stack, | |
IN VA_LIST Args | |
) | |
{ | |
UINT8 *Cert; | |
UINTN CertSize; | |
X509 *X509Cert; | |
STACK_OF (X509) *CertStack; | |
BOOLEAN Status; | |
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; | |
} | |
} | |
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); | |
} | |
if (!Status) { | |
sk_X509_pop_free (CertStack, X509_free); | |
} else { | |
*X509Stack = (UINT8 *)CertStack; | |
} | |
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 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, | |
... | |
) | |
{ | |
VA_LIST Args; | |
BOOLEAN Result; | |
VA_START (Args, X509Stack); | |
Result = X509ConstructCertificateStackV (X509Stack, Args); | |
VA_END (Args); | |
return Result; | |
} | |
/** | |
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 a string from one X.509 certificate base on the Request_NID. | |
@param[in] Cert Pointer to the DER-encoded X509 certificate. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[in] Request_NID NID of string to obtain | |
@param[out] CommonName Buffer to contain the retrieved certificate common | |
name string (UTF8). At most CommonNameSize bytes will be | |
written and the string will be null terminated. May be | |
NULL in order to determine the size buffer needed. | |
@param[in,out] CommonNameSize The size in bytes of the CommonName buffer on input, | |
and the size of buffer returned CommonName on output. | |
If CommonName is NULL then the amount of space needed | |
in buffer (including the final null) is returned. | |
@retval RETURN_SUCCESS The certificate CommonName retrieved successfully. | |
@retval RETURN_INVALID_PARAMETER If Cert is NULL. | |
If CommonNameSize is NULL. | |
If CommonName is not NULL and *CommonNameSize is 0. | |
If Certificate is invalid. | |
@retval RETURN_NOT_FOUND If no NID Name entry exists. | |
@retval RETURN_BUFFER_TOO_SMALL If the CommonName is NULL. The required buffer size | |
(including the final null) is returned in the | |
CommonNameSize parameter. | |
@retval RETURN_UNSUPPORTED The operation is not supported. | |
**/ | |
STATIC | |
RETURN_STATUS | |
InternalX509GetNIDName ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
IN INT32 Request_NID, | |
OUT CHAR8 *CommonName OPTIONAL, | |
IN OUT UINTN *CommonNameSize | |
) | |
{ | |
RETURN_STATUS ReturnStatus; | |
BOOLEAN Status; | |
X509 *X509Cert; | |
X509_NAME *X509Name; | |
INT32 Index; | |
INTN Length; | |
X509_NAME_ENTRY *Entry; | |
ASN1_STRING *EntryData; | |
UINT8 *UTF8Name; | |
ReturnStatus = RETURN_INVALID_PARAMETER; | |
UTF8Name = NULL; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (CertSize > INT_MAX) || (CommonNameSize == NULL)) { | |
return ReturnStatus; | |
} | |
if ((CommonName != NULL) && (*CommonNameSize == 0)) { | |
return ReturnStatus; | |
} | |
X509Cert = NULL; | |
// | |
// Read DER-encoded X509 Certificate and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
// | |
// Invalid X.509 Certificate | |
// | |
goto _Exit; | |
} | |
Status = FALSE; | |
// | |
// Retrieve subject name from certificate object. | |
// | |
X509Name = X509_get_subject_name (X509Cert); | |
if (X509Name == NULL) { | |
// | |
// Fail to retrieve subject name content | |
// | |
goto _Exit; | |
} | |
// | |
// Retrive the string from X.509 Subject base on the Request_NID | |
// | |
Index = X509_NAME_get_index_by_NID (X509Name, Request_NID, -1); | |
if (Index < 0) { | |
// | |
// No Request_NID name entry exists in X509_NAME object | |
// | |
*CommonNameSize = 0; | |
ReturnStatus = RETURN_NOT_FOUND; | |
goto _Exit; | |
} | |
Entry = X509_NAME_get_entry (X509Name, Index); | |
if (Entry == NULL) { | |
// | |
// Fail to retrieve name entry data | |
// | |
*CommonNameSize = 0; | |
ReturnStatus = RETURN_NOT_FOUND; | |
goto _Exit; | |
} | |
EntryData = X509_NAME_ENTRY_get_data (Entry); | |
Length = ASN1_STRING_to_UTF8 (&UTF8Name, EntryData); | |
if (Length < 0) { | |
// | |
// Fail to convert the Name string | |
// | |
*CommonNameSize = 0; | |
ReturnStatus = RETURN_INVALID_PARAMETER; | |
goto _Exit; | |
} | |
if (CommonName == NULL) { | |
*CommonNameSize = Length + 1; | |
ReturnStatus = RETURN_BUFFER_TOO_SMALL; | |
} else { | |
*CommonNameSize = MIN ((UINTN)Length, *CommonNameSize - 1) + 1; | |
CopyMem (CommonName, UTF8Name, *CommonNameSize - 1); | |
CommonName[*CommonNameSize - 1] = '\0'; | |
ReturnStatus = RETURN_SUCCESS; | |
} | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
if (UTF8Name != NULL) { | |
OPENSSL_free (UTF8Name); | |
} | |
return ReturnStatus; | |
} | |
/** | |
Retrieve the common name (CN) string 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] CommonName Buffer to contain the retrieved certificate common | |
name string. At most CommonNameSize bytes will be | |
written and the string will be null terminated. May be | |
NULL in order to determine the size buffer needed. | |
@param[in,out] CommonNameSize The size in bytes of the CommonName buffer on input, | |
and the size of buffer returned CommonName on output. | |
If CommonName is NULL then the amount of space needed | |
in buffer (including the final null) is returned. | |
@retval RETURN_SUCCESS The certificate CommonName retrieved successfully. | |
@retval RETURN_INVALID_PARAMETER If Cert is NULL. | |
If CommonNameSize is NULL. | |
If CommonName is not NULL and *CommonNameSize is 0. | |
If Certificate is invalid. | |
@retval RETURN_NOT_FOUND If no CommonName entry exists. | |
@retval RETURN_BUFFER_TOO_SMALL If the CommonName is NULL. The required buffer size | |
(including the final null) is returned in the | |
CommonNameSize parameter. | |
@retval RETURN_UNSUPPORTED The operation is not supported. | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
X509GetCommonName ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT CHAR8 *CommonName OPTIONAL, | |
IN OUT UINTN *CommonNameSize | |
) | |
{ | |
return InternalX509GetNIDName (Cert, CertSize, NID_commonName, CommonName, CommonNameSize); | |
} | |
/** | |
Retrieve the organization name (O) string 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] NameBuffer Buffer to contain the retrieved certificate organization | |
name string. At most NameBufferSize bytes will be | |
written and the string will be null terminated. May be | |
NULL in order to determine the size buffer needed. | |
@param[in,out] NameBufferSize The size in bytes of the Name buffer on input, | |
and the size of buffer returned Name on output. | |
If NameBuffer is NULL then the amount of space needed | |
in buffer (including the final null) is returned. | |
@retval RETURN_SUCCESS The certificate Organization Name retrieved successfully. | |
@retval RETURN_INVALID_PARAMETER If Cert is NULL. | |
If NameBufferSize is NULL. | |
If NameBuffer is not NULL and *CommonNameSize is 0. | |
If Certificate is invalid. | |
@retval RETURN_NOT_FOUND If no Organization Name entry exists. | |
@retval RETURN_BUFFER_TOO_SMALL If the NameBuffer is NULL. The required buffer size | |
(including the final null) is returned in the | |
CommonNameSize parameter. | |
@retval RETURN_UNSUPPORTED The operation is not supported. | |
**/ | |
RETURN_STATUS | |
EFIAPI | |
X509GetOrganizationName ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT CHAR8 *NameBuffer OPTIONAL, | |
IN OUT UINTN *NameBufferSize | |
) | |
{ | |
return InternalX509GetNIDName (Cert, CertSize, NID_organizationName, NameBuffer, NameBufferSize); | |
} | |
/** | |
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) || (EVP_PKEY_id (Pkey) != EVP_PKEY_RSA)) { | |
goto _Exit; | |
} | |
// | |
// Duplicate RSA Context from the retrieved EVP_PKEY. | |
// | |
if ((*RsaContext = RSAPublicKey_dup (EVP_PKEY_get0_RSA (Pkey))) != 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; | |
CertCtx = 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. | |
// | |
CertCtx = X509_STORE_CTX_new (); | |
if (CertCtx == NULL) { | |
goto _Exit; | |
} | |
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); | |
} | |
X509_STORE_CTX_free (CertCtx); | |
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; | |
UINT32 Asn1Tag; | |
UINT32 ObjClass; | |
UINTN Length; | |
UINTN Inf; | |
// | |
// 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; | |
Length = 0; | |
Inf = ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)CertSize); | |
if (((Inf & 0x80) == 0x80) || (Asn1Tag != V_ASN1_SEQUENCE)) { | |
return FALSE; | |
} | |
*TBSCert = (UINT8 *)Temp; | |
Inf = ASN1_get_object (&Temp, (long *)&Length, (int *)&Asn1Tag, (int *)&ObjClass, (long)Length); | |
// | |
// Verify the parsed TBSCertificate is one correct SEQUENCE data. | |
// | |
if (((Inf & 0x80) == 0x80) || (Asn1Tag != V_ASN1_SEQUENCE)) { | |
return FALSE; | |
} | |
*TBSCertSize = Length + (Temp - *TBSCert); | |
return TRUE; | |
} | |
/** | |
Retrieve the EC 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] EcContext Pointer to new-generated EC DSA context which contain the retrieved | |
EC public key component. Use EcFree() function to free the | |
resource. | |
If Cert is NULL, then return FALSE. | |
If EcContext is NULL, then return FALSE. | |
@retval TRUE EC Public Key was retrieved successfully. | |
@retval FALSE Fail to retrieve EC public key from X509 certificate. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EcGetPublicKeyFromX509 ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT VOID **EcContext | |
) | |
{ | |
BOOLEAN Status; | |
EVP_PKEY *Pkey; | |
X509 *X509Cert; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (EcContext == 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) || (EVP_PKEY_id (Pkey) != EVP_PKEY_EC)) { | |
goto _Exit; | |
} | |
// | |
// Duplicate EC Context from the retrieved EVP_PKEY. | |
// | |
if ((*EcContext = EC_KEY_dup (EVP_PKEY_get0_EC_KEY (Pkey))) != NULL) { | |
Status = TRUE; | |
} | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
if (Pkey != NULL) { | |
EVP_PKEY_free (Pkey); | |
} | |
return Status; | |
} | |
/** | |
Retrieve the version from one X.509 certificate. | |
If Cert is NULL, then return FALSE. | |
If CertSize is 0, then return FALSE. | |
If this interface is not supported, then return FALSE. | |
@param[in] Cert Pointer to the DER-encoded X509 certificate. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[out] Version Pointer to the retrieved version integer. | |
@retval TRUE The certificate version retrieved successfully. | |
@retval FALSE If Cert is NULL or CertSize is Zero. | |
@retval FALSE The operation is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetVersion ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINTN *Version | |
) | |
{ | |
BOOLEAN Status; | |
X509 *X509Cert; | |
X509Cert = NULL; | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
// | |
// Invalid X.509 Certificate | |
// | |
Status = FALSE; | |
} | |
if (Status) { | |
*Version = X509_get_version (X509Cert); | |
} | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
return Status; | |
} | |
/** | |
Retrieve the serialNumber from one X.509 certificate. | |
If Cert is NULL, then return FALSE. | |
If CertSize is 0, then return FALSE. | |
If this interface is not supported, then return FALSE. | |
@param[in] Cert Pointer to the DER-encoded X509 certificate. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[out] SerialNumber Pointer to the retrieved certificate SerialNumber bytes. | |
@param[in, out] SerialNumberSize The size in bytes of the SerialNumber buffer on input, | |
and the size of buffer returned SerialNumber on output. | |
@retval TRUE The certificate serialNumber retrieved successfully. | |
@retval FALSE If Cert is NULL or CertSize is Zero. | |
If SerialNumberSize is NULL. | |
If Certificate is invalid. | |
@retval FALSE If no SerialNumber exists. | |
@retval FALSE If the SerialNumber is NULL. The required buffer size | |
(including the final null) is returned in the | |
SerialNumberSize parameter. | |
@retval FALSE The operation is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetSerialNumber ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINT8 *SerialNumber, OPTIONAL | |
IN OUT UINTN *SerialNumberSize | |
) | |
{ | |
BOOLEAN Status; | |
X509 *X509Cert; | |
ASN1_INTEGER *Asn1Integer; | |
Status = FALSE; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (SerialNumberSize == NULL)) { | |
return Status; | |
} | |
X509Cert = NULL; | |
// | |
// Read DER-encoded X509 Certificate and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
*SerialNumberSize = 0; | |
Status = FALSE; | |
goto _Exit; | |
} | |
// | |
// Retrieve subject name from certificate object. | |
// | |
Asn1Integer = X509_get_serialNumber (X509Cert); | |
if (Asn1Integer == NULL) { | |
*SerialNumberSize = 0; | |
Status = FALSE; | |
goto _Exit; | |
} | |
if (*SerialNumberSize < (UINTN)Asn1Integer->length) { | |
*SerialNumberSize = (UINTN)Asn1Integer->length; | |
Status = FALSE; | |
goto _Exit; | |
} | |
if (SerialNumber != NULL) { | |
CopyMem (SerialNumber, Asn1Integer->data, *SerialNumberSize); | |
Status = TRUE; | |
} | |
*SerialNumberSize = (UINTN)Asn1Integer->length; | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
return Status; | |
} | |
/** | |
Retrieve the issuer bytes from one X.509 certificate. | |
If Cert is NULL, then return FALSE. | |
If CertIssuerSize is NULL, then return FALSE. | |
If this interface is not supported, then return FALSE. | |
@param[in] Cert Pointer to the DER-encoded X509 certificate. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[out] CertIssuer Pointer to the retrieved certificate subject bytes. | |
@param[in, out] CertIssuerSize The size in bytes of the CertIssuer buffer on input, | |
and the size of buffer returned CertSubject on output. | |
@retval TRUE The certificate issuer retrieved successfully. | |
@retval FALSE Invalid certificate, or the CertIssuerSize is too small for the result. | |
The CertIssuerSize will be updated with the required size. | |
@retval FALSE This interface is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetIssuerName ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINT8 *CertIssuer, | |
IN OUT UINTN *CertIssuerSize | |
) | |
{ | |
BOOLEAN Status; | |
X509 *X509Cert; | |
X509_NAME *X509Name; | |
UINTN X509NameSize; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (CertIssuerSize == 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 (*CertIssuerSize < X509NameSize) { | |
*CertIssuerSize = X509NameSize; | |
goto _Exit; | |
} | |
*CertIssuerSize = X509NameSize; | |
if (CertIssuer != NULL) { | |
i2d_X509_NAME (X509Name, &CertIssuer); | |
Status = TRUE; | |
} | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
return Status; | |
} | |
/** | |
Retrieve the Signature Algorithm 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] Oid Signature Algorithm Object identifier buffer. | |
@param[in,out] OidSize Signature Algorithm Object identifier buffer size | |
@retval TRUE The certificate Extension data retrieved successfully. | |
@retval FALSE If Cert is NULL. | |
If OidSize is NULL. | |
If Oid is not NULL and *OidSize is 0. | |
If Certificate is invalid. | |
@retval FALSE If no SignatureType. | |
@retval FALSE If the Oid is NULL. The required buffer size | |
is returned in the OidSize. | |
@retval FALSE The operation is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetSignatureAlgorithm ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINT8 *Oid, OPTIONAL | |
IN OUT UINTN *OidSize | |
) | |
{ | |
BOOLEAN Status; | |
X509 *X509Cert; | |
int Nid; | |
ASN1_OBJECT *Asn1Obj; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (OidSize == NULL) || (CertSize == 0)) { | |
return FALSE; | |
} | |
X509Cert = NULL; | |
Status = FALSE; | |
// | |
// Read DER-encoded X509 Certificate and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
Status = FALSE; | |
goto _Exit; | |
} | |
// | |
// Retrieve subject name from certificate object. | |
// | |
Nid = X509_get_signature_nid (X509Cert); | |
if (Nid == NID_undef) { | |
*OidSize = 0; | |
Status = FALSE; | |
goto _Exit; | |
} | |
Asn1Obj = OBJ_nid2obj (Nid); | |
if (Asn1Obj == NULL) { | |
*OidSize = 0; | |
Status = FALSE; | |
goto _Exit; | |
} | |
if (*OidSize < (UINTN)Asn1Obj->length) { | |
*OidSize = Asn1Obj->length; | |
Status = FALSE; | |
goto _Exit; | |
} | |
if (Oid != NULL) { | |
CopyMem (Oid, Asn1Obj->data, Asn1Obj->length); | |
} | |
*OidSize = Asn1Obj->length; | |
Status = TRUE; | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
return Status; | |
} | |
/** | |
Retrieve Extension data 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[in] Oid Object identifier buffer | |
@param[in] OidSize Object identifier buffer size | |
@param[out] ExtensionData Extension bytes. | |
@param[in, out] ExtensionDataSize Extension bytes size. | |
@retval TRUE The certificate Extension data retrieved successfully. | |
@retval FALSE If Cert is NULL. | |
If ExtensionDataSize is NULL. | |
If ExtensionData is not NULL and *ExtensionDataSize is 0. | |
If Certificate is invalid. | |
@retval FALSE If no Extension entry match Oid. | |
@retval FALSE If the ExtensionData is NULL. The required buffer size | |
is returned in the ExtensionDataSize parameter. | |
@retval FALSE The operation is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetExtensionData ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
IN CONST UINT8 *Oid, | |
IN UINTN OidSize, | |
OUT UINT8 *ExtensionData, | |
IN OUT UINTN *ExtensionDataSize | |
) | |
{ | |
BOOLEAN Status; | |
INTN i; | |
X509 *X509Cert; | |
CONST STACK_OF (X509_EXTENSION) *Extensions; | |
ASN1_OBJECT *Asn1Obj; | |
ASN1_OCTET_STRING *Asn1Oct; | |
X509_EXTENSION *Ext; | |
UINTN ObjLength; | |
UINTN OctLength; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (CertSize == 0) || (Oid == NULL) || (OidSize == 0) || (ExtensionDataSize == NULL)) { | |
return FALSE; | |
} | |
X509Cert = NULL; | |
Status = FALSE; | |
// | |
// Read DER-encoded X509 Certificate and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
*ExtensionDataSize = 0; | |
goto Cleanup; | |
} | |
// | |
// Retrieve Extensions from certificate object. | |
// | |
Extensions = X509_get0_extensions (X509Cert); | |
if (sk_X509_EXTENSION_num (Extensions) <= 0) { | |
*ExtensionDataSize = 0; | |
goto Cleanup; | |
} | |
// | |
// Traverse Extensions | |
// | |
Status = FALSE; | |
Asn1Oct = NULL; | |
OctLength = 0; | |
for (i = 0; i < sk_X509_EXTENSION_num (Extensions); i++) { | |
Ext = sk_X509_EXTENSION_value (Extensions, (int)i); | |
if (Ext == NULL) { | |
continue; | |
} | |
Asn1Obj = X509_EXTENSION_get_object (Ext); | |
if (Asn1Obj == NULL) { | |
continue; | |
} | |
Asn1Oct = X509_EXTENSION_get_data (Ext); | |
if (Asn1Oct == NULL) { | |
continue; | |
} | |
ObjLength = OBJ_length (Asn1Obj); | |
OctLength = ASN1_STRING_length (Asn1Oct); | |
if ((OidSize == ObjLength) && (CompareMem (OBJ_get0_data (Asn1Obj), Oid, OidSize) == 0)) { | |
// | |
// Extension Found | |
// | |
Status = TRUE; | |
break; | |
} | |
// | |
// reset to 0 if not found | |
// | |
OctLength = 0; | |
} | |
if (Status) { | |
if (*ExtensionDataSize < OctLength) { | |
*ExtensionDataSize = OctLength; | |
Status = FALSE; | |
goto Cleanup; | |
} | |
if (Asn1Oct != NULL) { | |
CopyMem (ExtensionData, ASN1_STRING_get0_data (Asn1Oct), OctLength); | |
} | |
*ExtensionDataSize = OctLength; | |
} else { | |
*ExtensionDataSize = 0; | |
} | |
Cleanup: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
return Status; | |
} | |
/** | |
Retrieve the Extended Key Usage 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] Usage Key Usage bytes. | |
@param[in, out] UsageSize Key Usage buffer sizs in bytes. | |
@retval TRUE The Usage bytes retrieve successfully. | |
@retval FALSE If Cert is NULL. | |
If CertSize is NULL. | |
If Usage is not NULL and *UsageSize is 0. | |
If Cert is invalid. | |
@retval FALSE If the Usage is NULL. The required buffer size | |
is returned in the UsageSize parameter. | |
@retval FALSE The operation is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetExtendedKeyUsage ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINT8 *Usage, | |
IN OUT UINTN *UsageSize | |
) | |
{ | |
BOOLEAN Status; | |
Status = X509GetExtensionData (Cert, CertSize, mOidExtKeyUsage, sizeof (mOidExtKeyUsage), Usage, UsageSize); | |
return Status; | |
} | |
/** | |
Retrieve the Validity from one X.509 certificate | |
If Cert is NULL, then return FALSE. | |
If CertIssuerSize is NULL, then return FALSE. | |
If this interface is not supported, then return FALSE. | |
@param[in] Cert Pointer to the DER-encoded X509 certificate. | |
@param[in] CertSize Size of the X509 certificate in bytes. | |
@param[out] From notBefore Pointer to DateTime object. | |
@param[in,out] FromSize notBefore DateTime object size. | |
@param[out] To notAfter Pointer to DateTime object. | |
@param[in,out] ToSize notAfter DateTime object size. | |
Note: X509CompareDateTime to compare DateTime oject | |
x509SetDateTime to get a DateTime object from a DateTimeStr | |
@retval TRUE The certificate Validity retrieved successfully. | |
@retval FALSE Invalid certificate, or Validity retrieve failed. | |
@retval FALSE This interface is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetValidity ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
IN UINT8 *From, | |
IN OUT UINTN *FromSize, | |
IN UINT8 *To, | |
IN OUT UINTN *ToSize | |
) | |
{ | |
BOOLEAN Status; | |
X509 *X509Cert; | |
CONST ASN1_TIME *F; | |
CONST ASN1_TIME *T; | |
UINTN TSize; | |
UINTN FSize; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (FromSize == NULL) || (ToSize == NULL) || (CertSize == 0)) { | |
return FALSE; | |
} | |
X509Cert = NULL; | |
Status = FALSE; | |
// | |
// Read DER-encoded X509 Certificate and Construct X509 object. | |
// | |
Status = X509ConstructCertificate (Cert, CertSize, (UINT8 **)&X509Cert); | |
if ((X509Cert == NULL) || (!Status)) { | |
goto _Exit; | |
} | |
// | |
// Retrieve Validity from/to from certificate object. | |
// | |
F = X509_get0_notBefore (X509Cert); | |
T = X509_get0_notAfter (X509Cert); | |
if ((F == NULL) || (T == NULL)) { | |
goto _Exit; | |
} | |
FSize = sizeof (ASN1_TIME) + F->length; | |
if (*FromSize < FSize) { | |
*FromSize = FSize; | |
goto _Exit; | |
} | |
*FromSize = FSize; | |
if (From != NULL) { | |
CopyMem (From, F, sizeof (ASN1_TIME)); | |
((ASN1_TIME *)From)->data = From + sizeof (ASN1_TIME); | |
CopyMem (From + sizeof (ASN1_TIME), F->data, F->length); | |
} | |
TSize = sizeof (ASN1_TIME) + T->length; | |
if (*ToSize < TSize) { | |
*ToSize = TSize; | |
goto _Exit; | |
} | |
*ToSize = TSize; | |
if (To != NULL) { | |
CopyMem (To, T, sizeof (ASN1_TIME)); | |
((ASN1_TIME *)To)->data = To + sizeof (ASN1_TIME); | |
CopyMem (To + sizeof (ASN1_TIME), T->data, T->length); | |
} | |
Status = TRUE; | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
return Status; | |
} | |
/** | |
Format a DateTimeStr to DataTime object in DataTime Buffer | |
If DateTimeStr is NULL, then return FALSE. | |
If DateTimeSize is NULL, then return FALSE. | |
If this interface is not supported, then return FALSE. | |
@param[in] DateTimeStr DateTime string like YYYYMMDDhhmmssZ | |
Ref: https://www.w3.org/TR/NOTE-datetime | |
Z stand for UTC time | |
@param[out] DateTime Pointer to a DateTime object. | |
@param[in,out] DateTimeSize DateTime object buffer size. | |
@retval TRUE The DateTime object create successfully. | |
@retval FALSE If DateTimeStr is NULL. | |
If DateTimeSize is NULL. | |
If DateTime is not NULL and *DateTimeSize is 0. | |
If Year Month Day Hour Minute Second combination is invalid datetime. | |
@retval FALSE If the DateTime is NULL. The required buffer size | |
(including the final null) is returned in the | |
DateTimeSize parameter. | |
@retval FALSE The operation is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509FormatDateTime ( | |
IN CONST CHAR8 *DateTimeStr, | |
OUT VOID *DateTime, | |
IN OUT UINTN *DateTimeSize | |
) | |
{ | |
BOOLEAN Status; | |
INT32 Ret; | |
ASN1_TIME *Dt; | |
UINTN DSize; | |
Dt = NULL; | |
Status = FALSE; | |
Dt = ASN1_TIME_new (); | |
if (Dt == NULL) { | |
Status = FALSE; | |
goto Cleanup; | |
} | |
Ret = ASN1_TIME_set_string_X509 (Dt, DateTimeStr); | |
if (Ret != 1) { | |
Status = FALSE; | |
goto Cleanup; | |
} | |
DSize = sizeof (ASN1_TIME) + Dt->length; | |
if (*DateTimeSize < DSize) { | |
*DateTimeSize = DSize; | |
Status = FALSE; | |
goto Cleanup; | |
} | |
*DateTimeSize = DSize; | |
if (DateTime != NULL) { | |
CopyMem (DateTime, Dt, sizeof (ASN1_TIME)); | |
((ASN1_TIME *)DateTime)->data = (UINT8 *)DateTime + sizeof (ASN1_TIME); | |
CopyMem ((UINT8 *)DateTime + sizeof (ASN1_TIME), Dt->data, Dt->length); | |
} | |
Status = TRUE; | |
Cleanup: | |
if (Dt != NULL) { | |
ASN1_TIME_free (Dt); | |
} | |
return Status; | |
} | |
/** | |
Compare DateTime1 object and DateTime2 object. | |
If DateTime1 is NULL, then return -2. | |
If DateTime2 is NULL, then return -2. | |
If DateTime1 == DateTime2, then return 0 | |
If DateTime1 > DateTime2, then return 1 | |
If DateTime1 < DateTime2, then return -1 | |
@param[in] DateTime1 Pointer to a DateTime Ojbect | |
@param[in] DateTime2 Pointer to a DateTime Object | |
@retval 0 If DateTime1 == DateTime2 | |
@retval 1 If DateTime1 > DateTime2 | |
@retval -1 If DateTime1 < DateTime2 | |
**/ | |
INT32 | |
EFIAPI | |
X509CompareDateTime ( | |
IN CONST VOID *DateTime1, | |
IN CONST VOID *DateTime2 | |
) | |
{ | |
return (INT32)ASN1_TIME_compare (DateTime1, DateTime2); | |
} | |
/** | |
Retrieve the Key Usage 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] Usage Key Usage (CRYPTO_X509_KU_*) | |
@retval TRUE The certificate Key Usage retrieved successfully. | |
@retval FALSE Invalid certificate, or Usage is NULL | |
@retval FALSE This interface is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetKeyUsage ( | |
IN CONST UINT8 *Cert, | |
IN UINTN CertSize, | |
OUT UINTN *Usage | |
) | |
{ | |
BOOLEAN Status; | |
X509 *X509Cert; | |
// | |
// Check input parameters. | |
// | |
if ((Cert == NULL) || (Usage == NULL)) { | |
return FALSE; | |
} | |
X509Cert = NULL; | |
Status = FALSE; | |
// | |
// 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. | |
// | |
*Usage = X509_get_key_usage (X509Cert); | |
if (*Usage == NID_undef) { | |
goto _Exit; | |
} | |
Status = TRUE; | |
_Exit: | |
// | |
// Release Resources. | |
// | |
if (X509Cert != NULL) { | |
X509_free (X509Cert); | |
} | |
return Status; | |
} | |
/** | |
Verify one X509 certificate was issued by the trusted CA. | |
@param[in] RootCert Trusted Root Certificate buffer | |
@param[in] RootCertLength Trusted Root Certificate buffer length | |
@param[in] CertChain One or more ASN.1 DER-encoded X.509 certificates | |
where the first certificate is signed by the Root | |
Certificate or is the Root Cerificate itself. and | |
subsequent cerificate is signed by the preceding | |
cerificate. | |
@param[in] CertChainLength Total length of the certificate chain, in bytes. | |
@retval TRUE All cerificates was issued by the first certificate in X509Certchain. | |
@retval FALSE Invalid certificate or the certificate was not issued by the given | |
trusted CA. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509VerifyCertChain ( | |
IN CONST UINT8 *RootCert, | |
IN UINTN RootCertLength, | |
IN CONST UINT8 *CertChain, | |
IN UINTN CertChainLength | |
) | |
{ | |
CONST UINT8 *TmpPtr; | |
UINTN Length; | |
UINT32 Asn1Tag; | |
UINT32 ObjClass; | |
CONST UINT8 *CurrentCert; | |
UINTN CurrentCertLen; | |
CONST UINT8 *PrecedingCert; | |
UINTN PrecedingCertLen; | |
BOOLEAN VerifyFlag; | |
INT32 Ret; | |
PrecedingCert = RootCert; | |
PrecedingCertLen = RootCertLength; | |
CurrentCert = CertChain; | |
Length = 0; | |
CurrentCertLen = 0; | |
VerifyFlag = FALSE; | |
while (TRUE) { | |
TmpPtr = CurrentCert; | |
Ret = ASN1_get_object ( | |
(CONST UINT8 **)&TmpPtr, | |
(long *)&Length, | |
(int *)&Asn1Tag, | |
(int *)&ObjClass, | |
(long)(CertChainLength + CertChain - TmpPtr) | |
); | |
if ((Asn1Tag != V_ASN1_SEQUENCE) || (Ret == 0x80)) { | |
break; | |
} | |
// | |
// Calculate CurrentCert length; | |
// | |
CurrentCertLen = TmpPtr - CurrentCert + Length; | |
// | |
// Verify CurrentCert with preceding cert; | |
// | |
VerifyFlag = X509VerifyCert (CurrentCert, CurrentCertLen, PrecedingCert, PrecedingCertLen); | |
if (VerifyFlag == FALSE) { | |
break; | |
} | |
// | |
// move Current cert to Preceding cert | |
// | |
PrecedingCertLen = CurrentCertLen; | |
PrecedingCert = CurrentCert; | |
// | |
// Move to next | |
// | |
CurrentCert = CurrentCert + CurrentCertLen; | |
} | |
return VerifyFlag; | |
} | |
/** | |
Get one X509 certificate from CertChain. | |
@param[in] CertChain One or more ASN.1 DER-encoded X.509 certificates | |
where the first certificate is signed by the Root | |
Certificate or is the Root Cerificate itself. and | |
subsequent cerificate is signed by the preceding | |
cerificate. | |
@param[in] CertChainLength Total length of the certificate chain, in bytes. | |
@param[in] CertIndex Index of certificate. | |
@param[out] Cert The certificate at the index of CertChain. | |
@param[out] CertLength The length certificate at the index of CertChain. | |
@retval TRUE Success. | |
@retval FALSE Failed to get certificate from certificate chain. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetCertFromCertChain ( | |
IN CONST UINT8 *CertChain, | |
IN UINTN CertChainLength, | |
IN CONST INT32 CertIndex, | |
OUT CONST UINT8 **Cert, | |
OUT UINTN *CertLength | |
) | |
{ | |
UINTN Asn1Len; | |
INT32 CurrentIndex; | |
UINTN CurrentCertLen; | |
CONST UINT8 *CurrentCert; | |
CONST UINT8 *TmpPtr; | |
INT32 Ret; | |
UINT32 Asn1Tag; | |
UINT32 ObjClass; | |
// | |
// Check input parameters. | |
// | |
if ((CertChain == NULL) || (Cert == NULL) || | |
(CertIndex < -1) || (CertLength == NULL)) | |
{ | |
return FALSE; | |
} | |
Asn1Len = 0; | |
CurrentCertLen = 0; | |
CurrentCert = CertChain; | |
CurrentIndex = -1; | |
// | |
// Traverse the certificate chain | |
// | |
while (TRUE) { | |
TmpPtr = CurrentCert; | |
// Get asn1 object and taglen | |
Ret = ASN1_get_object ( | |
(CONST UINT8 **)&TmpPtr, | |
(long *)&Asn1Len, | |
(int *)&Asn1Tag, | |
(int *)&ObjClass, | |
(long)(CertChainLength + CertChain - TmpPtr) | |
); | |
if ((Asn1Tag != V_ASN1_SEQUENCE) || (Ret == 0x80)) { | |
break; | |
} | |
// | |
// Calculate CurrentCert length; | |
// | |
CurrentCertLen = TmpPtr - CurrentCert + Asn1Len; | |
CurrentIndex++; | |
if (CurrentIndex == CertIndex) { | |
*Cert = CurrentCert; | |
*CertLength = CurrentCertLen; | |
return TRUE; | |
} | |
// | |
// Move to next | |
// | |
CurrentCert = CurrentCert + CurrentCertLen; | |
} | |
// | |
// If CertIndex is -1, Return the last certificate | |
// | |
if ((CertIndex == -1) && (CurrentIndex >= 0)) { | |
*Cert = CurrentCert - CurrentCertLen; | |
*CertLength = CurrentCertLen; | |
return TRUE; | |
} | |
return FALSE; | |
} | |
/** | |
Retrieve the tag and length of the tag. | |
@param Ptr The position in the ASN.1 data | |
@param End End of data | |
@param Length The variable that will receive the length | |
@param Tag The expected tag | |
@retval TRUE Get tag successful | |
@retval FALSe Failed to get tag or tag not match | |
**/ | |
BOOLEAN | |
EFIAPI | |
Asn1GetTag ( | |
IN OUT UINT8 **Ptr, | |
IN CONST UINT8 *End, | |
OUT UINTN *Length, | |
IN UINT32 Tag | |
) | |
{ | |
UINT8 *PtrOld; | |
INT32 ObjTag; | |
INT32 ObjCls; | |
long ObjLength; | |
UINT32 Inf; | |
// | |
// Save Ptr position | |
// | |
PtrOld = *Ptr; | |
Inf = ASN1_get_object ((CONST UINT8 **)Ptr, &ObjLength, &ObjTag, &ObjCls, (INT32)(End - (*Ptr))); | |
if (((Inf & 0x80) == 0x00) && | |
(ObjTag == (INT32)(Tag & CRYPTO_ASN1_TAG_VALUE_MASK)) && | |
(ObjCls == (INT32)(Tag & CRYPTO_ASN1_TAG_CLASS_MASK))) | |
{ | |
*Length = (UINTN)ObjLength; | |
return TRUE; | |
} else { | |
// | |
// if doesn't match Tag, restore Ptr to origin Ptr | |
// | |
*Ptr = PtrOld; | |
return FALSE; | |
} | |
} | |
/** | |
Retrieve the basic constraints 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] BasicConstraints basic constraints bytes. | |
@param[in, out] BasicConstraintsSize basic constraints buffer sizs in bytes. | |
@retval TRUE The basic constraints retrieve successfully. | |
@retval FALSE If cert is NULL. | |
If cert_size is NULL. | |
If basic_constraints is not NULL and *basic_constraints_size is 0. | |
If cert is invalid. | |
@retval FALSE The required buffer size is small. | |
The return buffer size is basic_constraints_size parameter. | |
@retval FALSE If no Extension entry match oid. | |
@retval FALSE The operation is not supported. | |
**/ | |
BOOLEAN | |
EFIAPI | |
X509GetExtendedBasicConstraints ( | |
CONST UINT8 *Cert, | |
UINTN CertSize, | |
UINT8 *BasicConstraints, | |
UINTN *BasicConstraintsSize | |
) | |
{ | |
BOOLEAN Status; | |
if ((Cert == NULL) || (CertSize == 0) || (BasicConstraintsSize == NULL)) { | |
return FALSE; | |
} | |
Status = X509GetExtensionData ( | |
(UINT8 *)Cert, | |
CertSize, | |
mOidBasicConstraints, | |
sizeof (mOidBasicConstraints), | |
BasicConstraints, | |
BasicConstraintsSize | |
); | |
return Status; | |
} |