/**
  ******************************************************************************
  * \file    certificate.h
  * \author  STMicroelectronics - CS application team
  * \brief   x509 certificate Parser
  ******************************************************************************
  * \attention
  *
  * <h2><center>&copy; COPYRIGHT 2022 STMicroelectronics</center></h2>
  *
  * This software is licensed under terms that can be found in the LICENSE file in
  * the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */

#ifndef CERTIFICATE_H
#define CERTIFICATE_H

#include <stdint.h>

/** \defgroup 	certificate_core  Certificate parser
 *  \ingroup 	certificate
 *  \brief		Certificate parser
 *  @{
 */

/**
 * \enum certificate_status_t
 * \brief response codes enumeration
 */
typedef enum {
	CERT_OK               			  	= 0x00, /*!< Successful processing */
	CERT_INVALID_PARAMETER	  		= 0x01,	/*!< Wrong function parameters */
	CERT_UNEXPECTED_SEQUENCE,
	CERT_UNEXPECTED_BITSTRING,
	CERT_INVALID_SIGNATURE,
	CERT_INVALID_CERTIFICATE,
	CERT_CA_NOT_MATCHING,
	CERT_UNSUPPORTED_FEATURE,
	CERT_BAD_SIZE,
}certificate_status_t;

/** \brief Definitions of available signature algorithms */
typedef enum {
	CERT_SIG_ECDSA_SHA1 = 0,	/*!< ECDSA with SHA1 algorithm */
	CERT_SIG_ECDSA_SHA224,	/*!< ECDSA with SHA224 algorithm */
	CERT_SIG_ECDSA_SHA256,	/*!< ECDSA with SHA256 algorithm */
	CERT_SIG_ECDSA_SHA384,	/*!< ECDSA with SHA384 algorithm */
	CERT_SIG_ECDSA_SHA512,	/*!< ECDSA with SHA512 algorithm */
}certificate_signature_algorithms_t;

/** \brief Definitions of supported Elliptic Curve Names */
typedef enum {
	CERT_EC_P256 = 0,		/*!< NIST P-256 */
	CERT_EC_P384, 			/*!< NIST P-384 */
	CERT_EC_P521, 			/*!< NIST P-521 */
	CERT_EC_bp256r1,  		/*!< brainpoolP256r1 */
	CERT_EC_bp256t1,  		/*!< brainpoolP256t1 */
	CERT_EC_bp384r1,  		/*!< brainpoolP384r1 */
	CERT_EC_bp384t1,  		/*!< brainpoolP384t1 */
	CERT_EC_bp512r1,  		/*!< brainpoolP512r1 */
	CERT_EC_bp512t1,  		/*!< brainpoolP512t1 */
}certificate_elliptic_curves_t;


/** \brief typedef for the structure keeping the validity */
typedef struct validity_st {
   uint8_t seconds;    /*!< seconds (range 0 to 59) */
   uint8_t minutes;    /*!< minutes (range 0 to 59) */
   uint8_t hours;      /*!< hours   (range 0 to 23) */
   uint8_t days;       /*!< days    (range 1 to 31) */
   uint8_t month;      /*!< month   (range 1 to 12) */
   uint32_t year;      /*!< years 0 to whatever     */
} cert_validity_t;

/** \brief typedef for the structure keeping the Ellipitc Curve Public Key */
typedef struct cert_public_key_t
{
  const uint8_t *pX; 	/*!<  Pointer to X Coordinate */
  const uint8_t *pY; 	/*!<  Pointer to Y Coordiante */
  uint32_t fsize; 		/*!< Size of the field in bytes */
} cert_public_key_t;

/** \brief typedef for the structure keeping the ECDSA Signature */
typedef struct ECDSAsign_st
{
  const uint8_t *pR; 	/*!< Pointer to r */
  uint32_t  rSize;   	/*!< size to r */
  const uint8_t *pS; 	/*!<  Pointer to s*/
  uint32_t  sSize;    	/*!< size to s */
} cert_signature_t; 

