projects/linux/asn1/x509_openssl.c (1,007 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <stdlib.h> #include <stddef.h> #include <stdbool.h> #include <string.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #include <openssl/conf.h> #include <openssl/bio.h> #include <openssl/evp.h> #include <openssl/err.h> #include "platform_api.h" #include "asn1/x509_openssl.h" #include "common/unused.h" /** * OpenSSL data for managing CA certificates. */ struct x509_openssl_ca_store_context { X509_STORE *trusted; /**< Store for Root CAs. */ STACK_OF (X509) *intermediate; /**< Store for intermediate CAs. */ unsigned long flags; /**< Verification flags to apply when verifying. */ }; #ifdef X509_ENABLE_CREATE_CERTIFICATES /** * Load a DER formatted key. * * @param key The key instance that will be allocated. * @param der The DER formatted key to load. * @param length The length of the DER key. * @param require_priv Flag indicating if the key must be a private a key. * * @return 0 if the key was successfully loaded or an error code. */ static int x509_openssl_load_key (EVP_PKEY **key, const uint8_t *der, size_t length, bool require_priv) { BIO *bio; int status; ERR_clear_error (); bio = BIO_new_mem_buf (der, length); if (bio == NULL) { status = ERR_get_error (); goto err_bio; } *key = d2i_PrivateKey_bio (bio, NULL); if (*key == NULL) { status = ERR_get_error (); if (require_priv) { goto err_key; } status = BIO_reset (bio); if (status <= 0) { status = ERR_get_error (); goto err_key; } *key = d2i_PUBKEY_bio (bio, NULL); if (*key == NULL) { status = ERR_get_error (); goto err_key; } } status = 0; err_key: BIO_free (bio); err_bio: return -status; } /** * Parse a custom OID that is already encoded, rather than being in a numerical format. * * @param encoded_oid The encoded OID to parse. * @param length Length of the encoded OID. * @param oid Output for the object for the OID. * * @return 0 if the OID was parsed successfully or an error code. */ int x509_openssl_parse_encoded_oid (const uint8_t *encoded_oid, size_t length, ASN1_OBJECT **oid) { uint8_t oid_der[256]; uint8_t *oid_ptr = oid_der; int status; if (length > (sizeof (oid_der) - 2)) { return X509_ENGINE_LONG_OID; } oid_der[0] = 0x06; oid_der[1] = length; memcpy (&oid_der[2], encoded_oid, length); *oid = d2i_ASN1_OBJECT (NULL, (const unsigned char**) &oid_ptr, length + 2); if (*oid == NULL) { status = -ERR_get_error (); return status; } return 0; } /** * Set the value of an ASN.1 BIT STRING, ensuring that the unused bits field is always 0. * * @param data The data to encode as a BIT STRING. * @param length Length of the data. * @param bits The BIT STRING object that will be updated. * * @return 0 if the bit string was updated successfully or an error code. */ int x509_openssl_set_bit_string (const uint8_t *data, size_t length, ASN1_BIT_STRING *bits) { int status; status = ASN1_BIT_STRING_set (bits, (uint8_t*) data, length); if (status == 0) { status = -ERR_get_error (); return status; } /* Make sure unused bits is always 0. */ bits->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); bits->flags |= ASN1_STRING_FLAG_BITS_LEFT; return 0; } /** * Create an custom formatted X.509 extension. * * @param builder The extension to create. * @param ext Output for the new extension. * @param ext_data Output for the data object of the extension. * * @return 0 if the extension was created successfully or an error code. */ static int x509_openssl_create_custom_extension (const struct x509_extension_builder *builder, X509_EXTENSION **ext, ASN1_OCTET_STRING **ext_data) { struct x509_extension extension; ASN1_OBJECT *ext_oid; int status; status = builder->build (builder, &extension); if (status != 0) { return status; } status = x509_openssl_parse_encoded_oid (extension.oid, extension.oid_length, &ext_oid); if (status != 0) { goto free_ext; } *ext_data = ASN1_OCTET_STRING_new (); if (*ext_data == NULL) { status = X509_ENGINE_NO_MEMORY; goto free_oid; } status = ASN1_OCTET_STRING_set (*ext_data, extension.data, extension.data_length); if (status == 0) { status = -ERR_get_error (); goto free_str; } *ext = X509_EXTENSION_create_by_OBJ (NULL, ext_oid, extension.critical, *ext_data); if (*ext == NULL) { status = -ERR_get_error (); goto free_str; } status = 0; goto free_oid; free_str: ASN1_OCTET_STRING_free (*ext_data); free_oid: ASN1_OBJECT_free (ext_oid); free_ext: builder->free (builder, &extension); return status; } /** * Add a standard extension to a CSR to be included in the signed certificate. * * @param request The context for the CSR being generated. * @param extensions The set of extensions to update for the CSR. * @param nid The type of extension to add. * @param value The value to assign to the extension. * * @return 0 if the extension was added or an error code. */ static int x509_openssl_add_standard_csr_extension (X509_REQ *request, STACK_OF (X509_EXTENSION) *extenions, int nid, const char *value) { X509V3_CTX ext_ctx; X509_EXTENSION *ext; int status; X509V3_set_ctx (&ext_ctx, NULL, NULL, request, NULL, 0); X509V3_set_ctx_nodb (&ext_ctx); ext = X509V3_EXT_conf_nid (NULL, &ext_ctx, nid, (char*) value); if (ext == NULL) { status = -ERR_get_error (); goto err_conf; } status = sk_X509_EXTENSION_push (extenions, ext); if (status == 0) { status = X509_ENGINE_NO_MEMORY; goto err_push; } return 0; err_push: X509_EXTENSION_free (ext); err_conf: return status; } /** * Add any extension to a CSR to be included in the signed certificate. * * @param request The context for the CSR being generated. * @param extensions The set of extensions to update for the CSR. * @param ext The extension to add to the CSR. This will be freed as part of the call. * @param data The extension data. This will be freed as part of the call. * * @return 0 if the extension was added or an error code. On failure, the extension will be freed. */ static int x509_openssl_add_custom_csr_extension (X509_REQ *request, STACK_OF (X509_EXTENSION) *extensions, X509_EXTENSION *ext, ASN1_OCTET_STRING *data) { X509V3_CTX ext_ctx; int status; X509V3_set_ctx (&ext_ctx, NULL, NULL, request, NULL, 0); X509V3_set_ctx_nodb (&ext_ctx); status = sk_X509_EXTENSION_push (extensions, ext); if (status == 0) { status = X509_ENGINE_NO_MEMORY; goto err_push; } ASN1_OCTET_STRING_free (data); return 0; err_push: X509_EXTENSION_free (ext); ASN1_OCTET_STRING_free (data); return status; } int x509_openssl_create_csr (const struct x509_engine *engine, const uint8_t *priv_key, size_t key_length, enum hash_type sig_hash, const char *name, int type, const uint8_t *eku, size_t eku_length, const struct x509_extension_builder *const *extra_extensions, size_t ext_count, uint8_t **csr, size_t *csr_length) { X509_REQ *request; EVP_PKEY *req_key; X509_NAME *subject; STACK_OF (X509_EXTENSION) *extensions; const EVP_MD *hash_algo; char *key_usage; size_t i; int status; if (csr == NULL) { return X509_ENGINE_INVALID_ARGUMENT; } *csr = NULL; if ((engine == NULL) || (priv_key == NULL) || (name == NULL) || (csr_length == NULL) || (key_length == 0)) { return X509_ENGINE_INVALID_ARGUMENT; } if ((eku_length != 0) && (eku == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } if ((ext_count != 0) && (extra_extensions == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } if ((type == X509_CERT_END_ENTITY) && (eku != NULL)) { return X509_ENGINE_NOT_CA_CERT; } switch (sig_hash) { case HASH_TYPE_SHA256: hash_algo = EVP_sha256 (); break; #ifdef HASH_ENABLE_SHA384 case HASH_TYPE_SHA384: hash_algo = EVP_sha384 (); break; #endif #ifdef HASH_ENABLE_SHA512 case HASH_TYPE_SHA512: hash_algo = EVP_sha512 (); break; #endif default: return X509_ENGINE_UNSUPPORTED_SIG_HASH; } ERR_clear_error (); request = X509_REQ_new (); if (request == NULL) { status = -ERR_get_error (); goto err_req; } status = x509_openssl_load_key (&req_key, priv_key, key_length, true); if (status != 0) { goto err_key; } status = X509_REQ_set_pubkey (request, req_key); if (status == 0) { status = -ERR_get_error (); goto err_req_key; } subject = X509_REQ_get_subject_name (request); status = X509_NAME_add_entry_by_txt (subject, "CN", MBSTRING_ASC, (unsigned char*) name, -1, -1, 0); if (status == 0) { status = -ERR_get_error (); goto err_req_key; } extensions = sk_X509_EXTENSION_new_null (); if (extensions == NULL) { status = X509_ENGINE_NO_MEMORY; goto err_req_key; } if (type) { key_usage = "critical,keyCertSign"; } else { key_usage = "critical,digitalSignature,keyAgreement"; } status = x509_openssl_add_standard_csr_extension (request, extensions, NID_key_usage, key_usage); if (status != 0) { goto err_ext; } if (type == X509_CERT_END_ENTITY) { status = x509_openssl_add_standard_csr_extension (request, extensions, NID_ext_key_usage, "critical,clientAuth"); if (status != 0) { goto err_ext; } } if (eku != NULL) { ASN1_OBJECT *oid; char oid_str[256]; status = x509_openssl_parse_encoded_oid (eku, eku_length, &oid); if (status != 0) { goto err_ext; } status = OBJ_obj2txt (oid_str, sizeof (oid_str), oid, 1); ASN1_OBJECT_free (oid); if (status > (int) sizeof (oid_str)) { status = X509_ENGINE_LONG_OID; goto err_ext; } status = x509_openssl_add_standard_csr_extension (request, extensions, NID_ext_key_usage, oid_str); if (status != 0) { goto err_ext; } } if (type) { char constraint[35]; if (X509_CERT_PATHLEN (type) <= X509_CERT_MAX_PATHLEN) { sprintf (constraint, "critical,CA:TRUE,pathlen:%d", X509_CERT_PATHLEN (type)); } else { strcpy (constraint, "critical,CA:TRUE"); } status = x509_openssl_add_standard_csr_extension (request, extensions, NID_basic_constraints, constraint); if (status != 0) { goto err_ext; } } for (i = 0; i < ext_count; i++) { X509_EXTENSION *ext; ASN1_OCTET_STRING *ext_data; if (extra_extensions[i] != NULL) { status = x509_openssl_create_custom_extension (extra_extensions[i], &ext, &ext_data); if (status != 0) { goto err_ext; } status = x509_openssl_add_custom_csr_extension (request, extensions, ext, ext_data); if (status != 0) { goto err_ext; } } } status = X509_REQ_add_extensions (request, extensions); if (status == 0) { status = -ERR_get_error (); goto err_ext; } status = X509_REQ_sign (request, req_key, hash_algo); if (status == 0) { status = -ERR_get_error (); goto err_ext; } status = i2d_X509_REQ (request, csr); if (status >= 0) { *csr_length = status; status = 0; } else { status = -ERR_get_error (); } err_ext: sk_X509_EXTENSION_pop_free (extensions, X509_EXTENSION_free); err_req_key: EVP_PKEY_free (req_key); err_key: X509_REQ_free (request); err_req: return status; } /** * Add a standard X.509v3 extension to a certificate. * * @param cert The certificate to add the extension to. * @param issuer The issuing certificate instance. * @param nid The type of extension to add. * @param value The value to assign to the extension. * * @return 0 if the extension was added or an error code. */ static int x509_openssl_add_standard_v3_extension (X509 *cert, X509 *issuer, int nid, const char *value) { X509V3_CTX ext_ctx; X509_EXTENSION *ext; int status; X509V3_set_ctx (&ext_ctx, issuer, cert, NULL, NULL, 0); X509V3_set_ctx_nodb (&ext_ctx); ext = X509V3_EXT_conf_nid (NULL, &ext_ctx, nid, value); if (ext == NULL) { status = ERR_get_error (); goto err_conf; } status = X509_add_ext (cert, ext, -1); if (status == 0) { status = ERR_get_error (); } else { status = 0; } X509_EXTENSION_free (ext); err_conf: return -status; } /** * Add a custom X.509v3 extension to a certificate. * * @param cert The certificate to add the extension to. * @param ext The extension to add to the certificate. This will be freed as part of the call. * @param data The extension data. This will be freed as part of the call. * * @return 0 if the extension was added or an error code. */ static int x509_openssl_add_custom_v3_extension (X509 *cert, X509_EXTENSION *ext, ASN1_OCTET_STRING *data) { int status; status = X509_add_ext (cert, ext, -1); if (status == 0) { status = -ERR_get_error (); } else { status = 0; } X509_EXTENSION_free (ext); ASN1_OCTET_STRING_free (data); return status; } /** * Create a new certificate. This can be self-signed, or signed by a CA. * * @param cert The instance to initialize with the new certificate. * @param cert_key The key to use to create the certificate. For self-signed certificates, this * must be a private key. * @param sig_hash The hash algorithm to use for signing the certificate. * @param serial_num The serial number to assign to the certificate. * @param serial_length The length of the serial number. * @param name The common name for the certificate subject. * @param type The type of certificate to create. * @param ca_key The private key of the CA to use for certificate signing. Set this to null for a * self-signed certificate. * @param ca_cert The certificate for the CA key. This is unused for a self-signed certificate and * can be set to null. * @param extra_extensions List of custom extensions that should be added to the certificate. * @param ext_count The number of custom extensions to add. * * @return 0 if the certificate was successfully created or an error code. */ static int x509_openssl_create_certificate (struct x509_certificate *cert, EVP_PKEY *cert_key, enum hash_type sig_hash, const uint8_t *serial_num, size_t serial_length, const char *name, int type, EVP_PKEY *ca_key, const struct x509_certificate *ca_cert, const struct x509_extension_builder *const *extra_extensions, size_t ext_count) { X509 *x509; X509 *ca_x509; BIGNUM *serial; X509_NAME *subject; ASN1_TIME *validity; const EVP_MD *hash_algo; char *key_usage; size_t i; int status; switch (sig_hash) { case HASH_TYPE_SHA256: hash_algo = EVP_sha256 (); break; #ifdef HASH_ENABLE_SHA384 case HASH_TYPE_SHA384: hash_algo = EVP_sha384 (); break; #endif #ifdef HASH_ENABLE_SHA512 case HASH_TYPE_SHA512: hash_algo = EVP_sha512 (); break; #endif default: return X509_ENGINE_UNSUPPORTED_SIG_HASH; } ERR_clear_error (); x509 = X509_new (); if (x509 == NULL) { status = -ERR_get_error (); goto err_cert; } if (ca_key) { ca_x509 = (X509*) ca_cert->context; } else { ca_x509 = x509; } status = X509_set_pubkey (x509, cert_key); if (status == 0) { status = -ERR_get_error (); goto err_req_key; } status = X509_set_version (x509, 2); if (status == 0) { status = -ERR_get_error (); goto err_req_key; } serial = BN_bin2bn (serial_num, serial_length, NULL); if (serial == NULL) { status = -ERR_get_error (); goto err_serial; } if (BN_is_zero (serial)) { status = X509_ENGINE_INVALID_SERIAL_NUM; goto err_serial; } if (BN_to_ASN1_INTEGER (serial, X509_get_serialNumber (x509)) == NULL) { status = -ERR_get_error (); goto err_serial; } subject = X509_get_subject_name (x509); status = X509_NAME_add_entry_by_txt (subject, "CN", MBSTRING_ASC, (unsigned char*) name, -1, -1, 0); if (status == 0) { status = -ERR_get_error (); goto err_serial; } subject = X509_get_subject_name (ca_x509); status = X509_set_issuer_name (x509, subject); if (status == 0) { status = -ERR_get_error (); goto err_serial; } validity = X509_get_notBefore (x509); status = ASN1_TIME_set_string (validity, "180101000000Z"); if (status == 0) { status = -ERR_get_error (); goto err_serial; } validity = X509_get_notAfter (x509); status = ASN1_TIME_set_string (validity, "99991231235959Z"); if (status == 0) { status = -ERR_get_error (); goto err_serial; } status = x509_openssl_add_standard_v3_extension (x509, ca_x509, NID_subject_key_identifier, "hash"); if (status != 0) { goto err_serial; } status = x509_openssl_add_standard_v3_extension (x509, ca_x509, NID_authority_key_identifier, "keyid:always"); if (status != 0) { goto err_serial; } if (type) { key_usage = "critical,keyCertSign"; } else { key_usage = "critical,digitalSignature,keyAgreement"; } status = x509_openssl_add_standard_v3_extension (x509, ca_x509, NID_key_usage, key_usage); if (status != 0) { goto err_serial; } if (type == X509_CERT_END_ENTITY) { status = x509_openssl_add_standard_v3_extension (x509, ca_x509, NID_ext_key_usage, "critical,clientAuth"); if (status != 0) { goto err_serial; } } if (type) { char constraint[35]; if (X509_CERT_PATHLEN (type) <= X509_CERT_MAX_PATHLEN) { sprintf (constraint, "critical,CA:TRUE,pathlen:%d", X509_CERT_PATHLEN (type)); } else { strcpy (constraint, "critical,CA:TRUE"); } status = x509_openssl_add_standard_v3_extension (x509, ca_x509, NID_basic_constraints, constraint); if (status != 0) { goto err_serial; } } for (i = 0; i < ext_count; i++) { X509_EXTENSION *ext; ASN1_OCTET_STRING *ext_data; if (extra_extensions[i] != NULL) { status = x509_openssl_create_custom_extension (extra_extensions[i], &ext, &ext_data); if (status != 0) { goto err_serial; } status = x509_openssl_add_custom_v3_extension (x509, ext, ext_data); if (status != 0) { goto err_serial; } } } if (ca_key) { status = X509_sign (x509, ca_key, hash_algo); } else { status = X509_sign (x509, cert_key, hash_algo); } if (status == 0) { status = -ERR_get_error (); goto err_serial; } cert->context = x509; status = 0; err_serial: BN_free (serial); err_req_key: if (status != 0) { X509_free (x509); } err_cert: return status; } int x509_openssl_create_self_signed_certificate (const struct x509_engine *engine, struct x509_certificate *cert, const uint8_t *priv_key, size_t key_length, enum hash_type sig_hash, const uint8_t *serial_num, size_t serial_length, const char *name, int type, const struct x509_extension_builder *const *extra_extensions, size_t ext_count) { EVP_PKEY *cert_key; int status; if ((engine == NULL) || (cert == NULL) || (priv_key == NULL) || (serial_num == NULL) || (name == NULL) || (key_length == 0) || (serial_length == 0)) { return X509_ENGINE_INVALID_ARGUMENT; } if ((ext_count != 0) && (extra_extensions == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } status = x509_openssl_load_key (&cert_key, priv_key, key_length, true); if (status != 0) { return status; } status = x509_openssl_create_certificate (cert, cert_key, sig_hash, serial_num, serial_length, name, type, NULL, NULL, extra_extensions, ext_count); EVP_PKEY_free (cert_key); return status; } int x509_openssl_create_ca_signed_certificate (const struct x509_engine *engine, struct x509_certificate *cert, const uint8_t *key, size_t key_length, const uint8_t *serial_num, size_t serial_length, const char *name, int type, const uint8_t* ca_priv_key, size_t ca_key_length, enum hash_type sig_hash, const struct x509_certificate *ca_cert, const struct x509_extension_builder *const *extra_extensions, size_t ext_count) { EVP_PKEY *cert_key; EVP_PKEY *ca_key; int status; if ((engine == NULL) || (cert == NULL) || (key == NULL) || (serial_num == NULL) || (name == NULL) || (ca_priv_key == NULL) || (ca_cert == NULL) || (key_length == 0) || (serial_length == 0) || (ca_key_length == 0)) { return X509_ENGINE_INVALID_ARGUMENT; } if ((ext_count != 0) && (extra_extensions == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } status = x509_openssl_load_key (&cert_key, key, key_length, false); if (status != 0) { goto err_key; } status = x509_openssl_load_key (&ca_key, ca_priv_key, ca_key_length, true); if (status != 0) { goto err_ca_key; } status = x509_openssl_create_certificate (cert, cert_key, sig_hash, serial_num, serial_length, name, type, ca_key, ca_cert, extra_extensions, ext_count); EVP_PKEY_free (ca_key); err_ca_key: EVP_PKEY_free (cert_key); err_key: return status; } #endif int x509_openssl_load_certificate (const struct x509_engine *engine, struct x509_certificate *cert, const uint8_t *der, size_t length) { X509 *x509; BIO *bio; int status; if ((engine == NULL) || (cert == NULL) || (der == NULL) || (length == 0)) { return X509_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); bio = BIO_new_mem_buf (der, length); if (bio == NULL) { status = ERR_get_error (); goto err_bio; } x509 = d2i_X509_bio (bio, NULL); if (x509 == NULL) { status = ERR_get_error (); goto err_cert; } cert->context = x509; status = 0; err_cert: BIO_free (bio); err_bio: return -status; } void x509_openssl_release_certificate (const struct x509_engine *engine, struct x509_certificate *cert) { UNUSED (engine); if (cert) { X509_free ((X509*) cert->context); memset (cert, 0, sizeof (struct x509_certificate)); } } #ifdef X509_ENABLE_CREATE_CERTIFICATES int x509_openssl_get_certificate_der (const struct x509_engine *engine, const struct x509_certificate *cert, uint8_t **der, size_t *length) { int status; if (der == NULL) { return X509_ENGINE_INVALID_ARGUMENT; } *der = NULL; if ((engine == NULL) || (cert == NULL) || (length == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); status = i2d_X509 ((X509*) cert->context, der); if (status >= 0) { *length = status; status = 0; } else { status = -ERR_get_error (); } return status; } #endif #ifdef X509_ENABLE_AUTHENTICATION int x509_openssl_get_certificate_version (const struct x509_engine *engine, const struct x509_certificate *cert) { if ((engine == NULL) || (cert == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } return X509_get_version ((X509*) cert->context) + 1; } int x509_openssl_get_serial_number (const struct x509_engine *engine, const struct x509_certificate *cert, uint8_t *serial_num, size_t length) { ASN1_INTEGER *encoded; BIGNUM *serial; int bytes; if ((engine == NULL) || (cert == NULL) || (serial_num == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); encoded = X509_get_serialNumber ((X509*) cert->context); serial = ASN1_INTEGER_to_BN (encoded, NULL); if (serial == NULL) { return -ERR_get_error (); } if ((int) length < BN_num_bytes (serial)) { bytes = X509_ENGINE_SMALL_SERIAL_BUFFER; goto err_length; } bytes = BN_bn2bin (serial, serial_num); err_length: BN_free (serial); return bytes; } int x509_openssl_get_public_key_type (const struct x509_engine *engine, const struct x509_certificate *cert) { EVP_PKEY *key; int type; if ((engine == NULL) || (cert == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); key = X509_get_pubkey ((X509*) cert->context); if (key == NULL) { type = -ERR_get_error (); return type; } type = EVP_PKEY_base_id (key); switch (type) { case EVP_PKEY_RSA: type = X509_PUBLIC_KEY_RSA; break; case EVP_PKEY_EC: type = X509_PUBLIC_KEY_ECC; break; case EVP_PKEY_NONE: type = X509_ENGINE_UNKNOWN_KEY_TYPE; break; default: type = X509_ENGINE_UNSUPPORTED_KEY_TYPE; } EVP_PKEY_free (key); return type; } int x509_openssl_get_public_key_length (const struct x509_engine *engine, const struct x509_certificate *cert) { EVP_PKEY *key; int bits; if ((engine == NULL) || (cert == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); key = X509_get_pubkey ((X509*) cert->context); if (key == NULL) { return -ERR_get_error (); } bits = EVP_PKEY_bits (key); EVP_PKEY_free (key); return bits; } int x509_openssl_get_public_key (const struct x509_engine *engine, const struct x509_certificate *cert, uint8_t **key, size_t *key_length) { EVP_PKEY *cert_key; int status; if (key == NULL) { return X509_ENGINE_INVALID_ARGUMENT; } *key = NULL; if ((engine == NULL) || (cert == NULL) || (key_length == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); cert_key = X509_get_pubkey ((X509*) cert->context); if (cert_key == NULL) { status = ERR_get_error (); goto err_key; } status = i2d_PUBKEY (cert_key, key); if (status >= 0) { *key_length = status; status = 0; } else { status = ERR_get_error (); } EVP_PKEY_free (cert_key); err_key: return -status; } int x509_openssl_init_ca_cert_store (const struct x509_engine *engine, struct x509_ca_certs *store) { struct x509_openssl_ca_store_context *store_ctx; int status; if ((engine == NULL) || (store == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } store_ctx = platform_malloc (sizeof (struct x509_openssl_ca_store_context)); if (store_ctx == NULL) { return X509_ENGINE_NO_MEMORY; } memset (store_ctx, 0, sizeof (*store_ctx)); ERR_clear_error (); store_ctx->trusted = X509_STORE_new (); if (store_ctx->trusted == NULL) { status = -ERR_get_error (); goto ctx_free; } store_ctx->intermediate = sk_X509_new_null (); if (store_ctx->intermediate == NULL) { status = -ERR_get_error (); goto store_free; } store->context = store_ctx; return 0; store_free: X509_STORE_free (store_ctx->trusted); ctx_free: platform_free (store_ctx); return status; } void x509_openssl_release_ca_cert_store (const struct x509_engine *engine, struct x509_ca_certs *store) { UNUSED (engine); if (store && store->context) { struct x509_openssl_ca_store_context *store_ctx = store->context; X509_STORE_free (store_ctx->trusted); sk_X509_pop_free (store_ctx->intermediate, X509_free); platform_free (store_ctx); memset (store, 0, sizeof (struct x509_ca_certs)); } } int x509_openssl_add_root_ca (const struct x509_engine *engine, struct x509_ca_certs *store, const uint8_t *der, size_t length) { struct x509_certificate cert; EVP_PKEY *cert_key; int status; if (store == NULL) { return X509_ENGINE_INVALID_ARGUMENT; } status = x509_openssl_load_certificate (engine, &cert, der, length); if (status != 0) { goto err_cert; } ERR_clear_error (); status = X509_check_ca ((X509*) cert.context); if (status == 0) { status = X509_ENGINE_NOT_CA_CERT; goto err_chk; } status = X509_check_issued ((X509*) cert.context, (X509*) cert.context); if (status != X509_V_OK) { status = X509_ENGINE_NOT_SELF_SIGNED; goto err_chk; } cert_key = X509_get_pubkey ((X509*) cert.context); if (cert_key == NULL) { status = -ERR_get_error (); goto err_chk; } status = X509_verify ((X509*) cert.context, cert_key); if (status == 1) { status = 0; } else { status = X509_ENGINE_BAD_SIGNATURE; goto err_sig; } status = X509_STORE_add_cert (((struct x509_openssl_ca_store_context*) store->context)->trusted, (X509*) cert.context); if (status == 0) { status = -ERR_get_error (); goto err_sig; } status = 0; err_sig: EVP_PKEY_free (cert_key); err_chk: x509_openssl_release_certificate (engine, &cert); err_cert: return status; } int x509_openssl_add_trusted_ca (const struct x509_engine *engine, struct x509_ca_certs *store, const uint8_t *der, size_t length) { struct x509_openssl_ca_store_context *store_ctx; struct x509_certificate cert; int status; if (store == NULL) { return X509_ENGINE_INVALID_ARGUMENT; } status = x509_openssl_load_certificate (engine, &cert, der, length); if (status != 0) { goto err_cert; } ERR_clear_error (); status = X509_check_ca ((X509*) cert.context); if (status == 0) { status = X509_ENGINE_NOT_CA_CERT; goto err_chk; } status = X509_check_issued ((X509*) cert.context, (X509*) cert.context); if (status == X509_V_OK) { status = X509_ENGINE_IS_SELF_SIGNED; goto err_chk; } store_ctx = (struct x509_openssl_ca_store_context*) store->context; status = X509_STORE_add_cert (store_ctx->trusted, (X509*) cert.context); if (status == 0) { status = -ERR_get_error (); goto err_chk; } /* There is an ICA in the trusted store, so allow partial chain verification. */ store_ctx->flags = X509_V_FLAG_PARTIAL_CHAIN; status = 0; err_chk: x509_openssl_release_certificate (engine, &cert); err_cert: return status; } int x509_openssl_add_intermediate_ca (const struct x509_engine *engine, struct x509_ca_certs *store, const uint8_t *der, size_t length) { struct x509_certificate cert; int status; if (store == NULL) { return X509_ENGINE_INVALID_ARGUMENT; } status = x509_openssl_load_certificate (engine, &cert, der, length); if (status != 0) { goto err_cert; } status = X509_check_ca ((X509*) cert.context); if (status == 0) { status = X509_ENGINE_NOT_CA_CERT; goto err_chk; } status = X509_check_issued ((X509*) cert.context, (X509*) cert.context); if (status == X509_V_OK) { status = X509_ENGINE_IS_SELF_SIGNED; goto err_chk; } status = sk_X509_push (((struct x509_openssl_ca_store_context*) store->context)->intermediate, (X509*) cert.context); if (status == 0) { status = X509_ENGINE_NO_MEMORY; goto err_chk; } return 0; err_chk: x509_openssl_release_certificate (engine, &cert); err_cert: return status; } int x509_openssl_authenticate (const struct x509_engine *engine, const struct x509_certificate *cert, const struct x509_ca_certs *store) { struct x509_openssl_ca_store_context *store_ctx = NULL; X509_STORE_CTX *auth_ctx; X509_VERIFY_PARAM *verify_param; int status; if ((engine == NULL) || (cert == NULL) || (store == NULL)) { return X509_ENGINE_INVALID_ARGUMENT; } ERR_clear_error (); auth_ctx = X509_STORE_CTX_new (); if (auth_ctx == NULL) { status = -ERR_get_error (); goto err_ctx; } if (store) { store_ctx = store->context; } status = X509_STORE_CTX_init (auth_ctx, store_ctx->trusted, (X509*) cert->context, store_ctx->intermediate); if (status == 0) { status = -ERR_get_error (); goto err_init; } /* Apply any verification flags that are configured for this cert store. */ verify_param = X509_STORE_CTX_get0_param (auth_ctx); status = X509_VERIFY_PARAM_set_flags (verify_param, store_ctx->flags); if (status == 0) { status = -ERR_get_error (); goto err_init; } X509_verify_cert (auth_ctx); status = X509_STORE_CTX_get_error (auth_ctx); if (status != 0) { status = X509_ENGINE_CERT_NOT_VALID; } err_init: X509_STORE_CTX_free (auth_ctx); err_ctx: return status; } #endif /** * Initialize an instance for handling X.509 certificates using OpenSSL. * * @param engine The X.509 engine to initialize. * * @return 0 if the X.509 engine was successfully initialized or an error code. */ int x509_openssl_init (struct x509_engine_openssl *engine) { if (engine == NULL) { return X509_ENGINE_INVALID_ARGUMENT; } memset (engine, 0, sizeof (struct x509_engine_openssl)); #ifdef X509_ENABLE_CREATE_CERTIFICATES engine->base.create_csr = x509_openssl_create_csr; engine->base.create_self_signed_certificate = x509_openssl_create_self_signed_certificate; engine->base.create_ca_signed_certificate = x509_openssl_create_ca_signed_certificate; #endif engine->base.load_certificate = x509_openssl_load_certificate; engine->base.release_certificate = x509_openssl_release_certificate; #ifdef X509_ENABLE_CREATE_CERTIFICATES engine->base.get_certificate_der = x509_openssl_get_certificate_der; #endif #ifdef X509_ENABLE_AUTHENTICATION engine->base.get_certificate_version = x509_openssl_get_certificate_version; engine->base.get_serial_number = x509_openssl_get_serial_number; engine->base.get_public_key_type = x509_openssl_get_public_key_type; engine->base.get_public_key_length = x509_openssl_get_public_key_length; engine->base.get_public_key = x509_openssl_get_public_key; engine->base.init_ca_cert_store = x509_openssl_init_ca_cert_store; engine->base.release_ca_cert_store = x509_openssl_release_ca_cert_store; engine->base.add_root_ca = x509_openssl_add_root_ca; engine->base.add_trusted_ca = x509_openssl_add_trusted_ca; engine->base.add_intermediate_ca = x509_openssl_add_intermediate_ca; engine->base.authenticate = x509_openssl_authenticate; #endif return 0; } /** * Release an OpenSSL X.509 engine. * * @param engine The X.509 engine to release. */ void x509_openssl_release (const struct x509_engine_openssl *engine) { UNUSED (engine); }