Merge branch 'feature/esp_tls_add_wolfssl_and_segregate_files' into 'master'

feature/esp_tls :segregate esp_tls , seperate mbedtls api and add wolfssl support for esp32

See merge request espressif/esp-idf!6095
This commit is contained in:
Mahavir Jain 2019-10-29 12:49:40 +08:00
commit 67a2f558df
13 changed files with 1358 additions and 638 deletions

View file

@ -1,5 +1,21 @@
idf_component_register(SRCS "esp_tls.c"
set(srcs esp_tls.c)
if(CONFIG_ESP_TLS_USING_MBEDTLS)
list(APPEND srcs
"esp_tls_mbedtls.c")
endif()
if(CONFIG_ESP_TLS_USING_WOLFSSL)
list(APPEND srcs
"esp_tls_wolfssl.c")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "."
PRIV_INCLUDE_DIRS "private_include"
REQUIRES mbedtls
PRIV_REQUIRES lwip nghttp)
if(CONFIG_ESP_TLS_USING_WOLFSSL)
idf_component_get_property(wolfssl esp-wolfssl COMPONENT_LIB)
target_link_libraries(${COMPONENT_LIB} PUBLIC ${wolfssl})
endif()

View file

@ -1,12 +1,28 @@
menu "ESP-TLS"
choice ESP_TLS_LIBRARY_CHOOSE
prompt "Choose SSL/TLS library for ESP-TLS (See help for more Info)"
default ESP_TLS_USING_MBEDTLS
help
The ESP-TLS APIs support multiple backend TLS libraries. Currently mbedTLS and WolfSSL are
supported. Different TLS libraries may support different features and have different resource
usage. Consult the ESP-TLS documentation in ESP-IDF Programming guide for more details.
config ESP_TLS_USING_MBEDTLS
bool "mbedTLS"
config ESP_TLS_USING_WOLFSSL
depends on TLS_STACK_WOLFSSL
bool "wolfSSL (License info in wolfSSL directory README)"
endchoice
config ESP_TLS_SERVER
bool "Enable ESP-TLS Server"
depends on ESP_TLS_USING_MBEDTLS
default n
help
Enable support for creating server side SSL/TLS session
Enable support for creating server side SSL/TLS session, uses the mbedtls crypto library
config ESP_TLS_PSK_VERIFICATION
bool "Enable PSK verification"
depends on ESP_TLS_USING_MBEDTLS
select MBEDTLS_PSK_MODES
select MBEDTLS_KEY_EXCHANGE_PSK
select MBEDTLS_KEY_EXCHANGE_DHE_PSK
@ -14,7 +30,22 @@ menu "ESP-TLS"
select MBEDTLS_KEY_EXCHANGE_RSA_PSK
default n
help
Enable support for pre shared key ciphers
Enable support for pre shared key ciphers, uses the mbedtls crypto library
config ESP_WOLFSSL_SMALL_CERT_VERIFY
bool "Enable SMALL_CERT_VERIFY"
depends on ESP_TLS_USING_WOLFSSL
default y
help
Enables server verification with Intermediate CA cert, does not authenticate full chain
of trust upto the root CA cert (After Enabling this option client only needs to have Intermediate
CA certificate of the server to authenticate server, root CA cert is not necessary).
config ESP_DEBUG_WOLFSSL
bool "Enable debug logs for wolfSSL"
depends on ESP_TLS_USING_WOLFSSL
default n
help
Enable detailed debug prints for wolfSSL SSL library.
endmenu

View file

@ -1,3 +1,16 @@
COMPONENT_SRCDIRS := .
COMPONENT_OBJS := esp_tls.o
COMPONENT_ADD_INCLUDEDIRS := . private_include
ifneq ($(CONFIG_ESP_TLS_USING_MBEDTLS), )
COMPONENT_OBJS += esp_tls_mbedtls.o
endif
ifneq ($(CONFIG_ESP_TLS_USING_WOLFSSL), )
COMPONENT_OBJS += esp_tls_wolfssl.o
endif
CFLAGS += -DWOLFSSL_USER_SETTINGS

View file

