/*
 * Copyright (C) 2024 - 2026, Stephan Mueller <smueller@chronox.de>
 *
 * License: see LICENSE file in root directory
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
 * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#ifndef LC_X509_COMMON_H
#define LC_X509_COMMON_H

#include "ext_headers.h"
#include "lc_asn1.h"
#include "lc_hash.h"
#include "lc_sha3.h"

#if defined __has_include

#ifdef LC_DILITHIUM
#if __has_include("lc_dilithium.h")
#include "lc_dilithium.h"
#define LC_DILITHIUM_ENABLED
#endif
#endif

#ifdef LC_SPHINCS
#if __has_include("lc_sphincs.h")
#include "lc_sphincs.h"
#define LC_SPHINCS_ENABLED
#endif
#endif

#else
#error "Compiler misses __has_include"
#endif

#ifdef __cplusplus
extern "C" {
#endif

/// \cond DO_NOT_DOCUMENT

/*
 * Default hash type used to generate the SKID / AKID from the public key.
 * The algorithm is only used if the caller does not set an SKID/AKID.
 *
 * NOTE: X.509 support requires asymmetric algorithms. All asymmetric algorithms
 * require the presence of SHA-3 which means the use of SHA-3 is a safe choice.
 */
#define LC_X509_SKID_DEFAULT_HASH lc_sha3_256
#define LC_X509_SKID_DEFAULT_HASHSIZE LC_SHA3_256_SIZE_DIGEST
#define LC_X509_PQC_SK_SEED_SIZE 32

/*
 * According to RFC5280 section 4.2.1.2, the serial number must not be larger
 * than 20 bytes.
 */
#define LC_X509_SERIAL_MAX_SIZE 20
#define LC_X509_SERIAL_DEFAULT_HASH lc_sha3_256
#define LC_X509_SERIAL_DEFAULT_HASHSIZE LC_SHA3_256_SIZE_DIGEST

/*
 * Identifiers for an asymmetric key ID.  We have three ways of looking up a
 * key derived from an X.509 certificate:
 *
 * (1) Serial Number & Issuer.  Non-optional.  This is the only valid way to
 *     map a PKCS#7 signature to an X.509 certificate.
 *
 * (2) Issuer & Subject Unique IDs.  Optional.  These were the original way to
 *     match X.509 certificates, but have fallen into disuse in favour of (3).
 *
 * (3) Auth & Subject Key Identifiers.  Optional.  SKIDs are only provided on
 *     CA keys that are intended to sign other keys, so don't appear in end
 *     user certificates unless forced.
 *
 * We could also support an PGP key identifier, which is just a SHA1 sum of the
 * public key and certain parameters, but since we don't support PGP keys at
 * the moment, we shall ignore those.
 *
 * What we actually do is provide a place where binary identifiers can be
 * stashed and then compare against them when checking for an id match.
 *
 * The following size constraints are considered:
 *
 * * Serial number can be up to 20 octets (RFC 5280 section 4.1.2.2)
 *
 * * Subject Key Identifier can be SHA-1 or other sizes (RFC 5280 section
 *   4.2.1.2) - we allow up to 64 bytes
 *
 * * Issuer can be an arbitrary size of bytes but at least one (RFC 5280
 *   section 4.1.2.4) - we apply the upper limit of 128 bytes
 *
 * * Subject Key Identifier "MAY be based on either the key identifier (the
 *   subject key identifier in the issuer's certificate) or the issuer name
 *   and serial number." (RFC 5280 section 4.2.1.1)
 */
#define LC_ASN1_MAX_ISSUER_NAME 128
#define LC_ASN1_MAX_SKID 64
struct lc_asymmetric_key_id {
	uint8_t len;
	uint8_t data[LC_ASN1_MAX_SKID + LC_ASN1_MAX_ISSUER_NAME];
};

