/**
 *  Copyright Notice:
 *  Copyright 2021-2024 DMTF. All rights reserved.
 *  License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md
 **/

#ifndef CRYPTLIB_CERT_H
#define CRYPTLIB_CERT_H

#if LIBSPDM_CERT_PARSE_SUPPORT

/**
 * 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.
 **/
extern bool libspdm_asn1_get_tag(uint8_t **ptr, const uint8_t *end, size_t *length, uint32_t tag);

/**
 * Retrieve the subject bytes from one X.509 certificate.
 *
 * If cert is NULL, then return false.
 * If subject_size 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]      cert_size     Size of the X509 certificate in bytes.
 * @param[out]     cert_subject  Pointer to the retrieved certificate subject bytes.
 * @param[in, out] subject_size  The size in bytes of the cert_subject buffer on input,
 *                               and the size of buffer returned cert_subject on output.
 *
 * @retval  true   If the subject_size is not equal 0. The certificate subject retrieved successfully.
 * @retval  true   If the subject_size is equal 0. The certificate parse successful. But the cert doesn't have subject.
 * @retval  false  If the subject_size is not equal 0. The certificate subject retrieved successfully.But the subject_size is too small for the result.
 * @retval  false  If the subject_size is equal 0. Invalid certificate.
 **/
extern bool libspdm_x509_get_subject_name(const uint8_t *cert, size_t cert_size,
                                          uint8_t *cert_subject,
                                          size_t *subject_size);

/**
 * Retrieve the version from one X.509 certificate.
 *
 * If cert is NULL, then return false.
 * If cert_size 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]      cert_size  Size of the X509 certificate in bytes.
 * @param[out]     version    Pointer to the retrieved version integer.
 *
 * @retval  true
 * @retval  false
 **/
extern bool libspdm_x509_get_version(const uint8_t *cert, size_t cert_size, size_t *version);

/**
 * Retrieve the serialNumber from one X.509 certificate.
 *
 * If cert is NULL, then return false.
 * If cert_size 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]      cert_size           Size of the X509 certificate in bytes.
 * @param[out]     serial_number       Pointer to the retrieved certificate serial_number bytes.
 * @param[in, out] serial_number_size  The size in bytes of the serial_number buffer on input,
 *                                     and the size of buffer returned serial_number on output.
 *
 * @retval  true
 * @retval  false
 **/
extern bool libspdm_x509_get_serial_number(const uint8_t *cert, size_t cert_size,
                                           uint8_t *serial_number,
                                           size_t *serial_number_size);

#if LIBSPDM_ADDITIONAL_CHECK_CERT
/**
 * Retrieve the signature algorithm from one X.509 certificate.
 *
 * @param[in]      cert       Pointer to the DER-encoded X509 certificate.
 * @param[in]      cert_size  Size of the X509 certificate in bytes.
 * @param[out]     oid        Signature algorithm Object identifier buffer.
 * @param[in,out]  oid_size   Signature algorithm Object identifier buffer size.
 *
 * @retval  true    if the oid_size is equal 0, the cert parse successfully, but cert doesn't have signature algo.
 * @retval  true    if the oid_size is not equal 0, the cert parse and get signature algo successfully.
 * @retval  false   if the oid_size is equal 0, the cert parse failed.
 * @retval  false   if the oid_size is not equal 0, the cert parse and get signature algo successfully, but the input buffer size is small.
 **/
extern bool libspdm_x509_get_signature_algorithm(const uint8_t *cert,
                                                 size_t cert_size, uint8_t *oid,
                                                 size_t *oid_size);
#endif /* LIBSPDM_ADDITIONAL_CHECK_CERT */

/**
 * Retrieve the issuer bytes from one X.509 certificate.
 *
 * If cert is NULL, then return false.
 * If issuer_size 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]      cert_size    Size of the X509 certificate in bytes.
 * @param[out]     cert_issuer  Pointer to the retrieved certificate subject bytes.
 * @param[in, out] issuer_size  The size in bytes of the cert_issuer buffer on input,
 *                              and the size of buffer returned cert_issuer on output.
 *
 * @retval  true   If the issuer_size is not equal 0. The certificate issuer retrieved successfully.
 * @retval  true   If the issuer_size is equal 0. The certificate parse successful. But the cert doesn't have issuer.
 * @retval  false  If the issuer_size is not equal 0. The certificate issuer retrieved successfully. But the issuer_size is too small for the result.
 * @retval  false  If the issuer_size is equal 0. Invalid certificate.
 **/