@ -1,4 +1,4 @@
// Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -24,9 +24,13 @@
#include "esp_tls.h"
#include "esp_tls_error_capture_internal.h"
#include <errno.h>
static const char *TAG = "esp-tls";
static mbedtls_x509_crt *global_cacert = NULL;
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
#include "esp_tls_mbedtls.h"
#elif CONFIG_ESP_TLS_USING_WOLFSSL
#include "esp_tls_wolfssl.h"
#endif
#ifdef ESP_PLATFORM
#include <esp_log.h>
@ -35,16 +39,86 @@ static mbedtls_x509_crt *global_cacert = NULL;
#define ESP_LOGE(TAG, ...) printf(__VA_ARGS__);
#endif
typedef struct esp_tls_pki_t {
mbedtls_x509_crt *public_cert;
mbedtls_pk_context *pk_key;
const unsigned char *publiccert_pem_buf;
unsigned int publiccert_pem_bytes;
const unsigned char *privkey_pem_buf;
unsigned int privkey_pem_bytes;
const unsigned char *privkey_password;
unsigned int privkey_password_len;
} esp_tls_pki_t;
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
#define _esp_create_ssl_handle esp_create_mbedtls_handle
#define _esp_tls_handshake esp_mbedtls_handshake
#define _esp_tls_read esp_mbedtls_read
#define _esp_tls_write esp_mbedtls_write
#define _esp_tls_conn_delete esp_mbedtls_conn_delete
#ifdef CONFIG_ESP_TLS_SERVER
#define _esp_tls_server_session_create esp_mbedtls_server_session_create
#define _esp_tls_server_session_delete esp_mbedtls_server_session_delete
#endif /* CONFIG_ESP_TLS_SERVER */
#define _esp_tls_get_bytes_avail esp_mbedtls_get_bytes_avail
#define _esp_tls_init_global_ca_store esp_mbedtls_init_global_ca_store
#define _esp_tls_set_global_ca_store esp_mbedtls_set_global_ca_store /*!< Callback function for setting global CA store data for TLS/SSL */
#define _esp_tls_get_global_ca_store esp_mbedtls_get_global_ca_store
#define _esp_tls_free_global_ca_store esp_mbedtls_free_global_ca_store /*!< Callback function for freeing global ca store for TLS/SSL */
#elif CONFIG_ESP_TLS_USING_WOLFSSL /* CONFIG_ESP_TLS_USING_MBEDTLS */
#define _esp_create_ssl_handle esp_create_wolfssl_handle
#define _esp_tls_handshake esp_wolfssl_handshake
#define _esp_tls_read esp_wolfssl_read
#define _esp_tls_write esp_wolfssl_write
#define _esp_tls_conn_delete esp_wolfssl_conn_delete
#define _esp_tls_get_bytes_avail esp_wolfssl_get_bytes_avail
#define _esp_tls_init_global_ca_store esp_wolfssl_init_global_ca_store
#define _esp_tls_set_global_ca_store esp_wolfssl_set_global_ca_store /*!< Callback function for setting global CA store data for TLS/SSL */
#define _esp_tls_free_global_ca_store esp_wolfssl_free_global_ca_store /*!< Callback function for freeing global ca store for TLS/SSL */
#else /* ESP_TLS_USING_WOLFSSL */
#error "No TLS stack configured"
#endif
static esp_err_t create_ssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls)
{
return _esp_create_ssl_handle(hostname, hostlen, cfg, tls);
}
static esp_err_t esp_tls_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
{
return _esp_tls_handshake(tls, cfg);
}
static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
{
return recv(tls->sockfd, data, datalen, 0);
}
static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
{
return send(tls->sockfd, data, datalen, 0);
}
/**
* @brief Close the TLS connection and free any allocated resources.
*/
void esp_tls_conn_delete(esp_tls_t *tls)
{
if (tls != NULL) {
_esp_tls_conn_delete(tls);
if (tls->sockfd >= 0) {
close(tls->sockfd);
}
free(tls->error_handle);
free(tls);
}
}
esp_tls_t *esp_tls_init(void)
{
esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
if (!tls) {
return NULL;
}
tls->error_handle = calloc(1, sizeof(esp_tls_last_error_t));
if (!tls->error_handle) {
free(tls);
return NULL;
}
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
tls->server_fd.fd = tls->sockfd = -1;
#endif
return tls;
}
static esp_err_t resolve_host_name(const char *host, size_t hostlen, struct addrinfo **address_info)
{
@ -68,26 +142,6 @@ static esp_err_t resolve_host_name(const char *host, size_t hostlen, struct addr
return ESP_OK;
}
static ssize_t tcp_read(esp_tls_t *tls, char *data, size_t datalen)
{
return recv(tls->sockfd, data, datalen, 0);
}
static ssize_t tls_read(esp_tls_t *tls, char *data, size_t datalen)
{
ssize_t ret = mbedtls_ssl_read(&tls->ssl, (unsigned char *)data, datalen);
if (ret < 0) {
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
return 0;
}
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
ESP_LOGE(TAG, "read error :%d:", ret);
}
}
return ret;
}
static void ms_to_timeval(int timeout_ms, struct timeval *tv)
{
tv->tv_sec = timeout_ms / 1000;
@ -159,426 +213,6 @@ err_freeaddr:
return ret;
}
esp_err_t esp_tls_init_global_ca_store(void)
{
if (global_cacert == NULL) {
global_cacert = (mbedtls_x509_crt *)calloc(1, sizeof(mbedtls_x509_crt));
if (global_cacert == NULL) {
ESP_LOGE(TAG, "global_cacert not allocated");
return ESP_ERR_NO_MEM;
}
mbedtls_x509_crt_init(global_cacert);
}
return ESP_OK;
}
esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes)
{
int ret;
if (cacert_pem_buf == NULL) {
ESP_LOGE(TAG, "cacert_pem_buf is null");
return ESP_ERR_INVALID_ARG;
}
if (global_cacert == NULL) {
ret = esp_tls_init_global_ca_store();
if (ret != ESP_OK) {
return ret;
}
}
ret = mbedtls_x509_crt_parse(global_cacert, cacert_pem_buf, cacert_pem_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x", -ret);
mbedtls_x509_crt_free(global_cacert);
global_cacert = NULL;
return ESP_FAIL;
} else if (ret > 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse was partly successful. No. of failed certificates: %d", ret);
return ESP_ERR_MBEDTLS_CERT_PARTLY_OK;
}
return ESP_OK;
}
mbedtls_x509_crt *esp_tls_get_global_ca_store(void)
{
return global_cacert;
}
void esp_tls_free_global_ca_store(void)
{
if (global_cacert) {
mbedtls_x509_crt_free(global_cacert);
global_cacert = NULL;
}
}
static void verify_certificate(esp_tls_t *tls)
{
int flags;
char buf[100];
if ((flags = mbedtls_ssl_get_verify_result(&tls->ssl)) != 0) {
ESP_LOGI(TAG, "Failed to verify peer certificate!");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS_CERT_FLAGS, flags);
bzero(buf, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags);
ESP_LOGI(TAG, "verification info: %s", buf);
} else {
ESP_LOGI(TAG, "Certificate verified.");
}
}
static void mbedtls_cleanup(esp_tls_t *tls)
{
if (!tls) {
return;
}
if (tls->cacert_ptr != global_cacert) {
mbedtls_x509_crt_free(tls->cacert_ptr);
}
tls->cacert_ptr = NULL;
#ifdef CONFIG_ESP_TLS_SERVER
mbedtls_x509_crt_free(&tls->servercert);
mbedtls_pk_free(&tls->serverkey);
#endif
mbedtls_x509_crt_free(&tls->cacert);
mbedtls_x509_crt_free(&tls->clientcert);
mbedtls_pk_free(&tls->clientkey);
mbedtls_entropy_free(&tls->entropy);
mbedtls_ssl_config_free(&tls->conf);
mbedtls_ctr_drbg_free(&tls->ctr_drbg);
mbedtls_ssl_free(&tls->ssl);
}
static esp_err_t set_global_ca_store(esp_tls_t *tls)
{
assert(tls);
if (global_cacert == NULL) {
ESP_LOGE(TAG, "global_cacert is NULL");
return ESP_ERR_INVALID_STATE;
}
tls->cacert_ptr = global_cacert;
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&tls->conf, tls->cacert_ptr, NULL);
return ESP_OK;
}
static esp_err_t set_ca_cert(esp_tls_t *tls, const unsigned char *cacert, size_t cacert_len)
{
assert(tls);
tls->cacert_ptr = &tls->cacert;
mbedtls_x509_crt_init(tls->cacert_ptr);
int ret = mbedtls_x509_crt_parse(tls->cacert_ptr, cacert, cacert_len);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED;
}
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&tls->conf, tls->cacert_ptr, NULL);
return ESP_OK;
}
static esp_err_t set_pki_context(esp_tls_t *tls, const esp_tls_pki_t *pki)
{
assert(tls);
assert(pki);
int ret;
if (pki->publiccert_pem_buf != NULL &&
pki->privkey_pem_buf != NULL &&
pki->public_cert != NULL &&
pki->pk_key != NULL) {
mbedtls_x509_crt_init(pki->public_cert);
mbedtls_pk_init(pki->pk_key);
ret = mbedtls_x509_crt_parse(pki->public_cert, pki->publiccert_pem_buf, pki->publiccert_pem_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED;
}
ret = mbedtls_pk_parse_key(pki->pk_key, pki->privkey_pem_buf, pki->privkey_pem_bytes,
NULL, 0);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_pk_parse_keyfile returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED;
}
ret = mbedtls_ssl_conf_own_cert(&tls->conf, pki->public_cert, pki->pk_key);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_own_cert returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONF_OWN_CERT_FAILED;
}
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
#ifdef CONFIG_ESP_TLS_SERVER
static esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls)
{
assert(cfg != NULL);
assert(tls != NULL);
int ret;
esp_err_t esp_ret;
if ((ret = mbedtls_ssl_config_defaults(&tls->conf,
MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED;
}
#ifdef CONFIG_MBEDTLS_SSL_ALPN
if (cfg->alpn_protos) {
mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos);
}
#endif
if (cfg->cacert_buf != NULL) {
esp_ret = set_ca_cert(tls, cfg->cacert_buf, cfg->cacert_bytes);
if (esp_ret != ESP_OK) {
return esp_ret;
}
} else {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
}
if (cfg->servercert_buf != NULL && cfg->serverkey_buf != NULL) {
esp_tls_pki_t pki = {
.public_cert = &tls->servercert,
.pk_key = &tls->serverkey,
.publiccert_pem_buf = cfg->servercert_buf,
.publiccert_pem_bytes = cfg->servercert_bytes,
.privkey_pem_buf = cfg->serverkey_buf,
.privkey_pem_bytes = cfg->serverkey_bytes,
.privkey_password = cfg->serverkey_password,
.privkey_password_len = cfg->serverkey_password_len,
};
esp_ret = set_pki_context(tls, &pki);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set server pki context");
return esp_ret;
}
} else {
ESP_LOGE(TAG, "Missing server certificate and/or key");
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
#endif /* ! CONFIG_ESP_TLS_SERVER */
static esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t *cfg, esp_tls_t *tls)
{
assert(cfg != NULL);
assert(tls != NULL);
int ret;
if (!cfg->skip_common_name) {
char *use_host = NULL;
if (cfg->common_name != NULL) {
use_host = strndup(cfg->common_name, strlen(cfg->common_name));
} else {
use_host = strndup(hostname, hostlen);
}
if (use_host == NULL) {
return ESP_ERR_NO_MEM;
}
/* Hostname set here should match CN in server certificate */
if ((ret = mbedtls_ssl_set_hostname(&tls->ssl, use_host)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
free(use_host);
return ESP_ERR_MBEDTLS_SSL_SET_HOSTNAME_FAILED;
}
free(use_host);
}
if ((ret = mbedtls_ssl_config_defaults(&tls->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED;
}
#ifdef CONFIG_MBEDTLS_SSL_ALPN
if (cfg->alpn_protos) {
if ((ret =mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos) != 0)) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_alpn_protocols returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED;
}
}
#endif
if (cfg->use_global_ca_store == true) {
esp_err_t esp_ret = set_global_ca_store(tls);
if (esp_ret != ESP_OK) {
return esp_ret;
}
} else if (cfg->cacert_buf != NULL) {
esp_err_t esp_ret = set_ca_cert(tls, cfg->cacert_buf, cfg->cacert_bytes);
if (esp_ret != ESP_OK) {
return esp_ret;
}
mbedtls_ssl_conf_ca_chain(&tls->conf, tls->cacert_ptr, NULL);
} else if (cfg->psk_hint_key) {
#if defined(CONFIG_ESP_TLS_PSK_VERIFICATION)
//
// PSK encryption mode is configured only if no certificate supplied and psk pointer not null
ESP_LOGD(TAG, "ssl psk authentication");
ret = mbedtls_ssl_conf_psk(&tls->conf, cfg->psk_hint_key->key, cfg->psk_hint_key->key_size,
(const unsigned char *)cfg->psk_hint_key->hint, strlen(cfg->psk_hint_key->hint));
if (ret != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_psk returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONF_PSK_FAILED;
}
#else
ESP_LOGE(TAG, "psk_hint_key configured but not enabled in menuconfig: Please enable ESP_TLS_PSK_VERIFICATION option");
return ESP_ERR_INVALID_STATE;
#endif
} else {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
}
if (cfg->clientcert_buf != NULL && cfg->clientkey_buf != NULL) {
esp_tls_pki_t pki = {
.public_cert = &tls->clientcert,
.pk_key = &tls->clientkey,
.publiccert_pem_buf = cfg->clientcert_buf,
.publiccert_pem_bytes = cfg->clientcert_bytes,
.privkey_pem_buf = cfg->clientkey_buf,
.privkey_pem_bytes = cfg->clientkey_bytes,
.privkey_password = cfg->clientkey_password,
.privkey_password_len = cfg->clientkey_password_len,
};
esp_err_t esp_ret = set_pki_context(tls, &pki);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set client pki context");
return esp_ret;
}
} else if (cfg->clientcert_buf != NULL || cfg->clientkey_buf != NULL) {
ESP_LOGE(TAG, "You have to provide both clientcert_buf and clientkey_buf for mutual authentication");
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
static esp_err_t create_ssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls)
{
assert(cfg != NULL);
assert(tls != NULL);
int ret;
esp_err_t esp_ret;
tls->server_fd.fd = tls->sockfd;
mbedtls_ssl_init(&tls->ssl);
mbedtls_ctr_drbg_init(&tls->ctr_drbg);
mbedtls_ssl_config_init(&tls->conf);
mbedtls_entropy_init(&tls->entropy);
if (tls->role == ESP_TLS_CLIENT) {
esp_ret = set_client_config(hostname, hostlen, (esp_tls_cfg_t *)cfg, tls);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set client configurations");
goto exit;
}
#ifdef CONFIG_ESP_TLS_SERVER
} else if (tls->role == ESP_TLS_SERVER) {
esp_ret = set_server_config((esp_tls_cfg_server_t *) cfg, tls);
if (esp_ret != 0) {
ESP_LOGE(TAG, "Failed to set server configurations");
goto exit;
}
#endif
}
if ((ret = mbedtls_ctr_drbg_seed(&tls->ctr_drbg,
mbedtls_entropy_func, &tls->entropy, NULL, 0)) != 0) {
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
esp_ret = ESP_ERR_MBEDTLS_CTR_DRBG_SEED_FAILED;
goto exit;
}
mbedtls_ssl_conf_rng(&tls->conf, mbedtls_ctr_drbg_random, &tls->ctr_drbg);
#ifdef CONFIG_MBEDTLS_DEBUG
mbedtls_esp_enable_debug_log(&tls->conf, CONFIG_MBEDTLS_DEBUG_LEVEL);
#endif
if ((ret = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
esp_ret = ESP_ERR_MBEDTLS_SSL_SETUP_FAILED;
goto exit;
}
mbedtls_ssl_set_bio(&tls->ssl, &tls->server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
return ESP_OK;
exit:
mbedtls_cleanup(tls);
return esp_ret;
}
/**
* @brief Close the TLS connection and free any allocated resources.
*/
void esp_tls_conn_delete(esp_tls_t *tls)
{
if (tls != NULL) {
mbedtls_cleanup(tls);
if (tls->is_tls) {
mbedtls_net_free(&tls->server_fd);
} else if (tls->sockfd >= 0) {
close(tls->sockfd);
}
free(tls->error_handle);
free(tls);
}
};
static ssize_t tcp_write(esp_tls_t *tls, const char *data, size_t datalen)
{
return send(tls->sockfd, data, datalen, 0);
}
static ssize_t tls_write(esp_tls_t *tls, const char *data, size_t datalen)
{
size_t written = 0;
size_t write_len = datalen;
while (written < datalen) {
if (write_len > MBEDTLS_SSL_OUT_CONTENT_LEN) {
write_len = MBEDTLS_SSL_OUT_CONTENT_LEN;
}
if (datalen > MBEDTLS_SSL_OUT_CONTENT_LEN) {
ESP_LOGD(TAG, "Fragmenting data of excessive size :%d, offset: %d, size %d", datalen, written, write_len);
}
ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) data + written, write_len);
if (ret <= 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != 0) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_MBEDTLS_SSL_WRITE_FAILED);
ESP_LOGE(TAG, "write error :%d:", ret);
return ret;
} else {
// Exitting the tls-write process as less than desired datalen are writable
ESP_LOGD(TAG, "mbedtls_ssl_write() returned %d, already written %d, exitting...", ret, written);
return written;
}
}
written += ret;
write_len = datalen - written;
}
return written;
}
static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
{
if (!tls) {
@ -586,101 +220,82 @@ static int esp_tls_low_level_conn(const char *hostname, int hostlen, int port, c
return -1;
}
esp_err_t esp_ret;
int ret;
/* These states are used to keep a tab on connection progress in case of non-blocking connect,
and in case of blocking connect these cases will get executed one after the other */
switch (tls->conn_state) {
case ESP_TLS_INIT:
tls->sockfd = -1;
if (cfg != NULL) {
mbedtls_net_init(&tls->server_fd);
tls->is_tls = true;
}
if ((esp_ret = esp_tcp_connect(hostname, hostlen, port, &tls->sockfd, tls, cfg)) != ESP_OK) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
return -1;
}
if (!cfg) {
tls->read = tcp_read;
tls->write = tcp_write;
ESP_LOGD(TAG, "non-tls connection established");
return 1;
}
if (cfg->non_block) {
FD_ZERO(&tls->rset);
FD_SET(tls->sockfd, &tls->rset);
tls->wset = tls->rset;
}
tls->conn_state = ESP_TLS_CONNECTING;
/* falls through */
case ESP_TLS_CONNECTING:
if (cfg->non_block) {
ESP_LOGD(TAG, "connecting...");
struct timeval tv;
ms_to_timeval(cfg->timeout_ms, &tv);
case ESP_TLS_INIT:
tls->sockfd = -1;
if (cfg != NULL) {
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
mbedtls_net_init(&tls->server_fd);
#endif
tls->is_tls = true;
}
if ((esp_ret = esp_tcp_connect(hostname, hostlen, port, &tls->sockfd, tls, cfg)) != ESP_OK) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
return -1;
}
if (!cfg) {
tls->read = tcp_read;
tls->write = tcp_write;
ESP_LOGD(TAG, "non-tls connection established");
return 1;
}
if (cfg->non_block) {
FD_ZERO(&tls->rset);
FD_SET(tls->sockfd, &tls->rset);
tls->wset = tls->rset;
}
tls->conn_state = ESP_TLS_CONNECTING;
/* falls through */
case ESP_TLS_CONNECTING:
if (cfg->non_block) {
ESP_LOGD(TAG, "connecting...");
struct timeval tv;
ms_to_timeval(cfg->timeout_ms, &tv);
/* In case of non-blocking I/O, we use the select() API to check whether
connection has been estbalished or not*/
if (select(tls->sockfd + 1, &tls->rset, &tls->wset, NULL,
cfg->timeout_ms ? &tv : NULL) == 0) {
ESP_LOGD(TAG, "select() timed out");
return 0;
}
if (FD_ISSET(tls->sockfd, &tls->rset) || FD_ISSET(tls->sockfd, &tls->wset)) {
int error;
unsigned int len = sizeof(error);
/* pending error check */
if (getsockopt(tls->sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
ESP_LOGD(TAG, "Non blocking connect failed");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_SYSTEM, errno);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED);
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
}
/* In case of non-blocking I/O, we use the select() API to check whether
connection has been estbalished or not*/
if (select(tls->sockfd + 1, &tls->rset, &tls->wset, NULL,
cfg->timeout_ms ? &tv : NULL) == 0) {
ESP_LOGD(TAG, "select() timed out");
return 0;
}
/* By now, the connection has been established */
esp_ret = create_ssl_handle(hostname, hostlen, cfg, tls);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "create_ssl_handle failed");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
tls->read = tls_read;
tls->write = tls_write;
tls->conn_state = ESP_TLS_HANDSHAKE;
/* falls through */
case ESP_TLS_HANDSHAKE:
ESP_LOGD(TAG, "handshake in progress...");
ret = mbedtls_ssl_handshake(&tls->ssl);
if (ret == 0) {
tls->conn_state = ESP_TLS_DONE;
return 1;
} else {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED);
if (cfg->cacert_buf != NULL || cfg->use_global_ca_store == true) {
/* This is to check whether handshake failed due to invalid certificate*/
verify_certificate(tls);
}
if (FD_ISSET(tls->sockfd, &tls->rset) || FD_ISSET(tls->sockfd, &tls->wset)) {
int error;
unsigned int len = sizeof(error);
/* pending error check */
if (getsockopt(tls->sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
ESP_LOGD(TAG, "Non blocking connect failed");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_SYSTEM, errno);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_ESP_TLS_SOCKET_SETOPT_FAILED);
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
/* Irrespective of blocking or non-blocking I/O, we return on getting MBEDTLS_ERR_SSL_WANT_READ
or MBEDTLS_ERR_SSL_WANT_WRITE during handshake */
return 0;
}
break;
case ESP_TLS_FAIL:
ESP_LOGE(TAG, "failed to open a new connection");;
break;
default:
ESP_LOGE(TAG, "invalid esp-tls state");
break;
}
/* By now, the connection has been established */
esp_ret = create_ssl_handle(hostname, hostlen, cfg, tls);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "create_ssl_handle failed");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
tls->read = _esp_tls_read;
tls->write = _esp_tls_write;
tls->conn_state = ESP_TLS_HANDSHAKE;
/* falls through */
case ESP_TLS_HANDSHAKE:
ESP_LOGD(TAG, "handshake in progress...");
return esp_tls_handshake(tls, cfg);
break;
case ESP_TLS_FAIL:
ESP_LOGE(TAG, "failed to open a new connection");;
break;
default:
ESP_LOGE(TAG, "invalid esp-tls state");
break;
}
return -1;
}
@ -728,7 +343,7 @@ int esp_tls_conn_new_sync(const char *hostname, int hostlen, int port, const esp
/*
* @brief Create a new TLS/SSL non-blocking connection
*/
int esp_tls_conn_new_async(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg , esp_tls_t *tls)
int esp_tls_conn_new_async(const char *hostname, int hostlen, int port, const esp_tls_cfg_t *cfg, esp_tls_t *tls)
{
return esp_tls_low_level_conn(hostname, hostlen, port, cfg, tls);
}
@ -757,24 +372,17 @@ esp_tls_t *esp_tls_conn_http_new(const char *url, const esp_tls_cfg_t *cfg)
http_parser_url_init(&u);
http_parser_parse_url(url, strlen(url), 0, &u);
esp_tls_t *tls = esp_tls_init();
if (!tls) return NULL;
if (!tls) {
return NULL;
}
/* Connect to host */
if (esp_tls_conn_new_sync(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
get_port(url, &u), cfg, tls) == 1) {
get_port(url, &u), cfg, tls) == 1) {
return tls;
}
return NULL;
}
ssize_t esp_tls_get_bytes_avail(esp_tls_t *tls)
{
if (!tls) {
ESP_LOGE(TAG, "empty arg passed to esp_tls_get_bytes_avail()");
return -1;
}
return mbedtls_ssl_get_bytes_avail(&tls->ssl);
}
/**
* @brief Create a new non-blocking TLS/SSL connection with a given "HTTP" url
*/
@ -787,78 +395,66 @@ int esp_tls_conn_http_new_async(const char *url, const esp_tls_cfg_t *cfg, esp_t
/* Connect to host */
return esp_tls_conn_new_async(&url[u.field_data[UF_HOST].off], u.field_data[UF_HOST].len,
get_port(url, &u), cfg, tls);
get_port(url, &u), cfg, tls);
}
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
mbedtls_x509_crt *esp_tls_get_global_ca_store(void)
{
return _esp_tls_get_global_ca_store();
}
#ifdef CONFIG_ESP_TLS_SERVER
/**
* @brief Create TLS/SSL server session
* @brief Create a server side TLS/SSL connection
*/
int esp_tls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls)
{
if (tls == NULL || cfg == NULL) {
return -1;
}
tls->role = ESP_TLS_SERVER;
tls->sockfd = sockfd;
esp_err_t esp_ret = create_ssl_handle(NULL, 0, cfg, tls);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "create_ssl_handle failed");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
tls->read = tls_read;
tls->write = tls_write;
int ret;
while ((ret = mbedtls_ssl_handshake(&tls->ssl)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "mbedtls_ssl_handshake returned %d", ret);
tls->conn_state = ESP_TLS_FAIL;
return ret;
}
}
return 0;
return _esp_tls_server_session_create(cfg, sockfd, tls);
}
/**
* @brief Close the server side TLS/SSL connection and free any allocated resources.
*/
void esp_tls_server_session_delete(esp_tls_t *tls)
{
if (tls != NULL) {
mbedtls_cleanup(tls);
free(tls);
}
};
#endif /* ! CONFIG_ESP_TLS_SERVER */
return _esp_tls_server_session_delete(tls);
}
#endif /* CONFIG_ESP_TLS_SERVER */
#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
esp_tls_t *esp_tls_init(void)
ssize_t esp_tls_get_bytes_avail(esp_tls_t *tls)
{
esp_tls_t *tls = (esp_tls_t *)calloc(1, sizeof(esp_tls_t));
if (!tls) {
return NULL;
}
tls->error_handle = calloc(1, sizeof(esp_tls_last_error_t));
if (!tls->error_handle) {
free(tls);
return NULL;
}
tls->server_fd.fd = tls->sockfd = -1;
return tls;
return _esp_tls_get_bytes_avail(tls);
}
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *mbedtls_code, int *mbedtls_flags)
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tls_code, int *esp_tls_flags)
{
if (!h) {
return ESP_ERR_INVALID_STATE;
}
esp_err_t last_err = h->last_error;
if (mbedtls_code) {
*mbedtls_code = h->mbedtls_error_code;
if (esp_tls_code) {
*esp_tls_code = h->esp_tls_error_code;
}
if (mbedtls_flags) {
*mbedtls_flags = h->mbedtls_flags;
if (esp_tls_flags) {
*esp_tls_flags = h->esp_tls_flags;
}
memset(h, 0, sizeof(esp_tls_last_error_t));
return last_err;
}
esp_err_t esp_tls_init_global_ca_store(void)
{
return _esp_tls_init_global_ca_store();
}
esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes)
{
return _esp_tls_set_global_ca_store(cacert_pem_buf, cacert_pem_bytes);
}
void esp_tls_free_global_ca_store(void)
{
return _esp_tls_free_global_ca_store();
}

View file

@ -18,7 +18,7 @@
#include <sys/socket.h>
#include <fcntl.h>
#include "esp_err.h"
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
#include "mbedtls/platform.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/esp_debug.h"
@ -27,6 +27,10 @@
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
#elif CONFIG_ESP_TLS_USING_WOLFSSL
#include "wolfssl/wolfcrypt/settings.h"
#include "wolfssl/ssl.h"
#endif
#ifdef __cplusplus
extern "C" {
@ -51,6 +55,15 @@ extern "C" {
#define ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED (ESP_ERR_ESP_TLS_BASE + 0x10) /*!< mbedtls api returned failed */
#define ESP_ERR_MBEDTLS_SSL_CONF_PSK_FAILED (ESP_ERR_ESP_TLS_BASE + 0x11) /*!< mbedtls api returned failed */
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
#define ESP_TLS_ERR_SSL_WANT_READ MBEDTLS_ERR_SSL_WANT_READ
#define ESP_TLS_ERR_SSL_WANT_WRITE MBEDTLS_ERR_SSL_WANT_WRITE
#define ESP_TLS_ERR_SSL_TIMEOUT MBEDTLS_ERR_SSL_TIMEOUT
#elif CONFIG_ESP_TLS_USING_WOLFSSL /* CONFIG_ESP_TLS_USING_MBEDTLS */
#define ESP_TLS_ERR_SSL_WANT_READ WOLFSSL_ERROR_WANT_READ
#define ESP_TLS_ERR_SSL_WANT_WRITE WOLFSSL_ERROR_WANT_WRITE
#define ESP_TLS_ERR_SSL_TIMEOUT WOLFSSL_CBIO_ERR_TIMEOUT
#endif /*CONFIG_ESP_TLS_USING_WOLFSSL */
typedef struct esp_tls_last_error* esp_tls_error_handle_t;
/**
@ -58,8 +71,8 @@ typedef struct esp_tls_last_error* esp_tls_error_handle_t;
*/
typedef struct esp_tls_last_error {
esp_err_t last_error; /*!< error code (based on ESP_ERR_ESP_TLS_BASE) of the last occurred error */
int mbedtls_error_code; /*!< mbedtls error code from last mbedtls failed api */
int mbedtls_flags; /*!< last certification verification flags */
int esp_tls_error_code; /*!< esp_tls error code from last esp_tls failed api */
int esp_tls_flags; /*!< last certification verification flags */
} esp_tls_last_error_t;
/**
@ -236,6 +249,7 @@ typedef struct esp_tls_cfg_server {
* @brief ESP-TLS Connection Handle
*/
typedef struct esp_tls {
#ifdef CONFIG_ESP_TLS_USING_MBEDTLS
mbedtls_ssl_context ssl; /*!< TLS/SSL context */
mbedtls_entropy_context entropy; /*!< mbedTLS entropy context structure */
@ -263,6 +277,10 @@ typedef struct esp_tls {
mbedtls_pk_context serverkey; /*!< Container for the private key of the server
certificate */
#endif
#elif CONFIG_ESP_TLS_USING_WOLFSSL
void *priv_ctx;
void *priv_ssl;
#endif
int sockfd; /*!< Underlying socket file descriptor. */
@ -491,20 +509,6 @@ esp_err_t esp_tls_init_global_ca_store(void);
*/
esp_err_t esp_tls_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes);
/**
* @brief Get the pointer to the global CA store currently being used.
*
* The application must first call esp_tls_set_global_ca_store(). Then the same
* CA store could be used by the application for APIs other than esp_tls.
*
* @note Modifying the pointer might cause a failure in verifying the certificates.
*
* @return
* - Pointer to the global CA store currently being used if successful.
* - NULL if there is no global CA store set.
*/
mbedtls_x509_crt *esp_tls_get_global_ca_store(void);
/**
* @brief Free the global CA store currently being used.
*
@ -518,17 +522,32 @@ void esp_tls_free_global_ca_store(void);
* The error information is cleared internally upon return
*
* @param[in] h esp-tls error handle.
* @param[out] mbedtls_code last error code returned from mbedtls api (set to zero if none)
* This pointer could be NULL if caller does not care about mbedtls_code
* @param[out] mbedtls_flags last certification verification flags (set to zero if none)
* This pointer could be NULL if caller does not care about mbedtls_flags
* @param[out] esp_tls_code last error code returned from mbedtls api (set to zero if none)
* This pointer could be NULL if caller does not care about esp_tls_code
* @param[out] esp_tls_flags last certification verification flags (set to zero if none)
* This pointer could be NULL if caller does not care about esp_tls_code
*
* @return
* - ESP_ERR_INVALID_STATE if invalid parameters
* - ESP_OK (0) if no error occurred
* - specific error code (based on ESP_ERR_ESP_TLS_BASE) otherwise
*/
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *mbedtls_code, int *mbedtls_flags);
esp_err_t esp_tls_get_and_clear_last_error(esp_tls_error_handle_t h, int *esp_tls_code, int *esp_tls_flags);
#if CONFIG_ESP_TLS_USING_MBEDTLS
/**
* @brief Get the pointer to the global CA store currently being used.
*
* The application must first call esp_tls_set_global_ca_store(). Then the same
* CA store could be used by the application for APIs other than esp_tls.
*
* @note Modifying the pointer might cause a failure in verifying the certificates.
*
* @return
* - Pointer to the global CA store currently being used if successful.
* - NULL if there is no global CA store set.
*/
mbedtls_x509_crt *esp_tls_get_global_ca_store(void);
#ifdef CONFIG_ESP_TLS_SERVER
/**
@ -557,6 +576,7 @@ int esp_tls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls
*/
void esp_tls_server_session_delete(esp_tls_t *tls);
#endif /* ! CONFIG_ESP_TLS_SERVER */
#endif /* CONFIG_ESP_TLS_USING_MBEDTLS */
#ifdef __cplusplus
}

View file

@ -0,0 +1,547 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <http_parser.h>
#include "esp_tls_mbedtls.h"
#include "esp_tls_error_capture_internal.h"
#include <errno.h>
#include "esp_log.h"
static const char *TAG = "esp-tls-mbedtls";
static mbedtls_x509_crt *global_cacert = NULL;
typedef struct esp_tls_pki_t {
mbedtls_x509_crt *public_cert;
mbedtls_pk_context *pk_key;
const unsigned char *publiccert_pem_buf;
unsigned int publiccert_pem_bytes;
const unsigned char *privkey_pem_buf;
unsigned int privkey_pem_bytes;
const unsigned char *privkey_password;
unsigned int privkey_password_len;
} esp_tls_pki_t;
esp_err_t esp_create_mbedtls_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls)
{
assert(cfg != NULL);
assert(tls != NULL);
int ret;
esp_err_t esp_ret = ESP_FAIL;
tls->server_fd.fd = tls->sockfd;
mbedtls_ssl_init(&tls->ssl);
mbedtls_ctr_drbg_init(&tls->ctr_drbg);
mbedtls_ssl_config_init(&tls->conf);
mbedtls_entropy_init(&tls->entropy);
if (tls->role == ESP_TLS_CLIENT) {
esp_ret = set_client_config(hostname, hostlen, (esp_tls_cfg_t *)cfg, tls);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set client configurations");
goto exit;
}
} else if (tls->role == ESP_TLS_SERVER) {
#ifdef CONFIG_ESP_TLS_SERVER
esp_ret = set_server_config((esp_tls_cfg_server_t *) cfg, tls);
if (esp_ret != 0) {
ESP_LOGE(TAG, "Failed to set server configurations");
goto exit;
}
#else
ESP_LOGE(TAG, "ESP_TLS_SERVER Not enabled in Kconfig");
goto exit;
#endif
}
if ((ret = mbedtls_ctr_drbg_seed(&tls->ctr_drbg,
mbedtls_entropy_func, &tls->entropy, NULL, 0)) != 0) {
ESP_LOGE(TAG, "mbedtls_ctr_drbg_seed returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
esp_ret = ESP_ERR_MBEDTLS_CTR_DRBG_SEED_FAILED;
goto exit;
}
mbedtls_ssl_conf_rng(&tls->conf, mbedtls_ctr_drbg_random, &tls->ctr_drbg);
#ifdef CONFIG_MBEDTLS_DEBUG
mbedtls_esp_enable_debug_log(&tls->conf, CONFIG_MBEDTLS_DEBUG_LEVEL);
#endif
if ((ret = mbedtls_ssl_setup(&tls->ssl, &tls->conf)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_setup returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
esp_ret = ESP_ERR_MBEDTLS_SSL_SETUP_FAILED;
goto exit;
}
mbedtls_ssl_set_bio(&tls->ssl, &tls->server_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
return ESP_OK;
exit:
esp_mbedtls_cleanup(tls);
return esp_ret;
}
int esp_mbedtls_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
{
int ret;
ret = mbedtls_ssl_handshake(&tls->ssl);
if (ret == 0) {
tls->conn_state = ESP_TLS_DONE;
return 1;
} else {
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "mbedtls_ssl_handshake returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_MBEDTLS_SSL_HANDSHAKE_FAILED);
if (cfg->cacert_buf != NULL || cfg->use_global_ca_store == true) {
/* This is to check whether handshake failed due to invalid certificate*/
esp_mbedtls_verify_certificate(tls);
}
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
/* Irrespective of blocking or non-blocking I/O, we return on getting ESP_TLS_ERR_SSL_WANT_READ
or ESP_TLS_ERR_SSL_WANT_WRITE during handshake */
return 0;
}
}
ssize_t esp_mbedtls_read(esp_tls_t *tls, char *data, size_t datalen)
{
ssize_t ret = mbedtls_ssl_read(&tls->ssl, (unsigned char *)data, datalen);
if (ret < 0) {
if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
return 0;
}
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
ESP_LOGE(TAG, "read error :%d:", ret);
}
}
return ret;
}
ssize_t esp_mbedtls_write(esp_tls_t *tls, const char *data, size_t datalen)
{
size_t written = 0;
size_t write_len = datalen;
while (written < datalen) {
if (write_len > MBEDTLS_SSL_OUT_CONTENT_LEN) {
write_len = MBEDTLS_SSL_OUT_CONTENT_LEN;
}
if (datalen > MBEDTLS_SSL_OUT_CONTENT_LEN) {
ESP_LOGD(TAG, "Fragmenting data of excessive size :%d, offset: %d, size %d", datalen, written, write_len);
}
ssize_t ret = mbedtls_ssl_write(&tls->ssl, (unsigned char*) data + written, write_len);
if (ret <= 0) {
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE && ret != 0) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, ESP_ERR_MBEDTLS_SSL_WRITE_FAILED);
ESP_LOGE(TAG, "write error :%d:", ret);
return ret;
} else {
// Exitting the tls-write process as less than desired datalen are writable
ESP_LOGD(TAG, "mbedtls_ssl_write() returned %d, already written %d, exitting...", ret, written);
return written;
}
}
written += ret;
write_len = datalen - written;
}
return written;
}
void esp_mbedtls_conn_delete(esp_tls_t *tls)
{
if (tls != NULL) {
esp_mbedtls_cleanup(tls);
if (tls->is_tls) {
mbedtls_net_free(&tls->server_fd);
}
}
}
void esp_mbedtls_verify_certificate(esp_tls_t *tls)
{
int flags;
char buf[100];
if ((flags = mbedtls_ssl_get_verify_result(&tls->ssl)) != 0) {
ESP_LOGI(TAG, "Failed to verify peer certificate!");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS_CERT_FLAGS, flags);
bzero(buf, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags);
ESP_LOGI(TAG, "verification info: %s", buf);
} else {
ESP_LOGI(TAG, "Certificate verified.");
}
}
ssize_t esp_mbedtls_get_bytes_avail(esp_tls_t *tls)
{
if (!tls) {
ESP_LOGE(TAG, "empty arg passed to esp_tls_get_bytes_avail()");
return ESP_FAIL;
}
return mbedtls_ssl_get_bytes_avail(&tls->ssl);
}
void esp_mbedtls_cleanup(esp_tls_t *tls)
{
if (!tls) {
return;
}
if (tls->cacert_ptr != global_cacert) {
mbedtls_x509_crt_free(tls->cacert_ptr);
}
tls->cacert_ptr = NULL;
#ifdef CONFIG_ESP_TLS_SERVER
mbedtls_x509_crt_free(&tls->servercert);
mbedtls_pk_free(&tls->serverkey);
#endif
mbedtls_x509_crt_free(&tls->cacert);
mbedtls_x509_crt_free(&tls->clientcert);
mbedtls_pk_free(&tls->clientkey);
mbedtls_entropy_free(&tls->entropy);
mbedtls_ssl_config_free(&tls->conf);
mbedtls_ctr_drbg_free(&tls->ctr_drbg);
mbedtls_ssl_free(&tls->ssl);
}
static esp_err_t set_ca_cert(esp_tls_t *tls, const unsigned char *cacert, size_t cacert_len)
{
assert(tls);
tls->cacert_ptr = &tls->cacert;
mbedtls_x509_crt_init(tls->cacert_ptr);
int ret = mbedtls_x509_crt_parse(tls->cacert_ptr, cacert, cacert_len);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED;
}
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&tls->conf, tls->cacert_ptr, NULL);
return ESP_OK;
}
static esp_err_t set_pki_context(esp_tls_t *tls, const esp_tls_pki_t *pki)
{
assert(tls);
assert(pki);
int ret;
if (pki->publiccert_pem_buf != NULL &&
pki->privkey_pem_buf != NULL &&
pki->public_cert != NULL &&
pki->pk_key != NULL) {
mbedtls_x509_crt_init(pki->public_cert);
mbedtls_pk_init(pki->pk_key);
ret = mbedtls_x509_crt_parse(pki->public_cert, pki->publiccert_pem_buf, pki->publiccert_pem_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_X509_CRT_PARSE_FAILED;
}
ret = mbedtls_pk_parse_key(pki->pk_key, pki->privkey_pem_buf, pki->privkey_pem_bytes,
NULL, 0);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_pk_parse_keyfile returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_PK_PARSE_KEY_FAILED;
}
ret = mbedtls_ssl_conf_own_cert(&tls->conf, pki->public_cert, pki->pk_key);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_own_cert returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONF_OWN_CERT_FAILED;
}
} else {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
static esp_err_t set_global_ca_store(esp_tls_t *tls)
{
assert(tls);
if (global_cacert == NULL) {
ESP_LOGE(TAG, "global_cacert is NULL");
return ESP_ERR_INVALID_STATE;
}
tls->cacert_ptr = global_cacert;
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&tls->conf, tls->cacert_ptr, NULL);
return ESP_OK;
}
#ifdef CONFIG_ESP_TLS_SERVER
esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls)
{
assert(cfg != NULL);
assert(tls != NULL);
int ret;
esp_err_t esp_ret;
if ((ret = mbedtls_ssl_config_defaults(&tls->conf,
MBEDTLS_SSL_IS_SERVER,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned %d", ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED;
}
#ifdef CONFIG_MBEDTLS_SSL_ALPN
if (cfg->alpn_protos) {
mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos);
}
#endif
if (cfg->cacert_buf != NULL) {
esp_ret = set_ca_cert(tls, cfg->cacert_buf, cfg->cacert_bytes);
if (esp_ret != ESP_OK) {
return esp_ret;
}
} else {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
}
if (cfg->servercert_buf != NULL && cfg->serverkey_buf != NULL) {
esp_tls_pki_t pki = {
.public_cert = &tls->servercert,
.pk_key = &tls->serverkey,
.publiccert_pem_buf = cfg->servercert_buf,
.publiccert_pem_bytes = cfg->servercert_bytes,
.privkey_pem_buf = cfg->serverkey_buf,
.privkey_pem_bytes = cfg->serverkey_bytes,
.privkey_password = cfg->serverkey_password,
.privkey_password_len = cfg->serverkey_password_len,
};
esp_ret = set_pki_context(tls, &pki);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set server pki context");
return esp_ret;
}
} else {
ESP_LOGE(TAG, "Missing server certificate and/or key");
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
#endif /* ! CONFIG_ESP_TLS_SERVER */
esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t *cfg, esp_tls_t *tls)
{
assert(cfg != NULL);
assert(tls != NULL);
int ret;
if (!cfg->skip_common_name) {
char *use_host = NULL;
if (cfg->common_name != NULL) {
use_host = strndup(cfg->common_name, strlen(cfg->common_name));
} else {
use_host = strndup(hostname, hostlen);
}
if (use_host == NULL) {
return ESP_ERR_NO_MEM;
}
/* Hostname set here should match CN in server certificate */
if ((ret = mbedtls_ssl_set_hostname(&tls->ssl, use_host)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_set_hostname returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
free(use_host);
return ESP_ERR_MBEDTLS_SSL_SET_HOSTNAME_FAILED;
}
free(use_host);
}
if ((ret = mbedtls_ssl_config_defaults(&tls->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_config_defaults returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONFIG_DEFAULTS_FAILED;
}
#ifdef CONFIG_MBEDTLS_SSL_ALPN
if (cfg->alpn_protos) {
if ((ret = mbedtls_ssl_conf_alpn_protocols(&tls->conf, cfg->alpn_protos) != 0)) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_alpn_protocols returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONF_ALPN_PROTOCOLS_FAILED;
}
}
#endif
if (cfg->use_global_ca_store == true) {
esp_err_t esp_ret = set_global_ca_store(tls);
if (esp_ret != ESP_OK) {
return esp_ret;
}
} else if (cfg->cacert_buf != NULL) {
esp_err_t esp_ret = set_ca_cert(tls, cfg->cacert_buf, cfg->cacert_bytes);
if (esp_ret != ESP_OK) {
return esp_ret;
}
mbedtls_ssl_conf_ca_chain(&tls->conf, tls->cacert_ptr, NULL);
} else if (cfg->psk_hint_key) {
#if defined(CONFIG_ESP_TLS_PSK_VERIFICATION)
//
// PSK encryption mode is configured only if no certificate supplied and psk pointer not null
ESP_LOGD(TAG, "ssl psk authentication");
ret = mbedtls_ssl_conf_psk(&tls->conf, cfg->psk_hint_key->key, cfg->psk_hint_key->key_size,
(const unsigned char *)cfg->psk_hint_key->hint, strlen(cfg->psk_hint_key->hint));
if (ret != 0) {
ESP_LOGE(TAG, "mbedtls_ssl_conf_psk returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_MBEDTLS, -ret);
return ESP_ERR_MBEDTLS_SSL_CONF_PSK_FAILED;
}
#else
ESP_LOGE(TAG, "psk_hint_key configured but not enabled in menuconfig: Please enable ESP_TLS_PSK_VERIFICATION option");
return ESP_ERR_INVALID_STATE;
#endif
} else {
mbedtls_ssl_conf_authmode(&tls->conf, MBEDTLS_SSL_VERIFY_NONE);
}
if (cfg->clientcert_buf != NULL && cfg->clientkey_buf != NULL) {
esp_tls_pki_t pki = {
.public_cert = &tls->clientcert,
.pk_key = &tls->clientkey,
.publiccert_pem_buf = cfg->clientcert_buf,
.publiccert_pem_bytes = cfg->clientcert_bytes,
.privkey_pem_buf = cfg->clientkey_buf,
.privkey_pem_bytes = cfg->clientkey_bytes,
.privkey_password = cfg->clientkey_password,
.privkey_password_len = cfg->clientkey_password_len,
};
esp_err_t esp_ret = set_pki_context(tls, &pki);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to set client pki context");
return esp_ret;
}
} else if (cfg->clientcert_buf != NULL || cfg->clientkey_buf != NULL) {
ESP_LOGE(TAG, "You have to provide both clientcert_buf and clientkey_buf for mutual authentication");
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
#ifdef CONFIG_ESP_TLS_SERVER
/**
* @brief Create TLS/SSL server session
*/
int esp_mbedtls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls)
{
if (tls == NULL || cfg == NULL) {
return -1;
}
tls->role = ESP_TLS_SERVER;
tls->sockfd = sockfd;
esp_err_t esp_ret = esp_create_mbedtls_handle(NULL, 0, cfg, tls);
if (esp_ret != ESP_OK) {
ESP_LOGE(TAG, "create_ssl_handle failed");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_ESP, esp_ret);
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
tls->read = esp_mbedtls_read;
tls->write = esp_mbedtls_write;
int ret;
while ((ret = mbedtls_ssl_handshake(&tls->ssl)) != 0) {
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "mbedtls_ssl_handshake returned %d", ret);
tls->conn_state = ESP_TLS_FAIL;
return ret;
}
}
return 0;
}
/**
* @brief Close the server side TLS/SSL connection and free any allocated resources.
*/
void esp_mbedtls_server_session_delete(esp_tls_t *tls)
{
if (tls != NULL) {
esp_mbedtls_cleanup(tls);
free(tls);
}
};
#endif /* ! CONFIG_ESP_TLS_SERVER */
esp_err_t esp_mbedtls_init_global_ca_store(void)
{
if (global_cacert == NULL) {
global_cacert = (mbedtls_x509_crt *)calloc(1, sizeof(mbedtls_x509_crt));
if (global_cacert == NULL) {
ESP_LOGE(TAG, "global_cacert not allocated");
return ESP_ERR_NO_MEM;
}
mbedtls_x509_crt_init(global_cacert);
}
return ESP_OK;
}
esp_err_t esp_mbedtls_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes)
{
if (cacert_pem_buf == NULL) {
ESP_LOGE(TAG, "cacert_pem_buf is null");
return ESP_ERR_INVALID_ARG;
}
int ret;
if (global_cacert == NULL) {
ret = esp_mbedtls_init_global_ca_store();
if (ret != ESP_OK) {
return ret;
}
}
ret = mbedtls_x509_crt_parse(global_cacert, cacert_pem_buf, cacert_pem_bytes);
if (ret < 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse returned -0x%x", -ret);
mbedtls_x509_crt_free(global_cacert);
global_cacert = NULL;
return ESP_FAIL;
} else if (ret > 0) {
ESP_LOGE(TAG, "mbedtls_x509_crt_parse was partly successful. No. of failed certificates: %d", ret);
return ESP_ERR_MBEDTLS_CERT_PARTLY_OK;
}
return ESP_OK;
}
mbedtls_x509_crt *esp_mbedtls_get_global_ca_store(void)
{
return global_cacert;
}
void esp_mbedtls_free_global_ca_store(void)
{
if (global_cacert) {
mbedtls_x509_crt_free(global_cacert);
global_cacert = NULL;
}
}

View file

@ -0,0 +1,244 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <http_parser.h>
#include "esp_tls_wolfssl.h"
#include "esp_tls_error_capture_internal.h"
#include <errno.h>
#include "esp_log.h"
static unsigned char *global_cacert = NULL;
static unsigned int global_cacert_pem_bytes = 0;
static const char *TAG = "esp-tls-wolfssl";
int esp_create_wolfssl_handle(const char *hostname, size_t hostlen, const void *cfg1, esp_tls_t *tls)
{
#ifdef CONFIG_ESP_DEBUG_WOLFSSL
wolfSSL_Debugging_ON();
#endif
const esp_tls_cfg_t *cfg = cfg1;
assert(cfg != NULL);
assert(tls != NULL);
int ret;
ret = wolfSSL_Init();
if (ret != WOLFSSL_SUCCESS) {
ESP_LOGE(TAG, "Init wolfSSL failed: %d", ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
goto exit;
}
tls->priv_ctx = (void *)wolfSSL_CTX_new(wolfTLSv1_2_client_method());
if (!tls->priv_ctx) {
ESP_LOGE(TAG, "Set wolfSSL ctx failed");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
goto exit;
}
#ifdef HAVE_ALPN
if (cfg->alpn_protos) {
char **alpn_list = (char **)cfg->alpn_protos;
for (; *alpn_list != NULL; alpn_list ++) {
if (wolfSSL_UseALPN( (WOLFSSL *)tls->priv_ssl, *alpn_list, strlen(*alpn_list), WOLFSSL_ALPN_FAILED_ON_MISMATCH) != WOLFSSL_SUCCESS) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
ESP_LOGE(TAG, "Use wolfSSL ALPN failed");
goto exit;
}
}
}
#endif
if ( cfg->use_global_ca_store == true) {
wolfSSL_CTX_load_verify_buffer( (WOLFSSL_CTX *)tls->priv_ctx, global_cacert, global_cacert_pem_bytes, WOLFSSL_FILETYPE_PEM);
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, SSL_VERIFY_PEER, NULL);
} else if (cfg->cacert_pem_buf != NULL) {
wolfSSL_CTX_load_verify_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cfg->cacert_pem_buf, cfg->cacert_pem_bytes, WOLFSSL_FILETYPE_PEM);
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, SSL_VERIFY_PEER, NULL);
} else if (cfg->psk_hint_key) {
ESP_LOGE(TAG,"psk_hint_key not supported in wolfssl");
goto exit;
} else {
wolfSSL_CTX_set_verify( (WOLFSSL_CTX *)tls->priv_ctx, SSL_VERIFY_NONE, NULL);
}
if (cfg->clientcert_pem_buf != NULL && cfg->clientkey_pem_buf != NULL) {
wolfSSL_CTX_use_certificate_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cfg->clientcert_pem_buf, cfg->clientcert_pem_bytes, WOLFSSL_FILETYPE_PEM);
wolfSSL_CTX_use_PrivateKey_buffer( (WOLFSSL_CTX *)tls->priv_ctx, cfg->clientkey_pem_buf, cfg->clientkey_pem_bytes, WOLFSSL_FILETYPE_PEM);
} else if (cfg->clientcert_pem_buf != NULL || cfg->clientkey_pem_buf != NULL) {
ESP_LOGE(TAG, "You have to provide both clientcert_pem_buf and clientkey_pem_buf for mutual authentication\n\n");
goto exit;
}
tls->priv_ssl =(void *)wolfSSL_new( (WOLFSSL_CTX *)tls->priv_ctx);
if (!tls->priv_ssl) {
ESP_LOGE(TAG, "Create wolfSSL failed");
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
goto exit;
}
#ifdef HAVE_SNI
/* Hostname set here should match CN in server certificate */
char *use_host = strndup(hostname, hostlen);
if (!use_host) {
goto exit;
}
wolfSSL_set_tlsext_host_name( (WOLFSSL *)tls->priv_ssl, use_host);
free(use_host);
#endif
wolfSSL_set_fd((WOLFSSL *)tls->priv_ssl, tls->sockfd);
return 0;
exit:
esp_wolfssl_cleanup(tls);
return ret;
}
int esp_wolfssl_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg)
{
int ret;
ret = wolfSSL_connect( (WOLFSSL *)tls->priv_ssl);
if (ret == WOLFSSL_SUCCESS) {
tls->conn_state = ESP_TLS_DONE;
return 1;
} else {
int err = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret);
if (err != ESP_TLS_ERR_SSL_WANT_READ && err != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "wolfSSL_connect returned -0x%x", -ret);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
if (cfg->cacert_pem_buf != NULL || cfg->use_global_ca_store == true) {
/* This is to check whether handshake failed due to invalid certificate*/
esp_wolfssl_verify_certificate(tls);
}
tls->conn_state = ESP_TLS_FAIL;
return -1;
}
/* Irrespective of blocking or non-blocking I/O, we return on getting wolfSSL_want_read
or wolfSSL_want_write during handshake */
return 0;
}
}
ssize_t esp_wolfssl_read(esp_tls_t *tls, char *data, size_t datalen)
{
ssize_t ret = wolfSSL_read( (WOLFSSL *)tls->priv_ssl, (unsigned char *)data, datalen);
if (ret < 0) {
ret = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret);
/* peer sent close notify */
if (ret == WOLFSSL_ERROR_ZERO_RETURN) {
return 0;
}
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
ESP_LOGE(TAG, "read error :%d:", ret);
}
}
return ret;
}
ssize_t esp_wolfssl_write(esp_tls_t *tls, const char *data, size_t datalen)
{
ssize_t ret = wolfSSL_write( (WOLFSSL *)tls->priv_ssl, (unsigned char *) data, datalen);
if (ret < 0) {
ret = wolfSSL_get_error( (WOLFSSL *)tls->priv_ssl, ret);
if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL, -ret);
ESP_LOGE(TAG, "write error :%d:", ret);
}
}
return ret;
}
void esp_wolfssl_verify_certificate(esp_tls_t *tls)
{
int flags;
if ((flags = wolfSSL_get_verify_result( (WOLFSSL *)tls->priv_ssl)) != WOLFSSL_SUCCESS) {
ESP_LOGE(TAG, "Failed to verify peer certificate %d!", flags);
ESP_INT_EVENT_TRACKER_CAPTURE(tls->error_handle, ERR_TYPE_WOLFSSL_CERT_FLAGS, flags);
} else {
ESP_LOGI(TAG, "Certificate verified.");
}
}
ssize_t esp_wolfssl_get_bytes_avail(esp_tls_t *tls)
{
if (!tls) {
ESP_LOGE(TAG, "empty arg passed to esp_tls_get_bytes_avail()");
return ESP_FAIL;
}
return wolfSSL_pending( (WOLFSSL *)tls->priv_ssl);
}
void esp_wolfssl_conn_delete(esp_tls_t *tls)
{
if (tls != NULL) {
esp_wolfssl_cleanup(tls);
}
}
void esp_wolfssl_cleanup(esp_tls_t *tls)
{
if (!tls) {
return;
}
wolfSSL_shutdown( (WOLFSSL *)tls->priv_ssl);
wolfSSL_free( (WOLFSSL *)tls->priv_ssl);
wolfSSL_CTX_free( (WOLFSSL_CTX *)tls->priv_ctx);
wolfSSL_Cleanup();
}
esp_err_t esp_wolfssl_init_global_ca_store(void)
{
/* This function is just to provide consistancy between function calls of esp_tls.h and wolfssl */
return ESP_OK;
}
esp_err_t esp_wolfssl_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes)
{
if (cacert_pem_buf == NULL) {
ESP_LOGE(TAG, "cacert_pem_buf is null");
return ESP_ERR_INVALID_ARG;
}
if (global_cacert != NULL) {
esp_wolfssl_free_global_ca_store();
}
global_cacert = (unsigned char *)strndup((const char *)cacert_pem_buf, cacert_pem_bytes);
if (!global_cacert) {
return ESP_FAIL;
}
global_cacert_pem_bytes = cacert_pem_bytes;
return ESP_OK;
}
void esp_wolfssl_free_global_ca_store(void)
{
if (global_cacert) {
free(global_cacert);
global_cacert = NULL;
global_cacert_pem_bytes = 0;
}
}

