/**
  ******************************************************************************
  * \file    certificate_subparsing.h
  * \author  STMicroelectronics - CS application team
  * \brief   Helper for the 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_SUBPARSING_H
#define CERTIFICATE_SUBPARSING_H

/** \defgroup 	certificate_subparsing Certificate subparsing routines
 *  \ingroup 	certificate
 *  \brief		Certificate subparsing routines
 *  \{
 */

/** \brief Definitions of X509 TAGs */
typedef enum {
    TAG_BOOLEAN				= 0x01,	/*!< x509 tag for BOOLEAN */
    TAG_INTEGER,					/*!< x509 tag for INTEGER */
    TAG_BITSTRING,					/*!< x509 tag for BITSTRING */
    TAG_OCTETSTRING,				/*!< x509 tag for OCTETSTRING */
    TAG_NULL,						/*!< x509 tag for NULL */
    TAG_OBJECT_IDENTIFIER,			/*!< x509 tag for OBJECT_IDENTIFIER */
    TAG_ObjectDescriptor,			/*!< x509 tag for ObjectDescriptor */
    TAG_ENUMERATED          = 0x0A,	/*!< x509 tag for ENUMERATED */
    TAG_UTF8String          = 0x0C,	/*!< x509 tag for UTF8String */
    TAG_PrintableString     = 0x13,	/*!< x509 tag for PrintableString */
    TAG_IA5String           = 0x16,	/*!< x509 tag for IA5String */
    TAG_UTCTime,					/*!< x509 tag for UTCTime */
    TAG_GeneralizedTime,			/*!< x509 tag for GeneralizedTime */
    TAG_SEQUENCE            = 0x30,	/*!< x509 tag for SEQUENCE */
    TAG_SET,						/*!< x509 tag for SET */
    TAG_OPTION_0            = 0x80,						/*!< x509 tag for option 0 */
    TAG_OPTION_1            ,						/*!< x509 tag for option 1 */
    TAG_OPTION_2            ,						/*!< x509 tag for option 2 */
    TAG_OPTION_3            ,						/*!< x509 tag for option 3 */
    TAG_x509VERSION         = 0xA0,	/*!< x509 tag for x509VERSION */
    TAG_issuerUniqueID,				/*!< x509 tag for issuerUniqueID */
    TAG_subjectUniqueID,			/*!< x509 tag for subjectUniqueID */
    TAG_extensions,					/*!< x509 tag for extensions */
}certificate_tag_t;

/** \brief Definitions of supported Name Attributes */
typedef enum {
	ATTR_CN 	= 3,	/*!< CommonName */
	ATTR_SN 	= 5, 	/*!< SerialNumber */
	ATTR_C 		= 6,  	/*!< Country */
	ATTR_LN 	= 7, 	/*!< LocalityName */
	ATTR_SOPN   = 8, 	/*!< stateOrProvinceName */
	ATTR_ON 	= 10,	/*!< Organization Name */
	ATTR_OUN 	= 11, 	/*!< Organization Unit Name */
	ATTR_UID 	= 45, 	/*!< UniqueIdentifier */
	ATTR_DN 	= 49, 	/*!< DistinguishedName */
}certificate_name_attributes_t;

/** \brief Definitions of supported Extensions types */
typedef enum {
	EXTENSION_KU  =	15, /*!< KeyUsage */
	EXTENSION_BC  =	19, /*!< BasicContrains */
	EXTENSION_AKI = 35, /*!< authority key identifier */
	EXTENSION_EKU = 37, /*!< extKeyUsage */
}certificate_extensions_type_t;

/** \brief Structure used to keep SignatureAlgorithm OIDs */
struct SignatureAlgorithmOID_st
{
  uint32_t len;         /*!< size of the encoded OID */
  uint32_t type;        /*!< type of OID */
  uint8_t oid[8];      /*!< encoded OID */
};

/** \brief Structure used to keep Name Attribute OIDs */
struct AttributeOID_st
{
  uint32_t len;         /*!< size of the encoded OID */
  uint32_t type;        /*!< type of OID */
  uint8_t oid[3];      /*!< encoded OID */
};

/** \brief Structure used to keep named Elliptic Curve OIDs */
struct EllipticCurveOID_st
{
  uint32_t len;         /*!< size of the encoded OID */
  uint32_t type;        /*!< type of OID */
  uint8_t oid[9];      /*!< encoded OID */
};