extern bool libspdm_x509_get_issuer_name(const uint8_t *cert, size_t cert_size,
                                         uint8_t *cert_issuer,
                                         size_t *issuer_size);

/**
 * Retrieve Extension data from one X.509 certificate.
 *
 * @param[in]      cert                 Pointer to the DER-encoded X509 certificate.
 * @param[in]      cert_size            Size of the X509 certificate in bytes.
 * @param[in]      oid                  Object identifier buffer
 * @param[in]      oid_size             Object identifier buffer size
 * @param[out]     extension_data       Extension bytes.
 * @param[in, out] extension_data_size  Extension bytes size.
 *
 * @retval true   If the returned extension_data_size == 0, it means that cert and oid are valid, but the oid extension is not found;
 *                If the returned extension_data_size != 0, it means that cert and oid are valid, and the oid extension is found;
 * @retval false  If the returned extension_data_size == 0, it means that cert or oid are invalid;
 *                If the returned extension_data_size != 0, it means that cert and oid are valid, and the oid extension is found,
 *                                                          but the store buffer is too small.
 **/
extern bool libspdm_x509_get_extension_data(const uint8_t *cert, size_t cert_size,
                                            const uint8_t *oid, size_t oid_size,
                                            uint8_t *extension_data,
                                            size_t *extension_data_size);

/**
 * 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]      cert_size  Size of the X509 certificate in bytes.
 * @param[out]     from       notBefore Pointer to date_time object.
 * @param[in,out]  from_size  notBefore date_time object size.
 * @param[out]     to         notAfter Pointer to date_time object.
 * @param[in,out]  to_size    notAfter date_time object size.
 *
 * Note: libspdm_x509_compare_date_time to compare date_time oject
 *       x509SetDateTime to get a date_time object from a date_time_str
 *
 * @retval  true   if the from_size and from_size are not equal 0.
 *                 The certificate Validity retrieved successfully.
 * @retval  true   if the from_size and from_size are equal 0.
 *                 The certificate Validity does not exist.
 * @retval  false  if the from_size and from_size are not equal 0.
 *                 The certificate Validity retrieved successfully, but the input buffer size is small.
 * @retval  false  if the from_size and from_size are equal 0.
 *                 Invalid certificate, or Validity retrieve failed.
 **/
extern bool libspdm_x509_get_validity(const uint8_t *cert, size_t cert_size,
                                      uint8_t *from, size_t *from_size, uint8_t *to,
                                      size_t *to_size);

/**
 * Format a date_time object into DataTime buffer
 *
 * If date_time_str is NULL, then return false.
 * If date_time_size is NULL, then return false.
 * If this interface is not supported, then return false.
 *
 * @param[in]      date_time_str    date_time string like YYYYMMDDhhmmssZ
 *                                  Ref: https://www.w3.org/TR/NOTE-datetime
 *                                  Z stand for UTC time
 * @param[out]     date_time       Pointer to a date_time object.
 * @param[in,out]  date_time_size  date_time object buffer size.
 *
 * @retval  true
 * @retval  false
 **/
extern bool libspdm_x509_set_date_time(const char *date_time_str, void *date_time,
                                       size_t *date_time_size);

/**
 * Compare date_time1 object and date_time2 object.
 *
 * If date_time1 is NULL, then return -2.
 * If date_time2 is NULL, then return -2.
 * If date_time1 == date_time2, then return 0
 * If date_time1 > date_time2, then return 1
 * If date_time1 < date_time2, then return -1
 *
 * @param[in]      date_time1  Pointer to a date_time Ojbect
 * @param[in]      date_time2  Pointer to a date_time Object
 *
 * @retval   0  If date_time1 == date_time2
 * @retval   1  If date_time1 > date_time2
 * @retval  -1  If date_time1 < date_time2
 **/
extern int32_t libspdm_x509_compare_date_time(const void *date_time1, const void *date_time2);

/**
 * Retrieve the key usage from one X.509 certificate.
 *
 * @param[in]      cert       Pointer to the DER-encoded X509 certificate.
 * @param[in]      cert_size  Size of the X509 certificate in bytes.
 * @param[out]     usage      Key usage (LIBSPDM_CRYPTO_X509_KU_*)
 *
 * @retval  true   if the usage is no equal 0. The certificate key usage retrieved successfully.
 * @retval  true   if the usage is equal 0. The certificate parse successfully, but the cert doesn't have key usage.
 * @retval  false  Invalid certificate, or usage is NULL.
 **/