View file

@ -33,11 +33,13 @@ typedef enum {
ERR_TYPE_MBEDTLS,
ERR_TYPE_MBEDTLS_CERT_FLAGS,
ERR_TYPE_ESP,
ERR_TYPE_WOLFSSL,
ERR_TYPE_WOLFSSL_CERT_FLAGS,
} err_type_t;
/**
* Error tracker logging macro, this implementation saves latest errors of
* ERR_TYPE_ESP, ERR_TYPE_MBEDTLS and ERR_TYPE_MBEDTLS_CERT_FLAGS types
* ERR_TYPE_ESP, ERR_TYPE_ESP_TLS and ERR_TYPE_ESP_TLS_CERT_FLAGS types
*/
#define ESP_INT_EVENT_TRACKER_CAPTURE(h, type, code) esp_int_event_tracker_capture(h, type, code)
@ -47,13 +49,19 @@ static inline void esp_int_event_tracker_capture(esp_tls_error_handle_t h, uint3
if (type == ERR_TYPE_ESP) {
h->last_error = code;
} else if (type == ERR_TYPE_MBEDTLS) {
h->mbedtls_error_code = code;
h->esp_tls_error_code = code;
} else if (type == ERR_TYPE_MBEDTLS_CERT_FLAGS) {
h->mbedtls_flags = code;
h->esp_tls_flags = code;
} else if (type == ERR_TYPE_WOLFSSL) {
h->esp_tls_error_code = code;
} else if (type == ERR_TYPE_WOLFSSL_CERT_FLAGS) {
h->esp_tls_flags = code;
}
}
}
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,104 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_tls.h"
/**
* Internal Callback API for mbedtls_ssl_read
*/
ssize_t esp_mbedtls_read(esp_tls_t *tls, char *data, size_t datalen);
/**
* Internal callback API for mbedtls_ssl_write
*/
ssize_t esp_mbedtls_write(esp_tls_t *tls, const char *data, size_t datalen);
/**
* Internal Callback for mbedtls_handshake
*/
int esp_mbedtls_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg);
/**
* Internal Callback for mbedtls_cleanup , frees up all the memory used by mbedtls
*/
void esp_mbedtls_cleanup(esp_tls_t *tls);
/**
* Internal Callback for Certificate verification for mbedtls
*/
void esp_mbedtls_verify_certificate(esp_tls_t *tls);
/**
* Internal Callback for deleting the mbedtls connection
*/
void esp_mbedtls_conn_delete(esp_tls_t *tls);
/**
* Internal Callback for mbedtls_get_bytes_avail
*/
ssize_t esp_mbedtls_get_bytes_avail(esp_tls_t *tls);
/**
* Internal Callback for creating ssl handle for mbedtls
*/
esp_err_t esp_create_mbedtls_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls);
#ifdef CONFIG_ESP_TLS_SERVER
/**
* Internal Callback for set_server_config
*
* /note :- can only be used with mbedtls ssl library
*/
esp_err_t set_server_config(esp_tls_cfg_server_t *cfg, esp_tls_t *tls);
/**
* Internal Callback for mbedtls_server_session_create
*
* /note :- The function can only be used with mbedtls ssl library
*/
int esp_mbedtls_server_session_create(esp_tls_cfg_server_t *cfg, int sockfd, esp_tls_t *tls);
/**
* Internal Callback for mbedtls_server_session_delete
*
* /note :- The function can only be used with mbedtls ssl library
*/
void esp_mbedtls_server_session_delete(esp_tls_t *tls);
#endif
/**
* Internal Callback for set_client_config_function
*/
esp_err_t set_client_config(const char *hostname, size_t hostlen, esp_tls_cfg_t *cfg, esp_tls_t *tls);
/**
* Internal Callback for mbedtls_init_global_ca_store
*/
esp_err_t esp_mbedtls_init_global_ca_store(void);
/**
* Callback function for setting global CA store data for TLS/SSL using mbedtls
*/
esp_err_t esp_mbedtls_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes);
/**
* Internal Callback for esp_tls_global_ca_store
*/
mbedtls_x509_crt *esp_mbedtls_get_global_ca_store(void);
/**
* Callback function for freeing global ca store for TLS/SSL using mbedtls
*/
void esp_mbedtls_free_global_ca_store(void);