enum lc_sig_types {
	/** Undefined signature */
	LC_SIG_UNKNOWN,
	/** ML-DSA / Dilithium 44 */
	LC_SIG_DILITHIUM_44,
	/** ML-DSA / Dilithium 65 */
	LC_SIG_DILITHIUM_65,
	/** ML-DSA / Dilithium 87 */
	LC_SIG_DILITHIUM_87,
	/** ML-DSA / Dilithium 44 hybrid with ED25519 */
	LC_SIG_DILITHIUM_44_ED25519,
	/** ML-DSA / Dilithium 65 hybrid with ED25519 */
	LC_SIG_DILITHIUM_65_ED25519,
	/** ML-DSA / Dilithium 87 hybrid with ED25519 */
	LC_SIG_DILITHIUM_87_ED25519,
	/** ML-DSA / Dilithium 44 hybrid with ED448 */
	LC_SIG_DILITHIUM_44_ED448,
	/** ML-DSA / Dilithium 65 hybrid with ED448 */
	LC_SIG_DILITHIUM_65_ED448,
	/** ML-DSA / Dilithium 87 hybrid with ED448 */
	LC_SIG_DILITHIUM_87_ED448,
	/** SLH-DSA / Sphincs Plus SHAKE 256s */
	LC_SIG_SPHINCS_SHAKE_256S,
	/** SLH-DSA / Sphincs Plus SHAKE 256f */
	LC_SIG_SPHINCS_SHAKE_256F,
	/** SLH-DSA / Sphincs Plus SHAKE 192s */
	LC_SIG_SPHINCS_SHAKE_192S,
	/** SLH-DSA / Sphincs Plus SHAKE 192f */
	LC_SIG_SPHINCS_SHAKE_192F,
	/** SLH-DSA / Sphincs Plus SHAKE 128s */
	LC_SIG_SPHINCS_SHAKE_128S,
	/** SLH-DSA / Sphincs Plus SHAKE 128f */
	LC_SIG_SPHINCS_SHAKE_128F,

	/* ED25519 */
	LC_SIG_ED25519,
	/* ED448 */
	LC_SIG_ED448,

	/** RSA with PKCS1 */
	LC_SIG_RSA_PKCS1,
	LC_SIG_RSA_PKCS1_SHA2_256,
	LC_SIG_RSA_PKCS1_SHA2_384,
	LC_SIG_RSA_PKCS1_SHA2_512,
	LC_SIG_RSA_PKCS1_SHA3_256,
	LC_SIG_RSA_PKCS1_SHA3_384,
	LC_SIG_RSA_PKCS1_SHA3_512,
	/** ECDSA following X9.63 */
	LC_SIG_ECDSA_X963,
	LC_SIG_ECDSA_X963_SHA2_256,
	LC_SIG_ECDSA_X963_SHA2_384,
	LC_SIG_ECDSA_X963_SHA2_512,
	LC_SIG_ECDSA_X963_SHA3_256,
	LC_SIG_ECDSA_X963_SHA3_384,
	LC_SIG_ECDSA_X963_SHA3_512,
	/** SM2 */
	LC_SIG_SM2,
	/** ECRDSA with PKCS1 */
	LC_SIG_ECRDSA_PKCS1,
};

/*
 * Cryptographic data for the public-key subtype of the asymmetric key type.
 *
 * Note that this may include private part of the key as well as the public
 * part.
 */
struct lc_public_key {
	const uint8_t *key;
	size_t keylen;
	const uint8_t *params;
	size_t paramlen;
	enum OID algo;
	enum lc_sig_types pkey_algo;