extern bool libspdm_x509_get_key_usage(const uint8_t *cert, size_t cert_size, size_t *usage);

/**
 * Retrieve the Extended key usage from one X.509 certificate.
 *
 * @param[in]      cert        Pointer to the DER-encoded X509 certificate.
 * @param[in]      cert_size   Size of the X509 certificate in bytes.
 * @param[out]     usage       Key usage bytes.
 * @param[in, out] usage_size  Key usage buffer size in bytes.
 *
 * @retval true   If the returned usage_size == 0, it means that cert and oid are valid, but the Extended key usage is not found;
 *                If the returned usage_size != 0, it means that cert and oid are valid, and the Extended key usage is found;
 * @retval false  If the returned usage_size == 0, it means that cert or oid are invalid;
 *                If the returned usage_size != 0, it means that cert and oid are valid, and the Extended key usage is found,
 *                                                 but the store buffer is too small.
 **/
extern bool libspdm_x509_get_extended_key_usage(const uint8_t *cert,
                                                size_t cert_size, uint8_t *usage,
                                                size_t *usage_size);

/**
 * Retrieve the basic constraints from one X.509 certificate.
 *
 * @param[in]      cert                     Pointer to the DER-encoded X509 certificate.
 * @param[in]      cert_size                Size of the X509 certificate in bytes.
 * @param[out]     basic_constraints        Basic constraints bytes.
 * @param[in, out] basic_constraints_size   Basic constraints buffer size in bytes.
 *
 * @retval true   If the returned basic_constraints_size == 0, it means that cert and oid are valid, but the basic_constraints is not found;
 *                If the returned basic_constraints_size != 0, it means that cert and oid are valid, and the basic_constraints is found;
 * @retval false  If the returned basic_constraints_size == 0, it means that cert or oid are invalid;
 *                If the returned basic_constraints_size != 0, it means that cert and oid are valid, and the basic_constraints is found,
 *                                                             but the store buffer is too small.
 **/
extern bool libspdm_x509_get_extended_basic_constraints(const uint8_t *cert,
                                                        size_t cert_size,
                                                        uint8_t *basic_constraints,
                                                        size_t *basic_constraints_size);

/**
 * Verify one X509 certificate was issued by the trusted CA.
 *
 * If cert is NULL, then return false.
 * If ca_cert is NULL, then return false.
 * If this interface is not supported, then return false.
 *
 * @param[in]      cert          Pointer to the DER-encoded X509 certificate to be verified.
 * @param[in]      cert_size     Size of the X509 certificate in bytes.
 * @param[in]      ca_cert       Pointer to the DER-encoded trusted CA certificate.
 * @param[in]      ca_cert_size  Size of the CA Certificate in bytes.
 *
 * @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.
 * @retval  false  This interface is not supported.
 *
 **/
extern bool libspdm_x509_verify_cert(const uint8_t *cert, size_t cert_size,
                                     const uint8_t *ca_cert, size_t ca_cert_size);

/**
 * Verify one X509 certificate was issued by the trusted CA.
 *
 * @param[in]      cert_chain         One or more ASN.1 DER-encoded X.509 certificates
 *                                    where the first certificate is signed by the Root
 *                                    Certificate or is the Root Certificate itself. and
 *                                    subsequent certificate is signed by the preceding
 *                                    certificate.
 * @param[in]      cert_chain_length  Total length of the certificate chain, in bytes.
 *
 * @param[in]      root_cert          Trusted Root Certificate buffer.
 *
 * @param[in]      root_cert_length   Trusted Root Certificate buffer length.
 *
 * @retval  true   All certificates were issued by the first certificate in X509Certchain.
 * @retval  false  Invalid certificate or the certificate was not issued by the given
 *                 trusted CA.
 **/
extern bool libspdm_x509_verify_cert_chain(const uint8_t *root_cert, size_t root_cert_length,
                                           const uint8_t *cert_chain,
                                           size_t cert_chain_length);

/**
 * Get one X509 certificate from cert_chain.
 *
 * @param[in]      cert_chain         One or more ASN.1 DER-encoded X.509 certificates
 *                                    where the first certificate is signed by the Root
 *                                    Certificate or is the Root Certificate itself. and
 *                                    subsequent certificate is signed by the preceding
 *                                    certificate.
 * @param[in]      cert_chain_length  Total length of the certificate chain, in bytes.
 *
 * @param[in]      cert_index         Index of certificate. If index is -1 indicates the
 *                                    last certificate in cert_chain.
 *
 * @param[out]     cert              The certificate at the index of cert_chain.
 * @param[out]     cert_length       The length certificate at the index of cert_chain.
 *
 * @retval  true   Success.
 * @retval  false  Failed to get certificate from certificate chain.
 **/