View file

@ -0,0 +1,72 @@
// Copyright 2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_tls.h"
/**
* Internal Callback for creating ssl handle for wolfssl
*/
int esp_create_wolfssl_handle(const char *hostname, size_t hostlen, const void *cfg, esp_tls_t *tls);
/**
* Internal Callback for wolfssl_handshake
*/
int esp_wolfssl_handshake(esp_tls_t *tls, const esp_tls_cfg_t *cfg);
/**
* Internal Callback API for wolfssl_ssl_read
*/
ssize_t esp_wolfssl_read(esp_tls_t *tls, char *data, size_t datalen);
/**
* Internal callback API for wolfssl_ssl_write
*/
ssize_t esp_wolfssl_write(esp_tls_t *tls, const char *data, size_t datalen);
/**
* Internal Callback for wolfssl_cleanup , frees up all the memory used by wolfssl
*/
void esp_wolfssl_cleanup(esp_tls_t *tls);
/**
* Internal Callback for Certificate verification for wolfssl
*/
void esp_wolfssl_verify_certificate(esp_tls_t *tls);
/**
* Internal Callback for deleting the wolfssl connection
*/
void esp_wolfssl_conn_delete(esp_tls_t *tls);
/**
* Internal Callback for wolfssl_get_bytes_avail
*/
ssize_t esp_wolfssl_get_bytes_avail(esp_tls_t *tls);
/**
* Callback function for setting global CA store data for TLS/SSL using wolfssl
*/
esp_err_t esp_wolfssl_set_global_ca_store(const unsigned char *cacert_pem_buf, const unsigned int cacert_pem_bytes);
/**
* Callback function for freeing global ca store for TLS/SSL using wolfssl
*/
void esp_wolfssl_free_global_ca_store(void);
/**
*
* Callback function for Initializing the global ca store for TLS?SSL using wolfssl
*/
esp_err_t esp_wolfssl_init_global_ca_store(void);