/** \brief internal structure used to keep the values parsed from the x509 */
struct internal_certificate {
  const uint8_t *pPubKey_point_representation_id; /*!< 0x04: Uncompressed key, 0x02 or 0x03: Compressed key */
  const uint8_t *tbs;              /*!<  Pointer to tcs field */
  uint32_t tbsSize;            		/*!<  Size of tcs */
  uint32_t x509Version;        		/*!<  x509 Version */
  const uint8_t *serialNumber;     /*!<  Pointer to SerialNumber*/
  uint32_t serialNumberSize;   		/*!<  Size of SerialNumber */
  uint32_t signature;          		/*!<  Signature (algorithm) but from tcs */
  const uint8_t *issuer;           /*!<  Pointer to Issuer */
  uint32_t issuerSize;         		/*!<  Size of Issuer */
  const uint8_t *validity;         /*!<  Pointer to Validity */
  uint32_t validitySize;       		/*!<  Size of Validity */
  const uint8_t *subject;          /*!<  Pointer to subject */
  uint32_t subjectSize;        		/*!<  Size of Subject */
  uint32_t EllipticCurve;      		/*!<  Identifies the Elliptic Curve */
  cert_public_key_t PubKey;    /*!<  Contain the struct \ref cert_public_key_t */
  const uint8_t *extensions;  		/*!<  Pointer to Extensions */
  uint32_t extensionsSize;			/*!<  Size of Extensions */
  uint32_t extensionsFlags;   		/*!<  Integer encoding the fields present in the tcs:			\n
									 *	bit 0:   BasicConstraints is present						\n
									 *  bit 1:   BasicConstraints is critical						\n
									 *  bit 2:   BasicConstraints indicates this is a CA			\n
									 *  bit 3: 	 BasicConstraints has a pathLenConstraint			\n
									 *  bit 4-7: PathLen											\n
									 *  bit 8: 	 keyUsage is present								\n
									 *  bit 9: 	 keyUsage is critical								\n
									 *  bit 15:  keyUsage field decipherOnly						\n
									 *  bit 16:  keyUsage field digitalSignature					\n
									 *  bit 17:  keyUsage field contentCommitment/nonRepudiation	\n
									 *  bit 18:  keyUsage field keyEncipherment						\n
									 *  bit 19:  keyUsage field dataEncipherment					\n
									 *  bit 20:  keyUsage field keyAgreement						\n
									 *  bit 21:  keyUsage field keyCertSign							\n
									 *  bit 22:  keyUsage field cRLSign								\n
									 *  bit 23:  keyUsage field encipherOnly						\n
									 *  bit 24:  ExtendedKeyUsage is present						\n
									 *  bit 25:  ExtendedKeyUsage is critical						\n
									 *  bit 27-29: Unused
                              	  	 */
  const uint8_t *extAKIkeyIdentifier;   		/*!<  field key identifier of extension authority key identifier */ 
  uint32_t extAKIkeyIdentifierSize;   		/*!<  size of previous field */ 
  uint32_t SignatureAlgorithm; 		/*!<  SignatureAlgorithm */
  cert_signature_t Sign;       /*!<  Contain the struct \ref cert_signature_t */
};

/** \brief typedef for the certificate_t */
typedef struct internal_certificate certificate_t;


/* Exported Functions */

/**
 * \brief  Initialize a certificate (\ref certificate_t) to an empty one
 * \param[in,out]  cert 				pointer to the certificate_t to be initialized
 * \note  This functions should be called before parsing a certificate
 * \details 	\include{doc} Authentication.dox
 */
void certificate_init(certificate_t *cert);

/**
 * \brief  Parse an x509 certificate
 * \param[in]  cert 				pointer to the x509 certificate to be parsed
 * \param[in]  max_size 		maximum size of certificate to be parsed
 * \param[out] certificate 	pointer to the certificate_t that will be filled
 * \param[out] next					pointer to cert array after the parsed certificate (it can be NULL)
 * \return \ref certificate_status_t : CERT_OK on success ; error code otherwise
 * \warning The parsed certificated could be empty, it is necessary to call a function
 *       	which verifies the certificate to be valid before using it.
 */