extern bool libspdm_x509_get_cert_from_cert_chain(const uint8_t *cert_chain,
                                                  size_t cert_chain_length,
                                                  const int32_t cert_index, const uint8_t **cert,
                                                  size_t *cert_length);

#if (LIBSPDM_RSA_SSA_SUPPORT) || (LIBSPDM_RSA_PSS_SUPPORT)
/**
 * Retrieve the RSA public key from one DER-encoded X509 certificate.
 *
 * If cert is NULL, then return false.
 * If rsa_context 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]  cert_size    Size of the X509 certificate in bytes.
 * @param[out] rsa_context  Pointer to newly generated RSA context which contain the retrieved
 *                          RSA public key component. Use libspdm_rsa_free() function to free the
 *                          resource.
 *
 * @retval  true   RSA public key was retrieved successfully.
 * @retval  false  Fail to retrieve RSA public key from X509 certificate.
 * @retval  false  This interface is not supported.
 **/
extern bool libspdm_rsa_get_public_key_from_x509(const uint8_t *cert, size_t cert_size,
                                                 void **rsa_context);
#endif /* (LIBSPDM_RSA_SSA_SUPPORT) || (LIBSPDM_RSA_PSS_SUPPORT) */

#if LIBSPDM_ECDSA_SUPPORT
/**
 * Retrieve the EC public key from one DER-encoded X509 certificate.
 *
 * @param[in]  cert        Pointer to the DER-encoded X509 certificate.
 * @param[in]  cert_size   Size of the X509 certificate in bytes.
 * @param[out] ec_context  Pointer to newly generated EC DSA context which contain the retrieved
 *                         EC public key component. Use libspdm_ec_free() function to free the
 *                         resource.
 *
 * If cert is NULL, then return false.
 * If ec_context is NULL, then return false.
 *
 * @retval  true   EC public key was retrieved successfully.
 * @retval  false  Fail to retrieve EC public key from X509 certificate.
 *
 **/
extern bool libspdm_ec_get_public_key_from_x509(const uint8_t *cert, size_t cert_size,
                                                void **ec_context);
#endif /* LIBSPDM_ECDSA_SUPPORT */

#if (LIBSPDM_EDDSA_ED25519_SUPPORT) || (LIBSPDM_EDDSA_ED448_SUPPORT)
/**
 * Retrieve the Ed public key from one DER-encoded X509 certificate.
 *
 * @param[in]  cert         Pointer to the DER-encoded X509 certificate.
 * @param[in]  cert_size    Size of the X509 certificate in bytes.
 * @param[out] ecd_context  Pointer to newly generated Ed DSA context which contain the retrieved
 *                          Ed public key component. Use libspdm_ecd_free() function to free the
 *                          resource.
 *
 * If cert is NULL, then return false.
 * If ecd_context is NULL, then return false.
 *
 * @retval  true   Ed public key was retrieved successfully.
 * @retval  false  Fail to retrieve Ed public key from X509 certificate.
 *
 **/
extern bool libspdm_ecd_get_public_key_from_x509(const uint8_t *cert, size_t cert_size,
                                                 void **ecd_context);
#endif /* (LIBSPDM_EDDSA_ED25519_SUPPORT) || (LIBSPDM_EDDSA_ED448_SUPPORT) */

#if LIBSPDM_SM2_DSA_SUPPORT
/**
 * Retrieve the sm2 public key from one DER-encoded X509 certificate.
 *
 * @param[in]  cert         Pointer to the DER-encoded X509 certificate.
 * @param[in]  cert_size    Size of the X509 certificate in bytes.
 * @param[out] sm2_context  Pointer to newly generated sm2 context which contain the retrieved
 *                          sm2 public key component. Use sm2_free() function to free the
 *                          resource.
 *
 * If cert is NULL, then return false.
 * If sm2_context is NULL, then return false.
 *
 * @retval  true   sm2 public key was retrieved successfully.
 * @retval  false  Fail to retrieve sm2 public key from X509 certificate.
 *
 **/
extern bool libspdm_sm2_get_public_key_from_x509(const uint8_t *cert, size_t cert_size,
                                                 void **sm2_context);
#endif /* LIBSPDM_SM2_DSA_SUPPORT */

#endif /* LIBSPDM_CERT_PARSE_SUPPORT */

#endif /* CRYPTLIB_CERT_H */
