| /*-----------------------------------------------------------------------------------------------*/ | |
| /** | |
| @file mbtk_openssl.c | |
| @brief OPENSSL API | |
| */ | |
| /*-----------------------------------------------------------------------------------------------*/ | |
| /*------------------------------------------------------------------------------------------------- | |
| Copyright (c) 2024 mobiletek Wireless Solution, Co., Ltd. All Rights Reserved. | |
| mobiletek Wireless Solution Proprietary and Confidential. | |
| -------------------------------------------------------------------------------------------------*/ | |
| /*------------------------------------------------------------------------------------------------- | |
| EDIT HISTORY | |
| This section contains comments describing changes made to the file. | |
| Notice that changes are listed in reverse chronological order. | |
| $Header: $ | |
| when who what, where, why | |
| -------- --------- ----------------------------------------------------------------- | |
| 20250410 yq.wang Created . | |
| -------------------------------------------------------------------------------------------------*/ | |
| #ifdef MBTK_OPENSSL_V3_0_0_SUPPORT | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <sys/socket.h> | |
| #include <sys/select.h> | |
| #include <sys/time.h> | |
| #include "mbtk_openssl.h" | |
| #include "mbtk_log.h" | |
| #define MBTK_SSL_INFO_FD_DEFAULT -1 | |
| // X509 * SSL_get1_peer_certificate(SSL *ssl); | |
| static mbtk_openssl_result_e mbtk_openssl_wait_for_socket(int sockfd, bool is_read) | |
| { | |
| int ret = -1; | |
| fd_set fds; | |
| struct timeval tv = {.tv_sec = 5, .tv_usec = 0}; // 5s timeout | |
| FD_ZERO(&fds); | |
| FD_SET(sockfd, &fds); | |
| ret = select(sockfd + 1, is_read ? &fds : NULL, is_read ? NULL : &fds, NULL, &tv); | |
| if (0 >= ret) | |
| { | |
| LOGE("[%s] select() fail.[%d]", __func__, ret); | |
| return MBTK_OPENSSL_RESULT_FAIL; | |
| } | |
| return MBTK_OPENSSL_RESULT_SUCCESS; | |
| } | |
| mbtk_openssl_result_e mbtk_openssl_options_default(mbtk_openssl_options_s *opt) | |
| { | |
| if(NULL == opt) | |
| { | |
| LOGE("[%s] opt [NULL]", __func__); | |
| return MBTK_OPENSSL_RESULT_FAIL; | |
| } | |
| opt->load_cert = false; | |
| opt->ca_file = NULL; | |
| opt->crt_file = NULL; | |
| opt->key_file = NULL; | |
| opt->ssl_filetype = MBTK_OPENSSL_FILETYPE_PEM; | |
| opt->verify_mode = MBTK_OPENSSL_VERIFY_PEER; | |
| opt->verify_cb = NULL; | |
| opt->init_opts = MBTK_OPENSSL_INIT_LOAD_SSL_STRINGS | MBTK_OPENSSL_INIT_LOAD_CRYPTO_STRINGS \ | |
| | MBTK_OPENSSL_INIT_ADD_ALL_CIPHERS | MBTK_OPENSSL_INIT_ADD_ALL_DIGESTS; | |
| opt->safety_level = MBTK_OPENSSL_SAFETY_LEVEL_2; | |
| return MBTK_OPENSSL_RESULT_SUCCESS; | |
| } | |
| int mbtk_openssl_write(SSL *ssl, const void *buf, int len) | |
| { | |
| return SSL_write(ssl, buf, len); | |
| } | |
| int mbtk_openssl_read(SSL *ssl, void *buf, int len) | |
| { | |
| return SSL_read(ssl, buf, len); | |
| } | |
| mbtk_openssl_result_e mbtk_openssl_init(int fd, mbtk_openssl_options_s *opt, mbtk_openssl_info_s *inter_info) | |
| { | |
| int ret = -1; | |
| int ssl_error = -1; | |
| long verify_res = -1; | |
| char *line = NULL; | |
| X509 *cert = NULL; | |
| mbtk_openssl_result_e mbtk_ssl_ret = MBTK_OPENSSL_RESULT_SUCCESS; | |
| const SSL_METHOD *method = NULL; | |
| mbtk_openssl_info_s temp_inter_info = {0}; | |
| mbtk_openssl_options_s temp_opt = {0}; | |
| if(NULL == inter_info) | |
| { | |
| LOGE("[%s] inter_info [NULL]", __func__); | |
| return MBTK_OPENSSL_RESULT_FAIL; | |
| } | |
| if(NULL == opt) | |
| { | |
| mbtk_openssl_options_default(&temp_opt); | |
| } | |
| else | |
| { | |
| memset(&temp_opt, 0x00, sizeof(mbtk_openssl_options_s)); | |
| memcpy(&temp_opt, opt, sizeof(mbtk_openssl_options_s)); | |
| } | |
| //1.Initializes the OPENSSL library | |
| OPENSSL_init_ssl(temp_opt.init_opts, NULL); | |
| memset(&temp_inter_info, 0x00, sizeof(mbtk_openssl_info_s)); | |
| //2.Create an SSL/TLS context object | |
| method = TLS_client_method(); | |
| temp_inter_info.ctx = SSL_CTX_new(method); | |
| if(NULL == temp_inter_info.ctx) | |
| { | |
| LOGE("[%s] SSL_CTX_new() fail", __func__); | |
| goto error; | |
| } | |
| //3.Load certificate | |
| if(temp_opt.load_cert) | |
| { | |
| //3.1-Set the certificate security level | |
| SSL_CTX_set_security_level(temp_inter_info.ctx, temp_opt.safety_level); | |
| //3.2-Loading a CA Certificate | |
| if(NULL != temp_opt.ca_file) | |
| { | |
| ret = SSL_CTX_load_verify_locations(temp_inter_info.ctx, temp_opt.ca_file, NULL); | |
| if(1 != ret) | |
| { | |
| LOGE("[%s] SSL_CTX_load_verify_locations() fail.[%d]", __func__, ret); | |
| goto error; | |
| } | |
| } | |
| //3.3-Load the client public key | |
| if(NULL != temp_opt.crt_file) | |
| { | |
| ret = SSL_CTX_use_certificate_file(temp_inter_info.ctx, temp_opt.crt_file, temp_opt.ssl_filetype); | |
| if(1 != ret) | |
| { | |
| LOGE("[%s] SSL_CTX_use_certificate_file() fail.[%d]", __func__, ret); | |
| goto error; | |
| } | |
| } | |
| //3.4-Load the client private key | |
| if(NULL != temp_opt.key_file) | |
| { | |
| ret = SSL_CTX_use_PrivateKey_file(temp_inter_info.ctx, temp_opt.key_file, temp_opt.ssl_filetype); | |
| if(1 != ret) | |
| { | |
| LOGE("[%s] SSL_CTX_use_PrivateKey_file() fail.[%d]", __func__, ret); | |
| goto error; | |
| } | |
| } | |
| //3.5-Verify the private key matching certificate | |
| ret = SSL_CTX_check_private_key(temp_inter_info.ctx); | |
| if (1 != ret) | |
| { | |
| LOGE("[%s] SSL_CTX_check_private_key() fail.[%d]", __func__, ret); | |
| goto error; | |
| } | |
| //3.6-Set verification mode | |
| SSL_CTX_set_verify(temp_inter_info.ctx, temp_opt.verify_mode, temp_opt.verify_cb); | |
| } | |
| //4.Creates and initializes a new SSL/TLS session object | |
| temp_inter_info.ssl = SSL_new(temp_inter_info.ctx); | |
| if(NULL == temp_inter_info.ssl) | |
| { | |
| LOGE("[%s] SSL_new() fail", __func__); | |
| goto error; | |
| } | |
| SSL_set_fd(temp_inter_info.ssl, fd); | |
| LOGD("[%s] Performing the SSL/TLS handshake...", __func__); | |
| //5.Executive handshake | |
| //SSL_set_connect_state(temp_inter_info.ssl); | |
| while((ret = SSL_connect(temp_inter_info.ssl)) <= 0) | |
| { | |
| ssl_error = SSL_get_error(temp_inter_info.ssl, ret); | |
| if(ssl_error == SSL_ERROR_WANT_READ) | |
| { | |
| mbtk_ssl_ret = mbtk_openssl_wait_for_socket(fd, true); | |
| if(MBTK_OPENSSL_RESULT_SUCCESS != mbtk_ssl_ret) | |
| { | |
| LOGE("[%s] mbtk_openssl_wait_for_socket() fail", __func__); | |
| goto error; | |
| } | |
| } | |
| else if(ssl_error == SSL_ERROR_WANT_WRITE) | |
| { | |
| mbtk_ssl_ret = mbtk_openssl_wait_for_socket(fd, false); | |
| if(MBTK_OPENSSL_RESULT_SUCCESS != mbtk_ssl_ret) | |
| { | |
| LOGE("[%s] mbtk_openssl_wait_for_socket() fail", __func__); | |
| goto error; | |
| } | |
| } | |
| else | |
| { | |
| LOGE("[%s] SSL_connect() fail.[%d]", __func__, ssl_error); | |
| goto error; | |
| } | |
| } | |
| LOGD("[%s] SSL connect ok: Protocol[%s], Ciphersuite[%s]", __func__, SSL_get_version(temp_inter_info.ssl), SSL_get_cipher_name(temp_inter_info.ssl)); | |
| //6.Verification certificate | |
| if(temp_opt.load_cert) | |
| { | |
| cert = SSL_get1_peer_certificate(temp_inter_info.ssl); | |
| if(NULL != cert) | |
| { | |
| verify_res = SSL_get_verify_result(temp_inter_info.ssl); | |
| if(X509_V_OK != verify_res) | |
| { | |
| LOGE("[%s] SSL_get_verify_result() fail.[%s]", __func__, X509_verify_cert_error_string(verify_res)); | |
| goto error; | |
| } | |
| LOGD("[%s] Digital certificate information:", __func__); | |
| line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); | |
| LOGD("[%s] certificate: [%s]", __func__, line); | |
| free(line); | |
| line = NULL; | |
| line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); | |
| LOGD("[%s] issuer: [%s]", __func__, line); | |
| free(line); | |
| line = NULL; | |
| X509_free(cert); | |
| cert = NULL; | |
| } | |
| else | |
| { | |
| LOGD("[%s] No server certificate received", __func__); | |
| if(temp_opt.verify_mode != MBTK_OPENSSL_VERIFY_NONE) | |
| { | |
| LOGE("[%s] Verification fail", __func__); | |
| goto error; | |
| } | |
| } | |
| } | |
| temp_inter_info.fd = fd; | |
| memcpy(inter_info, &temp_inter_info, sizeof(mbtk_openssl_info_s)); | |
| return MBTK_OPENSSL_RESULT_SUCCESS; | |
| error: | |
| if(NULL != cert) | |
| { | |
| X509_free(cert); | |
| cert = NULL; | |
| } | |
| if(NULL != temp_inter_info.ssl) | |
| { | |
| SSL_shutdown(temp_inter_info.ssl); | |
| SSL_free(temp_inter_info.ssl); | |
| temp_inter_info.ssl = NULL; | |
| } | |
| if(NULL != temp_inter_info.ctx) | |
| { | |
| SSL_CTX_free(temp_inter_info.ctx); | |
| temp_inter_info.ctx = NULL; | |
| } | |
| return MBTK_OPENSSL_RESULT_FAIL; | |
| } | |
| mbtk_openssl_result_e mbtk_openssl_deinit(mbtk_openssl_info_s *inter_info) | |
| { | |
| if(NULL == inter_info) | |
| { | |
| LOGE("[%s] inter_info [NULL]", __func__); | |
| return MBTK_OPENSSL_RESULT_FAIL; | |
| } | |
| if(NULL != inter_info->ssl) | |
| { | |
| SSL_shutdown(inter_info->ssl); | |
| SSL_free(inter_info->ssl); | |
| inter_info->ssl = NULL; | |
| } | |
| if(NULL != inter_info->ctx) | |
| { | |
| SSL_CTX_free(inter_info->ctx); | |
| inter_info->ctx = NULL; | |
| } | |
| inter_info->fd = MBTK_SSL_INFO_FD_DEFAULT; | |
| return MBTK_OPENSSL_RESULT_SUCCESS; | |
| } | |
| #endif |