certificate_status_t certificate_parse(const uint8_t *cert, uint32_t max_size, certificate_t *certificate, const uint8_t **next);


/**
 * \brief  Parse the top of x509 certificate to find the tbs and the signature
 * \param[in]  cert 				pointer to the x509 certificate to be parsed
 * \param[in]  max_size 		maximum size of certificate to be parsed
 * \param[out] certificate 	pointer to the certificate_t that will be filled
 * \param[out] next					pointer to cert array after the parsed certificate (it can be NULL)
 * \return \ref certificate_status_t : CERT_OK on success ; error code otherwise
 * \warning The parsed certificated could be empty, it is necessary to call a function
 *       	which verifies the certificate to be valid before using it.
 */
certificate_status_t certificate_find_tbs_and_signature(const uint8_t *cert, uint32_t max_size, certificate_t *certificate, const uint8_t **next);

/**
 * \brief  Parse the tbs part of a certificate
 * \param[in/out] *certificate pointer to the certificate_t 
 */
void certificate_parse_tbs(certificate_t *certificate);

/**
 * \brief  Check that an imported x509 certificate is valid
 * \param[in]  certificate 	pointer to the parsed x509 certificate to be validated
 * \param[in]  currentTime 			pointer to a cert_validity_t with the current DateTime. If NULL no date check is done
 * \return \ref certificate_status_t : CERT_OK on success ; error code otherwise
 * \warning If currentTime==NULL the check on the validity dates of the certificate will be bypassed.
 */
certificate_status_t certificate_is_valid(const certificate_t *certificate, const cert_validity_t *currentTime);

/**
 * \brief  Check whether a certificate is marked as belonging to a CA
 * \param[in]  *cert 				pointer to the parsed x509 certificate to be checked
 * \return \ref certificate_status_t : CERT_OK on success ; error code otherwise
 */
certificate_status_t certificate_is_CA(const certificate_t *cert);

/**
 * \brief  Check whether "parent" issued "child"
 * \param[in]  parent 			pointer to the parsed x509 certificate of the supposed issuer of child
 * \param[in]  child  			pointer to the parsed x509 certificate of the certificated supposedly issued by parent
 * \param[in]  currentTime 		pointer to a cert_validity_t with the current DateTime. If NULL no date check is done.
 * \return \ref certificate_status_t : CERT_OK on success ; error code otherwise
 * \note RFC 5280 mandates an extremely expensive comparison, which we don't perform.
 * 		 As result of this simplification some legitimate parentship relation could be not acknowledged (the opposite should not occur).
 * \warning This check only a single level parent relashion (i.e. whether parent issued child)
 */
certificate_status_t certificate_is_parent(const certificate_t *parent, const certificate_t *child, const cert_validity_t *currentTime);

/**
 * \brief  Copy a certificate (\ref certificate_t) into another structure
 * \param[out]  copiedCert 		pointer to the certificate_t that will be written
 * \param[out]  originalCert 	pointer to the certificate_t that will be copied
 * \return \ref certificate_status_t : CERT_OK on success ; error code otherwise
 */
void certificate_copy(certificate_t *copiedCert, const certificate_t *originalCert);

/**
 * \brief Iterate over the certificate chain in argument and check each child/parent pairs
 * \param[in] 	rootCA 			Pointer to the root CA certificate buffer
 * \param[in] 	certChain 		Pointer to the certificate chain buffer
 * \param[in] 	certChainSize 	Size of the certificate chain buffer
 * \param[out] 	leafCert 		Pointer to the leaf certificate structure
 * \return  : ertificate_status_t 0 on success ; error code otherwise
 */
certificate_status_t certificate_parse_chain(
		uint8_t *rootCA,
		uint8_t *certChain,
		uint16_t certChainSize,
		certificate_t *leafCert);

/** @}*/

#endif /* CERTIFICATE_H */
