From 40fa5cf2995e9de6c9853945428f407f208be1e1 Mon Sep 17 00:00:00 2001 From: Wenxing Hou Date: Tue, 12 Mar 2024 19:40:02 +0800 Subject: CryptoPkg: Add X509 functions based on Mbedtls REF: https://bugzilla.tianocore.org/show_bug.cgi?id=4177 X.509 Certificate Handler Wrapper Implementation over MbedTLS. Cc: Jiewen Yao Cc: Yi Li Signed-off-by: Wenxing Hou Reviewed-by: Yi Li Acked-by: Jiewen Yao --- .../Library/BaseCryptLibMbedTls/Pk/CryptX509.c | 1940 ++++++++++++++++++++ 1 file changed, 1940 insertions(+) create mode 100644 CryptoPkg/Library/BaseCryptLibMbedTls/Pk/CryptX509.c (limited to 'CryptoPkg') diff --git a/CryptoPkg/Library/BaseCryptLibMbedTls/Pk/CryptX509.c b/CryptoPkg/Library/BaseCryptLibMbedTls/Pk/CryptX509.c new file mode 100644 index 0000000..84b67c8 --- /dev/null +++ b/CryptoPkg/Library/BaseCryptLibMbedTls/Pk/CryptX509.c @@ -0,0 +1,1940 @@ +/** @file + X.509 Certificate Handler Wrapper Implementation over MbedTLS. + +Copyright (c) 2024, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "InternalCryptLib.h" +#include +#include +#include +#include +#include +#include + +/// +/// OID +/// +STATIC CONST UINT8 OID_commonName[] = { + 0x55, 0x04, 0x03 +}; +STATIC CONST UINT8 OID_organizationName[] = { + 0x55, 0x04, 0x0A +}; +STATIC CONST UINT8 OID_extKeyUsage[] = { + 0x55, 0x1D, 0x25 +}; +STATIC CONST UINT8 OID_BasicConstraints[] = { + 0x55, 0x1D, 0x13 +}; + +/* Profile for backward compatibility. Allows RSA 1024, unlike the default + profile. */ +STATIC mbedtls_x509_crt_profile gCompatProfile = +{ + /* Hashes from SHA-256 and above. Note that this selection + * should be aligned with ssl_preset_default_hashes in ssl_tls.c. */ + MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA256) | + MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA384) | + MBEDTLS_X509_ID_FLAG (MBEDTLS_MD_SHA512), + 0xFFFFFFF, /* Any PK alg */ + + /* Curves at or above 128-bit security level. Note that this selection + * should be aligned with ssl_preset_default_curves in ssl_tls.c. */ + MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP256R1) | + MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP384R1) | + MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_SECP521R1) | + MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP256R1) | + MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP384R1) | + MBEDTLS_X509_ID_FLAG (MBEDTLS_ECP_DP_BP512R1) | + 0, + 1024, +}; + +/** + 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 + ) +{ + mbedtls_x509_crt *MbedTlsCert; + INT32 Ret; + + if ((Cert == NULL) || (SingleX509Cert == NULL) || (CertSize == 0)) { + return FALSE; + } + + MbedTlsCert = AllocateZeroPool (sizeof (mbedtls_x509_crt)); + if (MbedTlsCert == NULL) { + return FALSE; + } + + mbedtls_x509_crt_init (MbedTlsCert); + + *SingleX509Cert = (UINT8 *)(VOID *)MbedTlsCert; + Ret = mbedtls_x509_crt_parse_der (MbedTlsCert, Cert, CertSize); + if (Ret == 0) { + return TRUE; + } else { + mbedtls_x509_crt_free (MbedTlsCert); + FreePool (MbedTlsCert); + return FALSE; + } +} + +/** + 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; + INT32 Index; + INT32 Ret; + mbedtls_x509_crt *Crt; + + if (X509Stack == NULL) { + return FALSE; + } + + Ret = 0; + Crt = NULL; + if (*X509Stack == NULL) { + Crt = AllocateZeroPool (sizeof (mbedtls_x509_crt)); + if (Crt == NULL) { + return FALSE; + } + + mbedtls_x509_crt_init (Crt); + *X509Stack = (UINT8 *)Crt; + } + + 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; + } + + Ret = mbedtls_x509_crt_parse_der ((mbedtls_x509_crt *)*X509Stack, Cert, CertSize); + + if (Ret != 0) { + break; + } + } + + if (Ret == 0) { + return TRUE; + } else { + if (Crt != NULL) { + mbedtls_x509_crt_free (Crt); + FreePool (Crt); + *X509Stack = NULL; + } + + return FALSE; + } +} + +/** + 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 + ) +{ + if (X509Cert != NULL) { + mbedtls_x509_crt_free (X509Cert); + FreePool (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 + ) +{ + if (X509Stack == NULL) { + return; + } + + mbedtls_x509_crt_free (X509Stack); +} + +/** + 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 + ) +{ + if (mbedtls_asn1_get_tag (Ptr, End, Length, (INT32)Tag) == 0) { + return TRUE; + } else { + return FALSE; + } +} + +/** + 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 + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + + if (Cert == NULL) { + return FALSE; + } + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + if (CertSubject != NULL) { + CopyMem (CertSubject, Crt.subject_raw.p, Crt.subject_raw.len); + } + + *SubjectSize = Crt.subject_raw.len; + } + + mbedtls_x509_crt_free (&Crt); + + return Ret == 0; +} + +/** + Retrieve a string from one X.509 certificate base on the Request_NID. + + @param[in] Name mbedtls_x509_name + @param[in] Oid Oid + @param[in] OidSize Size of Oid + @param[in,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. + +**/ +RETURN_STATUS +EFIAPI +InternalX509GetNIDName ( + IN mbedtls_x509_name *Name, + IN CHAR8 *Oid, + IN UINTN OidSize, + IN OUT CHAR8 *CommonName OPTIONAL, + IN OUT UINTN *CommonNameSize + ) +{ + CONST mbedtls_asn1_named_data *data; + + data = mbedtls_asn1_find_named_data (Name, Oid, OidSize); + if (data != NULL) { + if (*CommonNameSize <= data->val.len) { + *CommonNameSize = data->val.len + 1; + return RETURN_BUFFER_TOO_SMALL; + } + + if (CommonName != NULL) { + CopyMem (CommonName, data->val.p, data->val.len); + CommonName[data->val.len] = '\0'; + } + + *CommonNameSize = data->val.len + 1; + return RETURN_SUCCESS; + } else { + return RETURN_NOT_FOUND; + } +} + +/** + Get X509 SubjectNIDName by OID. + + @param[in] Cert certificate + @param[in] CertSize certificate size. + @param[in] Oid Oid + @param[in] OidSize Size of Oid + @param[in,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. + +**/ +RETURN_STATUS +EFIAPI +InternalX509GetSubjectNIDName ( + IN CONST UINT8 *Cert, + IN UINTN CertSize, + IN CHAR8 *Oid, + IN UINTN OidSize, + IN OUT CHAR8 *CommonName OPTIONAL, + IN OUT UINTN *CommonNameSize + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + mbedtls_x509_name *Name; + RETURN_STATUS ReturnStatus; + + if (Cert == NULL) { + return FALSE; + } + + ReturnStatus = RETURN_INVALID_PARAMETER; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + Name = &(Crt.subject); + ReturnStatus = InternalX509GetNIDName (Name, Oid, OidSize, CommonName, CommonNameSize); + } + + mbedtls_x509_crt_free (&Crt); + + return ReturnStatus; +} + +/** + Get X509 IssuerNIDName by OID. + + @param[in] Cert certificate + @param[in] CertSize certificate size. + @param[in] Oid Oid + @param[in] OidSize Size of Oid + @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. + +**/ +RETURN_STATUS +EFIAPI +InternalX509GetIssuerNIDName ( + IN CONST UINT8 *Cert, + IN UINTN CertSize, + IN CHAR8 *Oid, + IN UINTN OidSize, + OUT CHAR8 *CommonName OPTIONAL, + IN OUT UINTN *CommonNameSize + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + mbedtls_x509_name *Name; + RETURN_STATUS ReturnStatus; + + if (Cert == NULL) { + return FALSE; + } + + ReturnStatus = RETURN_INVALID_PARAMETER; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + Name = &(Crt.issuer); + ReturnStatus = InternalX509GetNIDName (Name, Oid, OidSize, CommonName, CommonNameSize); + } + + mbedtls_x509_crt_free (&Crt); + + 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 InternalX509GetSubjectNIDName (Cert, CertSize, (CHAR8 *)OID_commonName, sizeof (OID_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 InternalX509GetSubjectNIDName (Cert, CertSize, (CHAR8 *)OID_organizationName, sizeof (OID_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 + ) +{ + mbedtls_x509_crt Crt; + mbedtls_rsa_context *Rsa; + INT32 Ret; + + mbedtls_x509_crt_init (&Crt); + + if (mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize) != 0) { + return FALSE; + } + + if (mbedtls_pk_get_type (&Crt.pk) != MBEDTLS_PK_RSA) { + mbedtls_x509_crt_free (&Crt); + return FALSE; + } + + Rsa = RsaNew (); + if (Rsa == NULL) { + mbedtls_x509_crt_free (&Crt); + return FALSE; + } + + Ret = mbedtls_rsa_copy (Rsa, mbedtls_pk_rsa (Crt.pk)); + if (Ret != 0) { + RsaFree (Rsa); + mbedtls_x509_crt_free (&Crt); + return FALSE; + } + + mbedtls_x509_crt_free (&Crt); + + *RsaContext = Rsa; + 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 + ) +{ + ASSERT (FALSE); + return FALSE; +} + +/** + 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 + ) +{ + INT32 Ret; + mbedtls_x509_crt Ca; + mbedtls_x509_crt End; + UINT32 VFlag; + mbedtls_x509_crt_profile Profile; + + if ((Cert == NULL) || (CACert == NULL)) { + return FALSE; + } + + VFlag = 0; + CopyMem (&Profile, &gCompatProfile, sizeof (mbedtls_x509_crt_profile)); + + mbedtls_x509_crt_init (&Ca); + mbedtls_x509_crt_init (&End); + + Ret = mbedtls_x509_crt_parse_der (&Ca, CACert, CACertSize); + + if (Ret == 0) { + Ret = mbedtls_x509_crt_parse_der (&End, Cert, CertSize); + } + + if (Ret == 0) { + Ret = mbedtls_x509_crt_verify_with_profile (&End, &Ca, NULL, &Profile, NULL, &VFlag, NULL, NULL); + } + + mbedtls_x509_crt_free (&Ca); + mbedtls_x509_crt_free (&End); + + return Ret == 0; +} + +/** + 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 + ) +{ + UINTN Asn1Len; + UINTN PrecedingCertLen; + CONST UINT8 *PrecedingCert; + UINTN CurrentCertLen; + CONST UINT8 *CurrentCert; + CONST UINT8 *TmpPtr; + UINT32 Ret; + BOOLEAN VerifyFlag; + + VerifyFlag = FALSE; + PrecedingCert = RootCert; + PrecedingCertLen = RootCertLength; + + CurrentCert = CertChain; + + // + // Get Current certificate from Certificates buffer and Verify with preciding cert + // + do { + TmpPtr = CurrentCert; + Ret = mbedtls_asn1_get_tag ((UINT8 **)&TmpPtr, CertChain + CertChainLength, &Asn1Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (Ret != 0) { + break; + } + + CurrentCertLen = Asn1Len + (TmpPtr - CurrentCert); + + if (!X509VerifyCert (CurrentCert, CurrentCertLen, PrecedingCert, PrecedingCertLen)) { + VerifyFlag = FALSE; + break; + } else { + VerifyFlag = TRUE; + } + + // + // Save preceding certificate + // + PrecedingCert = CurrentCert; + PrecedingCertLen = CurrentCertLen; + + // + // Move current certificate to next; + // + CurrentCert = CurrentCert + CurrentCertLen; + } while (1); + + 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; + + // + // Check input parameters. + // + if ((CertChain == NULL) || (Cert == NULL) || + (CertIndex < -1) || (CertLength == NULL)) + { + return FALSE; + } + + CurrentCert = CertChain; + CurrentIndex = -1; + + // + // Traverse the certificate chain + // + while (TRUE) { + // + // Get asn1 tag len + // + TmpPtr = CurrentCert; + Ret = mbedtls_asn1_get_tag ((UINT8 **)&TmpPtr, CertChain + CertChainLength, &Asn1Len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (Ret != 0) { + break; + } + + CurrentCertLen = Asn1Len + (TmpPtr - CurrentCert); + 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 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 + ) +{ + UINTN Length; + UINTN Ret; + UINT8 *Ptr; + CONST UINT8 *Temp; + CONST UINT8 *End; + + // + // 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. + // + + Length = 0; + + Ptr = (UINT8 *)Cert; + End = Cert + CertSize; + + Ret = mbedtls_asn1_get_tag (&Ptr, End, &Length, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (Ret != 0) { + return FALSE; + } + + Temp = Ptr; + End = Ptr + Length; + Ret = mbedtls_asn1_get_tag (&Ptr, End, &Length, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (Ret != 0) { + return FALSE; + } + + *TBSCert = (UINT8 *)Temp; + *TBSCertSize = Length + (Ptr - Temp); + + return TRUE; +} + +/** + 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 + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + BOOLEAN ReturnStatus; + + if (Cert == NULL) { + return FALSE; + } + + ReturnStatus = FALSE; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + *Version = Crt.version - 1; + ReturnStatus = TRUE; + } + + mbedtls_x509_crt_free (&Crt); + + return ReturnStatus; +} + +/** + 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 + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + BOOLEAN ReturnStatus; + + if (Cert == NULL) { + return FALSE; + } + + ReturnStatus = FALSE; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + if (*SerialNumberSize <= Crt.serial.len) { + *SerialNumberSize = Crt.serial.len + 1; + ReturnStatus = FALSE; + goto Cleanup; + } + + if (SerialNumber != NULL) { + CopyMem (SerialNumber, Crt.serial.p, Crt.serial.len); + SerialNumber[Crt.serial.len] = '\0'; + } + + *SerialNumberSize = Crt.serial.len + 1; + ReturnStatus = TRUE; + } + +Cleanup: + mbedtls_x509_crt_free (&Crt); + + return ReturnStatus; +} + +/** + 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 + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + BOOLEAN Status; + + if (Cert == NULL) { + return FALSE; + } + + Status = FALSE; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + if (*CertIssuerSize < Crt.serial.len) { + *CertIssuerSize = Crt.serial.len; + Status = FALSE; + goto Cleanup; + } + + if (CertIssuer != NULL) { + CopyMem (CertIssuer, Crt.serial.p, Crt.serial.len); + } + + *CertIssuerSize = Crt.serial.len; + Status = TRUE; + } + +Cleanup: + mbedtls_x509_crt_free (&Crt); + + return Status; +} + +/** + Retrieve the issuer 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 issuer 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 Issuer 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 +X509GetIssuerCommonName ( + IN CONST UINT8 *Cert, + IN UINTN CertSize, + OUT CHAR8 *CommonName OPTIONAL, + IN OUT UINTN *CommonNameSize + ) +{ + return InternalX509GetIssuerNIDName (Cert, CertSize, (CHAR8 *)OID_commonName, sizeof (OID_commonName), CommonName, CommonNameSize); +} + +/** + Retrieve the issuer 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 issuer 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 issuer 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 +X509GetIssuerOrganizationName ( + IN CONST UINT8 *Cert, + IN UINTN CertSize, + OUT CHAR8 *NameBuffer OPTIONAL, + IN OUT UINTN *NameBufferSize + ) +{ + return InternalX509GetIssuerNIDName (Cert, CertSize, (CHAR8 *)OID_organizationName, sizeof (OID_organizationName), NameBuffer, NameBufferSize); +} + +/** + 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 + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + BOOLEAN ReturnStatus; + + if ((Cert == NULL) || (CertSize == 0) || (OidSize == NULL)) { + return FALSE; + } + + ReturnStatus = FALSE; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + if (*OidSize < Crt.sig_oid.len) { + *OidSize = Crt.serial.len; + ReturnStatus = FALSE; + goto Cleanup; + } + + if (Oid != NULL) { + CopyMem (Oid, Crt.sig_oid.p, Crt.sig_oid.len); + } + + *OidSize = Crt.sig_oid.len; + ReturnStatus = TRUE; + } + +Cleanup: + mbedtls_x509_crt_free (&Crt); + + return ReturnStatus; +} + +/** + Find first Extension data match with given OID + + @param[in] Start Pointer to the DER-encoded Extensions Data + @param[in] End Extensions Data size in bytes + @param[in ] Oid OID for match + @param[in ] OidSize OID size in bytes + @param[out] FindExtensionData output matched extension data. + @param[out] FindExtensionDataLen matched extension data size. + + **/ +STATIC +RETURN_STATUS +InternalX509FindExtensionData ( + UINT8 *Start, + UINT8 *End, + CONST UINT8 *Oid, + UINTN OidSize, + UINT8 **FindExtensionData, + UINTN *FindExtensionDataLen + ) +{ + UINT8 *Ptr; + UINT8 *ExtensionPtr; + size_t ObjLen; + INT32 Ret; + RETURN_STATUS ReturnStatus; + size_t FindExtensionLen; + size_t HeaderLen; + + ReturnStatus = RETURN_INVALID_PARAMETER; + Ptr = Start; + + Ret = 0; + + while (TRUE) { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + ExtensionPtr = Ptr; + Ret = mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (Ret == 0) { + HeaderLen = (size_t)(Ptr - ExtensionPtr); + FindExtensionLen = ObjLen; + // Get Object Identifier + Ret = mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OID); + } else { + break; + } + + if ((Ret == 0) && (CompareMem (Ptr, Oid, OidSize) == 0)) { + Ptr += ObjLen; + + Ret = mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_BOOLEAN); + if (Ret == 0) { + Ptr += ObjLen; + } + + Ret = mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_OCTET_STRING); + } else { + Ret = 1; + } + + if (Ret == 0) { + *FindExtensionData = Ptr; + *FindExtensionDataLen = ObjLen; + ReturnStatus = RETURN_SUCCESS; + break; + } + + // move to next + Ptr = ExtensionPtr + HeaderLen + FindExtensionLen; + Ret = 0; + } + + return ReturnStatus; +} + +/** + 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 + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + RETURN_STATUS ReturnStatus; + BOOLEAN Status; + UINT8 *Ptr; + UINT8 *End; + size_t ObjLen; + + if ((Cert == NULL) || + (CertSize == 0) || + (Oid == NULL) || + (OidSize == 0) || + (ExtensionDataSize == NULL)) + { + return FALSE; + } + + ReturnStatus = RETURN_INVALID_PARAMETER; + Status = FALSE; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + Ptr = Crt.v3_ext.p; + End = Crt.v3_ext.p + Crt.v3_ext.len; + Ret = mbedtls_asn1_get_tag (&Ptr, End, &ObjLen, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + } + + if (Ret == 0) { + ReturnStatus = InternalX509FindExtensionData (Ptr, End, Oid, OidSize, &Ptr, &ObjLen); + } + + if (ReturnStatus == RETURN_SUCCESS) { + if (*ExtensionDataSize < ObjLen) { + *ExtensionDataSize = ObjLen; + Status = FALSE; + goto Cleanup; + } + + if (Oid != NULL) { + CopyMem (ExtensionData, Ptr, ObjLen); + } + + *ExtensionDataSize = ObjLen; + Status = TRUE; + } + +Cleanup: + mbedtls_x509_crt_free (&Crt); + + 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[in] From notBefore Pointer to DateTime object. + @param[in,out] FromSize notBefore DateTime object size. + @param[in] 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 + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + BOOLEAN Status; + UINTN TSize; + UINTN FSize; + + if (Cert == NULL) { + return FALSE; + } + + Status = FALSE; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + FSize = sizeof (mbedtls_x509_time); + if (*FromSize < FSize) { + *FromSize = FSize; + goto _Exit; + } + + *FromSize = FSize; + if (From != NULL) { + CopyMem (From, &(Crt.valid_from), FSize); + } + + TSize = sizeof (mbedtls_x509_time); + if (*ToSize < TSize) { + *ToSize = TSize; + goto _Exit; + } + + *ToSize = TSize; + if (To != NULL) { + CopyMem (To, &(Crt.valid_to), sizeof (mbedtls_x509_time)); + } + + Status = TRUE; + } + +_Exit: + mbedtls_x509_crt_free (&Crt); + + return Status; +} + +/** + 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 + ) +{ + mbedtls_x509_crt Crt; + INT32 Ret; + BOOLEAN Status; + + if (Cert == NULL) { + return FALSE; + } + + Status = FALSE; + + mbedtls_x509_crt_init (&Crt); + + Ret = mbedtls_x509_crt_parse_der (&Crt, Cert, CertSize); + + if (Ret == 0) { + *Usage = Crt.key_usage; + Status = TRUE; + } + + mbedtls_x509_crt_free (&Crt); + + 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 ReturnStatus; + + if ((Cert == NULL) || (CertSize == 0) || (UsageSize == NULL)) { + return FALSE; + } + + ReturnStatus = X509GetExtensionData ((UINT8 *)Cert, CertSize, (UINT8 *)OID_extKeyUsage, sizeof (OID_extKeyUsage), Usage, UsageSize); + + return ReturnStatus; +} + +/** + Compare DateTime1 object and DateTime2 object time. + + @param[in] Before Pointer to a DateTime Ojbect + @param[in] After Pointer to a DateTime Object + + @retval 0 If DateTime1 <= DateTime2 + @retval 1 If DateTime1 > DateTime2 +**/ +STATIC +INTN +InternalX509CheckTime ( + CONST mbedtls_x509_time *Before, + CONST mbedtls_x509_time *After + ) +{ + if (Before->year > After->year) { + return (1); + } + + if ((Before->year == After->year) && + (Before->mon > After->mon)) + { + return (1); + } + + if ((Before->year == After->year) && + (Before->mon == After->mon) && + (Before->day > After->day)) + { + return (1); + } + + if ((Before->year == After->year) && + (Before->mon == After->mon) && + (Before->day == After->day) && + (Before->hour > After->hour)) + { + return (1); + } + + if ((Before->year == After->year) && + (Before->mon == After->mon) && + (Before->day == After->day) && + (Before->hour == After->hour) && + (Before->min > After->min)) + { + return (1); + } + + if ((Before->year == After->year) && + (Before->mon == After->mon) && + (Before->day == After->day) && + (Before->hour == After->hour) && + (Before->min == After->min) && + (Before->sec > After->sec)) + { + return (1); + } + + return (0); +} + +/** + change string to int. + + @param[in] PStart Pointer to a string Start + @param[in] PEnd Pointer to a string End + + @return number +**/ +STATIC +INT32 +InternalAtoI ( + CHAR8 *PStart, + CHAR8 *PEnd + ) +{ + CHAR8 *Ptr; + INT32 Knum; + + Knum = 0; + Ptr = PStart; + + while (Ptr < PEnd) { + /// + /// k = k * 2^3 + k * 2^1 = k * 8 + k * 2 = k * 10 + /// + Knum = (Knum << 3) + (Knum << 1) + (*Ptr) - '0'; + Ptr++; + } + + return Knum; +} + +/** + Format a DateTime object into 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[in,out] DateTime Pointer to a DateTime object. + @param[in,out] DateTimeSize DateTime object buffer size. + + @retval RETURN_SUCCESS The DateTime object create successfully. + @retval RETURN_INVALID_PARAMETER 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 RETURN_BUFFER_TOO_SMALL If the DateTime is NULL. The required buffer size + (including the final null) is returned in the + DateTimeSize parameter. + @retval RETURN_UNSUPPORTED The operation is not supported. +**/ +RETURN_STATUS +EFIAPI +X509SetDateTime ( + CHAR8 *DateTimeStr, + IN OUT VOID *DateTime, + IN OUT UINTN *DateTimeSize + ) +{ + mbedtls_x509_time Dt; + + INT32 Year; + INT32 Month; + INT32 Day; + INT32 Hour; + INT32 Minute; + INT32 Second; + RETURN_STATUS ReturnStatus; + CHAR8 *Ptr; + + Ptr = DateTimeStr; + + Year = InternalAtoI (Ptr, Ptr + 4); + Ptr += 4; + Month = InternalAtoI (Ptr, Ptr + 2); + Ptr += 2; + Day = InternalAtoI (Ptr, Ptr + 2); + Ptr += 2; + Hour = InternalAtoI (Ptr, Ptr + 2); + Ptr += 2; + Minute = InternalAtoI (Ptr, Ptr + 2); + Ptr += 2; + Second = InternalAtoI (Ptr, Ptr + 2); + Ptr += 2; + Dt.year = (int)Year; + Dt.mon = (int)Month; + Dt.day = (int)Day; + Dt.hour = (int)Hour; + Dt.min = (int)Minute; + Dt.sec = (int)Second; + + if (*DateTimeSize < sizeof (mbedtls_x509_time)) { + *DateTimeSize = sizeof (mbedtls_x509_time); + ReturnStatus = RETURN_BUFFER_TOO_SMALL; + goto Cleanup; + } + + if (DateTime != NULL) { + CopyMem (DateTime, &Dt, sizeof (mbedtls_x509_time)); + } + + *DateTimeSize = sizeof (mbedtls_x509_time); + ReturnStatus = RETURN_SUCCESS; +Cleanup: + return ReturnStatus; +} + +/** + 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 + ) +{ + if ((DateTime1 == NULL) || (DateTime2 == NULL)) { + return -2; + } + + if (CompareMem (DateTime2, DateTime1, sizeof (mbedtls_x509_time)) == 0) { + return 0; + } + + if (InternalX509CheckTime ((mbedtls_x509_time *)DateTime1, (mbedtls_x509_time *)DateTime2) == 0) { + return -1; + } else { + return 1; + } +} + +/** + 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, + OID_BasicConstraints, + sizeof (OID_BasicConstraints), + BasicConstraints, + BasicConstraintsSize + ); + + 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 + ) +{ + mbedtls_x509_time *Tm; + + if (*DateTimeSize < sizeof (mbedtls_x509_time)) { + return FALSE; + } + + if (DateTime == NULL) { + return FALSE; + } + + Tm = (mbedtls_x509_time *)DateTime; + + Tm->year = (DateTimeStr[0] + '0') * 1000 + (DateTimeStr[1] + '0') * 100 + + (DateTimeStr[2] + '0') * 10 + (DateTimeStr[3] + '0') * 1; + + Tm->mon = (DateTimeStr[4] + '0') * 10 + (DateTimeStr[5] + '0') * 1; + + Tm->day = (DateTimeStr[6] + '0') * 10 + (DateTimeStr[7] + '0') * 1; + + Tm->hour = (DateTimeStr[8] + '0') * 10 + (DateTimeStr[9] + '0') * 1; + + Tm->min = (DateTimeStr[10] + '0') * 10 + (DateTimeStr[11] + '0') * 1; + + Tm->sec = (DateTimeStr[12] + '0') * 10 + (DateTimeStr[13] + '0') * 1; + + return TRUE; +} -- cgit v1.1