View file

@ -6,10 +6,13 @@ Overview
The ESP-TLS component provides a simplified API interface for accessing the commonly used TLS functionality.
It supports common scenarios like CA certification validation, SNI, ALPN negotiation, non-blocking connection among others.
All the configuration can be specified in the esp_tls_cfg_t data structure. Once done, TLS communication can be conducted using the following APIs:
* esp_tls_conn_new(): for opening a new TLS connection
* esp_tls_conn_read/write(): for reading/writing from the connection
* esp_tls_conn_delete(): for freeing up the connection
All the configuration can be specified in the ``esp_tls_cfg_t`` data structure. Once done, TLS communication can be conducted using the following APIs:
* :cpp:func:`esp_tls_conn_new`: for opening a new TLS connection.
* :cpp:func:`esp_tls_conn_read`: for reading from the connection.
* :cpp:func:`esp_tls_conn_write`: for writing into the connection.
* :cpp:func:`esp_tls_conn_delete`: for freeing up the connection.
Any application layer protocol like HTTP1, HTTP2 etc can be executed on top of this layer.
Application Example
@ -17,8 +20,75 @@ Application Example
Simple HTTPS example that uses ESP-TLS to establish a secure socket connection: :example:`protocols/https_request`.
Tree structure for ESP-TLS component
-------------------------------------
.. highlight:: none
::
├── esp_tls.c
├── esp_tls.h
├── esp_tls_mbedtls.c
├── esp_tls_wolfssl.c
└── private_include
├── esp_tls_mbedtls.h
└── esp_tls_wolfssl.h
The ESP-TLS component has a file :component_file:`esp-tls/esp_tls.h` which contain the public API headers for the component. Internally ESP-TLS component uses one
of the two SSL/TLS Libraries between mbedtls and wolfssl for its operation. API specific to mbedtls are present in :component_file:`esp-tls/private_include/esp_tls_mbedtls.h` and API
specific to wolfssl are present in :component_file:`esp-tls/private_include/esp_tls_wolfssl.h`.
Underlying SSL/TLS Library Options
----------------------------------
The ESP-TLS component has an option to use mbedtls or wolfssl as their underlying SSL/TLS library. By default only mbedtls is available and is
used, wolfssl SSL/TLS library is available publicly at https://github.com/espressif/esp-wolfssl. The repository provides wolfssl component in binary format, it
also provides few examples which are useful for understanding the API. Please refer the repository README.md for
information on licensing and other options. Please see below option for using wolfssl in your project.
.. note:: `As the library options are internal to ESP-TLS, switching the libries will not change ESP-TLS specific code for a project.`
How to use wolfssl with ESP-IDF
-------------------------------
There are two ways to use wolfssl in your project
1) Directly add wolfssl as a component in your project with following three commands.::
(First change directory (cd) to your project directory)
mkdir components
cd components
git clone https://github.com/espressif/esp-wolfssl.git
2) Add wolfssl as an extra component in your project.
* Download wolfssl with::
git clone https://github.com/espressif/esp-wolfssl.git
* Include esp-wolfssl in ESP-IDF with setting EXTRA_COMPONENT_DIRS in CMakeLists.txt/Makefile of your project as done in `wolfssl/examples <https://github.com/espressif/esp-wolfssl/tree/master/examples>`_. For reference see Optional Project variables in :doc:`build-system.</api-guides/build-system>`
After above steps, you will have option to choose wolfssl as underlying SSL/TLS library in configuration menu of your project as follows::
idf.py/make menuconfig -> ESP-TLS -> choose SSL/TLS Library -> mbedtls/wolfssl
Comparison between mbedtls and wolfssl
--------------------------------------
The following table shows a typical comparison between wolfssl and mbedtls when :example:`protocols/https_request` example `(which has server authentication)` was run with both
SSL/TLS libraries and with all respective configurations set to default.
`(mbedtls IN_CONTENT length and OUT_CONTENT length were set to 16384 bytes and 4096 bytes respectively)`
+---------------------+------------+-----------+
| Property | Wolfssl | Mbedtls |
+=====================+============+===========+
| Total Heap Consumed | ~19 Kb | ~37 Kb |
+---------------------+------------+-----------+
| Task Stack Used | ~2.2 Kb | ~3.6 Kb |
+---------------------+------------+-----------+
| Bin size | ~858 Kb | ~736 Kb |
+---------------------+------------+-----------+
.. note:: `These values are subject to change with change in configuration options and version of respective libraries`.
API Reference
-------------
.. include:: /_build/inc/esp_tls.inc

