/* * Copyright (C) 2024 - 2026, Stephan Mueller * * 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_PARSER_H #define LC_X509_PARSER_H #include "ext_headers.h" #include "lc_x509_common.h" #ifdef __cplusplus extern "C" { #endif /** @defgroup X509 X.509 Certificate Parsing Handling * * Concept of X.509 certificate parsing handling in leancrypto * * The leancrypto library provides an X.509 parser which can read and understand * X.509 certificates. To appropriately use the X.509 parser, please consider * the following rules: * * 1. The parser interprets the provided X.509 data blob and fills a data * structure which allows immediate access to the certificate properties * by the leancrypto code. The data structure \p x509_certificate is provided * as part of the official header file. But it is NOT considered to be an * API. I.e. member variables or the structure format may change between * versions of leancrypto without announcement. The reason for providing the * data structure in the official header file is to support stack-only * clients. * * 2. The parser fills the data structure with pointers into the original X.509 * data blob. The caller MUST keep the original X.509 data blob at the same * location for the life time of the associated instance of the * \p x509_certificate data structure. * * 3. The X.509 parser API call only interprets and parses the X.509 data blob. * It does NOT enforce any kind of restrictions or policies. The caller * MUST use the provided X.509 policy API to enforce policies on the given * certificate. */ /** * @ingroup X509 * @brief Clear the resources used by the X.509 certificate parsing state * * @param [in] cert Certificate structure to be cleared */ void lc_x509_cert_clear(struct lc_x509_certificate *cert); /** * @ingroup X509 * @brief Decode an X.509 certificate * * The function parses an X.509 data buffer into a data structure that allows * accessing the various data points of the certificate. * * \note The \p cert data structure will contain pointers to the \p data * buffer. I.e. the certificate parsing analyzes \p data and finds all relevant * data in the raw X.509 data blob. The caller MUST therefore keep the * \p data pointer constant as long as the \p cert pointer is valid. * * \note This function only loads and parses the certificate into the data * structure to allow leancrypto to immediately access the information. This * function call does not validate the certificate (except for a self-signed * signature). Thus, the caller MUST apply the X.509 policy check functions * to validate the certificate considering that the loading of the certificate * has no information about the use case. * * @param [in,out] cert The data structure that is filled with all parameters * from the X.509 certificate data buffer. The buffer must * have been allocated by the caller. It is permissible * to keep it on the stack. * @param [in] data Raw X.509 data blob in DER / BER format * @param [in] datalen Length of the raw X.509 certificate buffer * * @return 0 on success or < 0 on error */ int lc_x509_cert_decode(struct lc_x509_certificate *cert, const uint8_t *data, size_t datalen); /// \cond DO_NOT_DOCUMENT #define LC_X509_KEYS_SPHINCS_SIZE \ (sizeof(struct lc_sphincs_pk) + sizeof(struct lc_sphincs_sk) + \ sizeof(struct lc_x509_key_data)) #define LC_X509_KEYS_SPHINCS_SET(name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"")(name) \ ->pk.sphincs_pk = \ (struct lc_sphincs_pk *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data)); \ (name)->sk.sphincs_sk = \ (struct lc_sphincs_sk *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data) + \ sizeof(struct lc_sphincs_pk)); \ (name)->data_struct_size = LC_X509_KEYS_SPHINCS_SIZE; \ _Pragma("GCC diagnostic pop") #define LC_X509_PRIVKEYS_SPHINCS_SIZE \ (sizeof(struct lc_sphincs_sk) + sizeof(struct lc_x509_key_data)) #define LC_X509_PRIVKEYS_SPHINCS_SET(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wcast-align\"")(name) \ ->pk.sphincs_pk = NULL, \ (name)->sk.sphincs_sk = \ (struct lc_sphincs_sk *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data)); \ (name)->data_struct_size = LC_X509_PRIVKEYS_SPHINCS_SIZE; \ _Pragma("GCC diagnostic pop") /// \endcond /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Sphincs Plus keys * on stack * * @param [in] name Name of stack variable */ #define LC_X509_KEYS_SPHINCS_ON_STACK(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_X509_KEYS_SPHINCS_SIZE, \ LC_HASH_COMMON_ALIGNMENT); \ struct lc_x509_key_data *name = \ (struct lc_x509_key_data *)name##_ctx_buf; \ LC_X509_KEYS_SPHINCS_SET(name); \ _Pragma("GCC diagnostic pop") /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Sphincs Plus * private keys on stack. This call is intended when only the private key * shall be held (e.g. only a siggen is intended). * * @param [in] name Name of stack variable */ #define LC_X509_PRIVKEYS_SPHINCS_ON_STACK(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \ LC_ALIGNED_BUFFER(name##_ctx_buf, \ LC_X509_PRIVKEYS_SPHINCS_SIZE, \ LC_HASH_COMMON_ALIGNMENT); \ struct lc_x509_key_data *name = \ (struct lc_x509_key_data *)name##_ctx_buf; \ LC_X509_PRIVKEYS_SPHINCS_SET(name); \ _Pragma("GCC diagnostic pop") /// \cond DO_NOT_DOCUMENT #define LC_X509_KEYS_DILITHIUM_SIZE \ (sizeof(struct lc_dilithium_pk) + sizeof(struct lc_dilithium_sk) + \ sizeof(struct lc_x509_key_data)) #define LC_X509_KEYS_DILITHIUM_SET(name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"")(name) \ ->pk.dilithium_pk = \ (struct lc_dilithium_pk *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data)); \ (name)->sk.dilithium_sk = \ (struct lc_dilithium_sk *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data) + \ sizeof(struct lc_dilithium_pk)); \ (name)->data_struct_size = LC_X509_KEYS_DILITHIUM_SIZE; \ _Pragma("GCC diagnostic pop") #define LC_X509_PRIVKEYS_DILITHIUM_SIZE \ (sizeof(struct lc_dilithium_sk) + sizeof(struct lc_x509_key_data)) #define LC_X509_PRIVKEYS_DILITHIUM_SET(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wcast-align\"")(name) \ ->pk.dilithium_pk = NULL, \ (name)->sk.dilithium_sk = \ (struct lc_dilithium_sk *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data)); \ (name)->data_struct_size = LC_X509_PRIVKEYS_DILITHIUM_SIZE; \ _Pragma("GCC diagnostic pop") /// \endcond /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium keys * on stack * * @param [in] name Name of stack variable */ #define LC_X509_KEYS_DILITHIUM_ON_STACK(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \ LC_ALIGNED_BUFFER(name##_ctx_buf, LC_X509_KEYS_DILITHIUM_SIZE, \ LC_HASH_COMMON_ALIGNMENT); \ struct lc_x509_key_data *name = \ (struct lc_x509_key_data *)name##_ctx_buf; \ LC_X509_KEYS_DILITHIUM_SET(name); \ _Pragma("GCC diagnostic pop") /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium * private keys on stack. This call is intended when only the private key * shall be held (e.g. only a siggen is intended). * * @param [in] name Name of stack variable */ #define LC_X509_PRIVKEYS_DILITHIUM_ON_STACK(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \ LC_ALIGNED_BUFFER(name##_ctx_buf, \ LC_X509_PRIVKEYS_DILITHIUM_SIZE, \ LC_HASH_COMMON_ALIGNMENT); \ struct lc_x509_key_data *name = \ (struct lc_x509_key_data *)name##_ctx_buf; \ LC_X509_PRIVKEYS_DILITHIUM_SET(name); \ _Pragma("GCC diagnostic pop") /// \cond DO_NOT_DOCUMENT #ifdef LC_DILITHIUM_ED25519_SIG #define LC_X509_KEYS_DILITHIUM_ED25519_SIZE \ (sizeof(struct lc_dilithium_ed25519_pk) + \ sizeof(struct lc_dilithium_ed25519_sk) + \ sizeof(struct lc_x509_key_data)) #define LC_X509_KEYS_DILITHIUM_ED25519_SET(name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"")(name) \ ->pk.dilithium_ed25519_pk = \ (struct lc_dilithium_ed25519_pk \ *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data)); \ (name)->sk.dilithium_ed25519_sk = \ (struct lc_dilithium_ed25519_sk \ *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data) + \ sizeof(struct lc_dilithium_ed25519_pk)); \ (name)->data_struct_size = LC_X509_KEYS_DILITHIUM_ED25519_SIZE; \ _Pragma("GCC diagnostic pop") #define LC_X509_PRIVKEYS_DILITHIUM_ED25519_SIZE \ (sizeof(struct lc_dilithium_ed25519_sk) + \ sizeof(struct lc_x509_key_data)) #define LC_X509_PRIVKEYS_DILITHIUM_ED25519_SET(name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"")(name) \ ->pk.dilithium_ed25519_pk = NULL, \ (name)->sk.dilithium_ed25519_sk = \ (struct lc_dilithium_ed25519_sk \ *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data)); \ (name)->data_struct_size = LC_X509_PRIVKEYS_DILITHIUM_ED25519_SIZE; \ _Pragma("GCC diagnostic pop") #endif #ifdef LC_DILITHIUM_ED448_SIG #define LC_X509_KEYS_DILITHIUM_ED448_SIZE \ (sizeof(struct lc_dilithium_ed448_pk) + \ sizeof(struct lc_dilithium_ed448_sk) + \ sizeof(struct lc_x509_key_data)) #define LC_X509_KEYS_DILITHIUM_ED448_SET(name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"")(name) \ ->pk.dilithium_ed448_pk = \ (struct lc_dilithium_ed448_pk \ *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data)); \ (name)->sk.dilithium_ed448_sk = \ (struct lc_dilithium_ed448_sk \ *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data) + \ sizeof(struct lc_dilithium_ed448_pk)); \ (name)->data_struct_size = LC_X509_KEYS_DILITHIUM_ED448_SIZE; \ _Pragma("GCC diagnostic pop") #define LC_X509_PRIVKEYS_DILITHIUM_ED448_SIZE \ (sizeof(struct lc_dilithium_ed448_sk) + sizeof(struct lc_x509_key_data)) #define LC_X509_PRIVKEYS_DILITHIUM_ED448_SET(name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"")(name) \ ->pk.dilithium_ed448_pk = NULL, \ (name)->sk.dilithium_ed448_sk = \ (struct lc_dilithium_ed448_sk \ *)((uint8_t *)(name) + \ sizeof(struct lc_x509_key_data)); \ (name)->data_struct_size = LC_X509_PRIVKEYS_DILITHIUM_ED448_SIZE; \ _Pragma("GCC diagnostic pop") #endif /// \endcond /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium-ED25519 * keys on stack * * @param [in] name Name of stack variable */ #define LC_X509_KEYS_DILITHIUM_ED25519_ON_STACK(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") \ LC_ALIGNED_BUFFER(name##_ctx_buf, \ LC_X509_KEYS_DILITHIUM_ED25519_SIZE, \ LC_HASH_COMMON_ALIGNMENT); \ struct lc_x509_key_data *name = \ (struct lc_x509_key_data *)name##_ctx_buf; \ LC_X509_KEYS_DILITHIUM_ED25519_SET(name); \ _Pragma("GCC diagnostic pop") /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium-ED25519 * private keys on stack. This call is intended when only the private key * shall be held (e.g. only a siggen is intended). * * @param [in] name Name of stack variable */ #define LC_X509_PRIVKEYS_DILITHIUM_ED25519_ON_STACK(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") \ LC_ALIGNED_BUFFER( \ name##_ctx_buf, \ LC_X509_PRIVKEYS_DILITHIUM_ED25519_SIZE, \ LC_HASH_COMMON_ALIGNMENT); \ struct lc_x509_key_data *name = \ (struct lc_x509_key_data *)name##_ctx_buf; \ LC_X509_PRIVKEYS_DILITHIUM_ED25519_SET(name); \ _Pragma("GCC diagnostic pop") /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium-ED448 * keys on stack * * @param [in] name Name of stack variable */ #define LC_X509_KEYS_DILITHIUM_ED448_ON_STACK(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") \ LC_ALIGNED_BUFFER(name##_ctx_buf, \ LC_X509_KEYS_DILITHIUM_ED448_SIZE, \ LC_HASH_COMMON_ALIGNMENT); \ struct lc_x509_key_data *name = \ (struct lc_x509_key_data *)name##_ctx_buf; \ LC_X509_KEYS_DILITHIUM_ED448_SET(name); \ _Pragma("GCC diagnostic pop") /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium-ED448 * private keys on stack. This call is intended when only the private key * shall be held (e.g. only a siggen is intended). * * @param [in] name Name of stack variable */ #define LC_X509_PRIVKEYS_DILITHIUM_ED448_ON_STACK(name) \ _Pragma("GCC diagnostic push") _Pragma( \ "GCC diagnostic ignored \"-Wdeclaration-after-statement\"") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") \ LC_ALIGNED_BUFFER( \ name##_ctx_buf, \ LC_X509_PRIVKEYS_DILITHIUM_ED448_SIZE, \ LC_HASH_COMMON_ALIGNMENT); \ struct lc_x509_key_data *name = \ (struct lc_x509_key_data *)name##_ctx_buf; \ LC_X509_PRIVKEYS_DILITHIUM_ED448_SET(name); \ _Pragma("GCC diagnostic pop") /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding any kind of key * type on stack * * @param [in] name Name of stack variable */ #ifdef LC_DILITHIUM_ED448_SIG #define LC_X509_KEYS_ON_STACK(name) LC_X509_KEYS_DILITHIUM_ED448_ON_STACK(name) #define LC_X509_PRIVKEYS_ON_STACK(name) \ LC_X509_PRIVKEYS_DILITHIUM_ED448_ON_STACK(name) #elif defined LC_DILITHIUM_ED25519_SIG #define LC_X509_KEYS_ON_STACK(name) \ LC_X509_KEYS_DILITHIUM_ED25519_ON_STACK(name) #define LC_X509_PRIVKEYS_ON_STACK(name) \ LC_X509_PRIVKEYS_DILITHIUM_ED25519_ON_STACK(name) #elif defined(LC_DILITHIUM_ENABLED) #define LC_X509_KEYS_ON_STACK(name) LC_X509_KEYS_DILITHIUM_ON_STACK(name) #define LC_X509_PRIVKEYS_ON_STACK(name) \ LC_X509_PRIVKEYS_DILITHIUM_ON_STACK(name) #elif defined(LC_SPHINCS_ENABLED) #define LC_X509_KEYS_ON_STACK(name) LC_X509_KEYS_SPHINCS_ON_STACK(name) #define LC_X509_PRIVKEYS_ON_STACK(name) LC_X509_PRIVKEYS_SPHINCS_ON_STACK(name) #else #error "No known signature schemas enabled" #endif /** * @brief Zeroize Dilithium context allocated with * LC_X509_*KEYS*_ON_STACK * * @param [in] keys Keys to be zeroized */ static inline void lc_x509_keys_zero(struct lc_x509_key_data *keys) { if (!keys) return; lc_memset_secure(keys, 0, keys->data_struct_size); } /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium-ED25519 * keys on heap * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_keys_dilithium_ed25519_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium-ED448 * keys on heap * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_keys_dilithium_ed448_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium * keys on heap * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_keys_dilithium_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Sphincs Plus * keys on heap * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_keys_sphincs_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding holding any kind * of key type on heap * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_keys_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium-ED25519 * private keys on heap. This call is intended when only the private key * shall be held (e.g. only a siggen is intended). * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_privkeys_dilithium_ed25519_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium-ED448 * private keys on heap. This call is intended when only the private key * shall be held (e.g. only a siggen is intended). * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_privkeys_dilithium_ed448_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Dilithium * private keys on heap. This call is intended when only the private key * shall be held (e.g. only a siggen is intended). * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_privkeys_dilithium_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding Sphincs Plus * private keys on heap. This call is intended when only the private key * shall be held (e.g. only a siggen is intended). * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_privkeys_sphincs_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Allocate memory for struct lc_x509_keys_data holding holding any kind * of private key type on heap. This call is intended when only the * private key shall be held (e.g. only a siggen is intended). * * @param [in] keys Variable to allocate * * @return 0 on success or < 0 on error */ int lc_x509_privkeys_alloc(struct lc_x509_key_data **keys); /** * @ingroup X509 * @brief Zeroize and free keys buffer * * @param [in] keys Variable to zeroize and free */ void lc_x509_keys_zero_free(struct lc_x509_key_data *keys); /** * @ingroup X509 * @brief Decode a private key in DER format * * The function parses a private data buffer into a data structure that allows * immediate use of the parsed key data with the cryptographic primitives. * * \note The \p key data structure will contain the data of the * secret keys. I.e. the key material is loaded into the databuffer as during * load time, various checks are applied. The caller MUST ensure proper disposal * of the buffer holding sensitive data. * * @param [out] key The data structure that is filled with the private key. The * caller must have allocated sufficient space with one of * \p LC_X509_KEYS*_ON_STACK or \p lc_x509_keys*_alloc * @param [in] key_type Type of the private key - prevent the deduction of the * the private key from the key file * @param [in] data Raw DER data blob in DER format * @param [in] datalen Length of the raw DER buffer * * @return 0 on success or < 0 on error */ int lc_x509_sk_decode(struct lc_x509_key_data *key, enum lc_sig_types key_type, const uint8_t *data, size_t datalen); /** * @ingroup X509 * @brief Decode a public key in raw format * * The function parses a public data buffer into a data structure that allows * immediate use of the parsed key data with the cryptographic primitives. * * \note The \p key data structure will contain the data of the * public keys. I.e. the key material is loaded into the databuffer as during * load time, various checks are applied. The caller MUST ensure proper disposal * of the buffer holding sensitive data. * * @param [out] key The data structure that is filled with the public key. The * caller must have allocated sufficient space with one of * \p LC_X509_KEYS*_ON_STACK or \p lc_x509_keys*_alloc * @param [in] key_type Type of the private key - prevent the deduction of the * the private key from the key file * @param [in] data Raw data blob * @param [in] datalen Length of the raw buffer * * @return 0 on success or < 0 on error */ int lc_x509_pk_decode(struct lc_x509_key_data *key, enum lc_sig_types key_type, const uint8_t *data, size_t datalen); /** * @ingroup X509 * @brief Verify signature over user-supplied data * * \note This function only performs the signature verification. It does not * enforce any key usage or EKU definition present in the X.509 certificate. * * @param [in] sig_data Caller-supplied buffer with signature * @param [in] siglen Length of the \p sig_data buffer * @param [in] cert The certificate to be used to verify signature * @param [in] m Message to be verified * @param [in] mlen Length of message * @param [in] prehash_algo It is permissible that the message is prehashed. If * so, it is indicated by this parameter which points * to the used message digest the caller used to * generate the prehashed message digest. This * forces the use of the Hash[ML|SLH|Composite]-DSA. * * @return 0 on success or < 0 on error */ int lc_x509_signature_verify(const uint8_t *sig_data, size_t siglen, const struct lc_x509_certificate *cert, const uint8_t *m, size_t mlen, const struct lc_hash *prehash_algo); #ifdef LC_DILITHIUM_ED25519_SIG /** * @ingroup X509 * @brief Parse a Composite ML-DSA ASN.1 structure into a public key structure * * @param [out] dilithium_ed25519_pk Public key to be filled * @param [in] pk_ptr Pointer to ASN.1 structure * @param [out] pk_len Size of the public key ASN.1 structure * * @return 0 on success or < 0 on error */ int lc_x509_cert_load_pk_dilithium_ed25519( struct lc_dilithium_ed25519_pk *dilithium_ed25519_pk, const uint8_t *pk_ptr, size_t pk_len); #endif #ifdef LC_DILITHIUM_ED448_SIG /** * @ingroup X509 * @brief Parse a Composite ML-DSA ASN.1 structure into a public key structure * * @param [out] dilithium_ed448_pk Public key to be filled * @param [in] pk_ptr Pointer to ASN.1 structure * @param [out] pk_len Size of the public key ASN.1 structure * * @return 0 on success or < 0 on error */ int lc_x509_cert_load_pk_dilithium_ed448( struct lc_dilithium_ed448_pk *dilithium_ed448_pk, const uint8_t *pk_ptr, size_t pk_len); #endif /** * @ingroup X509 * @brief Get a reference of the public key data * * The service function returns a pointer to the public key data in the * certificate. * * \note The returned pointers have the same life time as \p cert. * * @param [in] cert X.509 certificate from which the public is to be obtained * @param [out] pk X.509 public key buffer reference (may be NULL) * @param [out] pk_size Size of the public key (may be NULL) * @param [out] key_type Type of the public key (may be NULL) * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_pubkey(const struct lc_x509_certificate *cert, const uint8_t **pk, size_t *pk_size, enum lc_sig_types *key_type); /** * @ingroup X509 * @brief Get the extended key usage in human readable form * * The service function returns an array of EKU names the certificate contains * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] eku_names Reference to an array of strings * @param [out] num_eku Number of returned EKU strings * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_eku(const struct lc_x509_certificate *cert, const char ***eku_names, unsigned int *num_eku); /** * @ingroup X509 * @brief Get the extended key usage in integer form * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] val EKU value holding the LC_KEY_EKU_* flags * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_eku_val(const struct lc_x509_certificate *cert, uint16_t *val); /** * @ingroup X509 * @brief Get the key usage in human readable form * * The service function returns an array of key usage names the certificate * contains * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] keyusage_names Reference to an array of strings * @param [out] num_keyusage Number of returned key usage strings * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_keyusage(const struct lc_x509_certificate *cert, const char ***keyusage_names, unsigned int *num_keyusage); /** * @ingroup X509 * @brief Get the key usage in integer form * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] val key usage value holding the LC_KEY_USAGE_* flags * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_keyusage_val(const struct lc_x509_certificate *cert, uint16_t *val); /** * @ingroup X509 * @brief Get the SAN DNS name * * \note The \p san_dns_name may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * \note The returned pointers have the same life time as \p cert. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] san_dns_name Reference to the SAN DNS name * @param [out] san_dns_len Length of the SAN DNS name * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_san_dns(const struct lc_x509_certificate *cert, const char **san_dns_name, size_t *san_dns_len); /** * @ingroup X509 * @brief Helper to convert the human IP address value into binary form * * @param [in] ip_name Caller-provided buffer to fill with human-readable form * @param [out] ip Caller-provided buffer of binary representation of IP address * @param [in] ip_len Length of the IP address buffer * * @return 0 on success or < 0 on error */ int lc_x509_enc_san_ip(const char *ip_name, uint8_t *ip, size_t *ip_len); /** * @ingroup X509 * @brief Get the SAN IP value * * \note The returned pointers have the same life time as \p cert. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] san_ip Binary representation of IP address * @param [out] san_ip_len Length of the SAN IP address * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_san_ip(const struct lc_x509_certificate *cert, const uint8_t **san_ip, size_t *san_ip_len); /** * @ingroup X509 * @brief Helper to convert the binary IP address value into human-readable form * * @param [in] ip Binary representation of IP address * @param [in] ip_len Length of the IP address * @param [out] ip_name Caller-provided buffer to fill with human-readable form * @param [in] ip_name_len Size of the ip_name buffer that can be filled * * @return 0 on success or < 0 on error */ int lc_x509_dec_san_ip(const uint8_t *ip, size_t ip_len, char *ip_name, size_t ip_name_len); /** * @ingroup X509 * @brief Get the SKID value * * \note The returned pointers have the same life time as \p cert. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] skid Binary representation of SKID * @param [out] skidlen length of the SKID buffer * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_skid(const struct lc_x509_certificate *cert, const uint8_t **skid, size_t *skidlen); /** * @ingroup X509 * @brief Get the AKID value * * \note The returned pointers have the same life time as \p cert. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] akid Binary representation of AKID * @param [out] akidlen length of the AKID buffer * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_akid(const struct lc_x509_certificate *cert, const uint8_t **akid, size_t *akidlen); /** * @ingroup X509 * @brief Get the valid-from data from the certificate * * The returned time data is an integer giving the data in seconds since EPOCH. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] time_since_epoch Time in seconds since EPOCH * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_valid_from(const struct lc_x509_certificate *cert, time64_t *time_since_epoch); /** * @ingroup X509 * @brief Get the valid-to data from the certificate * * The returned time data is an integer giving the data in seconds since EPOCH. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] time_since_epoch Time in seconds since EPOCH * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_valid_to(const struct lc_x509_certificate *cert, time64_t *time_since_epoch); /** * @ingroup X509 * @brief Get the subject CN field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_subject_cn(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the subject email field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_subject_email(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the subject OU field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_subject_ou(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the subject O field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_subject_o(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the subject ST field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_subject_st(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the subject C field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_subject_c(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the issuer CN field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_issuer_cn(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the issuer email field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_issuer_email(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the issuer OU field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_issuer_ou(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the issuer O field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_issuer_o(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the issuer ST field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_issuer_st(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the issuer C field from the certificate * * \note The returned pointers have the same life time as \p cert. * * \note The returned pointer may *not* be NULL-terminated which implies that * this function returns also the size of the string. Yet, it is a human * readable string. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] string Reference to data field * @param [out] string_len Length of the data field * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_issuer_c(const struct lc_x509_certificate *cert, const char **string, size_t *string_len); /** * @ingroup X509 * @brief Get the serial number of the certificate * * \note The returned pointers have the same life time as \p cert. * * @param [in] cert X.509 certificate from which the data is to be obtained * @param [out] serial Binary representation of serial number * @param [out] serial_len Length of the serial number * * @return 0 on success or < 0 on error */ int lc_x509_cert_get_serial(const struct lc_x509_certificate *cert, const uint8_t **serial, size_t *serial_len); /****************************************************************************** * X.509 Certificate policy service functions ******************************************************************************/ /** X.509 Policy checks: returns True or False, or a POSIX error */ typedef int lc_x509_pol_ret_t /* __attribute__((warn_unused_result)) */; /** X.509 Policy checks: "True" result */ #define LC_X509_POL_TRUE 1 /** X.509 Policy checks: "False" result */ #define LC_X509_POL_FALSE 0 /** * @ingroup X509 * @brief Is the given certificate a CA certificate (root or intermediate)? * * \note The verification of the signature and the general constraints of the * certificate is not performed here but must be invoked with * \p lc_x509_policy_verify_cert. * * @param [in] cert Reference to the certificate * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_is_ca(const struct lc_x509_certificate *cert); /** * @ingroup X509 * @brief Can the given certificate validate CRLs? * * @param [in] cert Reference to the certificate * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_can_validate_crls(const struct lc_x509_certificate *cert); /** * @ingroup X509 * @brief Is the given certificate a self-signed certificate? * * @param [in] cert Reference to the certificate * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_is_selfsigned(const struct lc_x509_certificate *cert); /** * @ingroup X509 * @brief Is the given certificate a root CA certificate? * * \note The verification of the signature and the general constraints of the * certificate is not performed here but must be invoked with * \p lc_x509_policy_verify_cert. * * @param [in] cert Reference to the certificate * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_is_root_ca(const struct lc_x509_certificate *cert); /** * @ingroup X509 * @brief Does the given AKID match the certificate AKID? * * @param [in] cert Reference to the certificate * @param [in] reference_akid AKID in binary format to be matched * @param [in] reference_akid_len length of AKID binary buffer * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_match_akid(const struct lc_x509_certificate *cert, const uint8_t *reference_akid, size_t reference_akid_len); /** * @ingroup X509 * @brief Does the given SKID match the certificate SKID? * * @param [in] cert Reference to the certificate * @param [in] reference_skid SKID in binary format to be matched * @param [in] reference_skid_len length of SKID binary buffer * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_match_skid(const struct lc_x509_certificate *cert, const uint8_t *reference_skid, size_t reference_skid_len); /** * @ingroup X509 * @brief Check if set of required key usage flags are present * * @param [in] cert Reference to the certificate * @param [in] required_key_usage flags field with the bits set that the * certificate must contain * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_match_key_usage(const struct lc_x509_certificate *cert, uint16_t required_key_usage); /** * @ingroup X509 * @brief Check if set of required extended key usage flags are present * * @param [in] cert Reference to the certificate * @param [in] required_eku flags field with the bits set that the certificate * must contain * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_match_extended_key_usage(const struct lc_x509_certificate *cert, uint16_t required_eku); /** * @ingroup X509 * @brief Check if the given time falls within the range of the certificate * validity time. * * @param [in] cert Reference to the certificate * @param [in] current_time Time value to verify - this time is given in seconds * since EPOCH, e.g. by the POSIX service function * `time`. * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_time_valid(const struct lc_x509_certificate *cert, time64_t current_time); /** * @ingroup X509 * @brief Check if certificate is valid * * This check validates all RFC5280 constraints for a conforming certificate. * * @param [in] cert Reference to the certificate * * @return < 0 on error, LC_X509_POL_TRUE or LC_X509_POL_FALSE */ lc_x509_pol_ret_t lc_x509_policy_cert_valid(const struct lc_x509_certificate *cert); /** * @ingroup X509 * @brief Verification of an X.509 certificate against a public key * * This function performs the signature verification of the signature associated * with an X.509 certificate against the public key provided by the caller. * In addition, it performs all validity checks required as part of the * verification operation, including the validity time enforcement. Only if all * checks pass, the certificate is considered to be validated. * * @param [in] pkey Public key to check the certificate against * @param [in] cert Reference to the certificate to be validated * @param [in] flags Flags for the verification process (currently unused) * * @return 0 on success, < 0 on error */ int lc_x509_policy_verify_cert(const struct lc_public_key *pkey, const struct lc_x509_certificate *cert, uint64_t flags); enum lc_x509_policy_cert_subject_match_flag { /** Match DN or SAN IP or DNS - success if one matches */ lc_x509_policy_cert_subject_match_dn_and_san, /** Match DN only */ lc_x509_policy_cert_subject_match_dn_only, /** Match SAN IP only */ lc_x509_policy_cert_subject_match_san_ip_only, /** Match SAN DNS only */ lc_x509_policy_cert_subject_match_san_dns_only, /** Match SAN email only */ lc_x509_policy_cert_subject_match_san_email_only, /** Match SAN name segments */ lc_x509_policy_cert_subject_match_san_name_only, /** Match issuer only */ lc_x509_policy_cert_subject_match_issuer_only, }; /** * @ingroup X509 * @brief Match certificate against a provided name * * This function matches the \p search_name information against the certificate * DN and/or the SAN or the issuer as specified with the \p flag field. Only the * name components specified with \p search_name are checked. If \p search_name * does not contain a component, but the certificate has it, it will be matched. * * For example: The certificate has a DN with cn="aaa", c="bbb" and the search * contains cn="aaa" with all other components empty, it will match the * certificate. The string matching is an exact match (string length and * content must be identical). * * \note The SAN DNS and IP search string must be provided in * \p search_name->cn. For the IP, the unencoded IP is to be provided. * * @param [in] cert Reference to the certificate to be validated * @param [in] search_name Search information * @param [in] flag Flags for the search process * * @return 0 on success, < 0 on error */ lc_x509_pol_ret_t lc_x509_policy_cert_subject_match( const struct lc_x509_certificate *cert, const struct lc_x509_certificate_name *search_name, enum lc_x509_policy_cert_subject_match_flag flag); #ifdef __cplusplus } #endif #endif /* LC_X509_PARSER_H */