	uint16_t key_usage; /* key extension flags */
#define LC_KEY_USAGE_DIGITALSIG 0x0080 /* (0) */
#define LC_KEY_USAGE_CONTENT_COMMITMENT 0x0040 /* (1) */
#define LC_KEY_USAGE_KEY_ENCIPHERMENT 0x0020 /* (2) */
#define LC_KEY_USAGE_DATA_ENCIPHERMENT 0x0010 /* (3) */
#define LC_KEY_USAGE_KEY_AGREEMENT 0x0008 /* (4) */
#define LC_KEY_USAGE_KEYCERTSIGN 0x0004 /* (5) */
#define LC_KEY_USAGE_CRLSIGN 0x0002 /* (6) */
#define LC_KEY_USAGE_ENCIPHER_ONLY 0x0001 /* (7) */
#define LC_KEY_USAGE_DECIPHER_ONLY 0x8000 /* (8) */
#define LC_KEY_USAGE_CRITICAL 0x4000
#define LC_KEY_USAGE_EXTENSION_PRESENT 0x2000
#define LC_KEY_USAGE_MASK                                                      \
	((uint16_t)~(LC_KEY_USAGE_CRITICAL | LC_KEY_USAGE_EXTENSION_PRESENT))

	uint16_t key_eku;
#define LC_KEY_EKU_CRITICAL (1 << 1)
#define LC_KEY_EKU_EXTENSION_PRESENT (1 << 2)
#define LC_KEY_EKU_ANY (1 << 3)
#define LC_KEY_EKU_SERVER_AUTH (1 << 4)
#define LC_KEY_EKU_CLIENT_AUTH (1 << 5)
#define LC_KEY_EKU_CODE_SIGNING (1 << 6)
#define LC_KEY_EKU_EMAIL_PROTECTION (1 << 7)
#define LC_KEY_EKU_TIME_STAMPING (1 << 8)
#define LC_KEY_EKU_OCSP_SIGNING (1 << 9)
#define LC_KEY_EKU_MODULE_SIGNING (1 << 10)
#define LC_KEY_EKU_MASK                                                        \
	((uint16_t)~LC_KEY_EKU_CRITICAL | LC_KEY_EKU_EXTENSION_PRESENT)

	uint8_t basic_constraint;
#define LC_KEY_CA (1 << 2)
#define LC_KEY_NOCA (1 << 1)
#define LC_KEY_BASIC_CONSTRAINT_CRITICAL (1 << 0)
#define LC_KEY_IS_CA (LC_KEY_CA | LC_KEY_BASIC_CONSTRAINT_CRITICAL)

	uint8_t ca_pathlen;
#define LC_KEY_CA_MAXLEN 16
#define LC_KEY_CA_MASK ((LC_KEY_CA_MAXLEN << 1) - 1)

	unsigned int key_is_private : 1;
};

/*
 * Public key cryptography signature data
 */
struct lc_public_key_signature {
	/*
	  * Signature
	  */
	const uint8_t *s;

	/*
	  * Number of bytes in signature
	  */
	size_t s_size;

	/*
	 * Digest size (0 if no digest was calculated and in this case the
	 * raw_data is used for signature). In case of having no digest, the
	 * signature algorithm must calculate the signature over the full
	 * ->raw_data.
	 */
	size_t digest_size;
	uint8_t digest[LC_SHA_MAX_SIZE_DIGEST];
	const struct lc_hash *hash_algo;

	enum lc_sig_types pkey_algo;

	/*
	 * Pointers to raw daa to be signed in case no message digest is
	 * calculated. This pointer is set if no message digest is calculated.
	 */
	const uint8_t *raw_data;
	size_t raw_data_len;

	/*
	 * Pointers to authenticated attributes to be signed.
	 */
	const uint8_t *authattrs;
	size_t authattrs_size;

	/*
	 * Auth IDs of the signer
	 */
	struct lc_asymmetric_key_id auth_ids[3];
};

struct lc_x509_certificate_name_component {
	const char *value;
	size_t size;
};

struct lc_x509_certificate_name {
	struct lc_x509_certificate_name_component email;
	struct lc_x509_certificate_name_component cn;
	struct lc_x509_certificate_name_component ou;
	struct lc_x509_certificate_name_component o;
	struct lc_x509_certificate_name_component st;
	struct lc_x509_certificate_name_component c;
};

/*
 * The X.509 Generator also uses the parser for final operations. This
 * data structure encapsulates the information only required during generation.
 */