/** \brief typedef for the attribute */
typedef struct attribute_st
{
  uint32_t type;        /*!< type of attribute (corresponding to OID) */
  uint32_t strFormat;   /*!< format of the string */
  const uint8_t *str;  /*!< pointer to the string of the attribute */
  uint32_t strSize;     /*!< size of the string */
} cert_attribute_t;

/** \brief Structure used to keep Name Attribute OIDs */
struct ExtensionOID_st
{
  uint32_t len;    		/*!< size of the encoded OID */
  uint32_t type;   		/*!< type of OID */
  uint8_t oid[5]; 		/*!< encoded OID */
};

/** \brief typedef for the extension */
typedef struct extension_st
{
  uint32_t type;       	/*!< type of extension (corresponding to OID) */
  uint32_t critical;   	/*!< critical */
  const uint8_t *value;/*!< pointer to the value hold by the extension (this is an octet string)  */
  uint32_t valueSize;   /*!< size of the string */
} cert_extension_t;


/* Exported Macros */

/** \brief Macro which checks if a TAG is valid (known) */
#define IS_VALID_TAG(tag) (( \
		(TAG_BOOLEAN 	<= (tag) && (tag) <= TAG_ObjectDescriptor) 	|| \
		(tag) == TAG_ENUMERATED  									|| \
		(tag) == TAG_UTF8String 									|| \
		(tag) == TAG_PrintableString 								|| \
		(TAG_IA5String 	<= (tag) && (tag) <= TAG_GeneralizedTime)	|| \
		(tag) == TAG_SEQUENCE 										|| \
		(tag) == TAG_SET 											|| \
		(TAG_OPTION_0 	<= (tag) && (tag) <= TAG_OPTION_3)	|| \
		(TAG_x509VERSION <= (tag) && (tag) <= TAG_extensions) 	) ? 1 : 0)



/**
* \brief  Identify the ASN.1 TLV
* \param[in]  *asn1 pointer to the ASN.1 TLV
* \param[out] *parsed will contain the number of processed bytes (rarely used)
* \param[out] *size  will contain the size (in bytes) of the value field of this ASN.1 TLV
* \param[out] **value  will contain a pointer to the value field of this ASN.1 TLV
* \param[in/out] *max_size maximum size left for value
* \return   The internal TAG identifier of this TLV or -1 if failure
*/
int32_t certificate_identify_ASN1_TLV(const uint8_t *asn1, int32_t *parsed, int32_t *size, const uint8_t **value, uint32_t* max_size);

/**
* \brief  Identify the Name Attribute from its OID
* \param[in]  *oid The value of the OID of the Attribute (got from \ref certificate_identify_ASN1_TLV )
* \param[in]  size size (in bytes) of the value field of this OID (got from \ref certificate_identify_ASN1_TLV )
* \note  Only a very limited set of Attributes is supported
* \return    The internal type identifing the Attribute or -1 for failure
*/
int32_t certificate_identify_attribute(const uint8_t *oid, int32_t size);

/**
* \brief  Parse an ECDSA signature
* \param[in]  *signature pointer to the expected signature field of the x509 certificate
* \param[out] *certificate pointer to the certificate_t that will be filled
* \param[out] **next_thing  will contain a pointer to the next TLV
*/
void certificate_parse_ECDSA_signature(const uint8_t *signature, certificate_t *certificate, const uint8_t **next_thing, uint32_t* max_size);

/**
* \brief  Parse the x509 Version of a certificate
* \param[in]  *x509VersionField pointer to the version field of a certificate
* \param[out] *certificate pointer to the certificate_t that will be filled
* \param[out] **next_thing output pointer to next TLV
*/
void certificate_parse_X509_version(const uint8_t *x509VersionField, certificate_t *certificate, const uint8_t **next_thing, uint32_t* max_size);

/**
* \brief  Parse a SignatureAlgorithm (or signature of a tbsCertificate)
* \param[in]  *SA pointer to the SignatureAlgorithm (or signature of a tbsCertificate)
* \param[out] *singatureAlgorithm integer that will be filled with the internal encoding of signatureAlgorithm
* \param[out] **next_thing output pointer to next TLV
* \note  Only a very limited set of SignatureAlgorithm is supported
*/
void certificate_parse_signature_algorithm(const uint8_t *SA, uint32_t *singatureAlgorithm, const uint8_t **next_thing, uint32_t* max_size);

/**
* \brief  Parse an ECC public Key from a certificate
* \param[in]  *EccPK pointer to the ECC public Key
* \param[out] *certificate pointer to the certificate_t that will be filled
* \param[out] **next_thing output pointer to next TLV
*/
void certificate_parse_ECC_public_key(const uint8_t *EccPK, certificate_t *certificate, const uint8_t **next_thing, uint32_t* max_size);