View file

@ -39,7 +39,7 @@ static ssize_t callback_send_inner(struct sh2lib_handle *hd, const uint8_t *data
{
int rv = esp_tls_conn_write(hd->http2_tls, data, length);
if (rv <= 0) {
if (rv == MBEDTLS_ERR_SSL_WANT_READ || rv == MBEDTLS_ERR_SSL_WANT_WRITE) {
if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) {
rv = NGHTTP2_ERR_WOULDBLOCK;
} else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
@ -88,7 +88,7 @@ static ssize_t callback_recv(nghttp2_session *session, uint8_t *buf,
int rv;
rv = esp_tls_conn_read(hd->http2_tls, (char *)buf, (int)length);
if (rv < 0) {
if (rv == MBEDTLS_ERR_SSL_WANT_READ || rv == MBEDTLS_ERR_SSL_WANT_WRITE) {
if (rv == ESP_TLS_ERR_SSL_WANT_READ || rv == ESP_TLS_ERR_SSL_WANT_WRITE) {
rv = NGHTTP2_ERR_WOULDBLOCK;
} else {
rv = NGHTTP2_ERR_CALLBACK_FAILURE;
@ -366,4 +366,3 @@ int sh2lib_do_put(struct sh2lib_handle *hd, const char *path,
};
return sh2lib_do_putpost_with_nv(hd, nva, sizeof(nva) / sizeof(nva[0]), send_cb, recv_cb);
}

View file

@ -96,7 +96,7 @@ static void https_get_task(void *pvParameters)
if (ret >= 0) {
ESP_LOGI(TAG, "%d bytes written", ret);
written_bytes += ret;
} else if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
} else if (ret != ESP_TLS_ERR_SSL_WANT_READ && ret != ESP_TLS_ERR_SSL_WANT_WRITE) {
ESP_LOGE(TAG, "esp_tls_conn_write returned 0x%x", ret);
goto exit;
}
@ -110,7 +110,7 @@ static void https_get_task(void *pvParameters)
bzero(buf, sizeof(buf));
ret = esp_tls_conn_read(tls, (char *)buf, len);
if(ret == MBEDTLS_ERR_SSL_WANT_WRITE || ret == MBEDTLS_ERR_SSL_WANT_READ)
if(ret == ESP_TLS_ERR_SSL_WANT_WRITE || ret == ESP_TLS_ERR_SSL_WANT_READ)
continue;
if(ret < 0)