struct lc_x509_key_data {
	enum lc_sig_types sig_type;
	unsigned int data_struct_size;
	union {
		struct lc_dilithium_pk *dilithium_pk;
#ifdef LC_DILITHIUM_ED448_SIG
		struct lc_dilithium_ed448_pk *dilithium_ed448_pk;
		struct lc_ed448_pk *ed448_pk;
#endif
#ifdef LC_DILITHIUM_ED25519_SIG
		struct lc_dilithium_ed25519_pk *dilithium_ed25519_pk;
		struct lc_ed25519_pk *ed25519_pk;
#endif
		struct lc_sphincs_pk *sphincs_pk;
	} pk;
	union {
		struct lc_dilithium_sk *dilithium_sk;
#ifdef LC_DILITHIUM_ED448_SIG
		struct lc_dilithium_ed448_sk *dilithium_ed448_sk;
		struct lc_ed448_sk *ed448_sk;
#endif
#ifdef LC_DILITHIUM_ED25519_SIG
		struct lc_dilithium_ed25519_sk *dilithium_ed25519_sk;
		struct lc_ed25519_sk *ed25519_sk;
#endif
		struct lc_sphincs_sk *sphincs_sk;
	} sk;
	uint8_t sk_seed[LC_X509_PQC_SK_SEED_SIZE];
	uint8_t pk_digest[LC_X509_SKID_DEFAULT_HASHSIZE];

	unsigned int sk_seed_set : 1;
	unsigned int sk_seed_memory_only : 1;
};

#ifdef LC_DILITHIUM_ED448
#define LC_X509_KEYS_PK_SIZE (sizeof(struct lc_dilithium_ed448_pk))
#define LC_X509_KEYS_SK_SIZE (sizeof(struct lc_dilithium_ed448_sk))
#elif defined(LC_DILITHIUM_ED25519)
#define LC_X509_KEYS_PK_SIZE (sizeof(struct lc_dilithium_ed25519_pk))
#define LC_X509_KEYS_SK_SIZE (sizeof(struct lc_dilithium_ed25519_sk))
#elif defined(LC_DILITHIUM)
#define LC_X509_KEYS_PK_SIZE (sizeof(struct lc_dilithium_pk))
#define LC_X509_KEYS_SK_SIZE (sizeof(struct lc_dilithium_sk))
#elif defined(LC_SPHINCS)
#define LC_X509_KEYS_PK_SIZE (sizeof(struct lc_sphincs_pk))
#define LC_X509_KEYS_SK_SIZE (sizeof(struct lc_sphincs_sk))
#else
#define LC_X509_KEYS_PK_SIZE (0)
#define LC_X509_KEYS_SK_SIZE (0)
#endif

#if defined(LC_SPHINCS)
#define LC_X509_KEYS_SIG_SIZE (sizeof(struct lc_sphincs_sig))
#elif defined(LC_DILITHIUM_ED448)
#define LC_X509_KEYS_SIG_SIZE (sizeof(struct lc_dilithium_ed448_sig))
#elif defined(LC_DILITHIUM_ED25519)
#define LC_X509_KEYS_SIG_SIZE (sizeof(struct lc_dilithium_ed25519_sig))
#elif defined(LC_DILITHIUM)
#define LC_X509_KEYS_SIG_SIZE (sizeof(struct lc_dilithium_sig))
#else
#define LC_X509_KEYS_SIG_SIZE (0)
#endif

#define LC_X509_KEYS_SIZE_META (sizeof(struct lc_x509_key_data))
#define LC_X509_KEYS_PK_SIZE_ALIGNED (8 + LC_X509_KEYS_PK_SIZE)
#define LC_X509_KEYS_SK_SIZE_ALIGNED (8 + LC_X509_KEYS_SK_SIZE)
#define LC_X509_KEYS_SIZE                                                      \
	(LC_X509_KEYS_SIZE_META + LC_X509_KEYS_PK_SIZE_ALIGNED +               \
	 LC_X509_KEYS_SK_SIZE_ALIGNED)