/**
* \brief  Parse an ASN.1 INTEGER
* \param[in]  *integer pointer to the expected INTEGER TLV
* \param[out] *outp will point to the starting byte of the integer
* \param[out] *outSize will contain the size of the integer in bytes
* \param[out] **next_thing  will contain a pointer to the next TLV
* \note This doesn't copy the integer. To keep memory small this just returns a pointer to it.
* \warning only non-negative integers are supported
*/
void certificate_parse_integer(const uint8_t *integer, const uint8_t **outp, uint32_t *outSize, const uint8_t **next_thing, uint32_t* max_size);

/**
* \brief  Parse the validity of a certificate
* \param[in]  *p pointer to the Name Attribute SET to be parsed
* \param[out] *notBefore_st pointer to cert_validity_t structure that will be filled with the "not before" date
* \param[out] *notAfter_st  pointer to cert_validity_t structure that will be filled with the "not after" date
* \param[out] **next_thing output pointer to next TLV
* \note In this function next_thing can be NULL
*/
void certificate_parse_validity(const uint8_t *p, cert_validity_t *notBefore_st, cert_validity_t *notAfter_st, const uint8_t **next_thing, uint32_t* max_size);

/**
* \brief  Parse an RelativeDistinguishedName
* \param[in]  *p pointer to the RelativeDistinguishedName (expeting a SET)
* \param[out] **nextRDN output pointer to next RDN
* \param[out] **attribute output pointer to RDN first attribute
*/
void certificate_parse_relative_distinguished_name(const uint8_t *p, const uint8_t **nextRDN, const uint8_t **attribute, uint32_t* max_size);

/**
* \brief  Parse a Name Attribute
* \param[in]  *p pointer to the AttributeTypeAndValue SEQUENCE to be parsed
* \param[out] *attribute_st pointer to cert_attribute_t structure that will be filled
* \param[out] **next_thing output pointer to next TLV
*/
void certificate_parse_attribute(const uint8_t *p, cert_attribute_t *attribute_st, const uint8_t **next_thing, uint32_t* max_size);

/**
* \brief  Compare two cert_validity_t structures
* \param[in]  *D1 pointer to the first cert_validity_t
* \param[in]  *D2 pointer to the second cert_validity_t
* \return Result of Comparison
* \retval   -1  D1 < D2
* \retval    0  D1 = D2
* \retval    1  D1 > D2
*/
int32_t certificate_date_compare(const cert_validity_t *D1, const cert_validity_t *D2);

/**
* \brief  Count the number of Attributes within a NAME
* \param[in]  *p pointer to the RDNSequence to be parsed
* \returun The number of Attributes
*/
int32_t certificate_count_attributes(const uint8_t *p);

/**
* \brief  String Comparison (case insensitive and for utf8)
* \param[in]  *p1 first string to compare
* \param[in]  *p2 second string to compare
* \param[in]  size p1/p2 size
* \return  result of comparison
* \retval -1 strings are different
* \retval 0 strings match
*/
int32_t certificate_case_insensitive_compare(const uint8_t *p1, const uint8_t *p2, int32_t size);

/**
* \brief  Identify the Extension from its OID
* \param[in]  *oid The value of the OID of the Extension (got from \ref certificate_identify_ASN1_TLV )
* \param[in]  size size (in bytes) of the value field of this OID (got from \ref certificate_identify_ASN1_TLV )
* \note  Only a very limited set of Extension is supported
* \return    The internal type identifing the Extension or -1 for failure
*/
int32_t certificate_case_identify_extension(const uint8_t *oid, int32_t size);

/**
* \brief  Parse an x509 Extension
* \param[in]  *ext pointer to the extension
* \param[out]  certificate structure
* \param[out]  *ext_st pointer to an cert_extension_t structure
*/
void certificate_parse_extension(const uint8_t *ext, certificate_t *certificate, cert_extension_t *ext_st, uint32_t* max_size);

/**
* \brief  Reads the value of an INTEGER and returns it as a int32_t
* \param[in]  *value pointer to the value field of an INTEGER TLV (got from \ref certificate_identify_ASN1_TLV)
* \param[in]  size size of the integer (got from \ref certificate_identify_ASN1_TLV)
* \return the integer value as an int32_t
* \warning only non-negative integers are supported
*/
int32_t certificate_get_small_integer(const uint8_t *value, int32_t size);

/** \}*/

#endif /* CERTIFICATE_SUBPARSING_H */