struct lc_x509_certificate {
	struct lc_x509_certificate *next;
	struct lc_x509_certificate *signer; /* Certificate that signed this one */
	struct lc_x509_key_data sig_gen_data;
	struct lc_x509_key_data pub_gen_data;
	struct lc_public_key pub; /* Public key details */
	struct lc_public_key_signature sig; /* Signature parameters */
	struct lc_asymmetric_key_id id; /* Issuer + Serial number */
	struct lc_asymmetric_key_id skid; /* Subject + subjectKeyId (optional) */

	struct lc_x509_certificate_name issuer_segments;
	struct lc_x509_certificate_name subject_segments;
	struct lc_x509_certificate_name san_directory_name_segments;

	/*
	 * Pointer to encoded certificate data. This is used when parsing
	 * a certificate.
	 */
	const uint8_t *raw_cert;
	size_t raw_cert_size;

	const char *san_dns; /* Subject Alternative Name DNS */
	size_t san_dns_len;
	const uint8_t *san_ip; /* Subject Alternative Name IP */
	size_t san_ip_len;
	const char *san_email; /* Subject Alternative Name email */
	size_t san_email_len;
	time64_t valid_from; /* Time since EPOCH in UTC */
	time64_t valid_to; /* Time since EPOCH in UTC */
	const uint8_t *tbs; /* Signed data */
	size_t tbs_size; /* Size of signed data */
	size_t raw_sig_size; /* Size of signature */
	const uint8_t *raw_sig; /* Signature data */
	const uint8_t *raw_serial; /* Raw serial number in ASN.1 */
	size_t raw_serial_size;
	size_t raw_issuer_size;
	const uint8_t *raw_issuer; /* Raw issuer name in ASN.1 */
	const uint8_t *raw_subject; /* Raw subject name in ASN.1 */
	size_t raw_subject_size;
	size_t raw_skid_size;
	const uint8_t *raw_skid; /* subjectKeyId in binary format */
	size_t raw_akid_size;
	const uint8_t *raw_akid; /* authority key Id binary format */
	unsigned int index;

	uint8_t x509_version; /* X.509 Version of certificate */

	/* Certificate chain path length up to the CA certificate. */
#define LC_PATHLEN_MAXSIZE ((1 << (sizeof(uint8_t) << 3)) - 1)
	uint8_t consumed_pathlen;

	unsigned int seen : 1; /* Infinite recursion prevention */
	unsigned int verified : 1;
	unsigned int self_signed
		: 1; /* T if self-signed (check unsupported_sig too) */
	unsigned int unsupported_sig
		: 1; /* T if signature uses unsupported crypto */
	unsigned int blacklisted : 1;
	unsigned int allocated : 1;

	/*
	 * Structure pertains to a CSR and shall not be handled as a regular
	 * certificate.
	 */
	unsigned int is_csr : 1;
};

/// \endcond

/**
 * @brief Convert a leancrypto public key algorithm reference into human
 *	  readable form
 *
 * @param [in] pkey_algo public key algorithm type
 * @param [out] alg Algorithm string
 *
 * @return 0 on success, < 0 on error
 */
int lc_x509_sig_type_to_name(enum lc_sig_types pkey_algo, const char **alg);

/**
 * @brief Obtain the hash type to be used with a given public key algorithm
 *
 * @param [in] pkey_algo public key algorithm type
 * @param [out] hash_algo Hash reference (or NULL if none is to be used)
 *
 * @return 0 on success, < 0 on error
 */
int lc_x509_sig_type_to_hash(enum lc_sig_types pkey_algo,
			     const struct lc_hash **hash_algo);

/**
 * @brief Obtain the hash type to be used with a given OID
 *
 * @param [in] oid OID to convert
 * @param [out] hash_algo Hash reference (or NULL if none is to be used)
 *
 * @return 0 on success, < 0 on error
 */
int lc_x509_oid_to_hash(enum OID oid, const struct lc_hash **hash_algo);

/**
 * @brief Convert a binary representation of an OID to leancrypto internal
 *	  reference
 *
 * @param [in] data Binary OID data
 * @param [in] datasize Length of data buffer
 *
 * @return OID
 */
enum OID lc_look_up_OID(const uint8_t *data, size_t datasize);

struct lc_tm {
	unsigned short year;
	unsigned char month;
	unsigned char day;
	unsigned char hour;
	unsigned char min;
	unsigned char sec;
};

/**
 * @brief Trivial conversion of time to human-readable time value
 *
 * It only shows UTC considering that Epoch is UTC (i.e. it does not apply
 * any time-zone conversions).
 *
 * Furthermore, it does not apply leap seconds.
 *
 * @param [in] timeval Time in seconds since Epoch
 * @param [out] tm Decoded time
 *
 * @return 0 on success; < 0 on error
 */
int lc_gmtime(time64_t timeval, struct lc_tm *tm);

/**
 * @brief Conversion of OID to binary representation using an internal lookup
 *	  table.
 *
 * @param [in] oid OID reference to convert
 * @param [out] data Pointer to binary representation (reference into a
 *		     leancrypto-internal database)
 * @param [out] datalen Length of the binary representation
 *
 * @return 0 on success; < 0 on error
 */
int lc_OID_to_data(enum OID oid, const uint8_t **data, size_t *datalen);

struct lc_asn1_encoder;

/**
 * @brief ASN.1 encoder with 10 level hierarchy
 *
 * See asn1_encoder_impl.h for details.
 *
 * @param [in] encoder Encoder definition
 * @param [in] context Arbitrary data handed to the decoding functions
 * @param [out] data Encoded data
 * @param [in,out] in_out_avail_datalen Length of the encoded data
 *
 * @return 0 on success; < 0 on error
 */
int lc_asn1_ber_encoder(const struct lc_asn1_encoder *encoder, void *context,
			uint8_t *data, size_t *in_out_avail_datalen);

/**
 * @brief ASN.1 encoder with 4 level hierarchy defined on stack
 *
 * See asn1_encoder_impl.h for details.
 *
 * @param [in] encoder Encoder definition
 * @param [in] context Arbitrary data handed to the decoding functions
 * @param [out] data Encoded data
 * @param [in,out] in_out_avail_datalen Length of the encoded data
 *
 * @return 0 on success; < 0 on error
 */
int lc_asn1_ber_encoder_small(const struct lc_asn1_encoder *encoder,
			      void *context, uint8_t *data,
			      size_t *in_out_avail_datalen);

struct lc_asn1_decoder;

/**
 * @brief Decoder BER/DER/CER ASN.1 according to pattern
 *
 * @param [in] decoder The decoder definition (produced by asn1_compiler)
 * @param [in] context The caller's context (to be passed to the action
 *			functions)
 * @param [in] data The encoded data
 * @param [in] datalen The size of the encoded data
 *
 * Decode BER/DER/CER encoded ASN.1 data according to a bytecode pattern
 * produced by asn1_compiler.  Action functions are called on marked tags to
 * allow the caller to retrieve significant data.
 *
 * LIMITATIONS:
 *
 * To keep down the amount of stack used by this function, the following limits
 * have been imposed:
 *
 *  (1) This won't handle datalen > 65535 without increasing the size of the
 *	cons stack elements and length_too_long checking.
 *
 *  (2) The stack of constructed types is 10 deep.  If the depth of non-leaf
 *	constructed types exceeds this, the decode will fail.
 *
 *  (3) The SET type (not the SET OF type) isn't really supported as tracking
 *	what members of the set have been seen is a pain.
 */
int lc_asn1_ber_decoder(const struct lc_asn1_decoder *decoder, void *context,
			const uint8_t *data, size_t datalen);

#ifdef __cplusplus
}
#endif

#endif /* LC_X509_COMMON_H */
